@kubex/zinc 1.0.9 → 1.0.11

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 (29) hide show
  1. package/dist/custom-elements.json +282 -83
  2. package/dist/vscode.html-custom-data.json +25 -13
  3. package/dist/web-types.json +84 -27
  4. package/dist/zn.d.ts +40 -2
  5. package/dist/zn.min.js +260 -243
  6. package/docs/pages/components/dropdown.md +6 -1
  7. package/docs/pages/components/table.md +1 -1
  8. package/package.json +1 -1
  9. package/src/components/button-menu/button-menu.component.ts +1 -0
  10. package/src/components/editor/editor.component.ts +110 -55
  11. package/src/components/editor/modules/context-menu/context-menu-component.ts +1 -0
  12. package/src/components/editor/modules/context-menu/context-menu.scss +1 -1
  13. package/src/components/editor/modules/context-menu/context-menu.ts +58 -26
  14. package/src/components/editor/modules/context-menu/quick-action/quick-action.component.ts +1 -0
  15. package/src/components/editor/modules/toolbar/toolbar.component.ts +61 -33
  16. package/src/components/editor/modules/toolbar/toolbar.ts +19 -4
  17. package/src/components/header/header.scss +1 -0
  18. package/src/components/menu/menu.component.ts +2 -1
  19. package/src/components/menu-item/menu-item.component.ts +4 -0
  20. package/src/components/note/note.component.ts +2 -25
  21. package/src/components/note/note.scss +0 -7
  22. package/src/components/order-table/order-table.scss +16 -10
  23. package/src/components/panel/panel.component.ts +4 -2
  24. package/src/components/panel/panel.scss +4 -0
  25. package/src/components/popup/popup.component.ts +11 -21
  26. package/src/components/settings-container/settings-container.component.ts +21 -15
  27. package/src/components/settings-container/settings-container.scss +5 -4
  28. package/src/components/tooltip/tooltip.component.ts +7 -7
  29. package/src/zinc.ts +7 -1
@@ -6,7 +6,12 @@ layout: component
6
6
  ---
7
7
 
8
8
  ```html:preview
9
- <zn-dropdown></zn-dropdown>
9
+ <zn-dropdown t="[object Object]" placement="bottom-start">
10
+ <zn-menu actions="[{&quot;confirm&quot;:{&quot;type&quot;:&quot;success&quot;,&quot;trigger&quot;:&quot;reactivate10&quot;,&quot;caption&quot;:&quot;Reactivate Access&quot;,&quot;content&quot;:&quot;Are you sure you want to reactivate access for John Doe?&quot;,&quot;action&quot;:&quot;#&quot;},&quot;title&quot;:&quot;Reactivate Access&quot;,&quot;style&quot;:&quot;success&quot;}]" t="[object Object]" role="menu"></zn-menu>
11
+ <button slot="trigger">
12
+ <zn-icon src="more_vert" t="[object Object]" size="24" library="material-symbols-outlined"></zn-icon>
13
+ </button>
14
+ </zn-dropdown>
10
15
  ```
11
16
 
12
17
  ## Examples
@@ -6,7 +6,7 @@ layout: component
6
6
  ---
7
7
 
8
8
  ```html:preview
9
- <zn-table></zn-table>
9
+ <zn-table fixed-first="" t="light" has-actions="" data="{&quot;header&quot;:[&quot;Full Name&quot;,&quot;User ID&quot;,&quot;Email&quot;,&quot;Type&quot;,&quot;Partner&quot;,&quot;State&quot;,&quot;Date Joined&quot;],&quot;items&quot;: [{&quot;actions&quot;: [{&quot;confirm&quot;: {&quot;type&quot;: &quot;success&quot;,&quot;trigger&quot;: &quot;reactivate10&quot;,&quot;caption&quot;: &quot;Reactivate Access&quot;,&quot;content&quot;: &quot;Are you sure you want to reactivate access for John Doe?&quot;,&quot;action&quot;: &quot;#&quot;},&quot;title&quot;: &quot;Reactivate Access&quot;,&quot;style&quot;: &quot;success&quot;}],&quot;caption&quot;: &quot;John Doe&quot;,&quot;data&quot;: [&quot;1&quot;,&quot;john.doe@example.com&quot;,&quot;Member&quot;,&quot;&quot;,&quot;&lt;span class='zn-success'&gt;Active&lt;/span&gt;&quot;,&quot;2025-01-01 12:10:20&quot;]}]}"></zn-table>
10
10
  ```
11
11
 
12
12
  ## Examples
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -307,6 +307,7 @@ export default class ZnButtonMenu extends ZincElement {
307
307
  public setDynamicButtons(btns: NodeListOf<ZnButton>) {
308
308
  // remove all buttons that have the zn-button-menu__added class
309
309
  this.removeAllButtons();
310
+ if (!btns || btns.length === 0) return;
310
311
 
311
312
  // add the new buttons
312
313
  btns.forEach((button: ZnButton) => {
@@ -25,6 +25,18 @@ import type ToolbarComponent from "./modules/toolbar/toolbar.component";
25
25
 
26
26
  import styles from './editor.scss';
27
27
 
28
+ export interface EditorFeatureConfig {
29
+ codeBlocksEnabled?: boolean;
30
+ dividersEnabled?: boolean;
31
+ linksEnabled?: boolean;
32
+ attachmentsEnabled?: boolean;
33
+ imagesEnabled?: boolean;
34
+ videosEnabled?: boolean;
35
+ datesEnabled?: boolean;
36
+ emojisEnabled?: boolean;
37
+ codeEnabled?: boolean;
38
+ }
39
+
28
40
  /**
29
41
  * @summary Short summary of the component's intended use.
30
42
  * @documentation https://zinc.style/components/editor
@@ -66,6 +78,17 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
66
78
  @property({attribute: 'attachment-url', type: String})
67
79
  uploadAttachmentUrl: string;
68
80
 
81
+ /* Feature Permission Attributes */
82
+ @property({attribute: 'code-blocks', type: Boolean}) codeBlocksEnabled: boolean = false;
83
+ @property({attribute: 'dividers', type: Boolean}) dividersEnabled: boolean = false;
84
+ @property({attribute: 'links', type: Boolean}) linksEnabled: boolean = false;
85
+ @property({attribute: 'attachments', type: Boolean}) attachmentsEnabled: boolean = false;
86
+ @property({attribute: 'images', type: Boolean}) imagesEnabled: boolean = false;
87
+ @property({attribute: 'videos', type: Boolean}) videosEnabled: boolean = false;
88
+ @property({attribute: 'dates', type: Boolean}) datesEnabled: boolean = false;
89
+ @property({attribute: 'emojis', type: Boolean}) emojisEnabled: boolean = false;
90
+ @property({attribute: 'code', type: Boolean}) codeEnabled: boolean = false;
91
+ @property({attribute: 'ai', type: Boolean}) aiEnabled: boolean = false;
69
92
  @property({attribute: 'ai-path'}) aiPath: string = '';
70
93
 
71
94
  private quillElement: Quill;
@@ -111,10 +134,16 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
111
134
  });
112
135
 
113
136
  Quill.register({'modules/toolbar': Toolbar}, true);
114
- Quill.register({'modules/datePicker': DatePicker}, true);
115
- Quill.register({'modules/emoji': Emoji}, true);
116
- Quill.register({'modules/headlessEmoji': HeadlessEmoji}, true);
117
- Quill.register({'modules/attachment': Attachment}, true);
137
+ if (this.datesEnabled) {
138
+ Quill.register({'modules/datePicker': DatePicker}, true);
139
+ }
140
+ if (this.emojisEnabled) {
141
+ Quill.register({'modules/emoji': Emoji}, true);
142
+ Quill.register({'modules/headlessEmoji': HeadlessEmoji}, true);
143
+ }
144
+ if (this.attachmentsEnabled) {
145
+ Quill.register({'modules/attachment': Attachment}, true);
146
+ }
118
147
  Quill.register({'modules/timeTracking': TimeTracking}, true);
119
148
  Quill.register({'modules/dragAndDrop': DragAndDrop}, true);
120
149
  Quill.register({'modules/imageResize': ImageResize}, true);
@@ -122,80 +151,106 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
122
151
  Quill.register({'modules/dialog': Dialog}, true);
123
152
 
124
153
  /* AI Modules */
125
- Quill.register('modules/ai', QuillAI as any, true);
154
+ if (this.aiEnabled) {
155
+ Quill.register('modules/ai', QuillAI as any, true);
156
+ }
126
157
 
127
158
  // Register a custom HR blot so we can insert an inline horizontal rule block
128
- const BlockEmbed = Quill.import('blots/block/embed') as { new(...args: any[]): any };
129
-
130
- class HrBlot extends BlockEmbed {
131
- static blotName = 'hr';
132
- static tagName = 'HR';
133
- static className = 'ql-hr';
134
-
135
- static create(): HTMLElement {
136
- const node = document.createElement('hr');
137
- node.classList.add('ql-hr');
138
- return node;
159
+ if (this.dividersEnabled) {
160
+ const BlockEmbed = Quill.import('blots/block/embed') as { new(...args: any[]): any };
161
+
162
+ class HrBlot extends BlockEmbed {
163
+ static blotName = 'hr';
164
+ static tagName = 'HR';
165
+ static className = 'ql-hr';
166
+
167
+ static create(): HTMLElement {
168
+ const node = document.createElement('hr');
169
+ node.classList.add('ql-hr');
170
+ return node;
171
+ }
139
172
  }
140
- }
141
173
 
142
- Quill.register({'formats/hr': HrBlot}, true);
174
+ Quill.register({'formats/hr': HrBlot}, true);
175
+ }
143
176
 
144
- const attachmentInput = this.getForm()?.querySelector('input[name="attachments"]');
145
177
  const startTimeInput = this.getForm()?.querySelector('input[name="startTime"]');
146
178
  const openTimeInput = this.getForm()?.querySelector('input[name="openTime"]');
147
179
 
180
+ const featureConfig: EditorFeatureConfig = {
181
+ codeBlocksEnabled: this.codeBlocksEnabled,
182
+ dividersEnabled: this.dividersEnabled,
183
+ linksEnabled: this.linksEnabled,
184
+ attachmentsEnabled: this.attachmentsEnabled,
185
+ imagesEnabled: this.imagesEnabled,
186
+ videosEnabled: this.videosEnabled,
187
+ datesEnabled: this.datesEnabled,
188
+ emojisEnabled: this.emojisEnabled,
189
+ codeEnabled: this.codeEnabled,
190
+ };
191
+
148
192
  const quill = new Quill(this.editor, {
149
193
  modules: {
150
194
  toolbar: {
151
195
  container: this.toolbar,
196
+ config: featureConfig
197
+ },
198
+ contextMenu: {
199
+ config: featureConfig
152
200
  },
153
- contextMenu: {},
154
201
  keyboard: {
155
202
  bindings: bindings
156
203
  },
157
204
  dialog: {
158
205
  editorId: this.id
159
206
  },
160
- emoji: {},
161
- headlessEmoji: {},
162
- datePicker: {},
163
- ai: {
164
- path: this.aiPath
165
- },
207
+ ...(this.datesEnabled && {
208
+ datePicker: {}
209
+ }),
210
+ ...(this.emojisEnabled && {
211
+ emoji: {},
212
+ headlessEmoji: {},
213
+ }),
214
+ ...(this.aiEnabled && {
215
+ ai: {
216
+ path: this.aiPath
217
+ }
218
+ }),
166
219
  timeTracking: {
167
220
  startTimeInput: startTimeInput as HTMLInputElement,
168
221
  openTimeInput: openTimeInput as HTMLInputElement
169
222
  },
170
- attachment: {
171
- attachmentInput: attachmentInput,
172
- onFileUploaded: () => {
173
- window.onbeforeunload = () => null;
223
+ ...(this.attachmentsEnabled && {
224
+ attachment: {
225
+ attachmentInput: this.getForm()?.querySelector('input[name="attachments"]'),
226
+ onFileUploaded: () => {
227
+ window.onbeforeunload = () => null;
228
+ },
229
+ upload: (file: File) => {
230
+ window.onbeforeunload = () => 'You have unsaved changes. Are you sure you want to leave?';
231
+ return new Promise((resolve) => {
232
+ const fd = new FormData();
233
+ fd.append('filename', file.name);
234
+ fd.append('size', file.size.toString());
235
+ fd.append('mimeType', file.type);
236
+
237
+ const xhr = new XMLHttpRequest();
238
+ xhr.open('POST', this.uploadAttachmentUrl, true);
239
+ xhr.onload = () => {
240
+ if (xhr.status === 200) {
241
+ const response = JSON.parse(xhr.responseText) as {
242
+ uploadPath: string;
243
+ uploadUrl: string;
244
+ originalFilename: string;
245
+ };
246
+ resolve({path: response.uploadPath, url: response.uploadUrl, filename: response.originalFilename});
247
+ }
248
+ };
249
+ xhr.send(fd);
250
+ });
251
+ },
174
252
  },
175
- upload: (file: File) => {
176
- window.onbeforeunload = () => 'You have unsaved changes. Are you sure you want to leave?';
177
- return new Promise((resolve) => {
178
- const fd = new FormData();
179
- fd.append('filename', file.name);
180
- fd.append('size', file.size.toString());
181
- fd.append('mimeType', file.type);
182
-
183
- const xhr = new XMLHttpRequest();
184
- xhr.open('POST', this.uploadAttachmentUrl, true);
185
- xhr.onload = () => {
186
- if (xhr.status === 200) {
187
- const response = JSON.parse(xhr.responseText) as {
188
- uploadPath: string;
189
- uploadUrl: string;
190
- originalFilename: string;
191
- };
192
- resolve({path: response.uploadPath, url: response.uploadUrl, filename: response.originalFilename});
193
- }
194
- };
195
- xhr.send(fd);
196
- });
197
- },
198
- },
253
+ }),
199
254
  imageResize: {},
200
255
  history: {}
201
256
  },
@@ -339,7 +394,7 @@ export default class ZnEditor extends ZincElement implements ZincFormControl {
339
394
  const emoji = this.quillElement.getModule('headlessEmoji') as HeadlessEmoji;
340
395
  const menu = this.quillElement.getModule('contextMenu') as ContextMenu;
341
396
  const dialog = this.quillElement.getModule('dialog') as Dialog;
342
- if (emoji.isOpen() || menu.isOpen() || dialog.isOpen()) {
397
+ if (emoji?.isOpen() || menu?.isOpen() || dialog?.isOpen()) {
343
398
  return false;
344
399
  }
345
400
 
@@ -10,6 +10,7 @@ export interface ResultItem {
10
10
  format?: string;
11
11
  key?: string;
12
12
  value?: string | boolean;
13
+ order?: number;
13
14
  }
14
15
 
15
16
  export default class ContextMenuComponent extends ZincElement {
@@ -6,7 +6,7 @@
6
6
  border: 1px solid var(--zn-border-color);
7
7
  border-radius: 8px;
8
8
  box-shadow: var(--zn-shadow-medium);
9
- padding: 6px;
9
+ padding: 6px 0;
10
10
  max-height: 220px;
11
11
  overflow-y: auto;
12
12
  min-width: 360px;
@@ -4,6 +4,7 @@ import {litToHTML} from "../../../../utilities/lit-to-html";
4
4
  import {type ResultItem} from "./context-menu-component";
5
5
  import Quill, {Delta} from "quill";
6
6
  import ZnEditorQuickAction from "./quick-action";
7
+ import type {EditorFeatureConfig} from "../../editor.component";
7
8
  import type ContextMenuComponent from "./context-menu-component";
8
9
  import type Toolbar from "../toolbar/toolbar";
9
10
 
@@ -14,10 +15,12 @@ class ContextMenu {
14
15
  private _startIndex = -1;
15
16
  private _keydownHandler = (e: KeyboardEvent) => this.onKeydown(e);
16
17
  private _docClickHandler = (e: MouseEvent) => this.onDocumentClick(e);
18
+ private _featureConfig: EditorFeatureConfig = {};
17
19
 
18
- constructor(quill: Quill) {
20
+ constructor(quill: Quill, options: { config: EditorFeatureConfig }) {
19
21
  this._quill = quill;
20
22
  this._toolbarModule = quill.getModule('toolbar') as Toolbar;
23
+ this._featureConfig = options.config || {};
21
24
 
22
25
  this.initComponent();
23
26
  this.attachEvents();
@@ -199,7 +202,7 @@ class ContextMenu {
199
202
  Quill.sources.USER
200
203
  );
201
204
 
202
- setTimeout(() => this._quill.setSelection(insertIndex + contentDelta.length(), 0, Quill.sources.SILENT), 0);
205
+ setTimeout(() => this._quill.setSelection(insertIndex + contentDelta.length() + 1, 0, Quill.sources.SILENT), 0);
203
206
  }
204
207
  this._quill.focus();
205
208
  this.hide();
@@ -225,6 +228,7 @@ class ContextMenu {
225
228
 
226
229
  private _getOptions(): ResultItem[] {
227
230
  const options: ResultItem[] = [];
231
+ let orderCounter = 0;
228
232
 
229
233
  // 1) Quick Actions
230
234
  const root = this._quill.container.getRootNode() as ShadowRoot;
@@ -236,41 +240,69 @@ class ContextMenu {
236
240
 
237
241
  const {label, content, uri, icon, key} = quickAction;
238
242
 
243
+ let order: number;
244
+ if (typeof quickAction.order === 'number') {
245
+ order = quickAction.order!;
246
+ } else {
247
+ order = orderCounter++;
248
+ }
249
+
239
250
  if (label && icon) {
240
251
  if (key) {
241
- options.push({icon, label, format: 'toolbar', key: key});
252
+ options.push({icon, label, format: 'toolbar', key: key, order});
242
253
  } else if (uri) {
243
- options.push({icon, label, format: 'dialog', value: uri});
254
+ options.push({icon, label, format: 'dialog', value: uri, order});
244
255
  } else if (content) {
245
- options.push({icon, label, format: 'insert', value: content});
256
+ options.push({icon, label, format: 'insert', value: content, order});
246
257
  }
247
258
  }
248
259
  });
249
260
  }
250
261
 
251
- // 2) Built-in Actions
262
+ // 2) Built-in Actions (In order they should appear)
252
263
  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'}
264
+ {icon: 'format_bold', label: 'Bold', format: 'bold', order: orderCounter++},
265
+ {icon: 'format_italic', label: 'Italic', format: 'italic', order: orderCounter++},
266
+ {icon: 'format_underlined', label: 'Underline', format: 'underline', order: orderCounter++},
267
+ {icon: 'strikethrough_s', label: 'Strikethrough', format: 'strike', order: orderCounter++},
268
+ {icon: 'format_quote', label: 'Blockquote', format: 'blockquote', order: orderCounter++},
273
269
  );
270
+ if (this._featureConfig.codeEnabled !== false) {
271
+ options.push({icon: 'code', label: 'Inline Code', format: 'code', order: orderCounter++});
272
+ }
273
+ if (this._featureConfig.codeBlocksEnabled !== false) {
274
+ options.push({icon: 'code_blocks', label: 'Code Block', format: 'code-block', order: orderCounter++});
275
+ }
276
+ options.push(
277
+ {icon: 'format_h1', label: 'Heading 1', format: 'header', value: '1', order: orderCounter++},
278
+ {icon: 'format_h2', label: 'Heading 2', format: 'header', value: '2', order: orderCounter++},
279
+ {icon: 'match_case', label: 'Normal Text', format: 'header', value: '', order: orderCounter++},
280
+ {icon: 'format_list_bulleted', label: 'Bulleted List', format: 'list', value: 'bullet', order: orderCounter++},
281
+ {icon: 'format_list_numbered', label: 'Numbered List', format: 'list', value: 'ordered', order: orderCounter++},
282
+ {icon: 'checklist', label: 'Checklist', format: 'list', value: 'checked', order: orderCounter++}
283
+ );
284
+ if (this._featureConfig.linksEnabled !== false) {
285
+ options.push({icon: 'link', label: 'Link', format: 'link', value: true, order: orderCounter++});
286
+ }
287
+ if (this._featureConfig.dividersEnabled !== false) {
288
+ options.push({icon: 'horizontal_rule', label: 'Divider', format: 'divider', order: orderCounter++});
289
+ }
290
+ if (this._featureConfig.attachmentsEnabled !== false) {
291
+ options.push({icon: 'attachment', label: 'Attachment', format: 'attachment', order: orderCounter++});
292
+ }
293
+ if (this._featureConfig.imagesEnabled !== false) {
294
+ options.push({icon: 'image', label: 'Image', format: 'image', order: orderCounter++});
295
+ }
296
+ if (this._featureConfig.videosEnabled !== false) {
297
+ options.push({icon: 'video_camera_back', label: 'Video', format: 'video', order: orderCounter++});
298
+ }
299
+ if (this._featureConfig.datesEnabled !== false) {
300
+ options.push({icon: 'calendar_today', label: 'Date', format: 'date', order: orderCounter++});
301
+ }
302
+ options.push({icon: 'format_clear', label: 'Clear Formatting', format: 'clean', order: orderCounter++});
303
+
304
+ // Sort options by order property
305
+ options.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
274
306
 
275
307
  return options;
276
308
  }
@@ -8,6 +8,7 @@ export default class ZnEditorQuickAction extends ZincElement {
8
8
  @property() content: string;
9
9
  @property() key: string;
10
10
  @property() icon: string;
11
+ @property({type: Number}) order?: number | null;
11
12
 
12
13
  render() {
13
14
  return html`
@@ -6,6 +6,7 @@ import ZnButton from "../../../button";
6
6
  import ZnEditorTool from "./tool";
7
7
  import ZnMenu from "../../../menu";
8
8
  import ZnMenuItem from "../../../menu-item";
9
+ import type {EditorFeatureConfig} from "../../editor.component";
9
10
 
10
11
  import styles from './toolbar.scss';
11
12
 
@@ -22,6 +23,7 @@ export default class ToolbarComponent extends ZincElement {
22
23
  @query('.toolbar__overflow-menu') private _overflowMenu!: HTMLElement;
23
24
  @query('.toolbar__group--overflow') private _overflowGroup!: HTMLElement;
24
25
 
26
+ private _featureConfig: EditorFeatureConfig = {};
25
27
  private _resizeObserver: ResizeObserver | null = null;
26
28
  private _resizeId: number | null = null;
27
29
 
@@ -78,6 +80,10 @@ export default class ToolbarComponent extends ZincElement {
78
80
  });
79
81
  }
80
82
 
83
+ public configureToolbar(config: EditorFeatureConfig) {
84
+ this._featureConfig = config;
85
+ }
86
+
81
87
  private getToolbarWidth() {
82
88
  const host = this as unknown as HTMLElement;
83
89
  let width = host.getBoundingClientRect?.().width || host.offsetWidth || 0;
@@ -456,6 +462,13 @@ export default class ToolbarComponent extends ZincElement {
456
462
  }
457
463
 
458
464
  render() {
465
+ const showInsertOptions = this._featureConfig.dividersEnabled ||
466
+ this._featureConfig.linksEnabled ||
467
+ this._featureConfig.attachmentsEnabled ||
468
+ this._featureConfig.imagesEnabled ||
469
+ this._featureConfig.videosEnabled;
470
+ const showInsertGroup = showInsertOptions || this._featureConfig.datesEnabled || this._featureConfig.emojisEnabled;
471
+
459
472
  return html`
460
473
  <div class="toolbar">
461
474
  <div class="toolbar__group">${this._textOptions()}</div>
@@ -463,11 +476,12 @@ export default class ToolbarComponent extends ZincElement {
463
476
  <div class="toolbar__group">${this._historyOptions()}</div>
464
477
  <div class="toolbar__group">${this._colorOptions()}</div>
465
478
  <div class="toolbar__group">${this._listOptions()}</div>
466
- <div class="toolbar__group">
467
- ${this._insertOptions()}
468
- ${this._dateOption()}
469
- ${this._emojiOptions()}
470
- </div>
479
+ ${showInsertGroup ? html`
480
+ <div class="toolbar__group">
481
+ ${showInsertOptions ? html`${this._insertOptions()}` : ''}
482
+ ${this._featureConfig.datesEnabled ? html`${this._dateOption()}` : ''}
483
+ ${this._featureConfig.emojisEnabled ? html`${this._emojiOptions()}` : ''}
484
+ </div>` : ''}
471
485
  <div class="toolbar__group">
472
486
  <slot></slot>
473
487
  </div>
@@ -550,14 +564,18 @@ export default class ToolbarComponent extends ZincElement {
550
564
  <zn-icon src="format_quote" size="18" slot="prefix"></zn-icon>
551
565
  Quote
552
566
  </zn-menu-item>
553
- <zn-menu-item type="checkbox" checked-position="right" data-format="code">
554
- <zn-icon src="code" size="18" slot="prefix"></zn-icon>
555
- Code
556
- </zn-menu-item>
557
- <zn-menu-item type="checkbox" checked-position="right" data-format="code-block">
558
- <zn-icon src="code_blocks" size="18" slot="prefix"></zn-icon>
559
- Code Block
560
- </zn-menu-item>
567
+ ${this._featureConfig.codeEnabled ? html`
568
+ <zn-menu-item type="checkbox" checked-position="right" data-format="code">
569
+ <zn-icon src="code" size="18" slot="prefix"></zn-icon>
570
+ Code
571
+ </zn-menu-item>
572
+ ` : ''}
573
+ ${this._featureConfig.codeBlocksEnabled ? html`
574
+ <zn-menu-item type="checkbox" checked-position="right" data-format="code-block">
575
+ <zn-icon src="code_blocks" size="18" slot="prefix"></zn-icon>
576
+ Code Block
577
+ </zn-menu-item>
578
+ ` : ''}
561
579
  <zn-menu-item data-format="clean">
562
580
  <zn-icon src="format_clear" size="18" slot="prefix"></zn-icon>
563
581
  Clear Formatting
@@ -680,26 +698,36 @@ export default class ToolbarComponent extends ZincElement {
680
698
  <zn-icon src="add" size="18"></zn-icon>
681
699
  </zn-button>
682
700
  <zn-menu>
683
- <zn-menu-item checked-position="right" data-format="divider">
684
- <zn-icon src="horizontal_rule" size="18" slot="prefix"></zn-icon>
685
- Divider
686
- </zn-menu-item>
687
- <zn-menu-item checked-position="right" data-format="link">
688
- <zn-icon src="link" size="18" slot="prefix"></zn-icon>
689
- Link
690
- </zn-menu-item>
691
- <zn-menu-item checked-position="right" data-format="attachment">
692
- <zn-icon src="attachment" size="18" slot="prefix"></zn-icon>
693
- Attachment
694
- </zn-menu-item>
695
- <zn-menu-item checked-position="right" data-format="image">
696
- <zn-icon src="image" size="18" slot="prefix"></zn-icon>
697
- Image
698
- </zn-menu-item>
699
- <zn-menu-item checked-position="right" data-format="video">
700
- <zn-icon src="video_camera_back" size="18" slot="prefix"></zn-icon>
701
- Video
702
- </zn-menu-item>
701
+ ${this._featureConfig.dividersEnabled ? html`
702
+ <zn-menu-item checked-position="right" data-format="divider">
703
+ <zn-icon src="horizontal_rule" size="18" slot="prefix"></zn-icon>
704
+ Divider
705
+ </zn-menu-item>
706
+ ` : ''}
707
+ ${this._featureConfig.linksEnabled ? html`
708
+ <zn-menu-item checked-position="right" data-format="link">
709
+ <zn-icon src="link" size="18" slot="prefix"></zn-icon>
710
+ Link
711
+ </zn-menu-item>
712
+ ` : ''}
713
+ ${this._featureConfig.attachmentsEnabled ? html`
714
+ <zn-menu-item checked-position="right" data-format="attachment">
715
+ <zn-icon src="attachment" size="18" slot="prefix"></zn-icon>
716
+ Attachment
717
+ </zn-menu-item>
718
+ ` : ''}
719
+ ${this._featureConfig.imagesEnabled ? html`
720
+ <zn-menu-item checked-position="right" data-format="image">
721
+ <zn-icon src="image" size="18" slot="prefix"></zn-icon>
722
+ Image
723
+ </zn-menu-item>
724
+ ` : ''}
725
+ ${this._featureConfig.videosEnabled ? html`
726
+ <zn-menu-item checked-position="right" data-format="video">
727
+ <zn-icon src="video_camera_back" size="18" slot="prefix"></zn-icon>
728
+ Video
729
+ </zn-menu-item>
730
+ ` : ''}
703
731
  </zn-menu>
704
732
  </zn-dropdown>`;
705
733
  }
@@ -1,6 +1,7 @@
1
1
  import './toolbar.component';
2
2
  import Quill from "quill";
3
3
  import QuillToolbar from "quill/modules/toolbar";
4
+ import type {EditorFeatureConfig} from "../../editor.component";
4
5
  import type Attachment from "../attachment/attachment";
5
6
  import type DialogComponent from "../dialog/dialog.component";
6
7
  import type Emoji from "../emoji/emoji";
@@ -17,20 +18,34 @@ class Toolbar extends QuillToolbar {
17
18
  constructor(quill: Quill, options: {
18
19
  container: ToolbarComponent;
19
20
  handlers?: Record<string, (value?: any) => void>;
21
+ config?: EditorFeatureConfig;
20
22
  }) {
21
23
  super(quill, options);
22
24
 
25
+ const featureConfig = options.config;
26
+
23
27
  // Add handlers after parent Toolbar initialization
24
- this.addHandler('attachment', () => null);
25
- this.addHandler('date', () => this._openDatePicker());
26
- this.addHandler('divider', () => this._insertDivider());
28
+ if (featureConfig?.attachmentsEnabled) {
29
+ this.addHandler('attachment', () => null);
30
+ }
31
+ if (featureConfig?.datesEnabled) {
32
+ this.addHandler('date', () => this._openDatePicker());
33
+ }
34
+ if (featureConfig?.dividersEnabled) {
35
+ this.addHandler('divider', () => this._insertDivider());
36
+ }
37
+ if (featureConfig?.imagesEnabled) {
38
+ this.addHandler('image', () => this._addImage());
39
+ }
27
40
  this.addHandler('redo', () => quill.history.redo());
28
41
  this.addHandler('undo', () => quill.history.undo());
29
42
  this.addHandler('dialog', (value: string) => this._openDialog(value));
30
- this.addHandler('image', () => this._addImage());
31
43
 
32
44
  this._quill = quill;
33
45
  this._component = options.container;
46
+ if (featureConfig) {
47
+ this._component.configureToolbar(featureConfig)
48
+ }
34
49
 
35
50
  this._component.updateComplete.then(() => {
36
51
  this._attachToolbarHandlers();
@@ -233,6 +233,7 @@
233
233
  align-items: center;
234
234
  flex-wrap: wrap;
235
235
  gap: var(--zn-spacing-x-small);
236
+ word-break: break-word;
236
237
  }
237
238
 
238
239
  slot[name="nav"] {
@@ -112,7 +112,8 @@ export default class ZnMenu extends ZincElement {
112
112
  action="${item.confirm?.action}"></zn-confirm>
113
113
  <zn-menu-item @mousedown=${this.handleMouseDown}
114
114
  @keydown=${this.handleKeyDown}
115
- id="${item.confirm?.trigger}">
115
+ id="${item.confirm?.trigger}"
116
+ confirm>
116
117
  ${(item.icon) ? html`
117
118
  <zn-icon src="${item.icon}" size="20" slot="prefix"></zn-icon>` : html``}
118
119
  ${item.title}