UNPKG

@edugouvfr/ngx-dsfr-ext

Version:

NgxDsfrExt est une extension au package @edugouvfr/ngx-dsfr (portage Angular des éléments DSFR)

453 lines 97.3 kB
import { CommonModule } from '@angular/common'; import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, inject, Input, NgZone, Renderer2, ViewChild, ViewEncapsulation, } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DsfrButtonModule, DsfrFormSelectModule, DsfrI18nPipe, DsfrI18nService, DsfrSkiplinksModule, } from '@edugouvfr/ngx-dsfr'; import { UnfocusableDirective } from '../../directives/unfocusable.directive'; import { DsfrEditorService } from '../../editor.service'; import { DEFAULT_TOOLBAR_OPTIONS } from './editor-toolbar-default-options'; import { ControlMode, FORMAT_I18N_KEYS, FORMAT_ICON, FormatConst, } from './editor-toolbar.model'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@angular/forms"; import * as i3 from "@edugouvfr/ngx-dsfr"; export class EduEditorToolbarComponent { constructor() { /** * L'identifiant du tag hôte pour le composant éditeur. */ this.editorId = 'editor'; /** * Active la lecure seule. */ //TODO: @Input() readOnly = false; /** * Options de la toolbar. */ this.toolbarOptions = DEFAULT_TOOLBAR_OPTIONS; /** * Modèle de présentation spécifique pour les select (header+size). * RPA: je passe par un modèle de présentation spécifique et je n'utilise pas directement un binding sur * currentSelectionFormats car j'ai besoin de reflêter l'état de sélection utilisateur sinon voici ce qui ce passe : * l'utilisateur sélectionne une portion de texte et sélectionnz un niveau de titre H2, puis il place le curseur dans * un paragraphe quelconque, le select reste sur H2 car la propriété interne 'value' n'est pas mise à jour lors de * la sélection utilisateur (binding non bi-directionnel). C'est pourquoi il est nécessaire de passer par un modèle * de présentation dédié à la gestion des select. */ this.selectModel = { header: null, size: null }; /** * Permet de gérer l'affichage d'un outline custom lorsque la toolbar prend le focus. */ this.toolbarFocused = false; this.ngZone = inject(NgZone); this.renderer2 = inject(Renderer2); this.elementRef = inject(ElementRef); this.i18n = inject(DsfrI18nService); this.editorService = inject(DsfrEditorService); /** * Positionne l'image Base64 dans l'editeur. */ this._uploadImage = async (event) => { const target = event.target; if (target?.files) { this.performAction('image', target.files); } }; } ngOnInit() { this._sub = this.editorService.currentFormatting$.subscribe((formatting) => { this._updateToolbar(formatting); }); // Hors zone Angular pour des raisons de perf this.ngZone.runOutsideAngular(() => { this._unlistenFn = this.renderer2.listen('document', 'click', (e) => { this.clickOutside(e); }); }); } ngOnDestroy() { this._sub?.unsubscribe(); this._unlistenFn(); } /** * Handler sur l'input file associé à une inserion d'image. */ //FIXME : erreur dans la console : ParchmentError: [Parchment] Cannot wrap imag onImageInputFileChange(event) { this._uploadImage(event); } /** * Permet de conditionner la structure HTML selon le type contrôle. */ getButtonType(control) { let result = 'btn'; if (Array.isArray(control) && control.length) { result = 'group'; } else if ('values' in control && control.values && control.values.length) { switch (control.mode) { case ControlMode.RADIO: result = 'radio'; break; case ControlMode.SELECT: result = 'select'; break; default: break; } } return result; } /** * Retourne l'icône correspondant au format passé en paramètre. * * @param format le format à considérer * @param value la vaaleur à prendre en compte le cas échéant * @returns la classe css correspondant à l'icône */ getIcon(format, value = undefined) { let icon = FORMAT_ICON[format]; // la valeur peut être falsy d'où le test explicite sur not(undefined) if (icon && value !== undefined) { // on doit aller chercher l'icône en utilisant la valeur comme clé icon = icon[value]; } return icon; } /** * Retourne la clé du libellé internationalisé. * @param control le contôle de formatage */ getI18nKey(format, value = undefined) { let key = FORMAT_I18N_KEYS[format]; if (value !== undefined) { key = key; key = key[String(value)]; } return key; } /** * Détermine si l'option de format doit être activée ou pas. * * @param format le nom de contrôle de format * @param value la valeur de l'option considérée * @returns vrai si le couple format/value fait partie des formats de la sélection courante, faux sinon */ isValueActive(format, value) { // @ts-ignore (RPA) le test sur currentSelectionFormats est effectué à l'intérieur de la fonction isFormatActive return this.isFormatActive(format) ? this._currentSelectionFormats[format] === value : false; } /** * Retourne la liste des optiosn à affichier au sein du contrôle multivalué (select). * * @param control le contrôle multivalué à considéré */ getSelectOptions(control) { return control.values.map((v) => this._buildSelectOption(control.name, v)); } /** * Permet d'indiquer si le contrôle de ce format doit être activé au niveau de la barre d'outils. * * @param format le format à considérer vis-à-vis des formats de la sélection courante * @returns vrai si ce format doit être actif, faux sinon */ isFormatActive(format) { return this._currentSelectionFormats ? format in this._currentSelectionFormats : false; } /** * Permet de réagir aux actions utilisateur sur la toolbar. */ onToolbarControlPerform(event, format, value) { event.preventDefault(); this._applyFormat(format, value); } /** * Permet d'écouter la capture d'une couleur par l'utilisateur. */ onColorPickerChange(event, action) { const target = event.target; const value = target.value ?? undefined; if (value) { this.performAction(action, value); } } /** * Permet d'écouter les changement sur le format "list". * * @param action * @param event */ onListSelect(action, event) { this.performAction(action, event); } /** * Permet de forcer la prise de focus sur le premier élément focusable de la barre d'outil lorsque le conteneur de * la toolbar reçoit le focus. */ onToolbarFocus() { this.toolbarFocused = true; if (this.lastActiveControlElement) { this.lastActiveControlElement.focus(); } else { const nextElem = this.elementRef.nativeElement.querySelector('.item-editor, .fr-select'); nextElem?.focus(); } } /** * Permet de gérer la sortie de focus */ onToolbarBlur(event) { // si le focus est pris par un élément de contrôle, on conserve l'outline custom if (!event.currentTarget?.contains(event.relatedTarget)) { this.toolbarFocused = false; } } /** * Permey de se mettre à l'écoute des actions utilisateur sur la barre d'outils. */ onToolbarKeydown(event) { const key = event.key; switch (key) { case 'Tab': event.preventDefault(); // évite la perte de focus asynchrone sur l'éditeur due à la propagation de l'event this.editorService.requestFocusOnEditorContent(); this.toolbarFocused = false; break; case 'ArrowRight': case 'ArrowLeft': event.preventDefault(); // évite la prise de focus sur l'éditeur suite propagation event let focusableElt = key === 'ArrowRight' ? this.findNextSiblingControl() : this.findPrevSiblingControl(); if (focusableElt) { focusableElt.focus(); this.lastActiveControlElement = focusableElt; } break; default: break; } } performAction(action, value) { if (action) { this.editorService.requestFormatting({ action: action, value: value }); } else { this.editorService.requestFormatting(undefined); } this.toolbarFocused = false; } /** * Détecte le clic en dehors du composant pour retirer le marqueur de focus custom sur la toolbar. */ clickOutside(event) { // comme on s'est mis à l'écoute de l'event hors Angular pour des raisons de perf, on doit mettre à jour l'input // en s'exécutant dans la zone Angular this.ngZone.run(() => { if (!this.elementRef.nativeElement.contains(event.target)) { this.toolbarFocused = false; } }); } _buildSelectOption(format, control) { return { label: this._getOptionLabel(format, control.value), value: control.value }; } _getOptionLabel(format, value) { const record = FORMAT_I18N_KEYS[format]; const key = record[String(value)]; return this.i18n.t(key); } findNextSiblingControl() { return this.findSiblingControl('nextElementSibling', () => this.getFirstToolbarControl()); } findPrevSiblingControl() { return this.findSiblingControl('previousElementSibling', () => this.getLastToolbarControl()); } /** * Recherche l'élément de contrôle focusable adjaçant à l'élément de contrôle actif. * * @param siblingProperty La propriété permettant de récupérer le voisin suivant ou précédent (selon) * @param failoverFunction La fonction permettant de récupérer un élément focusable lorsqu'il n'y plus de suivant * @returns Une élément HTML sur lequel il sera possible de prendre le focus */ findSiblingControl(siblingProperty, failoverFunction) { let result = undefined; let activeElt = document.activeElement; //TODO: imbrication conditionnelle complexe > à mettre sous harnais de tests pour ensuite tenter une simplification if (activeElt) { if (activeElt.tagName === 'BUTTON') { if (activeElt.closest('dsfr-buttons-group')) { const dsfrButtonsGroup = activeElt.parentElement; if (dsfrButtonsGroup && dsfrButtonsGroup[siblingProperty]) // directly try to get next sibling result = dsfrButtonsGroup[siblingProperty]; } else if (activeElt.closest('span.group')) { const spanControl = activeElt.closest('span.control'); if (spanControl && spanControl[siblingProperty]) { // directly try to get parent span to get next sibling result = spanControl[siblingProperty]; } } } if (result === undefined) { while (activeElt.parentElement !== null && !activeElt.parentElement.classList.contains('editor-toolbar')) { activeElt = activeElt.parentElement; } if (activeElt.parentElement?.classList.contains('editor-toolbar')) { result = activeElt[siblingProperty]; } } } return result ? this.getFocusableControl(result, siblingProperty === 'previousElementSibling') : failoverFunction(); } getFirstToolbarControl() { return this.getFocusableControl(this.controlsParent.nativeElement); } getLastToolbarControl() { let lastOne = this.controlsParent.nativeElement.lastElementChild; return this.getFocusableControl(lastOne, true); } /** * Search focusable control (depth first traversal). */ getFocusableControl(elt, reverse = false) { if (elt.tagName === 'BUTTON' || elt.tagName === 'SELECT') { return elt; } else if (elt.children.length !== 0) { let children = Array.from(elt.children); if (reverse) { children = children.reverse(); } for (const c of children) { const focusable = this.getFocusableControl(c); if (focusable) { return focusable; } } } return undefined; } /** * Répercute les formats actifs de la sélection courante vers la toolbar. */ _updateToolbar(formats) { // on force l'exécution dans zonejs car l'événement provient de Quill et n'est pas géré par Angular this.ngZone.run(() => { this._currentSelectionFormats = formats; this.selectModel.header = this._getSelectedOption(FormatConst.HEADER); this.selectModel.size = this._getSelectedOption(FormatConst.SIZE); }); // on met à jour les états aria-pressed sur les boutons //TODO: à remplacer par un input ariaPressed sur dsfr-button cf. ngx-dsfr# const dsfrButtons = this.elementRef.nativeElement.querySelectorAll('dsfr-button'); dsfrButtons.forEach((n) => { const e = n; const controlInfo = e.getAttribute('data-testid')?.split('-'); if (controlInfo) { const pressed = controlInfo.length === 1 ? this.isFormatActive(controlInfo[0]) : this.isValueActive(controlInfo[0], controlInfo[1]); if (pressed) { e.querySelector('button')?.setAttribute('aria-pressed', 'true'); } else { e.querySelector('button')?.removeAttribute('aria-pressed'); } } }); } /** * Retourne la valeur à sélectionner dans le cas de contôle multivalué (select). * * @internal */ _getSelectedOption(format) { const debug = this._currentSelectionFormats && this._currentSelectionFormats[format] ? this._currentSelectionFormats[format] : null; return debug; } /** * Supprime toutes les mises en forme sur la sélection courante. */ _removeSelectionFormats() { this.performAction(undefined); } /** * Applique le format défini par l'utilisateur via la toolbar sur la sélection courante de l'éditeur. */ _applyFormat(control, value) { // Cas particulier du clean if (control.name === FormatConst.CLEAN) { this._removeSelectionFormats(); } else { const baseControl = control; switch (baseControl.mode) { case ControlMode.TOGGLE: // cas d'une toggle action, on détermine la valeur à transmettre à l'éditeur value = this._currentSelectionFormats && control.name in this._currentSelectionFormats ? false : true; break; case ControlMode.RADIO: // si l'action envoyée est déjà présente dans la sélection il faut envoyer "false" comme valeur value = this._currentSelectionFormats && control.name in this._currentSelectionFormats && this._currentSelectionFormats[control.name] === value ? false : value; break; case ControlMode.PROMPT: switch (baseControl.name) { case FormatConst.IMAGE: //TODO: à remplacer par quelque chose de plus sexy this._displayPromptElement('#prompt-' + FormatConst.IMAGE); break; case FormatConst.LINK: //TODO: à remplacer par quelque chose de plus sexy value = window.prompt(this.i18n.t('editor.controls.link.prompt')); break; case FormatConst.COLOR: this._displayPromptElement('#picker-' + FormatConst.COLOR); break; case FormatConst.BACKGROUND: this._displayPromptElement('#picker-' + FormatConst.BACKGROUND); break; default: break; } break; default: break; } // si on a pu déterminer une valeur, on demande l'application du format à l'éditeur if (value !== undefined) { this.performAction(control.name, value); } } } /** * Permet de capturer une couleur depuis un color-picker natif. * * @param selector le sélecteur permettant de requêter l'élément de contrôle complémentaire */ _displayPromptElement(selector) { const elem = document.querySelector(selector); if (elem) elem.click(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EduEditorToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: EduEditorToolbarComponent, isStandalone: true, selector: "edu-editor-toolbar", inputs: { editorId: "editorId", toolbarOptions: "toolbarOptions" }, viewQueries: [{ propertyName: "controlsParent", first: true, predicate: ["controlsParent"], descendants: true }], ngImport: i0, template: "<div\n #controlsParent\n role=\"toolbar\"\n class=\"editor-toolbar\"\n tabindex=\"0\"\n [attr.aria-controls]=\"editorId\"\n [ngClass]=\"{ 'editor-toolbar--focus': toolbarFocused }\"\n (focus)=\"onToolbarFocus()\"\n (blur)=\"onToolbarBlur($event)\"\n (keydown)=\"onToolbarKeydown($event)\">\n @for (control of toolbarOptions; track control) {\n <ng-container\n [ngTemplateOutlet]=\"tplcontrol\"\n [ngTemplateOutletContext]=\"{ type: getButtonType(control), control: control }\">\n </ng-container>\n }\n</div>\n\n<ng-template #tplcontrol let-type=\"type\" let-control=\"control\">\n @if (type === 'btn') {\n <ng-container [ngTemplateOutlet]=\"btn\" [ngTemplateOutletContext]=\"{ control: control }\"></ng-container>\n } @else if (type === 'group') {\n <span class=\"group\">\n @for (item of control; track item) {\n <ng-container [ngTemplateOutlet]=\"btn\" [ngTemplateOutletContext]=\"{ control: item }\"></ng-container>\n }\n </span>\n } @else if (type === 'radio') {\n <dsfr-buttons-group inline=\"always\" size=\"SM\" [ngClass]=\"{ group: control.group }\">\n @for (elem of control.values; track elem) {\n <dsfr-button\n unfocusable=\"button\"\n type=\"button\"\n variant=\"tertiary-no-outline\"\n size=\"SM\"\n customClass=\"item-editor\"\n [icon]=\"getIcon(control.name, elem.value)\"\n [tooltipMessage]=\"getI18nKey(control.name, elem.value) | dsfrI18n\"\n [attr.aria-pressed]=\"isValueActive(control.name, elem.value) || null\"\n (click)=\"onToolbarControlPerform($event, control, elem.value)\"\n (keydown.enter)=\"onToolbarControlPerform($event, control, elem.value)\"\n [attr.data-testid]=\"control.name + '-' + elem.value\">\n </dsfr-button>\n }\n </dsfr-buttons-group>\n } @else if (type === 'select') {\n <dsfr-form-select\n unfocusable=\"select\"\n [label]=\"getI18nKey(control.name, 'label') | dsfrI18n\"\n [labelSrOnly]=\"true\"\n [(ngModel)]=\"selectModel[control.name]\"\n [placeholder]=\"getI18nKey(control.name, 'label') | dsfrI18n\"\n [options]=\"getSelectOptions(control)\"\n (selectChange)=\"onListSelect(control.name, $event)\"\n [attr.data-testid]=\"control.name\"></dsfr-form-select>\n }\n</ng-template>\n\n<!--TODO: RPA: remplacer les attributs tooltipMessage par des vrais tooltips dsfr -->\n<ng-template #btn let-control=\"control\">\n <span\n [ngClass]=\"{\n control: true,\n prompt: control.mode === 'prompt'\n }\">\n <dsfr-button\n unfocusable=\"button\"\n type=\"button\"\n variant=\"tertiary-no-outline\"\n size=\"SM\"\n customClass=\"item-editor\"\n [icon]=\"getIcon(control.name)\"\n [tooltipMessage]=\"getI18nKey(control.name) | dsfrI18n\"\n [attr.aria-pressed]=\"isFormatActive(control.name) || null\"\n (click)=\"onToolbarControlPerform($event, control)\"\n (keydown.enter)=\"onToolbarControlPerform($event, control)\"\n [attr.data-testid]=\"control.name\">\n </dsfr-button>\n @if (control.name === 'color' || control.name === 'background') {\n <input\n [id]=\"'picker-' + control.name\"\n class=\"item-editor prompt-color\"\n type=\"color\"\n tabindex=\"-1\"\n (change)=\"onColorPickerChange($event, control.name)\"\n [attr.aria-label]=\"getI18nKey(control.name) | dsfrI18n\" />\n } @else if (control.name === 'image') {\n <input\n [id]=\"'prompt-' + control.name\"\n class=\"item-editor prompt-file\"\n type=\"file\"\n accept=\"image/*\"\n tabindex=\"-1\"\n (change)=\"onImageInputFileChange($event)\"\n [attr.aria-label]=\"getI18nKey(control.name) | dsfrI18n\" />\n }\n </span>\n</ng-template>\n", styles: ["edu-editor-toolbar .editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;margin-bottom:3px}edu-editor-toolbar .editor-toolbar.editor-toolbar--focus{outline-offset:4px;outline:2px solid rgb(58,58,58)}edu-editor-toolbar .editor-toolbar .group{display:inline-block}edu-editor-toolbar .editor-toolbar .group:not(last-child){border-right:1px solid lightgray;padding-right:.75rem;margin-right:.75rem}edu-editor-toolbar .editor-toolbar dsfr-button[aria-pressed=true] button{background-color:var(--active-tint);border-bottom:1px solid var(--text-action-high-blue-france)}edu-editor-toolbar .editor-toolbar dsfr-button{margin-left:.1875rem}edu-editor-toolbar .editor-toolbar button:focus,edu-editor-toolbar .editor-toolbar select:focus{outline-offset:0px}edu-editor-toolbar .editor-toolbar .prompt{display:inline-block;position:relative}edu-editor-toolbar .editor-toolbar .prompt .prompt-color,edu-editor-toolbar .editor-toolbar .prompt .prompt-file{position:absolute;width:0%;height:0%;opacity:0}edu-editor-toolbar .editor-toolbar dsfr-form-select{margin-right:.25rem}edu-editor-toolbar .editor-toolbar dsfr-form-select>.fr-select-group{position:relative;top:-.25rem;width:11rem}edu-editor-toolbar .editor-toolbar dsfr-form-select>.fr-select-group .fr-select{box-shadow:none;padding:.2rem 2.5rem .2rem 1rem}edu-editor-toolbar .fr-icon-underline:before,edu-editor-toolbar .fr-icon-underline:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 3V12C8 14.2091 9.79086 16 12 16C14.2091 16 16 14.2091 16 12V3H18V12C18 15.3137 15.3137 18 12 18C8.68629 18 6 15.3137 6 12V3H8ZM4 20H20V22H4V20Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 3V12C8 14.2091 9.79086 16 12 16C14.2091 16 16 14.2091 16 12V3H18V12C18 15.3137 15.3137 18 12 18C8.68629 18 6 15.3137 6 12V3H8ZM4 20H20V22H4V20Z\"></path></svg>')}edu-editor-toolbar .fr-icon-strike:before,edu-editor-toolbar .fr-icon-strike:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M17.1538 14C17.3846 14.5161 17.5 15.0893 17.5 15.7196C17.5 17.0625 16.9762 18.1116 15.9286 18.867C14.8809 19.6223 13.4335 20 11.5862 20C9.94674 20 8.32335 19.6185 6.71592 18.8555V16.6009C8.23538 17.4783 9.7908 17.917 11.3822 17.917C13.9333 17.917 15.2128 17.1846 15.2208 15.7196C15.2208 15.0939 15.0049 14.5598 14.5731 14.1173C14.5339 14.0772 14.4939 14.0381 14.4531 14H3V12H21V14H17.1538ZM13.076 11H7.62908C7.4566 10.8433 7.29616 10.6692 7.14776 10.4778C6.71592 9.92084 6.5 9.24559 6.5 8.45207C6.5 7.21602 6.96583 6.165 7.89749 5.299C8.82916 4.43299 10.2706 4 12.2219 4C13.6934 4 15.1009 4.32808 16.4444 4.98426V7.13591C15.2448 6.44921 13.9293 6.10587 12.4978 6.10587C10.0187 6.10587 8.77917 6.88793 8.77917 8.45207C8.77917 8.87172 8.99709 9.23796 9.43293 9.55079C9.86878 9.86362 10.4066 10.1135 11.0463 10.3004C11.6665 10.4816 12.3431 10.7148 13.076 11H13.076Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M17.1538 14C17.3846 14.5161 17.5 15.0893 17.5 15.7196C17.5 17.0625 16.9762 18.1116 15.9286 18.867C14.8809 19.6223 13.4335 20 11.5862 20C9.94674 20 8.32335 19.6185 6.71592 18.8555V16.6009C8.23538 17.4783 9.7908 17.917 11.3822 17.917C13.9333 17.917 15.2128 17.1846 15.2208 15.7196C15.2208 15.0939 15.0049 14.5598 14.5731 14.1173C14.5339 14.0772 14.4939 14.0381 14.4531 14H3V12H21V14H17.1538ZM13.076 11H7.62908C7.4566 10.8433 7.29616 10.6692 7.14776 10.4778C6.71592 9.92084 6.5 9.24559 6.5 8.45207C6.5 7.21602 6.96583 6.165 7.89749 5.299C8.82916 4.43299 10.2706 4 12.2219 4C13.6934 4 15.1009 4.32808 16.4444 4.98426V7.13591C15.2448 6.44921 13.9293 6.10587 12.4978 6.10587C10.0187 6.10587 8.77917 6.88793 8.77917 8.45207C8.77917 8.87172 8.99709 9.23796 9.43293 9.55079C9.86878 9.86362 10.4066 10.1135 11.0463 10.3004C11.6665 10.4816 12.3431 10.7148 13.076 11H13.076Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-clean:before,edu-editor-toolbar .fr-icon-clean:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8.58564 8.85449L3.63589 13.8042L8.83021 18.9985L9.99985 18.9978V18.9966H11.1714L14.9496 15.2184L8.58564 8.85449ZM9.99985 7.44027L16.3638 13.8042L19.1922 10.9758L12.8283 4.61185L9.99985 7.44027ZM13.9999 18.9966H20.9999V20.9966H11.9999L8.00229 20.9991L1.51457 14.5113C1.12405 14.1208 1.12405 13.4877 1.51457 13.0971L12.1212 2.49053C12.5117 2.1 13.1449 2.1 13.5354 2.49053L21.3136 10.2687C21.7041 10.6592 21.7041 11.2924 21.3136 11.6829L13.9999 18.9966Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8.58564 8.85449L3.63589 13.8042L8.83021 18.9985L9.99985 18.9978V18.9966H11.1714L14.9496 15.2184L8.58564 8.85449ZM9.99985 7.44027L16.3638 13.8042L19.1922 10.9758L12.8283 4.61185L9.99985 7.44027ZM13.9999 18.9966H20.9999V20.9966H11.9999L8.00229 20.9991L1.51457 14.5113C1.12405 14.1208 1.12405 13.4877 1.51457 13.0971L12.1212 2.49053C12.5117 2.1 13.1449 2.1 13.5354 2.49053L21.3136 10.2687C21.7041 10.6592 21.7041 11.2924 21.3136 11.6829L13.9999 18.9966Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-font-color:before,edu-editor-toolbar .fr-icon-font-color:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.2459 14H8.75407L7.15407 18H5L11 3H13L19 18H16.8459L15.2459 14ZM14.4459 12L12 5.88516L9.55407 12H14.4459ZM3 20H21V22H3V20Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.2459 14H8.75407L7.15407 18H5L11 3H13L19 18H16.8459L15.2459 14ZM14.4459 12L12 5.88516L9.55407 12H14.4459ZM3 20H21V22H3V20Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-left:before,edu-editor-toolbar .fr-icon-align-left:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">%0A <path d=\"M3 4H21V6H3V4ZM3 19H17V21H3V19ZM3 14H21V16H3V14ZM3 9H17V11H3V9Z\"></path>%0A</svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">%0A <path d=\"M3 4H21V6H3V4ZM3 19H17V21H3V19ZM3 14H21V16H3V14ZM3 9H17V11H3V9Z\"></path>%0A</svg>%0A')}edu-editor-toolbar .fr-icon-align-right:before,edu-editor-toolbar .fr-icon-align-right:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM7 19H21V21H7V19ZM3 14H21V16H3V14ZM7 9H21V11H7V9Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM7 19H21V21H7V19ZM3 14H21V16H3V14ZM7 9H21V11H7V9Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-center:before,edu-editor-toolbar .fr-icon-align-center:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM5 19H19V21H5V19ZM3 14H21V16H3V14ZM5 9H19V11H5V9Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM5 19H19V21H5V19ZM3 14H21V16H3V14ZM5 9H19V11H5V9Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-justify:before,edu-editor-toolbar .fr-icon-align-justify:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM3 14H21V16H3V14ZM3 9H21V11H3V9Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM3 14H21V16H3V14ZM3 9H21V11H3V9Z\"></path></svg>')}edu-editor-toolbar .fr-icon-list-check:before,edu-editor-toolbar .fr-icon-list-check:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 4H21V6H8V4ZM3 3.5H6V6.5H3V3.5ZM3 10.5H6V13.5H3V10.5ZM3 17.5H6V20.5H3V17.5ZM8 11H21V13H8V11ZM8 18H21V20H8V18Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 4H21V6H8V4ZM3 3.5H6V6.5H3V3.5ZM3 10.5H6V13.5H3V10.5ZM3 17.5H6V20.5H3V17.5ZM8 11H21V13H8V11ZM8 18H21V20H8V18Z\"></path></svg>')}edu-editor-toolbar .fr-icon-indent-decrease:before,edu-editor-toolbar .fr-icon-indent-decrease:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM3 12.5L7 9V16L3 12.5Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM3 12.5L7 9V16L3 12.5Z\"></path></svg>')}edu-editor-toolbar .fr-icon-indent-increase:before,edu-editor-toolbar .fr-icon-indent-increase:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM7 12.5L3 16V9L7 12.5Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM7 12.5L3 16V9L7 12.5Z\"></path></svg>')}edu-editor-toolbar .fr-icon-direction-rtl:before,edu-editor-toolbar .fr-icon-direction-rtl:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11 5V15H9V11C6.79086 11 5 9.20914 5 7C5 4.79086 6.79086 3 9 3H17V5H15V15H13V5H11ZM9 5C7.89543 5 7 5.89543 7 7C7 8.10457 7.89543 9 9 9V5ZM7 17H19V19H7V21.5L3 18L7 14.5V17Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11 5V15H9V11C6.79086 11 5 9.20914 5 7C5 4.79086 6.79086 3 9 3H17V5H15V15H13V5H11ZM9 5C7.89543 5 7 5.89543 7 7C7 8.10457 7.89543 9 9 9V5ZM7 17H19V19H7V21.5L3 18L7 14.5V17Z\"></path></svg>')}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: DsfrSkiplinksModule }, { kind: "ngmodule", type: DsfrFormSelectModule }, { kind: "component", type: i3.DsfrFormSelectComponent, selector: "dsfr-form-select", inputs: ["placeholder", "required", "ariaLabel", "ariaInvalid", "error", "valid", "message", "messageSeverity", "labelSrOnly", "placeHolder", "options", "compareWith"], outputs: ["selectChange"] }, { kind: "ngmodule", type: DsfrButtonModule }, { kind: "component", type: i3.DsfrButtonComponent, selector: "dsfr-button", inputs: ["label", "type", "tooltipMessage", "variant", "buttonSize", "icon", "iconPosition", "disabled", "uppercase", "loader", "ariaLabel", "invertedOutlineContrast", "id", "buttonId", "ariaControls", "ariaPressed", "ariaHasPopup", "ariaExpanded", "tabIndex", "customClass", "buttonRole", "labelSrOnly", "size"] }, { kind: "directive", type: UnfocusableDirective, selector: "[unfocusable]", inputs: ["unfocusable"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: EduEditorToolbarComponent, decorators: [{ type: Component, args: [{ selector: 'edu-editor-toolbar', encapsulation: ViewEncapsulation.None, standalone: true, imports: [ CommonModule, FormsModule, ReactiveFormsModule, DsfrSkiplinksModule, DsfrFormSelectModule, DsfrButtonModule, UnfocusableDirective, DsfrI18nPipe, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div\n #controlsParent\n role=\"toolbar\"\n class=\"editor-toolbar\"\n tabindex=\"0\"\n [attr.aria-controls]=\"editorId\"\n [ngClass]=\"{ 'editor-toolbar--focus': toolbarFocused }\"\n (focus)=\"onToolbarFocus()\"\n (blur)=\"onToolbarBlur($event)\"\n (keydown)=\"onToolbarKeydown($event)\">\n @for (control of toolbarOptions; track control) {\n <ng-container\n [ngTemplateOutlet]=\"tplcontrol\"\n [ngTemplateOutletContext]=\"{ type: getButtonType(control), control: control }\">\n </ng-container>\n }\n</div>\n\n<ng-template #tplcontrol let-type=\"type\" let-control=\"control\">\n @if (type === 'btn') {\n <ng-container [ngTemplateOutlet]=\"btn\" [ngTemplateOutletContext]=\"{ control: control }\"></ng-container>\n } @else if (type === 'group') {\n <span class=\"group\">\n @for (item of control; track item) {\n <ng-container [ngTemplateOutlet]=\"btn\" [ngTemplateOutletContext]=\"{ control: item }\"></ng-container>\n }\n </span>\n } @else if (type === 'radio') {\n <dsfr-buttons-group inline=\"always\" size=\"SM\" [ngClass]=\"{ group: control.group }\">\n @for (elem of control.values; track elem) {\n <dsfr-button\n unfocusable=\"button\"\n type=\"button\"\n variant=\"tertiary-no-outline\"\n size=\"SM\"\n customClass=\"item-editor\"\n [icon]=\"getIcon(control.name, elem.value)\"\n [tooltipMessage]=\"getI18nKey(control.name, elem.value) | dsfrI18n\"\n [attr.aria-pressed]=\"isValueActive(control.name, elem.value) || null\"\n (click)=\"onToolbarControlPerform($event, control, elem.value)\"\n (keydown.enter)=\"onToolbarControlPerform($event, control, elem.value)\"\n [attr.data-testid]=\"control.name + '-' + elem.value\">\n </dsfr-button>\n }\n </dsfr-buttons-group>\n } @else if (type === 'select') {\n <dsfr-form-select\n unfocusable=\"select\"\n [label]=\"getI18nKey(control.name, 'label') | dsfrI18n\"\n [labelSrOnly]=\"true\"\n [(ngModel)]=\"selectModel[control.name]\"\n [placeholder]=\"getI18nKey(control.name, 'label') | dsfrI18n\"\n [options]=\"getSelectOptions(control)\"\n (selectChange)=\"onListSelect(control.name, $event)\"\n [attr.data-testid]=\"control.name\"></dsfr-form-select>\n }\n</ng-template>\n\n<!--TODO: RPA: remplacer les attributs tooltipMessage par des vrais tooltips dsfr -->\n<ng-template #btn let-control=\"control\">\n <span\n [ngClass]=\"{\n control: true,\n prompt: control.mode === 'prompt'\n }\">\n <dsfr-button\n unfocusable=\"button\"\n type=\"button\"\n variant=\"tertiary-no-outline\"\n size=\"SM\"\n customClass=\"item-editor\"\n [icon]=\"getIcon(control.name)\"\n [tooltipMessage]=\"getI18nKey(control.name) | dsfrI18n\"\n [attr.aria-pressed]=\"isFormatActive(control.name) || null\"\n (click)=\"onToolbarControlPerform($event, control)\"\n (keydown.enter)=\"onToolbarControlPerform($event, control)\"\n [attr.data-testid]=\"control.name\">\n </dsfr-button>\n @if (control.name === 'color' || control.name === 'background') {\n <input\n [id]=\"'picker-' + control.name\"\n class=\"item-editor prompt-color\"\n type=\"color\"\n tabindex=\"-1\"\n (change)=\"onColorPickerChange($event, control.name)\"\n [attr.aria-label]=\"getI18nKey(control.name) | dsfrI18n\" />\n } @else if (control.name === 'image') {\n <input\n [id]=\"'prompt-' + control.name\"\n class=\"item-editor prompt-file\"\n type=\"file\"\n accept=\"image/*\"\n tabindex=\"-1\"\n (change)=\"onImageInputFileChange($event)\"\n [attr.aria-label]=\"getI18nKey(control.name) | dsfrI18n\" />\n }\n </span>\n</ng-template>\n", styles: ["edu-editor-toolbar .editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;margin-bottom:3px}edu-editor-toolbar .editor-toolbar.editor-toolbar--focus{outline-offset:4px;outline:2px solid rgb(58,58,58)}edu-editor-toolbar .editor-toolbar .group{display:inline-block}edu-editor-toolbar .editor-toolbar .group:not(last-child){border-right:1px solid lightgray;padding-right:.75rem;margin-right:.75rem}edu-editor-toolbar .editor-toolbar dsfr-button[aria-pressed=true] button{background-color:var(--active-tint);border-bottom:1px solid var(--text-action-high-blue-france)}edu-editor-toolbar .editor-toolbar dsfr-button{margin-left:.1875rem}edu-editor-toolbar .editor-toolbar button:focus,edu-editor-toolbar .editor-toolbar select:focus{outline-offset:0px}edu-editor-toolbar .editor-toolbar .prompt{display:inline-block;position:relative}edu-editor-toolbar .editor-toolbar .prompt .prompt-color,edu-editor-toolbar .editor-toolbar .prompt .prompt-file{position:absolute;width:0%;height:0%;opacity:0}edu-editor-toolbar .editor-toolbar dsfr-form-select{margin-right:.25rem}edu-editor-toolbar .editor-toolbar dsfr-form-select>.fr-select-group{position:relative;top:-.25rem;width:11rem}edu-editor-toolbar .editor-toolbar dsfr-form-select>.fr-select-group .fr-select{box-shadow:none;padding:.2rem 2.5rem .2rem 1rem}edu-editor-toolbar .fr-icon-underline:before,edu-editor-toolbar .fr-icon-underline:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 3V12C8 14.2091 9.79086 16 12 16C14.2091 16 16 14.2091 16 12V3H18V12C18 15.3137 15.3137 18 12 18C8.68629 18 6 15.3137 6 12V3H8ZM4 20H20V22H4V20Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 3V12C8 14.2091 9.79086 16 12 16C14.2091 16 16 14.2091 16 12V3H18V12C18 15.3137 15.3137 18 12 18C8.68629 18 6 15.3137 6 12V3H8ZM4 20H20V22H4V20Z\"></path></svg>')}edu-editor-toolbar .fr-icon-strike:before,edu-editor-toolbar .fr-icon-strike:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M17.1538 14C17.3846 14.5161 17.5 15.0893 17.5 15.7196C17.5 17.0625 16.9762 18.1116 15.9286 18.867C14.8809 19.6223 13.4335 20 11.5862 20C9.94674 20 8.32335 19.6185 6.71592 18.8555V16.6009C8.23538 17.4783 9.7908 17.917 11.3822 17.917C13.9333 17.917 15.2128 17.1846 15.2208 15.7196C15.2208 15.0939 15.0049 14.5598 14.5731 14.1173C14.5339 14.0772 14.4939 14.0381 14.4531 14H3V12H21V14H17.1538ZM13.076 11H7.62908C7.4566 10.8433 7.29616 10.6692 7.14776 10.4778C6.71592 9.92084 6.5 9.24559 6.5 8.45207C6.5 7.21602 6.96583 6.165 7.89749 5.299C8.82916 4.43299 10.2706 4 12.2219 4C13.6934 4 15.1009 4.32808 16.4444 4.98426V7.13591C15.2448 6.44921 13.9293 6.10587 12.4978 6.10587C10.0187 6.10587 8.77917 6.88793 8.77917 8.45207C8.77917 8.87172 8.99709 9.23796 9.43293 9.55079C9.86878 9.86362 10.4066 10.1135 11.0463 10.3004C11.6665 10.4816 12.3431 10.7148 13.076 11H13.076Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M17.1538 14C17.3846 14.5161 17.5 15.0893 17.5 15.7196C17.5 17.0625 16.9762 18.1116 15.9286 18.867C14.8809 19.6223 13.4335 20 11.5862 20C9.94674 20 8.32335 19.6185 6.71592 18.8555V16.6009C8.23538 17.4783 9.7908 17.917 11.3822 17.917C13.9333 17.917 15.2128 17.1846 15.2208 15.7196C15.2208 15.0939 15.0049 14.5598 14.5731 14.1173C14.5339 14.0772 14.4939 14.0381 14.4531 14H3V12H21V14H17.1538ZM13.076 11H7.62908C7.4566 10.8433 7.29616 10.6692 7.14776 10.4778C6.71592 9.92084 6.5 9.24559 6.5 8.45207C6.5 7.21602 6.96583 6.165 7.89749 5.299C8.82916 4.43299 10.2706 4 12.2219 4C13.6934 4 15.1009 4.32808 16.4444 4.98426V7.13591C15.2448 6.44921 13.9293 6.10587 12.4978 6.10587C10.0187 6.10587 8.77917 6.88793 8.77917 8.45207C8.77917 8.87172 8.99709 9.23796 9.43293 9.55079C9.86878 9.86362 10.4066 10.1135 11.0463 10.3004C11.6665 10.4816 12.3431 10.7148 13.076 11H13.076Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-clean:before,edu-editor-toolbar .fr-icon-clean:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8.58564 8.85449L3.63589 13.8042L8.83021 18.9985L9.99985 18.9978V18.9966H11.1714L14.9496 15.2184L8.58564 8.85449ZM9.99985 7.44027L16.3638 13.8042L19.1922 10.9758L12.8283 4.61185L9.99985 7.44027ZM13.9999 18.9966H20.9999V20.9966H11.9999L8.00229 20.9991L1.51457 14.5113C1.12405 14.1208 1.12405 13.4877 1.51457 13.0971L12.1212 2.49053C12.5117 2.1 13.1449 2.1 13.5354 2.49053L21.3136 10.2687C21.7041 10.6592 21.7041 11.2924 21.3136 11.6829L13.9999 18.9966Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8.58564 8.85449L3.63589 13.8042L8.83021 18.9985L9.99985 18.9978V18.9966H11.1714L14.9496 15.2184L8.58564 8.85449ZM9.99985 7.44027L16.3638 13.8042L19.1922 10.9758L12.8283 4.61185L9.99985 7.44027ZM13.9999 18.9966H20.9999V20.9966H11.9999L8.00229 20.9991L1.51457 14.5113C1.12405 14.1208 1.12405 13.4877 1.51457 13.0971L12.1212 2.49053C12.5117 2.1 13.1449 2.1 13.5354 2.49053L21.3136 10.2687C21.7041 10.6592 21.7041 11.2924 21.3136 11.6829L13.9999 18.9966Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-font-color:before,edu-editor-toolbar .fr-icon-font-color:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.2459 14H8.75407L7.15407 18H5L11 3H13L19 18H16.8459L15.2459 14ZM14.4459 12L12 5.88516L9.55407 12H14.4459ZM3 20H21V22H3V20Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.2459 14H8.75407L7.15407 18H5L11 3H13L19 18H16.8459L15.2459 14ZM14.4459 12L12 5.88516L9.55407 12H14.4459ZM3 20H21V22H3V20Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-left:before,edu-editor-toolbar .fr-icon-align-left:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">%0A <path d=\"M3 4H21V6H3V4ZM3 19H17V21H3V19ZM3 14H21V16H3V14ZM3 9H17V11H3V9Z\"></path>%0A</svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">%0A <path d=\"M3 4H21V6H3V4ZM3 19H17V21H3V19ZM3 14H21V16H3V14ZM3 9H17V11H3V9Z\"></path>%0A</svg>%0A')}edu-editor-toolbar .fr-icon-align-right:before,edu-editor-toolbar .fr-icon-align-right:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM7 19H21V21H7V19ZM3 14H21V16H3V14ZM7 9H21V11H7V9Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM7 19H21V21H7V19ZM3 14H21V16H3V14ZM7 9H21V11H7V9Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-center:before,edu-editor-toolbar .fr-icon-align-center:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM5 19H19V21H5V19ZM3 14H21V16H3V14ZM5 9H19V11H5V9Z\"></path></svg>%0A');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM5 19H19V21H5V19ZM3 14H21V16H3V14ZM5 9H19V11H5V9Z\"></path></svg>%0A')}edu-editor-toolbar .fr-icon-align-justify:before,edu-editor-toolbar .fr-icon-align-justify:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM3 14H21V16H3V14ZM3 9H21V11H3V9Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM3 14H21V16H3V14ZM3 9H21V11H3V9Z\"></path></svg>')}edu-editor-toolbar .fr-icon-list-check:before,edu-editor-toolbar .fr-icon-list-check:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 4H21V6H8V4ZM3 3.5H6V6.5H3V3.5ZM3 10.5H6V13.5H3V10.5ZM3 17.5H6V20.5H3V17.5ZM8 11H21V13H8V11ZM8 18H21V20H8V18Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 4H21V6H8V4ZM3 3.5H6V6.5H3V3.5ZM3 10.5H6V13.5H3V10.5ZM3 17.5H6V20.5H3V17.5ZM8 11H21V13H8V11ZM8 18H21V20H8V18Z\"></path></svg>')}edu-editor-toolbar .fr-icon-indent-decrease:before,edu-editor-toolbar .fr-icon-indent-decrease:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM3 12.5L7 9V16L3 12.5Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM3 12.5L7 9V16L3 12.5Z\"></path></svg>')}edu-editor-toolbar .fr-icon-indent-increase:before,edu-editor-toolbar .fr-icon-indent-increase:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM7 12.5L3 16V9L7 12.5Z\"></path></svg>');mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M3 4H21V6H3V4ZM3 19H21V21H3V19ZM11 14H21V16H11V14ZM11 9H21V11H11V9ZM7 12.5L3 16V9L7 12.5Z\"></path></svg>')}edu-editor-toolbar .fr-icon-direction-rtl:before,edu-editor-toolbar .fr-icon-direction-rtl:after{-webkit-mask-image:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11 5V15H9V11C6.79086 11 5 9.20914 5 7C5 4.79086 6.79086 3 9 3H17V5H15V15H13V5H11ZM9 5C7.89543 5 7 5.89543 7 7C7 8.10457 7.89543 9 9 9V5ZM7 17H19V19H7V21.5L3 18L7 14.5V17Z\"></path></svg>');mask-image:url('data:image/svg+xml,<sv