commit
33bd7c75a6
|
@ -8,7 +8,7 @@
|
|||
{{ 'addon.mod_assign.feedbacknotsupported' | translate }}
|
||||
</ion-badge>
|
||||
<p *ngIf="text">
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text"
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text"
|
||||
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
</core-format-text>
|
||||
</p>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{{ 'addon.mod_assign.submissionnotsupported' | translate }}
|
||||
</ion-badge>
|
||||
<p *ngIf="text">
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text"
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text"
|
||||
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
</core-format-text>
|
||||
</p>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<ion-label>
|
||||
<h2>{{ plugin.name }}</h2>
|
||||
<p>
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="120" [text]="text" contextLevel="module"
|
||||
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text"
|
||||
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
</core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
|
|
|
@ -4,8 +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]="120" [text]="text" contextLevel="module"
|
||||
[contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [collapsible-item]="120" [text]="text"
|
||||
contextLevel="module" [contextInstanceId]="assign.cmid" [courseId]="assign.course">
|
||||
</core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div collapsible-footer appearOnBottom *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width">
|
||||
<ion-button expand="block" (click)="openBook()" class="ion-margin ion-text-wrap">
|
||||
<span *ngIf="!hasStartedBook">{{ 'core.start' | translate }}</span>
|
||||
|
|
|
@ -32,6 +32,6 @@
|
|||
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id"
|
||||
<core-course-module-navigation collapsible-footer appearOnBottom [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id"
|
||||
(completionChanged)="onCompletionChange()">
|
||||
</core-course-module-navigation>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div collapsible-footer appearOnBottom *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width">
|
||||
<ion-button class="ion-margin ion-text-wrap" expand="block" (click)="openImscp()">
|
||||
<span *ngIf="!hasStarted">{{ 'core.start' | translate }}</span>
|
||||
|
|
|
@ -75,12 +75,11 @@
|
|||
|
||||
<!-- Short answer. -->
|
||||
<ion-item class="ion-text-wrap" *ngSwitchCase="'shortanswer'">
|
||||
<ion-label>
|
||||
<ion-input [type]="question.input!.type" placeholder="{{ 'addon.mod_lesson.youranswer' | translate }}"
|
||||
[id]="question.input!.id" [formControlName]="question.input!.name" autocorrect="off"
|
||||
[maxlength]="question.input!.maxlength">
|
||||
</ion-input>
|
||||
</ion-label>
|
||||
<ion-label class="sr-only" stacked></ion-label>
|
||||
<ion-input [type]="question.input!.type" placeholder="{{ 'addon.mod_lesson.youranswer' | translate }}"
|
||||
[id]="question.input!.id" [formControlName]="question.input!.name" autocorrect="off"
|
||||
[maxlength]="question.input!.maxlength">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
|
||||
<!-- Essay. -->
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<ion-label>
|
||||
<h3 class="item-heading">{{ 'addon.mod_lesson.question' | translate }}</h3>
|
||||
<p>
|
||||
<core-format-text [component]="component" [componentId]="lesson?.coursemodule" [maxHeight]="50"
|
||||
<core-format-text [component]="component" [componentId]="lesson?.coursemodule" [collapsible-item]="50"
|
||||
[text]="page.contents" contextLevel="module" [contextInstanceId]="lesson?.coursemodule"
|
||||
[courseId]="courseId">
|
||||
</core-format-text>
|
||||
|
|
|
@ -32,5 +32,5 @@
|
|||
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id">
|
||||
<core-course-module-navigation collapsible-footer appearOnBottom [hidden]="showLoading" [courseId]="courseId" [currentModuleId]="module.id">
|
||||
</core-course-module-navigation>
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
</ion-list>
|
||||
</ng-container>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div collapsible-footer appearOnBottom *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="mode == 'external'">
|
||||
<ion-button expand="block" (click)="open(openFileAction.OPEN)" class="ion-margin ion-text-wrap">
|
||||
<ng-container *ngIf="isStreamedFile">
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<div collapsible-footer *ngIf="!showLoading" slot="fixed">
|
||||
<div collapsible-footer appearOnBottom *ngIf="!showLoading" slot="fixed">
|
||||
<div class="list-item-limited-width" *ngIf="url && !shouldIframe && (!shouldEmbed || !isOther)">
|
||||
<ion-button expand="block" (click)="go()" class="ion-margin ion-text-wrap">
|
||||
<ion-icon name="fas-link" slot="start" aria-hidden="true"></ion-icon>
|
||||
|
|
|
@ -58,8 +58,8 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_workshop.conclusion' | translate }}</h2>
|
||||
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.conclusion"
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
<core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id"
|
||||
[text]="workshop!.conclusion" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -91,8 +91,8 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_workshop.areainstructauthors' | translate }}</h2>
|
||||
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id" [text]="workshop!.instructauthors"
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
<core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id"
|
||||
[text]="workshop!.instructauthors" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -141,7 +141,7 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>{{ 'addon.mod_workshop.areainstructreviewers' | translate }}</h2>
|
||||
<core-format-text [maxHeight]="120" [component]="component" [componentId]="module.id"
|
||||
<core-format-text [collapsible-item]="120" [component]="component" [componentId]="module.id"
|
||||
[text]="workshop!.instructreviewers" contextLevel="module" [contextInstanceId]="module.id"
|
||||
[courseId]="courseId">
|
||||
</core-format-text>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<core-format-text [text]="notification.mobiletext | coreCreateLinks" contextLevel="system" [contextInstanceId]="0"
|
||||
[maxHeight]="120">
|
||||
[collapsible-item]="120">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ScrollDetail } from '@ionic/core';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
|
@ -34,8 +34,11 @@ import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@sin
|
|||
})
|
||||
export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||
|
||||
@Input() appearOnBottom = false;
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected initialHeight = 0;
|
||||
protected finalHeight = 0;
|
||||
protected initialPaddingBottom = '0px';
|
||||
protected previousTop = 0;
|
||||
protected previousHeight = 0;
|
||||
|
@ -58,6 +61,12 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
|
||||
// Set a minimum height value.
|
||||
this.initialHeight = this.element.getBoundingClientRect().height || 48;
|
||||
const moduleNav = this.element.querySelector('core-course-module-navigation');
|
||||
if (moduleNav) {
|
||||
this.element.classList.add('has-module-nav');
|
||||
this.finalHeight = this.initialHeight - (moduleNav.getBoundingClientRect().height);
|
||||
}
|
||||
|
||||
this.previousHeight = this.initialHeight;
|
||||
|
||||
this.content?.style.setProperty('--core-collapsible-footer-max-height', this.initialHeight + 'px');
|
||||
|
@ -127,12 +136,12 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
*/
|
||||
protected onScroll(scrollDetail: ScrollDetail, scrollElement: HTMLElement): void {
|
||||
const maxScroll = scrollElement.scrollHeight - scrollElement.offsetHeight;
|
||||
if (scrollDetail.scrollTop <= 0 || scrollDetail.scrollTop >= maxScroll) {
|
||||
if (scrollDetail.scrollTop <= 0 || (this.appearOnBottom && scrollDetail.scrollTop >= maxScroll)) {
|
||||
// Reset.
|
||||
this.setBarHeight(this.initialHeight);
|
||||
} else {
|
||||
let newHeight = this.previousHeight - (scrollDetail.scrollTop - this.previousTop);
|
||||
newHeight = CoreMath.clamp(newHeight, 0, this.initialHeight);
|
||||
newHeight = CoreMath.clamp(newHeight, this.finalHeight, this.initialHeight);
|
||||
|
||||
this.setBarHeight(newHeight);
|
||||
}
|
||||
|
@ -149,12 +158,14 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
clearTimeout(this.endAnimationTimeout);
|
||||
}
|
||||
|
||||
this.element.classList.toggle('footer-collapsed', height <= 0);
|
||||
this.element.classList.toggle('footer-expanded', height >= this.initialHeight);
|
||||
const collapsed = height <= this.finalHeight;
|
||||
const expanded = height >= this.initialHeight;
|
||||
this.element.classList.toggle('footer-collapsed', collapsed);
|
||||
this.element.classList.toggle('footer-expanded', expanded);
|
||||
this.content?.style.setProperty('--core-collapsible-footer-height', height + 'px');
|
||||
this.previousHeight = height;
|
||||
|
||||
if (height > 0 && height < this.initialHeight) {
|
||||
if (!collapsed && !expanded) {
|
||||
// Finish opening or closing the bar.
|
||||
this.endAnimationTimeout = window.setTimeout(() => this.endAnimation(height), 500);
|
||||
}
|
||||
|
@ -166,7 +177,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
* @param height Last height used.
|
||||
*/
|
||||
protected endAnimation(height: number): void {
|
||||
const newHeight = height < this.initialHeight / 2 ? 0 : this.initialHeight;
|
||||
const newHeight = (height - this.finalHeight) < (this.initialHeight - this.finalHeight) / 2
|
||||
? this.finalHeight
|
||||
: this.initialHeight;
|
||||
|
||||
this.setBarHeight(newHeight);
|
||||
}
|
||||
|
@ -181,6 +194,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
|||
|
||||
this.listenScrollEvents();
|
||||
|
||||
// Only if not present or explicitly falsy it will be false.
|
||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||
|
||||
// Recalculate the height if a parent core-loading displays the content.
|
||||
this.loadingChangedListener =
|
||||
CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, async (data: CoreEventLoadingChangedData) => {
|
||||
|
|
|
@ -363,8 +363,7 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
}
|
||||
|
||||
const scrollableHeight = contentScroll.scrollHeight - contentScroll.clientHeight;
|
||||
const collapsedHeight = expandedHeaderHeight - (expandedHeader.clientHeight ?? 0);
|
||||
const frozen = scrollableHeight + collapsedHeight <= 2 * expandedHeaderHeight;
|
||||
const frozen = scrollableHeight <= scrollingHeight;
|
||||
const progress = frozen
|
||||
? 0
|
||||
: CoreMath.clamp(contentScroll.scrollTop / scrollingHeight, 0, 1);
|
||||
|
@ -377,9 +376,9 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
.entries(progress > .5 ? collapsedFontStyles : expandedFontStyles)
|
||||
.forEach(([property, value]) => floatingTitle.style.setProperty(property, value as string));
|
||||
|
||||
if (progress > 0 || progress < 1) {
|
||||
if (progress > 0 && progress < 1) {
|
||||
// Finish opening or closing the bar.
|
||||
this.endAnimationTimeout = window.setTimeout(() => this.endAnimation(progress), 500);
|
||||
this.endAnimationTimeout = window.setTimeout(() => this.endAnimation(progress, contentScroll.scrollTop), 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -388,8 +387,9 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
* End of animation when stop scrolling.
|
||||
*
|
||||
* @param progress Progress.
|
||||
* @param scrollTop Current ScrollTop position.
|
||||
*/
|
||||
protected endAnimation(progress: number): void {
|
||||
protected endAnimation(progress: number, scrollTop: number): void {
|
||||
if(!this.page) {
|
||||
return;
|
||||
}
|
||||
|
@ -398,6 +398,14 @@ export class CoreCollapsibleHeaderDirective implements OnInit, OnChanges, OnDest
|
|||
|
||||
this.page.style.setProperty('--collapsible-header-progress', collapse ? '1' : '0');
|
||||
this.page.classList.toggle('is-collapsed', collapse);
|
||||
|
||||
if (collapse && this.scrollingHeight && this.scrollingHeight > 0 && scrollTop < this.scrollingHeight) {
|
||||
this.content?.scrollToPoint(null, this.scrollingHeight);
|
||||
}
|
||||
|
||||
if (!collapse && this.scrollingHeight && this.scrollingHeight > 0 && scrollTop > 0) {
|
||||
this.content?.scrollToPoint(null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
protected toggleExpandEnabled = false;
|
||||
protected expanded = false;
|
||||
protected maxHeight = defaultMaxHeight;
|
||||
protected expandedHeight = 0;
|
||||
protected loadingChangedListener?: CoreEventObserver;
|
||||
|
||||
constructor(el: ElementRef<HTMLElement>) {
|
||||
|
@ -72,9 +73,10 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
this.element.classList.add('collapsible-item');
|
||||
|
||||
// Calculate the height now.
|
||||
await this.calculateHeight();
|
||||
setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong.
|
||||
|
||||
// Recalculate the height if a parent core-loading displays the content.
|
||||
this.loadingChangedListener =
|
||||
|
@ -82,7 +84,6 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) {
|
||||
// The element is inside the loading, re-calculate the height.
|
||||
await this.calculateHeight();
|
||||
setTimeout(() => this.calculateHeight(), 200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -93,9 +94,15 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
* @param element Element.
|
||||
*/
|
||||
protected async waitFormatTextsRendered(element: Element): Promise<void> {
|
||||
const formatTexts = Array
|
||||
.from(element.querySelectorAll('core-format-text'))
|
||||
.map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective));
|
||||
let formatTextElements: HTMLElement[] = [];
|
||||
|
||||
if (this.element.tagName == 'CORE-FORMAT-TEXT') {
|
||||
formatTextElements = [this.element];
|
||||
} else {
|
||||
formatTextElements = Array.from(element.querySelectorAll('core-format-text'));
|
||||
}
|
||||
|
||||
const formatTexts = formatTextElements.map(element => CoreComponentsRegistry.resolve(element, CoreFormatTextDirective));
|
||||
|
||||
await Promise.all(formatTexts.map(formatText => formatText?.rendered()));
|
||||
}
|
||||
|
@ -103,22 +110,25 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
/**
|
||||
* Calculate the height and check if we need to display show more or not.
|
||||
*/
|
||||
protected async calculateHeight(): Promise<void> {
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
protected async calculateHeight(retries = 3): Promise<void> {
|
||||
// Remove max-height (if any) to calculate the real height.
|
||||
const initialMaxHeight = this.element.style.maxHeight;
|
||||
this.element.style.maxHeight = 'none';
|
||||
this.element.classList.add('collapsible-loading-height');
|
||||
|
||||
await this.waitFormatTextsRendered(this.element);
|
||||
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
const height = CoreDomUtils.getElementHeight(this.element) || 0;
|
||||
this.expandedHeight = CoreDomUtils.getElementHeight(this.element) || 0;
|
||||
|
||||
// Restore the max height now.
|
||||
this.element.style.maxHeight = initialMaxHeight;
|
||||
this.element.classList.remove('collapsible-loading-height');
|
||||
|
||||
// If cannot calculate height, shorten always.
|
||||
this.setExpandButtonEnabled(!height || height >= this.maxHeight);
|
||||
this.setExpandButtonEnabled(!this.expandedHeight || this.expandedHeight >= this.maxHeight);
|
||||
|
||||
if (this.expandedHeight == 0 && retries > 0) {
|
||||
setTimeout(() => this.calculateHeight(retries - 1), 200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,8 +173,11 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
protected setMaxHeight(maxHeight?: number): void {
|
||||
if (maxHeight) {
|
||||
this.element.style.setProperty('--max-height', maxHeight + buttonHeight + 'px');
|
||||
} else if (this.expandedHeight) {
|
||||
this.element.style.setProperty('--max-height', this.expandedHeight + 'px');
|
||||
} else {
|
||||
this.element.style.removeProperty('--max-height');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +208,7 @@ export class CoreCollapsibleItemDirective implements OnInit {
|
|||
*
|
||||
* @param e Click event.
|
||||
*/
|
||||
protected elementClicked(e: MouseEvent): void {
|
||||
elementClicked(e: MouseEvent): void {
|
||||
if (e.defaultPrevented) {
|
||||
// Ignore it if the event was prevented by some other listener.
|
||||
return;
|
||||
|
|
|
@ -22,10 +22,10 @@ import {
|
|||
SimpleChange,
|
||||
Optional,
|
||||
ViewContainerRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
|
||||
import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreIframeUtils, CoreIframeUtilsProvider } from '@services/utils/iframe';
|
||||
|
@ -40,6 +40,7 @@ import { CoreFilterDelegate } from '@features/filter/services/filter-delegate';
|
|||
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||
import { CoreCollapsibleItemDirective } from './collapsible-item';
|
||||
|
||||
/**
|
||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||
|
@ -55,6 +56,8 @@ import { CoreComponentsRegistry } from '@singletons/components-registry';
|
|||
})
|
||||
export class CoreFormatTextDirective implements OnChanges {
|
||||
|
||||
@ViewChild(CoreCollapsibleItemDirective) collapsible?: CoreCollapsibleItemDirective;
|
||||
|
||||
@Input() text?: string; // The text to format.
|
||||
@Input() siteId?: string; // Site ID to use.
|
||||
@Input() component?: string; // Component for CoreExternalContentDirective.
|
||||
|
@ -73,23 +76,18 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
@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..
|
||||
@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.
|
||||
* If you want to avoid this use class="inline" at the same time to use display: inline-block.
|
||||
*/
|
||||
@Input() maxHeight?: number;
|
||||
@Input() maxHeight?: number; // @deprecated on 4.0 Use collapsible-item directive instead.
|
||||
|
||||
@Output() afterRender: EventEmitter<void>; // Called when the data is rendered.
|
||||
@Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected expanded = false;
|
||||
protected loadingChangedListener?: CoreEventObserver;
|
||||
protected emptyText = '';
|
||||
protected contentSpan: HTMLElement;
|
||||
protected toggleExpandEnabled = false;
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
|
@ -115,6 +113,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.afterRender = new EventEmitter<void>();
|
||||
|
||||
this.element.addEventListener('click', this.elementClicked.bind(this));
|
||||
|
||||
this.siteId = this.siteId || CoreSites.getCurrentSiteId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,8 +122,6 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
*/
|
||||
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
||||
if (changes.text || changes.filter || changes.contextLevel || changes.contextInstanceId) {
|
||||
this.setExpandButtonEnabled(false);
|
||||
|
||||
this.formatAndRenderContents();
|
||||
}
|
||||
}
|
||||
|
@ -148,9 +146,13 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
* Apply CoreExternalContentDirective to a certain element.
|
||||
*
|
||||
* @param element Element to add the attributes to.
|
||||
* @return External content instance.
|
||||
* @return External content instance or undefined if siteId is not provided.
|
||||
*/
|
||||
protected addExternalContent(element: Element): CoreExternalContentDirective {
|
||||
protected addExternalContent(element: Element): CoreExternalContentDirective | undefined {
|
||||
if (!this.siteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Angular doesn't let adding directives dynamically. Create the CoreExternalContentDirective manually.
|
||||
const extContent = new CoreExternalContentDirective(new ElementRef(element));
|
||||
|
||||
|
@ -269,101 +271,6 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the height and check if we need to display show more or not.
|
||||
*/
|
||||
protected async calculateHeight(): Promise<void> {
|
||||
// @todo: Work on calculate this height better.
|
||||
if (!this.maxHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.rendered();
|
||||
|
||||
// Remove max-height (if any) to calculate the real height.
|
||||
const initialMaxHeight = this.element.style.maxHeight;
|
||||
this.element.style.maxHeight = 'none';
|
||||
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
const height = this.getElementHeight(this.element);
|
||||
|
||||
// Restore the max height now.
|
||||
this.element.style.maxHeight = initialMaxHeight;
|
||||
|
||||
// If cannot calculate height, shorten always.
|
||||
this.setExpandButtonEnabled(!height || height >= this.maxHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set max height to element.
|
||||
*
|
||||
* @param maxHeight Max height if collapsed or undefined if expanded.
|
||||
*/
|
||||
protected setMaxHeight(maxHeight?: number): void {
|
||||
if (maxHeight) {
|
||||
this.element.style.setProperty('--max-height', maxHeight + 'px');
|
||||
} else {
|
||||
this.element.style.removeProperty('--max-height');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if expand button is enabled or not.
|
||||
*
|
||||
* @param enable Wether enable or disable.
|
||||
*/
|
||||
protected setExpandButtonEnabled(enable: boolean): void {
|
||||
this.toggleExpandEnabled = enable;
|
||||
this.element.classList.toggle('collapsible-enabled', enable);
|
||||
|
||||
if (!enable || this.element.querySelector('ion-button.collapsible-toggle')) {
|
||||
this.setMaxHeight(!enable || this.expanded? undefined : this.maxHeight);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add expand/collapse buttons
|
||||
const toggleButton = document.createElement('ion-button');
|
||||
toggleButton.classList.add('collapsible-toggle');
|
||||
toggleButton.setAttribute('fill', 'clear');
|
||||
|
||||
const toggleText = document.createElement('span');
|
||||
toggleText.classList.add('collapsible-toggle-text');
|
||||
toggleText.classList.add('sr-only');
|
||||
toggleButton.appendChild(toggleText);
|
||||
|
||||
const expandArrow = document.createElement('span');
|
||||
expandArrow.classList.add('collapsible-toggle-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('collapsible-collapsed', !expand);
|
||||
this.setMaxHeight(!expand? this.maxHeight: undefined);
|
||||
|
||||
const toggleButton = this.element.querySelector('ion-button.collapsible-toggle');
|
||||
const toggleText = toggleButton?.querySelector('.collapsible-toggle-text');
|
||||
if (!toggleButton || !toggleText) {
|
||||
return;
|
||||
}
|
||||
toggleText.innerHTML = expand ? Translate.instant('core.showless') : Translate.instant('core.showmore');
|
||||
toggleButton.setAttribute('aria-expanded', expand ? 'true' : 'false');
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to call when the element is clicked.
|
||||
*
|
||||
|
@ -385,24 +292,18 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.toggleExpandEnabled) {
|
||||
// Nothing to do on click, just stop.
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.toggleExpand();
|
||||
this.collapsible?.elementClicked(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish the rendering, displaying the element again and calling afterRender.
|
||||
*/
|
||||
protected finishRender(): void {
|
||||
protected async finishRender(): Promise<void> {
|
||||
// Show the element again.
|
||||
this.element.classList.remove('core-format-text-loading');
|
||||
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
// Emit the afterRender output.
|
||||
this.afterRender.emit();
|
||||
}
|
||||
|
@ -413,15 +314,12 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
protected async formatAndRenderContents(): Promise<void> {
|
||||
if (!this.text) {
|
||||
this.contentSpan.innerHTML = this.emptyText; // Remove current contents.
|
||||
this.finishRender();
|
||||
|
||||
await this.finishRender();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// In AOT the inputs and ng-reflect aren't in the DOM sometimes. Add them so styles are applied.
|
||||
if (this.maxHeight && !this.element.getAttribute('maxHeight')) {
|
||||
this.element.setAttribute('maxHeight', String(this.maxHeight));
|
||||
}
|
||||
if (!this.element.getAttribute('singleLine')) {
|
||||
this.element.setAttribute('singleLine', String(CoreUtils.isTrueOrOne(this.singleLine)));
|
||||
}
|
||||
|
@ -434,36 +332,22 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.element.classList.add('core-disable-media-adapt');
|
||||
|
||||
this.contentSpan.innerHTML = ''; // Remove current contents.
|
||||
if (this.maxHeight && result.div.innerHTML != '') {
|
||||
|
||||
// Move the children to the current element to be able to calculate the height.
|
||||
CoreDomUtils.moveChildren(result.div, this.contentSpan);
|
||||
// Move the children to the current element to be able to calculate the height.
|
||||
CoreDomUtils.moveChildren(result.div, this.contentSpan);
|
||||
|
||||
// Calculate the height now.
|
||||
this.calculateHeight();
|
||||
setTimeout(() => this.calculateHeight(), 200); // Try again, sometimes the first calculation is wrong.
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
// Add magnifying glasses to images.
|
||||
this.addMagnifyingGlasses();
|
||||
|
||||
if (!this.loadingChangedListener) {
|
||||
// Recalculate the height if a parent core-loading displays the content.
|
||||
this.loadingChangedListener =
|
||||
CoreEvents.on(CoreEvents.CORE_LOADING_CHANGED, (data: CoreEventLoadingChangedData) => {
|
||||
if (data.loaded && CoreDomUtils.closest(this.element.parentElement, '#' + data.uniqueId)) {
|
||||
// The format-text is inside the loading, re-calculate the height.
|
||||
this.calculateHeight();
|
||||
setTimeout(() => this.calculateHeight(), 200);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
CoreDomUtils.moveChildren(result.div, this.contentSpan);
|
||||
|
||||
// Add magnifying glasses to images.
|
||||
this.addMagnifyingGlasses();
|
||||
// Use collapsible-item directive instead.
|
||||
if (this.maxHeight && !this.collapsible) {
|
||||
this.collapsible = new CoreCollapsibleItemDirective(new ElementRef(this.element));
|
||||
this.collapsible.height = this.maxHeight;
|
||||
this.collapsible.ngOnInit();
|
||||
}
|
||||
|
||||
// Add magnifying glasses to images.
|
||||
this.addMagnifyingGlasses();
|
||||
|
||||
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.
|
||||
CoreFilterDelegate.handleHtml(
|
||||
|
@ -479,7 +363,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
}
|
||||
|
||||
this.element.classList.remove('core-disable-media-adapt');
|
||||
this.finishRender();
|
||||
await this.finishRender();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -581,7 +465,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.addMediaAdaptClass(img);
|
||||
|
||||
const externalImage = this.addExternalContent(img);
|
||||
if (!externalImage.invalid) {
|
||||
if (externalImage && !externalImage.invalid) {
|
||||
externalImages.push(externalImage);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<ion-card *ngIf="description">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" [maxHeight]="120"
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" [collapsible-item]="120"
|
||||
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
|
|
|
@ -13,14 +13,6 @@
|
|||
<ng-content select="[title]"></ng-content>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="description">
|
||||
<ion-label>
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
|
||||
[contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="expandDescription ? null : 120">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ng-content select="[description]"></ng-content>
|
||||
|
||||
<!-- Module completion. -->
|
||||
<ion-item class="ion-text-wrap"
|
||||
|
@ -51,6 +43,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="description">
|
||||
<ion-label>
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
|
||||
[contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="expandDescription ? null : 120">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ng-content select="[description]"></ng-content>
|
||||
|
||||
<ng-content></ng-content>
|
||||
|
||||
<!-- Activity has something offline. -->
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
<ion-col size="auto" class="ion-no-padding core-course-module-navigation-arrow">
|
||||
<ion-button fill="clear" class="core-course-previous-module" [disabled]="!previousModule" (click)="goToActivity(false)"
|
||||
[attr.aria-label]="'core.course.gotopreviousactivity' | translate">
|
||||
<ion-icon name="fas-arrow-left" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
<ion-icon name="fas-arrow-left" slot="start" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.previous' | translate }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col size="auto" class="ion-no-padding core-course-module-navigation-arrow">
|
||||
<ion-button fill="clear" class="core-course-next-module" [disabled]="!nextModule" (click)="goToActivity(true)"
|
||||
[attr.aria-label]="'core.course.gotonextactivity' | translate">
|
||||
<ion-icon name="fas-arrow-right" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
{{ 'core.next' | translate }}
|
||||
<ion-icon name="fas-arrow-right" slot="end" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
{{ 'core.description' | translate}}
|
||||
</p>
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" contextLevel="module"
|
||||
[contextInstanceId]="module.id" [courseId]="courseId" [maxHeight]="120">
|
||||
[contextInstanceId]="module.id" [courseId]="courseId" [collapsible-item]="120">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -169,7 +169,7 @@
|
|||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.grades.feedback' | translate}}</p>
|
||||
<p>
|
||||
<core-format-text [maxHeight]="120" [text]="grade.feedback" contextLevel="course"
|
||||
<core-format-text [collapsible-item]="120" [text]="grade.feedback" contextLevel="course"
|
||||
[contextInstanceId]="courseId">
|
||||
</core-format-text>
|
||||
</p>
|
||||
|
|
|
@ -72,7 +72,8 @@
|
|||
<p class="item-heading">
|
||||
{{'core.summary' | translate}}
|
||||
</p>
|
||||
<core-format-text [text]="course.summary" [maxHeight]="120" contextLevel="course" [contextInstanceId]="course.id">
|
||||
<core-format-text [text]="course.summary" [collapsible-item]="120" contextLevel="course"
|
||||
[contextInstanceId]="course.id">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -104,7 +105,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" [collapsible-item]="120" contextLevel="course"
|
||||
[contextInstanceId]="course.id">
|
||||
</core-format-text>
|
||||
</span>
|
||||
|
|
|
@ -45,6 +45,6 @@
|
|||
<core-course-unsupported-module *ngIf="unsupported" [module]="module"></core-course-unsupported-module>
|
||||
</core-loading>
|
||||
|
||||
<core-course-module-navigation collapsible-footer [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
|
||||
<core-course-module-navigation collapsible-footer appearOnBottom [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
|
||||
</core-course-module-navigation>
|
||||
</ion-content>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
</core-format-text>
|
||||
</p>
|
||||
<p *ngIf="currentCategory.description">
|
||||
<core-format-text [text]="currentCategory.description" [maxHeight]="120" contextLevel="coursecat"
|
||||
<core-format-text [text]="currentCategory.description" [collapsible-item]="120" contextLevel="coursecat"
|
||||
[contextInstanceId]="currentCategory.id"></core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
</td>
|
||||
<td *ngIf="column.name === 'feedback' && row.feedback !== undefined"
|
||||
class="ion-text-start core-grades-table-feedback" [class.ion-hide-md-down]="column.hiddenPhone">
|
||||
<core-format-text [maxHeight]="120" [text]="row.feedback" contextLevel="course"
|
||||
<core-format-text [collapsible-item]="120" [text]="row.feedback" contextLevel="course"
|
||||
[contextInstanceId]="courseId">
|
||||
</core-format-text>
|
||||
</td>
|
||||
|
@ -124,7 +124,7 @@
|
|||
<ion-label>
|
||||
<h2>{{ 'core.grades.feedback' | translate}}</h2>
|
||||
<p>
|
||||
<core-format-text [maxHeight]="120" [text]="row.feedback" contextLevel="course"
|
||||
<core-format-text [collapsible-item]="120" [text]="row.feedback" contextLevel="course"
|
||||
[contextInstanceId]="courseId">
|
||||
</core-format-text>
|
||||
</p>
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
(onLoadingContent)="contentLoading()">
|
||||
</core-site-plugins-plugin-content>
|
||||
|
||||
<core-course-module-navigation collapsible-footer *ngIf="module" [courseId]="courseId" [currentModuleId]="module.id">
|
||||
<core-course-module-navigation collapsible-footer [appearOnBottom]="collapsibleFooterAppearOnBottom" *ngIf="module" [courseId]="courseId"
|
||||
[currentModuleId]="module.id">
|
||||
</core-course-module-navigation>
|
||||
|
|
|
@ -85,6 +85,8 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
|
|||
*/
|
||||
size?: string;
|
||||
|
||||
collapsibleFooterAppearOnBottom = true;
|
||||
|
||||
displayOpenInBrowser = true;
|
||||
displayDescription = true;
|
||||
displayRefresh = true;
|
||||
|
@ -133,6 +135,8 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
|
|||
this.displaySize = !CoreUtils.isFalseOrZero(handlerSchema.displaysize);
|
||||
this.displayGrades = CoreUtils.isTrueOrOne(handlerSchema.displaygrades); // False by default.
|
||||
this.ptrEnabled = !CoreUtils.isFalseOrZero(handlerSchema.ptrenabled);
|
||||
|
||||
this.collapsibleFooterAppearOnBottom = !CoreUtils.isFalseOrZero(handlerSchema.isresource);
|
||||
}
|
||||
|
||||
// Get the data for the context menu.
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
.collapsible-item {
|
||||
--display-toggle: none;
|
||||
--max-height: none;
|
||||
|
||||
&.collapsible-loading-height {
|
||||
display: block !important;
|
||||
height: auto !important;
|
||||
--max-height: none !important;
|
||||
--display-toggle: none !important;
|
||||
}
|
||||
|
||||
.collapsible-toggle {
|
||||
display: var(--display-toggle);
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
&.collapsible-enabled {
|
||||
position: relative;
|
||||
padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit.
|
||||
--display-toggle: block;
|
||||
@include core-transition(height max-height, 500ms);
|
||||
height: calc(var(--max-height, auto));
|
||||
|
||||
.collapsible-toggle {
|
||||
position: absolute;
|
||||
@include position (null, 0, 0, null);
|
||||
text-align: center;
|
||||
z-index: 7;
|
||||
text-transform: none;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
background-color: var(--collapsible-toggle-background);
|
||||
color: var(--collapsible-toggle-text);
|
||||
min-height: var(--a11y-min-target-size);
|
||||
min-width: var(--a11y-min-target-size);
|
||||
--border-radius: var(--huge-radius);
|
||||
border-radius: var(--border-radius);
|
||||
--padding-start: 0px;
|
||||
--padding-end: 0px;
|
||||
margin: 0px;
|
||||
|
||||
.collapsible-toggle-arrow {
|
||||
width: var(--a11y-min-target-size);
|
||||
height: var(--a11y-min-target-size);
|
||||
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 14px 14px;
|
||||
transform: rotate(-90deg);
|
||||
|
||||
@include core-transition(transform, 500ms);
|
||||
|
||||
@include push-arrow-color(626262, true);
|
||||
|
||||
@include darkmode() {
|
||||
@include push-arrow-color(ffffff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsible-collapsed {
|
||||
overflow: hidden;
|
||||
min-height: calc(var(--collapsible-min-button-height) + 12px);
|
||||
|
||||
.collapsible-toggle-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
@include position(null, 0, 0, 0);
|
||||
background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px));
|
||||
background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px));
|
||||
z-index: 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,14 +48,6 @@ core-format-text {
|
|||
opacity: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.collapsible-toggle {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.collapsible-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.core-format-text-content {
|
||||
|
@ -68,33 +60,20 @@ core-format-text {
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&[maxHeight],
|
||||
&[ng-reflect-max-height] {
|
||||
&.collapsible-item {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
/* Force display inline */
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// This is to allow clicks in radio/checkbox content.
|
||||
&.collapsible-enabled {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
|
||||
@include collapsible-item();
|
||||
.core-format-text-content {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@if ($core-format-text-never-shorten) {
|
||||
&[maxHeight],
|
||||
&[ng-reflect-max-height] {
|
||||
&.collapsible-enabled.collapsible-expanded {
|
||||
max-height: none !important;
|
||||
@if ($core-format-text-never-shorten) {
|
||||
&.collapsible-enabled {
|
||||
--display-toggle: none !important;
|
||||
--max-height: none !important;
|
||||
|
||||
.collapsible-toggle {
|
||||
display: none !important;
|
||||
|
|
|
@ -226,78 +226,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin collapsible-item() {
|
||||
--display-toggle: none;
|
||||
.collapsible-toggle {
|
||||
display: var(--display-toggle);
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
&.collapsible-enabled {
|
||||
position:relative;
|
||||
padding-bottom: var(--collapsible-min-button-height); // So the Show less button can fit.
|
||||
--display-toggle: block;
|
||||
|
||||
.collapsible-toggle {
|
||||
position: absolute;
|
||||
@include position (null, 0, 0, null);
|
||||
text-align: center;
|
||||
z-index: 7;
|
||||
text-transform: none;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
background-color: var(--collapsible-toggle-background);
|
||||
color: var(--collapsible-toggle-text);
|
||||
min-height: var(--a11y-min-target-size);
|
||||
min-width: var(--a11y-min-target-size);
|
||||
--border-radius: var(--huge-radius);
|
||||
border-radius: var(--border-radius);
|
||||
--padding-start: 0px;
|
||||
--padding-end: 0px;
|
||||
margin: 0px;
|
||||
|
||||
.collapsible-toggle-arrow {
|
||||
width: var(--a11y-min-target-size);
|
||||
height: var(--a11y-min-target-size);
|
||||
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 14px 14px;
|
||||
transform: rotate(-90deg);
|
||||
|
||||
@include core-transition(transform, 500ms);
|
||||
|
||||
@include push-arrow-color(626262, true);
|
||||
|
||||
@include darkmode() {
|
||||
@include push-arrow-color(ffffff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsible-collapsed {
|
||||
overflow: hidden;
|
||||
min-height: calc(var(--collapsible-min-button-height) + 12px);
|
||||
max-height: calc(var(--max-height, auto));
|
||||
|
||||
.collapsible-toggle-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
@include position(null, 0, 0, 0);
|
||||
background: -webkit-linear-gradient(top, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px));
|
||||
background: linear-gradient(to bottom, rgba(var(--background-gradient-rgb), 0) calc(100% - 56px), rgba(var(--background-gradient-rgb), 1) calc(100% - 5px));
|
||||
z-index: 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color mixins.
|
||||
@function get_brightness($color) {
|
||||
@return (red($color) + green($color) + blue($color)) / 3;
|
||||
|
|
|
@ -1419,15 +1419,21 @@ ion-grid.core-no-grid > ion-row {
|
|||
right: 0;
|
||||
}
|
||||
|
||||
[collapsible-item] {
|
||||
@include collapsible-item();
|
||||
}
|
||||
|
||||
[collapsible-footer] {
|
||||
&.footer-collapsed {
|
||||
--core-collapsible-footer-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
&.has-module-nav.footer-collapsed {
|
||||
--core-collapsible-footer-height: auto;
|
||||
opacity: 1;
|
||||
core-course-module-navigation {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
@include core-transition(all, 200ms);
|
||||
}
|
||||
|
||||
}
|
||||
&.footer-expanded {
|
||||
--core-collapsible-footer-height: auto;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
/* Components */
|
||||
@import "./components/collapsible-header.scss";
|
||||
@import "./components/collapsible-item.scss";
|
||||
@import "./components/format-text.scss";
|
||||
@import "./components/rubrics.scss";
|
||||
@import "./components/mod-label.scss";
|
||||
|
|
Loading…
Reference in New Issue