@theseam/ui-common 0.4.25 → 0.4.27

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.
Files changed (63) hide show
  1. package/esm2020/data-filters/filters/data-filter-search/data-filter-search.component.mjs +1 -1
  2. package/esm2020/data-filters/filters/data-filter-text/data-filter-text.component.mjs +1 -1
  3. package/esm2020/datatable/datatable-column-preferences/datatable-column-preferences.component.mjs +1 -1
  4. package/esm2020/form-field/input.directive.mjs +8 -4
  5. package/esm2020/framework/schema-form-controls/schema-form-input/schema-form-input.component.mjs +1 -1
  6. package/esm2020/framework/schema-form-controls/schema-form-number/schema-form-number.component.mjs +1 -1
  7. package/esm2020/framework/schema-form-controls/schema-form-select/schema-form-select.component.mjs +2 -2
  8. package/esm2020/framework/schema-form-controls/schema-form-tel/schema-form-tel.component.mjs +2 -2
  9. package/esm2020/google-maps/google-maps-places-autocomplete/google-maps-places-autocomplete.component.mjs +1 -1
  10. package/esm2020/rich-text/public-api.mjs +5 -0
  11. package/esm2020/rich-text/rich-text/rich-text.component.mjs +594 -0
  12. package/esm2020/rich-text/rich-text.module.mjs +32 -0
  13. package/esm2020/rich-text/theseam-ui-common-rich-text.mjs +5 -0
  14. package/esm2020/rich-text/utils/models.mjs +24 -0
  15. package/esm2020/rich-text/utils/utils.mjs +123 -0
  16. package/esm2020/tel-input/tel-input/tel-input.component.mjs +2 -2
  17. package/esm2020/utils/is-null-or-undefined-or-empty.mjs +4 -0
  18. package/esm2020/utils/not-null-or-undefined-or-empty.mjs +4 -0
  19. package/esm2020/utils/public-api.mjs +3 -1
  20. package/fesm2015/theseam-ui-common-data-filters.mjs +2 -2
  21. package/fesm2015/theseam-ui-common-data-filters.mjs.map +1 -1
  22. package/fesm2015/theseam-ui-common-datatable.mjs +1 -1
  23. package/fesm2015/theseam-ui-common-datatable.mjs.map +1 -1
  24. package/fesm2015/theseam-ui-common-form-field.mjs +7 -3
  25. package/fesm2015/theseam-ui-common-form-field.mjs.map +1 -1
  26. package/fesm2015/theseam-ui-common-framework.mjs +4 -4
  27. package/fesm2015/theseam-ui-common-framework.mjs.map +1 -1
  28. package/fesm2015/theseam-ui-common-google-maps.mjs +1 -1
  29. package/fesm2015/theseam-ui-common-google-maps.mjs.map +1 -1
  30. package/fesm2015/theseam-ui-common-rich-text.mjs +767 -0
  31. package/fesm2015/theseam-ui-common-rich-text.mjs.map +1 -0
  32. package/fesm2015/theseam-ui-common-tel-input.mjs +1 -1
  33. package/fesm2015/theseam-ui-common-tel-input.mjs.map +1 -1
  34. package/fesm2015/theseam-ui-common-utils.mjs +9 -1
  35. package/fesm2015/theseam-ui-common-utils.mjs.map +1 -1
  36. package/fesm2020/theseam-ui-common-data-filters.mjs +2 -2
  37. package/fesm2020/theseam-ui-common-data-filters.mjs.map +1 -1
  38. package/fesm2020/theseam-ui-common-datatable.mjs +1 -1
  39. package/fesm2020/theseam-ui-common-datatable.mjs.map +1 -1
  40. package/fesm2020/theseam-ui-common-form-field.mjs +7 -3
  41. package/fesm2020/theseam-ui-common-form-field.mjs.map +1 -1
  42. package/fesm2020/theseam-ui-common-framework.mjs +4 -4
  43. package/fesm2020/theseam-ui-common-framework.mjs.map +1 -1
  44. package/fesm2020/theseam-ui-common-google-maps.mjs +1 -1
  45. package/fesm2020/theseam-ui-common-google-maps.mjs.map +1 -1
  46. package/fesm2020/theseam-ui-common-rich-text.mjs +773 -0
  47. package/fesm2020/theseam-ui-common-rich-text.mjs.map +1 -0
  48. package/fesm2020/theseam-ui-common-tel-input.mjs +1 -1
  49. package/fesm2020/theseam-ui-common-tel-input.mjs.map +1 -1
  50. package/fesm2020/theseam-ui-common-utils.mjs +9 -1
  51. package/fesm2020/theseam-ui-common-utils.mjs.map +1 -1
  52. package/form-field/input.directive.d.ts +2 -1
  53. package/package.json +9 -1
  54. package/rich-text/index.d.ts +5 -0
  55. package/rich-text/public-api.d.ts +4 -0
  56. package/rich-text/rich-text/rich-text.component.d.ts +194 -0
  57. package/rich-text/rich-text.module.d.ts +10 -0
  58. package/rich-text/utils/models.d.ts +343 -0
  59. package/rich-text/utils/utils.d.ts +14 -0
  60. package/styles/vendor/quill/_quill.scss +162 -10
  61. package/utils/is-null-or-undefined-or-empty.d.ts +1 -0
  62. package/utils/not-null-or-undefined-or-empty.d.ts +1 -0
  63. package/utils/public-api.d.ts +2 -0
@@ -0,0 +1,773 @@
1
+ import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
+ import * as i0 from '@angular/core';
3
+ import { InjectionToken, forwardRef, EventEmitter, Component, Optional, Inject, Input, Output, ViewChild, HostListener, NgModule } from '@angular/core';
4
+ import * as i2 from '@angular/forms';
5
+ import { NG_VALUE_ACCESSOR, FormControl, ReactiveFormsModule } from '@angular/forms';
6
+ import 'quill-mention';
7
+ import { BehaviorSubject, filter, shareReplay, Subject, combineLatest, map, switchMap, startWith, of, tap, take, lastValueFrom } from 'rxjs';
8
+ import { isNullOrUndefined, notNullOrUndefined, notNullOrUndefinedOrEmpty } from '@theseam/ui-common/utils';
9
+ import * as i1 from '@angular/common';
10
+ import { CommonModule } from '@angular/common';
11
+ import * as i3 from 'ngx-quill';
12
+ import { QuillModule } from 'ngx-quill';
13
+
14
+ const HTML_ENTITY_REGEX = /<[^>]+>/gm;
15
+ const THESEAM_QUILL_TOOLBAR_OPTIONS_DEFAULT = [
16
+ ['bold', 'italic', 'underline', 'strike'],
17
+ [{ header: [0, 1, 2, 3, 4, 5] }],
18
+ [{ list: 'ordered' }, { list: 'bullet' }],
19
+ [{ align: [] }],
20
+ ['link'],
21
+ ['clean'],
22
+ ];
23
+ const THESEAM_QUILL_MENTION_OPTIONS_DEFAULT = {
24
+ spaceAfterInsert: false,
25
+ positioningStrategy: 'fixed',
26
+ dataAttributes: ['type']
27
+ };
28
+ const THESEAM_QUILL_MODULES_DEFAULT = {
29
+ toolbar: THESEAM_QUILL_TOOLBAR_OPTIONS_DEFAULT
30
+ };
31
+ const THESEAM_QUILL_FORMATS_DEFAULT = [
32
+ 'align',
33
+ 'bold',
34
+ 'header',
35
+ 'indent',
36
+ 'italic',
37
+ 'link',
38
+ 'list',
39
+ 'size',
40
+ 'strike',
41
+ 'underline',
42
+ 'mention'
43
+ ];
44
+ const THESEAM_QUILL_EDITOR_CONFIG_DEFAULT = {
45
+ format: 'html',
46
+ modules: THESEAM_QUILL_MODULES_DEFAULT,
47
+ formats: THESEAM_QUILL_FORMATS_DEFAULT,
48
+ linkPlaceholder: 'https://google.com',
49
+ customToolbarPosition: 'top',
50
+ sanitize: false,
51
+ strict: true,
52
+ customOptions: [],
53
+ customModules: [],
54
+ preserveWhitespace: false,
55
+ trimOnValidation: false,
56
+ compareValues: false,
57
+ filterNull: false
58
+ };
59
+ const THESEAM_QUILL_EDITOR_CONFIG = new InjectionToken('TheSeamQuillEditorConfig');
60
+ const defaultHtmlCharacterCounterFn = (value, format) => {
61
+ if (format === 'html') {
62
+ return value.replace(HTML_ENTITY_REGEX, ' ').replace(/\s\s+/g, ' ').trim().length;
63
+ }
64
+ else if (format === 'text') {
65
+ return value.replace(/\s\s+/g, ' ').trim().length;
66
+ }
67
+ else {
68
+ console.warn(`Format ${format} not supported!`);
69
+ return 0;
70
+ }
71
+ };
72
+ function isMentionMenuOption(value) {
73
+ if (isNullOrUndefined(value) || typeof value !== 'object') {
74
+ return false;
75
+ }
76
+ return Object.prototype.hasOwnProperty.call(value, 'id');
77
+ }
78
+ const defaultMentionSearchFn = (source, textAfter, mentionChar) => {
79
+ return source.filter(u => {
80
+ if (!isMentionMenuOption(u) || u.searchIgnore === true) {
81
+ return true;
82
+ }
83
+ return u.value.toLowerCase().includes(textAfter.toLowerCase());
84
+ });
85
+ };
86
+ const defaultMentionRenderListFn = (source, searchFn, emptyListItem, textAfter, renderList, mentionChar) => {
87
+ let list = searchFn(source, textAfter, mentionChar);
88
+ if (list.length === 0 && notNullOrUndefined(emptyListItem)) {
89
+ list.push(emptyListItem);
90
+ }
91
+ else {
92
+ let reduceIdx = 0;
93
+ list = list.reduce((acc, mention) => {
94
+ const previousMenuItem = acc[reduceIdx - 1];
95
+ const previousMenuItemGroupName = isMentionMenuOption(previousMenuItem) ? previousMenuItem.groupName : undefined;
96
+ if (isMentionMenuOption(mention)
97
+ && notNullOrUndefinedOrEmpty(mention.groupName)
98
+ && mention.groupName !== previousMenuItemGroupName) {
99
+ acc.push({
100
+ value: mention.groupName,
101
+ disabled: true,
102
+ type: 'groupName'
103
+ });
104
+ reduceIdx++;
105
+ }
106
+ if (!isMentionMenuOption(mention)) {
107
+ mention.disabled = true;
108
+ }
109
+ acc.push(mention);
110
+ reduceIdx++;
111
+ return acc;
112
+ }, []);
113
+ }
114
+ renderList(list, textAfter);
115
+ };
116
+ // Keeping this as an example for a custom menu item.
117
+ // There doesn't appear to be a better way of passing in a custom template.
118
+ // export const defaultMentionRenderItem = (item: TheSeamQuillMentionListItem) => {
119
+ // if (item.type === 'groupName') {
120
+ // const text = document.createElement('div')
121
+ // text.setAttribute('class', 'small text-black-50')
122
+ // text.innerText = item.value
123
+ // return text.outerHTML
124
+ // }
125
+ // else if (item.type === 'divider') {
126
+ // const div = document.createElement('hr')
127
+ // div.setAttribute('class', 'my-2')
128
+ // return div.outerHTML
129
+ // }
130
+ // else {
131
+ // return item.value
132
+ // }
133
+ // }
134
+
135
+ const RICH_TEXT_VALUE_ACCESSOR = {
136
+ provide: NG_VALUE_ACCESSOR,
137
+ useExisting: forwardRef(() => RichTextComponent),
138
+ multi: true
139
+ };
140
+ class RichTextComponent {
141
+ get required() {
142
+ return this._required;
143
+ }
144
+ set required(value) {
145
+ if (notNullOrUndefined(value)) {
146
+ this._required = coerceBooleanProperty(value);
147
+ }
148
+ }
149
+ /** @default '' (empty string) */
150
+ get placeholder() {
151
+ return this._placeholder;
152
+ }
153
+ set placeholder(value) {
154
+ if (notNullOrUndefined(value)) {
155
+ this._placeholder = value;
156
+ }
157
+ }
158
+ /**
159
+ * Initial rows visible in text area, set using `height` and `minHeight`.
160
+ *
161
+ * NOTE: Resizable editors will not be able to size below the height calculated from this value.
162
+ *
163
+ * @default 5
164
+ */
165
+ get rows() {
166
+ return this._rows;
167
+ }
168
+ set rows(value) {
169
+ if (notNullOrUndefined(value)) {
170
+ this._rows = value;
171
+ this._pollCalculatedRowHeight.next();
172
+ }
173
+ }
174
+ /**
175
+ * When `true`, text area can be resized vertically.
176
+ *
177
+ * @default true
178
+ */
179
+ get resizable() {
180
+ return this._resizable;
181
+ }
182
+ set resizable(value) {
183
+ if (notNullOrUndefined(value)) {
184
+ this._resizable = coerceBooleanProperty(value);
185
+ }
186
+ }
187
+ /** @default false */
188
+ get readOnly() {
189
+ return this._readOnly;
190
+ }
191
+ set readOnly(value) {
192
+ if (notNullOrUndefined(value)) {
193
+ this._readOnly = coerceBooleanProperty(value);
194
+ }
195
+ }
196
+ // TODO: fix bug where initial html value is rendered in plain text
197
+ /**
198
+ * When `true`, overrides all other provided settings to present the editor
199
+ * as a standard textbox.
200
+ *
201
+ * NOTE: For the moment, `<p></p>` tags are still allowed, triggered by the `Enter` key.
202
+ * https://github.com/slab/quill/issues/1432
203
+ *
204
+ * @default false
205
+ */
206
+ get disableRichText() {
207
+ return this._disableRichText;
208
+ }
209
+ set disableRichText(value) {
210
+ if (notNullOrUndefined(value)) {
211
+ this._disableRichText = coerceBooleanProperty(value);
212
+ this._updateQuillConfig();
213
+ }
214
+ }
215
+ /**
216
+ * When `true`, displays a character counter at the bottom of the editor.
217
+ * Character counter text is determined by values provided in `minLength`
218
+ * and `maxLength`.
219
+ *
220
+ * The default count algorithm strips out html entities and replaces them
221
+ * with spaces to get the string length. To override this behavior, pass
222
+ * a custom function to `characterCounterFn`.
223
+ *
224
+ * To override default character counter display, pass a custom template to `characterCountTpl`.
225
+ *
226
+ * @default false
227
+ */
228
+ get displayCharacterCounter() {
229
+ return this._displayCharacterCounter;
230
+ }
231
+ set displayCharacterCounter(value) {
232
+ if (notNullOrUndefined(value)) {
233
+ this._displayCharacterCounter = coerceBooleanProperty(value);
234
+ }
235
+ }
236
+ get minLength() {
237
+ return this._minLength;
238
+ }
239
+ set minLength(value) {
240
+ if (notNullOrUndefined(value)) {
241
+ this._minLength = value;
242
+ }
243
+ }
244
+ get maxLength() {
245
+ return this._maxLength;
246
+ }
247
+ set maxLength(value) {
248
+ if (notNullOrUndefined(value)) {
249
+ this._maxLength = value;
250
+ }
251
+ }
252
+ /**
253
+ * Set to override default character counter display.
254
+ *
255
+ * Template context includes variables `minLength`, `maxLength`, and `characterCount`.
256
+ */
257
+ get characterCounterTpl() {
258
+ return this._characterCounterTpl;
259
+ }
260
+ set characterCounterTpl(value) {
261
+ if (notNullOrUndefined(value)) {
262
+ this._characterCounterTpl = value;
263
+ }
264
+ }
265
+ /**
266
+ * Set to override default counter function, which strips html entities and
267
+ * replaces them with a space.
268
+ */
269
+ get characterCounterFn() {
270
+ return this._characterCounterFn;
271
+ }
272
+ set characterCounterFn(value) {
273
+ if (notNullOrUndefined(value)) {
274
+ this._characterCounterFn = value;
275
+ }
276
+ }
277
+ /**
278
+ * If `true`, component will configure the Quill editor with the quill-mentions extension
279
+ * and listen for values passed to `mentionItems` to populate the mentions menu.
280
+ *
281
+ * @default false
282
+ */
283
+ get useMentions() {
284
+ return this._useMentions;
285
+ }
286
+ set useMentions(value) {
287
+ if (notNullOrUndefined(value)) {
288
+ this._useMentions = coerceBooleanProperty(value);
289
+ this._updateQuillConfig();
290
+ }
291
+ }
292
+ /**
293
+ * List of users, user groups, or other entities to display in mentions menu.
294
+ * Minimum required properties are `id` (unique) and `value`, which acts as the label.
295
+ *
296
+ * By default, the menu is triggered by typing the `@` symbol into the text area.
297
+ */
298
+ get mentionItems() {
299
+ return this._mentionItems.value;
300
+ }
301
+ set mentionItems(value) {
302
+ if (notNullOrUndefined(value)) {
303
+ this._mentionItems.next(value);
304
+ }
305
+ }
306
+ /**
307
+ * Set to override default search function when user is typing a mention.
308
+ */
309
+ get mentionSearchFn() {
310
+ return this._mentionSearchFn;
311
+ }
312
+ set mentionSearchFn(value) {
313
+ if (notNullOrUndefined(value)) {
314
+ this._mentionSearchFn = value;
315
+ }
316
+ }
317
+ /**
318
+ * Set to override default render function for mentions list.
319
+ *
320
+ * Function should call `renderList`.
321
+ */
322
+ get mentionRenderListFn() {
323
+ return this._mentionRenderListFn;
324
+ }
325
+ set mentionRenderListFn(value) {
326
+ if (notNullOrUndefined(value)) {
327
+ this._mentionRenderListFn = value;
328
+ }
329
+ }
330
+ /**
331
+ * Set to override default text shown while mention items are loading.
332
+ *
333
+ * @default 'Loading...'
334
+ */
335
+ get mentionListLoadingText() {
336
+ return this._mentionListLoadingText;
337
+ }
338
+ set mentionListLoadingText(value) {
339
+ if (notNullOrUndefined(value)) {
340
+ this._mentionListLoadingText = value;
341
+ }
342
+ }
343
+ /**
344
+ * Set to override default text shown when a mention search returns no items.
345
+ *
346
+ * @default 'No matches found.'
347
+ */
348
+ get mentionListEmptyText() {
349
+ return this._mentionListEmptyText;
350
+ }
351
+ set mentionListEmptyText(value) {
352
+ if (notNullOrUndefined(value)) {
353
+ this._mentionListEmptyText = value;
354
+ }
355
+ }
356
+ get mentionListEmptyItem() {
357
+ return {
358
+ id: 'undefined',
359
+ value: this.mentionListEmptyText,
360
+ disabled: true,
361
+ emptyList: true
362
+ };
363
+ }
364
+ get quillEditor() {
365
+ return this._quillEditor;
366
+ }
367
+ set quillEditor(value) {
368
+ this._quillEditor = value;
369
+ // setTimeout because full html isn't available until the next tick
370
+ setTimeout(() => {
371
+ this._pollCalculatedRowHeight.next();
372
+ }, 0);
373
+ }
374
+ _handleKeydown(event) {
375
+ if (event.code === 'Escape') {
376
+ const qlEditor = this._quillEditor?.editorElem?.querySelector('.ql-editor');
377
+ if (notNullOrUndefined(qlEditor) && qlEditor === document.activeElement && qlEditor instanceof HTMLElement) {
378
+ event.preventDefault();
379
+ event.stopImmediatePropagation();
380
+ qlEditor.blur();
381
+ }
382
+ }
383
+ }
384
+ constructor(_renderer, _customConfig) {
385
+ this._renderer = _renderer;
386
+ this._customConfig = _customConfig;
387
+ this.formControl = new FormControl(null);
388
+ this._required = false;
389
+ this._placeholder = '';
390
+ this._rows = 5;
391
+ this._resizable = true;
392
+ this._readOnly = false;
393
+ this._disableRichText = false;
394
+ this._displayCharacterCounter = false;
395
+ this._characterCounterTpl = null;
396
+ this._characterCounterFn = defaultHtmlCharacterCounterFn;
397
+ this._useMentions = false;
398
+ this._mentionItems = new BehaviorSubject(undefined);
399
+ this.mentionItems$ = this._mentionItems.asObservable().pipe(filter(mentions => notNullOrUndefined(mentions) && mentions.length > 0), shareReplay({ bufferSize: 1, refCount: true }));
400
+ this._mentionSearchFn = defaultMentionSearchFn;
401
+ this._mentionRenderListFn = defaultMentionRenderListFn;
402
+ this._mentionListLoadingText = 'Loading...';
403
+ this._mentionListEmptyText = 'No matches found.';
404
+ this.quillEditorCreated = new EventEmitter();
405
+ this.quillEditorChanged = new EventEmitter();
406
+ this.quillContentChanged = new EventEmitter();
407
+ this.quillSelectionChanged = new EventEmitter();
408
+ this.quillFocus = new EventEmitter();
409
+ this.quillBlur = new EventEmitter();
410
+ this.mentionsUpdated = new EventEmitter();
411
+ this._isWritingValue = false;
412
+ this._pollCalculatedRowHeight = new Subject();
413
+ this._configSet = new BehaviorSubject(false);
414
+ this.configSet$ = this._configSet.asObservable();
415
+ this._stylesSet = new BehaviorSubject(false);
416
+ this._templateSet = new BehaviorSubject(false);
417
+ this._config = new BehaviorSubject(undefined);
418
+ this.config$ = this._config.asObservable();
419
+ this._selectedMentions = new BehaviorSubject([]);
420
+ this.selectedMentions$ = this._selectedMentions.asObservable();
421
+ this.initialized$ = combineLatest([
422
+ this._configSet.asObservable(),
423
+ this._stylesSet.asObservable(),
424
+ this._templateSet.asObservable(),
425
+ ]).pipe(map(sets => sets.findIndex(s => !s) === -1));
426
+ this.characterCount$ = this.initialized$.pipe(filter(i => i), switchMap(() => {
427
+ if (notNullOrUndefined(this.formControl)) {
428
+ return this.formControl.valueChanges.pipe(startWith(this.formControl.value), map(v => this.characterCounterFn(v || '', this._config.value?.format)));
429
+ }
430
+ return of(0);
431
+ }));
432
+ this.selectedMentions$.pipe(tap(mentions => this.mentionsUpdated.emit(mentions))).subscribe();
433
+ }
434
+ ngOnInit() {
435
+ // TODO: test this more
436
+ // ignore quill initial valueChange event, to keep functionality in line with other inputs
437
+ let initialEmitComplete = false;
438
+ this._configSet.pipe(filter(s => s), switchMap(() => this.formControl.valueChanges.pipe(
439
+ // skip(1),
440
+ take(1), tap(() => initialEmitComplete = true)))).subscribe();
441
+ this.formControl.valueChanges.pipe(filter(() => !this._isWritingValue && initialEmitComplete), tap(value => this.value = value)).subscribe();
442
+ this._pollCalculatedRowHeight.asObservable().pipe(tap(() => {
443
+ this._stylesSet.next(false);
444
+ const calculatedRowsHeight = `${this.rows * 1.5}rem`;
445
+ if (notNullOrUndefined(this.rows) && notNullOrUndefined(this._quillEditor)) {
446
+ const editorEl = this._quillEditor.elementRef.nativeElement.querySelector('.ql-editor');
447
+ if (notNullOrUndefined(editorEl)) {
448
+ this._renderer.setStyle(editorEl, 'height', calculatedRowsHeight);
449
+ this._renderer.setStyle(editorEl, 'min-height', calculatedRowsHeight);
450
+ }
451
+ }
452
+ this._stylesSet.next(true);
453
+ })).subscribe();
454
+ this._buildQuillConfig();
455
+ }
456
+ ngAfterViewInit() {
457
+ if (isNullOrUndefined(this.characterCounterTpl)) {
458
+ this.characterCounterTpl = this.defaultCharacterCounterTpl || null;
459
+ }
460
+ this._templateSet.next(true);
461
+ }
462
+ ngOnDestroy() {
463
+ // hacky way to ensure mentions menu gets destroyed when component is destroyed
464
+ const mentionModule = this._quillEditor?.quillEditor.getModule('mention');
465
+ const hideMentionList = mentionModule?.hideMentionList;
466
+ if (hideMentionList && typeof hideMentionList === 'function') {
467
+ hideMentionList.call(mentionModule);
468
+ }
469
+ }
470
+ _buildQuillConfig() {
471
+ this._configSet.next(false);
472
+ const config = {
473
+ ...THESEAM_QUILL_EDITOR_CONFIG_DEFAULT
474
+ };
475
+ if (this.disableRichText) {
476
+ config.format = 'text';
477
+ config.formats = [];
478
+ config.modules = {
479
+ toolbar: false
480
+ };
481
+ }
482
+ else {
483
+ config.format = this._getConfigOrDefault('format');
484
+ config.formats = this._getConfigOrDefault('formats');
485
+ config.modules = {
486
+ ...this._getConfigOrDefault('modules')
487
+ };
488
+ }
489
+ if (this.useMentions) {
490
+ const mentionModules = {
491
+ ...config.modules,
492
+ mention: {
493
+ ...THESEAM_QUILL_MENTION_OPTIONS_DEFAULT,
494
+ renderLoading: () => this.mentionListLoadingText,
495
+ source: async (searchTerm, renderList, mentionChar) => {
496
+ // this function is called every time the menu is triggered,
497
+ // so it will always have the latest value from mentionItems$
498
+ const mentionsAsync = await lastValueFrom(this.mentionItems$.pipe(take(1)));
499
+ if (notNullOrUndefined(mentionsAsync)) {
500
+ this.mentionRenderListFn(mentionsAsync, this.mentionSearchFn, this.mentionListEmptyItem, searchTerm, renderList, mentionChar);
501
+ }
502
+ },
503
+ ...config.modules?.mention,
504
+ }
505
+ };
506
+ config.modules = mentionModules;
507
+ }
508
+ config.theme = this._getConfigOrDefault('theme');
509
+ config.debug = this._getConfigOrDefault('debug');
510
+ config.customToolbarPosition = this._getConfigOrDefault('customToolbarPosition');
511
+ config.sanitize = this._getConfigOrDefault('sanitize');
512
+ config.styles = this._getConfigOrDefault('styles');
513
+ config.strict = this._getConfigOrDefault('strict');
514
+ config.scrollingContainer = this._getConfigOrDefault('scrollingContainer');
515
+ config.bounds = this._getConfigOrDefault('bounds');
516
+ config.customOptions = this._getConfigOrDefault('customOptions');
517
+ config.customModules = this._getConfigOrDefault('customModules');
518
+ config.trackChanges = this._getConfigOrDefault('trackChanges');
519
+ config.preserveWhitespace = this._getConfigOrDefault('preserveWhitespace');
520
+ config.classes = this._getConfigOrDefault('classes');
521
+ config.trimOnValidation = this._getConfigOrDefault('trimOnValidation');
522
+ config.linkPlaceholder = this._getConfigOrDefault('linkPlaceholder');
523
+ config.compareValues = this._getConfigOrDefault('compareValues');
524
+ config.filterNull = this._getConfigOrDefault('filterNull');
525
+ config.debounceTime = this._getConfigOrDefault('debounceTime');
526
+ this._config.next(config);
527
+ // setTimeout bc ngx-quill library doesn't listen for input changes,
528
+ // so we must destroy and create the component each time config is updated
529
+ setTimeout(() => {
530
+ this._configSet.next(true);
531
+ }, 0);
532
+ }
533
+ _getConfigOrDefault(prop) {
534
+ if (this._customConfig && Object.prototype.hasOwnProperty.call(this._customConfig, prop)) {
535
+ return this._customConfig[prop];
536
+ }
537
+ return THESEAM_QUILL_EDITOR_CONFIG_DEFAULT[prop];
538
+ }
539
+ _updateQuillConfig() {
540
+ if (this._configSet.value) {
541
+ this._buildQuillConfig();
542
+ }
543
+ }
544
+ // Not usable, bc config has to be set immediately, potentially before mentions populate.
545
+ // To set customDataAttributes, provide a custom config.
546
+ // private _getCustomDataAttributes(mentions: TheSeamQuillMentionMenuOption[]): string[] {
547
+ // return mentions.reduce((acc, mention) => {
548
+ // const keys = Object.keys(mention)
549
+ // keys.forEach(key => {
550
+ // if (acc.findIndex(a => a === key) === -1) {
551
+ // acc.push(key)
552
+ // }
553
+ // })
554
+ // return acc
555
+ // }, <string[]>[] satisfies string[])
556
+ // }
557
+ get value() {
558
+ return this._value;
559
+ }
560
+ set value(value) {
561
+ this._value = value;
562
+ if (this.onChange) {
563
+ this.onChange(value);
564
+ }
565
+ if (this.onTouched) {
566
+ this.onTouched();
567
+ }
568
+ }
569
+ writeValue(value) {
570
+ this._isWritingValue = true;
571
+ this.formControl.setValue(value);
572
+ this.value = value;
573
+ this._isWritingValue = false;
574
+ }
575
+ registerOnChange(fn) {
576
+ this.onChange = fn;
577
+ }
578
+ registerOnTouched(fn) {
579
+ this.onTouched = fn;
580
+ }
581
+ setDisabledState(isDisabled) {
582
+ this.readOnly = isDisabled;
583
+ }
584
+ _onEditorCreated(event) {
585
+ this.quillEditorCreated.emit(event);
586
+ }
587
+ _onEditorChanged(event) {
588
+ this.quillEditorChanged.emit(event);
589
+ }
590
+ _onContentChanged(event) {
591
+ this.quillContentChanged.emit(event);
592
+ this._updateMentionsFromDelta(event.content);
593
+ }
594
+ _onSelectionChanged(event) {
595
+ this.quillSelectionChanged.emit(event);
596
+ }
597
+ _onFocus(event) {
598
+ this.quillFocus.emit(event);
599
+ }
600
+ _onBlur(event) {
601
+ this.quillBlur.emit(event);
602
+ if (this.onTouched) {
603
+ this.onTouched();
604
+ }
605
+ }
606
+ /**
607
+ * Hacky way to track mention inserts/deletes
608
+ */
609
+ _updateMentionsFromDelta(content) {
610
+ if (notNullOrUndefined(content.ops)) {
611
+ const contentMentionIds = content.ops.map(o => o.insert?.mention?.id).filter(notNullOrUndefined);
612
+ const selectedMentions = [...this._selectedMentions.value];
613
+ const mentionOptions = [...this._mentionItems.value || []];
614
+ const newMentions = contentMentionIds.reduce((acc, mentionId) => {
615
+ const insertMention = mentionOptions.find(m => isMentionMenuOption(m) && m.id === mentionId);
616
+ if (notNullOrUndefined(insertMention)) {
617
+ acc.push(insertMention);
618
+ }
619
+ else {
620
+ console.warn('Mention addition failed! Selected mention option not found:', mentionId);
621
+ }
622
+ return acc;
623
+ }, []);
624
+ let emitUpdate = false;
625
+ if (selectedMentions.length !== newMentions.length) {
626
+ // if the length has changed, we know an update occurred
627
+ emitUpdate = true;
628
+ }
629
+ else {
630
+ // otherwise, test ids for old and new items to see if we need to emit a change
631
+ const selectedMentionIds = this._selectedMentions.value.map(m => m.id);
632
+ const newMentionIds = newMentions.map(m => m.id);
633
+ if (selectedMentionIds.findIndex(m => !newMentionIds.includes(m)) !== -1 ||
634
+ newMentionIds.findIndex(m => !selectedMentionIds.includes(m)) !== -1) {
635
+ emitUpdate = true;
636
+ }
637
+ }
638
+ if (emitUpdate) {
639
+ this._selectedMentions.next(newMentions);
640
+ }
641
+ }
642
+ }
643
+ }
644
+ RichTextComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: RichTextComponent, deps: [{ token: i0.Renderer2 }, { token: THESEAM_QUILL_EDITOR_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component });
645
+ RichTextComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.7", type: RichTextComponent, selector: "seam-rich-text", inputs: { val: ["value", "val"], required: "required", placeholder: "placeholder", rows: "rows", resizable: "resizable", disableRichText: "disableRichText", displayCharacterCounter: "displayCharacterCounter", minLength: "minLength", maxLength: "maxLength", characterCounterTpl: "characterCounterTpl", characterCounterFn: "characterCounterFn", useMentions: "useMentions", mentionItems: "mentionItems", mentionSearchFn: "mentionSearchFn", mentionRenderListFn: "mentionRenderListFn", mentionListLoadingText: "mentionListLoadingText", mentionListEmptyText: "mentionListEmptyText" }, outputs: { quillEditorCreated: "quillEditorCreated", quillEditorChanged: "quillEditorChanged", quillContentChanged: "quillContentChanged", quillSelectionChanged: "quillSelectionChanged", quillFocus: "quillFocus", quillBlur: "quillBlur", mentionsUpdated: "mentionsUpdated" }, host: { listeners: { "keydown": "_handleKeydown($event)" } }, providers: [
646
+ RICH_TEXT_VALUE_ACCESSOR
647
+ ], viewQueries: [{ propertyName: "quillEditor", first: true, predicate: ["quillEditor"], descendants: true }, { propertyName: "defaultCharacterCounterTpl", first: true, predicate: ["characterCounter"], descendants: true }], ngImport: i0, template: "<div class=\"editor\" *ngIf=\"configSet$ | async\" [class.initializing]=\"!(initialized$ | async)\">\n <ng-container *ngIf=\"config$ | async as config\">\n <quill-editor\n #quillEditor\n seamInput\n [class.disabled]=\"readOnly\"\n [attr.disabled]=\"readOnly\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [readOnly]=\"readOnly\"\n [required]=\"required\"\n [minLength]=\"minLength\"\n [maxLength]=\"maxLength\"\n [format]=\"config.format\"\n [theme]=\"config.theme\"\n [modules]=\"config.modules\"\n [debug]=\"config.debug\"\n [formats]=\"config.formats\"\n [customToolbarPosition]=\"config.customToolbarPosition\"\n [sanitize]=\"config.sanitize\"\n [styles]=\"config.styles\"\n [strict]=\"config.strict\"\n [scrollingContainer]=\"config.scrollingContainer\"\n [bounds]=\"config.bounds\"\n [customOptions]=\"config.customOptions\"\n [customModules]=\"config.customModules\"\n [trackChanges]=\"config.trackChanges\"\n [preserveWhitespace]=\"config.preserveWhitespace\"\n [classes]=\"config.classes\"\n [trimOnValidation]=\"config.trimOnValidation\"\n [linkPlaceholder]=\"config.linkPlaceholder\"\n [compareValues]=\"config.compareValues\"\n [filterNull]=\"config.filterNull\"\n [debounceTime]=\"config.debounceTime\"\n [class.can-resize]=\"resizable\"\n (onEditorCreated)=\"_onEditorCreated($event)\"\n (onEditorChanged)=\"_onEditorChanged($event)\"\n (onContentChanged)=\"_onContentChanged($event)\"\n (onSelectionChanged)=\"_onSelectionChanged($event)\"\n (onFocus)=\"_onFocus($event)\"\n (onBlur)=\"_onBlur($event)\"></quill-editor>\n <ng-container *ngIf=\"displayCharacterCounter\">\n <ng-container\n [ngTemplateOutlet]=\"characterCounterTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: characterCount$ | async, minLength: minLength, maxLength: maxLength, characterCount: characterCount$ | async }\"></ng-container>\n </ng-container>\n </ng-container>\n</div>\n\n<ng-template #characterCounter let-implicit let-minLength=\"minLength\" let-maxLength=\"maxLength\" let-characterCount=\"characterCount\">\n <div class=\"small text-black-50 text-right\">\n <ng-container *ngIf=\"characterCount || characterCount === 0\">\n Character Count:\n <span [class.text-danger]=\"characterCount && ((maxLength && characterCount > maxLength) || (minLength && characterCount < minLength))\">\n {{ characterCount | number }}\n </span>\n <ng-container *ngIf=\"maxLength\">\n / {{ maxLength }}\n </ng-container>\n <span *ngIf=\"minLength\" class=\"font-italic\">\n (minimum {{ minLength }} expected)\n </span>\n </ng-container>\n </div>\n</ng-template>\n", styles: [":host .editor{position:relative}:host .editor.initializing:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:#f8f9fa;z-index:1}:host quill-editor.can-resize ::ng-deep .ql-editor{overflow:auto;resize:vertical}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: i3.QuillEditorComponent, selector: "quill-editor" }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] });
648
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: RichTextComponent, decorators: [{
649
+ type: Component,
650
+ args: [{ selector: 'seam-rich-text', providers: [
651
+ RICH_TEXT_VALUE_ACCESSOR
652
+ ], template: "<div class=\"editor\" *ngIf=\"configSet$ | async\" [class.initializing]=\"!(initialized$ | async)\">\n <ng-container *ngIf=\"config$ | async as config\">\n <quill-editor\n #quillEditor\n seamInput\n [class.disabled]=\"readOnly\"\n [attr.disabled]=\"readOnly\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [readOnly]=\"readOnly\"\n [required]=\"required\"\n [minLength]=\"minLength\"\n [maxLength]=\"maxLength\"\n [format]=\"config.format\"\n [theme]=\"config.theme\"\n [modules]=\"config.modules\"\n [debug]=\"config.debug\"\n [formats]=\"config.formats\"\n [customToolbarPosition]=\"config.customToolbarPosition\"\n [sanitize]=\"config.sanitize\"\n [styles]=\"config.styles\"\n [strict]=\"config.strict\"\n [scrollingContainer]=\"config.scrollingContainer\"\n [bounds]=\"config.bounds\"\n [customOptions]=\"config.customOptions\"\n [customModules]=\"config.customModules\"\n [trackChanges]=\"config.trackChanges\"\n [preserveWhitespace]=\"config.preserveWhitespace\"\n [classes]=\"config.classes\"\n [trimOnValidation]=\"config.trimOnValidation\"\n [linkPlaceholder]=\"config.linkPlaceholder\"\n [compareValues]=\"config.compareValues\"\n [filterNull]=\"config.filterNull\"\n [debounceTime]=\"config.debounceTime\"\n [class.can-resize]=\"resizable\"\n (onEditorCreated)=\"_onEditorCreated($event)\"\n (onEditorChanged)=\"_onEditorChanged($event)\"\n (onContentChanged)=\"_onContentChanged($event)\"\n (onSelectionChanged)=\"_onSelectionChanged($event)\"\n (onFocus)=\"_onFocus($event)\"\n (onBlur)=\"_onBlur($event)\"></quill-editor>\n <ng-container *ngIf=\"displayCharacterCounter\">\n <ng-container\n [ngTemplateOutlet]=\"characterCounterTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: characterCount$ | async, minLength: minLength, maxLength: maxLength, characterCount: characterCount$ | async }\"></ng-container>\n </ng-container>\n </ng-container>\n</div>\n\n<ng-template #characterCounter let-implicit let-minLength=\"minLength\" let-maxLength=\"maxLength\" let-characterCount=\"characterCount\">\n <div class=\"small text-black-50 text-right\">\n <ng-container *ngIf=\"characterCount || characterCount === 0\">\n Character Count:\n <span [class.text-danger]=\"characterCount && ((maxLength && characterCount > maxLength) || (minLength && characterCount < minLength))\">\n {{ characterCount | number }}\n </span>\n <ng-container *ngIf=\"maxLength\">\n / {{ maxLength }}\n </ng-container>\n <span *ngIf=\"minLength\" class=\"font-italic\">\n (minimum {{ minLength }} expected)\n </span>\n </ng-container>\n </div>\n</ng-template>\n", styles: [":host .editor{position:relative}:host .editor.initializing:before{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;background:#f8f9fa;z-index:1}:host quill-editor.can-resize ::ng-deep .ql-editor{overflow:auto;resize:vertical}\n"] }]
653
+ }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: undefined, decorators: [{
654
+ type: Optional
655
+ }, {
656
+ type: Inject,
657
+ args: [THESEAM_QUILL_EDITOR_CONFIG]
658
+ }] }]; }, propDecorators: { val: [{
659
+ type: Input,
660
+ args: ['value']
661
+ }], required: [{
662
+ type: Input
663
+ }], placeholder: [{
664
+ type: Input
665
+ }], rows: [{
666
+ type: Input
667
+ }], resizable: [{
668
+ type: Input
669
+ }], disableRichText: [{
670
+ type: Input
671
+ }], displayCharacterCounter: [{
672
+ type: Input
673
+ }], minLength: [{
674
+ type: Input
675
+ }], maxLength: [{
676
+ type: Input
677
+ }], characterCounterTpl: [{
678
+ type: Input
679
+ }], characterCounterFn: [{
680
+ type: Input
681
+ }], useMentions: [{
682
+ type: Input
683
+ }], mentionItems: [{
684
+ type: Input
685
+ }], mentionSearchFn: [{
686
+ type: Input
687
+ }], mentionRenderListFn: [{
688
+ type: Input
689
+ }], mentionListLoadingText: [{
690
+ type: Input
691
+ }], mentionListEmptyText: [{
692
+ type: Input
693
+ }], quillEditorCreated: [{
694
+ type: Output
695
+ }], quillEditorChanged: [{
696
+ type: Output
697
+ }], quillContentChanged: [{
698
+ type: Output
699
+ }], quillSelectionChanged: [{
700
+ type: Output
701
+ }], quillFocus: [{
702
+ type: Output
703
+ }], quillBlur: [{
704
+ type: Output
705
+ }], mentionsUpdated: [{
706
+ type: Output
707
+ }], quillEditor: [{
708
+ type: ViewChild,
709
+ args: ['quillEditor']
710
+ }], defaultCharacterCounterTpl: [{
711
+ type: ViewChild,
712
+ args: ['characterCounter']
713
+ }], _handleKeydown: [{
714
+ type: HostListener,
715
+ args: ['keydown', ['$event']]
716
+ }] } });
717
+
718
+ const THESEAM_QUILL_FORMATS = [
719
+ 'align',
720
+ 'background',
721
+ 'blockquote',
722
+ 'bold',
723
+ 'code-block',
724
+ 'code',
725
+ 'color',
726
+ 'direction',
727
+ 'font',
728
+ 'header',
729
+ 'image',
730
+ 'indent',
731
+ 'italic',
732
+ 'link',
733
+ 'list',
734
+ 'script',
735
+ 'size',
736
+ 'strike',
737
+ 'underline',
738
+ 'video',
739
+ 'mention'
740
+ ];
741
+
742
+ class TheSeamRichTextModule {
743
+ }
744
+ TheSeamRichTextModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: TheSeamRichTextModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
745
+ TheSeamRichTextModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.7", ngImport: i0, type: TheSeamRichTextModule, declarations: [RichTextComponent], imports: [CommonModule,
746
+ ReactiveFormsModule,
747
+ QuillModule], exports: [RichTextComponent] });
748
+ TheSeamRichTextModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: TheSeamRichTextModule, imports: [CommonModule,
749
+ ReactiveFormsModule,
750
+ QuillModule] });
751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: TheSeamRichTextModule, decorators: [{
752
+ type: NgModule,
753
+ args: [{
754
+ declarations: [
755
+ RichTextComponent,
756
+ ],
757
+ imports: [
758
+ CommonModule,
759
+ ReactiveFormsModule,
760
+ QuillModule
761
+ ],
762
+ exports: [
763
+ RichTextComponent,
764
+ ]
765
+ }]
766
+ }] });
767
+
768
+ /**
769
+ * Generated bundle index. Do not edit.
770
+ */
771
+
772
+ export { HTML_ENTITY_REGEX, RICH_TEXT_VALUE_ACCESSOR, RichTextComponent, THESEAM_QUILL_EDITOR_CONFIG, THESEAM_QUILL_EDITOR_CONFIG_DEFAULT, THESEAM_QUILL_FORMATS, THESEAM_QUILL_FORMATS_DEFAULT, THESEAM_QUILL_MENTION_OPTIONS_DEFAULT, THESEAM_QUILL_MODULES_DEFAULT, THESEAM_QUILL_TOOLBAR_OPTIONS_DEFAULT, TheSeamRichTextModule, defaultHtmlCharacterCounterFn, defaultMentionRenderListFn, defaultMentionSearchFn, isMentionMenuOption };
773
+ //# sourceMappingURL=theseam-ui-common-rich-text.mjs.map