From d973bd3028255052140918cc161be0423fef436c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 1 Mar 2022 15:38:22 +0100 Subject: [PATCH 1/3] MOBILE-3814 messages: Fix on some styles --- .../messages/pages/discussion/discussion.html | 7 +- src/addons/mod/chat/pages/chat/chat.html | 6 +- .../session-messages/session-messages.html | 6 +- .../comments/pages/viewer/viewer.html | 12 +- .../components/user-menu/user-menu.ts | 2 +- src/theme/components/discussion.scss | 342 +++++++++--------- src/theme/theme.light.scss | 6 +- 7 files changed, 184 insertions(+), 197 deletions(-) diff --git a/src/addons/messages/pages/discussion/discussion.html b/src/addons/messages/pages/discussion/discussion.html index 7427dbd18..cc9770632 100644 --- a/src/addons/messages/pages/discussion/discussion.html +++ b/src/addons/messages/pages/discussion/discussion.html @@ -69,8 +69,7 @@

{{ title }}

- +

{{ message.timecreated | coreFormatDate: "strftimedayshort" }} @@ -100,10 +99,10 @@ -

+

-

+
{{ message.timecreated | coreFormatDate: "strftimetime" }} diff --git a/src/addons/mod/chat/pages/chat/chat.html b/src/addons/mod/chat/pages/chat/chat.html index 0fc26f280..92e022753 100644 --- a/src/addons/mod/chat/pages/chat/chat.html +++ b/src/addons/mod/chat/pages/chat/chat.html @@ -93,13 +93,13 @@
{{ message.userfullname }}

-

+

-

+
- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} + {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}
diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.html b/src/addons/mod/chat/pages/session-messages/session-messages.html index 7d0a57911..501e76cf8 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.html +++ b/src/addons/mod/chat/pages/session-messages/session-messages.html @@ -86,13 +86,13 @@
{{ message.userfullname }}
-

+

-

- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} +
+ {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}
diff --git a/src/core/features/comments/pages/viewer/viewer.html b/src/core/features/comments/pages/viewer/viewer.html index 4e1336a18..8a909686e 100644 --- a/src/core/features/comments/pages/viewer/viewer.html +++ b/src/core/features/comments/pages/viewer/viewer.html @@ -38,7 +38,7 @@ - +

@@ -56,13 +56,13 @@

{{ comment.fullname }}
-

+

-

+
- + {{ comment.timecreated * 1000 | coreFormatDate: 'strftimetime' }} @@ -94,11 +94,11 @@ {{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }}

-

+

-

+
{{ 'core.notsent' | translate }} diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.ts b/src/core/features/mainmenu/components/user-menu/user-menu.ts index a43cb3f66..87cd958f9 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.ts +++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts @@ -207,7 +207,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { const closeAll = await CoreDomUtils.openSideModal({ component: CoreLoginSitesComponent, - cssClass: 'core-modal-lateral-sm', + cssClass: 'core-modal-lateral core-modal-lateral-sm', }); if (closeAll) { diff --git a/src/theme/components/discussion.scss b/src/theme/components/discussion.scss index be9ec19d3..d6756b142 100644 --- a/src/theme/components/discussion.scss +++ b/src/theme/components/discussion.scss @@ -1,185 +1,173 @@ @import "~theme/globals.scss"; -:host { - ion-content { - --background: var(--background-alternative); - - &::part(scroll) { - padding-bottom: 0 !important; - } - } - - .addon-messages-discussion-container { - display: flex; - flex-direction: column; - padding-bottom: 15px; - background: var(--background-alternative); - } - - .addon-messages-date { - font-weight: normal; - font-size: 0.9rem; - } - - // Message item. - ion-item.addon-message { - border: 0; - border-radius: var(--small-radius); - padding: 0 8px 0 8px; - margin: 10px 8px 0 8px; - --background: var(--addon-messages-message-bg); - background: var(--background); - align-self: flex-start; - width: 90%; - max-width: 90%; - --min-height: var(--a11y-min-target-size); - position: relative; - @include core-transition(width); - // This is needed to display bubble tails. - overflow: visible; - - &::part(native) { - --inner-border-width: 0px; - --inner-padding-end: 0px; - padding: 0; - margin: 0; - } - - core-format-text > p:only-child { - display: inline; - } - - .addon-message-user { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-bottom: .5rem; - margin-top: 0; - color: var(--ion-text-color); - - core-user-avatar { - display: block; - --core-avatar-size: var(--addon-messages-avatar-size); - margin: 0; - } - - div { - font-weight: 500; - flex-grow: 1; - @include padding-horizontal(.5rem); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - - ion-note { - color: var(--addon-messages-message-note-text); - font-size: var(--addon-messages-message-note-font-size); - margin: 0; - padding: 0 0 8px 0; - align-self: flex-end; - } - - &[tappable]:active { - --background: var(--addon-messages-message-activated-bg); - } - - ion-label { - margin: 0; - padding: 8px 0; - } - - .addon-message-text { - display: inline-flex; - * { - color: var(--ion-text-color); - } - } - - .tail { - content: ''; - width: 0; - height: 0; - border: 0.5rem solid transparent; - position: absolute; - touch-action: none; - bottom: 0; - } - - // Defines when an item-message is the user's. - &.addon-message-mine { - --background: var(--addon-messages-message-mine-bg); - align-self: flex-end; - - &[tappable]:active { - --background: var(--addon-messages-message-mine-activated-bg); - } - - .spinner { - @include float(end); - @include margin(2px, -3px, -2px, 5px); - - svg { - width: 16px; - height: 16px; - } - } - - .tail { - @include position(null, -8px, null, null); - @include margin-horizontal(null, -0.5rem); - border-bottom-color: var(--addon-messages-message-mine-bg); - } - - &[tappable]:active .tail { - border-bottom-color: var(--addon-messages-message-mine-activated-bg); - } - } - - &.addon-message-not-mine .tail { - border-bottom-color: var(--addon-messages-message-bg); - @include position(null, null, null, -8px); - @include margin-horizontal(-0.5rem, null); - } - - &[tappable].addon-message-not-mine.activated .tail { - border-bottom-color: var(--addon-messages-message-activated-bg); - } - - .addon-messages-delete-button { - min-height: initial; - line-height: initial; - @include margin(0, null, 0, null); - height: var(--a11y-min-target-size) !important; - align-self: flex-end; - - ion-icon { - font-size: 1.4em; - line-height: initial; - color: var(--danger); - } - } - - &.addon-message-no-user { - margin-top: 8px; - } - } - - ion-item.addon-message.addon-message-mine + ion-item.addon-message.addon-message-no-user.addon-message-mine, - ion-item.addon-message.addon-message-not-mine + ion-item.addon-message.addon-message-no-user.addon-message-not-mine { - .item-heading { - margin-bottom: 0; - } - padding-top: 0; - } - -} - :host-context(.ios) { ion-footer .toolbar:last-child { padding-bottom: 4px; min-height: 0; } } + +ion-content { + --content-background: var(--background-alternative); + --background: var(--content-background); + + &::part(scroll) { + padding-bottom: 0 !important; + } +} + +.addon-messages-discussion-container { + display: flex; + flex-direction: column; + padding-bottom: 16px !important; + background: var(--content-background); +} + +.addon-messages-date { + font-weight: normal; + font-size: 0.9rem; +} + +// Message item. +ion-item.addon-message { + --message-background: var(--addon-messages-message-bg); + --message-activated-background: var(--addon-messages-message-activated-bg); + --message-alignment: flex-start; + + border: 0; + border-radius: var(--medium-radius); + padding: 0 8px 0 8px; + margin: 8px; + --background: var(--message-background); + background: var(--message-background); + align-self: var(--message-alignment); + width: 90%; + max-width: var(--list-item-max-width); + --min-height: var(--a11y-min-target-size); + position: relative; + @include core-transition(width); + // This is needed to display bubble tails. + overflow: visible; + + &::part(native) { + --inner-border-width: 0px; + --inner-padding-end: 0px; + padding: 0; + margin: 0; + } + + &:hover { + filter: drop-shadow(2px 2px 2px rgba(0,0,0,.3)); + } + + core-format-text > p:only-child { + display: inline; + } + + .addon-message-user { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: .5rem; + margin-top: 0; + color: var(--ion-text-color); + + core-user-avatar { + display: block; + --core-avatar-size: var(--addon-messages-avatar-size); + margin: 0; + } + + div { + font-weight: 500; + flex-grow: 1; + padding-left: .5rem; + padding-right: .5rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + ion-note { + color: var(--addon-messages-message-note-text); + font-size: var(--addon-messages-message-note-font-size); + margin: 0; + padding: 8px 0; + align-self: flex-start; + } + + &[tappable]:active { + --message-background: var(--message-activated-background); + } + + ion-label { + margin: 0; + padding: 8px 0; + } + + .addon-message-text { + display: inline-flex; + * { + color: var(--ion-text-color); + } + } + + .tail { + content: ''; + width: 0; + height: 0; + border: 0.5rem solid transparent; + position: absolute; + touch-action: none; + bottom: 0; + border-bottom-color: var(--message-background); + } + + // Defines when an item-message is the user's. + &.addon-message-mine { + --message-background: var(--addon-messages-message-mine-bg); + --message-activated-background: var(--addon-messages-message-mine-activated-bg); + --message-alignment: flex-end; + + .spinner { + @include float(end); + @include margin(2px, -3px, -2px, 5px); + + svg { + width: 16px; + height: 16px; + } + } + + .tail { + @include position(null, -8px, null, null); + @include margin-horizontal(null, -0.5rem); + } + } + + &.addon-message-not-mine .tail { + @include position(null, null, null, -8px); + @include margin-horizontal(-0.5rem, null); + } + + .addon-messages-delete-button { + min-height: initial; + line-height: initial; + margin-top: 0px; + margin-bottom: 0px; + height: var(--a11y-min-target-size) !important; + align-self: flex-end; + + ion-icon { + font-size: 1.4em; + line-height: initial; + color: var(--danger); + } + } + + &.addon-message-no-user { + margin-top: 0px; + } +} diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index e04f70066..5b44db892 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -104,7 +104,7 @@ ion-content { --background: var(--ion-background-color); - --background-alternative: var(--light); + --background-alternative: var(--gray-200); } --core-bottom-tabs-background: var(--white); @@ -324,8 +324,8 @@ --addon-messages-message-activated-bg: var(--gray-200); --addon-messages-message-note-text: var(--gray-500); --addon-messages-message-note-font-size: 75%; - --addon-messages-message-mine-bg: var(--gray-200); - --addon-messages-message-mine-activated-bg: var(--gray-300); + --addon-messages-message-mine-bg: var(--gray-300); + --addon-messages-message-mine-activated-bg: var(--gray-400); --addon-messages-avatar-size: 30px; --addon-messages-discussion-badge: var(--primary); --addon-messages-discussion-badge-text: var(--white); From a91b19aedb3d38bc73a69f27eb3b659d3a2190a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 1 Mar 2022 15:38:39 +0100 Subject: [PATCH 2/3] MOBILE-3814 styles: Improve side modal widths --- src/theme/globals.variables.scss | 2 -- src/theme/theme.base.scss | 60 ++++++++++---------------------- src/theme/theme.light.scss | 3 ++ 3 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/theme/globals.variables.scss b/src/theme/globals.variables.scss index 5ab1d48fb..09430b3a4 100644 --- a/src/theme/globals.variables.scss +++ b/src/theme/globals.variables.scss @@ -79,8 +79,6 @@ $screen-breakpoints: ( xl: 1200px ) !default; -$modal-lateral-width: 360px; - $core-course-image-background: #81ecec, #74b9ff, #a29bfe, #dfe6e9, #00b894, #0984e3, #b2bec3, #fdcb6e, #fd79a8, #6c5ce7 !default; $core-dd-question-colors: #FFFFFF, #B0C4DE, #DCDCDC, #D8BFD8, #87CEFA, #DAA520, #FFD700, #F0E68C !default; $core-text-hightlight-background-color: lighten($blue, 40%) !default; diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 2c79ec6b5..3181dbb72 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -628,55 +628,31 @@ body.core-iframe-fullscreen ion-router-outlet { z-index: 100000 !important; } -.core-modal-lateral .modal-wrapper { - @include margin-horizontal(16px, null); -} +.core-modal-lateral { + --ion-safe-area-left: 0px; + --ion-safe-area-right: 0px; -@media only screen and (min-height: 400px) and (min-width: #{$modal-lateral-width}) { - .core-modal-lateral { - --ion-safe-area-left: 0px; - --ion-safe-area-right: 0px; + .modal-wrapper { + @include margin-horizontal(var(--modal-lateral-margin), null); - .modal-wrapper { - position: absolute; - @include position(0 !important, 0 !important, 0 !important, unset !important); - display: block; - height: 100% !important; - width: auto; - min-width: calc(#{$modal-lateral-width} - 16px); - box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); - } - ion-backdrop { - visibility: visible; - } + position: absolute; + @include position(0 !important, 0 !important, 0 !important, unset !important); + display: block; + height: 100% !important; + width: calc(100% - var(--modal-lateral-margin)); + max-width: calc(var(--modal-lateral-max-width)); + box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); + } + + ion-backdrop { + visibility: visible; } } @each $breakpoint, $width in $screen-breakpoints { - .core-modal-lateral-#{$breakpoint} .modal-wrapper { - @include margin-horizontal(16px, null); + .core-modal-lateral-#{$breakpoint} { + --modal-lateral-max-width: #{$width}; } - - @media only screen and (min-height: 400px) and (min-width: #{$width}) { - .core-modal-lateral-#{$breakpoint} { - --ion-safe-area-left: 0px; - --ion-safe-area-right: 0px; - - .modal-wrapper { - position: absolute; - @include position(0 !important, 0 !important, 0 !important, unset !important); - display: block; - height: 100% !important; - width: auto; - min-width: #{$width}; - box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4); - } - ion-backdrop { - visibility: visible; - } - } - } - } // Hidden submit button. diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss index 5b44db892..99cc6f2a9 100644 --- a/src/theme/theme.light.scss +++ b/src/theme/theme.light.scss @@ -81,6 +81,9 @@ --list-item-max-width: 768px; + --modal-lateral-max-width: 320px; + --modal-lateral-margin: 56px; + --contrast-background: white; --ion-text-color: var(--text-color); From 389a1c896411fd5a4900e079ffce3ebdb1701e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 1 Mar 2022 14:23:29 +0100 Subject: [PATCH 3/3] MOBILE-3814 format-text: Fix expandable items height --- src/core/directives/collapsible-footer.ts | 81 ++++++++++++++++------- src/core/directives/collapsible-item.ts | 62 +++++++++++------ src/core/directives/format-text.ts | 35 ++++++---- src/theme/globals.mixins.scss | 14 ++-- 4 files changed, 126 insertions(+), 66 deletions(-) diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts index 71fd0b7fd..4f87f379f 100644 --- a/src/core/directives/collapsible-footer.ts +++ b/src/core/directives/collapsible-footer.ts @@ -17,6 +17,10 @@ import { ScrollDetail } from '@ionic/core'; import { IonContent } from '@ionic/angular'; import { CoreUtils } from '@services/utils/utils'; import { CoreMath } from '@singletons/math'; +import { CoreComponentsRegistry } from '@singletons/components-registry'; +import { CoreFormatTextDirective } from './format-text'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events'; /** * Directive to make an element fixed at the bottom collapsible when scrolling. @@ -32,11 +36,12 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { protected element: HTMLElement; protected initialHeight = 0; - protected initialPaddingBottom = 0; + protected initialPaddingBottom = '0px'; protected previousTop = 0; protected previousHeight = 0; protected stickTimeout?: number; protected content?: HTMLIonContentElement | null; + protected loadingChangedListener?: CoreEventObserver; constructor(el: ElementRef, protected ionContent: IonContent) { this.element = el.nativeElement; @@ -44,30 +49,28 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { } /** - * Setup scroll event listener. - * - * @param retries Number of retries left. + * Calculate the height of the footer. */ - protected async listenScrollEvents(retries = 5): Promise { - // Already initialized. - if (this.initialHeight > 0) { - return; - } + protected async calculateHeight(): Promise { + await this.waitFormatTextsRendered(this.element); - this.initialHeight = this.element.getBoundingClientRect().height; - - if (this.initialHeight == 0 && retries > 0) { - await CoreUtils.nextTicks(50); - - this.listenScrollEvents(retries - 1); - - return; - } + await CoreUtils.nextTick(); // Set a minimum height value. - this.initialHeight = this.initialHeight || 48; + this.initialHeight = this.element.getBoundingClientRect().height || 48; this.previousHeight = this.initialHeight; + this.setBarHeight(this.initialHeight); + } + + /** + * Setup scroll event listener. + */ + protected async listenScrollEvents(): Promise { + if (this.content) { + return; + } + this.content = this.element.closest('ion-content'); if (!this.content) { @@ -82,12 +85,15 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { } // Set a padding to not overlap elements. - this.initialPaddingBottom = parseFloat(this.content.style.getPropertyValue('--padding-bottom') || '0'); - this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + this.initialHeight + 'px'); + this.initialPaddingBottom = this.content.style.getPropertyValue('--padding-bottom') || this.initialPaddingBottom; + this.content.style.setProperty( + '--padding-bottom', + `calc(${this.initialPaddingBottom} + var(--core-collapsible-footer-height, 0px))`, + ); + const scroll = await this.content.getScrollElement(); this.content.scrollEvents = true; - this.setBarHeight(this.initialHeight); this.content.addEventListener('ionScroll', (e: CustomEvent): void => { if (!this.content) { return; @@ -98,6 +104,19 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { } + /** + * Wait until all children inside the element are done rendering. + * + * @param element Element. + */ + protected async waitFormatTextsRendered(element: Element): Promise { + const formatTexts = Array + .from(element.querySelectorAll('core-format-text')) + .map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective)); + + await Promise.all(formatTexts.map(formatText => formatText?.rendered())); + } + /** * On scroll function. * @@ -144,15 +163,29 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { /** * @inheritdoc */ - ngOnInit(): void { + async ngOnInit(): Promise { + // Calculate the height now. + await this.calculateHeight(); + setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong. + this.listenScrollEvents(); + + // Recalculate the height if a parent core-loading displays the content. + this.loadingChangedListener = + CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, async (data: CoreEventLoadingChangedData) => { + if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) { + // The format-text is inside the loading, re-calculate the height. + await this.calculateHeight(); + setTimeout(() => this.calculateHeight(), 200); + } + }); } /** * @inheritdoc */ async ngOnDestroy(): Promise { - this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px'); + this.content?.style.setProperty('--padding-bottom', this.initialPaddingBottom); } } diff --git a/src/core/directives/collapsible-item.ts b/src/core/directives/collapsible-item.ts index 5556d8669..3188421e9 100644 --- a/src/core/directives/collapsible-item.ts +++ b/src/core/directives/collapsible-item.ts @@ -14,8 +14,11 @@ import { Directive, ElementRef, Input, OnInit } from '@angular/core'; import { CoreDomUtils } from '@services/utils/dom'; +import { CoreUtils } from '@services/utils/utils'; import { Translate } from '@singletons'; +import { CoreComponentsRegistry } from '@singletons/components-registry'; import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreFormatTextDirective } from './format-text'; const defaultMaxHeight = 56; const buttonHeight = 44; @@ -54,7 +57,7 @@ export class CoreCollapsibleItemDirective implements OnInit { /** * @inheritdoc */ - ngOnInit(): void { + async ngOnInit(): Promise { if (typeof this.height === 'string') { this.maxHeight = this.height === '' ? defaultMaxHeight @@ -70,31 +73,44 @@ export class CoreCollapsibleItemDirective implements OnInit { } // Calculate the height now. - this.calculateHeight(); + await this.calculateHeight(); setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong. - this.setExpandButtonEnabled(false); - // Recalculate the height if a parent core-loading displays the content. this.loadingChangedListener = - CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, (data: CoreEventLoadingChangedData) => { + CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, async (data: CoreEventLoadingChangedData) => { if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) { - // The format-text is inside the loading, re-calculate the height. - this.calculateHeight(); + // The element is inside the loading, re-calculate the height. + await this.calculateHeight(); setTimeout(() => this.calculateHeight(), 200); } }); } + /** + * Wait until all children inside the element are done rendering. + * + * @param element Element. + */ + protected async waitFormatTextsRendered(element: Element): Promise { + const formatTexts = Array + .from(element.querySelectorAll('core-format-text')) + .map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective)); + + await Promise.all(formatTexts.map(formatText => formatText?.rendered())); + } + /** * Calculate the height and check if we need to display show more or not. */ - protected calculateHeight(): void { - // @todo: Work on calculate this height better. + protected async calculateHeight(): Promise { + await this.waitFormatTextsRendered(this.element); // Remove max-height (if any) to calculate the real height. const initialMaxHeight = this.element.style.maxHeight; - this.element.style.maxHeight = ''; + this.element.style.maxHeight = 'none'; + + await CoreUtils.nextTick(); const height = CoreDomUtils.getElementHeight(this.element) || 0; @@ -102,7 +118,7 @@ export class CoreCollapsibleItemDirective implements OnInit { this.element.style.maxHeight = initialMaxHeight; // If cannot calculate height, shorten always. - this.setExpandButtonEnabled(!height || height > this.maxHeight); + this.setExpandButtonEnabled(!height || height >= this.maxHeight); } /** @@ -115,9 +131,7 @@ export class CoreCollapsibleItemDirective implements OnInit { this.element.classList.toggle('collapsible-enabled', enable); if (!enable || this.element.querySelector('ion-button.collapsible-toggle')) { - this.element.style.maxHeight = !enable || this.expanded - ? '' - : this.maxHeight + 'px'; + this.setMaxHeight(!enable || this.expanded? undefined : this.maxHeight); return; } @@ -141,6 +155,19 @@ export class CoreCollapsibleItemDirective implements OnInit { this.toggleExpand(this.expanded); } + /** + * Set max height to element. + * + * @param maxHeight Max height if collapsed or undefined if expanded. + */ + protected setMaxHeight(maxHeight?: number): void { + if (maxHeight) { + this.element.style.setProperty('--max-height', maxHeight + buttonHeight + 'px'); + } else { + this.element.style.removeProperty('--max-height'); + } + } + /** * Expand or collapse text. * @@ -151,13 +178,8 @@ export class CoreCollapsibleItemDirective implements OnInit { expand = !this.expanded; } this.expanded = expand; - this.element.classList.toggle('collapsible-expanded', expand); this.element.classList.toggle('collapsible-collapsed', !expand); - if (expand) { - this.element.style.setProperty('--max-height', this.maxHeight + buttonHeight + 'px'); - } else { - this.element.style.removeProperty('--max-height'); - } + this.setMaxHeight(!expand? this.maxHeight: undefined); const toggleButton = this.element.querySelector('ion-button.collapsible-toggle'); const toggleText = toggleButton?.querySelector('.collapsible-toggle-text'); diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 5171ae54b..8d93e24d6 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -272,15 +272,19 @@ export class CoreFormatTextDirective implements OnChanges { /** * Calculate the height and check if we need to display show more or not. */ - protected calculateHeight(): void { + protected async calculateHeight(): Promise { // @todo: Work on calculate this height better. if (!this.maxHeight) { return; } + await this.rendered(); + // Remove max-height (if any) to calculate the real height. const initialMaxHeight = this.element.style.maxHeight; - this.element.style.maxHeight = ''; + this.element.style.maxHeight = 'none'; + + await CoreUtils.nextTick(); const height = this.getElementHeight(this.element); @@ -288,7 +292,20 @@ export class CoreFormatTextDirective implements OnChanges { this.element.style.maxHeight = initialMaxHeight; // If cannot calculate height, shorten always. - this.setExpandButtonEnabled(!height || height > this.maxHeight); + this.setExpandButtonEnabled(!height || height >= this.maxHeight); + } + + /** + * Set max height to element. + * + * @param maxHeight Max height if collapsed or undefined if expanded. + */ + protected setMaxHeight(maxHeight?: number): void { + if (maxHeight) { + this.element.style.setProperty('--max-height', maxHeight + 'px'); + } else { + this.element.style.removeProperty('--max-height'); + } } /** @@ -301,9 +318,7 @@ export class CoreFormatTextDirective implements OnChanges { this.element.classList.toggle('collapsible-enabled', enable); if (!enable || this.element.querySelector('ion-button.collapsible-toggle')) { - this.element.style.maxHeight = !enable || this.expanded - ? '' - : this.maxHeight + 'px'; + this.setMaxHeight(!enable || this.expanded? undefined : this.maxHeight); return; } @@ -337,13 +352,9 @@ export class CoreFormatTextDirective implements OnChanges { expand = !this.expanded; } this.expanded = expand; - this.element.classList.toggle('collapsible-expanded', expand); this.element.classList.toggle('collapsible-collapsed', !expand); - if (expand) { - this.element.style.setProperty('--max-height', this.maxHeight + 'px'); - } else { - this.element.style.removeProperty('--max-height'); - } + this.setMaxHeight(!expand? this.maxHeight: undefined); + const toggleButton = this.element.querySelector('ion-button.collapsible-toggle'); const toggleText = toggleButton?.querySelector('.collapsible-toggle-text'); if (!toggleButton || !toggleText) { diff --git a/src/theme/globals.mixins.scss b/src/theme/globals.mixins.scss index e77e74181..cbde34e92 100644 --- a/src/theme/globals.mixins.scss +++ b/src/theme/globals.mixins.scss @@ -235,6 +235,7 @@ @include media-breakpoint-down(sm) { &.collapsible-enabled { position:relative; + padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit. --display-toggle: block; .collapsible-toggle { @@ -262,6 +263,8 @@ background-position: center; background-repeat: no-repeat; background-size: 14px 14px; + transform: rotate(-90deg); + @include core-transition(transform, 500ms); @include push-arrow-color(626262, true); @@ -275,7 +278,7 @@ &.collapsible-collapsed { overflow: hidden; min-height: calc(var(--collapsible-min-button-height) + 12px); - max-height: calc(var(--max-height), auto); + max-height: calc(var(--max-height, auto)); .collapsible-toggle-arrow { transform: rotate(90deg); @@ -291,15 +294,6 @@ z-index: 6; } } - - &.collapsible-expanded { - max-height: none !important; - padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit. - - .collapsible-toggle-arrow { - transform: rotate(-90deg); - } - } } } }