UNPKG

ngx-quill

Version:

Angular components for the easy use of the QuillJS richt text editor.

1,633 lines 94.5 kB
import { defaultModules, QUILL_CONFIG_TOKEN } from 'ngx-quill/config';
export * from 'ngx-quill/config';
import { isPlatformServer } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, Injectable, model, input, booleanAttribute, ElementRef, PLATFORM_ID, Renderer2, DestroyRef, signal, EventEmitter, SecurityContext, afterNextRender, effect, Output, Directive, ChangeDetectionStrategy, ViewEncapsulation, Component, forwardRef, computed, NgModule } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DomSanitizer } from '@angular/platform-browser';
import { defer, forkJoin, of, isObservable, mergeMap, fromEvent, Subscription, debounceTime } from 'rxjs';
import { shareReplay, map, tap, mergeMap as mergeMap$1 } from 'rxjs/operators';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';

class QuillService {
    constructor() {
        this.config = inject(QUILL_CONFIG_TOKEN) || { modules: defaultModules };
        this.quill$ = defer(async () => {
            if (!this.Quill) {
                const { Quill } = await import('./ngx-quill-quill-Di9aaYtq.mjs');
                this.Quill = Quill;
            }
            // Only register custom options and modules once
            this.config.customOptions?.forEach((customOption) => {
                const newCustomOption = this.Quill.import(customOption.import);
                newCustomOption.whitelist = customOption.whitelist;
                this.Quill.register(newCustomOption, true, this.config.suppressGlobalRegisterWarning);
            });
            // Use `Promise` directly to avoid bundling `firstValueFrom`.
            return new Promise(resolve => {
                this.registerCustomModules(this.Quill, this.config.customModules, this.config.suppressGlobalRegisterWarning).subscribe(resolve);
            });
        }).pipe(shareReplay({
            bufferSize: 1,
            refCount: false
        }));
        // A list of custom modules that have already been registered,
        // so we don’t need to await their implementation.
        this.registeredModules = new Set();
    }
    getQuill() {
        return this.quill$;
    }
    /** @internal */
    beforeRender(Quill, customModules, beforeRender = this.config.beforeRender) {
        // This function is called each time the editor needs to be rendered,
        // so it operates individually per component. If no custom module needs to be
        // registered and no `beforeRender` function is provided, it will emit
        // immediately and proceed with the rendering.
        const sources = [this.registerCustomModules(Quill, customModules)];
        if (beforeRender) {
            sources.push(beforeRender());
        }
        return forkJoin(sources).pipe(map(() => Quill));
    }
    /** @internal */
    registerCustomModules(Quill, customModules, suppressGlobalRegisterWarning) {
        if (!Array.isArray(customModules)) {
            return of(Quill);
        }
        const sources = [];
        for (const customModule of customModules) {
            const { path, implementation: maybeImplementation } = customModule;
            // If the module is already registered, proceed to the next module...
            if (this.registeredModules.has(path)) {
                continue;
            }
            this.registeredModules.add(path);
            if (isObservable(maybeImplementation)) {
                // If the implementation is an observable, we will wait for it to load and
                // then register it with Quill. The caller will wait until the module is registered.
                sources.push(maybeImplementation.pipe(tap((implementation) => {
                    Quill.register(path, implementation, suppressGlobalRegisterWarning);
                })));
            }
            else {
                Quill.register(path, maybeImplementation, suppressGlobalRegisterWarning);
            }
        }
        return sources.length > 0 ? forkJoin(sources).pipe(map(() => Quill)) : of(Quill);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

var ValidationKind;
(function (ValidationKind) {
    ValidationKind["quillMinLength"] = "quillMinLength";
    ValidationKind["quillMaxLength"] = "quillMaxLength";
    ValidationKind["quillRequired"] = "quillRequired";
})(ValidationKind || (ValidationKind = {}));
const getFormat$1 = (format, configFormat) => {
    const passedFormat = format || configFormat;
    return passedFormat || 'html';
};
class QuillEditorFieldBase {
    constructor() {
        // Required
        this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
        // Writable interaction state - control updates these
        this.touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
        // Read-only state - form system manages these
        this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        // Custom component inputs
        this.format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
        this.theme = input(...(ngDevMode ? [undefined, { debugName: "theme" }] : /* istanbul ignore next */ []));
        this.modules = input(...(ngDevMode ? [undefined, { debugName: "modules" }] : /* istanbul ignore next */ []));
        this.debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
        this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
        this.formats = input(...(ngDevMode ? [undefined, { debugName: "formats" }] : /* istanbul ignore next */ []));
        this.customToolbarPosition = input('top', ...(ngDevMode ? [{ debugName: "customToolbarPosition" }] : /* istanbul ignore next */ []));
        this.sanitize = input(...(ngDevMode ? [undefined, { debugName: "sanitize" }] : /* istanbul ignore next */ []));
        this.beforeRender = input(...(ngDevMode ? [undefined, { debugName: "beforeRender" }] : /* istanbul ignore next */ []));
        this.styles = input(null, ...(ngDevMode ? [{ debugName: "styles" }] : /* istanbul ignore next */ []));
        this.registry = input(...(ngDevMode ? [undefined, { debugName: "registry" }] : /* istanbul ignore next */ []));
        this.bounds = input(...(ngDevMode ? [undefined, { debugName: "bounds" }] : /* istanbul ignore next */ []));
        this.customOptions = input([], ...(ngDevMode ? [{ debugName: "customOptions" }] : /* istanbul ignore next */ []));
        this.customModules = input([], ...(ngDevMode ? [{ debugName: "customModules" }] : /* istanbul ignore next */ []));
        this.trackChanges = input(...(ngDevMode ? [undefined, { debugName: "trackChanges" }] : /* istanbul ignore next */ []));
        this.classes = input(...(ngDevMode ? [undefined, { debugName: "classes" }] : /* istanbul ignore next */ []));
        this.trimOnValidation = input(false, { ...(ngDevMode ? { debugName: "trimOnValidation" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.linkPlaceholder = input(...(ngDevMode ? [undefined, { debugName: "linkPlaceholder" }] : /* istanbul ignore next */ []));
        this.compareValues = input(false, { ...(ngDevMode ? { debugName: "compareValues" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.filterNull = input(false, { ...(ngDevMode ? { debugName: "filterNull" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.debounceTime = input(...(ngDevMode ? [undefined, { debugName: "debounceTime" }] : /* istanbul ignore next */ []));
        this.onlyFormatEventData = input(false, ...(ngDevMode ? [{ debugName: "onlyFormatEventData" }] : /* istanbul ignore next */ []));
        /*
        https://github.com/KillerCodeMonkey/ngx-quill/issues/1257 - fix null value set
      
        provide default empty value
        by default null
      
        e.g. defaultEmptyValue="" - empty string
      
        <quill-editor
          defaultEmptyValue=""
          formControlName="message"
        ></quill-editor>
        */
        this.defaultEmptyValue = input(null, ...(ngDevMode ? [{ debugName: "defaultEmptyValue" }] : /* istanbul ignore next */ []));
        /* ---------------- DEPENDENCIES ---------------- */
        this.quillService = inject(QuillService);
        this.elementRef = inject(ElementRef);
        this.domSanitizer = inject(DomSanitizer);
        this.platformId = inject(PLATFORM_ID);
        this.renderer = inject(Renderer2);
        this.service = inject(QuillService);
        this.destroyRef = inject(DestroyRef);
        this.internalChange = false;
        this.eventsSubscription = null;
        this.quillSubscription = null;
        this.quillEditor = signal(null, ...(ngDevMode ? [{ debugName: "quillEditor" }] : /* istanbul ignore next */ []));
        this.onEditorCreated = new EventEmitter();
        this.onEditorChanged = new EventEmitter();
        this.onContentChanged = new EventEmitter();
        this.onSelectionChanged = new EventEmitter();
        this.onFocus = new EventEmitter();
        this.onBlur = new EventEmitter();
        this.onNativeFocus = new EventEmitter();
        this.onNativeBlur = new EventEmitter();
        this.toolbarPosition = signal('top', ...(ngDevMode ? [{ debugName: "toolbarPosition" }] : /* istanbul ignore next */ []));
        this.valueGetter = input(this.getter.bind(this), ...(ngDevMode ? [{ debugName: "valueGetter" }] : /* istanbul ignore next */ []));
        this.valueSetter = input((quillEditor, value) => {
            const format = getFormat$1(this.format(), this.service.config.format);
            if (format === 'html') {
                const sanitize = (typeof this.sanitize() === 'boolean') ? this.sanitize() : (this.service.config.sanitize || false);
                if (sanitize) {
                    value = this.domSanitizer.sanitize(SecurityContext.HTML, value);
                }
                return quillEditor.clipboard.convert({ html: value });
            }
            if (format === 'json') {
                try {
                    return JSON.parse(value);
                }
                catch {
                    return [{ insert: value }];
                }
            }
            return value;
        }, ...(ngDevMode ? [{ debugName: "valueSetter" }] : /* istanbul ignore next */ []));
        this.selectionChangeHandler = (range, oldRange, source) => {
            const trackChanges = this.trackChanges() || this.service.config.trackChanges;
            const shouldTriggerOnModelTouched = !range && (source === 'user' || trackChanges && trackChanges === 'all');
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            // only emit changes when there's any listener
            if (!this.onBlur.observed &&
                !this.onFocus.observed &&
                !this.onSelectionChanged.observed &&
                !shouldTriggerOnModelTouched) {
                return;
            }
            if (range === null) {
                this.onBlur.emit({
                    editor,
                    source
                });
            }
            else if (oldRange === null) {
                this.onFocus.emit({
                    editor,
                    source
                });
            }
            this.onSelectionChanged.emit({
                editor,
                oldRange,
                range,
                source
            });
            if (shouldTriggerOnModelTouched) {
                this.touched.set(true);
            }
        };
        this.textChangeHandler = (delta, oldDelta, source) => {
            const trackChanges = this.trackChanges() || this.service.config.trackChanges;
            const shouldTriggerOnModelChange = (source === 'user' || trackChanges && trackChanges === 'all');
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            // only emit changes when there's any listener
            if (!this.onContentChanged.observed && !shouldTriggerOnModelChange) {
                return;
            }
            const data = this.eventCallbackFormats();
            if (shouldTriggerOnModelChange) {
                // do not trigger value set again
                this.internalChange = true;
                this.value.set(
                // only call value getter again if not already done in eventCallbackFormats
                data.noFormat ? this.valueGetter()(editor) : data[data.format]);
            }
            if (this.onContentChanged.observed) {
                this.onContentChanged.emit({
                    content: data.object,
                    delta,
                    editor,
                    html: data.html,
                    oldDelta,
                    source,
                    text: data.text
                });
            }
        };
        this.editorChangeHandler = (event, current, old, source) => {
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            // only emit changes when there's any listener
            if (!this.onEditorChanged.observed) {
                return;
            }
            // only emit changes emitted by user interactions
            if (event === 'text-change') {
                const data = this.eventCallbackFormats();
                this.onEditorChanged.emit({
                    content: data.object,
                    delta: current,
                    editor,
                    event,
                    html: data.html,
                    oldDelta: old,
                    source,
                    text: data.text
                });
            }
            else {
                this.onEditorChanged.emit({
                    editor,
                    event,
                    oldRange: old,
                    range: current,
                    source
                });
            }
        };
        afterNextRender(() => {
            if (isPlatformServer(this.platformId)) {
                return;
            }
            // The `quill-editor` component might be destroyed before the `quill` chunk is loaded and its code is executed
            // this will lead to runtime exceptions, since the code will be executed on DOM nodes that don't exist within the tree.
            this.quillSubscription = this.service.getQuill().pipe(mergeMap((Quill) => this.service.beforeRender(Quill, this.customModules(), this.beforeRender()))).subscribe(Quill => {
                this.editorElem = this.elementRef.nativeElement.querySelector('[quill-editor-element]');
                const toolbarElem = this.elementRef.nativeElement.querySelector('[quill-editor-toolbar]');
                const modules = Object.assign({}, this.modules() || this.service.config.modules);
                if (toolbarElem) {
                    modules.toolbar = toolbarElem;
                }
                else if (modules.toolbar === undefined) {
                    modules.toolbar = defaultModules.toolbar;
                }
                let placeholder = this.placeholder() !== undefined ? this.placeholder() : this.service.config.placeholder;
                if (placeholder === undefined) {
                    placeholder = 'Insert text here ...';
                }
                const styles = this.styles();
                if (styles) {
                    this.previousStyles = styles;
                    Object.keys(styles).forEach((key) => {
                        this.renderer.setStyle(this.editorElem, key, styles[key]);
                    });
                }
                const previousClasses = this.classes();
                if (previousClasses) {
                    this.previousClasses = previousClasses;
                    this.addClasses(previousClasses);
                }
                this.customOptions().forEach((customOption) => {
                    const newCustomOption = Quill.import(customOption.import);
                    newCustomOption.whitelist = customOption.whitelist;
                    Quill.register(newCustomOption, true);
                });
                let bounds = this.bounds() && this.bounds() === 'self' ? this.editorElem : this.bounds();
                if (!bounds) {
                    // Can use global `document` because we execute this only in the browser.
                    bounds = this.service.config.bounds ? this.service.config.bounds : document.body;
                }
                let debug = this.debug();
                if (!debug && debug !== false && this.service.config.debug) {
                    debug = this.service.config.debug;
                }
                let readOnly = this.readonly();
                if (!readOnly && readOnly !== false) {
                    readOnly = this.service.config.readOnly !== undefined ? this.service.config.readOnly : false;
                }
                let formats = this.formats();
                if (!formats && formats === undefined) {
                    formats = this.service.config.formats ? [...this.service.config.formats] : (this.service.config.formats === null ? null : undefined);
                }
                const editor = new Quill(this.editorElem, {
                    bounds,
                    debug,
                    formats,
                    modules,
                    placeholder,
                    readOnly,
                    registry: this.registry(),
                    theme: this.theme() || (this.service.config.theme ? this.service.config.theme : 'snow')
                });
                if (this.onNativeBlur.observed) {
                    // https://github.com/quilljs/quill/issues/2186#issuecomment-533401328
                    fromEvent(editor.scroll.domNode, 'blur').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.onNativeBlur.next({
                        editor,
                        source: 'dom'
                    }));
                    // https://github.com/quilljs/quill/issues/2186#issuecomment-803257538
                    const toolbar = editor.getModule('toolbar');
                    if (toolbar.container) {
                        fromEvent(toolbar.container, 'mousedown').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(e => e.preventDefault());
                    }
                }
                if (this.onNativeFocus.observed) {
                    fromEvent(editor.scroll.domNode, 'focus').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.onNativeFocus.next({
                        editor,
                        source: 'dom'
                    }));
                }
                // Set optional link placeholder, Quill has no native API for it so using workaround
                if (this.linkPlaceholder()) {
                    const tooltip = editor?.theme?.tooltip;
                    const input = tooltip?.root?.querySelector('input[data-link]');
                    if (input?.dataset) {
                        input.dataset.link = this.linkPlaceholder();
                    }
                }
                const value = this.value();
                if (value) {
                    this.internalChange = true;
                    const format = getFormat$1(this.format(), this.service.config.format);
                    if (format === 'text') {
                        editor.setText(value, 'silent');
                    }
                    else {
                        const valueSetter = this.valueSetter();
                        const newValue = valueSetter(editor, value);
                        editor.setContents(newValue, 'silent');
                    }
                    const history = editor.getModule('history');
                    history.clear();
                    // trigger initial form validation
                    this.value.set(value);
                }
                // should trigger effects when editor set, setting init disabled, readonly + adds event listeners
                this.quillEditor.set(editor);
                // listening to the `onEditorCreated` event inside the template, for instance `<quill-view (onEditorCreated)="...">`.
                if (this.onEditorCreated.observed) {
                    this.onEditorCreated.emit(editor);
                }
            });
        });
        effect(() => {
            const customToolbarPosition = this.customToolbarPosition();
            if (this.toolbarPosition() !== customToolbarPosition) {
                this.toolbarPosition.set(customToolbarPosition);
            }
        });
        effect(() => {
            const readonly = this.readonly();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            if (readonly) {
                editor.disable();
            }
            else {
                editor.enable(true);
            }
        });
        effect(() => {
            let placeholder = this.placeholder();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            placeholder = placeholder !== undefined ? placeholder : this.service.config.placeholder;
            if (placeholder === undefined) {
                placeholder = 'Insert text here ...';
            }
            editor.root.dataset['placeholder'] = placeholder;
        });
        effect(() => {
            const styles = this.styles();
            const editor = this.quillEditor();
            if (!this.editorElem || !editor) {
                return;
            }
            const currentStyling = styles;
            const previousStyling = this.previousStyles;
            if (previousStyling) {
                Object.keys(previousStyling).forEach((key) => {
                    this.renderer.removeStyle(this.editorElem, key);
                });
            }
            if (currentStyling) {
                Object.keys(currentStyling).forEach((key) => {
                    this.renderer.setStyle(this.editorElem, key, currentStyling[key]);
                });
            }
        });
        effect(() => {
            const classes = this.classes();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            const currentClasses = classes;
            const previousClasses = this.previousClasses;
            if (previousClasses) {
                this.removeClasses(previousClasses);
            }
            if (currentClasses) {
                this.addClasses(currentClasses);
            }
        });
        effect(() => {
            const debounceTime = this.debounceTime();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            this.addQuillEventListeners(debounceTime);
        });
        effect(() => {
            const disabled = this.disabled();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            this.setDisabledState(disabled);
        });
        effect(() => {
            const value = this.value();
            const editor = this.quillEditor();
            if (!editor) {
                return;
            }
            if (this.internalChange) {
                this.internalChange = false;
                return;
            }
            this.writeValue(value);
        });
        this.destroyRef.onDestroy(() => {
            this.dispose();
            this.quillSubscription?.unsubscribe();
            this.quillSubscription = null;
        });
    }
    static normalizeClassNames(classes) {
        const classList = classes.trim().split(' ');
        return classList.reduce((prev, cur) => {
            const trimmed = cur.trim();
            if (trimmed) {
                prev.push(trimmed);
            }
            return prev;
        }, []);
    }
    addClasses(classList) {
        QuillEditorFieldBase.normalizeClassNames(classList).forEach((c) => {
            this.renderer.addClass(this.editorElem, c);
        });
    }
    removeClasses(classList) {
        QuillEditorFieldBase.normalizeClassNames(classList).forEach((c) => {
            this.renderer.removeClass(this.editorElem, c);
        });
    }
    writeValue(currentValue) {
        const editor = this.quillEditor();
        // optional fix for https://github.com/angular/angular/issues/14988
        if (this.filterNull() && currentValue === null) {
            return;
        }
        if (!editor) {
            return;
        }
        const format = getFormat$1(this.format(), this.service.config.format);
        const valueSetter = this.valueSetter();
        const newValue = valueSetter(editor, currentValue);
        if (this.compareValues()) {
            const currentEditorValue = editor.getContents();
            if (!currentEditorValue.diff(newValue).changeLength()) {
                return;
            }
        }
        if (currentValue) {
            if (format === 'text') {
                editor.setText(currentValue);
            }
            else {
                editor.setContents(newValue);
            }
            return;
        }
        editor.setText('');
    }
    setDisabledState(isDisabled) {
        const editor = this.quillEditor();
        if (!editor) {
            return;
        }
        if (isDisabled) {
            editor.disable();
            this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'disabled');
        }
        else {
            if (!this.readonly()) {
                editor.enable();
            }
            this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
        }
    }
    validate({ required = false, minLength, maxLength }) {
        const editor = this.quillEditor();
        if (!editor || (!required && !minLength && !maxLength)) {
            return null;
        }
        const text = editor.getText();
        // trim text if wanted + handle special case that an empty editor contains a new line
        const textLength = this.trimOnValidation() ? text.trim().length : (text.length === 1 && text.trim().length === 0 ? 0 : text.length - 1);
        const deltaOperations = editor.getContents().ops;
        const onlyEmptyOperation = !!deltaOperations && deltaOperations.length === 1 && ['\n', ''].includes(deltaOperations[0].insert?.toString() || '');
        const errors = [];
        if (minLength && textLength && textLength < minLength) {
            errors.push({
                kind: ValidationKind.quillMinLength,
                message: `text length: ${textLength}, min length: ${minLength}`
            });
        }
        if (maxLength && textLength > maxLength) {
            errors.push({
                kind: ValidationKind.quillMaxLength,
                message: `text length: ${textLength}, min length: ${maxLength}`
            });
        }
        if (required && !textLength && onlyEmptyOperation) {
            errors.push({
                kind: ValidationKind.quillRequired,
                message: `text length: ${textLength}`,
            });
        }
        return errors.length ? errors : null;
    }
    focus() {
        const editor = this.quillEditor();
        if (!editor) {
            return;
        }
        editor.focus();
    }
    addQuillEventListeners(dbt) {
        this.dispose();
        const editor = this.quillEditor();
        if (!editor) {
            return;
        }
        this.eventsSubscription = new Subscription();
        this.eventsSubscription.add(
        // mark model as touched if editor lost focus
        fromEvent(editor, 'selection-change').subscribe(([range, oldRange, source]) => {
            this.selectionChangeHandler(range, oldRange, source);
        }));
        // The `fromEvent` supports passing JQuery-style event targets, the editor has `on` and `off` methods which
        // will be invoked upon subscription and teardown.
        let textChange$ = fromEvent(editor, 'text-change');
        let editorChange$ = fromEvent(editor, 'editor-change');
        if (typeof dbt === 'number') {
            textChange$ = textChange$.pipe(debounceTime(dbt));
            editorChange$ = editorChange$.pipe(debounceTime(dbt));
        }
        this.eventsSubscription.add(
        // update model if text changes
        textChange$.subscribe(([delta, oldDelta, source]) => {
            this.textChangeHandler(delta, oldDelta, source);
        }));
        this.eventsSubscription.add(
        // triggered if selection or text changed
        editorChange$.subscribe(([event, current, old, source]) => {
            this.editorChangeHandler(event, current, old, source);
        }));
    }
    dispose() {
        this.eventsSubscription?.unsubscribe();
        this.eventsSubscription = null;
    }
    isEmptyValue(html) {
        return html === '<p></p>' || html === '<div></div>' || html === '<p><br></p>' || html === '<div><br></div>';
    }
    getter(quillEditor, forceFormat) {
        let modelValue = null;
        const format = forceFormat ?? getFormat$1(this.format(), this.service.config.format);
        if (format === 'html') {
            let html = quillEditor.getSemanticHTML();
            if (this.isEmptyValue(html)) {
                html = this.defaultEmptyValue();
            }
            modelValue = html;
        }
        else if (format === 'text') {
            modelValue = quillEditor.getText();
        }
        else if (format === 'object') {
            modelValue = quillEditor.getContents();
        }
        else if (format === 'json') {
            try {
                modelValue = JSON.stringify(quillEditor.getContents());
            }
            catch {
                modelValue = quillEditor.getText();
            }
        }
        return modelValue;
    }
    eventCallbackFormats() {
        const format = getFormat$1(this.format(), this.service.config.format);
        const onlyFormat = this.onlyFormatEventData() === true;
        const noFormat = this.onlyFormatEventData() === 'none';
        let text = null;
        let html = null;
        let object = null;
        let json = null;
        const editor = this.quillEditor();
        // do nothing if no formatted value needed
        if (noFormat || !editor) {
            return {
                format,
                onlyFormat,
                noFormat,
                text,
                object,
                json,
                html
            };
        }
        // use getter input to grab value
        const value = this.valueGetter()(editor);
        if (format === 'text') {
            text = value;
        }
        else if (format === 'html') {
            html = value;
        }
        else if (format === 'object') {
            object = value;
            json = JSON.stringify(value);
        }
        else if (format === 'json') {
            json = value;
            object = JSON.parse(value);
        }
        // return current values, if only the editor format is needed
        if (onlyFormat) {
            return {
                format,
                onlyFormat,
                noFormat,
                text,
                json,
                html,
                object
            };
        }
        // return all format values
        return {
            format,
            onlyFormat,
            noFormat,
            // use internal getter to retrieve correct other values - this.valueGetter can be overwritten
            text: format === 'text' ? text : this.getter(editor, 'text'),
            json: format === 'json' ? json : this.getter(editor, 'json'),
            html: format === 'html' ? html : this.getter(editor, 'html'),
            object: format === 'object' ? object : this.getter(editor, 'object')
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorFieldBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: QuillEditorFieldBase, isStandalone: true, inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, modules: { classPropertyName: "modules", publicName: "modules", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, formats: { classPropertyName: "formats", publicName: "formats", isSignal: true, isRequired: false, transformFunction: null }, customToolbarPosition: { classPropertyName: "customToolbarPosition", publicName: "customToolbarPosition", isSignal: true, isRequired: false, transformFunction: null }, sanitize: { classPropertyName: "sanitize", publicName: "sanitize", isSignal: true, isRequired: false, transformFunction: null }, beforeRender: { classPropertyName: "beforeRender", publicName: "beforeRender", isSignal: true, isRequired: false, transformFunction: null }, styles: { classPropertyName: "styles", publicName: "styles", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, bounds: { classPropertyName: "bounds", publicName: "bounds", isSignal: true, isRequired: false, transformFunction: null }, customOptions: { classPropertyName: "customOptions", publicName: "customOptions", isSignal: true, isRequired: false, transformFunction: null }, customModules: { classPropertyName: "customModules", publicName: "customModules", isSignal: true, isRequired: false, transformFunction: null }, trackChanges: { classPropertyName: "trackChanges", publicName: "trackChanges", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, trimOnValidation: { classPropertyName: "trimOnValidation", publicName: "trimOnValidation", isSignal: true, isRequired: false, transformFunction: null }, linkPlaceholder: { classPropertyName: "linkPlaceholder", publicName: "linkPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, compareValues: { classPropertyName: "compareValues", publicName: "compareValues", isSignal: true, isRequired: false, transformFunction: null }, filterNull: { classPropertyName: "filterNull", publicName: "filterNull", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null }, onlyFormatEventData: { classPropertyName: "onlyFormatEventData", publicName: "onlyFormatEventData", isSignal: true, isRequired: false, transformFunction: null }, defaultEmptyValue: { classPropertyName: "defaultEmptyValue", publicName: "defaultEmptyValue", isSignal: true, isRequired: false, transformFunction: null }, valueGetter: { classPropertyName: "valueGetter", publicName: "valueGetter", isSignal: true, isRequired: false, transformFunction: null }, valueSetter: { classPropertyName: "valueSetter", publicName: "valueSetter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange", onEditorCreated: "onEditorCreated", onEditorChanged: "onEditorChanged", onContentChanged: "onContentChanged", onSelectionChanged: "onSelectionChanged", onFocus: "onFocus", onBlur: "onBlur", onNativeFocus: "onNativeFocus", onNativeBlur: "onNativeBlur" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorFieldBase, decorators: [{
            type: Directive
        }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], modules: [{ type: i0.Input, args: [{ isSignal: true, alias: "modules", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], formats: [{ type: i0.Input, args: [{ isSignal: true, alias: "formats", required: false }] }], customToolbarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "customToolbarPosition", required: false }] }], sanitize: [{ type: i0.Input, args: [{ isSignal: true, alias: "sanitize", required: false }] }], beforeRender: [{ type: i0.Input, args: [{ isSignal: true, alias: "beforeRender", required: false }] }], styles: [{ type: i0.Input, args: [{ isSignal: true, alias: "styles", required: false }] }], registry: [{ type: i0.Input, args: [{ isSignal: true, alias: "registry", required: false }] }], bounds: [{ type: i0.Input, args: [{ isSignal: true, alias: "bounds", required: false }] }], customOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "customOptions", required: false }] }], customModules: [{ type: i0.Input, args: [{ isSignal: true, alias: "customModules", required: false }] }], trackChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackChanges", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], trimOnValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "trimOnValidation", required: false }] }], linkPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkPlaceholder", required: false }] }], compareValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareValues", required: false }] }], filterNull: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterNull", required: false }] }], debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }], onlyFormatEventData: [{ type: i0.Input, args: [{ isSignal: true, alias: "onlyFormatEventData", required: false }] }], defaultEmptyValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultEmptyValue", required: false }] }], onEditorCreated: [{
                type: Output
            }], onEditorChanged: [{
                type: Output
            }], onContentChanged: [{
                type: Output
            }], onSelectionChanged: [{
                type: Output
            }], onFocus: [{
                type: Output
            }], onBlur: [{
                type: Output
            }], onNativeFocus: [{
                type: Output
            }], onNativeBlur: [{
                type: Output
            }], valueGetter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueGetter", required: false }] }], valueSetter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueSetter", required: false }] }] } });
class QuillEditorFieldComponent extends QuillEditorFieldBase {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: QuillEditorFieldComponent, isStandalone: true, selector: "quill-editor-field", usesInheritance: true, ngImport: i0, template: `
    @if (toolbarPosition() !== 'top') {
        <div quill-editor-element></div>
    }

    <ng-content select="[above-quill-editor-toolbar]"></ng-content>
    <ng-content select="[quill-editor-toolbar]"></ng-content>
    <ng-content select="[below-quill-editor-toolbar]"></ng-content>

    @if (toolbarPosition() === 'top') {
        <div quill-editor-element></div>
    }
  `, isInline: true, styles: [":host{display:inline-block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorFieldComponent, decorators: [{
            type: Component,
            args: [{ encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'quill-editor-field', template: `
    @if (toolbarPosition() !== 'top') {
        <div quill-editor-element></div>
    }

    <ng-content select="[above-quill-editor-toolbar]"></ng-content>
    <ng-content select="[quill-editor-toolbar]"></ng-content>
    <ng-content select="[below-quill-editor-toolbar]"></ng-content>

    @if (toolbarPosition() === 'top') {
        <div quill-editor-element></div>
    }
  `, styles: [":host{display:inline-block}\n"] }]
        }] });

const getFormat = (format, configFormat) => {
    const passedFormat = format || configFormat;
    return passedFormat || 'html';
};

class QuillEditorBase {
    constructor() {
        this.format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
        this.theme = input(...(ngDevMode ? [undefined, { debugName: "theme" }] : /* istanbul ignore next */ []));
        this.modules = input(...(ngDevMode ? [undefined, { debugName: "modules" }] : /* istanbul ignore next */ []));
        this.debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
        this.readOnly = input(false, { ...(ngDevMode ? { debugName: "readOnly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
        this.maxLength = input(...(ngDevMode ? [undefined, { debugName: "maxLength" }] : /* istanbul ignore next */ []));
        this.minLength = input(...(ngDevMode ? [undefined, { debugName: "minLength" }] : /* istanbul ignore next */ []));
        this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.formats = input(...(ngDevMode ? [undefined, { debugName: "formats" }] : /* istanbul ignore next */ []));
        this.customToolbarPosition = input('top', ...(ngDevMode ? [{ debugName: "customToolbarPosition" }] : /* istanbul ignore next */ []));
        this.sanitize = input(...(ngDevMode ? [undefined, { debugName: "sanitize" }] : /* istanbul ignore next */ []));
        this.beforeRender = input(...(ngDevMode ? [undefined, { debugName: "beforeRender" }] : /* istanbul ignore next */ []));
        this.styles = input(null, ...(ngDevMode ? [{ debugName: "styles" }] : /* istanbul ignore next */ []));
        this.registry = input(...(ngDevMode ? [undefined, { debugName: "registry" }] : /* istanbul ignore next */ []));
        this.bounds = input(...(ngDevMode ? [undefined, { debugName: "bounds" }] : /* istanbul ignore next */ []));
        this.customOptions = input([], ...(ngDevMode ? [{ debugName: "customOptions" }] : /* istanbul ignore next */ []));
        this.customModules = input([], ...(ngDevMode ? [{ debugName: "customModules" }] : /* istanbul ignore next */ []));
        this.trackChanges = input(...(ngDevMode ? [undefined, { debugName: "trackChanges" }] : /* istanbul ignore next */ []));
        this.classes = input(...(ngDevMode ? [undefined, { debugName: "classes" }] : /* istanbul ignore next */ []));
        this.trimOnValidation = input(false, { ...(ngDevMode ? { debugName: "trimOnValidation" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.linkPlaceholder = input(...(ngDevMode ? [undefined, { debugName: "linkPlaceholder" }] : /* istanbul ignore next */ []));
        this.compareValues = input(false, { ...(ngDevMode ? { debugName: "compareValues" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.filterNull = input(false, { ...(ngDevMode ? { debugName: "filterNull" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
        this.debounceTime = input(...(ngDevMode ? [undefined, { debugName: "debounceTime" }] : /* istanbul ignore next */ []));
        this.onlyFormatEventData = input(false, ...(ngDevMode ? [{ debugName: "onlyFormatEventData" }] : /* istanbul ignore next */ []));
        /*
        https://github.com/KillerCodeMonkey/ngx-quill/issues/1257 - fix null value set
      
        provide default empty value
        by default null
      
        e.g. defaultEmptyValue="" - empty string
      
        <quill-editor
          defaultEmptyValue=""
          formControlName="message"
        ></quill-editor>
        */
        this.defaultEmptyValue = input(null, ...(ngDevMode ? [{ debugName: "defaultEmptyValue" }] : /* istanbul ignore next */ []));
        this.onEditorCreated = new EventEmitter();
        this.onEditorChanged = new EventEmitter();
        this.onContentChanged = new EventEmitter();
        this.onSelectionChanged = new EventEmitter();
        this.onFocus = new EventEmitter();
        this.onBlur = new EventEmitter();
        this.onNativeFocus = new EventEmitter();
        this.onNativeBlur = new EventEmitter();
        this.disabled = false; // used to store initial value before ViewInit
        this.toolbarPosition = signal('top', ...(ngDevMode ? [{ debugName: "toolbarPosition" }] : /* istanbul ignore next */ []));
        this.eventsSubscription = null;
        this.quillSubscription = null;
        this.elementRef = inject(ElementRef);
        this.domSanitizer = inject(DomSanitizer);
        this.platformId = inject(PLATFORM_ID);
        this.renderer = inject(Renderer2);
        this.service = inject(QuillService);
        this.destroyRef = inject(DestroyRef);
        this.init = false;
        this.valueGetter = input(this.getter.bind(this), ...(ngDevMode ? [{ debugName: "valueGetter" }] : /* istanbul ignore next */ []));
        this.valueSetter = input((quillEditor, value) => {
            const format = getFormat(this.format(), this.service.config.format);
            if (format === 'html') {
                const sanitize = (typeof this.sanitize() === 'boolean') ? this.sanitize() : (this.service.config.sanitize || false);
                if (sanitize) {
                    value = this.domSanitizer.sanitize(SecurityContext.HTML, value);
                }
                return quillEditor.clipboard.convert({ html: value });
            }
            else if (format === 'json') {
                try {
                    return JSON.parse(value);
                }
                catch {
                    return [{ insert: value }];
                }
            }
            return value;
        }, ...(ngDevMode ? [{ debugName: "valueSetter" }] : /* istanbul ignore next */ []));
        this.selectionChangeHandler = (range, oldRange, source) => {
            const trackChanges = this.trackChanges() || this.service.config.trackChanges;
            const shouldTriggerOnModelTouched = !range && !!this.onModelTouched && (source === 'user' || trackChanges && trackChanges === 'all');
            // only emit changes when there's any listener
            if (!this.onBlur.observed &&
                !this.onFocus.observed &&
                !this.onSelectionChanged.observed &&
                !shouldTriggerOnModelTouched) {
                return;
            }
            if (range === null) {
                this.onBlur.emit({
                    editor: this.quillEditor,
                    source
                });
            }
            else if (oldRange === null) {
                this.onFocus.emit({
                    editor: this.quillEditor,
                    source
                });
            }
            this.onSelectionChanged.emit({
                editor: this.quillEditor,
                oldRange,
                range,
                source
            });
            if (shouldTriggerOnModelTouched) {
                this.onModelTouched();
            }
        };
        this.textChangeHandler = (delta, oldDelta, source) => {
            const trackChanges = this.trackChanges() || this.service.config.trackChanges;
            const shouldTriggerOnModelChange = (source === 'user' || trackChanges && trackChanges === 'all') && !!this.onModelChange;
            // only emit changes when there's any listener
            if (!this.onContentChanged.observed && !shouldTriggerOnModelChange) {
                return;
            }
            const data = this.eventCallbackFormats();
            if (shouldTriggerOnModelChange) {
                this.onModelChange(
                // only call value getter again if not already done in eventCallbackFormats
                data.noFormat ? this.valueGetter()(this.quillEditor) : data[data.format]);
            }
            this.onContentChanged.emit({
                content: data.object,
                delta,
                editor: this.quillEditor,
                html: data.html,
                oldDelta,
                source,
                text: data.text
            });
        };
        this.editorChangeHandler = (event, current, old, source) => {
            // only emit changes when there's any listener
            if (!this.onEditorChanged.observed) {
                return;
            }
            // only emit changes emitted by user interactions
            if (event === 'text-change') {
                const data = this.eventCallbackFormats();
                this.onEditorChanged.emit({
                    content: data.object,
                    delta: current,
                    editor: this.quillEditor,
                    event,
                    html: data.html,
                    oldDelta: old,
                    source,
                    text: data.text
                });
            }
            else {
                this.onEditorChanged.emit({
                    editor: this.quillEditor,
                    event,
                    oldRange: old,
                    range: current,
                    source
                });
            }
        };
        afterNextRender(() => {
            if (isPlatformServer(this.platformId)) {
                return;
            }
            // The `quill-editor` component might be destroyed before the `quill` chunk is loaded and its code is executed
            // this will lead to runtime exceptions, since the code will be executed on DOM nodes that don't exist within the tree.
            this.quillSubscription = this.service.getQuill().pipe(mergeMap$1((Quill) => this.service.beforeRender(Quill, this.customModules(), this.beforeRender()))).subscribe(Quill => {
                this.editorElem = this.elementRef.nativeElement.querySelector('[quill-editor-element]');
                const toolbarElem = this.elementRef.nativeElement.querySelector('[quill-editor-toolbar]');
                const modules = Object.assign({}, this.modules() || this.service.config.modules);
                if (toolbarElem) {
                    modules.toolbar = toolbarElem;
                }
                else if (modules.toolbar === undefined) {
                    modules.toolbar = defaultModules.toolbar;
                }
                let placeholder = this.placeholder() !== undefined ? this.placeholder() : this.service.config.placeholder;
                if (placeholder === undefined) {
                    placeholder = 'Insert text here ...';
                }
                const styles = this.styles();
                if (styles) {
                    this.previousStyles = styles;
                    Object.keys(styles).forEach((key) => {
                        this.renderer.setStyle(this.editorElem, key, styles[key]);
                    });
                }
                const previousClasses = this.classes();
                if (previousClasses) {
                    this.previousClasses = previousClasses;
                    this.addClasses(previousClasses);
                }
                this.customOptions().forEach((customOption) => {
                    const newCustomOption = Quill.import(customOption.import);
                    newCustomOption.whitelist = customOption.whitelist;
                    Quill.register(newCustomOption, true);
                });
                let bounds = this.bounds() && this.bounds() === 'self' ? this.editorElem : this.bounds();
                if (!bounds) {
                    // Can use global `document` because we execute this only in the browser.
                    bounds = this.service.config.bounds ? this.service.config.bounds : document.body;
                }
                let debug = this.debug();
                if (!debug && debug !== false && this.service.config.debug) {
                    debug = this.service.config.debug;
                }
                let readOnly = this.readOnly();
                if (!readOnly && this.readOnly() !== false) {
                    readOnly = this.service.config.readOnly !== undefined ? this.service.config.readOnly : false;
                }
                let formats = this.formats();
                if (!formats && formats === undefined) {
                    formats = this.service.config.formats ? [...this.service.config.formats] : (this.service.config.formats === null ? null : undefined);
                }
                this.quillEditor = new Quill(this.editorElem, {
                    bounds,
                    debug,
                    formats,
                    modules,
                    placeholder,
                    readOnly,
                    registry: this.registry(),
                    theme: this.theme() || (this.service.config.theme ? this.service.config.theme : 'snow')
                });
                if (this.onNativeBlur.observed) {
                    // https://github.com/quilljs/quill/issues/2186#issuecomment-533401328
                    fromEvent(this.quillEditor.scroll.domNode, 'blur').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.onNativeBlur.next({
                        editor: this.quillEditor,
                        source: 'dom'
                    }));
                    // https://github.com/quilljs/quill/issues/2186#issuecomment-803257538
                    const toolbar = this.quillEditor.getModule('toolbar');
                    if (toolbar.container) {
                        fromEvent(toolbar.container, 'mousedown').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(e => e.preventDefault());
                    }
                }
                if (this.onNativeFocus.observed) {
                    fromEvent(this.quillEditor.scroll.domNode, 'focus').pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.onNativeFocus.next({
                        editor: this.quillEditor,
                        source: 'dom'
                    }));
                }
                // Set optional link placeholder, Quill has no native API for it so using workaround
                if (this.linkPlaceholder()) {
                    const tooltip = this.quillEditor?.theme?.tooltip;
                    const input = tooltip?.root?.querySelector('input[data-link]');
                    if (input?.dataset) {
                        input.dataset.link = this.linkPlaceholder();
                    }
                }
                if (this.content) {
                    const format = getFormat(this.format(), this.service.config.format);
                    if (format === 'text') {
                        this.quillEditor.setText(this.content, 'silent');
                    }
                    else {
                        const valueSetter = this.valueSetter();
                        const newValue = valueSetter(this.quillEditor, this.content);
                        this.quillEditor.setContents(newValue, 'silent');
                    }
                    const history = this.quillEditor.getModule('history');
                    history.clear();
                }
                // initialize disabled status based on this.disabled as default value
                this.setDisabledState();
                this.addQuillEventListeners();
                // listening to the `onEditorCreated` event inside the template, for instance `<quill-view (onEditorCreated)="...">`.
                if (!this.onEditorCreated.observed && !this.onValidatorChanged) {
                    this.init = true;
                    return;
                }
                if (this.onValidatorChanged) {
                    this.onValidatorChanged();
                }
                this.onEditorCreated.emit(this.quillEditor);
                this.init = true;
            });
        });
        effect(() => {
            const customToolbarPosition = this.customToolbarPosition();
            if (this.toolbarPosition() !== customToolbarPosition) {
                this.toolbarPosition.set(customToolbarPosition);
            }
        });
        effect(() => {
            const readOnly = this.readOnly();
            if (this.init) {
                if (readOnly) {
                    this.quillEditor?.disable();
                }
                else {
                    this.quillEditor?.enable(true);
                }
            }
        });
        effect(() => {
            const placeholder = this.placeholder();
            if (this.init && this.quillEditor) {
                this.quillEditor.root.dataset.placeholder = placeholder;
            }
        });
        effect(() => {
            const styles = this.styles();
            if (!this.init || !this.editorElem) {
                return;
            }
            const currentStyling = styles;
            const previousStyling = this.previousStyles;
            if (previousStyling) {
                Object.keys(previousStyling).forEach((key) => {
                    this.renderer.removeStyle(this.editorElem, key);
                });
            }
            if (currentStyling) {
                Object.keys(currentStyling).forEach((key) => {
                    this.renderer.setStyle(this.editorElem, key, currentStyling[key]);
                });
            }
        });
        effect(() => {
            const classes = this.classes();
            if (!this.init || !this.quillEditor) {
                return;
            }
            const currentClasses = classes;
            const previousClasses = this.previousClasses;
            if (previousClasses) {
                this.removeClasses(previousClasses);
            }
            if (currentClasses) {
                this.addClasses(currentClasses);
            }
        });
        effect(() => {
            const debounceTime = this.debounceTime();
            if (!this.init || !this.quillEditor) {
                return;
            }
            if (debounceTime) {
                this.addQuillEventListeners();
            }
        });
        this.destroyRef.onDestroy(() => {
            this.dispose();
            this.quillSubscription?.unsubscribe();
            this.quillSubscription = null;
        });
    }
    static normalizeClassNames(classes) {
        const classList = classes.trim().split(' ');
        return classList.reduce((prev, cur) => {
            const trimmed = cur.trim();
            if (trimmed) {
                prev.push(trimmed);
            }
            return prev;
        }, []);
    }
    addClasses(classList) {
        QuillEditorBase.normalizeClassNames(classList).forEach((c) => {
            this.renderer.addClass(this.editorElem, c);
        });
    }
    removeClasses(classList) {
        QuillEditorBase.normalizeClassNames(classList).forEach((c) => {
            this.renderer.removeClass(this.editorElem, c);
        });
    }
    writeValue(currentValue) {
        // optional fix for https://github.com/angular/angular/issues/14988
        if (this.filterNull() && currentValue === null) {
            return;
        }
        this.content = currentValue;
        if (!this.quillEditor) {
            return;
        }
        const format = getFormat(this.format(), this.service.config.format);
        const valueSetter = this.valueSetter();
        const newValue = valueSetter(this.quillEditor, currentValue);
        if (this.compareValues()) {
            const currentEditorValue = this.quillEditor.getContents();
            if (JSON.stringify(currentEditorValue) === JSON.stringify(newValue)) {
                return;
            }
        }
        if (currentValue) {
            if (format === 'text') {
                this.quillEditor.setText(currentValue);
            }
            else {
                this.quillEditor.setContents(newValue);
            }
            return;
        }
        this.quillEditor.setText('');
    }
    setDisabledState(isDisabled = this.disabled) {
        // store initial value to set appropriate disabled status after ViewInit
        this.disabled = isDisabled;
        if (this.quillEditor) {
            if (isDisabled) {
                this.quillEditor.disable();
                this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'disabled');
            }
            else {
                if (!this.readOnly()) {
                    this.quillEditor.enable();
                }
                this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
            }
        }
    }
    registerOnChange(fn) {
        this.onModelChange = fn;
    }
    registerOnTouched(fn) {
        this.onModelTouched = fn;
    }
    registerOnValidatorChange(fn) {
        this.onValidatorChanged = fn;
    }
    validate() {
        if (!this.quillEditor) {
            return null;
        }
        const err = {};
        let valid = true;
        const text = this.quillEditor.getText();
        // trim text if wanted + handle special case that an empty editor contains a new line
        const textLength = this.trimOnValidation() ? text.trim().length : (text.length === 1 && text.trim().length === 0 ? 0 : text.length - 1);
        const deltaOperations = this.quillEditor.getContents().ops;
        const onlyEmptyOperation = !!deltaOperations && deltaOperations.length === 1 && ['\n', ''].includes(deltaOperations[0].insert?.toString() || '');
        const minLength = this.minLength();
        if (minLength && textLength && textLength < minLength) {
            err.minLengthError = {
                given: textLength,
                minLength
            };
            valid = false;
        }
        const maxLength = this.maxLength();
        if (maxLength && textLength > maxLength) {
            err.maxLengthError = {
                given: textLength,
                maxLength
            };
            valid = false;
        }
        if (this.required() && !textLength && onlyEmptyOperation) {
            err.requiredError = {
                empty: true
            };
            valid = false;
        }
        return valid ? null : err;
    }
    addQuillEventListeners() {
        this.dispose();
        this.eventsSubscription = new Subscription();
        this.eventsSubscription.add(
        // mark model as touched if editor lost focus
        fromEvent(this.quillEditor, 'selection-change').subscribe(([range, oldRange, source]) => {
            this.selectionChangeHandler(range, oldRange, source);
        }));
        // The `fromEvent` supports passing JQuery-style event targets, the editor has `on` and `off` methods which
        // will be invoked upon subscription and teardown.
        let textChange$ = fromEvent(this.quillEditor, 'text-change');
        let editorChange$ = fromEvent(this.quillEditor, 'editor-change');
        const _debounceTime = this.debounceTime();
        if (typeof _debounceTime === 'number') {
            textChange$ = textChange$.pipe(debounceTime(_debounceTime));
            editorChange$ = editorChange$.pipe(debounceTime(_debounceTime));
        }
        this.eventsSubscription.add(
        // update model if text changes
        textChange$.subscribe(([delta, oldDelta, source]) => {
            this.textChangeHandler(delta, oldDelta, source);
        }));
        this.eventsSubscription.add(
        // triggered if selection or text changed
        editorChange$.subscribe(([event, current, old, source]) => {
            this.editorChangeHandler(event, current, old, source);
        }));
    }
    dispose() {
        this.eventsSubscription?.unsubscribe();
        this.eventsSubscription = null;
    }
    isEmptyValue(html) {
        return html === '<p></p>' || html === '<div></div>' || html === '<p><br></p>' || html === '<div><br></div>';
    }
    getter(quillEditor, forceFormat) {
        let modelValue = null;
        const format = forceFormat ?? getFormat(this.format(), this.service.config.format);
        if (format === 'html') {
            let html = quillEditor.getSemanticHTML();
            if (this.isEmptyValue(html)) {
                html = this.defaultEmptyValue();
            }
            modelValue = html;
        }
        else if (format === 'text') {
            modelValue = quillEditor.getText();
        }
        else if (format === 'object') {
            modelValue = quillEditor.getContents();
        }
        else if (format === 'json') {
            try {
                modelValue = JSON.stringify(quillEditor.getContents());
            }
            catch {
                modelValue = quillEditor.getText();
            }
        }
        return modelValue;
    }
    eventCallbackFormats() {
        const format = getFormat(this.format(), this.service.config.format);
        const onlyFormat = this.onlyFormatEventData() === true;
        const noFormat = this.onlyFormatEventData() === 'none';
        let text = null;
        let html = null;
        let object = null;
        let json = null;
        // do nothing if no formatted value needed
        if (noFormat) {
            return {
                format,
                onlyFormat,
                noFormat,
                text,
                object,
                json,
                html
            };
        }
        // use getter input to grab value
        const value = this.valueGetter()(this.quillEditor);
        if (format === 'text') {
            text = value;
        }
        else if (format === 'html') {
            html = value;
        }
        else if (format === 'object') {
            object = value;
            json = JSON.stringify(value);
        }
        else if (format === 'json') {
            json = value;
            object = JSON.parse(value);
        }
        // return current values, if only the editor format is needed
        if (onlyFormat) {
            return {
                format,
                onlyFormat,
                noFormat,
                text,
                json,
                html,
                object
            };
        }
        // return all format values
        return {
            format,
            onlyFormat,
            noFormat,
            // use internal getter to retrieve correct other values - this.valueGetter can be overwritten
            text: format === 'text' ? text : this.getter(this.quillEditor, 'text'),
            json: format === 'json' ? json : this.getter(this.quillEditor, 'json'),
            html: format === 'html' ? html : this.getter(this.quillEditor, 'html'),
            object: format === 'object' ? object : this.getter(this.quillEditor, 'object')
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: QuillEditorBase, isStandalone: true, inputs: { format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, modules: { classPropertyName: "modules", publicName: "modules", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, formats: { classPropertyName: "formats", publicName: "formats", isSignal: true, isRequired: false, transformFunction: null }, customToolbarPosition: { classPropertyName: "customToolbarPosition", publicName: "customToolbarPosition", isSignal: true, isRequired: false, transformFunction: null }, sanitize: { classPropertyName: "sanitize", publicName: "sanitize", isSignal: true, isRequired: false, transformFunction: null }, beforeRender: { classPropertyName: "beforeRender", publicName: "beforeRender", isSignal: true, isRequired: false, transformFunction: null }, styles: { classPropertyName: "styles", publicName: "styles", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, bounds: { classPropertyName: "bounds", publicName: "bounds", isSignal: true, isRequired: false, transformFunction: null }, customOptions: { classPropertyName: "customOptions", publicName: "customOptions", isSignal: true, isRequired: false, transformFunction: null }, customModules: { classPropertyName: "customModules", publicName: "customModules", isSignal: true, isRequired: false, transformFunction: null }, trackChanges: { classPropertyName: "trackChanges", publicName: "trackChanges", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, trimOnValidation: { classPropertyName: "trimOnValidation", publicName: "trimOnValidation", isSignal: true, isRequired: false, transformFunction: null }, linkPlaceholder: { classPropertyName: "linkPlaceholder", publicName: "linkPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, compareValues: { classPropertyName: "compareValues", publicName: "compareValues", isSignal: true, isRequired: false, transformFunction: null }, filterNull: { classPropertyName: "filterNull", publicName: "filterNull", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null }, onlyFormatEventData: { classPropertyName: "onlyFormatEventData", publicName: "onlyFormatEventData", isSignal: true, isRequired: false, transformFunction: null }, defaultEmptyValue: { classPropertyName: "defaultEmptyValue", publicName: "defaultEmptyValue", isSignal: true, isRequired: false, transformFunction: null }, valueGetter: { classPropertyName: "valueGetter", publicName: "valueGetter", isSignal: true, isRequired: false, transformFunction: null }, valueSetter: { classPropertyName: "valueSetter", publicName: "valueSetter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onEditorCreated: "onEditorCreated", onEditorChanged: "onEditorChanged", onContentChanged: "onContentChanged", onSelectionChanged: "onSelectionChanged", onFocus: "onFocus", onBlur: "onBlur", onNativeFocus: "onNativeFocus", onNativeBlur: "onNativeBlur" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorBase, decorators: [{
            type: Directive
        }], ctorParameters: () => [], propDecorators: { format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], modules: [{ type: i0.Input, args: [{ isSignal: true, alias: "modules", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], formats: [{ type: i0.Input, args: [{ isSignal: true, alias: "formats", required: false }] }], customToolbarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "customToolbarPosition", required: false }] }], sanitize: [{ type: i0.Input, args: [{ isSignal: true, alias: "sanitize", required: false }] }], beforeRender: [{ type: i0.Input, args: [{ isSignal: true, alias: "beforeRender", required: false }] }], styles: [{ type: i0.Input, args: [{ isSignal: true, alias: "styles", required: false }] }], registry: [{ type: i0.Input, args: [{ isSignal: true, alias: "registry", required: false }] }], bounds: [{ type: i0.Input, args: [{ isSignal: true, alias: "bounds", required: false }] }], customOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "customOptions", required: false }] }], customModules: [{ type: i0.Input, args: [{ isSignal: true, alias: "customModules", required: false }] }], trackChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackChanges", required: false }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }], trimOnValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "trimOnValidation", required: false }] }], linkPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkPlaceholder", required: false }] }], compareValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareValues", required: false }] }], filterNull: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterNull", required: false }] }], debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }], onlyFormatEventData: [{ type: i0.Input, args: [{ isSignal: true, alias: "onlyFormatEventData", required: false }] }], defaultEmptyValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultEmptyValue", required: false }] }], onEditorCreated: [{
                type: Output
            }], onEditorChanged: [{
                type: Output
            }], onContentChanged: [{
                type: Output
            }], onSelectionChanged: [{
                type: Output
            }], onFocus: [{
                type: Output
            }], onBlur: [{
                type: Output
            }], onNativeFocus: [{
                type: Output
            }], onNativeBlur: [{
                type: Output
            }], valueGetter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueGetter", required: false }] }], valueSetter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueSetter", required: false }] }] } });
class QuillEditorComponent extends QuillEditorBase {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: QuillEditorComponent, isStandalone: true, selector: "quill-editor", providers: [
            {
                multi: true,
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => QuillEditorComponent)
            },
            {
                multi: true,
                provide: NG_VALIDATORS,
                useExisting: forwardRef(() => QuillEditorComponent)
            }
        ], usesInheritance: true, ngImport: i0, template: `
    @if (toolbarPosition() !== 'top') {
        <div quill-editor-element></div>
    }

    <ng-content select="[above-quill-editor-toolbar]"></ng-content>
    <ng-content select="[quill-editor-toolbar]"></ng-content>
    <ng-content select="[below-quill-editor-toolbar]"></ng-content>

    @if (toolbarPosition() === 'top') {
        <div quill-editor-element></div>
    }
  `, isInline: true, styles: [":host{display:inline-block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillEditorComponent, decorators: [{
            type: Component,
            args: [{ encapsulation: ViewEncapsulation.Emulated, providers: [
                        {
                            multi: true,
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => QuillEditorComponent)
                        },
                        {
                            multi: true,
                            provide: NG_VALIDATORS,
                            useExisting: forwardRef(() => QuillEditorComponent)
                        }
                    ], changeDetection: ChangeDetectionStrategy.OnPush, selector: 'quill-editor', template: `
    @if (toolbarPosition() !== 'top') {
        <div quill-editor-element></div>
    }

    <ng-content select="[above-quill-editor-toolbar]"></ng-content>
    <ng-content select="[quill-editor-toolbar]"></ng-content>
    <ng-content select="[below-quill-editor-toolbar]"></ng-content>

    @if (toolbarPosition() === 'top') {
        <div quill-editor-element></div>
    }
  `, styles: [":host{display:inline-block}\n"] }]
        }] });

class QuillViewHTMLComponent {
    constructor() {
        this.content = input('', ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
        this.theme = input(...(ngDevMode ? [undefined, { debugName: "theme" }] : /* istanbul ignore next */ []));
        this.sanitize = input(...(ngDevMode ? [undefined, { debugName: "sanitize" }] : /* istanbul ignore next */ []));
        this.innerHTML = computed(() => {
            const sanitize = this.sanitize();
            const content = this.content();
            return ((typeof sanitize === 'boolean') ? sanitize : (this.service.config.sanitize || false)) ? content : this.sanitizer.bypassSecurityTrustHtml(content);
        }, ...(ngDevMode ? [{ debugName: "innerHTML" }] : /* istanbul ignore next */ []));
        this.themeClass = computed(() => {
            const base = this.service.config.theme ? this.service.config.theme : 'snow';
            return `ql-${this.theme() || base} ngx-quill-view-html`;
        }, ...(ngDevMode ? [{ debugName: "themeClass" }] : /* istanbul ignore next */ []));
        this.sanitizer = inject(DomSanitizer);
        this.service = inject(QuillService);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillViewHTMLComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: QuillViewHTMLComponent, isStandalone: true, selector: "quill-view-html", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, sanitize: { classPropertyName: "sanitize", publicName: "sanitize", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
  <div class="ql-container" [class]="themeClass()">
    <div class="ql-editor" [innerHTML]="innerHTML()">
    </div>
  </div>
`, isInline: true, styles: [".ql-container.ngx-quill-view-html{border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillViewHTMLComponent, decorators: [{
            type: Component,
            args: [{ changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, selector: 'quill-view-html', template: `
  <div class="ql-container" [class]="themeClass()">
    <div class="ql-editor" [innerHTML]="innerHTML()">
    </div>
  </div>
`, styles: [".ql-container.ngx-quill-view-html{border:0}\n"] }]
        }], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], sanitize: [{ type: i0.Input, args: [{ isSignal: true, alias: "sanitize", required: false }] }] } });

class QuillViewComponent {
    constructor() {
        this.format = input(...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
        this.theme = input(...(ngDevMode ? [undefined, { debugName: "theme" }] : /* istanbul ignore next */ []));
        this.modules = input(...(ngDevMode ? [undefined, { debugName: "modules" }] : /* istanbul ignore next */ []));
        this.debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
        this.formats = input(...(ngDevMode ? [undefined, { debugName: "formats" }] : /* istanbul ignore next */ []));
        this.sanitize = input(...(ngDevMode ? [undefined, { debugName: "sanitize" }] : /* istanbul ignore next */ []));
        this.beforeRender = input(...(ngDevMode ? [undefined, { debugName: "beforeRender" }] : /* istanbul ignore next */ []));
        this.strict = input(true, ...(ngDevMode ? [{ debugName: "strict" }] : /* istanbul ignore next */ []));
        this.content = input(...(ngDevMode ? [undefined, { debugName: "content" }] : /* istanbul ignore next */ []));
        this.customModules = input([], ...(ngDevMode ? [{ debugName: "customModules" }] : /* istanbul ignore next */ []));
        this.customOptions = input([], ...(ngDevMode ? [{ debugName: "customOptions" }] : /* istanbul ignore next */ []));
        this.onEditorCreated = new EventEmitter();
        this.init = false;
        this.elementRef = inject(ElementRef);
        this.renderer = inject(Renderer2);
        this.service = inject(QuillService);
        this.sanitizer = inject(DomSanitizer);
        this.platformId = inject(PLATFORM_ID);
        this.destroyRef = inject(DestroyRef);
        this.valueSetter = (quillEditor, value) => {
            const format = getFormat(this.format(), this.service.config.format);
            let content = value;
            if (format === 'text') {
                quillEditor.setText(content);
            }
            else {
                if (format === 'html') {
                    const sanitize = (typeof this.sanitize() === 'boolean') ? this.sanitize() : (this.service.config.sanitize || false);
                    if (sanitize) {
                        value = this.sanitizer.sanitize(SecurityContext.HTML, value);
                    }
                    content = quillEditor.clipboard.convert({ html: value });
                }
                else if (format === 'json') {
                    try {
                        content = JSON.parse(value);
                    }
                    catch {
                        content = [{ insert: value }];
                    }
                }
                quillEditor.setContents(content);
            }
        };
        afterNextRender(() => {
            if (isPlatformServer(this.platformId)) {
                return;
            }
            const quillSubscription = this.service.getQuill().pipe(mergeMap$1((Quill) => this.service.beforeRender(Quill, this.customModules(), this.beforeRender()))).subscribe(Quill => {
                const modules = Object.assign({}, this.modules() || this.service.config.modules);
                modules.toolbar = false;
                this.customOptions().forEach((customOption) => {
                    const newCustomOption = Quill.import(customOption.import);
                    newCustomOption.whitelist = customOption.whitelist;
                    Quill.register(newCustomOption, true);
                });
                let debug = this.debug();
                if (!debug && debug !== false && this.service.config.debug) {
                    debug = this.service.config.debug;
                }
                let formats = this.formats();
                if (formats === undefined) {
                    formats = this.service.config.formats ? [...this.service.config.formats] : (this.service.config.formats === null ? null : undefined);
                }
                const theme = this.theme() || (this.service.config.theme ? this.service.config.theme : 'snow');
                this.editorElem = this.elementRef.nativeElement.querySelector('[quill-view-element]');
                this.quillEditor = new Quill(this.editorElem, {
                    debug,
                    formats,
                    modules,
                    readOnly: true,
                    strict: this.strict(),
                    theme
                });
                this.renderer.addClass(this.editorElem, 'ngx-quill-view');
                if (this.content()) {
                    this.valueSetter(this.quillEditor, this.content());
                }
                // listening to the `onEditorCreated` event inside the template, for instance `<quill-view (onEditorCreated)="...">`.
                if (!this.onEditorCreated.observed) {
                    this.init = true;
                    return;
                }
                this.onEditorCreated.emit(this.quillEditor);
                this.init = true;
            });
            this.destroyRef.onDestroy(() => quillSubscription.unsubscribe());
        });
        effect(() => {
            const content = this.content();
            if (!this.quillEditor || !this.init) {
                return;
            }
            if (content) {
                this.valueSetter(this.quillEditor, content);
            }
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: QuillViewComponent, isStandalone: true, selector: "quill-view", inputs: { format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, modules: { classPropertyName: "modules", publicName: "modules", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, formats: { classPropertyName: "formats", publicName: "formats", isSignal: true, isRequired: false, transformFunction: null }, sanitize: { classPropertyName: "sanitize", publicName: "sanitize", isSignal: true, isRequired: false, transformFunction: null }, beforeRender: { classPropertyName: "beforeRender", publicName: "beforeRender", isSignal: true, isRequired: false, transformFunction: null }, strict: { classPropertyName: "strict", publicName: "strict", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, customModules: { classPropertyName: "customModules", publicName: "customModules", isSignal: true, isRequired: false, transformFunction: null }, customOptions: { classPropertyName: "customOptions", publicName: "customOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onEditorCreated: "onEditorCreated" }, ngImport: i0, template: `
  <div quill-view-element></div>
`, isInline: true, styles: [".ql-container.ngx-quill-view{border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillViewComponent, decorators: [{
            type: Component,
            args: [{ changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, selector: 'quill-view', template: `
  <div quill-view-element></div>
`, styles: [".ql-container.ngx-quill-view{border:0}\n"] }]
        }], ctorParameters: () => [], propDecorators: { format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], modules: [{ type: i0.Input, args: [{ isSignal: true, alias: "modules", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], formats: [{ type: i0.Input, args: [{ isSignal: true, alias: "formats", required: false }] }], sanitize: [{ type: i0.Input, args: [{ isSignal: true, alias: "sanitize", required: false }] }], beforeRender: [{ type: i0.Input, args: [{ isSignal: true, alias: "beforeRender", required: false }] }], strict: [{ type: i0.Input, args: [{ isSignal: true, alias: "strict", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], customModules: [{ type: i0.Input, args: [{ isSignal: true, alias: "customModules", required: false }] }], customOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "customOptions", required: false }] }], onEditorCreated: [{
                type: Output
            }] } });

class QuillModule {
    static forRoot(config) {
        return {
            ngModule: QuillModule,
            providers: [
                {
                    provide: QUILL_CONFIG_TOKEN,
                    useValue: config
                }
            ]
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: QuillModule, imports: [QuillEditorComponent, QuillViewComponent, QuillViewHTMLComponent], exports: [QuillEditorComponent, QuillViewComponent, QuillViewHTMLComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: QuillModule, decorators: [{
            type: NgModule,
            args: [{
                    imports: [QuillEditorComponent, QuillViewComponent, QuillViewHTMLComponent],
                    exports: [QuillEditorComponent, QuillViewComponent, QuillViewHTMLComponent],
                }]
        }] });

/*
 * Public API Surface of ngx-quill
 */
// Re-export everything from the secondary entry-point so we can be backwards-compatible
// and don't introduce breaking changes for consumers.

/**
 * Generated bundle index. Do not edit.
 */

export { QuillEditorBase, QuillEditorComponent, QuillEditorFieldBase, QuillEditorFieldComponent, QuillModule, QuillService, QuillViewComponent, QuillViewHTMLComponent, ValidationKind, getFormat$1 as getFormat };
//# sourceMappingURL=ngx-quill.mjs.map