@doxi/angular 0.11.0

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/doxiva-doxiva-editor.component.d.ts +118 -0
  4. package/dist/doxiva-doxiva-editor.component.d.ts.map +1 -0
  5. package/dist/doxiva-doxiva-editor.component.js +1095 -0
  6. package/dist/doxiva-doxiva-editor.component.js.map +1 -0
  7. package/dist/doxiva-editor.component.d.ts +23 -0
  8. package/dist/doxiva-editor.component.d.ts.map +1 -0
  9. package/dist/doxiva-editor.component.js +92 -0
  10. package/dist/doxiva-editor.component.js.map +1 -0
  11. package/dist/doxiva-toolbar.component.d.ts +118 -0
  12. package/dist/doxiva-toolbar.component.d.ts.map +1 -0
  13. package/dist/doxiva-toolbar.component.js +339 -0
  14. package/dist/doxiva-toolbar.component.js.map +1 -0
  15. package/dist/doxiva.types.d.ts +71 -0
  16. package/dist/doxiva.types.d.ts.map +1 -0
  17. package/dist/doxiva.types.js +28 -0
  18. package/dist/doxiva.types.js.map +1 -0
  19. package/dist/index.d.ts +8 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +8 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/toolbar-builders/alignment.d.ts +4 -0
  24. package/dist/toolbar-builders/alignment.d.ts.map +1 -0
  25. package/dist/toolbar-builders/alignment.js +56 -0
  26. package/dist/toolbar-builders/alignment.js.map +1 -0
  27. package/dist/toolbar-builders/blocks.d.ts +4 -0
  28. package/dist/toolbar-builders/blocks.d.ts.map +1 -0
  29. package/dist/toolbar-builders/blocks.js +39 -0
  30. package/dist/toolbar-builders/blocks.js.map +1 -0
  31. package/dist/toolbar-builders/font-size.d.ts +4 -0
  32. package/dist/toolbar-builders/font-size.d.ts.map +1 -0
  33. package/dist/toolbar-builders/font-size.js +35 -0
  34. package/dist/toolbar-builders/font-size.js.map +1 -0
  35. package/dist/toolbar-builders/headings.d.ts +4 -0
  36. package/dist/toolbar-builders/headings.d.ts.map +1 -0
  37. package/dist/toolbar-builders/headings.js +18 -0
  38. package/dist/toolbar-builders/headings.js.map +1 -0
  39. package/dist/toolbar-builders/history.d.ts +3 -0
  40. package/dist/toolbar-builders/history.d.ts.map +1 -0
  41. package/dist/toolbar-builders/history.js +13 -0
  42. package/dist/toolbar-builders/history.js.map +1 -0
  43. package/dist/toolbar-builders/images.d.ts +4 -0
  44. package/dist/toolbar-builders/images.d.ts.map +1 -0
  45. package/dist/toolbar-builders/images.js +8 -0
  46. package/dist/toolbar-builders/images.js.map +1 -0
  47. package/dist/toolbar-builders/line-height.d.ts +4 -0
  48. package/dist/toolbar-builders/line-height.d.ts.map +1 -0
  49. package/dist/toolbar-builders/line-height.js +46 -0
  50. package/dist/toolbar-builders/line-height.js.map +1 -0
  51. package/dist/toolbar-builders/links.d.ts +4 -0
  52. package/dist/toolbar-builders/links.d.ts.map +1 -0
  53. package/dist/toolbar-builders/links.js +19 -0
  54. package/dist/toolbar-builders/links.js.map +1 -0
  55. package/dist/toolbar-builders/lists.d.ts +4 -0
  56. package/dist/toolbar-builders/lists.d.ts.map +1 -0
  57. package/dist/toolbar-builders/lists.js +38 -0
  58. package/dist/toolbar-builders/lists.js.map +1 -0
  59. package/dist/toolbar-builders/marks.d.ts +4 -0
  60. package/dist/toolbar-builders/marks.d.ts.map +1 -0
  61. package/dist/toolbar-builders/marks.js +41 -0
  62. package/dist/toolbar-builders/marks.js.map +1 -0
  63. package/dist/toolbar-builders/system.d.ts +12 -0
  64. package/dist/toolbar-builders/system.d.ts.map +1 -0
  65. package/dist/toolbar-builders/system.js +28 -0
  66. package/dist/toolbar-builders/system.js.map +1 -0
  67. package/dist/toolbar-builders/tables.d.ts +4 -0
  68. package/dist/toolbar-builders/tables.d.ts.map +1 -0
  69. package/dist/toolbar-builders/tables.js +212 -0
  70. package/dist/toolbar-builders/tables.js.map +1 -0
  71. package/dist/toolbar-builders/typography.d.ts +4 -0
  72. package/dist/toolbar-builders/typography.d.ts.map +1 -0
  73. package/dist/toolbar-builders/typography.js +118 -0
  74. package/dist/toolbar-builders/typography.js.map +1 -0
  75. package/package.json +50 -0
@@ -0,0 +1,1095 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, Output, TemplateRef, ViewChild, inject, } from '@angular/core';
11
+ import { NgFor, NgIf } from '@angular/common';
12
+ import { defaultKeymapBindings, defaultRenderer, defaultSchema, EditorState, Fragment, getLinkAt, historyPlugin, insertImage, insertPageBreak, insertTable, installClipboardHandlers, installInputPipeline, keymap, printDocument, setLink, SetPageMetaStep, TextSelection, unsetLink, } from '@doxi/core';
13
+ import { DoxivaEditorComponent, } from './doxiva-editor.component.js';
14
+ import { DoxivaToolbarComponent, defaultToolbarActions, TOOLBAR_SEPARATOR, } from './doxiva-toolbar.component.js';
15
+ import { DEFAULT_FEATURES, } from './doxiva.types.js';
16
+ const SIZE_PX = {
17
+ letter: { w: 816, h: 1056 },
18
+ a4: { w: 794, h: 1123 },
19
+ legal: { w: 816, h: 1344 },
20
+ a5: { w: 559, h: 794 },
21
+ };
22
+ const MARGIN_PX = { normal: 96, narrow: 48, wide: 144 };
23
+ // Inline SVG markup, drawn inside the toolbar's shared 24×24 viewBox.
24
+ const ICON_NEW_PAGE = '<rect x="4" y="3" width="13" height="18" rx="2" />' +
25
+ '<path d="M7 10h7M7 14h7M7 18h4" />' +
26
+ '<circle cx="19" cy="6" r="3" />' +
27
+ '<path d="M19 4.5v3M17.5 6h3" />';
28
+ const ICON_HEADER_FOOTER = '<rect x="4" y="3" width="16" height="18" rx="2" />' +
29
+ '<path d="M4 7h16M4 17h16" />' +
30
+ '<path d="M8 11h8M8 14h6" />';
31
+ const ICON_PAGE_SETTINGS = '<rect x="4" y="3" width="13" height="18" rx="2" />' +
32
+ '<path d="M7 8h7M7 12h7M7 16h4" />' +
33
+ '<circle cx="19" cy="19" r="2.2" />';
34
+ /**
35
+ * Filter the default toolbar actions by feature flags. Removes-only — never
36
+ * reorders. After filtering, consecutive `separator` items (and leading /
37
+ * trailing separators) are collapsed so a removed group doesn't leave a
38
+ * double divider. Ported verbatim from `@doxi/react`'s `DoxivaEditor`.
39
+ */
40
+ function filterByFeatures(actions, features) {
41
+ const kept = actions.filter((item) => {
42
+ const t = item.type;
43
+ if (t === 'separator')
44
+ return true;
45
+ if (t === 'select') {
46
+ const id = item.id;
47
+ if (id === 'font-size')
48
+ return features.fontSize;
49
+ if (id === 'font-family')
50
+ return features.fontFamily;
51
+ return true;
52
+ }
53
+ if (t === 'palette') {
54
+ const id = item.id;
55
+ if (id === 'color')
56
+ return features.color;
57
+ if (id === 'cell-bg')
58
+ return features.tables;
59
+ return true;
60
+ }
61
+ if (t === 'popover') {
62
+ const id = item.id;
63
+ if (id === 'link')
64
+ return features.links;
65
+ if (id === 'table')
66
+ return features.tables;
67
+ if (id === 'image')
68
+ return features.images;
69
+ return true;
70
+ }
71
+ const id = item.id;
72
+ if (id === 'undo' || id === 'redo')
73
+ return features.history;
74
+ if (id === 'body' || id === 'h1' || id === 'h2' || id === 'h3')
75
+ return features.headings;
76
+ if (id === 'bold' || id === 'italic' || id === 'underline')
77
+ return features.marks;
78
+ if (id === 'align-left' ||
79
+ id === 'align-center' ||
80
+ id === 'align-right' ||
81
+ id === 'align-justify')
82
+ return features.alignment;
83
+ if (id === 'line-1' || id === 'line-1-5' || id === 'line-2')
84
+ return features.lineHeight;
85
+ if (id === 'list-bullet' || id === 'list-numbered')
86
+ return features.lists;
87
+ if (id === 'blockquote' || id === 'hr')
88
+ return features.blocks;
89
+ if (id === 'table-header-row')
90
+ return features.tables;
91
+ if (id === 'print')
92
+ return features.print;
93
+ return true;
94
+ });
95
+ return dedupeSeparators(kept);
96
+ }
97
+ function dedupeSeparators(items) {
98
+ const out = [];
99
+ for (const item of items) {
100
+ const isSep = item.type === 'separator';
101
+ if (isSep) {
102
+ if (out.length === 0)
103
+ continue;
104
+ const prev = out[out.length - 1];
105
+ if (prev.type === 'separator')
106
+ continue;
107
+ }
108
+ out.push(item);
109
+ }
110
+ while (out.length > 0 && out[out.length - 1].type === 'separator') {
111
+ out.pop();
112
+ }
113
+ return out;
114
+ }
115
+ function buildDefaultDoc() {
116
+ return defaultSchema.node('doc', null, [defaultSchema.node('paragraph', null)]);
117
+ }
118
+ /**
119
+ * The bundled Doxiva editor — a single Angular standalone component that
120
+ * internally orchestrates the toolbar, popovers (link / table / image /
121
+ * header-footer / page-settings), keymap, print bridge, DOCX import/export,
122
+ * page chrome, and theme handling. Hosts pass `features` to disable parts
123
+ * of the UX and `theme` to switch palettes. The low-level
124
+ * `DoxivaEditorComponent` (`<dx-editor>`), `DoxivaToolbarComponent`
125
+ * (`<dx-toolbar>`), and `defaultToolbarActions` remain exported for power
126
+ * users.
127
+ */
128
+ let DxDoxivaEditorComponent = class DxDoxivaEditorComponent {
129
+ constructor() {
130
+ this.theme = 'light';
131
+ this.paginated = true;
132
+ this.defaultPageSize = 'letter';
133
+ this.editable = true;
134
+ this.change = new EventEmitter();
135
+ this.stateChange = new EventEmitter();
136
+ this.importWarnings = new EventEmitter();
137
+ /** Emitted once the underlying EditorView is mounted (and again on remount). */
138
+ this.ready = new EventEmitter();
139
+ this.cdr = inject(ChangeDetectorRef);
140
+ this.host = inject((ElementRef));
141
+ this.history = historyPlugin();
142
+ this.renderer = defaultRenderer;
143
+ this.effectiveFeatures = DEFAULT_FEATURES;
144
+ this.actions = [];
145
+ // Page-settings state.
146
+ this.pageSize = 'letter';
147
+ this.pageOrientation = 'portrait';
148
+ this.pageMargins = 'normal';
149
+ this.editorVisible = true;
150
+ // Header/footer chrome (pure CSS, not part of the doc).
151
+ this.chromeHeaderEnabled = true;
152
+ this.chromeFooterEnabled = true;
153
+ this.chromeHeaderHeightPx = 24;
154
+ this.chromeFooterHeightPx = 24;
155
+ // Header/footer popover form state.
156
+ this.headerText = '';
157
+ this.footerText = '';
158
+ this.headerPageNum = false;
159
+ this.footerPageNum = false;
160
+ // Image popover form state.
161
+ this.imageSrc = '';
162
+ this.imageAlt = '';
163
+ // Table-picker grid (8×8).
164
+ this.tablePickerRows = [0, 1, 2, 3, 4, 5, 6, 7];
165
+ this.tablePickerCols = [0, 1, 2, 3, 4, 5, 6, 7];
166
+ this.tableHover = null;
167
+ // EditorView captured in onReady. Used by Mod-P, the doxiva:print bridge,
168
+ // and the top-row buttons (which gate `[disabled]` on view === null).
169
+ this.view = null;
170
+ this.importWarningsList = [];
171
+ // Resolved theme — for `theme: 'auto'` mirrors prefers-color-scheme.
172
+ this.resolvedTheme = 'light';
173
+ this.mediaQuery = null;
174
+ this.mediaListener = null;
175
+ this.onPrintListener = null;
176
+ this.popoverTemplates = {};
177
+ }
178
+ ngOnInit() {
179
+ this.pageSize = this.defaultPageSize;
180
+ this.recomputeFeatures();
181
+ this.state = EditorState.create({
182
+ schema: defaultSchema,
183
+ doc: this.initialDoc ?? buildDefaultDoc(),
184
+ selection: new TextSelection(1, 1),
185
+ plugins: [this.history.plugin],
186
+ });
187
+ this.recomputeActions();
188
+ this.resolveTheme();
189
+ }
190
+ ngAfterViewInit() {
191
+ this.popoverTemplates = this.buildPopoverTemplates();
192
+ this.installMediaListener();
193
+ // Templates only become available in AfterViewInit; trigger CD so the
194
+ // dx-toolbar input binding flips on this pass.
195
+ this.cdr.detectChanges();
196
+ }
197
+ ngOnChanges(changes) {
198
+ if (changes['features'] && !changes['features'].firstChange) {
199
+ this.recomputeFeatures();
200
+ this.recomputeActions();
201
+ if (this.popoverTemplates && Object.keys(this.popoverTemplates).length > 0) {
202
+ this.popoverTemplates = this.buildPopoverTemplates();
203
+ }
204
+ this.cdr.markForCheck();
205
+ }
206
+ if (changes['theme'] && !changes['theme'].firstChange) {
207
+ this.uninstallMediaListener();
208
+ this.resolveTheme();
209
+ this.installMediaListener();
210
+ this.cdr.markForCheck();
211
+ }
212
+ if (changes['initialDoc'] && !changes['initialDoc'].firstChange) {
213
+ // React's <DoxivaEditor> only reads initialDoc once (useMemo). Mirror
214
+ // that — subsequent host changes are intentionally ignored. Hosts that
215
+ // need to swap a doc should remount or use the editor's dispatch path.
216
+ }
217
+ if (changes['paginated'] && !changes['paginated'].firstChange) {
218
+ // Re-render the editor with the new pagination config.
219
+ this.remountEditor();
220
+ }
221
+ }
222
+ ngOnDestroy() {
223
+ if (this.onPrintListener && typeof document !== 'undefined') {
224
+ document.removeEventListener('doxiva:print', this.onPrintListener);
225
+ this.onPrintListener = null;
226
+ }
227
+ this.uninstallMediaListener();
228
+ this.view = null;
229
+ }
230
+ get showTopRow() {
231
+ const f = this.effectiveFeatures;
232
+ return f.print || f.downloadPdf || f.exportDocx || f.importDocx;
233
+ }
234
+ get showWarningsPanel() {
235
+ return this.importWarningsList.length > 0 && this.onImportWarnings !== null;
236
+ }
237
+ get paginatedConfig() {
238
+ const base = SIZE_PX[this.pageSize];
239
+ const { w, h } = this.pageOrientation === 'landscape' ? { w: base.h, h: base.w } : base;
240
+ const m = MARGIN_PX[this.pageMargins];
241
+ return {
242
+ size: { widthPx: w, heightPx: h },
243
+ margins: { topPx: m, rightPx: m, bottomPx: m, leftPx: m },
244
+ };
245
+ }
246
+ // -- internal: feature/action recomputation ------------------------------
247
+ recomputeFeatures() {
248
+ this.effectiveFeatures = { ...DEFAULT_FEATURES, ...(this.features ?? {}) };
249
+ }
250
+ recomputeActions() {
251
+ const f = this.effectiveFeatures;
252
+ const filtered = filterByFeatures(defaultToolbarActions(defaultSchema, f.history ? this.history : undefined), f);
253
+ const tail = [];
254
+ if (f.newPage) {
255
+ tail.push({
256
+ id: 'new-page',
257
+ label: 'Add new page (Ctrl+Enter)',
258
+ icon: ICON_NEW_PAGE,
259
+ command: (state, dispatch) => {
260
+ const pbType = state.schema.nodes.page_break;
261
+ if (!pbType)
262
+ return false;
263
+ return insertPageBreak(pbType)(state, dispatch);
264
+ },
265
+ });
266
+ }
267
+ if (f.headerFooter) {
268
+ tail.push({
269
+ type: 'popover',
270
+ id: 'headerFooter',
271
+ label: 'Header and footer',
272
+ icon: ICON_HEADER_FOOTER,
273
+ });
274
+ }
275
+ if (f.pageSettings) {
276
+ tail.push({
277
+ type: 'popover',
278
+ id: 'pageSettings',
279
+ label: 'Page settings',
280
+ icon: ICON_PAGE_SETTINGS,
281
+ });
282
+ }
283
+ if (tail.length === 0) {
284
+ this.actions = filtered;
285
+ return;
286
+ }
287
+ this.actions = dedupeSeparators([...filtered, TOOLBAR_SEPARATOR, ...tail]);
288
+ }
289
+ buildPopoverTemplates() {
290
+ const f = this.effectiveFeatures;
291
+ const map = {};
292
+ if (f.links)
293
+ map.link = this.linkTpl;
294
+ if (f.tables)
295
+ map.table = this.tableTpl;
296
+ if (f.images)
297
+ map.image = this.imageTpl;
298
+ if (f.pageSettings)
299
+ map.pageSettings = this.pageSettingsTpl;
300
+ if (f.headerFooter)
301
+ map.headerFooter = this.headerFooterTpl;
302
+ return map;
303
+ }
304
+ // -- theme ---------------------------------------------------------------
305
+ resolveTheme() {
306
+ if (this.theme === 'auto') {
307
+ if (typeof window === 'undefined') {
308
+ this.resolvedTheme = 'light';
309
+ return;
310
+ }
311
+ this.resolvedTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
312
+ ? 'dark'
313
+ : 'light';
314
+ return;
315
+ }
316
+ this.resolvedTheme = this.theme;
317
+ }
318
+ installMediaListener() {
319
+ if (this.theme !== 'auto' || typeof window === 'undefined')
320
+ return;
321
+ const mq = window.matchMedia('(prefers-color-scheme: dark)');
322
+ const listener = (e) => {
323
+ this.resolvedTheme = e.matches ? 'dark' : 'light';
324
+ this.cdr.markForCheck();
325
+ };
326
+ mq.addEventListener('change', listener);
327
+ this.mediaQuery = mq;
328
+ this.mediaListener = listener;
329
+ }
330
+ uninstallMediaListener() {
331
+ if (this.mediaQuery && this.mediaListener) {
332
+ this.mediaQuery.removeEventListener('change', this.mediaListener);
333
+ }
334
+ this.mediaQuery = null;
335
+ this.mediaListener = null;
336
+ }
337
+ // -- dispatch + state ----------------------------------------------------
338
+ onDispatch(tr) {
339
+ const prev = this.state;
340
+ this.state = this.state.apply(tr);
341
+ if (prev.doc !== this.state.doc) {
342
+ this.change.emit(this.state.doc);
343
+ }
344
+ this.stateChange.emit(this.state);
345
+ this.cdr.markForCheck();
346
+ }
347
+ // -- onReady (wire keymap + clipboard + print bridge) --------------------
348
+ onReady(view) {
349
+ this.view = view;
350
+ this.cdr.detectChanges();
351
+ const f = this.effectiveFeatures;
352
+ const handler = keymap({
353
+ ...defaultKeymapBindings(defaultSchema),
354
+ ...(f.history
355
+ ? {
356
+ 'Mod-z': this.history.commands.undo,
357
+ 'Mod-Shift-z': this.history.commands.redo,
358
+ 'Mod-y': this.history.commands.redo,
359
+ }
360
+ : {}),
361
+ ...(f.links
362
+ ? {
363
+ 'Mod-k': () => {
364
+ if (typeof document === 'undefined')
365
+ return false;
366
+ const root = this.host.nativeElement;
367
+ const btn = root.querySelector('[data-testid="btn-link"]');
368
+ if (!btn)
369
+ return false;
370
+ btn.click();
371
+ return true;
372
+ },
373
+ }
374
+ : {}),
375
+ ...(f.newPage
376
+ ? {
377
+ 'Mod-Enter': (state, dispatch) => {
378
+ const pbType = state.schema.nodes.page_break;
379
+ if (!pbType)
380
+ return false;
381
+ return insertPageBreak(pbType)(state, dispatch);
382
+ },
383
+ }
384
+ : {}),
385
+ ...(f.print
386
+ ? {
387
+ 'Mod-p': () => {
388
+ if (!this.view)
389
+ return false;
390
+ void printDocument(this.view);
391
+ return true;
392
+ },
393
+ }
394
+ : {}),
395
+ });
396
+ installInputPipeline(view, {
397
+ keymap: (event, state, dispatch) => handler(event, state, dispatch),
398
+ });
399
+ installClipboardHandlers(view);
400
+ if (typeof document !== 'undefined') {
401
+ if (this.onPrintListener) {
402
+ document.removeEventListener('doxiva:print', this.onPrintListener);
403
+ }
404
+ const onPrint = () => {
405
+ if (this.view)
406
+ void printDocument(this.view);
407
+ };
408
+ this.onPrintListener = onPrint;
409
+ document.addEventListener('doxiva:print', onPrint);
410
+ }
411
+ this.ready.emit(view);
412
+ }
413
+ // -- top-row button handlers --------------------------------------------
414
+ onPrintClick() {
415
+ if (this.view)
416
+ void printDocument(this.view);
417
+ }
418
+ onDownloadPdfClick() {
419
+ if (!this.view)
420
+ return;
421
+ const prev = typeof document !== 'undefined' ? document.title : '';
422
+ if (typeof document !== 'undefined')
423
+ document.title = 'doxiva-document';
424
+ void printDocument(this.view).finally(() => {
425
+ if (typeof document !== 'undefined')
426
+ document.title = prev;
427
+ });
428
+ }
429
+ async onExportDocxClick() {
430
+ if (!this.view)
431
+ return;
432
+ const { exportDocx } = await import('@doxi/docx');
433
+ const bytes = exportDocx(this.view.state.doc);
434
+ const blob = new Blob([bytes], {
435
+ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
436
+ });
437
+ const url = URL.createObjectURL(blob);
438
+ const a = document.createElement('a');
439
+ a.href = url;
440
+ a.download = 'doxiva-document.docx';
441
+ document.body.appendChild(a);
442
+ a.click();
443
+ document.body.removeChild(a);
444
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
445
+ }
446
+ async onImportFile(event) {
447
+ const input = event.target;
448
+ const file = input.files?.[0];
449
+ input.value = '';
450
+ if (!file)
451
+ return;
452
+ const { importDocx } = await import('@doxi/docx');
453
+ const buffer = await file.arrayBuffer();
454
+ const bytes = new Uint8Array(buffer);
455
+ const { doc, warnings } = await importDocx(bytes);
456
+ this.importWarningsList = warnings;
457
+ this.importWarnings.emit(warnings);
458
+ if (this.onImportWarnings && this.onImportWarnings !== null) {
459
+ this.onImportWarnings(warnings);
460
+ }
461
+ this.state = EditorState.create({
462
+ schema: defaultSchema,
463
+ doc,
464
+ selection: new TextSelection(1, 1),
465
+ plugins: [this.history.plugin],
466
+ });
467
+ this.cdr.detectChanges();
468
+ }
469
+ // -- page-settings apply (remount) --------------------------------------
470
+ applyPageSettings(close) {
471
+ close();
472
+ this.remountEditor();
473
+ }
474
+ remountEditor() {
475
+ this.editorVisible = false;
476
+ this.view = null;
477
+ this.cdr.detectChanges();
478
+ setTimeout(() => {
479
+ this.editorVisible = true;
480
+ this.cdr.detectChanges();
481
+ }, 0);
482
+ }
483
+ // -- header/footer popover ----------------------------------------------
484
+ setChromeHeaderEnabled(v) {
485
+ this.chromeHeaderEnabled = v;
486
+ this.cdr.markForCheck();
487
+ }
488
+ setChromeFooterEnabled(v) {
489
+ this.chromeFooterEnabled = v;
490
+ this.cdr.markForCheck();
491
+ }
492
+ setChromeHeaderHeight(raw) {
493
+ this.chromeHeaderHeightPx = this.clampChromeHeight(raw);
494
+ this.cdr.markForCheck();
495
+ }
496
+ setChromeFooterHeight(raw) {
497
+ this.chromeFooterHeightPx = this.clampChromeHeight(raw);
498
+ this.cdr.markForCheck();
499
+ }
500
+ clampChromeHeight(raw) {
501
+ const n = typeof raw === 'number' ? raw : Number(raw);
502
+ if (!Number.isFinite(n))
503
+ return 0;
504
+ if (n < 0)
505
+ return 0;
506
+ if (n > 400)
507
+ return 400;
508
+ return Math.round(n);
509
+ }
510
+ fragmentFromText(text) {
511
+ const trimmed = text.trim();
512
+ if (!trimmed)
513
+ return Fragment.empty;
514
+ return Fragment.from([
515
+ defaultSchema.node('paragraph', null, [defaultSchema.text(text)]),
516
+ ]);
517
+ }
518
+ fragmentWithPageNumbers(text) {
519
+ const prefix = text.trim();
520
+ const nodes = prefix
521
+ ? [
522
+ defaultSchema.text(prefix + ' — '),
523
+ defaultSchema.text('Page '),
524
+ defaultSchema.node('page_var', { kind: 'pageNumber' }),
525
+ defaultSchema.text(' of '),
526
+ defaultSchema.node('page_var', { kind: 'totalPages' }),
527
+ ]
528
+ : [
529
+ defaultSchema.text('Page '),
530
+ defaultSchema.node('page_var', { kind: 'pageNumber' }),
531
+ defaultSchema.text(' of '),
532
+ defaultSchema.node('page_var', { kind: 'totalPages' }),
533
+ ];
534
+ return Fragment.from([defaultSchema.node('paragraph', null, nodes)]);
535
+ }
536
+ applyHeaderFooter(state, dispatch, close) {
537
+ const headerFrag = this.headerPageNum
538
+ ? this.fragmentWithPageNumbers(this.headerText)
539
+ : this.fragmentFromText(this.headerText);
540
+ const footerFrag = this.footerPageNum
541
+ ? this.fragmentWithPageNumbers(this.footerText)
542
+ : this.fragmentFromText(this.footerText);
543
+ const tr = state.tr
544
+ .step(new SetPageMetaStep('header', headerFrag))
545
+ .step(new SetPageMetaStep('footer', footerFrag));
546
+ dispatch(tr);
547
+ close();
548
+ }
549
+ clearHeaderFooter(state, dispatch, close) {
550
+ const tr = state.tr
551
+ .step(new SetPageMetaStep('header', Fragment.empty))
552
+ .step(new SetPageMetaStep('footer', Fragment.empty));
553
+ dispatch(tr);
554
+ this.headerText = '';
555
+ this.footerText = '';
556
+ this.headerPageNum = false;
557
+ this.footerPageNum = false;
558
+ close();
559
+ }
560
+ // -- image popover -------------------------------------------------------
561
+ applyImage(state, dispatch, close) {
562
+ const trimmed = this.imageSrc.trim();
563
+ if (trimmed === '') {
564
+ close();
565
+ return;
566
+ }
567
+ insertImage(defaultSchema, { src: trimmed, alt: this.imageAlt })(state, dispatch);
568
+ this.imageSrc = '';
569
+ this.imageAlt = '';
570
+ close();
571
+ }
572
+ onImageKeydown(event, state, dispatch, close) {
573
+ if (event.key === 'Enter') {
574
+ event.preventDefault();
575
+ this.applyImage(state, dispatch, close);
576
+ }
577
+ else if (event.key === 'Escape') {
578
+ event.preventDefault();
579
+ close();
580
+ }
581
+ }
582
+ // -- table picker --------------------------------------------------------
583
+ setTableHover(r, c) {
584
+ this.tableHover = { r, c };
585
+ this.cdr.markForCheck();
586
+ }
587
+ resetTableHover() {
588
+ this.tableHover = null;
589
+ this.cdr.markForCheck();
590
+ }
591
+ isTableCellOn(r, c) {
592
+ const h = this.tableHover;
593
+ return h !== null && r <= h.r && c <= h.c;
594
+ }
595
+ tablePickerLabel() {
596
+ const h = this.tableHover;
597
+ return h ? `${h.r + 1} × ${h.c + 1}` : 'Insert table';
598
+ }
599
+ pickTable(state, dispatch, close, r, c) {
600
+ insertTable(defaultSchema, r + 1, c + 1)(state, dispatch);
601
+ this.tableHover = null;
602
+ close();
603
+ }
604
+ // -- link popover --------------------------------------------------------
605
+ get linkType() {
606
+ return defaultSchema.marks.link;
607
+ }
608
+ hasExistingLink(state) {
609
+ return this.linkType ? getLinkAt(state, this.linkType) !== null : false;
610
+ }
611
+ linkInitialHref(state) {
612
+ if (!this.linkType)
613
+ return '';
614
+ const mark = getLinkAt(state, this.linkType);
615
+ return mark ? String(mark.attrs.href ?? '') : '';
616
+ }
617
+ applyLink(state, dispatch, close, rawHref) {
618
+ if (!this.linkType) {
619
+ close();
620
+ return;
621
+ }
622
+ const trimmed = rawHref.trim();
623
+ if (trimmed === '') {
624
+ unsetLink(this.linkType)(state, dispatch);
625
+ close();
626
+ return;
627
+ }
628
+ setLink(this.linkType, { href: trimmed })(state, dispatch);
629
+ close();
630
+ }
631
+ removeLink(state, dispatch, close) {
632
+ if (!this.linkType) {
633
+ close();
634
+ return;
635
+ }
636
+ unsetLink(this.linkType)(state, dispatch);
637
+ close();
638
+ }
639
+ onLinkKeydown(event, state, dispatch, close, rawHref) {
640
+ if (event.key === 'Enter') {
641
+ event.preventDefault();
642
+ this.applyLink(state, dispatch, close, rawHref);
643
+ }
644
+ else if (event.key === 'Escape') {
645
+ event.preventDefault();
646
+ close();
647
+ }
648
+ }
649
+ };
650
+ __decorate([
651
+ Input(),
652
+ __metadata("design:type", Function)
653
+ ], DxDoxivaEditorComponent.prototype, "initialDoc", void 0);
654
+ __decorate([
655
+ Input(),
656
+ __metadata("design:type", String)
657
+ ], DxDoxivaEditorComponent.prototype, "theme", void 0);
658
+ __decorate([
659
+ Input(),
660
+ __metadata("design:type", Object)
661
+ ], DxDoxivaEditorComponent.prototype, "features", void 0);
662
+ __decorate([
663
+ Input(),
664
+ __metadata("design:type", Boolean)
665
+ ], DxDoxivaEditorComponent.prototype, "paginated", void 0);
666
+ __decorate([
667
+ Input(),
668
+ __metadata("design:type", String)
669
+ ], DxDoxivaEditorComponent.prototype, "defaultPageSize", void 0);
670
+ __decorate([
671
+ Input(),
672
+ __metadata("design:type", Boolean)
673
+ ], DxDoxivaEditorComponent.prototype, "editable", void 0);
674
+ __decorate([
675
+ Input(),
676
+ __metadata("design:type", String)
677
+ ], DxDoxivaEditorComponent.prototype, "ariaLabel", void 0);
678
+ __decorate([
679
+ Input(),
680
+ __metadata("design:type", Object)
681
+ ], DxDoxivaEditorComponent.prototype, "onImportWarnings", void 0);
682
+ __decorate([
683
+ Output(),
684
+ __metadata("design:type", Object)
685
+ ], DxDoxivaEditorComponent.prototype, "change", void 0);
686
+ __decorate([
687
+ Output(),
688
+ __metadata("design:type", Object)
689
+ ], DxDoxivaEditorComponent.prototype, "stateChange", void 0);
690
+ __decorate([
691
+ Output(),
692
+ __metadata("design:type", Object)
693
+ ], DxDoxivaEditorComponent.prototype, "importWarnings", void 0);
694
+ __decorate([
695
+ Output(),
696
+ __metadata("design:type", Object)
697
+ ], DxDoxivaEditorComponent.prototype, "ready", void 0);
698
+ __decorate([
699
+ ViewChild('linkTpl', { static: true }),
700
+ __metadata("design:type", TemplateRef)
701
+ ], DxDoxivaEditorComponent.prototype, "linkTpl", void 0);
702
+ __decorate([
703
+ ViewChild('tableTpl', { static: true }),
704
+ __metadata("design:type", TemplateRef)
705
+ ], DxDoxivaEditorComponent.prototype, "tableTpl", void 0);
706
+ __decorate([
707
+ ViewChild('imageTpl', { static: true }),
708
+ __metadata("design:type", TemplateRef)
709
+ ], DxDoxivaEditorComponent.prototype, "imageTpl", void 0);
710
+ __decorate([
711
+ ViewChild('pageSettingsTpl', { static: true }),
712
+ __metadata("design:type", TemplateRef)
713
+ ], DxDoxivaEditorComponent.prototype, "pageSettingsTpl", void 0);
714
+ __decorate([
715
+ ViewChild('headerFooterTpl', { static: true }),
716
+ __metadata("design:type", TemplateRef)
717
+ ], DxDoxivaEditorComponent.prototype, "headerFooterTpl", void 0);
718
+ DxDoxivaEditorComponent = __decorate([
719
+ Component({
720
+ selector: 'dx-doxiva-editor',
721
+ standalone: true,
722
+ imports: [DoxivaEditorComponent, DoxivaToolbarComponent, NgFor, NgIf],
723
+ changeDetection: ChangeDetectionStrategy.OnPush,
724
+ template: `
725
+ <div
726
+ class="dx-doxiva-shell"
727
+ [class.dx-theme-dark]="resolvedTheme === 'dark'"
728
+ [class.dx-theme-light]="resolvedTheme === 'light'"
729
+ [class.no-header]="!chromeHeaderEnabled"
730
+ [class.no-footer]="!chromeFooterEnabled"
731
+ [style.--dx-header-height.px]="chromeHeaderHeightPx"
732
+ [style.--dx-footer-height.px]="chromeFooterHeightPx"
733
+ data-doxiva-version="0.11.0"
734
+ data-testid="doxiva-shell"
735
+ >
736
+ <div
737
+ *ngIf="showTopRow"
738
+ class="dx-doxiva-toprow"
739
+ data-testid="doxiva-toprow"
740
+ >
741
+ <button
742
+ *ngIf="effectiveFeatures.print"
743
+ type="button"
744
+ data-testid="btn-print-top"
745
+ [disabled]="view === null"
746
+ (click)="onPrintClick()"
747
+ >Print</button>
748
+ <button
749
+ *ngIf="effectiveFeatures.downloadPdf"
750
+ type="button"
751
+ data-testid="btn-download-pdf"
752
+ [disabled]="view === null"
753
+ (click)="onDownloadPdfClick()"
754
+ >Download PDF</button>
755
+ <button
756
+ *ngIf="effectiveFeatures.exportDocx"
757
+ type="button"
758
+ data-testid="btn-export-docx"
759
+ [disabled]="view === null"
760
+ (click)="onExportDocxClick()"
761
+ >Export DOCX</button>
762
+ <ng-container *ngIf="effectiveFeatures.importDocx">
763
+ <button
764
+ type="button"
765
+ data-testid="btn-import-docx"
766
+ (click)="fileInput.click()"
767
+ >Import DOCX</button>
768
+ <input
769
+ #fileInput
770
+ type="file"
771
+ accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
772
+ data-testid="import-file-input"
773
+ style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
774
+ (change)="onImportFile($event)"
775
+ />
776
+ </ng-container>
777
+ </div>
778
+
779
+ <dx-toolbar
780
+ [state]="state"
781
+ [actions]="actions"
782
+ [popoverTemplates]="popoverTemplates"
783
+ (dispatch)="onDispatch($event)"
784
+ />
785
+
786
+ <ng-template
787
+ #tableTpl
788
+ let-popState="state"
789
+ let-popDispatch="dispatch"
790
+ let-popClose="close"
791
+ >
792
+ <div
793
+ class="dx-toolbar-popover dx-table-picker"
794
+ role="dialog"
795
+ aria-label="Insert table"
796
+ (mousedown)="$event.stopPropagation()"
797
+ (mouseleave)="resetTableHover()"
798
+ >
799
+ <div class="dx-table-picker-grid">
800
+ <ng-container *ngFor="let r of tablePickerRows; let rIdx = index">
801
+ <button
802
+ *ngFor="let c of tablePickerCols; let cIdx = index"
803
+ type="button"
804
+ [class]="'dx-table-picker-cell' + (isTableCellOn(rIdx, cIdx) ? ' is-on' : '')"
805
+ [attr.data-testid]="'table-picker-cell-' + rIdx + '-' + cIdx"
806
+ [attr.aria-label]="(rIdx + 1) + ' × ' + (cIdx + 1)"
807
+ (mouseenter)="setTableHover(rIdx, cIdx)"
808
+ (mousedown)="$event.preventDefault()"
809
+ (click)="pickTable(popState, popDispatch, popClose, rIdx, cIdx)"
810
+ ></button>
811
+ </ng-container>
812
+ </div>
813
+ <div class="dx-table-picker-label">{{ tablePickerLabel() }}</div>
814
+ </div>
815
+ </ng-template>
816
+
817
+ <ng-template #imageTpl let-popState="state" let-popDispatch="dispatch" let-popClose="close">
818
+ <div
819
+ class="dx-toolbar-popover dx-toolbar-image-popover"
820
+ role="dialog"
821
+ aria-label="Insert image"
822
+ (mousedown)="$event.stopPropagation()"
823
+ >
824
+ <input
825
+ type="url"
826
+ data-testid="image-input-src"
827
+ placeholder="https://example.com/image.jpg"
828
+ [value]="imageSrc"
829
+ (input)="imageSrc = $any($event.target).value"
830
+ (keydown)="onImageKeydown($event, popState, popDispatch, popClose)"
831
+ />
832
+ <input
833
+ type="text"
834
+ data-testid="image-input-alt"
835
+ placeholder="Alt text (optional)"
836
+ [value]="imageAlt"
837
+ (input)="imageAlt = $any($event.target).value"
838
+ (keydown)="onImageKeydown($event, popState, popDispatch, popClose)"
839
+ />
840
+ <div class="dx-toolbar-image-actions">
841
+ <button
842
+ type="button"
843
+ data-testid="image-apply"
844
+ (mousedown)="$event.preventDefault()"
845
+ (click)="applyImage(popState, popDispatch, popClose)"
846
+ >Apply</button>
847
+ </div>
848
+ </div>
849
+ </ng-template>
850
+
851
+ <ng-template
852
+ #headerFooterTpl
853
+ let-popState="state"
854
+ let-popDispatch="dispatch"
855
+ let-popClose="close"
856
+ >
857
+ <div
858
+ class="dx-toolbar-popover dx-header-footer-popover"
859
+ role="dialog"
860
+ aria-label="Header and footer settings"
861
+ (mousedown)="$event.stopPropagation()"
862
+ >
863
+ <label>
864
+ Header:
865
+ <input
866
+ type="text"
867
+ data-testid="header-text"
868
+ placeholder="Header text"
869
+ [value]="headerText"
870
+ (input)="headerText = $any($event.target).value"
871
+ />
872
+ </label>
873
+ <label class="dx-header-footer-checkbox">
874
+ <input
875
+ type="checkbox"
876
+ data-testid="header-pagenum"
877
+ [checked]="headerPageNum"
878
+ (change)="headerPageNum = $any($event.target).checked"
879
+ />
880
+ Show "Page N of M" in header
881
+ </label>
882
+ <fieldset class="dx-header-footer-section">
883
+ <legend>Header</legend>
884
+ <label class="dx-header-footer-checkbox">
885
+ <input
886
+ type="checkbox"
887
+ data-testid="header-enabled"
888
+ [checked]="chromeHeaderEnabled"
889
+ (change)="setChromeHeaderEnabled($any($event.target).checked)"
890
+ />
891
+ Enabled
892
+ </label>
893
+ <label>
894
+ Height (px):
895
+ <input
896
+ type="number"
897
+ data-testid="header-height"
898
+ min="0"
899
+ max="400"
900
+ [value]="chromeHeaderHeightPx"
901
+ (input)="setChromeHeaderHeight($any($event.target).value)"
902
+ />
903
+ </label>
904
+ </fieldset>
905
+ <label>
906
+ Footer:
907
+ <input
908
+ type="text"
909
+ data-testid="footer-text"
910
+ placeholder="Footer text"
911
+ [value]="footerText"
912
+ (input)="footerText = $any($event.target).value"
913
+ />
914
+ </label>
915
+ <label class="dx-header-footer-checkbox">
916
+ <input
917
+ type="checkbox"
918
+ data-testid="footer-pagenum"
919
+ [checked]="footerPageNum"
920
+ (change)="footerPageNum = $any($event.target).checked"
921
+ />
922
+ Show "Page N of M" in footer
923
+ </label>
924
+ <fieldset class="dx-header-footer-section">
925
+ <legend>Footer</legend>
926
+ <label class="dx-header-footer-checkbox">
927
+ <input
928
+ type="checkbox"
929
+ data-testid="footer-enabled"
930
+ [checked]="chromeFooterEnabled"
931
+ (change)="setChromeFooterEnabled($any($event.target).checked)"
932
+ />
933
+ Enabled
934
+ </label>
935
+ <label>
936
+ Height (px):
937
+ <input
938
+ type="number"
939
+ data-testid="footer-height"
940
+ min="0"
941
+ max="400"
942
+ [value]="chromeFooterHeightPx"
943
+ (input)="setChromeFooterHeight($any($event.target).value)"
944
+ />
945
+ </label>
946
+ </fieldset>
947
+ <div class="dx-header-footer-actions">
948
+ <button
949
+ type="button"
950
+ data-testid="header-footer-apply"
951
+ (mousedown)="$event.preventDefault()"
952
+ (click)="applyHeaderFooter(popState, popDispatch, popClose)"
953
+ >Apply</button>
954
+ <button
955
+ type="button"
956
+ data-testid="header-footer-clear"
957
+ (mousedown)="$event.preventDefault()"
958
+ (click)="clearHeaderFooter(popState, popDispatch, popClose)"
959
+ >Clear</button>
960
+ <button
961
+ type="button"
962
+ data-testid="header-footer-cancel"
963
+ (mousedown)="$event.preventDefault()"
964
+ (click)="popClose()"
965
+ >Cancel</button>
966
+ </div>
967
+ </div>
968
+ </ng-template>
969
+
970
+ <ng-template
971
+ #pageSettingsTpl
972
+ let-popState="state"
973
+ let-popDispatch="dispatch"
974
+ let-popClose="close"
975
+ >
976
+ <div
977
+ class="dx-toolbar-popover dx-page-settings-popover"
978
+ role="dialog"
979
+ aria-label="Page settings"
980
+ (mousedown)="$event.stopPropagation()"
981
+ >
982
+ <label>
983
+ Size:
984
+ <select
985
+ data-testid="page-size"
986
+ [value]="pageSize"
987
+ (change)="pageSize = $any($event.target).value"
988
+ >
989
+ <option value="letter">Letter (8.5 × 11 in)</option>
990
+ <option value="a4">A4 (210 × 297 mm)</option>
991
+ <option value="legal">Legal (8.5 × 14 in)</option>
992
+ <option value="a5">A5 (148 × 210 mm)</option>
993
+ </select>
994
+ </label>
995
+ <label>
996
+ Orientation:
997
+ <select
998
+ data-testid="page-orientation"
999
+ [value]="pageOrientation"
1000
+ (change)="pageOrientation = $any($event.target).value"
1001
+ >
1002
+ <option value="portrait">Portrait</option>
1003
+ <option value="landscape">Landscape</option>
1004
+ </select>
1005
+ </label>
1006
+ <label>
1007
+ Margins:
1008
+ <select
1009
+ data-testid="page-margins"
1010
+ [value]="pageMargins"
1011
+ (change)="pageMargins = $any($event.target).value"
1012
+ >
1013
+ <option value="normal">Normal (96 px)</option>
1014
+ <option value="narrow">Narrow (48 px)</option>
1015
+ <option value="wide">Wide (144 px)</option>
1016
+ </select>
1017
+ </label>
1018
+ <div class="dx-page-settings-actions">
1019
+ <button
1020
+ type="button"
1021
+ data-testid="page-settings-apply"
1022
+ (mousedown)="$event.preventDefault()"
1023
+ (click)="applyPageSettings(popClose)"
1024
+ >Apply</button>
1025
+ <button
1026
+ type="button"
1027
+ data-testid="page-settings-cancel"
1028
+ (mousedown)="$event.preventDefault()"
1029
+ (click)="popClose()"
1030
+ >Cancel</button>
1031
+ </div>
1032
+ </div>
1033
+ </ng-template>
1034
+
1035
+ <ng-template #linkTpl let-popState="state" let-popDispatch="dispatch" let-popClose="close">
1036
+ <div
1037
+ class="dx-toolbar-popover dx-toolbar-link-popover"
1038
+ role="dialog"
1039
+ aria-label="Insert link"
1040
+ (mousedown)="$event.stopPropagation()"
1041
+ >
1042
+ <input
1043
+ #linkInput
1044
+ type="url"
1045
+ data-testid="link-input"
1046
+ placeholder="https://example.com"
1047
+ [value]="linkInitialHref(popState)"
1048
+ (keydown)="onLinkKeydown($event, popState, popDispatch, popClose, linkInput.value)"
1049
+ />
1050
+ <button
1051
+ type="button"
1052
+ data-testid="link-apply"
1053
+ (mousedown)="$event.preventDefault()"
1054
+ (click)="applyLink(popState, popDispatch, popClose, linkInput.value)"
1055
+ >Apply</button>
1056
+ <button
1057
+ *ngIf="hasExistingLink(popState)"
1058
+ type="button"
1059
+ data-testid="link-remove"
1060
+ (mousedown)="$event.preventDefault()"
1061
+ (click)="removeLink(popState, popDispatch, popClose)"
1062
+ >Remove</button>
1063
+ </div>
1064
+ </ng-template>
1065
+
1066
+ <section class="dx-doxiva-editor-wrap" data-testid="editor-wrap">
1067
+ <dx-editor
1068
+ *ngIf="editorVisible"
1069
+ [state]="state"
1070
+ [renderer]="renderer"
1071
+ [paginated]="paginated ? paginatedConfig : undefined"
1072
+ [editable]="editable"
1073
+ [ariaLabel]="ariaLabel ?? 'Document editor'"
1074
+ (dispatch)="onDispatch($event)"
1075
+ (ready)="onReady($event)"
1076
+ />
1077
+ </section>
1078
+
1079
+ <div
1080
+ *ngIf="showWarningsPanel"
1081
+ class="dx-doxiva-import-warnings"
1082
+ data-testid="import-warnings"
1083
+ >
1084
+ <strong>{{ importWarningsList.length }} warning{{ importWarningsList.length === 1 ? '' : 's' }}:</strong>
1085
+ <ul>
1086
+ <li *ngFor="let w of importWarningsList.slice(0, 5)">{{ w.code }}: {{ w.message }}</li>
1087
+ <li *ngIf="importWarningsList.length > 5">… and {{ importWarningsList.length - 5 }} more</li>
1088
+ </ul>
1089
+ </div>
1090
+ </div>
1091
+ `,
1092
+ })
1093
+ ], DxDoxivaEditorComponent);
1094
+ export { DxDoxivaEditorComponent };
1095
+ //# sourceMappingURL=doxiva-doxiva-editor.component.js.map