commit
598f710678
|
@ -9,6 +9,12 @@
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 0.7rem;
|
padding: 0.7rem;
|
||||||
|
--margin-vertical: 12px;
|
||||||
|
--margin-end: 12px;
|
||||||
|
margin-top: var(--margin-vertical);
|
||||||
|
margin-bottom: var(--margin-vertical);
|
||||||
|
@include margin-horizontal(null, var(--margin-end));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@each $category, $value in $calendar-event-category-colors {
|
@each $category, $value in $calendar-event-category-colors {
|
||||||
|
|
|
@ -496,7 +496,6 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI
|
||||||
const weeks = result.weeks as AddonCalendarWeek[];
|
const weeks = result.weeks as AddonCalendarWeek[];
|
||||||
const currentDay = new Date().getDate();
|
const currentDay = new Date().getDate();
|
||||||
const currentTime = CoreTimeUtils.timestamp();
|
const currentTime = CoreTimeUtils.timestamp();
|
||||||
let isPast = true;
|
|
||||||
|
|
||||||
const preloadedMonth: PreloadedMonth = {
|
const preloadedMonth: PreloadedMonth = {
|
||||||
...month,
|
...month,
|
||||||
|
@ -523,8 +522,7 @@ class AddonCalendarMonthSlidesItemsManagerSource extends CoreSwipeSlidesDynamicI
|
||||||
|
|
||||||
if (preloadedMonth.isCurrentMonth) {
|
if (preloadedMonth.isCurrentMonth) {
|
||||||
day.istoday = day.mday == currentDay;
|
day.istoday = day.mday == currentDay;
|
||||||
day.ispast = isPast && !day.istoday;
|
day.ispast = preloadedMonth.isPastMonth || day.mday < currentDay;
|
||||||
isPast = day.ispast;
|
|
||||||
|
|
||||||
if (day.istoday) {
|
if (day.istoday) {
|
||||||
day.eventsFormated?.forEach((event) => {
|
day.eventsFormated?.forEach((event) => {
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-radio slot="end" value="custom"></ion-radio>
|
<ion-radio slot="end" value="custom"></ion-radio>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap" (click)="customInputClicked($event)">
|
||||||
<ion-label></ion-label>
|
<ion-label></ion-label>
|
||||||
|
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
import { AddonCalendar, AddonCalendarReminderUnits, AddonCalendarValueAndUnit } from '@addons/calendar/services/calendar';
|
import { AddonCalendar, AddonCalendarReminderUnits, AddonCalendarValueAndUnit } from '@addons/calendar/services/calendar';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
|
||||||
import { ModalController } from '@singletons';
|
import { ModalController } from '@singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,11 +165,9 @@ export class AddonCalendarReminderTimeModalComponent implements OnInit {
|
||||||
|
|
||||||
this.radioValue = 'custom';
|
this.radioValue = 'custom';
|
||||||
|
|
||||||
await CoreUtils.nextTick();
|
const target = <HTMLInputElement | HTMLElement | null> ev.target;
|
||||||
|
if (target) {
|
||||||
const target = <HTMLInputElement | Element | null> ev.target;
|
CoreDomUtils.focusElement(target);
|
||||||
if (target && 'focus' in target) {
|
|
||||||
target.focus();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,19 +49,7 @@ export class CoreAutoFocusDirective implements AfterViewInit {
|
||||||
|
|
||||||
await CoreDom.waitToBeInDOM(this.element);
|
await CoreDom.waitToBeInDOM(this.element);
|
||||||
|
|
||||||
let focusElement = this.element;
|
CoreDomUtils.focusElement(this.element);
|
||||||
|
|
||||||
if ('getInputElement' in focusElement) {
|
|
||||||
// If it's an Ionic element get the right input to use.
|
|
||||||
focusElement.componentOnReady && await focusElement.componentOnReady();
|
|
||||||
focusElement = await focusElement.getInputElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!focusElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreDomUtils.focusElement(focusElement);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,10 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
protected contentScrollListener?: EventListener;
|
protected contentScrollListener?: EventListener;
|
||||||
protected endContentScrollListener?: EventListener;
|
protected endContentScrollListener?: EventListener;
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
protected domPromise?: CoreCancellablePromise<void>;
|
protected slotPromise?: CoreCancellablePromise<void>;
|
||||||
|
|
||||||
constructor(el: ElementRef, protected ionContent: IonContent) {
|
constructor(el: ElementRef, protected ionContent: IonContent) {
|
||||||
this.element = el.nativeElement;
|
this.element = el.nativeElement;
|
||||||
this.element.setAttribute('slot', 'fixed'); // Just in case somebody forgets to add it.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,9 +62,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
// Only if not present or explicitly falsy it will be false.
|
// Only if not present or explicitly falsy it will be false.
|
||||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||||
this.domPromise = CoreDom.waitToBeInDOM(this.element);
|
this.slotPromise = CoreDom.slotOnContent(this.element);
|
||||||
|
|
||||||
await this.domPromise;
|
await this.slotPromise;
|
||||||
await this.waitLoadingsDone();
|
await this.waitLoadingsDone();
|
||||||
await this.waitFormatTextsRendered();
|
await this.waitFormatTextsRendered();
|
||||||
|
|
||||||
|
@ -229,7 +228,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeListener?.off();
|
this.resizeListener?.off();
|
||||||
this.domPromise?.cancel();
|
this.slotPromise?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,19 +31,18 @@ export class CoreFabDirective implements OnInit, OnDestroy {
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
protected content?: HTMLIonContentElement | null;
|
protected content?: HTMLIonContentElement | null;
|
||||||
protected initialPaddingBottom = 0;
|
protected initialPaddingBottom = 0;
|
||||||
protected domPromise?: CoreCancellablePromise<void>;
|
protected slotPromise?: CoreCancellablePromise<void>;
|
||||||
|
|
||||||
constructor(el: ElementRef) {
|
constructor(el: ElementRef) {
|
||||||
this.element = el.nativeElement;
|
this.element = el.nativeElement;
|
||||||
this.element.setAttribute('slot', 'fixed');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.domPromise = CoreDom.waitToBeInDOM(this.element);
|
this.slotPromise = CoreDom.slotOnContent(this.element);
|
||||||
await this.domPromise;
|
await this.slotPromise;
|
||||||
|
|
||||||
this.content = this.element.closest('ion-content');
|
this.content = this.element.closest('ion-content');
|
||||||
|
|
||||||
|
@ -70,12 +69,6 @@ export class CoreFabDirective implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialHeight = this.element.getBoundingClientRect().height || 56;
|
const initialHeight = this.element.getBoundingClientRect().height || 56;
|
||||||
|
|
||||||
// Move element to the nearest ion-content if it's not the parent
|
|
||||||
if (this.element.parentElement?.nodeName != 'ION-CONTENT') {
|
|
||||||
this.content.appendChild(this.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + initialHeight + 'px');
|
this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + initialHeight + 'px');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +79,7 @@ export class CoreFabDirective implements OnInit, OnDestroy {
|
||||||
if (this.content) {
|
if (this.content) {
|
||||||
this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px');
|
this.content.style.setProperty('--padding-bottom', this.initialPaddingBottom + 'px');
|
||||||
}
|
}
|
||||||
this.domPromise?.cancel();
|
this.slotPromise?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
|
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||||
import { CoreUserTours, CoreUserToursAlignment, CoreUserToursSide } from '@features/usertours/services/user-tours';
|
import { CoreUserTours, CoreUserToursAlignment, CoreUserToursSide } from '@features/usertours/services/user-tours';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreDom } from '@singletons/dom';
|
||||||
import { CoreBlockSideBlocksTourComponent } from '../side-blocks-tour/side-blocks-tour';
|
import { CoreBlockSideBlocksTourComponent } from '../side-blocks-tour/side-blocks-tour';
|
||||||
import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
|
import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
|
||||||
|
|
||||||
|
@ -26,11 +28,25 @@ import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
|
||||||
templateUrl: 'side-blocks-button.html',
|
templateUrl: 'side-blocks-button.html',
|
||||||
styleUrls: ['side-blocks-button.scss'],
|
styleUrls: ['side-blocks-button.scss'],
|
||||||
})
|
})
|
||||||
export class CoreBlockSideBlocksButtonComponent {
|
export class CoreBlockSideBlocksButtonComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() courseId!: number;
|
@Input() courseId!: number;
|
||||||
@ViewChild('button', { read: ElementRef }) button?: ElementRef<HTMLElement>;
|
@ViewChild('button', { read: ElementRef }) button?: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
protected element: HTMLElement;
|
||||||
|
protected slotPromise?: CoreCancellablePromise<void>;
|
||||||
|
|
||||||
|
constructor(el: ElementRef) {
|
||||||
|
this.element = el.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.slotPromise = CoreDom.slotOnContent(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open side blocks.
|
* Open side blocks.
|
||||||
*/
|
*/
|
||||||
|
@ -62,4 +78,11 @@ export class CoreBlockSideBlocksButtonComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.slotPromise?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component>
|
<core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component>
|
||||||
|
|
||||||
<core-block-side-blocks-button *ngIf="course && hasBlocks" [courseId]="course.id">
|
<core-block-side-blocks-button slot="fixed" *ngIf="course && hasBlocks" [courseId]="course.id">
|
||||||
</core-block-side-blocks-button>
|
</core-block-side-blocks-button>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
<core-block-side-blocks-button *ngIf="hasSideBlocks"></core-block-side-blocks-button>
|
<core-block-side-blocks-button slot="fixed" *ngIf="hasSideBlocks"></core-block-side-blocks-button>
|
||||||
|
|
||||||
<core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate">
|
<core-empty-box *ngIf="blocks.length == 0" icon="fas-cubes" [message]="'core.course.nocontentavailable' | translate">
|
||||||
</core-empty-box>
|
</core-empty-box>
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
</core-show-password>
|
</core-show-password>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-button expand="block" type="submit" [disabled]="siteChecked && !isBrowserSSO && !credForm.valid"
|
<ion-button expand="block" type="submit" [disabled]="siteChecked && !isBrowserSSO && !credForm.valid"
|
||||||
class="ion-margin core-login-login-button">
|
class="ion-margin core-login-login-button ion-text-wrap">
|
||||||
{{ 'core.login.loginbutton' | translate }}
|
{{ 'core.login.loginbutton' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
|
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
|
||||||
|
|
|
@ -51,20 +51,15 @@
|
||||||
</ion-input>
|
</ion-input>
|
||||||
</core-show-password>
|
</core-show-password>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-grid class="ion-padding">
|
<div class="adaptable-buttons-row">
|
||||||
<ion-row>
|
<ion-button expand="block" fill="outline" (click)="cancel($event)" class="ion-margin ion-text-wrap">
|
||||||
<ion-col>
|
{{ 'core.login.cancel' | translate }}
|
||||||
<ion-button expand="block" fill="outline" (click)="cancel($event)">
|
</ion-button>
|
||||||
{{ 'core.login.cancel' | translate }}
|
<ion-button type="submit" expand="block" [disabled]="!credForm.valid"
|
||||||
</ion-button>
|
class="ion-margin core-login-login-button ion-text-wrap">
|
||||||
</ion-col>
|
{{ 'core.login.loginbutton' | translate }}
|
||||||
<ion-col>
|
</ion-button>
|
||||||
<ion-button type="submit" expand="block" [disabled]="!credForm.valid">
|
</div>
|
||||||
{{ 'core.login.loginbutton' | translate }}
|
|
||||||
</ion-button>
|
|
||||||
</ion-col>
|
|
||||||
</ion-row>
|
|
||||||
</ion-grid>
|
|
||||||
|
|
||||||
<ng-container *ngIf="showScanQR">
|
<ng-container *ngIf="showScanQR">
|
||||||
<div class="ion-text-center ion-padding">{{ 'core.login.or' | translate }}</div>
|
<div class="ion-text-center ion-padding">{{ 'core.login.or' | translate }}</div>
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
<core-block-side-blocks-button *ngIf="hasBlocks" [courseId]="siteHomeId">
|
<core-block-side-blocks-button slot="fixed" *ngIf="hasBlocks" [courseId]="siteHomeId">
|
||||||
</core-block-side-blocks-button>
|
</core-block-side-blocks-button>
|
||||||
|
|
||||||
<core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate">
|
<core-empty-box *ngIf="!hasContent" icon="fas-box-open" [message]="'core.course.nocontentavailable' | translate">
|
||||||
|
|
|
@ -336,12 +336,22 @@ export class CoreDomUtilsProvider {
|
||||||
/**
|
/**
|
||||||
* Focus an element and open keyboard.
|
* Focus an element and open keyboard.
|
||||||
*
|
*
|
||||||
* @param focusElement HTML element to focus.
|
* @param element HTML element to focus.
|
||||||
*/
|
*/
|
||||||
async focusElement(focusElement: HTMLElement): Promise<void> {
|
async focusElement(
|
||||||
|
element: HTMLIonInputElement | HTMLIonTextareaElement | HTMLIonSearchbarElement | HTMLElement,
|
||||||
|
): Promise<void> {
|
||||||
let retries = 10;
|
let retries = 10;
|
||||||
|
|
||||||
if (!focusElement.focus) {
|
let focusElement = element;
|
||||||
|
|
||||||
|
if ('getInputElement' in focusElement) {
|
||||||
|
// If it's an Ionic element get the right input to use.
|
||||||
|
focusElement.componentOnReady && await focusElement.componentOnReady();
|
||||||
|
focusElement = await focusElement.getInputElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!focusElement || !focusElement.focus) {
|
||||||
throw new CoreError('Element to focus cannot be focused');
|
throw new CoreError('Element to focus cannot be focused');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,38 @@ export class CoreDom {
|
||||||
return CoreDom.scrollToElement(container, '.core-input-error');
|
return CoreDom.scrollToElement(container, '.core-input-error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move element to content so it can be slotted.
|
||||||
|
*
|
||||||
|
* @param element HTML Element.
|
||||||
|
* @param slot Slot name.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
static slotOnContent(element: HTMLElement, slot = 'fixed'): CoreCancellablePromise<void> {
|
||||||
|
element.setAttribute('slot', slot);
|
||||||
|
if (element.parentElement?.nodeName === 'ION-CONTENT') {
|
||||||
|
return CoreCancellablePromise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const domPromise = CoreDom.waitToBeInDOM(element);
|
||||||
|
|
||||||
|
return new CoreCancellablePromise<void>(
|
||||||
|
async (resolve) => {
|
||||||
|
await domPromise;
|
||||||
|
|
||||||
|
// Move element to the nearest ion-content if it's not the parent
|
||||||
|
if (element.parentElement?.nodeName !== 'ION-CONTENT') {
|
||||||
|
element.closest('ion-content')?.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
domPromise.cancel();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait an element to be added to the root DOM.
|
* Wait an element to be added to the root DOM.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue