- 0" slot="start">{{badge}}
{{ label }}:
{{selection}}
diff --git a/src/core/components/empty-box/empty-box.scss b/src/core/components/empty-box/empty-box.scss
index 812dde4ca..5f333fb36 100644
--- a/src/core/components/empty-box/empty-box.scss
+++ b/src/core/components/empty-box/empty-box.scss
@@ -13,6 +13,8 @@
padding: 16px;
--image-size: 120px;
+ height: 100%;
+
ion-icon {
font-size: var(--image-size);
}
diff --git a/src/core/components/spacer/spacer.ts b/src/core/components/spacer/spacer.ts
index e2196dd81..0bedc96bf 100644
--- a/src/core/components/spacer/spacer.ts
+++ b/src/core/components/spacer/spacer.ts
@@ -22,8 +22,9 @@ import { Component } from '@angular/core';
*/
@Component({
selector: 'core-spacer',
- template: '',
- styles: [':host {--item-divider-min-height: 30px;}'],
+ template: '',
+ styles: [':host { display: block; margin: var(--spacer-vertical) var(--spacer-horizontal); \
+ border-bottom: 1px solid var(--spacer-color);}'],
})
export class CoreSpacerComponent {
diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts
index f0b7c90ad..e90806092 100644
--- a/src/core/directives/collapsible-footer.ts
+++ b/src/core/directives/collapsible-footer.ts
@@ -50,7 +50,8 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
protected endContentScrollListener?: EventListener;
protected resizeListener?: CoreEventObserver;
protected slotPromise?: CoreCancellablePromise;
- protected calcPending = false;
+ protected viewportPromise?: CoreCancellablePromise;
+ protected loadingHeight = false;
protected pageDidEnterListener?: EventListener;
protected page?: HTMLElement;
@@ -85,13 +86,14 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
* Calculate the height of the footer.
*/
protected async calculateHeight(): Promise {
- if (!CoreDom.isElementVisible(this.element)) {
- this.calcPending = true;
-
+ if (this.loadingHeight) {
+ // Already calculating, return.
return;
}
+ this.loadingHeight = true;
- this.calcPending = false;
+ this.viewportPromise = CoreDom.waitToBeInViewport(this.element);
+ await this.viewportPromise;
this.element.classList.remove('is-active');
await CoreUtils.nextTick();
@@ -110,6 +112,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
this.element.classList.add('is-active');
this.setBarHeight(this.initialHeight);
+ this.loadingHeight = false;
}
/**
@@ -175,9 +178,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
this.page?.addEventListener(
'ionViewDidEnter',
this.pageDidEnterListener = () => {
- if (this.calcPending) {
- this.calculateHeight();
- }
+ this.calculateHeight();
},
);
}
@@ -255,6 +256,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
this.resizeListener?.off();
this.slotPromise?.cancel();
+ this.viewportPromise?.cancel();
}
}
diff --git a/src/core/directives/collapsible-header.ts b/src/core/directives/collapsible-header.ts
index 196a2b1ce..887918f05 100644
--- a/src/core/directives/collapsible-header.ts
+++ b/src/core/directives/collapsible-header.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core';
-import { CorePromisedValue } from '@classes/promised-value';
+import { CoreCancellablePromise } from '@classes/cancellable-promise';
import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreTabsOutletComponent } from '@components/tabs-outlet/tabs-outlet';
import { CoreTabsComponent } from '@components/tabs/tabs';
@@ -69,17 +69,15 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
protected content?: HTMLIonContentElement;
protected contentScrollListener?: EventListener;
protected endContentScrollListener?: EventListener;
- protected pageDidEnterListener?: EventListener;
protected resizeListener?: CoreEventObserver;
protected floatingTitle?: HTMLHeadingElement;
protected scrollingHeight?: number;
protected subscriptions: Subscription[] = [];
protected enabled = true;
protected isWithinContent = false;
- protected enteredPromise = new CorePromisedValue();
protected mutationObserver?: MutationObserver;
- protected firstEnter = true;
- protected initPending = false;
+ protected loadingFloatingTitle = false;
+ protected visiblePromise?: CoreCancellablePromise;
constructor(el: ElementRef) {
this.collapsedHeader = el.nativeElement;
@@ -89,6 +87,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
* @inheritdoc
*/
ngOnInit(): void {
+ this.collapsible = !CoreUtils.isFalseOrZero(this.collapsible);
this.init();
}
@@ -105,10 +104,9 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
await Promise.all([
this.initializeCollapsedHeader(),
this.initializeExpandedHeader(),
- await this.enteredPromise,
]);
- this.initializeFloatingTitle();
+ await this.initializeFloatingTitle();
this.initializeContent();
}
@@ -116,8 +114,9 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
* @inheritdoc
*/
async ngOnChanges(changes: {[name: string]: SimpleChange}): Promise {
- if (changes.collapsible) {
- this.enabled = !CoreUtils.isFalseOrZero(changes.collapsible.currentValue);
+ if (changes.collapsible && !changes.collapsible.firstChange) {
+ this.collapsible = !CoreUtils.isFalseOrZero(changes.collapsible.currentValue);
+ this.enabled = this.collapsible;
await this.init();
@@ -139,47 +138,16 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
if (this.content && this.endContentScrollListener) {
this.content.removeEventListener('ionScrollEnd', this.endContentScrollListener);
}
- if (this.page && this.pageDidEnterListener) {
- this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener);
- }
this.resizeListener?.off();
this.mutationObserver?.disconnect();
+ this.visiblePromise?.cancel();
}
/**
- * Search the page element, initialize it, and wait until it's ready for the transition to trigger on scroll.
+ * Listen to changing events.
*/
- protected initializePage(): void {
- if (!this.collapsedHeader.parentElement) {
- throw new Error('[collapsible-header] Couldn\'t get page');
- }
-
- // Find element and prepare classes.
- this.page = this.collapsedHeader.parentElement;
- this.page.classList.add('collapsible-header-page');
-
- this.page.addEventListener(
- 'ionViewDidEnter',
- this.pageDidEnterListener = () => {
- if (this.firstEnter) {
- this.firstEnter = false;
- clearTimeout(timeout);
- this.enteredPromise.resolve();
- } else if (this.initPending) {
- this.initializeFloatingTitle();
- }
- },
- );
-
- // Timeout in case event is never fired.
- const timeout = window.setTimeout(() => {
- if (this.firstEnter) {
- this.firstEnter = false;
- this.enteredPromise.reject(new Error('[collapsible-header] Waiting for ionViewDidEnter timeout reached'));
- }
- }, 5000);
-
+ protected listenEvents(): void {
this.resizeListener = CoreDom.onWindowResize(() => {
this.initializeFloatingTitle();
}, 50);
@@ -214,6 +182,19 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
});
}
+ /**
+ * Search the page element, initialize it, and wait until it's ready for the transition to trigger on scroll.
+ */
+ protected initializePage(): void {
+ if (!this.collapsedHeader.parentElement) {
+ throw new Error('[collapsible-header] Couldn\'t get page');
+ }
+
+ // Find element and prepare classes.
+ this.page = this.collapsedHeader.parentElement;
+ this.page.classList.add('collapsible-header-page');
+ }
+
/**
* Search the collapsed header element, initialize it, and wait until it's ready for the transition to trigger on scroll.
*/
@@ -251,6 +232,8 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
return;
}
+ this.listenEvents();
+
// Initialize from tabs.
const tabs = CoreComponentsRegistry.resolve(this.page.querySelector('core-tabs-outlet'), CoreTabsOutletComponent);
@@ -282,21 +265,22 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
/**
* Initialize a floating title to mimic transitioning the title from one state to the other.
*/
- protected initializeFloatingTitle(): void {
+ protected async initializeFloatingTitle(): Promise {
if (!this.page || !this.expandedHeader) {
- throw new Error('[collapsible-header] Couldn\'t create floating title');
- }
-
- if (!CoreDom.isElementVisible(this.expandedHeader)) {
- this.initPending = true;
-
return;
}
- this.initPending = false;
+ if (this.loadingFloatingTitle) {
+ // Already calculating, return.
+ return;
+ }
+ this.loadingFloatingTitle = true;
+
+ this.visiblePromise = CoreDom.waitToBeVisible(this.expandedHeader);
+ await this.visiblePromise;
this.page.classList.remove('collapsible-header-page-is-active');
- CoreUtils.nextTick();
+ await CoreUtils.nextTick();
// Add floating title and measure initial position.
const collapsedHeaderTitle = this.collapsedHeader.querySelector('h1') as HTMLHeadingElement;
@@ -368,6 +352,8 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
this.collapsedFontStyles = collapsedFontStyles;
this.expandedFontStyles = expandedFontStyles;
this.expandedHeaderHeight = expandedHeaderHeight;
+
+ this.loadingFloatingTitle = false;
}
/**
diff --git a/src/core/directives/collapsible-item.ts b/src/core/directives/collapsible-item.ts
index 6e95242f2..1a4950c8b 100644
--- a/src/core/directives/collapsible-item.ts
+++ b/src/core/directives/collapsible-item.ts
@@ -55,8 +55,9 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
protected resizeListener?: CoreEventObserver;
protected darkModeListener?: Subscription;
protected domPromise?: CoreCancellablePromise;
+ protected visiblePromise?: CoreCancellablePromise;
protected uniqueId: string;
- protected calcPending = false;
+ protected loadingHeight = false;
protected pageDidEnterListener?: EventListener;
protected page?: HTMLElement;
@@ -99,9 +100,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
this.page?.addEventListener(
'ionViewDidEnter',
this.pageDidEnterListener = () => {
- if (this.calcPending) {
- this.calculateHeight();
- }
+ this.calculateHeight();
},
);
@@ -143,20 +142,20 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
* Calculate the height and check if we need to display show more or not.
*/
protected async calculateHeight(): Promise {
+ if (this.loadingHeight) {
+ // Already calculating, return.
+ return;
+ }
+ this.loadingHeight = true;
+
+ this.visiblePromise = CoreDom.waitToBeVisible(this.element);
+ await this.visiblePromise;
+
// Remove max-height (if any) to calculate the real height.
this.element.classList.add('collapsible-loading-height');
await this.waitFormatTextsRendered();
- if (!this.element.clientHeight) {
- this.calcPending = true;
- this.element.classList.remove('collapsible-loading-height');
-
- return;
- }
-
- this.calcPending = false;
-
this.expandedHeight = this.element.getBoundingClientRect().height;
// Restore the max height now.
@@ -167,6 +166,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
this.setExpandButtonEnabled(enable);
this.setGradientColor();
+ this.loadingHeight = false;
}
/**
@@ -298,6 +298,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
this.resizeListener?.off();
this.darkModeListener?.unsubscribe();
this.domPromise?.cancel();
+ this.visiblePromise?.cancel();
if (this.page && this.pageDidEnterListener) {
this.page.removeEventListener('ionViewDidEnter', this.pageDidEnterListener);
diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts
index f134eddd6..a6f0d2f9d 100644
--- a/src/core/directives/format-text.ts
+++ b/src/core/directives/format-text.ts
@@ -256,7 +256,8 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncCompo
button.classList.add('hidden');
button.setAttribute('aria-label', label);
// Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed.
- button.innerHTML = '\
+ button.innerHTML = '\
';
button.addEventListener('click', (e: Event) => {
diff --git a/src/core/features/courses/pages/my/my.html b/src/core/features/courses/pages/my/my.html
index bf26674dc..1ea788466 100644
--- a/src/core/features/courses/pages/my/my.html
+++ b/src/core/features/courses/pages/my/my.html
@@ -11,9 +11,6 @@
-
-
-
diff --git a/src/core/features/courses/pages/my/my.ts b/src/core/features/courses/pages/my/my.ts
index 029225665..ad8f0a206 100644
--- a/src/core/features/courses/pages/my/my.ts
+++ b/src/core/features/courses/pages/my/my.ts
@@ -19,7 +19,6 @@ import { CoreCourseBlock } from '@features/course/services/course';
import { CoreCoursesDashboard, CoreCoursesDashboardProvider } from '@features/courses/services/dashboard';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { IonRefresher } from '@ionic/angular';
-import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
@@ -38,7 +37,6 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
@ViewChild(CoreBlockComponent) block!: CoreBlockComponent;
siteName = '';
- searchEnabled = false;
downloadCoursesEnabled = false;
userId: number;
loadedBlock?: Partial;
@@ -50,7 +48,6 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
constructor() {
// Refresh the enabled flags if site is updated.
this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
- this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
this.loadSiteName();
@@ -63,7 +60,6 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
* @inheritdoc
*/
ngOnInit(): void {
- this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
const deepLinkManager = new CoreMainMenuDeepLinkManager();
@@ -122,13 +118,6 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
};
}
- /**
- * Go to search courses.
- */
- async openSearch(): Promise {
- CoreNavigator.navigateToSitePath('/list', { params : { mode: 'search' } });
- }
-
/**
* Refresh the data.
*
diff --git a/src/core/features/sitehome/pages/index/index.scss b/src/core/features/sitehome/pages/index/index.scss
index f77fee9ef..2581b9366 100644
--- a/src/core/features/sitehome/pages/index/index.scss
+++ b/src/core/features/sitehome/pages/index/index.scss
@@ -11,10 +11,6 @@ ion-item ion-icon {
@include margin-horizontal(null, var(--margin-end));
}
-core-spacer ::ng-deep .item {
- border-radius: var(--medium-radius);
- --horizontal-margin: 10px;
- margin-left: var(--horizontal-margin);
- margin-right: var(--horizontal-margin);
- width: auto;
+core-spacer {
+ --spacer-horizontal: 10px;
}
diff --git a/src/core/lang.json b/src/core/lang.json
index a66c1a6a3..7429c930b 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -9,7 +9,6 @@
"allparticipants": "All participants",
"answer": "Answer",
"answered": "Answered",
- "applyfilters": "Apply filters",
"areyousure": "Are you sure?",
"back": "Back",
"browser": "Browser",
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index b10ac0d47..e135701a0 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -1020,10 +1020,16 @@ ion-select::part(icon) {
opacity: 1;
}
-ion-select-popover ion-item.core-select-option-title {
- cursor: pointer;
- ion-radio {
- display: none;
+ion-select-popover {
+ ion-item.core-select-option-border-bottom {
+ border-bottom: 1px solid var(--stroke);
+ }
+
+ ion-item.core-select-option-title {
+ cursor: pointer;
+ ion-radio {
+ display: none;
+ }
}
}
@@ -1091,20 +1097,28 @@ ion-chip {
}
ion-searchbar {
- .searchbar-search-icon.ios {
- top: 4px;
- }
- .searchbar-search-icon.md {
- }
+ height: var(--height) !important;
- .searchbar-input,
- .sc-ion-searchbar-md.searchbar-input,
- .sc-ion-searchbar-ios.searchbar-input {
- @include padding-horizontal(48px);
+ .searchbar-input-container {
+ color: var(--color) !important;
+ height: var(--height) !important;
}
.searchbar-input {
- @include padding-horizontal(48px);
+ height: var(--height) !important;
+ border: 1px solid var(--border-color) !important;
+ box-shadow: none !important;
+ border-radius: var(--border-radius) !important;
+ background: var(--background) !important;
+ @include padding-horizontal(var(--height) !important);
+ }
+
+ .searchbar-search-icon {
+ @include position(null, null, null, calc(var(--height) / 4) !important);
+ }
+
+ button {
+ @include position(null, 0 !important, null, null);
}
}
diff --git a/src/theme/theme.light.scss b/src/theme/theme.light.scss
index 14da0eacf..f8799c64f 100644
--- a/src/theme/theme.light.scss
+++ b/src/theme/theme.light.scss
@@ -190,23 +190,16 @@
--ion-searchbar-background: var(--ion-background-color);
--ion-searchbar-border-color: var(--core-input-stroke);
--ion-searchbar-border-radius: var(--core-input-radius);
+ --ion-searchbar-height: var(--a11y-min-target-size);
--ion-searchbar-color: var(--text-color);
--ion-searchbar-icon-color: var(--core-input-stroke);
ion-searchbar {
--background: var(--ion-searchbar-background);
--border-color: var(--ion-searchbar-border-color);
+ --color: var(--ion-searchbar-color);
+ --border-radius: var(--ion-searchbar-border-radius);
--icon-color: var(--ion-searchbar-icon-color);
-
- .searchbar-input-container {
- color: var(--ion-searchbar-color) !important;
- }
- .searchbar-input {
- height: var(--a11y-min-target-size);
- border: 1px solid var(--ion-searchbar-border-color);
- box-shadow: none;
- border-radius: var(--ion-searchbar-border-radius);
- background: var(--ion-searchbar-background) !important;
- }
+ --height: var(--ion-searchbar-height);
}
--core-search-box-background: var(--ion-background-color);
@@ -308,10 +301,8 @@
--min-height: var(--item-divider-min-height);
}
- --spacer-background: var(--light);
- core-spacer {
- --item-divider-background: var(--spacer-background);
- }
+ --spacer-vertical: 8px;
+ --spacer-color: var(--gray-300);
ion-note {
--color: var(--subdued-text-color);