Skip to content

Commit

Permalink
text
Browse files Browse the repository at this point in the history
  • Loading branch information
crazyserver committed Nov 25, 2024
1 parent a5cec58 commit 804d897
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 10 deletions.
48 changes: 48 additions & 0 deletions src/core/directives/format-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,52 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
});
}

/**
* Add text viewer button to view text at full size.
*/
protected async addTextViewerButton(): Promise<void> {
if (this.singleLine || !this.text) {
return;
}
const count = CoreText.countWords(this.text);

if (count < 20) {
console.error(this.text, count);

Check failure on line 327 in src/core/directives/format-text.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement

return;
}
console.error(count);

Check failure on line 331 in src/core/directives/format-text.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected console statement

const label = Translate.instant('core.openfulltext');
const button = document.createElement('button');

button.classList.add('core-text-viewer-icon');
button.setAttribute('aria-label', label);
const iconName = 'book-open-reader';
const src = CoreIcons.getIconSrc('font-awesome', 'solid', iconName);
// Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed.
button.innerHTML = `<ion-icon name="fas-${iconName}" aria-hidden="true" src="${src}"></ion-icon>`;

button.addEventListener('click', (e: Event) => {
if (!this.text) {
return;
}

e.preventDefault();
e.stopPropagation();

CoreViewer.viewText(label, this.text, {
component: this.component,
componentId: this.componentId,
contextLevel: this.contextLevel,
courseId: this.courseId,
zoomMode: true,
});
});

this.element.appendChild(button);
}

/**
* Listener to call when the element is clicked.
*
Expand Down Expand Up @@ -396,6 +442,8 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
// Add magnifying glasses to images.
this.addImageViewerButton();

this.addTextViewerButton();

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(
Expand Down
38 changes: 31 additions & 7 deletions src/core/features/viewer/components/text/text.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<ion-header>
<ion-header *ngIf="!zoomMode">
<ion-toolbar>
<ion-title>
<h1>{{ title }}</h1>
Expand All @@ -10,17 +10,41 @@ <h1>{{ title }}</h1>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-content class="ion-padding" [ngStyle]="{zoom: zoom+'%'}">
<core-format-text [text]="content" [component]="component" [componentId]="componentId" [filter]="filter" [contextLevel]="contextLevel"
[contextInstanceId]="instanceId" [courseId]="courseId" />

<ion-card *ngIf="files?.length">
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="componentId" />
</ion-card>
</ion-content>
<ion-footer *ngIf="displayCopyButton">
<ion-button expand="block" fill="outline" (click)="copyText()">
<ion-icon name="fas-copy" aria-hidden="true" slot="start" />
{{ 'core.copytoclipboard' | translate }}
</ion-button>
<ion-footer>
<ion-row class="ion-justify-content-between ion-align-items-center ion-no-padding">
<ion-col size="auto">
<ion-button fill="clear" (click)="closeModal()" [ariaLabel]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-col>
<ion-col *ngIf="displayCopyButton" size="auto">
<ion-button expand="block" fill="outline" (click)="copyText()">
<ion-icon name="fas-copy" aria-hidden="true" slot="start" />
{{ 'core.copytoclipboard' | translate }}
</ion-button>
</ion-col>
@if (zoomMode) {
<ion-col>
<ion-range aria-label="Zoom" min="80" max="300" step="10" [value]="zoom" (ionInput)="changeZoom($event.detail.value)">
<ion-button slot="start" fill="clear" [ariaLabel]="'core.zoomout' | translate" (click)="changeZoom(zoom - 10)"
class="button-small">
<ion-icon name="fas-a" slot="icon-only" aria-hidden="true" />
</ion-button>
<ion-button slot="end" fill="clear" [ariaLabel]="'core.zoomin' | translate" (click)="changeZoom(zoom + 10)">
<ion-icon name="fas-a" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-range>
</ion-col>
} @else {
<ion-col />
}
</ion-row>
</ion-footer>
4 changes: 4 additions & 0 deletions src/core/features/viewer/components/text/text.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ion-footer {
padding: 6px;
}

core-format-text ::ng-deep button.core-text-viewer-icon {
display: none !important;
}
27 changes: 25 additions & 2 deletions src/core/features/viewer/components/text/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
import { ContextLevel } from '@/core/constants';
import { CoreSharedModule } from '@/core/shared.module';
import { toBoolean } from '@/core/transforms/boolean';
import { Component, Input } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CoreFileEntry } from '@services/file-helper';

import { CoreText } from '@singletons/text';
import { ModalController } from '@singletons';
import { CoreDom } from '@singletons/dom';
import { CoreMath } from '@singletons/math';

/**
* Modal component to render a certain text.
Expand All @@ -33,7 +35,7 @@ import { ModalController } from '@singletons';
CoreSharedModule,
],
})
export class CoreViewerTextComponent {
export class CoreViewerTextComponent implements OnInit {

@Input() title?: string; // Modal title.
@Input() content?: string; // Modal content.
Expand All @@ -45,6 +47,18 @@ export class CoreViewerTextComponent {
@Input() instanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
@Input({ transform: toBoolean }) displayCopyButton = false; // Whether to display a button to copy the contents.
@Input() zoomMode = false; // Whether to display zoom controls.

private static readonly MAX_ZOOM = 300;
private static readonly MIN_ZOOM = 80;

zoom = 100; // Zoom level.

ngOnInit(): void {
if (this.zoomMode) {
this.content = CoreDom.removeAllStyles(this.content || '');
}
}

/**
* Close modal.
Expand All @@ -60,4 +74,13 @@ export class CoreViewerTextComponent {
CoreText.copyToClipboard(this.content || '');
}

/**
* Zoom In or Out.
*
* @param zoomIn True to zoom in, false to zoom out.
*/
changeZoom(zoomIn: number): void {
this.zoom = CoreMath.clamp(zoomIn, CoreViewerTextComponent.MIN_ZOOM, CoreViewerTextComponent.MAX_ZOOM);
}

}
2 changes: 2 additions & 0 deletions src/core/features/viewer/services/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class CoreViewerService {
content,
...options,
};
modalOptions.cssClass = 'core-modal-transparent';

await CoreModals.openModal(modalOptions);
}
Expand Down Expand Up @@ -112,5 +113,6 @@ export type CoreViewerTextOptions = {
instanceId?: number; // The instance ID related to the context.
courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
displayCopyButton?: boolean; // Whether to display a button to copy the text.
zoomMode?: boolean; // Whether to display zoom controls.
modalOptions?: Partial<ModalOptions>; // Modal options.
};
26 changes: 26 additions & 0 deletions src/core/singletons/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,32 @@ export class CoreDom {
return css.replace(regExp, prefix + ' $1 $2');
}

/**
* Remove all classes from all the DOM.
*
* @returns Clean text.
*/
static removeAllStyles(text: string | undefined): string {
if (!text) {
return '';
}

const dom = convertTextToHTMLElement(text);

// Remove all classes from all the DOM.
dom.querySelectorAll('*').forEach((element) => {
element.className = '';
element.removeAttribute('style');
});

// Remove all style tags.
dom.querySelectorAll('style').forEach((element) => {
element.remove();
});

return dom.innerHTML;
}

}

/**
Expand Down
35 changes: 34 additions & 1 deletion src/theme/components/format-text.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ core-format-text {
}

core-format-text {
display: contents;
display: block;
position: relative;
user-select: text;
word-break: break-word;
word-wrap: break-word;
Expand Down Expand Up @@ -103,6 +104,38 @@ core-format-text {
}
}

button.core-text-viewer-icon {
position: absolute;
@include position(0px, 0px, null, null);
color: var(--ion-text-color);
border-radius: var(--mdl-shape-borderRadius-xl);
background-color: var(--core-format-text-viewer-icon-background);
display: flex;

width: var(--a11y-sizing-minTargetSize);
height: var(--a11y-sizing-minTargetSize);
max-width: var(--a11y-sizing-minTargetSize);
font-size: var(--mdl-typography-icon-fontSize-lg);

ion-icon {
flex: 1;
align-self: center;
/** Fix iOS icon size */
margin: 0 auto;
position: absolute;
left: 0;
right: 0;
}

&:hover {
opacity: .7;
}

&.hidden {
display: none;
}
}

// Disable clicks in links inside MathJax equations.
.filter_mathjaxloader_equation .MathJax_Preview a {
pointer-events: none;
Expand Down

0 comments on commit 804d897

Please sign in to comment.