@kubex/zinc 1.0.4 → 1.0.5

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 (153) hide show
  1. package/dist/custom-elements.json +1034 -111
  2. package/dist/vscode.html-custom-data.json +85 -15
  3. package/dist/web-types.json +215 -24
  4. package/dist/zn.d.ts +1234 -888
  5. package/dist/zn.min.css +1 -1
  6. package/dist/zn.min.js +1979 -940
  7. package/docs/_includes/default.njk +1 -0
  8. package/docs/assets/scripts/docs.js +4 -5
  9. package/docs/assets/scripts/search.js +0 -9
  10. package/docs/assets/styles/docs.css +3 -3
  11. package/docs/data/data-table.json +79 -0
  12. package/docs/data/products-table.json +36 -0
  13. package/docs/eleventy.config.cjs +3 -0
  14. package/docs/pages/components/data-select.md +10 -0
  15. package/docs/pages/components/data-table.md +84 -45
  16. package/docs/pages/components/dialog.md +1 -1
  17. package/docs/pages/components/editor.md +1 -3
  18. package/docs/pages/components/filter-wrapper.md +20 -0
  19. package/docs/pages/components/icon.md +2 -0
  20. package/docs/pages/components/item.md +2 -2
  21. package/docs/pages/components/menu-item.md +12 -3
  22. package/docs/pages/components/panel.md +17 -0
  23. package/docs/pages/components/progress-tile.md +17 -1
  24. package/docs/pages/components/settings-container.md +137 -0
  25. package/docs/pages/components/textarea.md +1 -1
  26. package/package.json +3 -1
  27. package/scss/_root.scss +26 -0
  28. package/scss/boot.scss +27 -1
  29. package/scss/mixins/index.scss +30 -1
  30. package/scss/shared/_base.scss +1 -1
  31. package/scss/shared/_button.scss +1 -1
  32. package/scss/shared/layout.scss +7 -0
  33. package/scss/themes/_dark.scss +5 -5
  34. package/scss/themes/_light.scss +6 -5
  35. package/scss/variables/index.scss +9 -0
  36. package/src/components/alert/alert.component.ts +1 -1
  37. package/src/components/alert/alert.scss +18 -4
  38. package/src/components/button/button.component.ts +6 -2
  39. package/src/components/button/button.scss +16 -1
  40. package/src/components/button-menu/button-menu.component.ts +23 -15
  41. package/src/components/button-menu/button-menu.scss +4 -4
  42. package/src/components/checkbox/checkbox.component.ts +5 -1
  43. package/src/components/checkbox/checkbox.scss +1 -1
  44. package/src/components/chip/chip.component.ts +7 -3
  45. package/src/components/chip/chip.scss +9 -1
  46. package/src/components/collapsible/collapsible.component.ts +48 -2
  47. package/src/components/confirm/confirm.component.ts +1 -2
  48. package/src/components/content-block/content-block.component.ts +25 -1
  49. package/src/components/content-block/content-block.scss +8 -0
  50. package/src/components/data-select/data-select.component.ts +1 -1
  51. package/src/components/data-table/data-table.component.ts +361 -422
  52. package/src/components/data-table/data-table.scss +6 -1
  53. package/src/components/datepicker/datepicker.scss +1 -1
  54. package/src/components/dialog/dialog.component.ts +52 -33
  55. package/src/components/dialog/dialog.scss +15 -1
  56. package/src/components/dropdown/dropdown.component.ts +1 -1
  57. package/src/components/editor/editor.component.ts +196 -158
  58. package/src/components/editor/editor.scss +25 -5
  59. package/src/components/editor/modules/ai/index.ts +281 -0
  60. package/src/components/editor/modules/ai/panel/ai-panel.component.ts +102 -0
  61. package/src/components/editor/modules/ai/panel/ai-panel.scss +83 -0
  62. package/src/components/editor/modules/ai/tooltip/ai-tooltip.component.ts +30 -0
  63. package/src/components/editor/modules/ai/tooltip/ai-tooltip.scss +28 -0
  64. package/src/components/editor/modules/{attachment-module.ts → attachment/attachment.ts} +7 -7
  65. package/src/components/editor/modules/context-menu/context-menu-component.ts +115 -0
  66. package/src/components/editor/modules/context-menu/context-menu.scss +49 -0
  67. package/src/components/editor/modules/context-menu/context-menu.ts +296 -0
  68. package/src/components/editor/modules/context-menu/quick-action/index.ts +12 -0
  69. package/src/components/editor/modules/context-menu/quick-action/quick-action.component.ts +18 -0
  70. package/src/components/editor/modules/date-picker/date-picker.ts +77 -0
  71. package/src/components/editor/modules/dialog/dialog.component.ts +115 -0
  72. package/src/components/editor/modules/dialog/dialog.scss +43 -0
  73. package/src/components/editor/modules/dialog/dialog.ts +46 -0
  74. package/src/components/editor/modules/{drag-drop-module.ts → drag-drop/drag-drop.ts} +6 -6
  75. package/src/components/editor/modules/emoji/emoji.ts +106 -0
  76. package/src/components/editor/modules/emoji/headless/headless-emoji.component.ts +122 -0
  77. package/src/components/editor/modules/emoji/headless/headless-emoji.scss +52 -0
  78. package/src/components/editor/modules/emoji/headless/headless-emoji.ts +223 -0
  79. package/src/components/editor/modules/{image-resize-module/image-resize-module.ts → image-resize/image-resize.ts} +5 -5
  80. package/src/components/editor/modules/{time-tracking-module.ts → time-tracking/time-tracking.ts} +5 -5
  81. package/src/components/editor/modules/toolbar/tool/index.ts +12 -0
  82. package/src/components/editor/modules/toolbar/tool/tool.component.ts +30 -0
  83. package/src/components/editor/modules/toolbar/tool/tool.scss +5 -0
  84. package/src/components/editor/modules/toolbar/toolbar.component.ts +738 -0
  85. package/src/components/editor/modules/toolbar/toolbar.scss +98 -0
  86. package/src/components/editor/modules/toolbar/toolbar.ts +328 -0
  87. package/src/components/expanding-action/expanding-action.component.ts +2 -2
  88. package/src/components/expanding-action/expanding-action.scss +2 -2
  89. package/src/components/filter-wrapper/filter-wrapper.component.ts +91 -0
  90. package/src/components/filter-wrapper/filter-wrapper.scss +11 -0
  91. package/src/components/filter-wrapper/filter-wrapper.test.ts +10 -0
  92. package/src/components/filter-wrapper/index.ts +12 -0
  93. package/src/components/header/header.component.ts +1 -1
  94. package/src/components/header/header.scss +8 -1
  95. package/src/components/hover-container/hover-container.scss +39 -36
  96. package/src/components/icon/icon.component.ts +11 -5
  97. package/src/components/icon/icon.scss +15 -2
  98. package/src/components/inline-edit/inline-edit.component.ts +28 -3
  99. package/src/components/inline-edit/inline-edit.scss +4 -1
  100. package/src/components/input/input.scss +1 -1
  101. package/src/components/item/item.component.ts +30 -19
  102. package/src/components/item/item.scss +56 -14
  103. package/src/components/linked-select/linked-select.component.ts +5 -1
  104. package/src/components/menu/menu.component.ts +1 -0
  105. package/src/components/menu/menu.scss +1 -1
  106. package/src/components/menu-item/menu-item.component.ts +22 -4
  107. package/src/components/menu-item/menu-item.scss +54 -7
  108. package/src/components/navbar/navbar.component.ts +7 -1
  109. package/src/components/navbar/navbar.scss +26 -16
  110. package/src/components/option/option.component.ts +4 -1
  111. package/src/components/option/option.scss +9 -0
  112. package/src/components/page-nav/page-nav.component.ts +2 -2
  113. package/src/components/page-nav/page-nav.scss +4 -4
  114. package/src/components/panel/panel.component.ts +20 -9
  115. package/src/components/panel/panel.scss +5 -1
  116. package/src/components/popup/popup.component.ts +16 -2
  117. package/src/components/progress-tile/progress-tile.component.ts +10 -4
  118. package/src/components/progress-tile/progress-tile.scss +7 -4
  119. package/src/components/query-builder/query-builder.component.ts +1 -1
  120. package/src/components/radio/radio.scss +1 -1
  121. package/src/components/scroll-container/scroll-container.component.ts +56 -9
  122. package/src/components/scroll-container/scroll-container.scss +37 -2
  123. package/src/components/select/select.component.ts +3 -0
  124. package/src/components/select/select.scss +1 -1
  125. package/src/components/settings-container/index.ts +12 -0
  126. package/src/components/settings-container/settings-container.component.ts +226 -0
  127. package/src/components/settings-container/settings-container.scss +92 -0
  128. package/src/components/settings-container/settings-container.test.ts +10 -0
  129. package/src/components/skeleton/skeleton.scss +5 -4
  130. package/src/components/sp/sp.component.ts +2 -0
  131. package/src/components/sp/sp.scss +2 -0
  132. package/src/components/split-button/split-button.component.ts +24 -18
  133. package/src/components/split-pane/split-pane.component.ts +5 -5
  134. package/src/components/stat/stat.component.ts +5 -1
  135. package/src/components/stat/stat.scss +24 -1
  136. package/src/components/style/style.component.ts +18 -0
  137. package/src/components/tabs/tabs.component.ts +12 -1
  138. package/src/components/textarea/textarea.component.ts +3 -1
  139. package/src/components/textarea/textarea.scss +21 -2
  140. package/src/components/vertical-stepper/vertical-stepper.scss +1 -1
  141. package/src/internal/form.ts +3 -1
  142. package/src/internal/slot.ts +29 -3
  143. package/src/utilities/on.ts +7 -8
  144. package/src/utilities/query.ts +5 -1
  145. package/src/wc.scss +7 -0
  146. package/src/zinc.ts +5 -0
  147. package/src/components/editor/modules/dialog-module/dialog-module.component.ts +0 -289
  148. package/src/components/editor/modules/dialog-module/dialog-module.scss +0 -115
  149. package/src/components/editor/modules/dialog-module/dialog-module.ts +0 -99
  150. package/src/components/editor/modules/events/zn-show-canned-response-dialog.ts +0 -15
  151. package/src/components/editor/modules/menu-module/menu-module.component.ts +0 -149
  152. package/src/components/editor/modules/menu-module/menu-module.scss +0 -17
  153. package/src/components/editor/modules/menu-module/menu-module.ts +0 -203
@@ -0,0 +1,49 @@
1
+ :host {
2
+ position: absolute;
3
+ z-index: var(--zn-z-index-editor-action);
4
+ display: none;
5
+ background: var(--zn-panel-background-color);
6
+ border: 1px solid var(--zn-border-color);
7
+ border-radius: 8px;
8
+ box-shadow: var(--zn-shadow-medium);
9
+ padding: 6px;
10
+ max-height: 220px;
11
+ overflow-y: auto;
12
+ min-width: 360px;
13
+ }
14
+
15
+ :host([open]) {
16
+ display: block;
17
+ }
18
+
19
+ .header {
20
+ padding: 4px 6px;
21
+ font-size: var(--zn-font-size-x-small);
22
+ opacity: 0.6;
23
+ }
24
+
25
+ .item {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 8px;
29
+ padding: 8px;
30
+ width: 100%;
31
+ border: none;
32
+ background: transparent;
33
+ cursor: pointer;
34
+ font: inherit;
35
+
36
+ &[aria-selected="true"],
37
+ &:hover {
38
+ background: var(--zn-input-background-color-hover);
39
+ }
40
+ }
41
+
42
+ .label {
43
+ opacity: 0.7;
44
+ }
45
+
46
+ .empty {
47
+ padding: 8px;
48
+ opacity: 0.7;
49
+ }
@@ -0,0 +1,296 @@
1
+ import './context-menu-component';
2
+ import {html} from "lit";
3
+ import {litToHTML} from "../../../../utilities/lit-to-html";
4
+ import {type ResultItem} from "./context-menu-component";
5
+ import Quill, {Delta} from "quill";
6
+ import ZnEditorQuickAction from "./quick-action";
7
+ import type ContextMenuComponent from "./context-menu-component";
8
+ import type Toolbar from "../toolbar/toolbar";
9
+
10
+ class ContextMenu {
11
+ private _quill: Quill;
12
+ private readonly _toolbarModule: Toolbar;
13
+ private _component: ContextMenuComponent;
14
+ private _startIndex = -1;
15
+ private _keydownHandler = (e: KeyboardEvent) => this.onKeydown(e);
16
+ private _docClickHandler = (e: MouseEvent) => this.onDocumentClick(e);
17
+
18
+ constructor(quill: Quill) {
19
+ this._quill = quill;
20
+ this._toolbarModule = quill.getModule('toolbar') as Toolbar;
21
+
22
+ this.initComponent();
23
+ this.attachEvents();
24
+ }
25
+
26
+ private initComponent() {
27
+ this._component = this.createComponent()!;
28
+ this._quill.container.ownerDocument.body.appendChild(this._component);
29
+ }
30
+
31
+ private attachEvents() {
32
+ this._quill.on(Quill.events.TEXT_CHANGE, () => this.updateFromEditor());
33
+ this._quill.on(Quill.events.EDITOR_CHANGE, () => this.updateFromEditor());
34
+ this._quill.root.addEventListener('keydown', this._keydownHandler);
35
+ this._component.addEventListener('zn-format-select', (e: Event) => this.onToolbarSelect(e as CustomEvent<ResultItem>));
36
+ this._quill.on('editor-change', () => this.positionComponent());
37
+ this._quill.focus();
38
+ }
39
+
40
+ private createComponent() {
41
+ const tpl = html`
42
+ <zn-context-menu></zn-context-menu>`;
43
+ return litToHTML<ContextMenuComponent>(tpl);
44
+ }
45
+
46
+ private onDocumentClick(e: MouseEvent) {
47
+ const target = e.composedPath ? e.composedPath()[0] as Node : (e.target as Node);
48
+ if (!target) return;
49
+
50
+ if (!this._component.contains(target) && !this._quill.root.contains(target)) {
51
+ this.hide();
52
+ }
53
+ }
54
+
55
+ private updateFromEditor() {
56
+ const info = this.getToolbarQuery();
57
+ if (!info) {
58
+ this.hide();
59
+ return;
60
+ }
61
+
62
+ const {start, formatQuery} = info;
63
+ this._startIndex = start;
64
+
65
+ try {
66
+ const q = formatQuery.toLowerCase();
67
+ this._component.results = this._getOptions().filter(it => !q || it.label.toLowerCase().includes(q) || it?.format?.toLowerCase().includes(q));
68
+ } catch {
69
+ this._component.results = [];
70
+ }
71
+
72
+ this._component.query = formatQuery;
73
+ this.show();
74
+ this.positionComponent();
75
+ }
76
+
77
+ private positionComponent() {
78
+ if (!this._component || !this._component.open) return;
79
+
80
+ const range = this._quill.getSelection();
81
+ if (!range) return;
82
+
83
+ const bounds = this._quill.getBounds(range.index);
84
+ if (!bounds) return;
85
+
86
+ const editorBounds = this._quill.container.getBoundingClientRect();
87
+ const left = Math.max(0, editorBounds.left + bounds.left);
88
+ const top = editorBounds.top + bounds.bottom + 4;
89
+ this._component.setPosition(left, top);
90
+ }
91
+
92
+ private getToolbarQuery(): { start: number; formatQuery: string } | null {
93
+ try {
94
+ const range = this._quill.getSelection();
95
+ if (!range) return null;
96
+
97
+ const cursor = range.index;
98
+ const characterLimit = 50;
99
+ const textBefore = this._quill.getText(Math.max(0, cursor - characterLimit), Math.min(characterLimit, cursor));
100
+ const offset = cursor - Math.max(0, cursor - characterLimit);
101
+ const uptoCursor = textBefore.slice(0, offset);
102
+ const cIndex = uptoCursor.lastIndexOf('/');
103
+ if (cIndex === -1) return null;
104
+
105
+ const prev = cIndex > 0 ? uptoCursor[cIndex - 1] : ' ';
106
+ if (prev && /[^\s\n]/.test(prev)) return null; // must start at word boundary
107
+
108
+ const formatQuery = uptoCursor.substring(cIndex + 1);
109
+ if (/[\s\n]/.test(formatQuery)) return null; // stop at whitespace
110
+
111
+ return {start: cursor - formatQuery.length - 1, formatQuery};
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
117
+ private onKeydown(e: KeyboardEvent) {
118
+ if (!this._component?.open) return;
119
+
120
+ if (e.key === 'Escape') {
121
+ e.preventDefault();
122
+ e.stopPropagation();
123
+ this.hide();
124
+ return;
125
+ }
126
+
127
+ if (e.key === 'ArrowDown') {
128
+ e.preventDefault();
129
+ e.stopPropagation();
130
+ const next = (this._component.getActiveIndex?.() ?? -1) + 1;
131
+ this._component.setActiveIndex?.(next);
132
+ return;
133
+ }
134
+
135
+ if (e.key === 'ArrowUp') {
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ const prev = (this._component.getActiveIndex?.() ?? 0) - 1;
139
+ this._component.setActiveIndex?.(prev);
140
+ return;
141
+ }
142
+
143
+ if (e.key === 'Enter') {
144
+ const idx = this._component.getActiveIndex?.();
145
+ const results = (this._component.results as ResultItem[]) || [];
146
+ const item = (typeof idx === 'number' && idx >= 0 && idx < results.length) ? results[idx] : undefined;
147
+ if (item?.format === 'toolbar' && item?.key) {
148
+ e.preventDefault();
149
+ e.stopPropagation();
150
+ this._clickToolbarItem(item.key);
151
+ return;
152
+ }
153
+ if (item?.format) {
154
+ e.preventDefault();
155
+ e.stopPropagation();
156
+ this._applySelectedFormat(item.format, item.value);
157
+ }
158
+ }
159
+ }
160
+
161
+ private onToolbarSelect(e: CustomEvent<ResultItem>) {
162
+ const key: string | undefined = e.detail?.key as (string | undefined);
163
+ if (key) {
164
+ this._clickToolbarItem(key);
165
+ return;
166
+ }
167
+
168
+ const format: string = e.detail?.format || '';
169
+ const value: string | boolean | undefined = e.detail?.value as (string | boolean | undefined);
170
+ if (!format) return;
171
+
172
+ this._applySelectedFormat(format, value);
173
+ }
174
+
175
+ private _clickToolbarItem(key: string) {
176
+ this.deleteLastIndex();
177
+ this._toolbarModule.trigger?.(key);
178
+ this.hide();
179
+ }
180
+
181
+ private _applySelectedFormat(key: string, value?: string | boolean) {
182
+ this.deleteLastIndex();
183
+
184
+ if (key === 'insert' && typeof value === 'string') {
185
+ const range = this._quill.getSelection();
186
+ if (range) {
187
+ let insertIndex = range.index - 1;
188
+ this._quill.deleteText(insertIndex, 1, Quill.sources.USER);
189
+
190
+ const prevChar = this._quill.getText(insertIndex - 1, 1);
191
+ if (prevChar !== ' ' && insertIndex > 0) {
192
+ this._quill.insertText(insertIndex, ' ', Quill.sources.USER);
193
+ insertIndex += 1;
194
+ }
195
+
196
+ const contentDelta = this._quill.clipboard.convert({html: value});
197
+ this._quill.updateContents(
198
+ new Delta().retain(insertIndex).concat(contentDelta),
199
+ Quill.sources.USER
200
+ );
201
+
202
+ setTimeout(() => this._quill.setSelection(insertIndex + contentDelta.length(), 0, Quill.sources.SILENT), 0);
203
+ }
204
+ this._quill.focus();
205
+ this.hide();
206
+ return;
207
+ }
208
+
209
+ this._toolbarModule.callFormat(key, value);
210
+ this._quill.focus();
211
+ this.hide();
212
+ }
213
+
214
+ private deleteLastIndex() {
215
+ const sel = this._quill.getSelection();
216
+ if (sel && this._startIndex >= 0) {
217
+ const insertIndex = this._startIndex;
218
+ const length = sel.index - insertIndex;
219
+ if (length >= 0) {
220
+ this._quill.deleteText(insertIndex, length, Quill.sources.USER);
221
+ this._quill.setSelection(insertIndex, 0, Quill.sources.SILENT);
222
+ }
223
+ }
224
+ }
225
+
226
+ private _getOptions(): ResultItem[] {
227
+ const options: ResultItem[] = [];
228
+
229
+ // 1) Quick Actions
230
+ const root = this._quill.container.getRootNode() as ShadowRoot;
231
+ if (root?.host) {
232
+ const slot = root.querySelector('slot[name="context-items"]') as HTMLSlotElement | null;
233
+ const assigned = slot ? slot.assignedElements({flatten: true}) : [];
234
+ assigned.forEach((quickAction: Element) => {
235
+ if (!(quickAction instanceof ZnEditorQuickAction)) return;
236
+
237
+ const {label, content, uri, icon, key} = quickAction;
238
+
239
+ if (label && icon) {
240
+ if (key) {
241
+ options.push({icon, label, format: 'toolbar', key: key});
242
+ } else if (uri) {
243
+ options.push({icon, label, format: 'dialog', value: uri});
244
+ } else if (content) {
245
+ options.push({icon, label, format: 'insert', value: content});
246
+ }
247
+ }
248
+ });
249
+ }
250
+
251
+ // 2) Built-in Actions
252
+ options.push(
253
+ {icon: 'format_bold', label: 'Bold', format: 'bold'},
254
+ {icon: 'format_italic', label: 'Italic', format: 'italic'},
255
+ {icon: 'format_underlined', label: 'Underline', format: 'underline'},
256
+ {icon: 'strikethrough_s', label: 'Strikethrough', format: 'strike'},
257
+ {icon: 'format_quote', label: 'Blockquote', format: 'blockquote'},
258
+ {icon: 'code', label: 'Inline Code', format: 'code'},
259
+ {icon: 'code_blocks', label: 'Code Block', format: 'code-block'},
260
+ {icon: 'format_h1', label: 'Heading 1', format: 'header', value: '1'},
261
+ {icon: 'format_h2', label: 'Heading 2', format: 'header', value: '2'},
262
+ {icon: 'match_case', label: 'Normal Text', format: 'header', value: ''},
263
+ {icon: 'format_list_bulleted', label: 'Bulleted List', format: 'list', value: 'bullet'},
264
+ {icon: 'format_list_numbered', label: 'Numbered List', format: 'list', value: 'ordered'},
265
+ {icon: 'checklist', label: 'Checklist', format: 'list', value: 'checked'},
266
+ {icon: 'link', label: 'Link', format: 'link', value: true},
267
+ {icon: 'horizontal_rule', label: 'Divider', format: 'divider'},
268
+ {icon: 'attachment', label: 'Attachment', format: 'attachment'},
269
+ {icon: 'image', label: 'Image', format: 'image'},
270
+ {icon: 'video_camera_back', label: 'Video', format: 'video'},
271
+ {icon: 'calendar_today', label: 'Date', format: 'date'},
272
+ {icon: 'format_clear', label: 'Clear Formatting', format: 'clean'}
273
+ );
274
+
275
+ return options;
276
+ }
277
+
278
+ private show() {
279
+ if (!this._component.open) {
280
+ this._component.show();
281
+ this._quill.container.ownerDocument.addEventListener('click', this._docClickHandler);
282
+ }
283
+ }
284
+
285
+ private hide() {
286
+ this._component.hide();
287
+ this._startIndex = -1;
288
+ this._quill.container.ownerDocument.removeEventListener('click', this._docClickHandler);
289
+ }
290
+
291
+ public isOpen() {
292
+ return this._component?.open;
293
+ }
294
+ }
295
+
296
+ export default ContextMenu;
@@ -0,0 +1,12 @@
1
+ import ZnEditorQuickAction from './quick-action.component';
2
+
3
+ export * from './quick-action.component';
4
+ export default ZnEditorQuickAction;
5
+
6
+ ZnEditorQuickAction.define('zn-editor-quick-action');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-editor-quick-action': ZnEditorQuickAction;
11
+ }
12
+ }
@@ -0,0 +1,18 @@
1
+ import {html} from "lit";
2
+ import {property} from "lit/decorators.js";
3
+ import ZincElement from "../../../../../internal/zinc-element";
4
+
5
+ export default class ZnEditorQuickAction extends ZincElement {
6
+ @property() uri: string;
7
+ @property() label: string;
8
+ @property() content: string;
9
+ @property() key: string;
10
+ @property() icon: string;
11
+
12
+ render() {
13
+ return html`
14
+ <div style="display: none;"></div>`
15
+ }
16
+ }
17
+
18
+ ZnEditorQuickAction.define('zn-editor-quick-action');
@@ -0,0 +1,77 @@
1
+ import AirDatepicker from "air-datepicker";
2
+ import Quill from "quill";
3
+ import type ToolbarComponent from "../toolbar/toolbar.component";
4
+
5
+ class DatePicker {
6
+ private readonly _quill: Quill;
7
+
8
+ constructor(quill: Quill) {
9
+ this._quill = quill;
10
+
11
+ this._initPicker();
12
+ }
13
+
14
+ private _initPicker() {
15
+ this.getToolbarDateContainer().then((container) => {
16
+ if (!container) return;
17
+
18
+ container.innerHTML = '';
19
+
20
+ // eslint-disable-next-line no-new
21
+ new AirDatepicker(container, {
22
+ inline: true,
23
+ locale: {
24
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
25
+ daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
26
+ daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
27
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
28
+ monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
29
+ today: 'Today',
30
+ clear: 'Clear',
31
+ dateFormat: 'MM/dd/yyyy',
32
+ timeFormat: 'hh:ii aa',
33
+ firstDay: 0
34
+ },
35
+ onSelect: ({formattedDate}) => this._onDateSelect(formattedDate)
36
+ });
37
+ });
38
+ }
39
+
40
+ private async getToolbarDateContainer(): Promise<HTMLElement | null> {
41
+ try {
42
+ const root = this._quill.container?.getRootNode?.() as ShadowRoot | null;
43
+ if (!root) return null;
44
+
45
+ const toolbar = root.getElementById?.('toolbar') as ToolbarComponent | null;
46
+ await toolbar?.updateComplete;
47
+
48
+ const shadow = toolbar?.shadowRoot as ShadowRoot | undefined;
49
+ const container = shadow?.querySelector?.('.date-picker') as HTMLElement | null;
50
+ return container ?? null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ private _onDateSelect(formattedDate: string | string[]) {
57
+ try {
58
+ if (!formattedDate || !this._quill) return;
59
+
60
+ const range = this._quill.getSelection(true);
61
+ if (range) {
62
+ const text = Array.isArray(formattedDate) ? formattedDate.join(', ') : formattedDate;
63
+ this._quill.insertText(range.index, text, Quill.sources.USER);
64
+ this._quill.setSelection(range.index + text.length, 0, Quill.sources.USER);
65
+ } else {
66
+ const index = Math.max(0, this._quill.getLength() - 1);
67
+ const text = Array.isArray(formattedDate) ? formattedDate.join(', ') : formattedDate;
68
+ this._quill.insertText(index, text, Quill.sources.USER);
69
+ this._quill.setSelection(index + text.length, 0, Quill.sources.USER);
70
+ }
71
+ } catch (e) {
72
+ // no-op
73
+ }
74
+ }
75
+ }
76
+
77
+ export default DatePicker;
@@ -0,0 +1,115 @@
1
+ import {classMap} from "lit/directives/class-map.js";
2
+ import {type CSSResultGroup, html, nothing, type PropertyValues, unsafeCSS} from "lit";
3
+ import {property, query, state} from "lit/decorators.js";
4
+ import {watch} from "../../../../internal/watch";
5
+ import ZincElement from "../../../../internal/zinc-element";
6
+
7
+ import styles from './dialog.scss';
8
+
9
+ export default class DialogComponent extends ZincElement {
10
+ static styles: CSSResultGroup = unsafeCSS(styles);
11
+
12
+ @state() private hasFocus = false;
13
+
14
+ @query('dialog') dialogEl!: HTMLDialogElement;
15
+
16
+ @property({type: Boolean, reflect: true}) open = false;
17
+ @property() uri = '';
18
+
19
+ private closeWatcher: CloseWatcher | null;
20
+ private _editorId: string;
21
+ set editorId(value: string) {
22
+ this._editorId = value;
23
+ this.requestUpdate();
24
+ }
25
+
26
+ connectedCallback() {
27
+ super.connectedCallback();
28
+ }
29
+
30
+ protected firstUpdated(_changedProperties: PropertyValues) {
31
+ super.firstUpdated(_changedProperties);
32
+
33
+ this.dialogEl.addEventListener("close", () => {
34
+ this.open = false;
35
+ });
36
+ }
37
+
38
+ setContent(content: string) {
39
+ if (this.dialogEl) {
40
+ this.dialogEl.querySelector('.editor-dialog__content')!.innerHTML = content;
41
+ }
42
+ }
43
+
44
+ @watch('open', {waitUntilFirstUpdate: true})
45
+ handleOpenChange() {
46
+ if (this.open) {
47
+ this.addOpenListeners();
48
+ } else {
49
+ this.removeOpenListeners();
50
+ }
51
+ }
52
+
53
+ private addOpenListeners() {
54
+ if ('CloseWatcher' in window) {
55
+ this.closeWatcher?.destroy();
56
+ this.closeWatcher = new CloseWatcher();
57
+ this.closeWatcher.onclose = () => this.requestClose('keyboard');
58
+ }
59
+ }
60
+
61
+ private removeOpenListeners() {
62
+ this.closeWatcher?.destroy();
63
+ }
64
+
65
+ private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
66
+ const znRequestClose = this.emit('zn-request-close', {
67
+ cancelable: true,
68
+ detail: {source}
69
+ });
70
+
71
+ if (znRequestClose.defaultPrevented) {
72
+ return;
73
+ }
74
+
75
+ this.dialogEl.close();
76
+ }
77
+
78
+ private _getLoadingState() {
79
+ return html`
80
+ <div class="loading-wrapper">
81
+ <zn-skeleton height="55px"></zn-skeleton>
82
+ <zn-skeleton height="55px"></zn-skeleton>
83
+ <zn-skeleton height="55px"></zn-skeleton>
84
+ <zn-skeleton height="55px"></zn-skeleton>
85
+ </div>`;
86
+ }
87
+
88
+ // Defaults loading skeletons before content is fetched
89
+ render() {
90
+ return html`
91
+ <dialog closedby="any"
92
+ open="${this.open || nothing}"
93
+ class="${classMap({
94
+ 'editor-dialog': true,
95
+ 'editor-dialog--has-focus': this.hasFocus,
96
+ })}"
97
+ context-data=${JSON.stringify({'editor-id': this._editorId})}>
98
+ <zn-button
99
+ class="editor-dialog__close"
100
+ icon="close"
101
+ icon-size="24"
102
+ type="button"
103
+ color="transparent"
104
+ size="content"
105
+ @click="${() => this.dialogEl.close()}"
106
+ ></zn-button>
107
+ <div class="editor-dialog__content">
108
+ ${this._getLoadingState()}
109
+ </div>
110
+ </dialog>
111
+ `;
112
+ }
113
+ }
114
+
115
+ DialogComponent.define('zn-editor-dialog');
@@ -0,0 +1,43 @@
1
+ @use "../../../../wc";
2
+
3
+ :host {
4
+ @include wc.scrollbars;
5
+ display: block;
6
+ position: absolute;
7
+ overflow: hidden;
8
+ }
9
+
10
+ ::backdrop {
11
+ background-color: rgba(128, 128, 128, 0.6);
12
+ }
13
+
14
+ .editor-dialog {
15
+ background-color: rgb(var(--zn-panel));
16
+ border: 1px solid rgb(var(--zn-border-color));
17
+ border-radius: var(--zn-border-radius-large);
18
+ width: 100%;
19
+ max-width: 975px;
20
+ max-height: 650px;
21
+ padding: 0;
22
+
23
+ .loading-wrapper {
24
+ display: flex;
25
+ flex-direction: column;
26
+ height: 100%;
27
+ padding: 1rem;
28
+ gap: 10px;
29
+ }
30
+
31
+ &__close {
32
+ position: absolute;
33
+ top: 0;
34
+ right: 0;
35
+ margin: var(--zn-spacing-small);
36
+ z-index: 10001;
37
+ }
38
+
39
+ &__content {
40
+ overflow: auto;
41
+ box-sizing: border-box;
42
+ }
43
+ }
@@ -0,0 +1,46 @@
1
+ import {html} from "lit";
2
+ import {litToHTML} from "../../../../utilities/lit-to-html";
3
+ import type DialogComponent from "./dialog.component";
4
+ import type Quill from "quill";
5
+
6
+ class Dialog {
7
+ private readonly _quill: Quill;
8
+ private readonly _editorId: string;
9
+ private readonly _document: Document;
10
+ private _component!: DialogComponent;
11
+
12
+ constructor(quill: Quill, options: { editorId: string }) {
13
+ this._quill = quill;
14
+ this._editorId = options.editorId;
15
+ this._document = this._quill.container.ownerDocument;
16
+ this._initDialog();
17
+ }
18
+
19
+ private _initDialog() {
20
+ const existing = this._document.querySelector<DialogComponent>('zn-editor-dialog');
21
+ if (existing) {
22
+ this._component = existing;
23
+ } else {
24
+ this._component = this.createComponent()!;
25
+ this._document.body.appendChild(this._component);
26
+ }
27
+
28
+ this._component.editorId = this._editorId;
29
+ }
30
+
31
+ private createComponent() {
32
+ const tpl = html`
33
+ <zn-editor-dialog></zn-editor-dialog>`;
34
+ return litToHTML<DialogComponent>(tpl);
35
+ }
36
+
37
+ public isOpen() {
38
+ return this._component.dialogEl.open;
39
+ }
40
+
41
+ public close() {
42
+ this._component.dialogEl.close();
43
+ }
44
+ }
45
+
46
+ export default Dialog;
@@ -1,25 +1,25 @@
1
- import Quill from 'quill';
1
+ import type Quill from 'quill';
2
2
 
3
- interface DragAndDropModuleOptions {
3
+ interface DragAndDropOptions {
4
4
  onDrop: (file: File, options: object) => void;
5
5
  draggableContentTypePattern: string;
6
6
  draggables: [];
7
7
  }
8
8
 
9
- const defaultOptions: DragAndDropModuleOptions = {
9
+ const defaultOptions: DragAndDropOptions = {
10
10
  onDrop: () => {
11
11
  },
12
12
  draggableContentTypePattern: '^image\\/',
13
13
  draggables: []
14
14
  };
15
15
 
16
- export default class DragAndDropModule {
16
+ export default class DragAndDrop {
17
17
  private _quill: Quill;
18
- private _options: DragAndDropModuleOptions;
18
+ private _options: DragAndDropOptions;
19
19
  private _container: any = null;
20
20
  private _draggables: any = [];
21
21
 
22
- constructor(quill: Quill, options: DragAndDropModuleOptions) {
22
+ constructor(quill: Quill, options: DragAndDropOptions) {
23
23
  this._quill = quill;
24
24
  this._options = Object.assign({}, defaultOptions, options);
25
25
  this._container = this._quill.addContainer('ql-editor');