@tedi-design-system/angular 8.1.0-rc.5 → 8.1.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/community/text-editor/index.d.ts +129 -0
- package/community/text-editor/index.d.ts.map +1 -0
- package/fesm2022/tedi-design-system-angular-community-text-editor.mjs +378 -0
- package/fesm2022/tedi-design-system-angular-community-text-editor.mjs.map +1 -0
- package/fesm2022/tedi-design-system-angular-tedi.mjs +296 -18
- package/fesm2022/tedi-design-system-angular-tedi.mjs.map +1 -1
- package/package.json +16 -2
- package/tedi/index.d.ts +248 -11
- package/tedi/index.d.ts.map +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as _tedi_design_system_angular_tedi from '@tedi-design-system/angular/tedi';
|
|
2
|
+
import { FormFieldControl } from '@tedi-design-system/angular/tedi';
|
|
3
|
+
import * as _angular_core from '@angular/core';
|
|
4
|
+
import { OnInit } from '@angular/core';
|
|
5
|
+
import { ControlValueAccessor, FormControl } from '@angular/forms';
|
|
6
|
+
import { QuillModules } from 'ngx-quill';
|
|
7
|
+
import Quill from 'quill';
|
|
8
|
+
|
|
9
|
+
declare const TEXT_EDITOR_DEFAULT_MODULES: QuillModules;
|
|
10
|
+
/**
|
|
11
|
+
* Rich text editor built on ngx-quill. The value is Quill HTML.
|
|
12
|
+
*
|
|
13
|
+
* Requires Quill's stylesheets in the global styles file:
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```scss
|
|
17
|
+
* @forward 'quill/dist/quill.core.css';
|
|
18
|
+
* @forward 'quill/dist/quill.snow.css';
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare class TextEditorComponent implements OnInit, ControlValueAccessor, FormFieldControl<string> {
|
|
22
|
+
private readonly renderer;
|
|
23
|
+
private readonly translationService;
|
|
24
|
+
private readonly fieldContext;
|
|
25
|
+
/**
|
|
26
|
+
* Editor contents as Quill HTML. Supports two-way binding; with reactive or
|
|
27
|
+
* template-driven forms bind the control instead.
|
|
28
|
+
*/
|
|
29
|
+
value: _angular_core.ModelSignal<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Id set on the editing area. Quill's editing area is a `contenteditable`
|
|
32
|
+
* div, which `<label for>` cannot target, so pair this with `ariaLabelledby`
|
|
33
|
+
* rather than relying on a label's `for`.
|
|
34
|
+
*/
|
|
35
|
+
inputId: _angular_core.InputSignal<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Id of the element labelling the editor, usually your own `<label>`.
|
|
38
|
+
*/
|
|
39
|
+
ariaLabelledby: _angular_core.InputSignal<string | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Accessible name, for when there is no visible label. Ignored when
|
|
42
|
+
* `ariaLabelledby` is set.
|
|
43
|
+
*/
|
|
44
|
+
ariaLabel: _angular_core.InputSignal<string | undefined>;
|
|
45
|
+
/**
|
|
46
|
+
* Forces the error state on. Combines with the state derived from the bound
|
|
47
|
+
* control, so `false` does not switch a derived error off.
|
|
48
|
+
*/
|
|
49
|
+
readonly invalidInput: _angular_core.InputSignal<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Marks the editor as required for assistive technology. Combines with the
|
|
52
|
+
* bound control's `Validators.required`.
|
|
53
|
+
*/
|
|
54
|
+
readonly requiredInput: _angular_core.InputSignal<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Placeholder shown while the editor is empty. Not translated.
|
|
57
|
+
*/
|
|
58
|
+
placeholder: _angular_core.InputSignal<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Quill module configuration. Replaces the default toolbar rather than
|
|
61
|
+
* extending it. Tab is always left to the browser so the editor can be tabbed
|
|
62
|
+
* out of; pass an explicit `keyboard.bindings.tab` to override that.
|
|
63
|
+
*/
|
|
64
|
+
modules: _angular_core.InputSignal<QuillModules>;
|
|
65
|
+
/**
|
|
66
|
+
* Rows the editing area rests at, and the fewest it can ever show.
|
|
67
|
+
*
|
|
68
|
+
* @default 10
|
|
69
|
+
*/
|
|
70
|
+
minRows: _angular_core.InputSignal<number>;
|
|
71
|
+
readonly focused: _angular_core.WritableSignal<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* `quill-editor` is itself a `ControlValueAccessor`, so the cleanest bridge to
|
|
74
|
+
* it is a control of our own rather than reaching into the Quill instance.
|
|
75
|
+
*/
|
|
76
|
+
protected readonly innerControl: FormControl<string>;
|
|
77
|
+
private readonly editorRoot;
|
|
78
|
+
private readonly toolbar;
|
|
79
|
+
private readonly formDisabled;
|
|
80
|
+
private onChange;
|
|
81
|
+
private onTouched;
|
|
82
|
+
private readonly derived;
|
|
83
|
+
readonly describedBy: _tedi_design_system_angular_tedi.ControlDescribedBy;
|
|
84
|
+
readonly touched: _angular_core.Signal<boolean>;
|
|
85
|
+
readonly dirty: _angular_core.Signal<boolean>;
|
|
86
|
+
readonly disabled: _angular_core.Signal<boolean>;
|
|
87
|
+
readonly invalid: _angular_core.Signal<boolean>;
|
|
88
|
+
readonly required: _angular_core.Signal<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* `tedi-form-field`'s counter measures the control's value, which here is
|
|
91
|
+
* Quill markup — a short sentence would blow a generous limit on tags alone.
|
|
92
|
+
* Report what the user can see instead.
|
|
93
|
+
*/
|
|
94
|
+
readonly characterCount: _angular_core.Signal<number>;
|
|
95
|
+
/**
|
|
96
|
+
* Quill's own Tab handling is disabled by default, but a caller-supplied
|
|
97
|
+
* binding of the same name still wins.
|
|
98
|
+
*/
|
|
99
|
+
readonly resolvedModules: _angular_core.Signal<QuillModules>;
|
|
100
|
+
constructor();
|
|
101
|
+
ngOnInit(): void;
|
|
102
|
+
handleEditorCreated: (quill: Quill) => void;
|
|
103
|
+
handleBlur(): void;
|
|
104
|
+
setDescribedBy(ids: string[]): void;
|
|
105
|
+
reset(): void;
|
|
106
|
+
focus(): void;
|
|
107
|
+
writeValue(value: string | null): void;
|
|
108
|
+
registerOnChange(fn: (value: string) => void): void;
|
|
109
|
+
registerOnTouched(fn: () => void): void;
|
|
110
|
+
setDisabledState(isDisabled: boolean): void;
|
|
111
|
+
/**
|
|
112
|
+
* Names the toolbar controls and gives them a hover tooltip. Quill's own
|
|
113
|
+
* `aria-label` is the untranslated format string, and it leaves the pickers
|
|
114
|
+
* nameless entirely, so both attributes are rewritten from the translations.
|
|
115
|
+
* Re-runs on a language change.
|
|
116
|
+
*/
|
|
117
|
+
private syncToolbarLabels;
|
|
118
|
+
/**
|
|
119
|
+
* Mirrors identity, name and state onto `.ql-editor`. Quill gives it no role
|
|
120
|
+
* of its own, so without this it is exposed as a nameless `generic` node and
|
|
121
|
+
* none of these attributes reach assistive technology.
|
|
122
|
+
*/
|
|
123
|
+
private syncEditorAttributes;
|
|
124
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TextEditorComponent, never>;
|
|
125
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TextEditorComponent, "tedi-text-editor", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": true; "isSignal": true; }; "ariaLabelledby": { "alias": "ariaLabelledby"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "invalidInput": { "alias": "invalid"; "required": false; "isSignal": true; }; "requiredInput": { "alias": "required"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "modules": { "alias": "modules"; "required": false; "isSignal": true; }; "minRows": { "alias": "minRows"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { TEXT_EDITOR_DEFAULT_MODULES, TextEditorComponent };
|
|
129
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../../../src/community/text-editor/text-editor.component.ts"],"mappings":";;;;;;;;;;;;;;;;;AA8JG;AACH;AA0BE;AACA,cAAQ,mBAAS,YAAoD,MAAA,EAAA,oBAAA,EAAA,gBAAA;AACrE;AAIA;;;AAGG;AACH;AACA;;;;AAIG;AACH;AACA;;AAEG;AACH;AACA;;;AAGG;AACH;AACA;;;AAGG;;AAGH;;;AAGG;;AAGH;;AAEG;AACH;AACA;;;;AAIG;AACH;AACA;;;;AAIG;AACH;;AAIA,aAAA,aAAA,CAAA,WAAA;;;AAGG;AACH;AAIA;AACA,qCAA4D,WAAA;AAC5D;;;AAIA;;;0BAIc,gCAAA,CAAA,kBAAsB;sBAEnB,aAAA,CAAA,MAAA;oBAID,aAAA,CAAA,MAAA;uBAOC,aAAA,CAAA,MAAA;AAIjB,sBAAA,aAAA,CAAA,MAAA;;;;AAIG;;AAGH;;;AAGG;;;8BA+CK,aAAA,CAAA,MAAA,CAAA,YAAA;AAIR;;AAaA,iCAA4B,KAAA;;;AAiB5B;;AAQA;AAIA;AAIA;;;;;AAKG;AACH;AAyBA;;;;AAIG;AACH;;;AA4BD,iBAAAA,aAAA,CAAA,oBAAA,CAAA,mBAAA;;;;;;","names":["i0"]}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, Renderer2, model, input, signal, computed, untracked, effect, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
+
import { TediTranslationService, TEDI_FIELD_CONTEXT, deriveControlState, controlDescribedBy, TEDI_FORM_FIELD_CONTROL } from '@tedi-design-system/angular/tedi';
|
|
6
|
+
import { QuillEditorComponent } from 'ngx-quill';
|
|
7
|
+
|
|
8
|
+
const TEXT_EDITOR_DEFAULT_MODULES = {
|
|
9
|
+
toolbar: [
|
|
10
|
+
[
|
|
11
|
+
"bold",
|
|
12
|
+
"italic",
|
|
13
|
+
"underline",
|
|
14
|
+
{ align: "justify" },
|
|
15
|
+
{ list: "bullet" },
|
|
16
|
+
{ list: "ordered" },
|
|
17
|
+
{ indent: "-1" },
|
|
18
|
+
{ indent: "+1" },
|
|
19
|
+
{ background: [] },
|
|
20
|
+
{ color: [] },
|
|
21
|
+
"clean",
|
|
22
|
+
],
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Quill binds Tab to "insert a tab character" and, inside a list, to indent and
|
|
27
|
+
* outdent. All three swallow the key and leave the user unable to tab out of the
|
|
28
|
+
* editor, which is a keyboard trap (WCAG 2.1.2). Quill skips any binding whose
|
|
29
|
+
* value is `null`, so nulling them lets Tab fall through to the browser's normal
|
|
30
|
+
* focus move. List indenting stays available from the toolbar`.
|
|
31
|
+
*/
|
|
32
|
+
const NO_KEYBOARD_TRAP_BINDINGS = {
|
|
33
|
+
tab: null,
|
|
34
|
+
indent: null,
|
|
35
|
+
outdent: null,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Translation key per toolbar control, keyed by Quill's format name and, where
|
|
39
|
+
* one format drives several buttons, its value. Quill names the buttons itself
|
|
40
|
+
* but only from the raw format (`aria-label="indent: +1"`), so every control
|
|
41
|
+
* listed here is relabelled; anything absent keeps Quill's own name.
|
|
42
|
+
*/
|
|
43
|
+
const TOOLBAR_LABEL_KEYS = {
|
|
44
|
+
bold: "text-editor.bold",
|
|
45
|
+
italic: "text-editor.italic",
|
|
46
|
+
underline: "text-editor.underline",
|
|
47
|
+
strike: "text-editor.strike",
|
|
48
|
+
blockquote: "text-editor.blockquote",
|
|
49
|
+
link: "text-editor.link",
|
|
50
|
+
clean: "text-editor.clean",
|
|
51
|
+
color: "text-editor.color",
|
|
52
|
+
background: "text-editor.background",
|
|
53
|
+
align: "text-editor.align.left",
|
|
54
|
+
"align:center": "text-editor.align.center",
|
|
55
|
+
"align:right": "text-editor.align.right",
|
|
56
|
+
"align:justify": "text-editor.align.justify",
|
|
57
|
+
"list:bullet": "text-editor.list.bullet",
|
|
58
|
+
"list:ordered": "text-editor.list.ordered",
|
|
59
|
+
"indent:+1": "text-editor.indent.increase",
|
|
60
|
+
"indent:-1": "text-editor.indent.decrease",
|
|
61
|
+
};
|
|
62
|
+
const STRUCTURAL_TOOLBAR_CLASSES = new Set([
|
|
63
|
+
"ql-picker",
|
|
64
|
+
"ql-color-picker",
|
|
65
|
+
"ql-icon-picker",
|
|
66
|
+
"ql-expanded",
|
|
67
|
+
"ql-active",
|
|
68
|
+
]);
|
|
69
|
+
/**
|
|
70
|
+
* Quill tags a control with `ql-<format>` alongside its structural classes, and
|
|
71
|
+
* puts the discriminating value in `value` where one format drives several
|
|
72
|
+
* buttons. Left align is given an empty value, so it falls back to the bare
|
|
73
|
+
* format name.
|
|
74
|
+
*/
|
|
75
|
+
function toolbarLabelKey(control) {
|
|
76
|
+
const format = Array.from(control.classList).find((name) => name.startsWith("ql-") && !STRUCTURAL_TOOLBAR_CLASSES.has(name));
|
|
77
|
+
if (!format)
|
|
78
|
+
return undefined;
|
|
79
|
+
const value = control.getAttribute("value");
|
|
80
|
+
const base = format.slice("ql-".length);
|
|
81
|
+
return TOOLBAR_LABEL_KEYS[value ? `${base}:${value}` : base];
|
|
82
|
+
}
|
|
83
|
+
const SERIALIZED_ENTITIES = {
|
|
84
|
+
"&": "&",
|
|
85
|
+
"<": "<",
|
|
86
|
+
">": ">",
|
|
87
|
+
""": '"',
|
|
88
|
+
"'": "'",
|
|
89
|
+
" ": "\u00a0",
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Length of the text a reader actually sees, with the markup stripped. Parsed
|
|
93
|
+
* rather than regex-stripped so entities (` `) count as the one character
|
|
94
|
+
* they render as; a `DOMParser` document is inert, so nothing in the markup
|
|
95
|
+
* runs or loads.
|
|
96
|
+
*/
|
|
97
|
+
function visibleTextLength(html) {
|
|
98
|
+
if (!html)
|
|
99
|
+
return 0;
|
|
100
|
+
if (typeof DOMParser === "undefined") {
|
|
101
|
+
return html
|
|
102
|
+
.replace(/<[^>]*>/g, "")
|
|
103
|
+
.replace(/&(?:amp|lt|gt|quot|nbsp|#39);/g, (entity) => SERIALIZED_ENTITIES[entity]).length;
|
|
104
|
+
}
|
|
105
|
+
const text = new DOMParser().parseFromString(html, "text/html").body.textContent ?? "";
|
|
106
|
+
return text.length;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Rich text editor built on ngx-quill. The value is Quill HTML.
|
|
110
|
+
*
|
|
111
|
+
* Requires Quill's stylesheets in the global styles file:
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```scss
|
|
115
|
+
* @forward 'quill/dist/quill.core.css';
|
|
116
|
+
* @forward 'quill/dist/quill.snow.css';
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
class TextEditorComponent {
|
|
120
|
+
renderer = inject(Renderer2);
|
|
121
|
+
translationService = inject(TediTranslationService);
|
|
122
|
+
fieldContext = inject(TEDI_FIELD_CONTEXT, {
|
|
123
|
+
optional: true,
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Editor contents as Quill HTML. Supports two-way binding; with reactive or
|
|
127
|
+
* template-driven forms bind the control instead.
|
|
128
|
+
*/
|
|
129
|
+
value = model("", ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
130
|
+
/**
|
|
131
|
+
* Id set on the editing area. Quill's editing area is a `contenteditable`
|
|
132
|
+
* div, which `<label for>` cannot target, so pair this with `ariaLabelledby`
|
|
133
|
+
* rather than relying on a label's `for`.
|
|
134
|
+
*/
|
|
135
|
+
inputId = input.required(...(ngDevMode ? [{ debugName: "inputId" }] : []));
|
|
136
|
+
/**
|
|
137
|
+
* Id of the element labelling the editor, usually your own `<label>`.
|
|
138
|
+
*/
|
|
139
|
+
ariaLabelledby = input(...(ngDevMode ? [undefined, { debugName: "ariaLabelledby" }] : []));
|
|
140
|
+
/**
|
|
141
|
+
* Accessible name, for when there is no visible label. Ignored when
|
|
142
|
+
* `ariaLabelledby` is set.
|
|
143
|
+
*/
|
|
144
|
+
ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
|
|
145
|
+
/**
|
|
146
|
+
* Forces the error state on. Combines with the state derived from the bound
|
|
147
|
+
* control, so `false` does not switch a derived error off.
|
|
148
|
+
*/
|
|
149
|
+
// eslint-disable-next-line @angular-eslint/no-input-rename
|
|
150
|
+
invalidInput = input(false, ...(ngDevMode ? [{ debugName: "invalidInput", alias: "invalid" }] : [{ alias: "invalid" }]));
|
|
151
|
+
/**
|
|
152
|
+
* Marks the editor as required for assistive technology. Combines with the
|
|
153
|
+
* bound control's `Validators.required`.
|
|
154
|
+
*/
|
|
155
|
+
// eslint-disable-next-line @angular-eslint/no-input-rename
|
|
156
|
+
requiredInput = input(false, ...(ngDevMode ? [{ debugName: "requiredInput", alias: "required" }] : [{ alias: "required" }]));
|
|
157
|
+
/**
|
|
158
|
+
* Placeholder shown while the editor is empty. Not translated.
|
|
159
|
+
*/
|
|
160
|
+
placeholder = input("", ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
161
|
+
/**
|
|
162
|
+
* Quill module configuration. Replaces the default toolbar rather than
|
|
163
|
+
* extending it. Tab is always left to the browser so the editor can be tabbed
|
|
164
|
+
* out of; pass an explicit `keyboard.bindings.tab` to override that.
|
|
165
|
+
*/
|
|
166
|
+
modules = input(TEXT_EDITOR_DEFAULT_MODULES, ...(ngDevMode ? [{ debugName: "modules" }] : []));
|
|
167
|
+
/**
|
|
168
|
+
* Rows the editing area rests at, and the fewest it can ever show.
|
|
169
|
+
*
|
|
170
|
+
* @default 10
|
|
171
|
+
*/
|
|
172
|
+
minRows = input(10, ...(ngDevMode ? [{ debugName: "minRows" }] : []));
|
|
173
|
+
focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : []));
|
|
174
|
+
/**
|
|
175
|
+
* `quill-editor` is itself a `ControlValueAccessor`, so the cleanest bridge to
|
|
176
|
+
* it is a control of our own rather than reaching into the Quill instance.
|
|
177
|
+
*/
|
|
178
|
+
innerControl = new FormControl("", {
|
|
179
|
+
nonNullable: true,
|
|
180
|
+
});
|
|
181
|
+
editorRoot = signal(null, ...(ngDevMode ? [{ debugName: "editorRoot" }] : []));
|
|
182
|
+
toolbar = signal(null, ...(ngDevMode ? [{ debugName: "toolbar" }] : []));
|
|
183
|
+
formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : []));
|
|
184
|
+
onChange = () => { };
|
|
185
|
+
onTouched = () => { };
|
|
186
|
+
derived = deriveControlState();
|
|
187
|
+
describedBy = controlDescribedBy();
|
|
188
|
+
touched = this.derived.touched;
|
|
189
|
+
dirty = this.derived.dirty;
|
|
190
|
+
disabled = computed(() => this.formDisabled() || (this.fieldContext?.disabled() ?? false), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
191
|
+
invalid = computed(() => this.invalidInput() ||
|
|
192
|
+
this.derived.invalid() ||
|
|
193
|
+
(this.fieldContext?.invalid() ?? false), ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
194
|
+
required = computed(() => this.requiredInput() || this.derived.required(), ...(ngDevMode ? [{ debugName: "required" }] : []));
|
|
195
|
+
/**
|
|
196
|
+
* `tedi-form-field`'s counter measures the control's value, which here is
|
|
197
|
+
* Quill markup — a short sentence would blow a generous limit on tags alone.
|
|
198
|
+
* Report what the user can see instead.
|
|
199
|
+
*/
|
|
200
|
+
characterCount = computed(() => visibleTextLength(this.value()), ...(ngDevMode ? [{ debugName: "characterCount" }] : []));
|
|
201
|
+
/**
|
|
202
|
+
* Quill's own Tab handling is disabled by default, but a caller-supplied
|
|
203
|
+
* binding of the same name still wins.
|
|
204
|
+
*/
|
|
205
|
+
resolvedModules = computed(() => {
|
|
206
|
+
// An explicit `[modules]="undefined"` binding overrides the input default,
|
|
207
|
+
// so fall back here rather than trusting it.
|
|
208
|
+
const modules = this.modules() ?? TEXT_EDITOR_DEFAULT_MODULES;
|
|
209
|
+
const keyboard = typeof modules.keyboard === "object" && modules.keyboard !== null
|
|
210
|
+
? modules.keyboard
|
|
211
|
+
: undefined;
|
|
212
|
+
return {
|
|
213
|
+
...modules,
|
|
214
|
+
keyboard: {
|
|
215
|
+
...keyboard,
|
|
216
|
+
bindings: { ...NO_KEYBOARD_TRAP_BINDINGS, ...keyboard?.bindings },
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedModules" }] : []));
|
|
220
|
+
constructor() {
|
|
221
|
+
this.innerControl.valueChanges.subscribe((html) => {
|
|
222
|
+
const next = html ?? "";
|
|
223
|
+
if (untracked(this.value) === next)
|
|
224
|
+
return;
|
|
225
|
+
this.value.set(next);
|
|
226
|
+
this.onChange(next);
|
|
227
|
+
});
|
|
228
|
+
effect(() => {
|
|
229
|
+
const next = this.value();
|
|
230
|
+
if (this.innerControl.value !== next) {
|
|
231
|
+
this.innerControl.setValue(next, { emitEvent: false });
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
effect(() => {
|
|
235
|
+
const disabled = this.disabled();
|
|
236
|
+
if (disabled === this.innerControl.disabled)
|
|
237
|
+
return;
|
|
238
|
+
if (disabled)
|
|
239
|
+
this.innerControl.disable({ emitEvent: false });
|
|
240
|
+
else
|
|
241
|
+
this.innerControl.enable({ emitEvent: false });
|
|
242
|
+
});
|
|
243
|
+
this.syncEditorAttributes();
|
|
244
|
+
this.syncToolbarLabels();
|
|
245
|
+
}
|
|
246
|
+
ngOnInit() {
|
|
247
|
+
this.derived.connect();
|
|
248
|
+
}
|
|
249
|
+
handleEditorCreated = (quill) => {
|
|
250
|
+
this.editorRoot.set(quill.root);
|
|
251
|
+
this.toolbar.set(quill.getModule("toolbar")
|
|
252
|
+
?.container ?? null);
|
|
253
|
+
};
|
|
254
|
+
handleBlur() {
|
|
255
|
+
this.focused.set(false);
|
|
256
|
+
this.onTouched();
|
|
257
|
+
}
|
|
258
|
+
setDescribedBy(ids) {
|
|
259
|
+
this.describedBy.set(ids);
|
|
260
|
+
}
|
|
261
|
+
reset() {
|
|
262
|
+
if (this.disabled())
|
|
263
|
+
return;
|
|
264
|
+
this.value.set("");
|
|
265
|
+
this.onChange("");
|
|
266
|
+
this.onTouched();
|
|
267
|
+
}
|
|
268
|
+
focus() {
|
|
269
|
+
if (this.disabled())
|
|
270
|
+
return;
|
|
271
|
+
this.editorRoot()?.focus();
|
|
272
|
+
}
|
|
273
|
+
writeValue(value) {
|
|
274
|
+
this.value.set(value ?? "");
|
|
275
|
+
}
|
|
276
|
+
registerOnChange(fn) {
|
|
277
|
+
this.onChange = fn;
|
|
278
|
+
}
|
|
279
|
+
registerOnTouched(fn) {
|
|
280
|
+
this.onTouched = fn;
|
|
281
|
+
}
|
|
282
|
+
setDisabledState(isDisabled) {
|
|
283
|
+
this.formDisabled.set(isDisabled);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Names the toolbar controls and gives them a hover tooltip. Quill's own
|
|
287
|
+
* `aria-label` is the untranslated format string, and it leaves the pickers
|
|
288
|
+
* nameless entirely, so both attributes are rewritten from the translations.
|
|
289
|
+
* Re-runs on a language change.
|
|
290
|
+
*/
|
|
291
|
+
syncToolbarLabels() {
|
|
292
|
+
effect(() => {
|
|
293
|
+
const toolbar = this.toolbar();
|
|
294
|
+
if (!toolbar)
|
|
295
|
+
return;
|
|
296
|
+
const controls = Array.from(toolbar.querySelectorAll("button, .ql-picker"));
|
|
297
|
+
for (const control of controls) {
|
|
298
|
+
const key = toolbarLabelKey(control);
|
|
299
|
+
if (!key)
|
|
300
|
+
continue;
|
|
301
|
+
const label = this.translationService.translate(key);
|
|
302
|
+
// A picker's own node is a `span`; the focusable, hoverable part is its
|
|
303
|
+
// label child, so that is what has to carry the name and the tooltip.
|
|
304
|
+
const target = control.querySelector(".ql-picker-label") ?? control;
|
|
305
|
+
this.renderer.setAttribute(target, "title", label);
|
|
306
|
+
this.renderer.setAttribute(target, "aria-label", label);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Mirrors identity, name and state onto `.ql-editor`. Quill gives it no role
|
|
312
|
+
* of its own, so without this it is exposed as a nameless `generic` node and
|
|
313
|
+
* none of these attributes reach assistive technology.
|
|
314
|
+
*/
|
|
315
|
+
syncEditorAttributes() {
|
|
316
|
+
effect(() => {
|
|
317
|
+
const editor = this.editorRoot();
|
|
318
|
+
if (!editor)
|
|
319
|
+
return;
|
|
320
|
+
const labelledby = this.ariaLabelledby();
|
|
321
|
+
const invalid = this.invalid();
|
|
322
|
+
const attributes = {
|
|
323
|
+
id: this.inputId(),
|
|
324
|
+
role: "textbox",
|
|
325
|
+
"aria-multiline": "true",
|
|
326
|
+
"aria-labelledby": labelledby ?? null,
|
|
327
|
+
"aria-label": labelledby ? null : (this.ariaLabel() ?? null),
|
|
328
|
+
"aria-describedby": this.describedBy.attribute(),
|
|
329
|
+
"aria-required": this.required() ? "true" : null,
|
|
330
|
+
"aria-invalid": invalid ? "true" : null,
|
|
331
|
+
};
|
|
332
|
+
for (const [name, attributeValue] of Object.entries(attributes)) {
|
|
333
|
+
if (attributeValue === null) {
|
|
334
|
+
this.renderer.removeAttribute(editor, name);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
this.renderer.setAttribute(editor, name, attributeValue);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TextEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
343
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.24", type: TextEditorComponent, isStandalone: true, selector: "tedi-text-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: true, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, invalidInput: { classPropertyName: "invalidInput", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, requiredInput: { classPropertyName: "requiredInput", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, modules: { classPropertyName: "modules", publicName: "modules", isSignal: true, isRequired: false, transformFunction: null }, minRows: { classPropertyName: "minRows", publicName: "minRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "style.--_tedi-text-editor-min-rows": "minRows()" }, classAttribute: "tedi-text-editor" }, providers: [
|
|
344
|
+
{
|
|
345
|
+
provide: NG_VALUE_ACCESSOR,
|
|
346
|
+
useExisting: forwardRef(() => TextEditorComponent),
|
|
347
|
+
multi: true,
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
provide: TEDI_FORM_FIELD_CONTROL,
|
|
351
|
+
useExisting: forwardRef(() => TextEditorComponent),
|
|
352
|
+
},
|
|
353
|
+
], ngImport: i0, template: "<quill-editor\n [formControl]=\"innerControl\"\n [modules]=\"resolvedModules()\"\n [placeholder]=\"placeholder()\"\n [class.tedi-text-editor__editor--disabled]=\"disabled()\"\n [class.tedi-text-editor__editor--error]=\"invalid()\"\n [class.tedi-text-editor__editor--focused]=\"focused()\"\n class=\"tedi-text-editor__editor\"\n (onEditorCreated)=\"handleEditorCreated($event)\"\n (onFocus)=\"focused.set(true)\"\n (onBlur)=\"handleBlur()\"\n/>\n", styles: [".tedi-text-editor,.tedi-text-editor .tedi-text-editor__editor{display:block}.tedi-text-editor__editor{--_border-radius: var(--form-field-radius);--_border-color: var(--form-input-border-default);--_color: var(--form-input-text-filled);--_background-color: var(--form-input-background-default);--_placeholder-color: var(--form-input-text-placeholder);width:100%;color:var(--_color);border:1px solid var(--_border-color);border-radius:var(--_border-radius)}.tedi-text-editor__editor.tedi-text-editor__editor--focused:not(.tedi-text-editor__editor--error,.tedi-text-editor__editor--disabled){border-color:var(--form-input-border-hover);box-shadow:inset 0 0 0 1px var(--form-input-border-hover),0 0 0 1px var(--form-input-border-hover)}.tedi-text-editor__editor:hover{--_border-color: var(--form-input-border-hover)}.tedi-text-editor__editor--disabled{--_border-color: var(--form-input-border-disabled);color:var(--form-input-text-disabled);pointer-events:none;background-color:var(--form-input-background-disabled)}.tedi-text-editor__editor--error:not(.tedi-text-editor__editor--disabled){--_border-color: var(--form-general-feedback-error-border)}.tedi-text-editor__editor--error:not(.tedi-text-editor__editor--disabled).tedi-text-editor__editor--focused:not(.tedi-text-editor__editor--error,.tedi-text-editor__editor--disabled){border-color:var(--form-general-feedback-error-border);box-shadow:inset 0 0 0 1px var(--form-general-feedback-error-border),0 0 0 1px var(--form-general-feedback-error-border)}.tedi-text-editor__editor .ql-toolbar.ql-snow{border:0;border-bottom:1px solid var(--_border-color)}.tedi-text-editor__editor .ql-container.ql-snow{border:0}.tedi-text-editor__editor .ql-toolbar .ql-formats{display:inline-flex;flex-wrap:wrap;gap:.5rem;align-items:center}.tedi-text-editor__editor .ql-toolbar .ql-formats>button,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button]{border-radius:var(--tedi-radius-02-default)}.tedi-text-editor__editor .ql-toolbar .ql-formats>button:hover,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker:hover,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button]:hover{background-color:var(--general-surface-hover)}.tedi-text-editor__editor .ql-toolbar .ql-formats>button.ql-active,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker.ql-active,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button].ql-active{background-color:var(--general-icon-background-brand-primary)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-stroke{stroke:var(--general-icon-brand)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-active .ql-stroke{stroke:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label{color:var(--general-icon-white);background-color:var(--general-icon-background-brand-primary);border-color:transparent}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label .ql-stroke{stroke:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label .ql-fill{fill:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-options{background-color:var(--card-background-primary);border-color:var(--general-border-primary);border-radius:var(--tedi-radius-02-default);box-shadow:0 1px 5px 0 var(--tedi-alpha-20)}.tedi-text-editor__editor .ql-toolbar .ql-color-picker .ql-picker-item.ql-selected,.tedi-text-editor__editor .ql-toolbar .ql-color-picker .ql-picker-item:hover{border-color:var(--general-border-brand)}.tedi-text-editor__editor .ql-container .ql-editor{--_font-size: var(--body-regular-size);--_line-height: var(--body-regular-line-height);--_padding-y: var(--form-field-padding-y-md-default);--_padding-x: var(--form-field-padding-x-md-default);--_form-field-outer-gap: var(--form-field-outer-spacing);min-height:calc(var(--_tedi-text-editor-min-rows) * 1lh + 2 * var(--_padding-y));padding:var(--_padding-y) var(--_padding-x);font-family:var(--family-default);font-size:var(--_font-size);line-height:var(--_line-height);resize:vertical;border-radius:var(--_border-radius)}.tedi-text-editor__editor .ql-editor.ql-blank:before{font-style:normal;color:var(--_placeholder-color)}\n"], dependencies: [{ kind: "component", type: QuillEditorComponent, selector: "quill-editor" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
354
|
+
}
|
|
355
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TextEditorComponent, decorators: [{
|
|
356
|
+
type: Component,
|
|
357
|
+
args: [{ selector: "tedi-text-editor", standalone: true, imports: [QuillEditorComponent, ReactiveFormsModule], providers: [
|
|
358
|
+
{
|
|
359
|
+
provide: NG_VALUE_ACCESSOR,
|
|
360
|
+
useExisting: forwardRef(() => TextEditorComponent),
|
|
361
|
+
multi: true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
provide: TEDI_FORM_FIELD_CONTROL,
|
|
365
|
+
useExisting: forwardRef(() => TextEditorComponent),
|
|
366
|
+
},
|
|
367
|
+
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
368
|
+
class: "tedi-text-editor",
|
|
369
|
+
"[style.--_tedi-text-editor-min-rows]": "minRows()",
|
|
370
|
+
}, template: "<quill-editor\n [formControl]=\"innerControl\"\n [modules]=\"resolvedModules()\"\n [placeholder]=\"placeholder()\"\n [class.tedi-text-editor__editor--disabled]=\"disabled()\"\n [class.tedi-text-editor__editor--error]=\"invalid()\"\n [class.tedi-text-editor__editor--focused]=\"focused()\"\n class=\"tedi-text-editor__editor\"\n (onEditorCreated)=\"handleEditorCreated($event)\"\n (onFocus)=\"focused.set(true)\"\n (onBlur)=\"handleBlur()\"\n/>\n", styles: [".tedi-text-editor,.tedi-text-editor .tedi-text-editor__editor{display:block}.tedi-text-editor__editor{--_border-radius: var(--form-field-radius);--_border-color: var(--form-input-border-default);--_color: var(--form-input-text-filled);--_background-color: var(--form-input-background-default);--_placeholder-color: var(--form-input-text-placeholder);width:100%;color:var(--_color);border:1px solid var(--_border-color);border-radius:var(--_border-radius)}.tedi-text-editor__editor.tedi-text-editor__editor--focused:not(.tedi-text-editor__editor--error,.tedi-text-editor__editor--disabled){border-color:var(--form-input-border-hover);box-shadow:inset 0 0 0 1px var(--form-input-border-hover),0 0 0 1px var(--form-input-border-hover)}.tedi-text-editor__editor:hover{--_border-color: var(--form-input-border-hover)}.tedi-text-editor__editor--disabled{--_border-color: var(--form-input-border-disabled);color:var(--form-input-text-disabled);pointer-events:none;background-color:var(--form-input-background-disabled)}.tedi-text-editor__editor--error:not(.tedi-text-editor__editor--disabled){--_border-color: var(--form-general-feedback-error-border)}.tedi-text-editor__editor--error:not(.tedi-text-editor__editor--disabled).tedi-text-editor__editor--focused:not(.tedi-text-editor__editor--error,.tedi-text-editor__editor--disabled){border-color:var(--form-general-feedback-error-border);box-shadow:inset 0 0 0 1px var(--form-general-feedback-error-border),0 0 0 1px var(--form-general-feedback-error-border)}.tedi-text-editor__editor .ql-toolbar.ql-snow{border:0;border-bottom:1px solid var(--_border-color)}.tedi-text-editor__editor .ql-container.ql-snow{border:0}.tedi-text-editor__editor .ql-toolbar .ql-formats{display:inline-flex;flex-wrap:wrap;gap:.5rem;align-items:center}.tedi-text-editor__editor .ql-toolbar .ql-formats>button,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button]{border-radius:var(--tedi-radius-02-default)}.tedi-text-editor__editor .ql-toolbar .ql-formats>button:hover,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker:hover,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button]:hover{background-color:var(--general-surface-hover)}.tedi-text-editor__editor .ql-toolbar .ql-formats>button.ql-active,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker.ql-active,.tedi-text-editor__editor .ql-toolbar .ql-formats>.ql-picker>[role=button].ql-active{background-color:var(--general-icon-background-brand-primary)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-stroke{stroke:var(--general-icon-brand)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-active .ql-stroke{stroke:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label{color:var(--general-icon-white);background-color:var(--general-icon-background-brand-primary);border-color:transparent}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label .ql-stroke{stroke:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-label .ql-fill{fill:var(--general-icon-white)}.tedi-text-editor__editor .ql-toolbar .ql-formats .ql-picker.ql-expanded>.ql-picker-options{background-color:var(--card-background-primary);border-color:var(--general-border-primary);border-radius:var(--tedi-radius-02-default);box-shadow:0 1px 5px 0 var(--tedi-alpha-20)}.tedi-text-editor__editor .ql-toolbar .ql-color-picker .ql-picker-item.ql-selected,.tedi-text-editor__editor .ql-toolbar .ql-color-picker .ql-picker-item:hover{border-color:var(--general-border-brand)}.tedi-text-editor__editor .ql-container .ql-editor{--_font-size: var(--body-regular-size);--_line-height: var(--body-regular-line-height);--_padding-y: var(--form-field-padding-y-md-default);--_padding-x: var(--form-field-padding-x-md-default);--_form-field-outer-gap: var(--form-field-outer-spacing);min-height:calc(var(--_tedi-text-editor-min-rows) * 1lh + 2 * var(--_padding-y));padding:var(--_padding-y) var(--_padding-x);font-family:var(--family-default);font-size:var(--_font-size);line-height:var(--_line-height);resize:vertical;border-radius:var(--_border-radius)}.tedi-text-editor__editor .ql-editor.ql-blank:before{font-style:normal;color:var(--_placeholder-color)}\n"] }]
|
|
371
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: true }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], invalidInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], requiredInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], modules: [{ type: i0.Input, args: [{ isSignal: true, alias: "modules", required: false }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }] } });
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Generated bundle index. Do not edit.
|
|
375
|
+
*/
|
|
376
|
+
|
|
377
|
+
export { TEXT_EDITOR_DEFAULT_MODULES, TextEditorComponent };
|
|
378
|
+
//# sourceMappingURL=tedi-design-system-angular-community-text-editor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tedi-design-system-angular-community-text-editor.mjs","sources":["../../src/community/text-editor/text-editor.component.ts","../../src/community/text-editor/text-editor.component.html","../../src/community/text-editor/tedi-design-system-angular-community-text-editor.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n forwardRef,\n inject,\n input,\n model,\n OnInit,\n Renderer2,\n signal,\n untracked,\n ViewEncapsulation,\n} from \"@angular/core\";\nimport {\n ControlValueAccessor,\n FormControl,\n NG_VALUE_ACCESSOR,\n ReactiveFormsModule,\n} from \"@angular/forms\";\nimport {\n controlDescribedBy,\n deriveControlState,\n FormFieldControl,\n TEDI_FIELD_CONTEXT,\n TEDI_FORM_FIELD_CONTROL,\n TediTranslationService,\n} from \"@tedi-design-system/angular/tedi\";\nimport { QuillEditorComponent, QuillModules } from \"ngx-quill\";\nimport type Quill from \"quill\";\n\nexport const TEXT_EDITOR_DEFAULT_MODULES: QuillModules = {\n toolbar: [\n [\n \"bold\",\n \"italic\",\n \"underline\",\n { align: \"justify\" },\n { list: \"bullet\" },\n { list: \"ordered\" },\n { indent: \"-1\" },\n { indent: \"+1\" },\n { background: [] },\n { color: [] },\n \"clean\",\n ],\n ],\n};\n\n/**\n * Quill binds Tab to \"insert a tab character\" and, inside a list, to indent and\n * outdent. All three swallow the key and leave the user unable to tab out of the\n * editor, which is a keyboard trap (WCAG 2.1.2). Quill skips any binding whose\n * value is `null`, so nulling them lets Tab fall through to the browser's normal\n * focus move. List indenting stays available from the toolbar`.\n */\nconst NO_KEYBOARD_TRAP_BINDINGS = {\n tab: null,\n indent: null,\n outdent: null,\n};\n\n/**\n * Translation key per toolbar control, keyed by Quill's format name and, where\n * one format drives several buttons, its value. Quill names the buttons itself\n * but only from the raw format (`aria-label=\"indent: +1\"`), so every control\n * listed here is relabelled; anything absent keeps Quill's own name.\n */\nconst TOOLBAR_LABEL_KEYS: Record<string, string> = {\n bold: \"text-editor.bold\",\n italic: \"text-editor.italic\",\n underline: \"text-editor.underline\",\n strike: \"text-editor.strike\",\n blockquote: \"text-editor.blockquote\",\n link: \"text-editor.link\",\n clean: \"text-editor.clean\",\n color: \"text-editor.color\",\n background: \"text-editor.background\",\n align: \"text-editor.align.left\",\n \"align:center\": \"text-editor.align.center\",\n \"align:right\": \"text-editor.align.right\",\n \"align:justify\": \"text-editor.align.justify\",\n \"list:bullet\": \"text-editor.list.bullet\",\n \"list:ordered\": \"text-editor.list.ordered\",\n \"indent:+1\": \"text-editor.indent.increase\",\n \"indent:-1\": \"text-editor.indent.decrease\",\n};\n\nconst STRUCTURAL_TOOLBAR_CLASSES = new Set([\n \"ql-picker\",\n \"ql-color-picker\",\n \"ql-icon-picker\",\n \"ql-expanded\",\n \"ql-active\",\n]);\n\n/**\n * Quill tags a control with `ql-<format>` alongside its structural classes, and\n * puts the discriminating value in `value` where one format drives several\n * buttons. Left align is given an empty value, so it falls back to the bare\n * format name.\n */\nfunction toolbarLabelKey(control: HTMLElement): string | undefined {\n const format = Array.from(control.classList).find(\n (name) => name.startsWith(\"ql-\") && !STRUCTURAL_TOOLBAR_CLASSES.has(name),\n );\n if (!format) return undefined;\n\n const value = control.getAttribute(\"value\");\n const base = format.slice(\"ql-\".length);\n\n return TOOLBAR_LABEL_KEYS[value ? `${base}:${value}` : base];\n}\n\nconst SERIALIZED_ENTITIES: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \""\": '\"',\n \"'\": \"'\",\n \" \": \"\\u00a0\",\n};\n\n/**\n * Length of the text a reader actually sees, with the markup stripped. Parsed\n * rather than regex-stripped so entities (` `) count as the one character\n * they render as; a `DOMParser` document is inert, so nothing in the markup\n * runs or loads.\n */\nfunction visibleTextLength(html: string): number {\n if (!html) return 0;\n\n if (typeof DOMParser === \"undefined\") {\n return html\n .replace(/<[^>]*>/g, \"\")\n .replace(\n /&(?:amp|lt|gt|quot|nbsp|#39);/g,\n (entity) => SERIALIZED_ENTITIES[entity],\n ).length;\n }\n\n const text =\n new DOMParser().parseFromString(html, \"text/html\").body.textContent ?? \"\";\n\n return text.length;\n}\n\n/**\n * Rich text editor built on ngx-quill. The value is Quill HTML.\n *\n * Requires Quill's stylesheets in the global styles file:\n *\n * @example\n * ```scss\n * @forward 'quill/dist/quill.core.css';\n * @forward 'quill/dist/quill.snow.css';\n * ```\n */\n@Component({\n selector: \"tedi-text-editor\",\n standalone: true,\n imports: [QuillEditorComponent, ReactiveFormsModule],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TextEditorComponent),\n multi: true,\n },\n {\n provide: TEDI_FORM_FIELD_CONTROL,\n useExisting: forwardRef(() => TextEditorComponent),\n },\n ],\n templateUrl: \"./text-editor.component.html\",\n styleUrl: \"./text-editor.component.scss\",\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: \"tedi-text-editor\",\n \"[style.--_tedi-text-editor-min-rows]\": \"minRows()\",\n },\n})\nexport class TextEditorComponent\n implements OnInit, ControlValueAccessor, FormFieldControl<string> {\n private readonly renderer = inject(Renderer2);\n private readonly translationService = inject(TediTranslationService);\n private readonly fieldContext = inject(TEDI_FIELD_CONTEXT, {\n optional: true,\n });\n\n /**\n * Editor contents as Quill HTML. Supports two-way binding; with reactive or\n * template-driven forms bind the control instead.\n */\n value = model<string>(\"\");\n /**\n * Id set on the editing area. Quill's editing area is a `contenteditable`\n * div, which `<label for>` cannot target, so pair this with `ariaLabelledby`\n * rather than relying on a label's `for`.\n */\n inputId = input.required<string>();\n /**\n * Id of the element labelling the editor, usually your own `<label>`.\n */\n ariaLabelledby = input<string>();\n /**\n * Accessible name, for when there is no visible label. Ignored when\n * `ariaLabelledby` is set.\n */\n ariaLabel = input<string>();\n /**\n * Forces the error state on. Combines with the state derived from the bound\n * control, so `false` does not switch a derived error off.\n */\n // eslint-disable-next-line @angular-eslint/no-input-rename\n readonly invalidInput = input<boolean>(false, { alias: \"invalid\" });\n /**\n * Marks the editor as required for assistive technology. Combines with the\n * bound control's `Validators.required`.\n */\n // eslint-disable-next-line @angular-eslint/no-input-rename\n readonly requiredInput = input<boolean>(false, { alias: \"required\" });\n /**\n * Placeholder shown while the editor is empty. Not translated.\n */\n placeholder = input<string>(\"\");\n /**\n * Quill module configuration. Replaces the default toolbar rather than\n * extending it. Tab is always left to the browser so the editor can be tabbed\n * out of; pass an explicit `keyboard.bindings.tab` to override that.\n */\n modules = input<QuillModules>(TEXT_EDITOR_DEFAULT_MODULES);\n /**\n * Rows the editing area rests at, and the fewest it can ever show.\n *\n * @default 10\n */\n minRows = input<number>(10);\n\n readonly focused = signal(false);\n\n /**\n * `quill-editor` is itself a `ControlValueAccessor`, so the cleanest bridge to\n * it is a control of our own rather than reaching into the Quill instance.\n */\n protected readonly innerControl = new FormControl<string>(\"\", {\n nonNullable: true,\n });\n\n private readonly editorRoot = signal<HTMLElement | null>(null);\n private readonly toolbar = signal<HTMLElement | null>(null);\n private readonly formDisabled = signal(false);\n private onChange: (value: string) => void = () => { };\n private onTouched: () => void = () => { };\n\n private readonly derived = deriveControlState();\n readonly describedBy = controlDescribedBy();\n\n readonly touched = this.derived.touched;\n readonly dirty = this.derived.dirty;\n\n readonly disabled = computed(\n () => this.formDisabled() || (this.fieldContext?.disabled() ?? false),\n );\n\n readonly invalid = computed(\n () =>\n this.invalidInput() ||\n this.derived.invalid() ||\n (this.fieldContext?.invalid() ?? false),\n );\n\n readonly required = computed(\n () => this.requiredInput() || this.derived.required(),\n );\n\n /**\n * `tedi-form-field`'s counter measures the control's value, which here is\n * Quill markup — a short sentence would blow a generous limit on tags alone.\n * Report what the user can see instead.\n */\n readonly characterCount = computed(() => visibleTextLength(this.value()));\n\n /**\n * Quill's own Tab handling is disabled by default, but a caller-supplied\n * binding of the same name still wins.\n */\n readonly resolvedModules = computed<QuillModules>(() => {\n // An explicit `[modules]=\"undefined\"` binding overrides the input default,\n // so fall back here rather than trusting it.\n const modules = this.modules() ?? TEXT_EDITOR_DEFAULT_MODULES;\n const keyboard =\n typeof modules.keyboard === \"object\" && modules.keyboard !== null\n ? modules.keyboard\n : undefined;\n\n return {\n ...modules,\n keyboard: {\n ...keyboard,\n bindings: { ...NO_KEYBOARD_TRAP_BINDINGS, ...keyboard?.bindings },\n },\n };\n });\n\n constructor() {\n this.innerControl.valueChanges.subscribe((html) => {\n const next = html ?? \"\";\n if (untracked(this.value) === next) return;\n\n this.value.set(next);\n this.onChange(next);\n });\n\n effect(() => {\n const next = this.value();\n if (this.innerControl.value !== next) {\n this.innerControl.setValue(next, { emitEvent: false });\n }\n });\n\n effect(() => {\n const disabled = this.disabled();\n if (disabled === this.innerControl.disabled) return;\n\n if (disabled) this.innerControl.disable({ emitEvent: false });\n else this.innerControl.enable({ emitEvent: false });\n });\n\n this.syncEditorAttributes();\n this.syncToolbarLabels();\n }\n\n ngOnInit() {\n this.derived.connect();\n }\n\n handleEditorCreated = (quill: Quill): void => {\n this.editorRoot.set(quill.root);\n this.toolbar.set(\n (quill.getModule(\"toolbar\") as { container?: HTMLElement } | undefined)\n ?.container ?? null,\n );\n };\n\n handleBlur() {\n this.focused.set(false);\n this.onTouched();\n }\n\n setDescribedBy(ids: string[]) {\n this.describedBy.set(ids);\n }\n\n reset() {\n if (this.disabled()) return;\n\n this.value.set(\"\");\n this.onChange(\"\");\n this.onTouched();\n }\n\n focus() {\n if (this.disabled()) return;\n this.editorRoot()?.focus();\n }\n\n writeValue(value: string | null): void {\n this.value.set(value ?? \"\");\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.formDisabled.set(isDisabled);\n }\n\n /**\n * Names the toolbar controls and gives them a hover tooltip. Quill's own\n * `aria-label` is the untranslated format string, and it leaves the pickers\n * nameless entirely, so both attributes are rewritten from the translations.\n * Re-runs on a language change.\n */\n private syncToolbarLabels() {\n effect(() => {\n const toolbar = this.toolbar();\n if (!toolbar) return;\n\n const controls = Array.from(\n toolbar.querySelectorAll<HTMLElement>(\"button, .ql-picker\"),\n );\n\n for (const control of controls) {\n const key = toolbarLabelKey(control);\n if (!key) continue;\n\n const label = this.translationService.translate(key);\n // A picker's own node is a `span`; the focusable, hoverable part is its\n // label child, so that is what has to carry the name and the tooltip.\n const target =\n control.querySelector<HTMLElement>(\".ql-picker-label\") ?? control;\n\n this.renderer.setAttribute(target, \"title\", label);\n this.renderer.setAttribute(target, \"aria-label\", label);\n }\n });\n }\n\n /**\n * Mirrors identity, name and state onto `.ql-editor`. Quill gives it no role\n * of its own, so without this it is exposed as a nameless `generic` node and\n * none of these attributes reach assistive technology.\n */\n private syncEditorAttributes() {\n effect(() => {\n const editor = this.editorRoot();\n if (!editor) return;\n\n const labelledby = this.ariaLabelledby();\n const invalid = this.invalid();\n\n const attributes: Record<string, string | null> = {\n id: this.inputId(),\n role: \"textbox\",\n \"aria-multiline\": \"true\",\n \"aria-labelledby\": labelledby ?? null,\n \"aria-label\": labelledby ? null : (this.ariaLabel() ?? null),\n \"aria-describedby\": this.describedBy.attribute(),\n \"aria-required\": this.required() ? \"true\" : null,\n \"aria-invalid\": invalid ? \"true\" : null,\n };\n\n for (const [name, attributeValue] of Object.entries(attributes)) {\n if (attributeValue === null) {\n this.renderer.removeAttribute(editor, name);\n } else {\n this.renderer.setAttribute(editor, name, attributeValue);\n }\n }\n });\n }\n}\n","<quill-editor\n [formControl]=\"innerControl\"\n [modules]=\"resolvedModules()\"\n [placeholder]=\"placeholder()\"\n [class.tedi-text-editor__editor--disabled]=\"disabled()\"\n [class.tedi-text-editor__editor--error]=\"invalid()\"\n [class.tedi-text-editor__editor--focused]=\"focused()\"\n class=\"tedi-text-editor__editor\"\n (onEditorCreated)=\"handleEditorCreated($event)\"\n (onFocus)=\"focused.set(true)\"\n (onBlur)=\"handleBlur()\"\n/>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|