@kubex/zinc 1.0.3 → 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 (154) hide show
  1. package/.github/workflows/js_build_and_deploy.yaml +1 -1
  2. package/dist/custom-elements.json +25150 -0
  3. package/dist/vscode.html-custom-data.json +2874 -0
  4. package/dist/web-types.json +6611 -0
  5. package/dist/zn.d.ts +6664 -0
  6. package/dist/zn.min.css +1 -0
  7. package/dist/zn.min.js +4740 -0
  8. package/docs/_includes/default.njk +1 -0
  9. package/docs/assets/scripts/docs.js +4 -5
  10. package/docs/assets/scripts/search.js +0 -9
  11. package/docs/assets/styles/docs.css +3 -3
  12. package/docs/data/data-table.json +79 -0
  13. package/docs/data/products-table.json +36 -0
  14. package/docs/eleventy.config.cjs +3 -0
  15. package/docs/pages/components/data-select.md +10 -0
  16. package/docs/pages/components/data-table.md +84 -45
  17. package/docs/pages/components/dialog.md +1 -1
  18. package/docs/pages/components/editor.md +1 -3
  19. package/docs/pages/components/filter-wrapper.md +20 -0
  20. package/docs/pages/components/icon.md +2 -0
  21. package/docs/pages/components/item.md +2 -2
  22. package/docs/pages/components/menu-item.md +12 -3
  23. package/docs/pages/components/panel.md +17 -0
  24. package/docs/pages/components/progress-tile.md +17 -1
  25. package/docs/pages/components/settings-container.md +137 -0
  26. package/docs/pages/components/textarea.md +1 -1
  27. package/package.json +3 -1
  28. package/scss/_root.scss +26 -0
  29. package/scss/boot.scss +27 -1
  30. package/scss/mixins/index.scss +30 -1
  31. package/scss/shared/_base.scss +1 -1
  32. package/scss/shared/_button.scss +1 -1
  33. package/scss/shared/layout.scss +7 -0
  34. package/scss/themes/_dark.scss +5 -5
  35. package/scss/themes/_light.scss +6 -5
  36. package/scss/variables/index.scss +9 -0
  37. package/src/components/alert/alert.component.ts +1 -1
  38. package/src/components/alert/alert.scss +18 -4
  39. package/src/components/button/button.component.ts +6 -2
  40. package/src/components/button/button.scss +16 -1
  41. package/src/components/button-menu/button-menu.component.ts +23 -15
  42. package/src/components/button-menu/button-menu.scss +4 -4
  43. package/src/components/checkbox/checkbox.component.ts +5 -1
  44. package/src/components/checkbox/checkbox.scss +1 -1
  45. package/src/components/chip/chip.component.ts +7 -3
  46. package/src/components/chip/chip.scss +9 -1
  47. package/src/components/collapsible/collapsible.component.ts +48 -2
  48. package/src/components/confirm/confirm.component.ts +1 -2
  49. package/src/components/content-block/content-block.component.ts +25 -1
  50. package/src/components/content-block/content-block.scss +8 -0
  51. package/src/components/data-select/data-select.component.ts +1 -1
  52. package/src/components/data-table/data-table.component.ts +361 -422
  53. package/src/components/data-table/data-table.scss +6 -1
  54. package/src/components/datepicker/datepicker.scss +1 -1
  55. package/src/components/dialog/dialog.component.ts +52 -33
  56. package/src/components/dialog/dialog.scss +15 -1
  57. package/src/components/dropdown/dropdown.component.ts +1 -1
  58. package/src/components/editor/editor.component.ts +196 -158
  59. package/src/components/editor/editor.scss +25 -5
  60. package/src/components/editor/modules/ai/index.ts +281 -0
  61. package/src/components/editor/modules/ai/panel/ai-panel.component.ts +102 -0
  62. package/src/components/editor/modules/ai/panel/ai-panel.scss +83 -0
  63. package/src/components/editor/modules/ai/tooltip/ai-tooltip.component.ts +30 -0
  64. package/src/components/editor/modules/ai/tooltip/ai-tooltip.scss +28 -0
  65. package/src/components/editor/modules/{attachment-module.ts → attachment/attachment.ts} +7 -7
  66. package/src/components/editor/modules/context-menu/context-menu-component.ts +115 -0
  67. package/src/components/editor/modules/context-menu/context-menu.scss +49 -0
  68. package/src/components/editor/modules/context-menu/context-menu.ts +296 -0
  69. package/src/components/editor/modules/context-menu/quick-action/index.ts +12 -0
  70. package/src/components/editor/modules/context-menu/quick-action/quick-action.component.ts +18 -0
  71. package/src/components/editor/modules/date-picker/date-picker.ts +77 -0
  72. package/src/components/editor/modules/dialog/dialog.component.ts +115 -0
  73. package/src/components/editor/modules/dialog/dialog.scss +43 -0
  74. package/src/components/editor/modules/dialog/dialog.ts +46 -0
  75. package/src/components/editor/modules/{drag-drop-module.ts → drag-drop/drag-drop.ts} +6 -6
  76. package/src/components/editor/modules/emoji/emoji.ts +106 -0
  77. package/src/components/editor/modules/emoji/headless/headless-emoji.component.ts +122 -0
  78. package/src/components/editor/modules/emoji/headless/headless-emoji.scss +52 -0
  79. package/src/components/editor/modules/emoji/headless/headless-emoji.ts +223 -0
  80. package/src/components/editor/modules/{image-resize-module/image-resize-module.ts → image-resize/image-resize.ts} +5 -5
  81. package/src/components/editor/modules/{time-tracking-module.ts → time-tracking/time-tracking.ts} +5 -5
  82. package/src/components/editor/modules/toolbar/tool/index.ts +12 -0
  83. package/src/components/editor/modules/toolbar/tool/tool.component.ts +30 -0
  84. package/src/components/editor/modules/toolbar/tool/tool.scss +5 -0
  85. package/src/components/editor/modules/toolbar/toolbar.component.ts +738 -0
  86. package/src/components/editor/modules/toolbar/toolbar.scss +98 -0
  87. package/src/components/editor/modules/toolbar/toolbar.ts +328 -0
  88. package/src/components/expanding-action/expanding-action.component.ts +2 -2
  89. package/src/components/expanding-action/expanding-action.scss +2 -2
  90. package/src/components/filter-wrapper/filter-wrapper.component.ts +91 -0
  91. package/src/components/filter-wrapper/filter-wrapper.scss +11 -0
  92. package/src/components/filter-wrapper/filter-wrapper.test.ts +10 -0
  93. package/src/components/filter-wrapper/index.ts +12 -0
  94. package/src/components/header/header.component.ts +1 -1
  95. package/src/components/header/header.scss +8 -1
  96. package/src/components/hover-container/hover-container.scss +39 -36
  97. package/src/components/icon/icon.component.ts +11 -5
  98. package/src/components/icon/icon.scss +15 -2
  99. package/src/components/inline-edit/inline-edit.component.ts +28 -3
  100. package/src/components/inline-edit/inline-edit.scss +4 -1
  101. package/src/components/input/input.scss +1 -1
  102. package/src/components/item/item.component.ts +30 -19
  103. package/src/components/item/item.scss +56 -14
  104. package/src/components/linked-select/linked-select.component.ts +5 -1
  105. package/src/components/menu/menu.component.ts +1 -0
  106. package/src/components/menu/menu.scss +1 -1
  107. package/src/components/menu-item/menu-item.component.ts +22 -4
  108. package/src/components/menu-item/menu-item.scss +54 -7
  109. package/src/components/navbar/navbar.component.ts +7 -1
  110. package/src/components/navbar/navbar.scss +26 -16
  111. package/src/components/option/option.component.ts +4 -1
  112. package/src/components/option/option.scss +9 -0
  113. package/src/components/page-nav/page-nav.component.ts +2 -2
  114. package/src/components/page-nav/page-nav.scss +4 -4
  115. package/src/components/panel/panel.component.ts +20 -9
  116. package/src/components/panel/panel.scss +5 -1
  117. package/src/components/popup/popup.component.ts +16 -2
  118. package/src/components/progress-tile/progress-tile.component.ts +10 -4
  119. package/src/components/progress-tile/progress-tile.scss +7 -4
  120. package/src/components/query-builder/query-builder.component.ts +1 -1
  121. package/src/components/radio/radio.scss +1 -1
  122. package/src/components/scroll-container/scroll-container.component.ts +56 -9
  123. package/src/components/scroll-container/scroll-container.scss +37 -2
  124. package/src/components/select/select.component.ts +3 -0
  125. package/src/components/select/select.scss +1 -1
  126. package/src/components/settings-container/index.ts +12 -0
  127. package/src/components/settings-container/settings-container.component.ts +226 -0
  128. package/src/components/settings-container/settings-container.scss +92 -0
  129. package/src/components/settings-container/settings-container.test.ts +10 -0
  130. package/src/components/skeleton/skeleton.scss +5 -4
  131. package/src/components/sp/sp.component.ts +2 -0
  132. package/src/components/sp/sp.scss +2 -0
  133. package/src/components/split-button/split-button.component.ts +24 -18
  134. package/src/components/split-pane/split-pane.component.ts +5 -5
  135. package/src/components/stat/stat.component.ts +5 -1
  136. package/src/components/stat/stat.scss +24 -1
  137. package/src/components/style/style.component.ts +18 -0
  138. package/src/components/tabs/tabs.component.ts +12 -1
  139. package/src/components/textarea/textarea.component.ts +3 -1
  140. package/src/components/textarea/textarea.scss +21 -2
  141. package/src/components/vertical-stepper/vertical-stepper.scss +1 -1
  142. package/src/internal/form.ts +3 -1
  143. package/src/internal/slot.ts +29 -3
  144. package/src/utilities/on.ts +7 -8
  145. package/src/utilities/query.ts +5 -1
  146. package/src/wc.scss +7 -0
  147. package/src/zinc.ts +5 -0
  148. package/src/components/editor/modules/dialog-module/dialog-module.component.ts +0 -289
  149. package/src/components/editor/modules/dialog-module/dialog-module.scss +0 -115
  150. package/src/components/editor/modules/dialog-module/dialog-module.ts +0 -99
  151. package/src/components/editor/modules/events/zn-show-canned-response-dialog.ts +0 -15
  152. package/src/components/editor/modules/menu-module/menu-module.component.ts +0 -149
  153. package/src/components/editor/modules/menu-module/menu-module.scss +0 -17
  154. package/src/components/editor/modules/menu-module/menu-module.ts +0 -203
@@ -54,6 +54,7 @@ export default class ZnTabs extends ZincElement {
54
54
  protected _activeClicks = 0;
55
55
  private _panel: Element | null | undefined;
56
56
  private _panels: Map<string, Element[]>;
57
+ private _activeTab: Element | null = null;
57
58
  private _tabs: HTMLElement[] = [];
58
59
  private _actions: HTMLElement[] = [];
59
60
  private _knownUri: Map<string, string> = new Map<string, string>();
@@ -261,6 +262,7 @@ export default class ZnTabs extends ZincElement {
261
262
  if (!target.hasAttribute('tab') && target.hasAttribute('tab-uri')) {
262
263
  const tabUri: string | null = target.getAttribute("tab-uri") ?? "";
263
264
  this._createUriPanel(target, tabUri, this._uriToId(tabUri));
265
+ target.setAttribute('tab-id', this._uriToId(tabUri));
264
266
  }
265
267
  }
266
268
 
@@ -320,6 +322,9 @@ export default class ZnTabs extends ZincElement {
320
322
  }
321
323
 
322
324
  _setTabEleActive(ele: Element, active: boolean) {
325
+ if (active) {
326
+ this._activeTab = ele
327
+ }
323
328
  ele.classList.toggle('zn-tb-active', active);
324
329
  ele.classList.toggle('active', active);
325
330
  }
@@ -349,8 +354,14 @@ export default class ZnTabs extends ZincElement {
349
354
  }
350
355
  element.toggleAttribute('selected', isActive);
351
356
  if (isActive && refresh) {
357
+ let uri = "";
358
+ let gaid = "";
359
+ if (this._activeTab && this._activeTab.hasAttribute('tab-uri')) {
360
+ uri = this._activeTab.getAttribute('tab-uri')!;
361
+ gaid = this._activeTab.getAttribute('gaid')!;
362
+ }
352
363
  document.dispatchEvent(new CustomEvent('zn-refresh-element', {
353
- detail: {element: element}
364
+ detail: {element: element, uri: uri, gaid: gaid}
354
365
  }));
355
366
  }
356
367
  });
@@ -141,6 +141,7 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
141
141
  /** The default value of the form control. Primarily used for resetting the form control. */
142
142
  @defaultValue() defaultValue = '';
143
143
 
144
+ @property({type: Boolean}) transparent = false;
144
145
 
145
146
  /** Gets the validity state object */
146
147
  get validity() {
@@ -373,7 +374,8 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
373
374
  'textarea--empty': !this.value,
374
375
  'textarea--resize-none': this.resize === 'none',
375
376
  'textarea--resize-vertical': this.resize === 'vertical',
376
- 'textarea--resize-auto': this.resize === 'auto'
377
+ 'textarea--resize-auto': this.resize === 'auto',
378
+ 'textarea--transparent': this.transparent
377
379
  })}>
378
380
  <textarea
379
381
  part="textarea"
@@ -16,7 +16,7 @@
16
16
  align-items: center;
17
17
  position: relative;
18
18
  width: 100%;
19
- box-shadow: var(--zn-shadow-medium);
19
+ box-shadow: var(--zn-shadow-x-small);
20
20
  font-family: var(--zn-input-font-family);
21
21
  font-weight: var(--zn-input-font-weight);
22
22
  line-height: var(--zn-line-height-normal);
@@ -79,7 +79,7 @@
79
79
  color: var(--zn-input-color);
80
80
  border: none;
81
81
  background: none;
82
- box-shadow: var(--zn-shadow-small);
82
+ box-shadow: var(--zn-shadow-x-small);
83
83
  cursor: inherit;
84
84
  -webkit-appearance: none;
85
85
  height: 100%;
@@ -150,3 +150,22 @@
150
150
  resize: none;
151
151
  overflow-y: hidden;
152
152
  }
153
+
154
+ .form-control-input {
155
+ margin: 0 !important;
156
+ }
157
+
158
+ .textarea--transparent {
159
+ background-color: transparent;
160
+ box-shadow: none !important;
161
+ border: none !important;
162
+
163
+ &:hover {
164
+ background-color: transparent !important;
165
+ }
166
+
167
+ &.textarea--focused {
168
+ border: none !important;
169
+ background-color: transparent !important;
170
+ }
171
+ }
@@ -69,7 +69,7 @@
69
69
 
70
70
  :host([first]) {
71
71
  .vs {
72
- margin-top: 40px;
72
+ margin-top: 10px;
73
73
  }
74
74
  }
75
75
 
@@ -267,7 +267,9 @@ export class FormControlController implements ReactiveController {
267
267
  setTimeout(() => {
268
268
  // disable duplicate submit
269
269
  submitButton.disabled = true;
270
- submitButton.innerHTML = this.form?.getAttribute('data-loading-text') || 'Processing...';
270
+ submitButton.innerHTML = !this.form?.hasAttribute('data-no-loading-text')
271
+ ? this.form?.getAttribute('data-loading-text') || 'Processing...'
272
+ : content;
271
273
  }, 20);
272
274
  }
273
275
 
@@ -1,4 +1,4 @@
1
- import type { ReactiveController, ReactiveControllerHost } from 'lit';
1
+ import type {ReactiveController, ReactiveControllerHost} from 'lit';
2
2
 
3
3
  /** A reactive controller that determines when slots exist. */
4
4
  export class HasSlotController implements ReactiveController {
@@ -55,6 +55,32 @@ export class HasSlotController implements ReactiveController {
55
55
  return this.host.querySelector(`:scope > [slot="${slotName}"]`)!;
56
56
  }
57
57
 
58
+ getDefaultSlot() {
59
+ // Get all child nodes that do not have a slot attribute
60
+ return [...this.host.childNodes].filter(node => {
61
+ if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
62
+ return true;
63
+ }
64
+
65
+ if (node.nodeType === node.ELEMENT_NODE) {
66
+ const el = node as HTMLElement;
67
+ const tagName = el.tagName.toLowerCase();
68
+
69
+ // Ignore visually hidden elements since they aren't rendered
70
+ if (tagName === 'zn-visually-hidden') {
71
+ return false;
72
+ }
73
+
74
+ // If it doesn't have a slot attribute, it's part of the default slot
75
+ if (!el.hasAttribute('slot')) {
76
+ return true;
77
+ }
78
+ }
79
+
80
+ return false;
81
+ }) as HTMLElement[];
82
+ }
83
+
58
84
  getSlots(slotName: string) {
59
85
  return this.host.querySelectorAll(`:scope > [slot="${slotName}"]`) as NodeListOf<HTMLElement>;
60
86
  }
@@ -73,7 +99,7 @@ export class HasSlotController implements ReactiveController {
73
99
  * HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
74
100
  */
75
101
  export function getInnerHTML(slot: HTMLSlotElement): string {
76
- const nodes = slot.assignedNodes({ flatten: true });
102
+ const nodes = slot.assignedNodes({flatten: true});
77
103
  let html = '';
78
104
 
79
105
  [...nodes].forEach(node => {
@@ -97,7 +123,7 @@ export function getTextContent(slot: HTMLSlotElement | undefined | null): string
97
123
  if (!slot) {
98
124
  return '';
99
125
  }
100
- const nodes = slot.assignedNodes({ flatten: true });
126
+ const nodes = slot.assignedNodes({flatten: true});
101
127
  let text = '';
102
128
 
103
129
  [...nodes].forEach(node => {
@@ -1,4 +1,4 @@
1
- type OnEvent = Event & { selectedTarget: EventTarget; path: EventTarget[] };
1
+ export type OnEvent = Event & { selectedTarget: EventTarget; path: EventTarget[] };
2
2
 
3
3
  interface OnEventListener {
4
4
  (evt: OnEvent): void;
@@ -6,20 +6,19 @@ interface OnEventListener {
6
6
 
7
7
  export function on(delegate: EventTarget, eventName: string, targetSelector: string, callback: OnEventListener) {
8
8
  function _fn(e: OnEvent) {
9
- const path = e.path || (e.composedPath?.());
10
- let t: Element | null = (path?.[0] || e.target) as Element;
11
- do {
12
- if (!targetSelector || (t.matches && t.matches(targetSelector))) {
13
- e.selectedTarget = t;
9
+ const path = e.composedPath?.() || e.path || [e.target];
10
+
11
+ for (const node of path) {
12
+ if (node instanceof Element && node.matches(targetSelector)) {
13
+ e.selectedTarget = node;
14
14
  return callback(e);
15
15
  }
16
16
 
17
17
  // stop traversing up the dom once we reach the delegate
18
- if (delegate === t) {
18
+ if (node === delegate) {
19
19
  break;
20
20
  }
21
21
  }
22
- while ((t = t.parentElement));
23
22
 
24
23
  return false;
25
24
  }
@@ -2,8 +2,12 @@ export function deepQuerySelectorAll(selector: string, element: Element, stopSel
2
2
  const arr: Element[] = [];
3
3
 
4
4
  const traverser = (node: Element, sub: boolean) => {
5
+ if (!node) {
6
+ return;
7
+ }
8
+
5
9
  // 1. decline all nodes that are not elements
6
- if (node.nodeType !== Node.ELEMENT_NODE || (sub && stopSelector && node.matches(stopSelector))) {
10
+ if ((node.nodeType !== Node.ELEMENT_NODE) || (sub && stopSelector && node.matches(stopSelector))) {
7
11
  return;
8
12
  }
9
13
 
package/src/wc.scss CHANGED
@@ -31,3 +31,10 @@
31
31
  max-width: var(--zn-container);
32
32
  margin: 0 auto;
33
33
  }
34
+
35
+ .form-container {
36
+ height: 100%;
37
+ width: 100%;
38
+ max-width: var(--zn-container-ph);
39
+ margin: 0 auto;
40
+ }
package/src/zinc.ts CHANGED
@@ -46,6 +46,9 @@ export {default as ProgressBar} from './components/progress-bar';
46
46
  export {default as OrderTable} from './components/order-table';
47
47
  export {default as BulkActions} from './components/bulk-actions';
48
48
  export {default as Editor} from './components/editor';
49
+ export {default as EditorTool} from './components/editor/modules/toolbar/tool';
50
+ export {default as EditorQuickAction} from './components/editor/modules/context-menu/quick-action';
51
+ export {default as EditorDialog} from './components/editor/modules/dialog/dialog.component';
49
52
  export {default as Toggle} from './components/toggle';
50
53
  export {default as Input} from './components/input';
51
54
  export {default as Select} from './components/select';
@@ -75,6 +78,8 @@ export {default as SplitButton} from './components/split-button';
75
78
  export {default as Skeleton} from './components/skeleton';
76
79
  export {default as Style} from './components/style';
77
80
  export {default as ContentBlock} from './components/content-block';
81
+ export {default as FilterWrapper} from './components/filter-wrapper';
82
+ export { default as SettingsContainer } from './components/settings-container';
78
83
  /* plop:component */
79
84
 
80
85
  // Utilities
@@ -1,289 +0,0 @@
1
- import {classMap} from "lit/directives/class-map.js";
2
- import {type CSSResultGroup, html, nothing, type PropertyValues, type TemplateResult, unsafeCSS} from "lit";
3
- import {property, query, state} from "lit/decorators.js";
4
- import {repeat} from "lit/directives/repeat.js";
5
- import {watch} from "../../../../internal/watch";
6
- import ZincElement from "../../../../internal/zinc-element";
7
- import type {CannedResponse} from "../../editor.component";
8
- import type {ZnInputEvent} from "../../../../events/zn-input";
9
- import type ZnInput from "../../../input";
10
-
11
- import styles from './dialog-module.scss';
12
-
13
- export default class DialogModuleComponent extends ZincElement {
14
- static styles: CSSResultGroup = unsafeCSS(styles);
15
-
16
- @state() private hasFocus = false;
17
- @state() private isSearching = false;
18
-
19
- @query('dialog') dialogEl!: HTMLDialogElement;
20
-
21
- @query('zn-input#search-input') searchInput!: ZnInput;
22
- @query('.dialog-module__content') commandList!: HTMLElement;
23
-
24
- @query('.dialog-module') dialogModule!: HTMLElement;
25
-
26
- @property({type: Array}) allCommands: CannedResponse[] = [];
27
- @property({type: Array, reflect: true}) commands: CannedResponse[] = [];
28
- @property({type: Boolean, reflect: true}) open = false;
29
-
30
- private closeWatcher: CloseWatcher | null;
31
-
32
- connectedCallback() {
33
- super.connectedCallback();
34
- this.addEventListener('keydown', this.handleKeyDown);
35
- }
36
-
37
- disconnectedCallback() {
38
- super.disconnectedCallback();
39
- this.removeEventListener('keydown', this.handleKeyDown);
40
- }
41
-
42
- protected firstUpdated(_changedProperties: PropertyValues) {
43
- super.firstUpdated(_changedProperties);
44
-
45
- this.dialogEl.addEventListener("close", () => {
46
- this.open = false;
47
- });
48
- }
49
-
50
- private handleKeyDown(event: KeyboardEvent) {
51
- if (event.key === 'Enter') {
52
- const item = this.getCurrentItem();
53
- event.preventDefault();
54
- event.stopPropagation();
55
-
56
- // Simulate a click to support @click handlers on menu items that also work with the keyboard
57
- item?.click();
58
- }
59
-
60
- // Move the selection when pressing down or up
61
- else if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) {
62
- const items = this.getAllItems();
63
- const activeItem = this.getCurrentItem();
64
- let index = activeItem ? items.indexOf(activeItem) : 0;
65
-
66
- if (!activeItem) {
67
- this.setCurrentItem(items[0]);
68
- items[0].focus();
69
- return;
70
- }
71
-
72
- if (items.length > 0) {
73
- event.preventDefault();
74
- event.stopPropagation();
75
-
76
- if (event.key === 'ArrowDown') {
77
- index++;
78
- } else if (event.key === 'ArrowUp') {
79
- index--;
80
- } else if (event.key === 'Home') {
81
- index = 0;
82
- } else if (event.key === 'End') {
83
- index = items.length - 1;
84
- }
85
-
86
- if (index < 0) {
87
- index = items.length - 1;
88
- }
89
- if (index > items.length - 1) {
90
- index = 0;
91
- }
92
-
93
- this.setCurrentItem(items[index]);
94
- items[index].focus();
95
- }
96
- }
97
- }
98
-
99
- getAllItems() {
100
- if (!this.commandList) {
101
- return [];
102
- }
103
-
104
- return [...this.commandList.children].filter((el: HTMLElement) => {
105
- return el.getAttribute('data-command') !== null;
106
- }) as HTMLElement[];
107
- }
108
-
109
- getCurrentItem() {
110
- return this.getAllItems().find(i => i.getAttribute('tabindex') === '0');
111
- }
112
-
113
- setCurrentItem(item: HTMLElement) {
114
- const items = this.getAllItems();
115
-
116
- // Update tab indexes
117
- items.forEach(i => {
118
- i.setAttribute('tabindex', i === item ? '0' : '-1');
119
- });
120
- }
121
-
122
- focus() {
123
- this.searchInput.focus();
124
- }
125
-
126
- @watch('open', {waitUntilFirstUpdate: true})
127
- handleOpenChange() {
128
- if (this.open) {
129
- this.addOpenListeners();
130
- setTimeout(() => this.focus(), 0);
131
- } else {
132
- this.removeOpenListeners();
133
- }
134
- }
135
-
136
- private addOpenListeners() {
137
- if ('CloseWatcher' in window) {
138
- this.closeWatcher?.destroy();
139
- this.closeWatcher = new CloseWatcher();
140
- this.closeWatcher.onclose = () => this.requestClose('keyboard');
141
- }
142
- }
143
-
144
- private removeOpenListeners() {
145
- this.closeWatcher?.destroy();
146
- }
147
-
148
- private requestClose(source: 'close-button' | 'keyboard' | 'overlay') {
149
- const znRequestClose = this.emit('zn-request-close', {
150
- cancelable: true,
151
- detail: {source}
152
- });
153
-
154
- if (znRequestClose.defaultPrevented) {
155
- return;
156
- }
157
-
158
- this.dialogEl.close();
159
- }
160
-
161
- private handleClick = (event: MouseEvent) => {
162
- const item = (event.target as HTMLElement).closest('.command__wrapper');
163
- if (!item) return;
164
- this.emit('zn-command-select', {detail: {item: item as HTMLElement}});
165
- this.emit('zn-editor-update');
166
- }
167
-
168
- private _createCommand(command: CannedResponse): TemplateResult {
169
- const labels: string[] = [];
170
- if (command.labels) {
171
- command.labels.forEach((label: string) => {
172
- if (label.includes(',')) {
173
- labels.push(...label.split(',').map((l: string) => l.trim()));
174
- } else {
175
- labels.push(label);
176
- }
177
- });
178
- }
179
-
180
- return html`
181
- <div data-command="${command.title}" class="command__wrapper" @click="${this.handleClick}">
182
- <div class="command__left">
183
- <div class="command__command">
184
- ${'/' + (command.command ? command.command :
185
- command.title.toLowerCase().replace(' ', '-'))}
186
- </div>
187
- <div class="command__labels">
188
- ${repeat(labels, (label: string) => html`
189
- <zn-chip class="command__labels__label" size="small"># ${label}</zn-label>
190
- `)}
191
- </div>
192
- </div>
193
- <div class="command__right">
194
- <div class="command__title">
195
- ${command.title}
196
- </div>
197
- <div class="command__snippet">
198
- ${command.content.length > 50 ? command.content.substring(0, 50) + '...' : command.content}
199
- </div>
200
- </div>
201
- </div>`
202
- }
203
-
204
- private isFuzzyMatch(target: string, search: string): boolean {
205
- let tIdx = 0;
206
- let sIdx = 0;
207
- target = target.toLowerCase();
208
- search = search.toLowerCase();
209
- while (tIdx < target.length && sIdx < search.length) {
210
- if (target[tIdx] === search[sIdx]) sIdx++;
211
- tIdx++;
212
- }
213
- return sIdx === search.length;
214
- }
215
-
216
- handleSearch(event: ZnInputEvent) {
217
- const target = event.target as HTMLInputElement | HTMLSelectElement;
218
- const searchValue = target.value.trim().toLowerCase();
219
- this.isSearching = !!searchValue;
220
- if (!searchValue) {
221
- this.commands = [...this.allCommands];
222
- return;
223
- }
224
- this.commands = this.allCommands.filter(command =>
225
- this.isFuzzyMatch(command.title, searchValue) ||
226
- this.isFuzzyMatch(command.content, searchValue) ||
227
- (command.labels && command.labels.some(label => this.isFuzzyMatch(label, searchValue)))
228
- );
229
- }
230
-
231
- render() {
232
- const visibleCommands = this.isSearching
233
- ? this.commands
234
- : this.commands.sort((a, b) => parseInt(b.count) - parseInt(a.count))
235
- .slice(0, 5);
236
- return html`
237
- <dialog closedby="any"
238
- open="${this.open || nothing}"
239
- class="${classMap({
240
- 'dialog-module': true,
241
- 'dialog-module--has-focus': this.hasFocus,
242
- 'dialog-module--has-results': visibleCommands.length > 0,
243
- })}">
244
-
245
- <div class="dialog-module__header">
246
- <div class="dialog-module__header--caption">
247
- Canned Responses
248
- </div>
249
-
250
- <div class="dialog-module__header--search">
251
- <zn-input id="search-input"
252
- type="search"
253
- placeholder="Search..."
254
- class="search-input"
255
- autocomplete="off"
256
- autocorrect="off"
257
- @zn-input="${this.handleSearch}">
258
- <zn-icon src="search" slot="prefix"></zn-icon>
259
- </zn-input>
260
- </div>
261
- </div>
262
-
263
- <div class="dialog-module__body">
264
- <div class="dialog-module__content">
265
- ${repeat(visibleCommands, (command: CannedResponse) => this._createCommand(command))}
266
- </div>
267
-
268
- <slot></slot>
269
- </div>
270
-
271
- <div class="dialog-module__footer">
272
- <div>
273
- Showing ${visibleCommands.length} of ${this.allCommands.length} responses
274
- </div>
275
- <zn-button class="dialog-module__close-button"
276
- size="medium"
277
- color="secondary"
278
- @click="${() => this.requestClose('close-button')}">
279
- Close
280
- </zn-button>
281
- </div>
282
-
283
- </dialog>
284
- `;
285
- }
286
-
287
- }
288
-
289
- DialogModuleComponent.define('zn-dialog-module');
@@ -1,115 +0,0 @@
1
- @use "../../../../wc";
2
-
3
- :host {
4
- @include wc.scrollbars;
5
- display: block;
6
- position: absolute;
7
- }
8
-
9
- ::backdrop {
10
- background-color: rgba(128, 128, 128, 0.6);
11
- }
12
-
13
- dialog[open] {
14
- display: grid;
15
- grid-template-rows: auto 1fr auto;
16
- gap: var(--zn-spacing-medium);
17
- }
18
-
19
- .dialog-module {
20
- background-color: rgb(var(--zn-panel));
21
- border: 1px solid rgb(var(--zn-border-color));
22
- overflow: auto;
23
- padding: var(--zn-spacing-medium) 0;
24
- border-radius: var(--zn-border-radius-medium);
25
- height: 100%;
26
- width: 100%;
27
- max-width: 975px;
28
- max-height: 650px;
29
-
30
- &__header {
31
- display: flex;
32
- align-items: center;
33
- gap: var(--zn-spacing-large);
34
- padding-inline: var(--zn-spacing-medium);
35
-
36
- &--caption {
37
- font-weight: var(--zn-font-weight-bold);
38
- font-size: var(--zn-font-size-large);
39
- color: rgba(var(--zn-text-panel-title), 100%);
40
- }
41
-
42
- &--search {
43
- flex-grow: 1;
44
- }
45
- }
46
-
47
- &__content {
48
- overflow: auto;
49
- max-height: 500px;
50
- }
51
-
52
- &__footer {
53
- display: flex;
54
- align-items: center;
55
- justify-content: space-between;
56
- gap: var(--zn-spacing-small);
57
- padding: 0 var(--zn-spacing-medium) var(--zn-spacing-medium);
58
- }
59
-
60
- .command {
61
- &__wrapper {
62
- padding: var(--zn-spacing-medium) var(--zn-spacing-x-large);
63
- cursor: pointer;
64
- border-bottom: 1px solid rgb(var(--zn-border-color));
65
- display: flex;
66
- align-items: center;
67
- gap: var(--zn-spacing-medium);
68
-
69
- &:hover {
70
- background-color: rgba(var(--zn-primary), 0.05);
71
- }
72
-
73
- &:focus-visible {
74
- outline: none;
75
- background-color: var(--zn-input-background-color-focus);
76
- border-color: var(--zn-input-border-color-focus);
77
- box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color);
78
- }
79
- }
80
-
81
- &__left {
82
- width: 50%;
83
- flex-basis: 50%;
84
- }
85
-
86
- &__command {
87
- font-size: var(--zn-font-size-small);
88
- color: rgb(var(--zn-text-heading));
89
- }
90
-
91
- &__labels {
92
- display: flex;
93
- gap: var(--zn-spacing-small);
94
- flex-wrap: wrap;
95
- padding-top: var(--zn-spacing-small);
96
- font-size: var(--zn-font-size-small);
97
- }
98
-
99
- &__right {
100
- width: 50%;
101
- flex-basis: 50%;
102
- }
103
-
104
- &__title {
105
- font-size: var(--zn-font-size-small);
106
- font-weight: 500;
107
- }
108
-
109
- &__snippet {
110
- margin-top: var(--zn-spacing-small);
111
- font-size: var(--zn-font-size-small);
112
- color: rgb(var(--zn-text));
113
- }
114
- }
115
- }