commit
89123e0df7
|
@ -10,8 +10,5 @@ module.exports = {
|
||||||
'!src/testing/**/*',
|
'!src/testing/**/*',
|
||||||
],
|
],
|
||||||
transformIgnorePatterns: ['node_modules/(?!@stencil|@angular|@ionic|@moodlehq|@ngx-translate|swiper)'],
|
transformIgnorePatterns: ['node_modules/(?!@stencil|@angular|@ionic|@moodlehq|@ngx-translate|swiper)'],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
|
||||||
...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/src/' }),
|
|
||||||
'^!raw-loader!.*': 'jest-raw-loader',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -141,7 +141,6 @@
|
||||||
"gulp-slash": "^1.1.3",
|
"gulp-slash": "^1.1.3",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-preset-angular": "^13.1.4",
|
"jest-preset-angular": "^13.1.4",
|
||||||
"jest-raw-loader": "^1.0.1",
|
|
||||||
"jsonc-parser": "^2.3.1",
|
"jsonc-parser": "^2.3.1",
|
||||||
"keytar": "^7.2.0",
|
"keytar": "^7.2.0",
|
||||||
"minimatch": "^5.1.0",
|
"minimatch": "^5.1.0",
|
||||||
|
@ -18822,12 +18821,6 @@
|
||||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/jest-raw-loader": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/jest-raw-loader/-/jest-raw-loader-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-g9oaAjeC4/rIJk1Wd3RxVbOfMizowM7LSjEJqa4R9qDX0OjQNABXOhH+GaznUp+DjTGVPi2vPPbQXyX87DOnYg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/jest-regex-util": {
|
"node_modules/jest-regex-util": {
|
||||||
"version": "29.6.3",
|
"version": "29.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||||
|
|
|
@ -176,7 +176,6 @@
|
||||||
"gulp-slash": "^1.1.3",
|
"gulp-slash": "^1.1.3",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-preset-angular": "^13.1.4",
|
"jest-preset-angular": "^13.1.4",
|
||||||
"jest-raw-loader": "^1.0.1",
|
|
||||||
"jsonc-parser": "^2.3.1",
|
"jsonc-parser": "^2.3.1",
|
||||||
"keytar": "^7.2.0",
|
"keytar": "^7.2.0",
|
||||||
"minimatch": "^5.1.0",
|
"minimatch": "^5.1.0",
|
||||||
|
|
|
@ -29,8 +29,14 @@
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
<p class="item-heading" [core-mark-required]="true">{{ 'core.date' | translate }}</p>
|
<p class="item-heading" [core-mark-required]="true">{{ 'core.date' | translate }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-datetime formControlName="timestart" [placeholder]="'core.date' | translate" [displayFormat]="dateFormat"
|
<ion-datetime-button datetime="timestart" />
|
||||||
[max]="maxDate" [min]="minDate" [displayTimezone]="displayTimezone" />
|
<ion-modal [keepContentsMounted]="true">
|
||||||
|
<ng-template>
|
||||||
|
<ion-datetime id="timestart" formControlName="timestart" presentation="date-time" [max]="maxDate" [min]="minDate">
|
||||||
|
<span slot="title">{{'core.date' | translate}}</span>
|
||||||
|
</ion-datetime>
|
||||||
|
</ng-template>
|
||||||
|
</ion-modal>
|
||||||
<core-input-errors [control]="form.controls.timestart" [errorMessages]="errors" />
|
<core-input-errors [control]="form.controls.timestart" [errorMessages]="errors" />
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
|
@ -154,9 +160,15 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="form.controls.duration.value === 1">
|
<ion-item *ngIf="form.controls.duration.value === 1">
|
||||||
<ion-label position="stacked" />
|
<ion-label position="stacked" />
|
||||||
<ion-datetime formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
|
<ion-datetime-button datetime="timedurationuntil" />
|
||||||
[placeholder]="'addon.calendar.durationuntil' | translate" [displayFormat]="dateFormat"
|
<ion-modal [keepContentsMounted]="true">
|
||||||
[displayTimezone]="displayTimezone" />
|
<ng-template>
|
||||||
|
<ion-datetime id="timedurationuntil" formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
|
||||||
|
presentation="date-time">
|
||||||
|
<span slot="title">{{'addon.calendar.durationuntil' | translate}}</span>
|
||||||
|
</ion-datetime>
|
||||||
|
</ng-template>
|
||||||
|
</ion-modal>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
|
|
@ -45,7 +45,6 @@ import { CoreForms } from '@singletons/form';
|
||||||
import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@features/reminders/services/reminders';
|
import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@features/reminders/services/reminders';
|
||||||
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
|
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { CoreAppProvider } from '@services/app';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a form to create/edit an event.
|
* Page that displays a form to create/edit an event.
|
||||||
|
@ -61,7 +60,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
@ViewChild('editEventForm') formElement!: ElementRef;
|
@ViewChild('editEventForm') formElement!: ElementRef;
|
||||||
|
|
||||||
title = 'addon.calendar.newevent';
|
title = 'addon.calendar.newevent';
|
||||||
dateFormat: string;
|
|
||||||
component = AddonCalendarProvider.COMPONENT;
|
component = AddonCalendarProvider.COMPONENT;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
hasOffline = false;
|
hasOffline = false;
|
||||||
|
@ -78,7 +76,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
eventId?: number;
|
eventId?: number;
|
||||||
maxDate: string;
|
maxDate: string;
|
||||||
minDate: string;
|
minDate: string;
|
||||||
displayTimezone?: string;
|
|
||||||
|
|
||||||
// Form variables.
|
// Form variables.
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
|
@ -107,11 +104,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
||||||
required: Translate.instant('core.required'),
|
required: Translate.instant('core.required'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them.
|
|
||||||
this.dateFormat = CoreTimeUtils.convertPHPToMoment(Translate.instant('core.strftimedatetimeshort'))
|
|
||||||
.replace(/[[\]]/g, '');
|
|
||||||
this.displayTimezone = CoreAppProvider.getForcedTimezone();
|
|
||||||
|
|
||||||
this.form = new FormGroup({});
|
this.form = new FormGroup({});
|
||||||
|
|
||||||
// Initialize form variables.
|
// Initialize form variables.
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
<span *ngIf="inputMode && form" [formGroup]="form">
|
<span *ngIf="inputMode && form && searchFields" [formGroup]="form">
|
||||||
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
|
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
|
||||||
<ion-datetime [formControlName]="'f_'+field.id" [placeholder]="'core.date' | translate" [max]="maxDate" [min]="minDate"
|
|
||||||
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format" [displayTimezone]="displayTimezone" />
|
<ion-datetime-button datetime="datetime" />
|
||||||
|
<ion-modal [keepContentsMounted]="true">
|
||||||
|
<ng-template>
|
||||||
|
<ion-datetime id="datetime" [formControlName]="'f_'+field.id" [max]="maxDate" [min]="minDate"
|
||||||
|
[disabled]="searchMode && !searchFields['f_'+field.id+'_z']" presentation="date" />
|
||||||
|
</ng-template>
|
||||||
|
</ion-modal>
|
||||||
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
|
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
|
||||||
|
|
||||||
<ion-item *ngIf="searchMode">
|
<ion-item *ngIf="searchMode">
|
||||||
<ion-label>{{ 'addon.mod_data.usedate' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_data.usedate' | translate }}</ion-label>
|
||||||
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_z'" [(ngModel)]="searchFields!['f_'+field.id+'_z']" />
|
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_z'" [(ngModel)]="searchFields['f_'+field.id+'_z']" />
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { CoreAppProvider } from '@services/app';
|
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { Translate } from '@singletons';
|
|
||||||
import moment, { Moment } from 'moment-timezone';
|
import moment, { Moment } from 'moment-timezone';
|
||||||
import { AddonModDataFieldPluginBaseComponent } from '../../../classes/base-field-plugin-component';
|
import { AddonModDataFieldPluginBaseComponent } from '../../../classes/base-field-plugin-component';
|
||||||
|
|
||||||
|
@ -28,11 +26,9 @@ import { AddonModDataFieldPluginBaseComponent } from '../../../classes/base-fiel
|
||||||
})
|
})
|
||||||
export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginBaseComponent {
|
export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginBaseComponent {
|
||||||
|
|
||||||
format!: string;
|
|
||||||
displayDate?: number;
|
displayDate?: number;
|
||||||
maxDate?: string;
|
maxDate?: string;
|
||||||
minDate?: string;
|
minDate?: string;
|
||||||
displayTimezone?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -48,23 +44,18 @@ export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginBaseC
|
||||||
|
|
||||||
let momentInstance: Moment;
|
let momentInstance: Moment;
|
||||||
|
|
||||||
// Calculate format to use.
|
|
||||||
this.format = CoreTimeUtils.fixFormatForDatetime(CoreTimeUtils.convertPHPToMoment(
|
|
||||||
Translate.instant('core.strftimedate'),
|
|
||||||
));
|
|
||||||
this.maxDate = CoreTimeUtils.getDatetimeDefaultMax();
|
this.maxDate = CoreTimeUtils.getDatetimeDefaultMax();
|
||||||
this.minDate = CoreTimeUtils.getDatetimeDefaultMin();
|
this.minDate = CoreTimeUtils.getDatetimeDefaultMin();
|
||||||
this.displayTimezone = CoreAppProvider.getForcedTimezone();
|
|
||||||
|
|
||||||
if (this.searchMode) {
|
if (this.searchMode && this.searchFields) {
|
||||||
this.addControl('f_' + this.field.id + '_z');
|
this.addControl('f_' + this.field.id + '_z');
|
||||||
|
|
||||||
momentInstance = this.searchFields!['f_' + this.field.id + '_y']
|
momentInstance = this.searchFields['f_' + this.field.id + '_y']
|
||||||
? moment(this.searchFields!['f_' + this.field.id + '_y'] + '-' +
|
? moment(this.searchFields['f_' + this.field.id + '_y'] + '-' +
|
||||||
this.searchFields!['f_' + this.field.id + '_m'] + '-' + this.searchFields!['f_' + this.field.id + '_d'])
|
this.searchFields['f_' + this.field.id + '_m'] + '-' + this.searchFields['f_' + this.field.id + '_d'])
|
||||||
: moment();
|
: moment();
|
||||||
|
|
||||||
this.searchFields!['f_' + this.field.id] = CoreTimeUtils.toDatetimeFormat(momentInstance.unix() * 1000);
|
this.searchFields['f_' + this.field.id] = CoreTimeUtils.toDatetimeFormat(momentInstance.unix() * 1000);
|
||||||
} else {
|
} else {
|
||||||
momentInstance = this.value?.content
|
momentInstance = this.value?.content
|
||||||
? moment(parseInt(this.value.content, 10) * 1000)
|
? moment(parseInt(this.value.content, 10) * 1000)
|
||||||
|
|
|
@ -18,7 +18,12 @@
|
||||||
[courseId]="courseId" [wsNotFiltered]="true" />
|
[courseId]="courseId" [wsNotFiltered]="true" />
|
||||||
</span>
|
</span>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-datetime [formControlName]="modelName" [placeholder]="'core.choosedots' | translate" [displayFormat]="format" [max]="max"
|
|
||||||
[min]="min" [monthNames]="monthNames" [displayTimezone]="displayTimezone" />
|
<ion-datetime-button datetime="datetime" />
|
||||||
|
<ion-modal [keepContentsMounted]="true">
|
||||||
|
<ng-template>
|
||||||
|
<ion-datetime id="datetime" [formControlName]="modelName" [presentation]="ionDateTimePresentation" [max]="max" [min]="min" />
|
||||||
|
</ng-template>
|
||||||
|
</ion-modal>
|
||||||
<core-input-errors [control]="form.controls[modelName]" />
|
<core-input-errors [control]="form.controls[modelName]" />
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -19,10 +19,7 @@ import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { AuthEmailSignupProfileField } from '@features/login/services/login-helper';
|
import { AuthEmailSignupProfileField } from '@features/login/services/login-helper';
|
||||||
import { CoreUserProfileField } from '@features/user/services/user';
|
import { CoreUserProfileField } from '@features/user/services/user';
|
||||||
import { Translate } from '@singletons';
|
|
||||||
import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component';
|
import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-profilefield-component';
|
||||||
import { CoreLang } from '@services/lang';
|
|
||||||
import { CoreAppProvider } from '@services/app';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to render a datetime user profile field.
|
* Directive to render a datetime user profile field.
|
||||||
|
@ -33,13 +30,11 @@ import { CoreAppProvider } from '@services/app';
|
||||||
})
|
})
|
||||||
export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent {
|
export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent {
|
||||||
|
|
||||||
format?: string;
|
ionDateTimePresentation = 'date';
|
||||||
min?: string;
|
min?: string;
|
||||||
max?: string;
|
max?: string;
|
||||||
valueNumber?: number;
|
valueNumber?: number;
|
||||||
displayValue?: string;
|
displayValue?: string;
|
||||||
monthNames?: string[];
|
|
||||||
displayTimezone?: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the data when the field is meant to be displayed without editing.
|
* Init the data when the field is meant to be displayed without editing.
|
||||||
|
@ -64,16 +59,11 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField
|
||||||
protected initForEdit(field: AuthEmailSignupProfileField): void {
|
protected initForEdit(field: AuthEmailSignupProfileField): void {
|
||||||
super.initForEdit(field);
|
super.initForEdit(field);
|
||||||
|
|
||||||
this.monthNames = CoreLang.getMonthNames();
|
|
||||||
this.displayTimezone = CoreAppProvider.getForcedTimezone();
|
|
||||||
|
|
||||||
// Check if it's only date or it has time too.
|
// Check if it's only date or it has time too.
|
||||||
const hasTime = CoreUtils.isTrueOrOne(field.param3);
|
const hasTime = CoreUtils.isTrueOrOne(field.param3);
|
||||||
|
|
||||||
// Calculate format to use.
|
// Calculate format to use.
|
||||||
this.format = CoreTimeUtils.fixFormatForDatetime(CoreTimeUtils.convertPHPToMoment(
|
this.ionDateTimePresentation = hasTime ? 'date-time' : 'date';
|
||||||
Translate.instant('core.' + (hasTime ? 'strftimedatetime' : 'strftimedate')),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Check min value.
|
// Check min value.
|
||||||
if (field.param1 && Number(field.param1)) {
|
if (field.param1 && Number(field.param1)) {
|
||||||
|
|
|
@ -52,6 +52,8 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
||||||
IonicModule.forRoot(
|
IonicModule.forRoot(
|
||||||
{
|
{
|
||||||
navAnimation: moodleTransitionAnimation,
|
navAnimation: moodleTransitionAnimation,
|
||||||
|
innerHTMLTemplatesEnabled: true,
|
||||||
|
sanitizerEnabled: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
HttpClientModule, // HttpClient is used to make JSON requests. It fails for HEAD requests because there is no content.
|
HttpClientModule, // HttpClient is used to make JSON requests. It fails for HEAD requests because there is no content.
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
import { Component, ElementRef, Input, OnChanges, OnInit } from '@angular/core';
|
import { Component, ElementRef, Input, OnChanges, OnInit } from '@angular/core';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CoreForms } from '@singletons/form';
|
import { CoreForms } from '@singletons/form';
|
||||||
import ChevronUpSVG from '!raw-loader!ionicons/dist/svg/chevron-up.svg';
|
|
||||||
import ChevronDownSVG from '!raw-loader!ionicons/dist/svg/chevron-down.svg';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to show error details.
|
* Component to show error details.
|
||||||
|
@ -54,11 +52,11 @@ export class CoreErrorInfoComponent implements OnInit, OnChanges {
|
||||||
<label for="${toggleId}" class="core-error-info--toggle" aria-hidden="true">
|
<label for="${toggleId}" class="core-error-info--toggle" aria-hidden="true">
|
||||||
<span class="core-error-info--hide-content">
|
<span class="core-error-info--hide-content">
|
||||||
${hideDetailsLabel}
|
${hideDetailsLabel}
|
||||||
${ChevronUpSVG}
|
<ion-icon name="chevron-up" />
|
||||||
</span>
|
</span>
|
||||||
<span class="core-error-info--show-content">
|
<span class="core-error-info--show-content">
|
||||||
${showDetailsLabel}
|
${showDetailsLabel}
|
||||||
${ChevronDownSVG}
|
<ion-icon name="chevron-down" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
:host {
|
:host {
|
||||||
|
display: contents;
|
||||||
|
.core-input-error-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.core-input-error-container {
|
|
||||||
.core-input-error {
|
.core-input-error {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
--backdrop-opacity: var(--ion-backdrop-opacity, 0.4);
|
--backdrop-opacity: var(--ion-backdrop-opacity, 0.4);
|
||||||
|
--border-radius: var(--modal-radius);
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.sheet-modal--wrapper {
|
.sheet-modal--wrapper {
|
||||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||||
@include padding(24px, 16px, 24px, 16px);
|
@include padding(24px, 16px, 24px, 16px);
|
||||||
|
|
||||||
background-color: var(--ion-overlay-background-color, var(--ion-background-color, #fff));
|
background-color: var(--ion-overlay-background-color, var(--ion-background-color, #fff));
|
||||||
|
|
|
@ -92,7 +92,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngDoCheck(): void {
|
ngDoCheck(): void {
|
||||||
if (!this.componentInstance || this.creatingComponent) {
|
if (!this.componentInstance || this.creatingComponent) {
|
||||||
|
@ -111,7 +111,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect changes on input properties.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngOnChanges(changes: Record<string, SimpleChange>): Promise<void> {
|
async ngOnChanges(changes: Record<string, SimpleChange>): Promise<void> {
|
||||||
// Only compile if text/javascript has changed or the forceCompile flag has been set to true.
|
// Only compile if text/javascript has changed or the forceCompile flag has been set to true.
|
||||||
|
@ -148,7 +148,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component destroyed.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.componentRef?.destroy();
|
this.componentRef?.destroy();
|
||||||
|
|
|
@ -81,5 +81,9 @@ ion-item.item {
|
||||||
&.restricted {
|
&.restricted {
|
||||||
font-size: var(--text-size);
|
font-size: var(--text-size);
|
||||||
}
|
}
|
||||||
|
&[role=button] {
|
||||||
|
min-height: auto;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -459,8 +459,8 @@ export class CoreLoginSitePage implements OnInit {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errorDetails) {
|
if (errorDetails) {
|
||||||
|
// Avoid sanitizing JS.
|
||||||
const containerElement = alertElement.querySelector('.core-error-info-container');
|
const containerElement = alertElement.querySelector('.core-error-info-container');
|
||||||
|
|
||||||
if (containerElement) {
|
if (containerElement) {
|
||||||
containerElement.innerHTML = CoreErrorInfoComponent.render(errorDetails, errorCode);
|
containerElement.innerHTML = CoreErrorInfoComponent.render(errorDetails, errorCode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { CorePlatform } from '@services/platform';
|
||||||
import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang';
|
import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/handlers/multilang';
|
||||||
import { AddonFilterMultilang2Handler } from '@addons/filter/multilang2/services/handlers/multilang2';
|
import { AddonFilterMultilang2Handler } from '@addons/filter/multilang2/services/handlers/multilang2';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CoreLogger } from '@singletons/logger';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Service to handle language features, like changing the current language.
|
* Service to handle language features, like changing the current language.
|
||||||
|
@ -40,6 +41,11 @@ export class CoreLangProvider {
|
||||||
protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
|
protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
|
||||||
protected customStringsRaw?: string;
|
protected customStringsRaw?: string;
|
||||||
protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
|
protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
|
||||||
|
protected logger: CoreLogger;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.logger = CoreLogger.getInstance('CoreLang');
|
||||||
|
}
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
// Set fallback language and language to use until the app determines the right language to use.
|
// Set fallback language and language to use until the app determines the right language to use.
|
||||||
|
@ -171,13 +177,21 @@ export class CoreLangProvider {
|
||||||
// Use british english when parent english is loaded.
|
// Use british english when parent english is loaded.
|
||||||
moment.locale(language == 'en' ? 'en-gb' : language);
|
moment.locale(language == 'en' ? 'en-gb' : language);
|
||||||
|
|
||||||
|
const previousLanguage = this.currentLanguage ?? this.getDefaultLanguage();
|
||||||
|
|
||||||
this.currentLanguage = language;
|
this.currentLanguage = language;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await this.reloadLanguageStrings();
|
||||||
this.reloadLanguageStrings(),
|
await CoreConfig.set('current_language', language);
|
||||||
CoreConfig.set('current_language', language),
|
} catch (error) {
|
||||||
]);
|
if (language !== previousLanguage) {
|
||||||
|
this.logger.error(`Language ${language} not available, reverting to ${previousLanguage}`, error);
|
||||||
|
|
||||||
|
return this.changeCurrentLanguage(previousLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
// Load the custom and site plugins strings for the language.
|
// Load the custom and site plugins strings for the language.
|
||||||
if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) {
|
if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) {
|
||||||
|
|
|
@ -815,14 +815,20 @@ export class CoreDomUtilsProvider {
|
||||||
* @returns Promise resolved with the alert modal.
|
* @returns Promise resolved with the alert modal.
|
||||||
*/
|
*/
|
||||||
async showAlertWithOptions(options: AlertOptions = {}, autocloseTime?: number): Promise<HTMLIonAlertElement> {
|
async showAlertWithOptions(options: AlertOptions = {}, autocloseTime?: number): Promise<HTMLIonAlertElement> {
|
||||||
const hasHTMLTags = CoreTextUtils.hasHTMLTags(<string> options.message || '');
|
let message = typeof options.message == 'string'
|
||||||
|
? options.message
|
||||||
|
: options.message?.value || '';
|
||||||
|
|
||||||
|
const hasHTMLTags = CoreTextUtils.hasHTMLTags(message);
|
||||||
|
|
||||||
if (hasHTMLTags && !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.7')) {
|
if (hasHTMLTags && !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.7')) {
|
||||||
// Treat multilang.
|
// Treat multilang.
|
||||||
options.message = await CoreLang.filterMultilang(<string> options.message);
|
message = await CoreLang.filterMultilang(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alertId = <string> Md5.hashAsciiStr((options.header || '') + '#' + (options.message || ''));
|
options.message = message;
|
||||||
|
|
||||||
|
const alertId = Md5.hashAsciiStr((options.header || '') + '#' + (message|| ''));
|
||||||
|
|
||||||
if (this.displayedAlerts[alertId]) {
|
if (this.displayedAlerts[alertId]) {
|
||||||
// There's already an alert with the same message and title. Return it.
|
// There's already an alert with the same message and title. Return it.
|
||||||
|
|
|
@ -117,7 +117,6 @@ export interface AsyncInstanceWrapper<
|
||||||
resetInstance(): void;
|
resetInstance(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
||||||
export type AsyncObject = object;
|
export type AsyncObject = object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -518,7 +518,7 @@ ion-alert .alert-button.timed-button{
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-alert {
|
ion-alert {
|
||||||
--border-radius: var(--radius-xl);
|
--border-radius: var(--modal-radius);
|
||||||
|
|
||||||
&.md, &.ios {
|
&.md, &.ios {
|
||||||
--max-width: 80%;
|
--max-width: 80%;
|
||||||
|
@ -545,7 +545,7 @@ ion-alert {
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-loading {
|
ion-loading {
|
||||||
--border-radius: var(--radius-xl);
|
--border-radius: var(--modal-radius);
|
||||||
|
|
||||||
.loading-wrapper {
|
.loading-wrapper {
|
||||||
border-radius: var(--border-radius) !important;
|
border-radius: var(--border-radius) !important;
|
||||||
|
@ -1585,37 +1585,6 @@ ion-item.item-input.item-multiple-inputs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus highlight for accessibility.
|
|
||||||
.ion-focused:not(.item-multiple-inputs):not(:focus),
|
|
||||||
ion-input.has-focus,
|
|
||||||
ion-card:focus {
|
|
||||||
@include core-focus();
|
|
||||||
}
|
|
||||||
.ion-focused.item-multiple-inputs,
|
|
||||||
.ion-focused.ion-activatable {
|
|
||||||
ion-toggle:focus-within,
|
|
||||||
ion-select:focus-within,
|
|
||||||
ion-checkbox:focus-within,
|
|
||||||
ion-radio:focus-within {
|
|
||||||
@include core-focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Treat cases where there's a focusable element inside an item, like a button.
|
|
||||||
ion-item.item-input:not(.item-multiple-inputs):not(:focus),
|
|
||||||
ion-item.item-has-focus:not(.item-multiple-inputs):not(:focus),
|
|
||||||
ion-item.item-input ion-input.has-focus {
|
|
||||||
position: relative;
|
|
||||||
&::after {
|
|
||||||
box-shadow: revert;
|
|
||||||
opacity: revert;
|
|
||||||
z-index: revert;
|
|
||||||
}
|
|
||||||
.item-highlight, .item-inner-highlight {
|
|
||||||
position: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-item-divider.item,
|
ion-item-divider.item,
|
||||||
ion-item.item.divider {
|
ion-item.item.divider {
|
||||||
min-height: var(--min-height);
|
min-height: var(--min-height);
|
||||||
|
@ -1653,6 +1622,42 @@ ion-item.item {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Focus highlight for accessibility.
|
||||||
|
.ion-focused:not(.item-multiple-inputs):not(:focus),
|
||||||
|
ion-input.has-focus,
|
||||||
|
ion-card:focus {
|
||||||
|
@include core-focus();
|
||||||
|
|
||||||
|
:focus-visible,
|
||||||
|
.clickable:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ion-focused.item-multiple-inputs {
|
||||||
|
ion-toggle:focus-within,
|
||||||
|
ion-select:focus-within,
|
||||||
|
ion-checkbox:focus-within,
|
||||||
|
ion-radio:focus-within {
|
||||||
|
@include core-focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat cases where there's a focusable element inside an item, like a button.
|
||||||
|
ion-item.item-input:not(.item-multiple-inputs):not(:focus),
|
||||||
|
ion-item.item-has-focus:not(.item-multiple-inputs):not(:focus),
|
||||||
|
ion-item.item-input ion-input.has-focus {
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
box-shadow: revert;
|
||||||
|
opacity: revert;
|
||||||
|
z-index: revert;
|
||||||
|
}
|
||||||
|
.item-highlight, .item-inner-highlight {
|
||||||
|
position: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textarea, button, select, input, a, .clickable {
|
textarea, button, select, input, a, .clickable {
|
||||||
&:focus {
|
&:focus {
|
||||||
@include core-focus-style();
|
@include core-focus-style();
|
||||||
|
@ -1660,10 +1665,6 @@ textarea, button, select, input, a, .clickable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ion-focused:not(.item-multiple-inputs):not(:focus) .clickable:focus {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-loading:focus-visible,
|
ion-loading:focus-visible,
|
||||||
ion-alert:focus-visible,
|
ion-alert:focus-visible,
|
||||||
ion-popover:focus-visible,
|
ion-popover:focus-visible,
|
||||||
|
@ -1672,11 +1673,6 @@ ion-modal:focus-visible {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
|
||||||
--placeholder-color: var(--ion-placeholder-color);
|
|
||||||
--placeholder-opacity: .85;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-input .native-input {
|
ion-input .native-input {
|
||||||
&:focus, &:focus-visible {
|
&:focus, &:focus-visible {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
@ -1684,6 +1680,11 @@ ion-input .native-input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
--placeholder-color: var(--ion-placeholder-color);
|
||||||
|
--placeholder-opacity: .85;
|
||||||
|
}
|
||||||
|
|
||||||
// Disable scroll on parent ion contents to enabled PTR on the ones inside the splitview. See split-view component for more info.
|
// Disable scroll on parent ion contents to enabled PTR on the ones inside the splitview. See split-view component for more info.
|
||||||
ion-content.disable-scroll-y::part(scroll) {
|
ion-content.disable-scroll-y::part(scroll) {
|
||||||
touch-action: auto;
|
touch-action: auto;
|
||||||
|
@ -1916,6 +1917,9 @@ ion-modal {
|
||||||
--border-radius: var(--modal-radius);
|
--border-radius: var(--modal-radius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.ion-datetime-button-overlay {
|
||||||
|
--border-radius: var(--modal-radius);
|
||||||
|
}
|
||||||
|
|
||||||
&.core-modal-lateral,
|
&.core-modal-lateral,
|
||||||
&.core-modal-fullscreen {
|
&.core-modal-fullscreen {
|
||||||
|
@ -1973,3 +1977,10 @@ ion-popover {
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ion Datetime
|
||||||
|
ion-item.item-label-stacked ion-datetime-button {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
align-self: self-end;
|
||||||
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
// (C) Copyright 2015 Moodle Pty Ltd.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import { UrlTree } from '@angular/router';
|
|
||||||
import { NavigationOptions } from '@ionic/angular/common/providers/nav-controller';
|
|
||||||
|
|
||||||
declare module '@ionic/angular' {
|
|
||||||
|
|
||||||
export class NavController {
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
navigateForward(url: string | UrlTree | any[], options?: NavigationOptions): Promise<boolean | null>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// (C) Copyright 2015 Moodle Pty Ltd.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
declare module '!raw-loader!*' {
|
|
||||||
const contents: string;
|
|
||||||
|
|
||||||
export = contents;
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ For more information about upgrading, read the official documentation: https://m
|
||||||
- Renamed CoreLoginSitesComponent to CoreLoginSitesModalComponent to make it clear that it's a modal and to avoid confusing it with the new CoreSitesListComponent.
|
- Renamed CoreLoginSitesComponent to CoreLoginSitesModalComponent to make it clear that it's a modal and to avoid confusing it with the new CoreSitesListComponent.
|
||||||
- Removed CoreToLocaleStringPipe deprecated since 3.6.0
|
- Removed CoreToLocaleStringPipe deprecated since 3.6.0
|
||||||
- With the upgrade to Ionic 7 ion-slides is no longer supported and now you need to use swiper-container and swiper-slide. More info here: https://ionicframework.com/docs/angular/slides
|
- With the upgrade to Ionic 7 ion-slides is no longer supported and now you need to use swiper-container and swiper-slide. More info here: https://ionicframework.com/docs/angular/slides
|
||||||
|
- With the upgrade to Ionic7 ion-datetime has changed its usage. We recommend using ion-datetime-button. More info here: https://ionicframework.com/docs/updating/6-0#datetime
|
||||||
|
|
||||||
=== 4.3.0 ===
|
=== 4.3.0 ===
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue