commit
9f0de18eaa
|
@ -5,7 +5,7 @@
|
||||||
</core-horizontal-scroll-controls>
|
</core-horizontal-scroll-controls>
|
||||||
</div>
|
</div>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<core-loading [hideUntil]="loaded" [fullscreen]="false" class="safe-area-page margin">
|
<core-loading [hideUntil]="loaded" [fullscreen]="false" class="safe-area-page">
|
||||||
<div
|
<div
|
||||||
[id]="scrollElementId"
|
[id]="scrollElementId"
|
||||||
[hidden]="!items || items.length === 0"
|
[hidden]="!items || items.length === 0"
|
||||||
|
|
|
@ -8,4 +8,7 @@
|
||||||
.core-course-module-handler {
|
.core-course-module-handler {
|
||||||
--inner-border-width: 0;
|
--inner-border-width: 0;
|
||||||
}
|
}
|
||||||
|
core-loading {
|
||||||
|
--loading-inline-min-height: 102px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="fas-calendar" inline="true"
|
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="fas-calendar"
|
||||||
[message]="'addon.calendar.noevents' | translate">
|
[message]="'addon.calendar.noevents' | translate">
|
||||||
</core-empty-box>
|
</core-empty-box>
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
<core-user-avatar slot="start" [user]="members[message.useridfrom]" [linkProfile]="false"
|
<core-user-avatar slot="start" [user]="members[message.useridfrom]" [linkProfile]="false"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
</core-user-avatar>
|
</core-user-avatar>
|
||||||
{{ members[message.useridfrom].fullname }}
|
<div>{{ members[message.useridfrom].fullname }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!message.showUserData" class="sr-only">
|
<div *ngIf="!message.showUserData" class="sr-only">
|
||||||
{{ message.useridfrom == currentUserId
|
{{ message.useridfrom == currentUserId
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
<h2>{{ user!.fullname }}</h2>
|
<h2>{{ user!.fullname }}</h2>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatusBadges"></ng-container>
|
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Status of the submission if user is blinded. -->
|
<!-- Status of the submission if user is blinded. -->
|
||||||
|
@ -17,7 +16,6 @@
|
||||||
<h2>{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}</h2>
|
<h2>{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}</h2>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatusBadges"></ng-container>
|
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Status of the submission in the rest of cases. -->
|
<!-- Status of the submission in the rest of cases. -->
|
||||||
|
@ -26,7 +24,6 @@
|
||||||
<h2>{{ 'addon.mod_assign.submissionstatus' | translate }}</h2>
|
<h2>{{ 'addon.mod_assign.submissionstatus' | translate }}</h2>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ng-container *ngTemplateOutlet="submissionStatusBadges"></ng-container>
|
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<!-- Tabs: see the submission or grade it. -->
|
<!-- Tabs: see the submission or grade it. -->
|
||||||
|
@ -385,12 +382,12 @@
|
||||||
{{ 'addon.mod_assign.defaultteam' | translate }}
|
{{ 'addon.mod_assign.defaultteam' | translate }}
|
||||||
</p>
|
</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
<p>
|
||||||
<ng-template #submissionStatusBadges>
|
<ion-badge *ngIf="statusTranslated" [color]="statusColor">
|
||||||
<ion-badge slot="end" *ngIf="statusTranslated" [color]="statusColor">
|
{{ statusTranslated }}
|
||||||
{{ statusTranslated }}
|
</ion-badge>
|
||||||
</ion-badge>
|
<ion-badge class="ion-margin-start" *ngIf="gradingStatusTranslationId" [color]="gradingColor">
|
||||||
<ion-badge slot="end" *ngIf="gradingStatusTranslationId" [color]="gradingColor">
|
{{ gradingStatusTranslationId | translate }}
|
||||||
{{ gradingStatusTranslationId | translate }}
|
</ion-badge>
|
||||||
</ion-badge>
|
</p>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
|
@ -43,10 +43,10 @@
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<ng-container *ngIf="chat">
|
<ng-container *ngIf="chat">
|
||||||
<ion-button class="ion-margin" expand="block" color="primary" (click)="enterChat()">
|
<ion-button class="ion-margin ion-text-wrap" expand="block" color="primary" (click)="enterChat()">
|
||||||
{{ 'addon.mod_chat.enterchat' | translate }}
|
{{ 'addon.mod_chat.enterchat' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
<ion-button class="ion-margin" expand="block" color="light" *ngIf="sessionsAvailable" (click)="viewSessions()">
|
<ion-button class="ion-margin ion-text-wrap" expand="block" color="light" *ngIf="sessionsAvailable" (click)="viewSessions()">
|
||||||
{{ 'addon.mod_chat.viewreport' | translate }}
|
{{ 'addon.mod_chat.viewreport' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
<core-context-menu-item *ngIf="blog"
|
<core-context-menu-item *ngIf="blog"
|
||||||
[priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'far-newspaper'" (action)="gotoBlog()">
|
[priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'far-newspaper'" (action)="gotoBlog()">
|
||||||
</core-context-menu-item>
|
</core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline"
|
<core-context-menu-item
|
||||||
|
*ngIf="discussions.onlineLoaded && discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline"
|
||||||
[priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" [iconAction]="refreshIcon" [closeOnClick]="false"
|
[priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" [iconAction]="refreshIcon" [closeOnClick]="false"
|
||||||
(action)="doRefresh(null, $event)">
|
(action)="doRefresh(null, $event)">
|
||||||
</core-context-menu-item>
|
</core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="discussions.loaded && (hasOffline || hasOfflineRatings) && isOnline"
|
<core-context-menu-item
|
||||||
|
*ngIf="discussions.onlineLoaded && discussions.loaded && (hasOffline || hasOfflineRatings) && isOnline"
|
||||||
[priority]="600" [content]="'core.settings.synchronizenow' | translate" [iconAction]="syncIcon" [closeOnClick]="false"
|
[priority]="600" [content]="'core.settings.synchronizenow' | translate" [iconAction]="syncIcon" [closeOnClick]="false"
|
||||||
(action)="doRefresh(null, $event, true)">
|
(action)="doRefresh(null, $event, true)">
|
||||||
</core-context-menu-item>
|
</core-context-menu-item>
|
||||||
|
@ -37,11 +39,11 @@
|
||||||
|
|
||||||
<!-- Content. -->
|
<!-- Content. -->
|
||||||
<core-split-view>
|
<core-split-view>
|
||||||
<ion-refresher slot="fixed" [disabled]="!discussions.loaded" (ionRefresh)="doRefresh($event.target)">
|
<ion-refresher slot="fixed" [disabled]="!discussions.onlineLoaded || !discussions.loaded" (ionRefresh)="doRefresh($event.target)">
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
|
|
||||||
<core-loading [hideUntil]="discussions.loaded">
|
<core-loading [hideUntil]="discussions.onlineLoaded && discussions.loaded">
|
||||||
<!-- Activity info. -->
|
<!-- Activity info. -->
|
||||||
<core-course-module-info *ngIf="showCompletion" [module]="module" [showManualCompletion]="true"
|
<core-course-module-info *ngIf="showCompletion" [module]="module" [showManualCompletion]="true"
|
||||||
(completionChanged)="onCompletionChange()">
|
(completionChanged)="onCompletionChange()">
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
[autoFocus]="true" [lengthCheck]="2" (onClear)="toggleSearch()" searchArea="AddonModGlossary-{{module.id}}">
|
[autoFocus]="true" [lengthCheck]="2" (onClear)="toggleSearch()" searchArea="AddonModGlossary-{{module.id}}">
|
||||||
</core-search-box>
|
</core-search-box>
|
||||||
|
|
||||||
<core-loading [hideUntil]="entries.loaded">
|
<core-loading [hideUntil]="loaded">
|
||||||
<!-- Activity info. -->
|
<!-- Activity info. -->
|
||||||
<core-course-module-info *ngIf="showCompletion" [module]="module" [showManualCompletion]="true"
|
<core-course-module-info *ngIf="showCompletion" [module]="module" [showManualCompletion]="true"
|
||||||
(completionChanged)="onCompletionChange()">
|
(completionChanged)="onCompletionChange()">
|
||||||
|
|
|
@ -47,12 +47,8 @@ export class AddonModGlossaryEditLinkHandlerService extends CoreContentLinksHand
|
||||||
const module = await CoreCourse.getModuleBasicInfo(cmId, siteId);
|
const module = await CoreCourse.getModuleBasicInfo(cmId, siteId);
|
||||||
|
|
||||||
await CoreNavigator.navigateToSitePath(
|
await CoreNavigator.navigateToSitePath(
|
||||||
AddonModGlossaryModuleHandlerService.PAGE_NAME + '/edit/0',
|
AddonModGlossaryModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/edit/0`,
|
||||||
{
|
{
|
||||||
params: {
|
|
||||||
cmId: module.id,
|
|
||||||
courseId: module.course,
|
|
||||||
},
|
|
||||||
siteId,
|
siteId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -52,12 +52,8 @@ export class AddonModGlossaryEntryLinkHandlerService extends CoreContentLinksHan
|
||||||
);
|
);
|
||||||
|
|
||||||
await CoreNavigator.navigateToSitePath(
|
await CoreNavigator.navigateToSitePath(
|
||||||
AddonModGlossaryModuleHandlerService.PAGE_NAME + `/entry/${entryId}`,
|
AddonModGlossaryModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/entry/${entryId}`,
|
||||||
{
|
{
|
||||||
params: {
|
|
||||||
cmId: module.id,
|
|
||||||
courseId: module.course,
|
|
||||||
},
|
|
||||||
siteId,
|
siteId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -144,15 +144,36 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
this.tabBarHeight = this.tabBarElement.offsetHeight;
|
this.tabBarHeight = this.tabBarElement.offsetHeight;
|
||||||
|
|
||||||
if (this.tabsShown) {
|
this.applyScroll(this.tabsShown, this.lastScroll);
|
||||||
// Smooth translation.
|
}
|
||||||
this.tabBarElement.style.top = - this.lastScroll + 'px';
|
|
||||||
this.tabBarElement.style.height = 'calc(100% + ' + scroll + 'px';
|
/**
|
||||||
} else {
|
* Apply scroll to hiding tabs.
|
||||||
this.tabBarElement.classList.add('tabs-hidden');
|
*
|
||||||
this.tabBarElement.style.top = '0';
|
* @param showTabs Show or completely hide tabs.
|
||||||
this.tabBarElement.style.height = '';
|
* @param scroll Scroll position.
|
||||||
|
*/
|
||||||
|
protected applyScroll(showTabs: boolean, scroll?: number): void {
|
||||||
|
if (!this.tabBarElement || !this.tabBarHeight) {
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showTabs) {
|
||||||
|
// Smooth translation.
|
||||||
|
this.tabBarElement!.classList.remove('tabs-hidden');
|
||||||
|
if (scroll === 0) {
|
||||||
|
this.tabBarElement!.style.height = '';
|
||||||
|
this.lastScroll = 0;
|
||||||
|
} else if (scroll !== undefined) {
|
||||||
|
this.tabBarElement!.style.height = (this.tabBarHeight - scroll) + 'px';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.tabBarElement!.classList.add('tabs-hidden');
|
||||||
|
this.tabBarElement!.style.height = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tabsShown = showTabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -478,11 +499,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
|
|
||||||
if (scrollTop <= 0) {
|
if (scrollTop <= 0) {
|
||||||
// Ensure tabbar is shown.
|
// Ensure tabbar is shown.
|
||||||
this.tabsElement.style.top = '0';
|
this.applyScroll(true, 0);
|
||||||
this.tabsElement.style.height = '';
|
|
||||||
this.tabBarElement.classList.remove('tabs-hidden');
|
|
||||||
this.tabsShown = true;
|
|
||||||
this.lastScroll = 0;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -493,22 +510,17 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tabsShown && scrollTop > this.tabBarHeight) {
|
if (this.tabsShown && scrollTop > this.tabBarHeight) {
|
||||||
this.tabsShown = false;
|
|
||||||
|
|
||||||
// Hide tabs.
|
// Hide tabs.
|
||||||
this.tabBarElement.classList.add('tabs-hidden');
|
this.applyScroll(false);
|
||||||
this.tabsElement.style.top = '0';
|
|
||||||
this.tabsElement.style.height = '';
|
|
||||||
} else if (!this.tabsShown && scrollTop <= this.tabBarHeight) {
|
} else if (!this.tabsShown && scrollTop <= this.tabBarHeight) {
|
||||||
this.tabsShown = true;
|
this.applyScroll(true);
|
||||||
this.tabBarElement.classList.remove('tabs-hidden');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tabsShown && scrollElement.scrollHeight > scrollElement.clientHeight + (this.tabBarHeight - scrollTop)) {
|
if (this.tabsShown && scrollElement.scrollHeight > scrollElement.clientHeight + (this.tabBarHeight - scrollTop)) {
|
||||||
// Smooth translation.
|
// Smooth translation.
|
||||||
this.tabsElement.style.top = - scrollTop + 'px';
|
this.applyScroll(true, scrollTop);
|
||||||
this.tabsElement.style.height = 'calc(100% + ' + scrollTop + 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use lastScroll after moving the tabs to avoid flickering.
|
// Use lastScroll after moving the tabs to avoid flickering.
|
||||||
this.lastScroll = scrollTop;
|
this.lastScroll = scrollTop;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
--color: var(--core-combobox-color);
|
--color: var(--core-combobox-color);
|
||||||
--color-activated: var(--core-combobox-color);
|
--color-activated: var(--core-combobox-color);
|
||||||
--border-color: var(--core-combobox-border-color);
|
--border-color: var(--core-combobox-border-color);
|
||||||
--border-width: 0 0 var(--core-combobox-border-width) 0;
|
|
||||||
--border-style: solid;
|
--border-style: solid;
|
||||||
--color-focused: currentcolor;
|
--color-focused: currentcolor;
|
||||||
--color-hover: currentcolor;
|
--color-hover: currentcolor;
|
||||||
|
@ -53,9 +52,7 @@ ion-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-select {
|
ion-select {
|
||||||
border-color: var(--border-color);
|
border-bottom: var(--core-combobox-border-width) var(--border-style) var(--border-color);
|
||||||
border-width: var(--border-width);
|
|
||||||
border-style: var(--border-style);
|
|
||||||
|
|
||||||
&::part(icon) {
|
&::part(icon) {
|
||||||
margin: var(--icon-margin);
|
margin: var(--icon-margin);
|
||||||
|
@ -84,6 +81,8 @@ ion-select {
|
||||||
|
|
||||||
ion-button {
|
ion-button {
|
||||||
--border-radius: 0;
|
--border-radius: 0;
|
||||||
|
--border-width: 0 0 var(--core-combobox-border-width) 0;
|
||||||
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 45px;
|
min-height: 45px;
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,24 @@
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
--loading-background: var(--ion-background-color);
|
--loading-background: var(--ion-background-color);
|
||||||
|
--loading-background-inline: var(--ion-background-color-rgb);
|
||||||
--loading-spinner: var(--ion-color-primary);
|
--loading-spinner: var(--ion-color-primary);
|
||||||
--loading-text-color: var(--ion-text-color);
|
--loading-text-color: var(--ion-text-color);
|
||||||
--loading-inline-margin: 0;
|
--loading-inline-margin: 0;
|
||||||
--loading-inline-min-height: 28px;
|
--loading-inline-min-height: 28px;
|
||||||
|
--internal-loading-inline-min-height: var(--loading-inline-min-height);
|
||||||
|
|
||||||
position: static;
|
position: static;
|
||||||
color: var(--loading-text-color);
|
color: var(--loading-text-color);
|
||||||
|
@include core-transition(all, 200ms);
|
||||||
|
|
||||||
&.margin {
|
&.margin {
|
||||||
--loading-inline-margin: 10px;
|
--loading-inline-margin: 10px;
|
||||||
|
--internal-loading-inline-min-height: calc(var(--loading-inline-min-height) + var(--loading-inline-margin) + var(--loading-inline-margin));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.core-loading-loaded {
|
&.core-loading-loaded {
|
||||||
--loading-inline-margin: 0;
|
--internal-loading-inline-min-height: 0;
|
||||||
--loading-inline-min-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-spinner {
|
ion-spinner {
|
||||||
|
@ -27,17 +30,17 @@
|
||||||
.core-loading-container {
|
.core-loading-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include position(0, 0, 0, 0);
|
@include position(0, 0, 0, 0);
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: var(--loading-background);
|
background-color: var(--loading-background);
|
||||||
@include core-transition(all, 200ms);
|
@include core-transition(all, 200ms);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-loading-message {
|
.core-loading-message {
|
||||||
|
@ -54,15 +57,14 @@
|
||||||
|
|
||||||
&.core-loading-loaded {
|
&.core-loading-loaded {
|
||||||
position: unset;
|
position: unset;
|
||||||
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.core-loading-inline {
|
&.core-loading-inline {
|
||||||
--loading-background: transparent;
|
--loading-background: rgba(var(--loading-background-inline), 0.5);
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
min-height: var(--loading-inline-min-height);
|
min-height: var(--internal-loading-inline-min-height);
|
||||||
margin-top: var(--loading-inline-margin);
|
|
||||||
margin-bottom: var(--loading-inline-margin);
|
|
||||||
|
|
||||||
.core-loading-message {
|
.core-loading-message {
|
||||||
@include margin(0, 0, 0, 10px);
|
@include margin(0, 0, 0, 10px);
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
-webkit-filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
-webkit-filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
||||||
filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
filter: drop-shadow(0px 3px 3px rgba(var(--drop-shadow)));
|
||||||
border: 0;
|
border: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
ion-row {
|
ion-row {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -72,6 +72,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
|
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
|
||||||
@Input() captureLinks?: boolean; // Whether links should tried to be opened inside the app. Defaults to true.
|
@Input() captureLinks?: boolean; // Whether links should tried to be opened inside the app. Defaults to true.
|
||||||
@Input() openLinksInApp?: boolean; // Whether links should be opened in InAppBrowser.
|
@Input() openLinksInApp?: boolean; // Whether links should be opened in InAppBrowser.
|
||||||
|
@Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Max height in pixels to render the content box. It should be 50 at least to make sense.
|
* Max height in pixels to render the content box. It should be 50 at least to make sense.
|
||||||
|
@ -86,6 +87,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
protected showMoreDisplayed = false;
|
protected showMoreDisplayed = false;
|
||||||
protected loadingChangedListener?: CoreEventObserver;
|
protected loadingChangedListener?: CoreEventObserver;
|
||||||
|
protected emptyText = '';
|
||||||
|
protected contentSpan: HTMLElement;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
element: ElementRef,
|
element: ElementRef,
|
||||||
|
@ -93,9 +96,20 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
protected viewContainerRef: ViewContainerRef,
|
protected viewContainerRef: ViewContainerRef,
|
||||||
protected sanitizer: DomSanitizer,
|
protected sanitizer: DomSanitizer,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
this.element = element.nativeElement;
|
this.element = element.nativeElement;
|
||||||
this.element.classList.add('opacity-hide'); // Hide contents until they're treated.
|
this.element.classList.add('core-format-text-loading'); // Hide contents until they're treated.
|
||||||
|
|
||||||
|
const placeholder = document.createElement('span');
|
||||||
|
placeholder.classList.add('core-format-text-loader');
|
||||||
|
this.element.appendChild(placeholder);
|
||||||
|
|
||||||
|
this.contentSpan = document.createElement('span');
|
||||||
|
this.contentSpan.classList.add('core-format-text-content');
|
||||||
|
this.element.appendChild(this.contentSpan);
|
||||||
|
|
||||||
|
this.emptyText = this.hideIfEmpty ? '' : ' ';
|
||||||
|
this.contentSpan.innerHTML = this.emptyText;
|
||||||
|
|
||||||
this.afterRender = new EventEmitter<void>();
|
this.afterRender = new EventEmitter<void>();
|
||||||
|
|
||||||
this.element.addEventListener('click', this.elementClicked.bind(this));
|
this.element.addEventListener('click', this.elementClicked.bind(this));
|
||||||
|
@ -183,7 +197,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
* Add magnifying glass icons to view adapted images at full size.
|
* Add magnifying glass icons to view adapted images at full size.
|
||||||
*/
|
*/
|
||||||
addMagnifyingGlasses(): void {
|
addMagnifyingGlasses(): void {
|
||||||
const imgs = Array.from(this.element.querySelectorAll('.core-adapted-img-container > img'));
|
const imgs = Array.from(this.contentSpan.querySelectorAll('.core-adapted-img-container > img'));
|
||||||
if (!imgs.length) {
|
if (!imgs.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +353,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
*/
|
*/
|
||||||
protected finishRender(): void {
|
protected finishRender(): void {
|
||||||
// Show the element again.
|
// Show the element again.
|
||||||
this.element.classList.remove('opacity-hide');
|
this.element.classList.remove('core-format-text-loading');
|
||||||
// Emit the afterRender output.
|
// Emit the afterRender output.
|
||||||
this.afterRender.emit();
|
this.afterRender.emit();
|
||||||
}
|
}
|
||||||
|
@ -349,7 +363,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
*/
|
*/
|
||||||
protected async formatAndRenderContents(): Promise<void> {
|
protected async formatAndRenderContents(): Promise<void> {
|
||||||
if (!this.text) {
|
if (!this.text) {
|
||||||
this.element.innerHTML = ''; // Remove current contents.
|
this.contentSpan.innerHTML = this.emptyText; // Remove current contents.
|
||||||
this.finishRender();
|
this.finishRender();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -370,12 +384,12 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
// Disable media adapt to correctly calculate the height.
|
// Disable media adapt to correctly calculate the height.
|
||||||
this.element.classList.add('core-disable-media-adapt');
|
this.element.classList.add('core-disable-media-adapt');
|
||||||
|
|
||||||
this.element.innerHTML = ''; // Remove current contents.
|
this.contentSpan.innerHTML = ''; // Remove current contents.
|
||||||
if (this.maxHeight && result.div.innerHTML != '' &&
|
if (this.maxHeight && result.div.innerHTML != '' &&
|
||||||
(this.fullOnClick || (window.innerWidth < 576 || window.innerHeight < 576))) { // Don't collapse in big screens.
|
(this.fullOnClick || (window.innerWidth < 576 || window.innerHeight < 576))) { // Don't collapse in big screens.
|
||||||
|
|
||||||
// Move the children to the current element to be able to calculate the height.
|
// Move the children to the current element to be able to calculate the height.
|
||||||
CoreDomUtils.moveChildren(result.div, this.element);
|
CoreDomUtils.moveChildren(result.div, this.contentSpan);
|
||||||
|
|
||||||
// Calculate the height now.
|
// Calculate the height now.
|
||||||
this.calculateHeight();
|
this.calculateHeight();
|
||||||
|
@ -396,7 +410,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CoreDomUtils.moveChildren(result.div, this.element);
|
CoreDomUtils.moveChildren(result.div, this.contentSpan);
|
||||||
|
|
||||||
// Add magnifying glasses to images.
|
// Add magnifying glasses to images.
|
||||||
this.addMagnifyingGlasses();
|
this.addMagnifyingGlasses();
|
||||||
|
@ -405,7 +419,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
if (result.options.filter) {
|
if (result.options.filter) {
|
||||||
// Let filters handle HTML. We do it here because we don't want them to block the render of the text.
|
// Let filters handle HTML. We do it here because we don't want them to block the render of the text.
|
||||||
CoreFilterDelegate.handleHtml(
|
CoreFilterDelegate.handleHtml(
|
||||||
this.element,
|
this.contentSpan,
|
||||||
result.filters,
|
result.filters,
|
||||||
this.viewContainerRef,
|
this.viewContainerRef,
|
||||||
result.options,
|
result.options,
|
||||||
|
|
|
@ -66,7 +66,7 @@ describe('CoreFormatTextDirective', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
const text = fixture.nativeElement.querySelector('core-format-text');
|
const text = fixture.nativeElement.querySelector('core-format-text .core-format-text-content');
|
||||||
expect(text).not.toBeNull();
|
expect(text).not.toBeNull();
|
||||||
expect(text.innerHTML).toEqual(sentence);
|
expect(text.innerHTML).toEqual(sentence);
|
||||||
});
|
});
|
||||||
|
|
|
@ -142,7 +142,10 @@
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
|
|
||||||
<ion-item class="ion-text-wrap" *ngIf="section.summary">
|
<ion-item class="ion-text-wrap" *ngIf="section.summary">
|
||||||
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course?.id"></core-format-text>
|
<ion-label>
|
||||||
|
<core-format-text [text]="section.summary" contextLevel="course" [contextInstanceId]="course?.id">
|
||||||
|
</core-format-text>
|
||||||
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
||||||
<ng-container *ngFor="let module of section.modules">
|
<ng-container *ngFor="let module of section.modules">
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
clip: rect(0, 0, 0, 0);
|
clip: rect(0, 0, 0, 0);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sr-only-focusable:active, .sr-only-focusable:focus {
|
.sr-only-focusable:active, .sr-only-focusable:focus {
|
||||||
|
|
|
@ -2,22 +2,70 @@
|
||||||
/** Styles of elements inside the directive should be placed in format-text.scss */
|
/** Styles of elements inside the directive should be placed in format-text.scss */
|
||||||
@import "~theme/globals";
|
@import "~theme/globals";
|
||||||
|
|
||||||
:root {
|
core-format-text {
|
||||||
--background: var(--background, #{$ion-item-background});
|
--core-format-text-background: var(--background, #{$ion-item-background});
|
||||||
--background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background)});
|
--core-format-text-background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background)});
|
||||||
--viewer-icon-background: rgba(255, 255, 255, .5);
|
--core-format-text-viewer-icon-background: rgba(255, 255, 255, .5);
|
||||||
|
--core-format-text-loader-shine: 251,251,251;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root body.dark {
|
body.dark core-format-text {
|
||||||
--background: var(--background, #{$ion-item-background-dark});
|
--core-format-text-background: var(--background, #{$ion-item-background-dark});
|
||||||
--background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background-dark)});
|
--core-format-text-background-gradient-rgb: var(--background-rgb, #{color-to-rgb-list($ion-item-background-dark)});
|
||||||
--viewer-icon-background: rgba(0, 0, 0, .5);
|
--core-format-text-viewer-icon-background: rgba(0, 0, 0, .5);
|
||||||
|
--core-format-text-loader-shine: 90,90,90;
|
||||||
}
|
}
|
||||||
|
|
||||||
core-format-text {
|
core-format-text {
|
||||||
user-select: text;
|
display: contents;
|
||||||
word-break: break-word;
|
|
||||||
word-wrap: break-word;
|
.core-format-text-loader {
|
||||||
|
opacity: 0;
|
||||||
|
@include core-transition(opacity, 200ms);
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.core-format-text-loading {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(0,0,0,.1);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.core-format-text-loader {
|
||||||
|
position: absolute;
|
||||||
|
left: -45%;
|
||||||
|
height: 100%;
|
||||||
|
width: 45%;
|
||||||
|
background-image: -webkit-linear-gradient(to left, rgba(var(--core-format-text-loader-shine), .05), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .6), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .05));
|
||||||
|
background-image: linear-gradient(to left, rgba(var(--core-format-text-loader-shine), .05), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .6), rgba(var(--core-format-text-loader-shine), .3), rgba(var(--core-format-text-loader-shine), .05));
|
||||||
|
animation: loading 1s infinite;
|
||||||
|
opacity: 1;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-format-text-content {
|
||||||
|
opacity: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-show-more {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-format-text-content {
|
||||||
|
opacity: 1;
|
||||||
|
@include core-transition(opacity, 200ms);
|
||||||
|
|
||||||
|
display: contents;
|
||||||
|
user-select: text;
|
||||||
|
word-break: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
&[maxHeight],
|
&[maxHeight],
|
||||||
&[ng-reflect-max-height] {
|
&[ng-reflect-max-height] {
|
||||||
|
@ -57,7 +105,7 @@ core-format-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include position(null, 0, 0, null);
|
@include position(null, 0, 0, null);
|
||||||
z-index: 7;
|
z-index: 7;
|
||||||
background-color: var(--background);
|
background-color: var(--core-format-text-background);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
@include padding(null, null, null, 10px);
|
@include padding(null, null, null, 10px);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -68,10 +116,8 @@ core-format-text {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include position(null, 0, 0, 0);
|
@include position(null, 0, 0, 0);
|
||||||
background: -moz-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
|
background: -webkit-linear-gradient(top, rgba(var(--core-format-text-background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 15px));
|
||||||
background: -webkit-gradient(left top, left bottom, color-stop(calc(100% - 50px), rgba(var(--background-gradient-rgb), 0)), color-stop(calc(100% - 15px), rgba(var(--background-gradient-rgb), 1)));
|
background: linear-gradient(to bottom, rgba(var(--core-format-text-background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 15px));
|
||||||
background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
|
|
||||||
background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 50px), rgba(var(--background-gradient-rgb), 1) calc(100% - 15px));
|
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,15 +161,15 @@ core-format-text {
|
||||||
.core-adapted-img-container {
|
.core-adapted-img-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-image-viewer-icon {
|
.core-image-viewer-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@include position(null, 10px, 10px, null);
|
@include position(null, 10px, 10px, null);
|
||||||
color: var(--black);
|
color: var(--ion-text-color);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: var(--viewer-icon-background);
|
background-color: var(--core-format-text-viewer-icon-background);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
width: var(--a11y-min-target-size);
|
width: var(--a11y-min-target-size);
|
||||||
|
@ -141,3 +187,13 @@ core-format-text {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
left: -45%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -685,7 +685,7 @@ audio.core-media-adapt-width {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make links clickable when inside radio or checkbox items. Style part.
|
// Make links clickable when inside radio or checkbox items. Style part.
|
||||||
@media (any-hover: hover) {
|
@media (hover: hover) {
|
||||||
ion-item.item-multiple-inputs:hover::part(native) {
|
ion-item.item-multiple-inputs:hover::part(native) {
|
||||||
color: var(--color-hover);
|
color: var(--color-hover);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue