diff --git a/scripts/langindex.json b/scripts/langindex.json index 3f8d345ee..db523decd 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2680,6 +2680,19 @@ "core.viewcode": "local_moodlemobileapp", "core.vieweditor": "local_moodlemobileapp", "core.viewembeddedcontent": "local_moodlemobileapp", + "core.viewer.decreasetextsize": "local_moodlemobileapp", + "core.viewer.default": "moodle", + "core.viewer.enterreadingmode": "local_moodlemobileapp", + "core.viewer.exitreadingmode": "local_moodlemobileapp", + "core.viewer.increasetextsize": "local_moodlemobileapp", + "core.viewer.openreadingmodesettings": "local_moodlemobileapp", + "core.viewer.readingthemeauto": "local_moodlemobileapp", + "core.viewer.readingthemedark": "local_moodlemobileapp/core.settings.colorscheme-dark", + "core.viewer.readingthemehcm": "local_moodlemobileapp", + "core.viewer.readingthemelight": "local_moodlemobileapp/core.settings.colorscheme-light", + "core.viewer.readingthemesepia": "local_moodlemobileapp", + "core.viewer.showmedia": "zoom", + "core.viewer.theme": "moodle", "core.viewprofile": "moodle", "core.wanttochangesite": "local_moodlemobileapp", "core.warningofflinedatadeleted": "local_moodlemobileapp", diff --git a/src/addons/mod/book/pages/contents/contents.html b/src/addons/mod/book/pages/contents/contents.html index 22501f112..83191f3c5 100644 --- a/src/addons/mod/book/pages/contents/contents.html +++ b/src/addons/mod/book/pages/contents/contents.html @@ -24,7 +24,7 @@
-
+
diff --git a/src/addons/mod/book/tests/behat/snapshots/test-basic-usage-of-book-activity-in-app-open-chapters-from-table-of-contents_11.png b/src/addons/mod/book/tests/behat/snapshots/test-basic-usage-of-book-activity-in-app-open-chapters-from-table-of-contents_11.png index 2f932c02b..416674093 100644 Binary files a/src/addons/mod/book/tests/behat/snapshots/test-basic-usage-of-book-activity-in-app-open-chapters-from-table-of-contents_11.png and b/src/addons/mod/book/tests/behat/snapshots/test-basic-usage-of-book-activity-in-app-open-chapters-from-table-of-contents_11.png differ diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index a8eaf4b32..eb99ae3e6 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -19,7 +19,7 @@ -
+
diff --git a/src/core/directives/directives.module.ts b/src/core/directives/directives.module.ts index 6225f56a1..f26fc2ab2 100644 --- a/src/core/directives/directives.module.ts +++ b/src/core/directives/directives.module.ts @@ -35,6 +35,7 @@ import { CoreContentDirective } from './content'; import { CoreUpdateNonReactiveAttributesDirective } from './update-non-reactive-attributes'; import { CoreUserTourDirective } from './user-tour'; import { CoreIonDatetimeDirective } from './datetime'; +import { CoreReadingModeDirective } from './reading-mode'; @NgModule({ declarations: [ @@ -59,6 +60,7 @@ import { CoreIonDatetimeDirective } from './datetime'; CoreUpdateNonReactiveAttributesDirective, CoreUserTourDirective, CoreIonDatetimeDirective, + CoreReadingModeDirective, ], exports: [ CoreAutoFocusDirective, @@ -82,6 +84,7 @@ import { CoreIonDatetimeDirective } from './datetime'; CoreUpdateNonReactiveAttributesDirective, CoreUserTourDirective, CoreIonDatetimeDirective, + CoreReadingModeDirective, ], }) export class CoreDirectivesModule {} diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 800756f0a..93379a63e 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -67,7 +67,7 @@ import { CorePromiseUtils } from '@singletons/promise-utils'; * Please use this directive if your text needs to be filtered or it can contain links or media (images, audio, video). * * Example usage: - * + * */ @Directive({ selector: 'core-format-text', diff --git a/src/core/directives/reading-mode.ts b/src/core/directives/reading-mode.ts new file mode 100644 index 000000000..0534e601c --- /dev/null +++ b/src/core/directives/reading-mode.ts @@ -0,0 +1,202 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + AfterViewInit, + Directive, + ElementRef, + OnDestroy, +} from '@angular/core'; + +import { Translate } from '@singletons'; +import { CoreIcons } from '@singletons/icons'; +import { CoreDom } from '@singletons/dom'; +import { CoreWait } from '@singletons/wait'; +import { CoreCancellablePromise } from '@classes/cancellable-promise'; +import { CoreModals } from '@services/modals'; +import { CoreViewer } from '@features/viewer/services/viewer'; + +/** + * Directive to add the reading mode to the selected html tag. + * + * Example usage: + *
+ */ +@Directive({ + selector: '[core-reading-mode]', +}) +export class CoreReadingModeDirective implements AfterViewInit, OnDestroy { + + protected element: HTMLElement; + protected viewportPromise?: CoreCancellablePromise; + protected disabledStyles: HTMLStyleElement[] = []; + protected hiddenElements: HTMLElement[] = []; + protected renamedStyles: HTMLElement[] = []; + protected enabled = false; + protected contentEl?: HTMLIonContentElement; + + constructor( + element: ElementRef, + ) { + this.element = element.nativeElement; + this.viewportPromise = CoreDom.waitToBeInViewport(this.element); + } + + /** + * @inheritdoc + */ + async ngAfterViewInit(): Promise { + await this.viewportPromise; + await CoreWait.nextTick(); + this.addTextViewerButton(); + } + + /** + * Add text viewer button to enable the reading mode. + */ + protected async addTextViewerButton(): Promise { + const page = CoreDom.closest(this.element, '.ion-page'); + this.contentEl = page?.querySelector('ion-content') ?? undefined; + + const toolbar = page?.querySelector('ion-header ion-toolbar ion-buttons[slot="end"]'); + + if (!toolbar || toolbar.querySelector('.core-text-viewer-button')) { + return; + } + + this.contentEl?.classList.add('core-reading-mode-content'); + + const label = Translate.instant('core.viewer.enterreadingmode'); + const button = document.createElement('ion-button'); + + button.classList.add('core-text-viewer-button'); + button.setAttribute('aria-label', label); + button.setAttribute('fill', 'clear'); + + 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 = ``; + toolbar.appendChild(button); + + button.addEventListener('click', (e: Event) => { + if (!this.element.innerHTML) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + if (!this.enabled) { + this.enterReadingMode(); + } else { + this.showReadingSettings(); + } + + }); + } + + /** + * Enters the reading mode. + */ + protected async enterReadingMode(): Promise { + this.enabled = true; + CoreViewer.loadReadingModeSettings(); + + document.body.classList.add('core-reading-mode-enabled'); + + // Disable all styles in element. + this.disabledStyles = Array.from(this.element.querySelectorAll('style:not(disabled)')); + this.disabledStyles.forEach((style) => { + style.disabled = true; + }); + + // Rename style attributes on DOM elements. + this.renamedStyles = Array.from(this.element.querySelectorAll('*[style]')); + this.renamedStyles.forEach((element: HTMLElement) => { + this.renamedStyles.push(element); + element.setAttribute('data-original-style', element.getAttribute('style') || ''); + element.removeAttribute('style'); + }); + + // Navigate to parent hidding all other elements. + let currentChild = this.element; + let parent = currentChild.parentElement; + while (parent && parent.tagName.toLowerCase() !== 'ion-content') { + Array.from(parent.children).forEach((child: HTMLElement) => { + if (child !== currentChild && child.tagName.toLowerCase() !== 'swiper-slide') { + this.hiddenElements.push(child); + child.classList.add('hide-on-reading-mode'); + } + }); + + currentChild = parent; + parent = currentChild.parentElement; + } + } + + /** + * Disable the reading mode. + */ + protected async disableReadingMode(): Promise { + this.enabled = false; + document.body.classList.remove('core-reading-mode-enabled'); + + // Enable all styles in element. + this.disabledStyles.forEach((style) => { + style.disabled = false; + }); + this.disabledStyles = []; + + // Rename style attributes on DOM elements. + this.renamedStyles.forEach((element) => { + element.setAttribute('style', element.getAttribute('data-original-style') || ''); + element.removeAttribute('data-original-style'); + }); + this.renamedStyles = []; + + this.hiddenElements.forEach((element) => { + element.classList.remove('hide-on-reading-mode'); + }); + this.hiddenElements = []; + } + + /** + * Show the reading settings. + */ + protected async showReadingSettings(): Promise { + const { CoreReadingModeSettingsModalComponent } = + await import('@features/viewer/components/reading-mode-settings/reading-mode-settings'); + + const exit = await CoreModals.openModal({ + component: CoreReadingModeSettingsModalComponent, + initialBreakpoint: 1, + breakpoints: [0, 1], + cssClass: 'core-modal-auto-height', + }); + + if (exit) { + this.disableReadingMode(); + } + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.disableReadingMode(); + this.viewportPromise?.cancel(); + } + +} diff --git a/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.html b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.html new file mode 100644 index 000000000..db01839d5 --- /dev/null +++ b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.html @@ -0,0 +1,53 @@ + + + + + + + + + + + +
{{ 'core.settings.fontsize' | translate }}
+
+
{{settings.zoom}}% @if (defaultZoom) { {{ 'core.viewer.default' | translate }} }
+
+ + + + + + + + + + + Theme + + + + @for (theme of themes; track $index; let last = $last;) { + + +
Aa
{{ 'core.viewer.readingtheme'+theme | translate }} +
+
+ } +
+ + +

{{ 'core.viewer.showmedia' | translate }}

+
+
+ + +
diff --git a/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.scss b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.scss new file mode 100644 index 000000000..93cf3faf0 --- /dev/null +++ b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.scss @@ -0,0 +1,55 @@ +@use "theme/globals" as *; + +ion-button ion-icon.zoom-decrease { + font-size: 1.5em; +} + +ion-button ion-icon.zoom-increase { + font-size: 2em; +} + +ion-radio.reading-theme { + + &::part(label) { + margin: 0px; + display: inline-flex; + align-items: center; + } + + .preview { + width: 40px; + height: 40px; + border-radius: 100%; + @include margin(4px, 16px, 4px, 2px); + text-align: center; + line-height: 40px; + font-size: #{dynamic-font(16px)}; + font-weight: bold; + border: 1px solid var(--stroke); + + &.auto { + background: linear-gradient(to right, #{$background-color-dark} 50%, #{$background-color} 50%); + color: #{$text-color}; + &::first-letter { + color: #{$text-color-dark}; + } + } + &.light { + background-color: #{$background-color}; + color: #{$text-color}; + } + &.dark { + background-color: #{$background-color-dark}; + color: #{$text-color-dark}; + } + &.sepia { + background-color: var(--core-reading-mode-sepia-background); + color: var(--core-reading-mode-sepia-text-color); + + } + &.hcm { + background-color: black; + color: white; + } + } +} diff --git a/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.ts b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.ts new file mode 100644 index 000000000..3bf67c5c4 --- /dev/null +++ b/src/core/features/viewer/components/reading-mode-settings/reading-mode-settings.ts @@ -0,0 +1,95 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreSharedModule } from '@/core/shared.module'; +import { Component, OnInit } from '@angular/core'; +import { + CORE_READING_MODE_DEFAULT_SETTINGS, + CoreViewerReadingModeThemes, + CoreViewerReadingModeThemesType, +} from '@features/viewer/constants'; +import { CoreViewer } from '@features/viewer/services/viewer'; + +import { ModalController } from '@singletons'; +import { CoreMath } from '@singletons/math'; + +/** + * Component to display a text modal. + */ +@Component({ + selector: 'core-reading-mode-settings-modal', + templateUrl: 'reading-mode-settings.html', + styleUrl: 'reading-mode-settings.scss', + standalone: true, + imports: [ + CoreSharedModule, + ], +}) +export class CoreReadingModeSettingsModalComponent implements OnInit { + + readonly MAX_TEXT_SIZE_ZOOM = 200; + readonly MIN_TEXT_SIZE_ZOOM = 75; + readonly TEXT_SIZE_ZOOM_STEP = 25; + + settings = CORE_READING_MODE_DEFAULT_SETTINGS; + + defaultZoom = true; + + themes: CoreViewerReadingModeThemesType[] = Object.values(CoreViewerReadingModeThemes); + + /** + * @inheritdoc + */ + async ngOnInit(): Promise { + this.settings = await CoreViewer.getReadingModeSettings(); + } + + /** + * Close modal. + */ + closeModal(): void { + ModalController.dismiss(); + } + + /** + * Close modal. + */ + exit(): void { + ModalController.dismiss(true); + } + + /** + * Change text size zoom. + * + * @param newTextSizeZoom New text size zoom. + */ + changeTextSizeZoom(newTextSizeZoom: number): void { + this.settings.zoom = CoreMath.clamp( + newTextSizeZoom, + this.MIN_TEXT_SIZE_ZOOM, + this.MAX_TEXT_SIZE_ZOOM, + ); + + this.defaultZoom = this.settings.zoom === 100; + this.onSettingChange(); + } + + /** + * Save settings on any change. + */ + onSettingChange(): void { + CoreViewer.setReadingModeSettings(this.settings); + } + +} diff --git a/src/core/features/viewer/constants.ts b/src/core/features/viewer/constants.ts new file mode 100644 index 000000000..3b56833d1 --- /dev/null +++ b/src/core/features/viewer/constants.ts @@ -0,0 +1,33 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { CoreViewerReadingModeSettings } from './services/viewer'; + +export const CORE_READING_MODE_SETTINGS = 'CoreReadingModeSettings'; + +export const CoreViewerReadingModeThemes = { + AUTO: 'auto', // eslint-disable-line @typescript-eslint/naming-convention + LIGHT: 'light', // eslint-disable-line @typescript-eslint/naming-convention + DARK: 'dark', // eslint-disable-line @typescript-eslint/naming-convention + SEPIA: 'sepia', // eslint-disable-line @typescript-eslint/naming-convention + HCM: 'hcm', // eslint-disable-line @typescript-eslint/naming-convention +} as const; + +export type CoreViewerReadingModeThemesType = typeof CoreViewerReadingModeThemes[keyof typeof CoreViewerReadingModeThemes]; + +export const CORE_READING_MODE_DEFAULT_SETTINGS: CoreViewerReadingModeSettings = { + zoom: 100, + showMultimedia: false, + theme: CoreViewerReadingModeThemes.HCM, +}; diff --git a/src/core/features/viewer/lang.json b/src/core/features/viewer/lang.json new file mode 100644 index 000000000..ab263e5ab --- /dev/null +++ b/src/core/features/viewer/lang.json @@ -0,0 +1,14 @@ +{ + "decreasetextsize": "Decrease text size", + "default": "(Default)", + "enterreadingmode": "Enter reading mode", + "exitreadingmode": "Exit reading mode", + "increasetextsize": "Increase text size", + "openreadingmodesettings": "Open reading mode settings", + "readingthemeauto": "Match app", + "readingthemedark": "Dark", + "readingthemehcm": "High contrast", + "readingthemelight": "Light", + "readingthemesepia": "Sepia", + "showmedia": "Show images and media" +} diff --git a/src/core/features/viewer/services/viewer.ts b/src/core/features/viewer/services/viewer.ts index 98b38f676..9efcfc277 100644 --- a/src/core/features/viewer/services/viewer.ts +++ b/src/core/features/viewer/services/viewer.ts @@ -15,10 +15,17 @@ import { ContextLevel } from '@/core/constants'; import { Injectable } from '@angular/core'; import { ModalOptions } from '@ionic/angular'; +import { CoreConfig } from '@services/config'; import { CoreModals } from '@services/modals'; import { CoreNavigator } from '@services/navigator'; import { CoreWSFile } from '@services/ws'; import { makeSingleton } from '@singletons'; +import { + CORE_READING_MODE_SETTINGS, + CoreViewerReadingModeThemes, + CoreViewerReadingModeThemesType, + CORE_READING_MODE_DEFAULT_SETTINGS, +} from '../constants'; /** * Viewer services. @@ -97,6 +104,49 @@ export class CoreViewerService { await CoreNavigator.navigateToSitePath('viewer/iframe', { params: { title, url, autoLogin } }); } + /** + * Get reading mode settings. + * + * @returns Reading mode settings. + */ + async getReadingModeSettings(): Promise { + return CoreConfig.getJSON(CORE_READING_MODE_SETTINGS, CORE_READING_MODE_DEFAULT_SETTINGS); + } + + /** + * Load and apply reading mode settings. + */ + async loadReadingModeSettings(): Promise { + const settings = await this.getReadingModeSettings(); + + this.applyReadingModeSettings(settings); + } + + /** + * Apply the reading mode settings to the DOM. + * + * @param settings Settings to apply. + */ + protected applyReadingModeSettings(settings: CoreViewerReadingModeSettings): void { + document.body.style.setProperty('--reading-mode-zoom', settings.zoom + '%'); + Object.values(CoreViewerReadingModeThemes).forEach((theme) => { + document.body.classList.remove(`core-reading-mode-theme-${theme}`); + }); + document.body.classList.add(`core-reading-mode-theme-${settings.theme}`); + document.body.classList.toggle('core-reading-mode-multimedia-hidden', !settings.showMultimedia); + } + + /** + * Save reading mode settings. + * + * @param settings Settings to save. + */ + async setReadingModeSettings(settings: CoreViewerReadingModeSettings): Promise { + await CoreConfig.setJSON(CORE_READING_MODE_SETTINGS, settings); + + this.applyReadingModeSettings(settings); + } + } export const CoreViewer = makeSingleton(CoreViewerService); @@ -114,3 +164,9 @@ export type CoreViewerTextOptions = { displayCopyButton?: boolean; // Whether to display a button to copy the text. modalOptions?: Partial; // Modal options. }; + +export type CoreViewerReadingModeSettings = { + zoom: number; // Zoom level. + showMultimedia: boolean; // Show images and multimedia. + theme: CoreViewerReadingModeThemesType; // Theme to use. +}; diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 5da40dbb9..c8c4165ee 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -346,7 +346,8 @@ export class CoreAppProvider { */ setSystemUIColors(): void { this.setStatusBarColor(); - this.setAndroidNavigationBarColor(); } + this.setAndroidNavigationBarColor(); + } /** * Set StatusBar color depending on platform. diff --git a/src/core/services/config.ts b/src/core/services/config.ts index aa27a80f2..53aa6b4f5 100644 --- a/src/core/services/config.ts +++ b/src/core/services/config.ts @@ -24,6 +24,7 @@ import { CoreDatabaseTable } from '@classes/database/database-table'; import { asyncInstance } from '../utils/async-instance'; import { CorePromisedValue } from '@classes/promised-value'; import { CoreBrowser } from '@singletons/browser'; +import { CoreText } from '@singletons/text'; declare module '@singletons/events' { @@ -118,6 +119,30 @@ export class CoreConfigProvider { } } + /** + * Get an app setting with json format + * + * @param name The config name. + * @param defaultValue Default value to use if the entry is not found. + * @returns Resolves upon success along with the config data. Reject on failure. + */ + async getJSON(name: string, defaultValue?: T): Promise { + try { + const configString = await CoreConfig.get(name); + if (!configString) { + throw new Error('Config not found'); + } + + return CoreText.parseJSON(configString, defaultValue); + } catch (error) { + if (defaultValue !== undefined) { + return defaultValue; + } + + throw error; + } + } + /** * Get an app setting directly from the database, without using any optimizations.. * @@ -152,12 +177,21 @@ export class CoreConfigProvider { * * @param name The config name. * @param value The config value. Can only store number or strings. - * @returns Promise resolved when done. */ async set(name: string, value: number | string): Promise { await this.table.insert({ name, value }); } + /** + * Set an app setting with json format. + * + * @param name The config name. + * @param value The config value. Can only store objects. + */ + async setJSON(name: string, value: unknown): Promise { + await this.set(name, JSON.stringify(value)); + } + /** * Update config with the given values. * diff --git a/src/core/tests/behat/snapshots/fontawesome-icons-are-correctly-shown-in-the-app-view-fontawesome-icons-in-the-app_7.png b/src/core/tests/behat/snapshots/fontawesome-icons-are-correctly-shown-in-the-app-view-fontawesome-icons-in-the-app_7.png index ce2463119..703afebe6 100644 Binary files a/src/core/tests/behat/snapshots/fontawesome-icons-are-correctly-shown-in-the-app-view-fontawesome-icons-in-the-app_7.png and b/src/core/tests/behat/snapshots/fontawesome-icons-are-correctly-shown-in-the-app-view-fontawesome-icons-in-the-app_7.png differ diff --git a/src/theme/components/ion-button.scss b/src/theme/components/ion-button.scss index 690456df3..0a22be6cb 100644 --- a/src/theme/components/ion-button.scss +++ b/src/theme/components/ion-button.scss @@ -105,6 +105,7 @@ ion-button { } ion-button, +ion-button.button, // Add specificity ion-fab-button, button, [role="button"] { diff --git a/src/theme/components/ion-modal.scss b/src/theme/components/ion-modal.scss index 93edc24e7..852ac0384 100644 --- a/src/theme/components/ion-modal.scss +++ b/src/theme/components/ion-modal.scss @@ -122,4 +122,21 @@ ion-modal { justify-content: space-between; } } + + &.core-modal-auto-height { + display: flex; + flex-direction: column; + justify-content: flex-end; + + &::part(content) { + position: relative; + display: block; + contain: content; + } + + .inner-content { + max-height: 80vh; + overflow: auto; + } + } } diff --git a/src/theme/components/reading-mode.scss b/src/theme/components/reading-mode.scss new file mode 100644 index 000000000..ed4ef820e --- /dev/null +++ b/src/theme/components/reading-mode.scss @@ -0,0 +1,57 @@ + +html { + --core-reading-mode-sepia-background: #f4ecd8; + --core-reading-mode-sepia-text-color: #5b4636; +} + +body.core-reading-mode-enabled { + .core-text-viewer-button { + --core-header-buttons-background: var(--stroke); + } + + &.core-reading-mode-theme-light { + --reading-mode-background: #{$background-color}; + --reading-mode-text-color: #{$text-color}; + } + + &.core-reading-mode-theme-dark { + --reading-mode-background: #{$background-color-dark}; + --reading-mode-text-color: #{$text-color-dark}; + } + + &.core-reading-mode-theme-sepia { + --reading-mode-background: var(--core-reading-mode-sepia-background); + --reading-mode-text-color: var(--core-reading-mode-sepia-text-color); + } + + &.core-reading-mode-theme-hcm { + --reading-mode-background: #000000; + --reading-mode-text-color: #ffffff; + } + + &.core-reading-mode-multimedia-hidden { + ion-content.core-reading-mode-content { + img, video, iframe { + display: none !important; + } + } + } + + ion-content.core-reading-mode-content { + --background: var(--reading-mode-background, --ion-background-color); + background: var(--background); + + [core-reading-mode] { + zoom: var(--reading-mode-zoom, 1); + &> * { + --text-color: var(--reading-mode-text-color, --text-color); + --color: var(--reading-mode-text-color, --text-color); + color: var(--text-color); + } + } + + .hide-on-reading-mode { + display: none !important; + } + } +} diff --git a/src/theme/theme.scss b/src/theme/theme.scss index e35ae118a..23181639d 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -21,6 +21,7 @@ @import "components/collapsible-header.scss"; @import "components/collapsible-item.scss"; @import "components/error-accordion.scss"; +@import "components/reading-mode.scss"; @import "components/format-text.scss"; @import "components/iframe.scss"; @import "components/mod-label.scss";