primeng
Version:
PrimeNG is a premium UI library for Angular featuring a rich set of 90+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBlock,
497 lines (492 loc) • 25.9 kB
JavaScript
export * from 'primeng/types/editor';
import { isPlatformServer, NgTemplateOutlet } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, InjectionToken, forwardRef, inject, input, output, contentChild, computed, effect, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { findSingle } from '@primeuix/utils';
import { Header, SharedModule } from 'primeng/api';
import { PARENT_INSTANCE } from 'primeng/basecomponent';
import { BaseEditableHolder } from 'primeng/baseeditableholder';
import * as i1 from 'primeng/bind';
import { Bind, BindModule } from 'primeng/bind';
import { style } from '@primeuix/styles/editor';
import { BaseStyle } from 'primeng/base';
const classes = {
root: ({ instance }) => [
'p-editor',
{
'p-invalid': instance.invalid()
}
],
toolbar: 'p-editor-toolbar',
content: 'p-editor-content'
};
class EditorStyle extends BaseStyle {
name = 'editor';
style = style;
classes = classes;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorStyle });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorStyle, decorators: [{
type: Injectable
}] });
/**
*
* Editor groups a collection of contents in tabs.
*
* [Live Demo](https://www.primeng.org/editor/)
*
* @module editorstyle
*
*/
var EditorClasses;
(function (EditorClasses) {
/**
* Class name of the root element
*/
EditorClasses["root"] = "p-editor";
/**
* Class name of the toolbar element
*/
EditorClasses["toolbar"] = "p-editor-toolbar";
/**
* Class name of the content element
*/
EditorClasses["content"] = "p-editor-content";
})(EditorClasses || (EditorClasses = {}));
const EDITOR_INSTANCE = new InjectionToken('EDITOR_INSTANCE');
const EDITOR_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Editor),
multi: true
};
/**
* Editor groups a collection of contents in tabs.
* @group Components
* @deprecated Editor will be removed in a future release in favor of PrimeUI PRO Editor.
*/
class Editor extends BaseEditableHolder {
componentName = 'Editor';
$pcEditor = inject(EDITOR_INSTANCE, { optional: true, skipSelf: true }) ?? undefined;
bindDirectiveInstance = inject(Bind, { self: true });
onAfterViewChecked() {
this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root']));
}
/**
* Inline style of the container.
* @group Props
*/
style = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "style" }] : /* istanbul ignore next */ []));
/**
* Placeholder text to show when editor is empty.
* @group Props
*/
placeholder = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
/**
* Whitelist of formats to display, see [here](https://quilljs.com/docs/formats/) for available options.
* @group Props
*/
formats = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "formats" }] : /* istanbul ignore next */ []));
/**
* Modules configuration of Editor, see [here](https://quilljs.com/docs/modules/) for available options.
* @group Props
*/
modules = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "modules" }] : /* istanbul ignore next */ []));
/**
* DOM Element or a CSS selector for a DOM Element, within which the editor's p elements (i.e. tooltips, etc.) should be confined. Currently, it only considers left and right boundaries.
* @group Props
*/
bounds = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "bounds" }] : /* istanbul ignore next */ []));
/**
* DOM Element or a CSS selector for a DOM Element, specifying which container has the scrollbars (i.e. overflow-y: auto), if is has been changed from the default ql-editor with custom CSS. Necessary to fix scroll jumping bugs when Quill is set to auto grow its height, and another ancestor container is responsible from the scrolling..
* @group Props
*/
scrollingContainer = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "scrollingContainer" }] : /* istanbul ignore next */ []));
/**
* Shortcut for debug. Note debug is a static method and will affect other instances of Quill editors on the page. Only warning and error messages are enabled by default.
* @group Props
*/
debug = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "debug" }] : /* istanbul ignore next */ []));
/**
* Whether to instantiate the editor to read-only mode.
* @group Props
*/
readonly = input(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
/**
* Callback to invoke when the quill modules are loaded.
* @param {EditorInitEvent} event - custom event.
* @group Emits
*/
onEditorInit = output({ alias: 'onInit' });
/**
* Callback to invoke when text of editor changes.
* @param {EditorTextChangeEvent} event - custom event.
* @group Emits
*/
onTextChange = output();
/**
* Callback to invoke when selection of the text changes.
* @param {EditorSelectionChangeEvent} event - custom event.
* @group Emits
*/
onSelectionChange = output();
/**
* Callback to invoke when editor content changes (combines both text and selection changes).
* @param {EditorChangeEvent} event - custom event.
* @group Emits
*/
onEditorChange = output();
/**
* Callback to invoke when editor receives focus.
* @param {EditorFocusEvent} event - custom event.
* @group Emits
*/
onFocus = output();
/**
* Callback to invoke when editor loses focus.
* @param {EditorBlurEvent} event - custom event.
* @group Emits
*/
onBlur = output();
toolbar = contentChild(Header, { ...(ngDevMode ? { debugName: "toolbar" } : /* istanbul ignore next */ {}), descendants: false });
/**
* Custom item template.
* @group Templates
*/
headerTemplate = contentChild('header', { ...(ngDevMode ? { debugName: "headerTemplate" } : /* istanbul ignore next */ {}), descendants: false });
$hasCustomToolbar = computed(() => !!this.toolbar() || !!this.headerTemplate(), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "$hasCustomToolbar" }] : /* istanbul ignore next */ []));
value;
delayedCommand = null;
quill;
dynamicQuill;
_destroyed = false;
get isAttachedQuillEditorToDOM() {
return this.quillElements?.editorElement?.isConnected;
}
quillElements;
focusListener = null;
blurListener = null;
_componentStyle = inject(EditorStyle);
constructor() {
super();
effect(() => {
const isReadonly = this.readonly();
if (this.quill) {
if (isReadonly)
this.quill.disable();
else
this.quill.enable();
}
});
/**
* Read or write the DOM once, when initializing non-Angular (Quill) library.
*/
afterNextRender(() => {
this.initQuillElements();
this.initQuillEditor();
});
}
/**
* @override
*
* @see {@link BaseEditableHolder.writeControlValue}
* Writes the value to the control.
*/
writeControlValue(value) {
if (this.quill && this.value === value) {
return;
}
this.value = value;
if (this.quill) {
if (value) {
const command = () => {
this.quill.setContents(this.quill.clipboard.convert(this.dynamicQuill.version.startsWith('2') ? { html: this.value } : this.value));
};
if (this.isAttachedQuillEditorToDOM) {
command();
}
else {
this.delayedCommand = command;
}
}
else {
const command = () => {
this.quill.setText('');
};
if (this.isAttachedQuillEditorToDOM) {
command();
}
else {
this.delayedCommand = command;
}
}
}
}
getQuill() {
return this.quill;
}
initQuillEditor() {
if (isPlatformServer(this.platformId)) {
return;
}
/**
* Importing Quill at top level, throws `document is undefined` error during when
* building for SSR, so this dynamically loads quill when it's in browser module.
*/
if (!this.dynamicQuill) {
import('quill')
.then((quillModule) => {
this.dynamicQuill = quillModule.default;
this.createQuillEditor();
})
.catch((e) => console.error(e.message));
}
else {
this.createQuillEditor();
}
}
createQuillEditor() {
// The Quill module is imported asynchronously; if the component was destroyed before the
// import resolved, bail so we don't build Quill and emit (onEditorInit etc.) after destroy.
if (this._destroyed)
return;
this.initQuillElements();
const { toolbarElement, editorElement } = this.quillElements;
let defaultModule = { toolbar: toolbarElement };
let modules = this.modules() ? { ...defaultModule, ...this.modules() } : defaultModule;
this.quill = new this.dynamicQuill(editorElement, {
modules: modules,
placeholder: this.placeholder(),
readOnly: this.readonly(),
theme: 'snow',
formats: this.formats(),
bounds: this.bounds(),
debug: this.debug(),
scrollingContainer: this.scrollingContainer()
});
const isQuill2 = this.dynamicQuill.version.startsWith('2');
if (this.value) {
this.quill.setContents(this.quill.clipboard.convert(isQuill2 ? { html: this.value } : this.value));
}
this.quill.on('text-change', (delta, oldContents, source) => {
if (this._destroyed)
return;
if (source === 'user') {
let html = isQuill2 ? this.quill.getSemanticHTML() : findSingle(editorElement, '.ql-editor')?.innerHTML;
let text = this.quill.getText().trim();
if (html === '<p><br></p>') {
html = null;
}
this.onTextChange.emit({
htmlValue: html,
textValue: text,
delta: delta,
source: source
});
this.value = html;
this.onModelChange(html);
this.onModelTouched();
}
});
this.quill.on('selection-change', (range, oldRange, source) => {
if (this._destroyed)
return;
this.onSelectionChange.emit({
range: range,
oldRange: oldRange,
source: source
});
});
this.quill.on('editor-change', (eventName, ...args) => {
if (this._destroyed)
return;
this.onEditorChange.emit({
eventName: eventName,
args: args
});
});
const editorEl = this.quill.root;
this.focusListener = () => {
this.onFocus.emit({
source: 'user'
});
};
this.blurListener = () => {
this.onBlur.emit({
source: 'user'
});
};
editorEl.addEventListener('focus', this.focusListener);
editorEl.addEventListener('blur', this.blurListener);
this.onEditorInit.emit({
editor: this.quill
});
}
onDestroy() {
this._destroyed = true;
if (this.quill && this.quill.root) {
const editorEl = this.quill.root;
if (this.focusListener) {
editorEl.removeEventListener('focus', this.focusListener);
this.focusListener = null;
}
if (this.blurListener) {
editorEl.removeEventListener('blur', this.blurListener);
this.blurListener = null;
}
}
}
initQuillElements() {
if (!this.quillElements) {
this.quillElements = {
editorElement: findSingle(this.el.nativeElement, 'div[data-pc-section="content"]'),
toolbarElement: findSingle(this.el.nativeElement, 'div[data-pc-section="toolbar"]')
};
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: Editor, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: Editor, isStandalone: true, selector: "p-editor", inputs: { style: { classPropertyName: "style", publicName: "style", 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 }, modules: { classPropertyName: "modules", publicName: "modules", isSignal: true, isRequired: false, transformFunction: null }, bounds: { classPropertyName: "bounds", publicName: "bounds", isSignal: true, isRequired: false, transformFunction: null }, scrollingContainer: { classPropertyName: "scrollingContainer", publicName: "scrollingContainer", 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 } }, outputs: { onEditorInit: "onInit", onTextChange: "onTextChange", onSelectionChange: "onSelectionChange", onEditorChange: "onEditorChange", onFocus: "onFocus", onBlur: "onBlur" }, host: { properties: { "class": "cn(cx('root'))" } }, providers: [EDITOR_VALUE_ACCESSOR, EditorStyle, { provide: EDITOR_INSTANCE, useExisting: Editor }, { provide: PARENT_INSTANCE, useExisting: Editor }], queries: [{ propertyName: "toolbar", first: true, predicate: Header, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: ["header"], isSignal: true }], usesInheritance: true, hostDirectives: [{ directive: i1.Bind }], ngImport: i0, template: `
($hasCustomToolbar()) {
<div [class]="cx('toolbar')" [pBind]="ptm('toolbar')">
<ng-content select="p-header" />
<ng-container *ngTemplateOutlet="headerTemplate()"></ng-container>
</div>
} {
<div [class]="cx('toolbar')" [pBind]="ptm('toolbar')">
<span class="ql-formats" [pBind]="ptm('formats')">
<select class="ql-header" [pBind]="ptm('header')">
<option value="1" [pBind]="ptm('option')">Heading</option>
<option value="2" [pBind]="ptm('option')">Subheading</option>
<option selected [pBind]="ptm('option')">Normal</option>
</select>
<select class="ql-font" [pBind]="ptm('select')">
<option selected [pBind]="ptm('option')">Sans Serif</option>
<option value="serif" [pBind]="ptm('option')">Serif</option>
<option value="monospace" [pBind]="ptm('option')">Monospace</option>
</select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-bold" aria-label="Bold" type="button" [pBind]="ptm('bold')"></button>
<button class="ql-italic" aria-label="Italic" type="button" [pBind]="ptm('italic')"></button>
<button class="ql-underline" aria-label="Underline" type="button" [pBind]="ptm('underline')"></button>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<select class="ql-color" [pBind]="ptm('color')"></select>
<select class="ql-background" [pBind]="ptm('background')"></select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-list" value="ordered" aria-label="Ordered List" type="button" [pBind]="ptm('list')"></button>
<button class="ql-list" value="bullet" aria-label="Unordered List" type="button" [pBind]="ptm('list')"></button>
<select class="ql-align" [pBind]="ptm('select')">
<option selected [pBind]="ptm('option')"></option>
<option value="center" [pBind]="ptm('option')">center</option>
<option value="right" [pBind]="ptm('option')">right</option>
<option value="justify" [pBind]="ptm('option')">justify</option>
</select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-link" aria-label="Insert Link" type="button" [pBind]="ptm('link')"></button>
<button class="ql-image" aria-label="Insert Image" type="button" [pBind]="ptm('image')"></button>
<button class="ql-code-block" aria-label="Insert Code Block" type="button" [pBind]="ptm('codeBlock')"></button>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-clean" aria-label="Remove Styles" type="button" [pBind]="ptm('clean')"></button>
</span>
</div>
}
<div [class]="cx('content')" [style]="style()" [pBind]="ptm('content')"></div>
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: BindModule }, { kind: "directive", type: i1.Bind, selector: "[pBind]", inputs: ["pBind"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: Editor, decorators: [{
type: Component,
args: [{
selector: 'p-editor',
standalone: true,
imports: [NgTemplateOutlet, SharedModule, BindModule],
template: `
($hasCustomToolbar()) {
<div [class]="cx('toolbar')" [pBind]="ptm('toolbar')">
<ng-content select="p-header" />
<ng-container *ngTemplateOutlet="headerTemplate()"></ng-container>
</div>
} {
<div [class]="cx('toolbar')" [pBind]="ptm('toolbar')">
<span class="ql-formats" [pBind]="ptm('formats')">
<select class="ql-header" [pBind]="ptm('header')">
<option value="1" [pBind]="ptm('option')">Heading</option>
<option value="2" [pBind]="ptm('option')">Subheading</option>
<option selected [pBind]="ptm('option')">Normal</option>
</select>
<select class="ql-font" [pBind]="ptm('select')">
<option selected [pBind]="ptm('option')">Sans Serif</option>
<option value="serif" [pBind]="ptm('option')">Serif</option>
<option value="monospace" [pBind]="ptm('option')">Monospace</option>
</select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-bold" aria-label="Bold" type="button" [pBind]="ptm('bold')"></button>
<button class="ql-italic" aria-label="Italic" type="button" [pBind]="ptm('italic')"></button>
<button class="ql-underline" aria-label="Underline" type="button" [pBind]="ptm('underline')"></button>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<select class="ql-color" [pBind]="ptm('color')"></select>
<select class="ql-background" [pBind]="ptm('background')"></select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-list" value="ordered" aria-label="Ordered List" type="button" [pBind]="ptm('list')"></button>
<button class="ql-list" value="bullet" aria-label="Unordered List" type="button" [pBind]="ptm('list')"></button>
<select class="ql-align" [pBind]="ptm('select')">
<option selected [pBind]="ptm('option')"></option>
<option value="center" [pBind]="ptm('option')">center</option>
<option value="right" [pBind]="ptm('option')">right</option>
<option value="justify" [pBind]="ptm('option')">justify</option>
</select>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-link" aria-label="Insert Link" type="button" [pBind]="ptm('link')"></button>
<button class="ql-image" aria-label="Insert Image" type="button" [pBind]="ptm('image')"></button>
<button class="ql-code-block" aria-label="Insert Code Block" type="button" [pBind]="ptm('codeBlock')"></button>
</span>
<span class="ql-formats" [pBind]="ptm('formats')">
<button class="ql-clean" aria-label="Remove Styles" type="button" [pBind]="ptm('clean')"></button>
</span>
</div>
}
<div [class]="cx('content')" [style]="style()" [pBind]="ptm('content')"></div>
`,
providers: [EDITOR_VALUE_ACCESSOR, EditorStyle, { provide: EDITOR_INSTANCE, useExisting: Editor }, { provide: PARENT_INSTANCE, useExisting: Editor }],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': "cn(cx('root'))"
},
hostDirectives: [Bind]
}]
}], ctorParameters: () => [], propDecorators: { style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], formats: [{ type: i0.Input, args: [{ isSignal: true, alias: "formats", required: false }] }], modules: [{ type: i0.Input, args: [{ isSignal: true, alias: "modules", required: false }] }], bounds: [{ type: i0.Input, args: [{ isSignal: true, alias: "bounds", required: false }] }], scrollingContainer: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollingContainer", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onEditorInit: [{ type: i0.Output, args: ["onInit"] }], onTextChange: [{ type: i0.Output, args: ["onTextChange"] }], onSelectionChange: [{ type: i0.Output, args: ["onSelectionChange"] }], onEditorChange: [{ type: i0.Output, args: ["onEditorChange"] }], onFocus: [{ type: i0.Output, args: ["onFocus"] }], onBlur: [{ type: i0.Output, args: ["onBlur"] }], toolbar: [{ type: i0.ContentChild, args: [i0.forwardRef(() => Header), { ...{ descendants: false }, isSignal: true }] }], headerTemplate: [{ type: i0.ContentChild, args: ['header', { ...{ descendants: false }, isSignal: true }] }] } });
class EditorModule {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.6", ngImport: i0, type: EditorModule, imports: [Editor, SharedModule], exports: [Editor, SharedModule] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorModule, imports: [Editor, SharedModule, SharedModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: EditorModule, decorators: [{
type: NgModule,
args: [{
imports: [Editor, SharedModule],
exports: [Editor, SharedModule]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { EDITOR_VALUE_ACCESSOR, Editor, EditorClasses, EditorModule, EditorStyle };
//# sourceMappingURL=primeng-editor.mjs.map