MOBILE-3810 format-text: Remove format text full view and review toggle

main
Pau Ferrer Ocaña 2021-11-17 11:46:37 +01:00
parent dace9898d0
commit f31fc5a6df
16 changed files with 212 additions and 230 deletions

View File

@ -1,4 +1,3 @@
<core-dynamic-component [component]="pluginComponent" [data]="data">
<!-- This content will be replaced by the component if found. -->
<core-loading [hideUntil]="pluginLoaded">
@ -9,9 +8,8 @@
{{ 'addon.mod_assign.feedbacknotsupported' | translate }}
</ion-badge>
<p *ngIf="text">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
[fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
[courseId]="assign.course">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text"
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
</p>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"

View File

@ -1,4 +1,3 @@
<core-dynamic-component [component]="pluginComponent" [data]="data">
<!-- This content will be replaced by the component if found. -->
<core-loading [hideUntil]="pluginLoaded">
@ -9,9 +8,8 @@
{{ 'addon.mod_assign.submissionnotsupported' | translate }}
</ion-badge>
<p *ngIf="text">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
[fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
[courseId]="assign.course">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text"
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
</p>
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"

View File

@ -3,16 +3,14 @@
<ion-label>
<h2>{{ plugin.name }}</h2>
<p>
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
[fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
[courseId]="assign.course">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" contextLevel="module"
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
</p>
</ion-label>
<div slot="end">
<div class="ion-text-end">
<ion-button fill="clear" *ngIf="canEdit" (click)="editComment()" color="dark"
[attr.aria-label]="'core.edit' | translate">
<ion-button fill="clear" *ngIf="canEdit" (click)="editComment()" color="dark" [attr.aria-label]="'core.edit' | translate">
<ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
@ -25,9 +23,8 @@
<!-- Edit -->
<ion-item class="ion-text-wrap" *ngIf="edit && loaded">
<ion-label class="sr-only">{{ plugin.name }}</ion-label>
<core-rich-text-editor [control]="control" [placeholder]="plugin.name"
name="assignfeedbackcomments_editor" [component]="component" [componentId]="assign.cmid" [autoSave]="true"
contextLevel="module" [contextInstanceId]="assign.cmid" elementId="assignfeedbackcomments_editor"
[draftExtraParams]="{userid: userId, action: 'grade'}">
<core-rich-text-editor [control]="control" [placeholder]="plugin.name" name="assignfeedbackcomments_editor" [component]="component"
[componentId]="assign.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="assign.cmid"
elementId="assignfeedbackcomments_editor" [draftExtraParams]="{userid: userId, action: 'grade'}">
</core-rich-text-editor>
</ion-item>

View File

@ -4,9 +4,8 @@
<h2>{{ plugin.name }}</h2>
<p *ngIf="words">{{ 'addon.mod_assign.numwords' | translate: {'$a': words} }}</p>
<p>
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
[fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
[courseId]="assign.course">
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" contextLevel="module"
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
</core-format-text>
</p>
</ion-label>
@ -15,7 +14,9 @@
<!-- Edit -->
<div *ngIf="edit && loaded">
<ion-item-divider class="ion-text-wrap" sticky="true">
<ion-label><h2>{{ plugin.name }}</h2></ion-label>
<ion-label>
<h2>{{ plugin.name }}</h2>
</ion-label>
</ion-item-divider>
<ion-item class="ion-text-wrap" *ngIf="wordLimitEnabled && words >= 0">
<ion-label>
@ -25,10 +26,10 @@
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label class="sr-only">{{ plugin.name }}</ion-label>
<core-rich-text-editor [control]="control" [placeholder]="plugin.name"
name="onlinetext_editor_text" (contentChanged)="onChange($event)" [component]="component"
[componentId]="assign.cmid" [autoSave]="true" contextLevel="module" [contextInstanceId]="assign.cmid"
elementId="onlinetext_editor" [draftExtraParams]="{userid: currentUserId, action: 'editsubmission'}">
<core-rich-text-editor [control]="control" [placeholder]="plugin.name" name="onlinetext_editor_text"
(contentChanged)="onChange($event)" [component]="component" [componentId]="assign.cmid" [autoSave]="true" contextLevel="module"
[contextInstanceId]="assign.cmid" elementId="onlinetext_editor"
[draftExtraParams]="{userid: currentUserId, action: 'editsubmission'}">
</core-rich-text-editor>
</ion-item>
</div>

View File

@ -87,7 +87,7 @@
<ion-item class="ion-text-wrap">
<ion-label>
<h2>{{ 'addon.mod_workshop.conclusion' | translate }}</h2>
<core-format-text fullOnClick="true" [component]="component" [componentId]="module.id" [text]="workshop!.conclusion"
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.conclusion"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</ion-label>
@ -120,8 +120,8 @@
<ion-item class="ion-text-wrap">
<ion-label>
<h2>{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h2>
<core-format-text fullOnClick="true" [component]="component" [componentId]="module.id"
[text]="workshop!.instructauthors" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.instructauthors"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</ion-label>
</ion-item>
@ -184,7 +184,7 @@
<ion-item class="ion-text-wrap">
<ion-label>
<h2>{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h2>
<core-format-text fullOnClick="true" [component]="component" [componentId]="module.id"
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id"
[text]="workshop!.instructreviewers" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="courseId">
</core-format-text>

View File

@ -61,8 +61,6 @@ export class CoreFormatTextDirective implements OnChanges {
@Input() adaptImg?: boolean | string = true; // Whether to adapt images to screen width.
@Input() clean?: boolean | string; // Whether all the HTML tags should be removed.
@Input() singleLine?: boolean | string; // Whether new lines should be removed (all text in single line). Only if clean=true.
@Input() fullOnClick?: boolean | string; // Whether it should open a new page with the full contents on click.
@Input() fullTitle?: string; // Title to use in full view. Defaults to "Description".
@Input() highlight?: string; // Text to highlight.
@Input() filter?: boolean | string; // Whether to filter the text. If not defined, true if contextLevel and instanceId are set.
@Input() contextLevel?: string; // The context level of the text.
@ -73,6 +71,8 @@ export class CoreFormatTextDirective implements OnChanges {
@Input() openLinksInApp?: boolean; // Whether links should be opened in InAppBrowser.
@Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
@Input() fullOnClick?: boolean | string; // @deprecated on 4.0 Won't do anything.
@Input() fullTitle?: string; // @deprecated on 4.0 Won't do anything..
/**
* Max height in pixels to render the content box. It should be 50 at least to make sense.
* Using this parameter will force display: block to calculate height better.
@ -84,10 +84,11 @@ export class CoreFormatTextDirective implements OnChanges {
@Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
protected element: HTMLElement;
protected showMoreDisplayed = false;
protected expanded = false;
protected loadingChangedListener?: CoreEventObserver;
protected emptyText = '';
protected contentSpan: HTMLElement;
protected toggleExpandEnabled = false;
constructor(
element: ElementRef,
@ -114,11 +115,12 @@ export class CoreFormatTextDirective implements OnChanges {
}
/**
* Detect changes on input properties.
* @inheritdoc
*/
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.text || changes.filter || changes.contextLevel || changes.contextInstanceId) {
this.hideShowMore();
this.setExpandButtonEnabled(false);
this.formatAndRenderContents();
}
}
@ -267,37 +269,61 @@ export class CoreFormatTextDirective implements OnChanges {
this.element.style.maxHeight = initialMaxHeight;
// If cannot calculate height, shorten always.
if (!height || height > this.maxHeight) {
if (!this.showMoreDisplayed) {
this.displayShowMore();
}
} else if (this.showMoreDisplayed) {
this.hideShowMore();
}
this.setExpandButtonEnabled(!height || height > this.maxHeight);
}
/**
* Display the "Show more" in the element.
* Sets if expand button is enabled or not.
*
* @param enable Wether enable or disable.
*/
protected displayShowMore(): void {
const expandInFullview = CoreUtils.isTrueOrOne(this.fullOnClick) || false;
const showMoreButton = document.createElement('ion-button');
protected setExpandButtonEnabled(enable: boolean): void {
this.toggleExpandEnabled = enable;
this.element.classList.toggle('core-text-formatted', enable);
showMoreButton.classList.add('core-show-more');
showMoreButton.setAttribute('fill', 'clear');
showMoreButton.innerHTML = Translate.instant('core.showmore');
this.element.appendChild(showMoreButton);
if (expandInFullview) {
this.element.classList.add('core-expand-in-fullview');
} else {
showMoreButton.setAttribute('aria-expanded', 'false');
if (!enable || this.element.querySelector('ion-button.core-format-text-toggle')) {
return;
}
this.element.classList.add('core-text-formatted');
this.element.classList.add('core-shortened');
this.element.style.maxHeight = this.maxHeight + 'px';
this.showMoreDisplayed = true;
// Add expand/collapse buttons
const toggleButton = document.createElement('ion-button');
toggleButton.classList.add('core-format-text-toggle');
toggleButton.setAttribute('fill', 'clear');
const toggleText = document.createElement('span');
toggleText.classList.add('core-format-text-toggle-text');
toggleButton.appendChild(toggleText);
const expandArrow = document.createElement('span');
expandArrow.classList.add('core-format-text-arrow');
toggleButton.appendChild(expandArrow);
this.element.appendChild(toggleButton);
this.toggleExpand(this.expanded);
}
/**
* Expand or collapse text.
*
* @param expand Wether expand or collapse text. If undefined, will toggle.
*/
protected toggleExpand(expand?: boolean): void {
if (expand === undefined) {
expand = !this.expanded;
}
this.expanded = expand;
this.element.classList.toggle('core-text-format-expanded', expand);
this.element.classList.toggle('core-text-format-collapsed', !expand);
this.element.style.maxHeight = expand ? '' : this.maxHeight + 'px';
const toggleButton = this.element.querySelector('ion-button.core-format-text-toggle');
const toggleText = toggleButton?.querySelector('.core-format-text-toggle-text');
if (!toggleButton || !toggleText) {
return;
}
toggleText.innerHTML = expand ? Translate.instant('core.showless') : Translate.instant('core.showmore');
toggleButton.setAttribute('aria-expanded', expand ? 'true' : 'false');
}
/**
@ -321,9 +347,7 @@ export class CoreFormatTextDirective implements OnChanges {
return;
}
const expandInFullview = CoreUtils.isTrueOrOne(this.fullOnClick) || false;
if (!expandInFullview && !this.showMoreDisplayed) {
if (!this.toggleExpandEnabled) {
// Nothing to do on click, just stop.
return;
}
@ -331,28 +355,7 @@ export class CoreFormatTextDirective implements OnChanges {
e.preventDefault();
e.stopPropagation();
if (!expandInFullview) {
// Change class.
this.element.classList.toggle('core-shortened');
return;
} else {
// Open a new state with the contents.
const filter = typeof this.filter != 'undefined' ? CoreUtils.isTrueOrOne(this.filter) : undefined;
CoreTextUtils.viewText(
this.fullTitle || Translate.instant('core.description'),
this.text,
{
component: this.component,
componentId: this.componentId,
filter: filter,
contextLevel: this.contextLevel,
instanceId: this.contextInstanceId,
courseId: this.courseId,
},
);
}
this.toggleExpand();
}
/**
@ -361,6 +364,7 @@ export class CoreFormatTextDirective implements OnChanges {
protected finishRender(): void {
// Show the element again.
this.element.classList.remove('core-format-text-loading');
// Emit the afterRender output.
this.afterRender.emit();
}
@ -393,7 +397,7 @@ export class CoreFormatTextDirective implements OnChanges {
this.contentSpan.innerHTML = ''; // Remove current contents.
if (this.maxHeight && result.div.innerHTML != '' &&
(this.fullOnClick || (window.innerWidth < 576 || window.innerHeight < 576))) { // Don't collapse in big screens.
(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.
CoreDomUtils.moveChildren(result.div, this.contentSpan);
@ -655,20 +659,6 @@ export class CoreFormatTextDirective implements OnChanges {
return CoreDomUtils.getElementHeight(element) || 0;
}
/**
* "Hide" the "Show more" in the element if it's shown.
*/
protected hideShowMore(): void {
const showMoreButton = this.element.querySelector('ion-button.core-show-more');
showMoreButton?.remove();
this.element.classList.remove('core-expand-in-fullview');
this.element.classList.remove('core-text-formatted');
this.element.classList.remove('core-shortened');
this.element.style.maxHeight = '';
this.showMoreDisplayed = false;
}
/**
* Add media adapt class and apply CoreExternalContentDirective to the media element and its sources and tracks.
*

View File

@ -1,9 +1,8 @@
<ion-card *ngIf="description">
<ion-item class="ion-text-wrap">
<ion-label>
<core-format-text [text]="description" [component]="component" [componentId]="componentId"
[maxHeight]="showFull && showFull !== 'false' ? 0 : 120" fullOnClick="true" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId" [courseId]="courseId">
<core-format-text [text]="description" [component]="component" [componentId]="componentId" [maxHeight]="120"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId">
</core-format-text>
</ion-label>
</ion-item>

View File

@ -13,7 +13,7 @@
<ion-item class="ion-text-wrap" *ngIf="description" lines="none">
<ion-label>
<core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
[contextInstanceId]="module.id" [courseId]="courseId" maxHeight="120">
[contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="120">
</core-format-text>
</ion-label>
</ion-item>

View File

@ -1,17 +1,12 @@
<ng-container *ngIf="module.handlerData && module.visibleoncoursepage !== 0">
<ng-container *ngIf="!module.handlerData.loading">
<ion-item
id="core-course-module-{{module.id}}"
<ion-item id="core-course-module-{{module.id}}"
class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}"
(click)="moduleClicked($event)"
[attr.aria-label]="module.handlerData.a11yTitle"
[ngClass]="{
(click)="moduleClicked($event)" [attr.aria-label]="module.handlerData.a11yTitle" [ngClass]="{
'has-module-info': hasInfo,
'item-media': module.handlerData.icon,
'item-dimmed': module.visible === 0 || module.uservisible === false
}"
[button]="module.handlerData.action && module.uservisible"
detail="false">
}" [button]="module.handlerData.action && module.uservisible" detail="false">
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
[componentId]="module.instance">
@ -23,11 +18,8 @@
[courseId]="courseId" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
</core-format-text>
</p>
<ion-badge
*ngIf="module.handlerData.extraBadge"
[color]="module.handlerData.extraBadgeColor"
class="ion-text-wrap ion-text-start"
>
<ion-badge *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor"
class="ion-text-wrap ion-text-start">
<span [innerHTML]="module.handlerData.extraBadge"></span>
</ion-badge>
<ion-badge *ngIf="module.visible === 0 && (!section || section.visible)" class="ion-text-wrap">
@ -47,12 +39,8 @@
</ion-badge>
</ion-label>
<!-- Buttons. -->
<div
slot="end"
*ngIf="module.uservisible !== false"
class="buttons core-module-buttons"
[ngClass]="{'core-button-completion': module.completiondata && showLegacyCompletion}"
>
<div slot="end" *ngIf="module.uservisible !== false" class="buttons core-module-buttons"
[ngClass]="{'core-button-completion': module.completiondata && showLegacyCompletion}">
<!-- Module completion (legacy). -->
<core-course-module-completion-legacy *ngIf="module.completiondata && showLegacyCompletion"
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
@ -60,31 +48,24 @@
</core-course-module-completion-legacy>
<div class="core-module-buttons-more">
<core-download-refresh [status]="downloadStatus" [enabled]="downloadEnabled"
[canTrustDownload]="true" [loading]="spinner || module.handlerData.spinner"
(action)="download($event)" size="small">
<core-download-refresh [status]="downloadStatus" [enabled]="downloadEnabled" [canTrustDownload]="true"
[loading]="spinner || module.handlerData.spinner" (action)="download($event)" size="small">
</core-download-refresh>
<!-- Buttons defined by the module handler. -->
<ion-button fill="clear" *ngFor="let button of module.handlerData.buttons" color="dark" size="small"
[hidden]="button.hidden || spinner || module.handlerData.spinner" class="core-animate-show-hide"
(click)="buttonClicked($event, button)"
[attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
(click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
<ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
</div>
</ion-item>
<ion-item
*ngIf="hasInfo"
id="core-course-module-{{module.id}}-info"
class="ion-text-wrap core-course-module-handler core-module-module-info {{module.handlerData.class}}"
[ngClass]="{
<ion-item *ngIf="hasInfo" id="core-course-module-{{module.id}}-info"
class="ion-text-wrap core-course-module-handler core-module-module-info {{module.handlerData.class}}" [ngClass]="{
'item-media': module.handlerData.icon,
'item-dimmed': module.visible === 0 || module.uservisible === false
}"
detail="false"
>
}" detail="false">
<ion-label>
<!-- Activity dates. -->
<div *ngIf="showActivityDates && module.dates && module.dates.length" class="core-module-dates">
@ -94,12 +75,12 @@
</div>
<!-- Module completion. -->
<core-course-module-completion *ngIf="module.completiondata" [completion]="module.completiondata"
[moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions"
<core-course-module-completion *ngIf="module.completiondata" [completion]="module.completiondata" [moduleName]="module.name"
[moduleId]="module.id" [showCompletionConditions]="showCompletionConditions"
[showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)">
</core-course-module-completion>
<core-format-text class="core-module-description" *ngIf="module.description" maxHeight="80" [text]="module.description"
<core-format-text class="core-module-description" *ngIf="module.description" [maxHeight]="80" [text]="module.description"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</ion-label>
@ -107,14 +88,11 @@
</ng-container>
<!-- Loading. -->
<ion-item *ngIf="module.handlerData.loading"
role="status"
class="ion-text-wrap"
id="core-course-module-{{module.id}}"
<ion-item *ngIf="module.handlerData.loading" role="status" class="ion-text-wrap" id="core-course-module-{{module.id}}"
[attr.aria-label]="module.handlerData.a11yTitle"
[ngClass]="['core-course-module-handler', 'core-module-loading', module.handlerData.class]"
detail="false"
>
<ion-label><ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner></ion-label>
[ngClass]="['core-course-module-handler', 'core-module-loading', module.handlerData.class]" detail="false">
<ion-label>
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
</ion-label>
</ion-item>
</ng-container>

View File

@ -27,8 +27,10 @@
<core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</h2>
<p *ngIf="course.categoryname"><core-format-text [text]="course.categoryname"
contextLevel="coursecat" [contextInstanceId]="course.categoryid"></core-format-text></p>
<p *ngIf="course.categoryname">
<core-format-text [text]="course.categoryname" contextLevel="coursecat" [contextInstanceId]="course.categoryid">
</core-format-text>
</p>
<p *ngIf="course.startdate">
{{course.startdate * 1000 | coreFormatDate:"strftimedatefullshort" }}
<span *ngIf="course.enddate"> - {{course.enddate * 1000 | coreFormatDate:"strftimedatefullshort" }}</span>
@ -38,7 +40,7 @@
<ion-item class="ion-text-wrap" *ngIf="course.summary" detail="false">
<ion-label>
<core-format-text [text]="course.summary" maxHeight="120" contextLevel="course" [contextInstanceId]="course.id">
<core-format-text [text]="course.summary" [maxHeight]="120" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</ion-label>
</ion-item>
@ -51,8 +53,7 @@
</ion-item-divider>
<ion-item class="ion-text-wrap" *ngFor="let contact of course.contacts" core-user-link [userId]="contact.id"
[courseId]="isEnrolled ? course.id : null" [attr.aria-label]="'core.viewprofile' | translate" detail="true">
<core-user-avatar [user]="contact" slot="start" [userId]="contact.id"
[courseId]="isEnrolled ? course.id : null">
<core-user-avatar [user]="contact" slot="start" [userId]="contact.id" [courseId]="isEnrolled ? course.id : null">
</core-user-avatar>
<ion-label>
<p class="item-heading">{{contact.fullname}}</p>
@ -71,7 +72,7 @@
</core-format-text>
</span><span class="core-customfieldseparator">: </span>
<span class="core-customfieldvalue">
<core-format-text [text]="field.value" maxHeight="120" contextLevel="course"
<core-format-text [text]="field.value" [maxHeight]="120" contextLevel="course"
[contextInstanceId]="course.id">
</core-format-text>
</span>
@ -100,19 +101,19 @@
</ion-label>
</ion-item>
<ion-item *ngIf="!isEnrolled && !selfEnrolInstances.length && !paypalEnabled">
<ion-label><p>{{ 'core.courses.notenrollable' | translate }}</p></ion-label>
<ion-label>
<p>{{ 'core.courses.notenrollable' | translate }}</p>
</ion-label>
</ion-item>
<ion-item *ngIf="canAccessCourse && downloadCourseEnabled" (click)="prefetchCourse()" detail="false"
[attr.aria-label]="prefetchCourseData.statusTranslatable | translate" button>
<ion-icon *ngIf="(prefetchCourseData.status != statusDownloaded) && !prefetchCourseData.loading"
[name]="prefetchCourseData.icon" slot="start" aria-hidden="true">
</ion-icon>
<ion-icon *ngIf="(prefetchCourseData.status == statusDownloaded) && !prefetchCourseData.loading"
slot="start" [name]="prefetchCourseData.icon" color="success"
aria-hidden="true" role="status">
<ion-icon *ngIf="(prefetchCourseData.status == statusDownloaded) && !prefetchCourseData.loading" slot="start"
[name]="prefetchCourseData.icon" color="success" aria-hidden="true" role="status">
</ion-icon>
<ion-spinner *ngIf="prefetchCourseData.loading" slot="start"
[attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-spinner *ngIf="prefetchCourseData.loading" slot="start" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-label>
<h2 *ngIf="prefetchCourseData.status != statusDownloaded">{{ 'core.course.downloadcourse' | translate }}</h2>
<h2 *ngIf="prefetchCourseData.status == statusDownloaded">{{ 'core.course.refreshcourse' | translate }}</h2>
@ -121,12 +122,15 @@
<ion-item button (click)="openCourse()" [attr.aria-label]="course.fullname" *ngIf="!avoidOpenCourse && canAccessCourse"
detail="true">
<ion-icon name="fas-briefcase" slot="start" aria-hidden="true"></ion-icon>
<ion-label><h2>{{ 'core.course.contents' | translate }}</h2></ion-label>
<ion-label>
<h2>{{ 'core.course.contents' | translate }}</h2>
</ion-label>
</ion-item>
<ion-item [href]="courseUrl" core-link [attr.aria-label]="course.fullname" button detail="false"
[showBrowserWarning]="false">
<ion-item [href]="courseUrl" core-link [attr.aria-label]="course.fullname" button detail="false" [showBrowserWarning]="false">
<ion-icon name="fas-external-link-alt" slot="start" aria-hidden="true"></ion-icon>
<ion-label><h2>{{ 'core.openinbrowser' | translate }}</h2></ion-label>
<ion-label>
<h2>{{ 'core.openinbrowser' | translate }}</h2>
</ion-label>
</ion-item>
</div>
</core-loading>

View File

@ -31,7 +31,7 @@
</core-format-text>
</p>
<p *ngIf="currentCategory.description">
<core-format-text [text]="currentCategory.description" maxHeight="60" contextLevel="coursecat"
<core-format-text [text]="currentCategory.description" [maxHeight]="120" contextLevel="coursecat"
[contextInstanceId]="currentCategory.id"></core-format-text>
</p>
</ion-label>

View File

@ -251,7 +251,7 @@ export class CoreCoursesHelperProvider {
case 'lastaccess':
courses.sort((a, b) => (b.lastaccess || 0) - (a.lastaccess || 0));
break;
// @todo Time modified property is not defined in CoreEnrolledCourseDataWithOptions, so it won't do nothing.
// @todo Time modified property is not defined in CoreEnrolledCourseDataWithOptions, so it Won't do anything.
// case 'timemodified':
// courses.sort((a, b) => b.timemodified - a.timemodified);
// break;

View File

@ -14,15 +14,16 @@
<core-empty-box *ngIf="!grade" icon="fas-chart-bar" [message]="'core.grades.nogradesreturned' | translate"></core-empty-box>
<ion-list *ngIf="grade">
<ion-item *ngIf="grade.itemname && grade.link" class="ion-text-wrap" detail="true" [href]="grade.link" core-link
capture="true">
<ion-item *ngIf="grade.itemname && grade.link" class="ion-text-wrap" detail="true" [href]="grade.link" core-link capture="true">
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon>
<img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image && grade.itemmodule" slot="start" [alt]="grade.iconAlt" />
<core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule">
</core-mod-icon>
<ion-label>
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text></h2>
<h2>
<core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
</h2>
</ion-label>
</ion-item>
@ -32,8 +33,10 @@
<core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule">
</core-mod-icon>
<ion-label>
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text></h2>
<h2>
<core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
</h2>
</ion-label>
</ion-item>
@ -89,8 +92,10 @@
<ion-item class="ion-text-wrap" *ngIf="grade.feedback">
<ion-label>
<h2>{{ 'core.grades.feedback' | translate}}</h2>
<p><core-format-text [fullTitle]="'core.grades.feedback' | translate" maxHeight="60" fullOnClick="true"
[text]="grade.feedback" contextLevel="course" [contextInstanceId]="courseId"></core-format-text></p>
<p>
<core-format-text [maxHeight]="120" [text]="grade.feedback" contextLevel="course" [contextInstanceId]="courseId">
</core-format-text>
</p>
</ion-label>
</ion-item>

View File

@ -571,7 +571,7 @@ export class CoreTextUtilsProvider {
const regex = new RegExp('(' + searchText + ')', 'gi');
return text.replace(regex, '<span class="matchtext">$1</span>');
return text.replace(regex, '<mark class="matchtext">$1</mark>');
}
/**

View File

@ -51,11 +51,15 @@ core-format-text {
display: inline;
}
.core-show-more {
.core-format-text-toggle {
display: none !important;
}
}
.core-format-text-toggle {
display: none;
}
.core-format-text-content {
opacity: 1;
@include core-transition(opacity, 200ms);
@ -84,30 +88,45 @@ core-format-text {
cursor: pointer;
pointer-events: auto;
.core-show-more {
display: none;
}
&:not(.core-shortened) {
max-height: none !important;
}
&.core-shortened {
overflow: hidden;
min-height: 50px;
.core-show-more {
.core-format-text-toggle {
display: block;
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
z-index: 7;
text-transform: none;
text-align: end;
font-size: 14px;
display: block;
position: absolute;
@include position(null, 0, 0, null);
z-index: 7;
background-color: var(--core-format-text-background);
color: var(--text-color);
@include padding(null, null, null, 10px);
margin: 0;
.core-format-text-arrow {
width: var(--a11y-min-target-size);
height: var(--a11y-min-target-size);
background-position: center;
background-repeat: no-repeat;
background-size: 14px 14px;
@include core-transition(transform, 500ms);
@include push-arrow-color(626262, true);
@include darkmode() {
@include push-arrow-color(ffffff, true);
}
}
}
&.core-text-format-collapsed {
overflow: hidden;
min-height: 50px;
.core-format-text-arrow {
transform: rotate(90deg);
}
&:before {
@ -115,26 +134,19 @@ core-format-text {
height: 100%;
position: absolute;
@include position(null, 0, 0, 0);
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: 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(--core-format-text-background-gradient-rgb), 0) calc(100% - 60px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 40px));
background: linear-gradient(to bottom, rgba(var(--core-format-text-background-gradient-rgb), 0) calc(100% - 60px), rgba(var(--core-format-text-background-gradient-rgb), 1) calc(100% - 40px));
z-index: 6;
}
}
}
&.core-expand-in-fullview {
cursor: pointer;
&.core-text-format-expanded {
max-height: none !important;
.core-show-more {
@include push-arrow-color(626262, true);
@include padding-horizontal(null, 5px);
@include background-position(end, 5px, center);
padding-bottom: 50px; // So the Show less button can fit.
background-repeat: no-repeat;
background-size: 14px 14px;
@include darkmode() {
@include push-arrow-color(ffffff, true);
.core-format-text-arrow {
transform: rotate(-90deg);
}
}
}
@ -143,10 +155,10 @@ core-format-text {
@if ($core-format-text-never-shorten) {
&[maxHeight],
&[ng-reflect-max-height] {
&.core-text-formatted.core-shortened {
&.core-text-formatted.core-text-format-expanded {
max-height: none !important;
.core-show-more {
.core-format-text-toggle {
display: none !important;
}
@ -354,7 +366,7 @@ core-rich-text-editor .core-rte-editor {
height: 30px;
display: inline-block;
border: 1px solid var(--gray-dark);
background: var(--background-contrast);
background: var(--contrast-background);
padding: 6px 8px;
border-radius: 4px;
margin-left: 2px;
@ -363,7 +375,7 @@ core-rich-text-editor .core-rte-editor {
}
select {
background-color: var(--background-contrast);
background-color: var(--contrast-background);
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23000000%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat, repeat;
background-position: right .7em top 50%, 0 0;

View File

@ -848,7 +848,7 @@ core-block ion-item-divider .core-button-spinner {
// Text formats.
// Highlight text.
.matchtext {
mark, .matchtext {
background-color: var(--text-hightlight-background-color);
}