diff --git a/libs/core/src/scss/_global-styles.scss b/libs/core/src/scss/_global-styles.scss index 92d841434a..8b13c3d3e8 100644 --- a/libs/core/src/scss/_global-styles.scss +++ b/libs/core/src/scss/_global-styles.scss @@ -234,3 +234,58 @@ ion-loading.kirby-loading-overlay { // Place icon to the right of text, with icons own width + the actual spacing padding-right: calc(#{$icon-scaling-factor} + #{utils.size('xxxs')}); } + +/* + * Experimental modal. + */ +$modal-max-height: 928px; +$modal-max-width: 594px; +$modal-border-radius: 32px; + +html.ios ion-modal.kirby-modal-experimental { + --background: var(--kirby-modal-background, #{utils.get-color('background-color')}); + --max-height: #{$modal-max-height}; + --min-height: #{$modal-max-width}; + + @include utils.media('>=medium') { + --border-radius: #{$modal-border-radius}; + + &::part(content) { + margin: 15px; + } + + &.xxs { + --width: #{map.get(utils.$experimental-modal-widths, 'xxs')}; + --height: #{map.get(utils.$experimental-modal-heights, 'xxs')}; + } + + &.xs { + --width: #{map.get(utils.$experimental-modal-widths, 'xs')}; + --height: #{map.get(utils.$experimental-modal-heights, 'xs')}; + } + + &.sm { + --width: #{map.get(utils.$experimental-modal-widths, 's')}; + --height: #{map.get(utils.$experimental-modal-heights, 's')}; + } + + &.md { + --width: #{map.get(utils.$experimental-modal-widths, 'm')}; + --height: #{map.get(utils.$experimental-modal-heights, 'm')}; + } + + &.lg { + --width: #{map.get(utils.$experimental-modal-widths, 'l')}; + --height: #{map.get(utils.$experimental-modal-heights, 'l')}; + } + } + + ion-toolbar { + padding-right: 0; + padding-left: 0; + } + + .kirby-grid { + display: flex; + } +} diff --git a/libs/core/src/scss/base/_variables.scss b/libs/core/src/scss/base/_variables.scss index dcccb2a688..5281d2036d 100644 --- a/libs/core/src/scss/base/_variables.scss +++ b/libs/core/src/scss/base/_variables.scss @@ -133,6 +133,23 @@ $modal-heights: ( $modal-default-height: map.get($modal-heights, 'm'); $drawer-default-height: map.get($modal-heights, 's'); +/* Modal Experimental +****************************************************************************/ +$experimental-modal-widths: ( + l: 960px, + m: 800px, + s: 720px, + xs: 600px, + xxs: 343px, +) !default; +$experimental-modal-heights: ( + l: math.floor(map.get($experimental-modal-widths, 'l') * 0.75), + m: math.floor(map.get($experimental-modal-widths, 'm') * 0.75), + s: math.floor(map.get($experimental-modal-widths, 's') * 0.75), + xs: math.floor(map.get($experimental-modal-widths, 'xs') * 0.75), + xxs: math.floor(map.get($experimental-modal-widths, 'xxs') * 0.75), +) !default; + /* Item ****************************************************************************/ $item-heights: ( diff --git a/libs/designsystem/modal/experimental/src/footer/footer.component.html b/libs/designsystem/modal/experimental/src/footer/footer.component.html index f514c9f971..72cb5517a0 100644 --- a/libs/designsystem/modal/experimental/src/footer/footer.component.html +++ b/libs/designsystem/modal/experimental/src/footer/footer.component.html @@ -1,3 +1,13 @@ - + + +
+ +
+ +
diff --git a/libs/designsystem/modal/experimental/src/footer/footer.component.scss b/libs/designsystem/modal/experimental/src/footer/footer.component.scss index aa9d7a17a9..91b9704d40 100644 --- a/libs/designsystem/modal/experimental/src/footer/footer.component.scss +++ b/libs/designsystem/modal/experimental/src/footer/footer.component.scss @@ -1,20 +1,33 @@ @use '@kirbydesign/core/src/scss/utils'; @use '@kirbydesign/core/src/scss/themes'; -$padding-horizontal: utils.size('s'); -$padding-vertical: utils.size('xxs'); +$padding: utils.size('m'); ion-footer { @include themes.apply-white-theme; box-shadow: utils.get-elevation(8); - display: flex; + display: grid; + grid-template-rows: utils.size('xl'); + grid-template-columns: utils.size('xl') 1fr utils.size('xl'); justify-content: var(--kirby-modal-footer-justify-content, center); align-items: center; background-color: var(--kirby-modal-footer-background, utils.get-color('white')); color: var(--kirby-modal-footer-color, utils.get-color('white-contrast')); - padding: $padding-vertical $padding-horizontal; - padding-bottom: calc(#{$padding-vertical} + var(--kirby-modal-footer-safe-area-bottom, 0px)); + padding: $padding; + padding-bottom: calc(#{$padding} + var(--kirby-modal-footer-safe-area-bottom, 0px)); + + .default-content { + display: flex; + align-items: center; + justify-content: center; + } + + .nav-button { + display: flex; + justify-content: center; + align-items: center; + } } @include utils.media(' - + - {{ title }} + + {{ title }} + - diff --git a/libs/designsystem/modal/experimental/src/modal/modal.component.scss b/libs/designsystem/modal/experimental/src/modal/modal.component.scss index fc798d0387..a39976dc71 100644 --- a/libs/designsystem/modal/experimental/src/modal/modal.component.scss +++ b/libs/designsystem/modal/experimental/src/modal/modal.component.scss @@ -1,54 +1,43 @@ +@use 'sass:map'; @use '@kirbydesign/core/src/scss/utils'; +@use '@kirbydesign/core/src/scss/themes'; // Global modal styling can be found at scss/base/_ionic.scss -@mixin contain-content() { - padding-top: 0; - position: relative; - contain: inherit; - min-height: min(var(--min-height), calc(var(--vh100) - var(--kirby-modal-padding-top, 0px))); - - ion-content { - contain: content; - max-height: calc( - var(--vh100) - var(--kirby-modal-padding-top, 0px) - var(--header-height) - - var(--footer-height) - ); - - &::part(scroll) { - height: '100%'; - position: relative; - } - } -} - -$toolbar-padding-inline: utils.size('s'); -$toolbar-padding-block: utils.size('xs'); - -@mixin phablet-toolbar-padding() { - $toolbar-padding-top: utils.size('xxs'); - @include utils.media('>=medium') { - padding-top: $toolbar-padding-top; - } -} +$toolbar-mobile-padding-inline: utils.size('s'); +$toolbar-mobile-padding-block: utils.size('xs'); +$toolbar-desktop-padding: utils.size('m'); +$modal-header-border-bottom: 1px solid utils.get-color('medium'); ion-header { + @include themes.apply-light-theme; + box-sizing: border-box; ion-toolbar { - --padding-start: #{$toolbar-padding-inline}; - --padding-end: #{$toolbar-padding-inline}; - --padding-bottom: #{$toolbar-padding-block}; - --padding-top: #{$toolbar-padding-block}; + --padding-start: #{$toolbar-mobile-padding-inline}; + --padding-end: #{$toolbar-mobile-padding-inline}; + --padding-bottom: #{$toolbar-mobile-padding-block}; + --padding-top: #{$toolbar-mobile-padding-block}; --border-width: 0; --background: transparent; - --color: var(--kirby-modal-color, #{utils.get-color('black')}); button { - color: var(--color); + background-color: utils.get-color('white'); } + } - @include phablet-toolbar-padding; + &.modal-header { + border-bottom: $modal-header-border-bottom; + } + + @include utils.media('>=medium') { + ion-toolbar { + --padding-start: #{$toolbar-desktop-padding}; + --padding-end: #{$toolbar-desktop-padding}; + --padding-bottom: #{$toolbar-desktop-padding}; + --padding-top: #{$toolbar-desktop-padding}; + } } } @@ -58,27 +47,19 @@ ion-header { --footer-height: 0px; } -:host-context(ion-modal) { - @include utils.media('>=medium') { - @include contain-content; - } -} - -ion-modal { - --background: var(--kirby-modal-background, #{utils.get-color('background-color')}); -} - ion-title { box-sizing: border-box; padding-inline-start: calc(48px + var(--padding-start)); padding-inline-end: calc(48px + var(--padding-end)); - font-size: utils.font-size('l'); - font-weight: utils.font-weight('bold'); } ion-content { --background: transparent; --color: var(--kirby-modal-color, #{utils.get-color('black')}); + --padding-top: #{utils.size('m')}; + --padding-bottom: #{utils.size('m')}; + --padding-start: #{utils.size('s')}; + --padding-end: #{utils.size('s')}; display: flex; flex-direction: column; @@ -88,22 +69,15 @@ ion-content { display: block; } - --padding-top: #{utils.size('m')}; - --padding-bottom: #{utils.size('m')}; - --padding-start: #{utils.size('s')}; - --padding-end: #{utils.size('s')}; + @include utils.media('>=medium') { + --padding-top: #{utils.size('xl')}; + --padding-bottom: #{utils.size('xl')}; + --padding-start: #{utils.size('xxl')}; + --padding-end: #{utils.size('xxl')}; + } } -// Ensure padding-rules are not merged with other media query, -// as this rule has to come AFTER the default mobile-first rule in order to override: - /* clean-css ignore:start */ -@include utils.media('>=medium') { - ion-content { - --padding-start: #{utils.size('xxxl')}; - --padding-end: #{utils.size('xxxl')}; - } -} :host(.collapsible-title) { ion-content { diff --git a/libs/designsystem/modal/experimental/src/modal/modal.component.ts b/libs/designsystem/modal/experimental/src/modal/modal.component.ts index 6b09cd0825..ce45dbb716 100644 --- a/libs/designsystem/modal/experimental/src/modal/modal.component.ts +++ b/libs/designsystem/modal/experimental/src/modal/modal.component.ts @@ -1,8 +1,11 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { IonContent, IonModal } from '@ionic/angular'; import { OverlayEventDetail } from '@ionic/core/components'; import { KirbyAnimation } from '@kirbydesign/designsystem/helpers'; +type Size = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'; +type SizeTemp = 'md'; + @Component({ selector: 'kirby-modal-experimental', templateUrl: './modal.component.html', @@ -10,6 +13,8 @@ import { KirbyAnimation } from '@kirbydesign/designsystem/helpers'; }) export class ModalExperimentalComponent { @ViewChild(IonModal) modal: IonModal; + @ViewChild(IonModal, { static: true, read: ElementRef }) + modalElement: ElementRef; @ViewChild(IonContent) ionContent: IonContent; @Input() open = false; @@ -17,6 +22,12 @@ export class ModalExperimentalComponent { @Input() title = ''; @Input() hasCollapsibleTitle = false; @Input() scrollDisabled = false; + @Input() size: SizeTemp = 'md'; + @Input() set height(userDefinedHeight: string) { + // If the user has defined a height, then we override the --height + // specified by the 'size' classes in /core/src/scss/_global-styles.scss + this.modalElement.nativeElement.style.setProperty('--height', userDefinedHeight); + } @Output() willPresent = new EventEmitter>(); @Output() didPresent = new EventEmitter>(); diff --git a/libs/designsystem/modal/experimental/src/services/modal.controller.ts b/libs/designsystem/modal/experimental/src/services/modal.controller.ts index 595999210f..f82ebbd5b8 100644 --- a/libs/designsystem/modal/experimental/src/services/modal.controller.ts +++ b/libs/designsystem/modal/experimental/src/services/modal.controller.ts @@ -1,9 +1,11 @@ import { Injectable } from '@angular/core'; import { ModalController } from '@ionic/angular'; -import { from, Observable, Subject, switchMap, tap } from 'rxjs'; +import { from, map, Observable, switchMap, take, tap } from 'rxjs'; import { OverlayEventDetail } from '@ionic/core/components'; export type ModalFlavor = 'modal'; +type Size = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'; +type SizeTemp = 'md'; export type ModalExperimentalConfig = { flavor?: ModalFlavor; @@ -13,6 +15,8 @@ export type ModalExperimentalConfig = { canDismiss?: boolean | (() => Promise); backdropDismiss?: boolean; showBackdrop?: boolean; + size?: SizeTemp; + height?: string; }; type ModalDismissObservables = { @@ -28,43 +32,45 @@ export class ModalExperimentalController { public showModal(config: ModalExperimentalConfig): ModalDismissObservables { if (this.isModalOpening) return; - const $onWillDismiss = new Subject(); - const onWillDismiss$ = $onWillDismiss.asObservable(); - - const $onDidDismiss = new Subject(); - const onDidDismiss$ = $onDidDismiss.asObservable(); + let customCssClasses: string[] = []; + if (config.cssClass) { + customCssClasses = Array.isArray(config.cssClass) ? config.cssClass : [config.cssClass]; + } const modal$ = from( this.ionicModalController.create({ component: config.component, componentProps: config.componentProps, - cssClass: config.cssClass, canDismiss: config.canDismiss, backdropDismiss: config.backdropDismiss, showBackdrop: config.showBackdrop, + cssClass: [ + 'kirby-modal-experimental', + config.size ? config.size : 'md', + ...customCssClasses, + ], }) ); this.isModalOpening = true; - modal$ - .pipe( - tap((modal) => from(modal.present())), - switchMap((modal) => modal.onWillDismiss()) - ) - .subscribe((res) => { - this.isModalOpening = false; - - $onWillDismiss.next(res); - $onWillDismiss.complete(); + const onWillDismiss$ = modal$.pipe( + map((modal) => { + if (config.height) { + modal.style.setProperty('--height', config.height); + } + return modal; + }), + tap((modal) => from(modal.present())), + switchMap((modal) => modal.onWillDismiss()), + take(1) + ); - $onDidDismiss.next(res); - $onDidDismiss.complete(); - }); + this.isModalOpening = false; return { onWillDismiss: onWillDismiss$, - onDidDismiss: onDidDismiss$, + onDidDismiss: onWillDismiss$, }; } diff --git a/libs/designsystem/modal/experimental/src/wrapper/wrapper.component.html b/libs/designsystem/modal/experimental/src/wrapper/wrapper.component.html index f1618179f3..5ede1a2f04 100644 --- a/libs/designsystem/modal/experimental/src/wrapper/wrapper.component.html +++ b/libs/designsystem/modal/experimental/src/wrapper/wrapper.component.html @@ -1,7 +1,7 @@ - {{ title }} + {{ title }}