@dataengineeringformachinelearning/viking-ui 4.0.4 → 4.0.6

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 (246) hide show
  1. package/README.md +35 -11
  2. package/dist/README.md +35 -11
  3. package/dist/design-tokens.css +300 -85
  4. package/dist/fesm2022/dataengineeringformachinelearning-viking-ui.mjs +100 -64
  5. package/dist/fesm2022/dataengineeringformachinelearning-viking-ui.mjs.map +1 -1
  6. package/dist/icons.js +306 -0
  7. package/dist/package.json +24 -5
  8. package/{src/lib/site-drakkar/site-drakkar.config.ts → dist/site-drakkar.js} +64 -130
  9. package/dist/src/tokens/viking-tokens.json +9 -7
  10. package/dist/types/dataengineeringformachinelearning-viking-ui.d.ts +97 -3
  11. package/dist/viking-components.css +161 -70
  12. package/dist/viking-tokens.json +9 -7
  13. package/dist/viking-ui-elements.js +44 -32
  14. package/dist/viking-ui.css +1 -1
  15. package/dist/web-components.js +44 -32
  16. package/package.json +30 -8
  17. package/dist/scripts/build-web-components.mjs +0 -37
  18. package/dist/src/web/badge/viking-badge-wc.ts +0 -116
  19. package/dist/src/web/button/viking-button-wc.ts +0 -165
  20. package/dist/src/web/callout/viking-callout-wc.ts +0 -105
  21. package/dist/src/web/card/viking-card-wc.ts +0 -67
  22. package/dist/src/web/core/base.ts +0 -56
  23. package/dist/src/web/core/dom.ts +0 -53
  24. package/dist/src/web/core/icons-inline.ts +0 -44
  25. package/dist/src/web/core/styles.ts +0 -1261
  26. package/dist/src/web/core/types.ts +0 -19
  27. package/dist/src/web/field/viking-field-wc.ts +0 -154
  28. package/dist/src/web/index.ts +0 -73
  29. package/dist/src/web/input/viking-input-wc.ts +0 -211
  30. package/dist/src/web/modal/viking-modal-wc.ts +0 -187
  31. package/dist/src/web/search-palette/viking-search-palette-wc.ts +0 -495
  32. package/dist/src/web/select/viking-select-wc.ts +0 -218
  33. package/dist/src/web/suite-header/viking-suite-header-wc.ts +0 -729
  34. package/dist/src/web/suite-search-palette/viking-suite-search-palette-wc.ts +0 -234
  35. package/dist/src/web/theme-toggle/viking-theme-toggle-wc.ts +0 -139
  36. package/dist/src/web/types.ts +0 -37
  37. package/dist/src/web/web-components.spec.ts +0 -297
  38. package/src/assets/drakkar-favicon.svg +0 -17
  39. package/src/assets/fonts/inter/InterVariable-Italic.woff2 +0 -0
  40. package/src/assets/fonts/inter/InterVariable.woff2 +0 -0
  41. package/src/core/brand/deml-lockup.svg +0 -5
  42. package/src/core/brand/deml-mark-compact.svg +0 -4
  43. package/src/core/brand/deml-mark-filled.svg +0 -3
  44. package/src/core/brand/deml-mark-outline.svg +0 -4
  45. package/src/core/brand-icons.ts +0 -62
  46. package/src/core/icons.spec.ts +0 -52
  47. package/src/core/icons.ts +0 -206
  48. package/src/core/integration-brand-icons.ts +0 -77
  49. package/src/core/lucide-paths.generated.ts +0 -130
  50. package/src/core/types.ts +0 -85
  51. package/src/core/uid.ts +0 -7
  52. package/src/elements/button/viking-button.ts +0 -201
  53. package/src/elements/card/viking-card.ts +0 -60
  54. package/src/elements/core/dom.ts +0 -33
  55. package/src/elements/core/styles.ts +0 -28
  56. package/src/elements/modal/viking-modal.ts +0 -154
  57. package/src/index.ts +0 -1
  58. package/src/lib/accordion/accordion.ts +0 -126
  59. package/src/lib/app-header/app-header.scss +0 -274
  60. package/src/lib/app-header/app-header.ts +0 -43
  61. package/src/lib/app-sidebar/app-sidebar.scss +0 -415
  62. package/src/lib/app-sidebar/app-sidebar.ts +0 -29
  63. package/src/lib/auth-panel/auth-panel.spec.ts +0 -132
  64. package/src/lib/auth-panel/auth-panel.ts +0 -228
  65. package/src/lib/autocomplete/autocomplete.ts +0 -223
  66. package/src/lib/avatar/avatar.ts +0 -126
  67. package/src/lib/badge/badge.ts +0 -182
  68. package/src/lib/bar/bar.ts +0 -31
  69. package/src/lib/brand/brand.ts +0 -60
  70. package/src/lib/breadcrumbs/breadcrumbs.ts +0 -81
  71. package/src/lib/button/button-group.ts +0 -40
  72. package/src/lib/button/button.scss +0 -119
  73. package/src/lib/button/button.ts +0 -98
  74. package/src/lib/calendar/calendar.ts +0 -253
  75. package/src/lib/callout/callout.ts +0 -211
  76. package/src/lib/card/card-title.spec.ts +0 -27
  77. package/src/lib/card/card-title.ts +0 -50
  78. package/src/lib/card/card.ts +0 -200
  79. package/src/lib/carousel/carousel.ts +0 -206
  80. package/src/lib/chart/chart-zoom.spec.ts +0 -65
  81. package/src/lib/chart/chart-zoom.ts +0 -71
  82. package/src/lib/chart/chart.ts +0 -1257
  83. package/src/lib/chart-card-header/chart-card-header.ts +0 -80
  84. package/src/lib/chart-empty-state/chart-empty-state.ts +0 -146
  85. package/src/lib/chart-panel/chart-panel.ts +0 -133
  86. package/src/lib/checkbox/checkbox.ts +0 -139
  87. package/src/lib/color-picker/color-picker.ts +0 -148
  88. package/src/lib/command/command.ts +0 -260
  89. package/src/lib/composer/composer.ts +0 -180
  90. package/src/lib/confirm-dialog/confirm-dialog.ts +0 -163
  91. package/src/lib/confirm-dialog/dialog.service.ts +0 -79
  92. package/src/lib/context/context.ts +0 -75
  93. package/src/lib/core/brand-icons.ts +0 -62
  94. package/src/lib/core/cva.ts +0 -33
  95. package/src/lib/core/icons.ts +0 -206
  96. package/src/lib/core/integration-brand-icons.ts +0 -77
  97. package/src/lib/core/lucide-paths.generated.ts +0 -130
  98. package/src/lib/date-picker/date-picker.ts +0 -139
  99. package/src/lib/dropdown/dropdown.ts +0 -188
  100. package/src/lib/editor/editor.ts +0 -170
  101. package/src/lib/empty-state/empty-state.ts +0 -100
  102. package/src/lib/fab/fab.ts +0 -76
  103. package/src/lib/field/field.ts +0 -111
  104. package/src/lib/file-upload/file-upload.ts +0 -208
  105. package/src/lib/footer/footer.scss +0 -162
  106. package/src/lib/footer/footer.ts +0 -28
  107. package/src/lib/form-section/form-section.ts +0 -200
  108. package/src/lib/forms.spec.ts +0 -92
  109. package/src/lib/gauge-arc/gauge-arc.ts +0 -85
  110. package/src/lib/hud-panel/hud-panel.ts +0 -27
  111. package/src/lib/icon/icon.ts +0 -284
  112. package/src/lib/icon-badge/icon-badge.ts +0 -87
  113. package/src/lib/icon-heading/icon-heading.spec.ts +0 -32
  114. package/src/lib/icon-heading/icon-heading.ts +0 -38
  115. package/src/lib/icon-text/icon-text.ts +0 -120
  116. package/src/lib/input/input.ts +0 -119
  117. package/src/lib/kanban/kanban.ts +0 -211
  118. package/src/lib/kbd/kbd.ts +0 -32
  119. package/src/lib/label/label.ts +0 -39
  120. package/src/lib/loading-overlay/loading-overlay.ts +0 -80
  121. package/src/lib/menubar/menubar.ts +0 -77
  122. package/src/lib/metric-card/metric-card.ts +0 -33
  123. package/src/lib/modal/modal.ts +0 -207
  124. package/src/lib/native-select/native-select.ts +0 -124
  125. package/src/lib/navbar/navbar.ts +0 -124
  126. package/src/lib/navigation-menu/navigation-menu.ts +0 -116
  127. package/src/lib/otp-input/otp-input.ts +0 -164
  128. package/src/lib/page-back-link/page-back-link.ts +0 -74
  129. package/src/lib/page-header/page-header.ts +0 -70
  130. package/src/lib/pagination/pagination.ts +0 -157
  131. package/src/lib/pillbox/pillbox.ts +0 -157
  132. package/src/lib/popover/popover.ts +0 -92
  133. package/src/lib/profile/profile.ts +0 -73
  134. package/src/lib/progress/progress.ts +0 -123
  135. package/src/lib/radio/radio.ts +0 -132
  136. package/src/lib/ring-gauge/ring-gauge.ts +0 -75
  137. package/src/lib/scroll-area/scroll-area.ts +0 -57
  138. package/src/lib/search-palette/search-palette.scss +0 -288
  139. package/src/lib/search-palette/search-palette.ts +0 -114
  140. package/src/lib/select/select.ts +0 -292
  141. package/src/lib/separator/separator.ts +0 -59
  142. package/src/lib/sheet/sheet.ts +0 -192
  143. package/src/lib/sidebar-nav/sidebar-nav.scss +0 -218
  144. package/src/lib/sidebar-nav/sidebar-nav.ts +0 -158
  145. package/src/lib/site-drakkar/site-footer.ts +0 -133
  146. package/src/lib/site-drakkar/site-navbar.scss +0 -2
  147. package/src/lib/site-drakkar/site-navbar.ts +0 -286
  148. package/src/lib/site-drakkar/suite-search-items.spec.ts +0 -61
  149. package/src/lib/site-drakkar/suite-search-items.ts +0 -246
  150. package/src/lib/skeleton/skeleton.ts +0 -51
  151. package/src/lib/slider/slider.ts +0 -133
  152. package/src/lib/spinner/spinner.ts +0 -75
  153. package/src/lib/status-metric-row/status-metric-row.ts +0 -74
  154. package/src/lib/suite-search-palette/suite-search-palette.ts +0 -95
  155. package/src/lib/switch/switch.ts +0 -131
  156. package/src/lib/table/table.ts +0 -137
  157. package/src/lib/tabs/tab-panel.ts +0 -61
  158. package/src/lib/tabs/tab.ts +0 -107
  159. package/src/lib/tabs/tabs.ts +0 -109
  160. package/src/lib/textarea/textarea.ts +0 -103
  161. package/src/lib/theme-toggle/theme-toggle.spec.ts +0 -40
  162. package/src/lib/theme-toggle/theme-toggle.ts +0 -40
  163. package/src/lib/time-picker/time-picker.ts +0 -197
  164. package/src/lib/timeline/timeline.ts +0 -152
  165. package/src/lib/toast/toast.ts +0 -196
  166. package/src/lib/toggle/toggle-group.ts +0 -60
  167. package/src/lib/toggle/toggle.ts +0 -101
  168. package/src/lib/tooltip/tooltip.ts +0 -119
  169. package/src/lib/typography/heading.ts +0 -52
  170. package/src/lib/typography/text.ts +0 -51
  171. package/src/lib/uptime-bar/uptime-bar.ts +0 -55
  172. package/src/lib/verification-code-field/verification-code-field.ts +0 -149
  173. package/src/lib/whitepaper-cta/whitepaper-cta.scss +0 -130
  174. package/src/lib/whitepaper-cta/whitepaper-cta.ts +0 -78
  175. package/src/lib/wizard/wizard.ts +0 -80
  176. package/src/public-api.ts +0 -172
  177. package/src/styles/_buttons.scss +0 -386
  178. package/src/styles/_component-primitives.scss +0 -183
  179. package/src/styles/_drakkar-buttons.scss +0 -85
  180. package/src/styles/_fonts.scss +0 -22
  181. package/src/styles/_forms.scss +0 -66
  182. package/src/styles/_input.scss +0 -106
  183. package/src/styles/_layout-enforcement.scss +0 -229
  184. package/src/styles/_layout-rhythm.scss +0 -158
  185. package/src/styles/_legacy-aliases.scss +0 -212
  186. package/src/styles/_series-colors.scss +0 -24
  187. package/src/styles/_static-primitives.scss +0 -857
  188. package/src/styles/_typography.scss +0 -87
  189. package/src/styles/_variables.scss +0 -527
  190. package/src/styles/components/a11y.scss +0 -90
  191. package/src/styles/components/badges.scss +0 -102
  192. package/src/styles/components/extracted-utilities.scss +0 -459
  193. package/src/styles/components/footer.scss +0 -168
  194. package/src/styles/components/icon-heading.scss +0 -47
  195. package/src/styles/components/icon-inline.scss +0 -101
  196. package/src/styles/components/layout.scss +0 -59
  197. package/src/styles/components/utilities.scss +0 -131
  198. package/src/styles/components/viking-inspired.scss +0 -204
  199. package/src/styles/components/whitepaper-cta.scss +0 -130
  200. package/src/styles/components-bundle.scss +0 -9
  201. package/src/styles/components.scss +0 -442
  202. package/src/styles/deml-components.scss +0 -10
  203. package/src/styles/index.scss +0 -8
  204. package/src/styles/layout-shell.scss +0 -111
  205. package/src/styles/page-shell.scss +0 -859
  206. package/src/styles/static-navbar.scss +0 -1250
  207. package/src/styles/surfaces/backend.scss +0 -154
  208. package/src/styles/surfaces/docs-global.scss +0 -73
  209. package/src/styles/surfaces/docs-shell.scss +0 -437
  210. package/src/styles/surfaces/docs-showcase.scss +0 -1153
  211. package/src/styles/surfaces/marketing-cta.scss +0 -62
  212. package/src/styles/surfaces/marketing-docs-bento.scss +0 -67
  213. package/src/styles/surfaces/marketing-global.scss +0 -748
  214. package/src/styles/surfaces/marketing-landing.scss +0 -1526
  215. package/src/styles/surfaces/marketing-publication.scss +0 -535
  216. package/src/styles/surfaces/marketing-theme.scss +0 -14
  217. package/src/styles/surfaces/swagger-ui.scss +0 -1013
  218. package/src/styles/tokens-export.scss +0 -18
  219. package/src/styles/tokens.scss +0 -5
  220. package/src/styles/viking-ui-bundle.scss +0 -23
  221. package/src/styles/viking-ui.scss +0 -272
  222. package/src/tokens/series-presets.ts +0 -15
  223. package/src/tokens/tailwind.preset.js +0 -209
  224. package/src/tokens/viking-tokens.json +0 -318
  225. package/src/viking-ui.spec.ts +0 -416
  226. package/src/web/badge/viking-badge-wc.ts +0 -116
  227. package/src/web/button/viking-button-wc.ts +0 -165
  228. package/src/web/callout/viking-callout-wc.ts +0 -105
  229. package/src/web/card/viking-card-wc.ts +0 -67
  230. package/src/web/core/base.ts +0 -56
  231. package/src/web/core/dom.ts +0 -53
  232. package/src/web/core/icons-inline.ts +0 -44
  233. package/src/web/core/styles.ts +0 -1261
  234. package/src/web/core/types.ts +0 -19
  235. package/src/web/field/viking-field-wc.ts +0 -154
  236. package/src/web/index.ts +0 -73
  237. package/src/web/input/viking-input-wc.ts +0 -211
  238. package/src/web/modal/viking-modal-wc.ts +0 -187
  239. package/src/web/search-palette/viking-search-palette-wc.ts +0 -495
  240. package/src/web/select/viking-select-wc.ts +0 -218
  241. package/src/web/suite-header/viking-suite-header-wc.ts +0 -729
  242. package/src/web/suite-search-palette/viking-suite-search-palette-wc.ts +0 -234
  243. package/src/web/theme-toggle/viking-theme-toggle-wc.ts +0 -139
  244. package/src/web/types.ts +0 -37
  245. package/src/web/web-components.spec.ts +0 -297
  246. package/src/web-components/index.ts +0 -1
@@ -1,495 +0,0 @@
1
- import {
2
- attachShadowStyles,
3
- readBoolAttr,
4
- closeModalDialog,
5
- showModalDialog,
6
- } from "../core/base";
7
- import {
8
- defineCustomElement,
9
- defineCustomElementAlias,
10
- escapeHtml,
11
- HTMLElementBase,
12
- modKeyLabel,
13
- vikingWcUid,
14
- } from "../core/dom";
15
- import { renderInlineIcon } from "../core/icons-inline";
16
- import { VIKING_SEARCH_PALETTE_STYLES } from "../core/styles";
17
- import type { VikingSearchPaletteItem } from "../core/types";
18
-
19
- export type { VikingSearchPaletteItem };
20
-
21
- const parseItems = (el: HTMLElement): VikingSearchPaletteItem[] => {
22
- const raw = el.getAttribute("items");
23
- if (!raw) {
24
- return [];
25
- }
26
- try {
27
- const parsed = JSON.parse(raw) as VikingSearchPaletteItem[];
28
- return Array.isArray(parsed) ? parsed : [];
29
- } catch {
30
- return [];
31
- }
32
- };
33
-
34
- const matchesQuery = (
35
- item: VikingSearchPaletteItem,
36
- query: string,
37
- ): boolean => {
38
- const haystack = [
39
- item.title,
40
- item.snippet ?? "",
41
- item.group ?? "",
42
- item.href,
43
- ...(item.keywords ?? []),
44
- ]
45
- .join(" ")
46
- .toLowerCase();
47
- return haystack.includes(query);
48
- };
49
-
50
- const filterItems = (
51
- items: VikingSearchPaletteItem[],
52
- query: string,
53
- ): VikingSearchPaletteItem[] => {
54
- const q = query.trim().toLowerCase();
55
- if (!q) {
56
- return items;
57
- }
58
- return items.filter((item) => matchesQuery(item, q));
59
- };
60
-
61
- const groupItems = (
62
- items: VikingSearchPaletteItem[],
63
- ): { group: string | null; items: VikingSearchPaletteItem[] }[] => {
64
- const groups = new Map<string | null, VikingSearchPaletteItem[]>();
65
- items.forEach((item) => {
66
- const key = item.group ?? null;
67
- const bucket = groups.get(key) ?? [];
68
- bucket.push(item);
69
- groups.set(key, bucket);
70
- });
71
- return Array.from(groups.entries()).map(([group, grouped]) => ({
72
- group,
73
- items: grouped,
74
- }));
75
- };
76
-
77
- /**
78
- * Framework-agnostic command palette / search overlay Web Component.
79
- * Tag: `viking-command-palette`
80
- * Aliases: `viking-search-palette`, `viking-search-palette-wc`
81
- *
82
- * @attr open - When present, shows the palette
83
- * @attr global-shortcut - Bind ⌘K / Ctrl+K to open
84
- * @attr placeholder - Search input placeholder
85
- * @attr items - JSON array of `{ title, href, snippet?, group? }`
86
- *
87
- * @method openPalette() - Programmatically open
88
- * @method closePalette() - Programmatically close
89
- *
90
- * @event viking-close - Palette closed
91
- * @event viking-query - `{ detail: { query: string } }` on input
92
- * @event viking-select - `{ detail: { item } }` when a result is activated
93
- *
94
- * @example
95
- * <viking-command-palette
96
- * global-shortcut
97
- * items='[{"title":"Components","href":"/components","group":"Docs"}]'
98
- * ></viking-command-palette>
99
- */
100
- export class VikingSearchPaletteWc extends HTMLElementBase {
101
- static readonly tag = "viking-command-palette";
102
- static readonly searchTag = "viking-search-palette";
103
- static readonly legacyTag = "viking-search-palette-wc";
104
-
105
- static get observedAttributes(): string[] {
106
- return ["open", "placeholder", "items", "global-shortcut"];
107
- }
108
-
109
- private readonly shadow: ShadowRoot;
110
- private dialogEl: HTMLDialogElement | null = null;
111
- private inputEl: HTMLInputElement | null = null;
112
- private resultsEl: HTMLDivElement | null = null;
113
- private globalKeyHandler: ((event: KeyboardEvent) => void) | null = null;
114
- private readonly resultsId = vikingWcUid("viking-search-results");
115
- private readonly inputId = vikingWcUid("viking-search-input");
116
- private query = "";
117
- private activeIndex = 0;
118
- private flatResults: VikingSearchPaletteItem[] = [];
119
-
120
- constructor() {
121
- super();
122
- this.shadow = this.attachShadow({ mode: "open" });
123
- attachShadowStyles(this.shadow, VIKING_SEARCH_PALETTE_STYLES);
124
- }
125
-
126
- connectedCallback(): void {
127
- this.render();
128
- this.removeAttribute("open");
129
- this.syncOpen();
130
- this.bindGlobalShortcut();
131
- this.dialogEl?.addEventListener("close", this.onClose);
132
- this.dialogEl?.addEventListener("click", this.onBackdropClick);
133
- this.inputEl?.addEventListener("input", this.onInput);
134
- this.inputEl?.addEventListener("keydown", this.onInputKeydown);
135
- }
136
-
137
- disconnectedCallback(): void {
138
- this.dialogEl?.removeEventListener("close", this.onClose);
139
- this.dialogEl?.removeEventListener("click", this.onBackdropClick);
140
- this.inputEl?.removeEventListener("input", this.onInput);
141
- this.inputEl?.removeEventListener("keydown", this.onInputKeydown);
142
- this.unbindGlobalShortcut();
143
- }
144
-
145
- attributeChangedCallback(name: string): void {
146
- if (!this.isConnected) {
147
- return;
148
- }
149
- if (name === "open") {
150
- this.syncOpen();
151
- }
152
- if (name === "global-shortcut") {
153
- this.unbindGlobalShortcut();
154
- this.bindGlobalShortcut();
155
- }
156
- if (name === "items" || name === "placeholder") {
157
- if (name === "placeholder" && this.inputEl) {
158
- const placeholder =
159
- this.getAttribute("placeholder") ??
160
- "Search documentation, dashboard, API…";
161
- this.inputEl.placeholder = placeholder;
162
- this.inputEl.setAttribute("aria-label", placeholder);
163
- } else {
164
- this.renderResults();
165
- }
166
- }
167
- }
168
-
169
- /** Opens the command palette. */
170
- openPalette(): void {
171
- this.setAttribute("open", "");
172
- this.syncOpen();
173
- }
174
-
175
- /** Closes the command palette. */
176
- closePalette(): void {
177
- this.removeAttribute("open");
178
- closeModalDialog(this.dialogEl);
179
- }
180
-
181
- /** Sets the query string and re-renders filtered results. */
182
- search(query: string): void {
183
- this.query = query;
184
- if (this.inputEl) {
185
- this.inputEl.value = query;
186
- }
187
- this.activeIndex = 0;
188
- this.renderResults();
189
- this.dispatchEvent(
190
- new CustomEvent("viking-query", {
191
- bubbles: true,
192
- composed: true,
193
- detail: { query: this.query },
194
- }),
195
- );
196
- }
197
-
198
- private readonly onClose = (): void => {
199
- this.removeAttribute("open");
200
- this.query = "";
201
- this.activeIndex = 0;
202
- if (this.inputEl) {
203
- this.inputEl.value = "";
204
- }
205
- this.dispatchEvent(
206
- new CustomEvent("viking-close", { bubbles: true, composed: true }),
207
- );
208
- };
209
-
210
- private readonly onBackdropClick = (event: MouseEvent): void => {
211
- if (event.target === this.dialogEl) {
212
- this.closePalette();
213
- }
214
- };
215
-
216
- private readonly onInput = (event: Event): void => {
217
- this.query =
218
- (event.target as HTMLInputElement).value ?? this.inputEl?.value ?? "";
219
- this.activeIndex = 0;
220
- this.renderResults();
221
- this.dispatchEvent(
222
- new CustomEvent("viking-query", {
223
- bubbles: true,
224
- composed: true,
225
- detail: { query: this.query },
226
- }),
227
- );
228
- };
229
-
230
- private readonly onInputKeydown = (event: KeyboardEvent): void => {
231
- if (this.flatResults.length === 0) {
232
- if (event.key === "Escape") {
233
- event.preventDefault();
234
- this.closePalette();
235
- }
236
- return;
237
- }
238
-
239
- if (event.key === "ArrowDown") {
240
- event.preventDefault();
241
- this.activeIndex = Math.min(
242
- this.flatResults.length - 1,
243
- this.activeIndex + 1,
244
- );
245
- this.renderResults();
246
- this.scrollActiveIntoView();
247
- return;
248
- }
249
-
250
- if (event.key === "ArrowUp") {
251
- event.preventDefault();
252
- this.activeIndex = Math.max(0, this.activeIndex - 1);
253
- this.renderResults();
254
- this.scrollActiveIntoView();
255
- return;
256
- }
257
-
258
- if (event.key === "Enter") {
259
- event.preventDefault();
260
- const item = this.flatResults[this.activeIndex];
261
- if (item) {
262
- this.activateItem(item);
263
- }
264
- }
265
- };
266
-
267
- private bindGlobalShortcut(): void {
268
- if (!readBoolAttr(this, "global-shortcut")) {
269
- return;
270
- }
271
- this.globalKeyHandler = (event: KeyboardEvent) => {
272
- if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
273
- event.preventDefault();
274
- if (this.hasAttribute("open")) {
275
- this.closePalette();
276
- } else {
277
- this.openPalette();
278
- }
279
- }
280
- };
281
- document.addEventListener("keydown", this.globalKeyHandler);
282
- }
283
-
284
- private unbindGlobalShortcut(): void {
285
- if (this.globalKeyHandler) {
286
- document.removeEventListener("keydown", this.globalKeyHandler);
287
- this.globalKeyHandler = null;
288
- }
289
- }
290
-
291
- private syncOpen(): void {
292
- if (!this.dialogEl) {
293
- return;
294
- }
295
- const shouldOpen = this.hasAttribute("open");
296
- if (shouldOpen && !this.dialogEl.open) {
297
- this.dialogEl.removeAttribute("aria-hidden");
298
- showModalDialog(this.dialogEl);
299
- this.activeIndex = 0;
300
- this.renderResults();
301
- queueMicrotask(() => this.inputEl?.focus());
302
- } else if (!shouldOpen && this.dialogEl.open) {
303
- closeModalDialog(this.dialogEl);
304
- this.dialogEl.setAttribute("aria-hidden", "true");
305
- } else if (!shouldOpen) {
306
- this.dialogEl.setAttribute("aria-hidden", "true");
307
- }
308
- }
309
-
310
- private scrollActiveIntoView(): void {
311
- const active = this.resultsEl?.querySelector(
312
- ".viking-search-result.is-selected",
313
- );
314
- active?.scrollIntoView({ block: "nearest" });
315
- }
316
-
317
- private activateItem(item: VikingSearchPaletteItem): void {
318
- this.dispatchEvent(
319
- new CustomEvent("viking-select", {
320
- bubbles: true,
321
- composed: true,
322
- detail: { item },
323
- }),
324
- );
325
- this.closePalette();
326
-
327
- if (item.action === "cookie-settings") {
328
- const widgets = (
329
- globalThis as { DemlWidgets?: { openCookieSettings?: () => void } }
330
- ).DemlWidgets;
331
- widgets?.openCookieSettings?.();
332
- return;
333
- }
334
-
335
- if (item.action === "bug-report") {
336
- const widgets = (
337
- globalThis as { DemlWidgets?: { openBugReport?: () => void } }
338
- ).DemlWidgets;
339
- if (widgets?.openBugReport) {
340
- widgets.openBugReport();
341
- return;
342
- }
343
- }
344
-
345
- if (item.href && item.href !== "#") {
346
- window.location.assign(item.href);
347
- }
348
- }
349
-
350
- private renderResults(): void {
351
- if (!this.resultsEl) {
352
- return;
353
- }
354
-
355
- const items = parseItems(this);
356
- const q = this.query.trim().toLowerCase();
357
- const filtered = filterItems(items, q);
358
- this.flatResults = filtered;
359
-
360
- if (filtered.length === 0) {
361
- const q = this.query.trim();
362
- this.resultsEl.innerHTML = `<p class="viking-search-empty" role="status">${q ? "No results found" : "Start typing to search…"}</p>`;
363
- this.inputEl?.removeAttribute("aria-activedescendant");
364
- return;
365
- }
366
-
367
- if (this.activeIndex >= filtered.length) {
368
- this.activeIndex = filtered.length - 1;
369
- }
370
-
371
- let resultIndex = 0;
372
- const grouped = groupItems(filtered);
373
-
374
- const markup = grouped
375
- .map(({ group, items: groupItemsList }) => {
376
- const groupLabel = group
377
- ? `<p class="viking-search-group-label" role="presentation">${escapeHtml(group)}</p>`
378
- : "";
379
- const rows = groupItemsList
380
- .map((item) => {
381
- const id = `${this.resultsId}-result-${resultIndex}`;
382
- const selected = resultIndex === this.activeIndex;
383
- resultIndex += 1;
384
- return `
385
- <a
386
- id="${id}"
387
- class="viking-search-result${selected ? " is-selected" : ""}"
388
- role="option"
389
- aria-selected="${selected}"
390
- href="${escapeHtml(item.href)}"
391
- part="result"
392
- data-index="${resultIndex - 1}"
393
- >
394
- <div>
395
- <div class="viking-search-result-title">${escapeHtml(item.title)}</div>
396
- ${item.snippet ? `<div class="viking-search-result-snippet">${escapeHtml(item.snippet)}</div>` : ""}
397
- </div>
398
- </a>`;
399
- })
400
- .join("");
401
- return `${groupLabel}${rows}`;
402
- })
403
- .join("");
404
-
405
- this.resultsEl.innerHTML = `<div class="viking-search-results" id="${this.resultsId}" role="listbox" aria-label="Search results">${markup}</div>`;
406
-
407
- const activeId = `${this.resultsId}-result-${this.activeIndex}`;
408
- this.inputEl?.setAttribute("aria-activedescendant", activeId);
409
- this.inputEl?.setAttribute("role", "combobox");
410
- this.inputEl?.setAttribute("aria-expanded", "true");
411
- this.inputEl?.setAttribute("aria-controls", this.resultsId);
412
-
413
- this.resultsEl.querySelectorAll(".viking-search-result").forEach((node) => {
414
- node.addEventListener("click", (event) => {
415
- event.preventDefault();
416
- const index = Number((node as HTMLElement).dataset["index"] ?? 0);
417
- const item = this.flatResults[index];
418
- if (item) {
419
- this.activateItem(item);
420
- }
421
- });
422
- node.addEventListener("mouseenter", () => {
423
- const index = Number((node as HTMLElement).dataset["index"] ?? 0);
424
- this.activeIndex = index;
425
- this.renderResults();
426
- });
427
- });
428
- }
429
-
430
- private render(): void {
431
- const placeholder =
432
- this.getAttribute("placeholder") ??
433
- "Search documentation, dashboard, API…";
434
- const mod = modKeyLabel();
435
-
436
- this.shadow.innerHTML = `
437
- <dialog class="viking-search-palette-backdrop" aria-label="Search" aria-hidden="true">
438
- <div class="viking-search-palette" part="panel" role="document">
439
- <div class="viking-search-palette-header" part="header">
440
- <span class="viking-search-palette-icon" aria-hidden="true">${renderInlineIcon("search", 24)}</span>
441
- <input
442
- id="${this.inputId}"
443
- type="search"
444
- class="viking-search-palette-input"
445
- part="input"
446
- placeholder="${escapeHtml(placeholder)}"
447
- aria-label="${escapeHtml(placeholder)}"
448
- aria-autocomplete="list"
449
- autocomplete="off"
450
- spellcheck="false"
451
- />
452
- <button type="button" class="viking-search-palette-close" part="close" aria-label="Close search">${renderInlineIcon("x", 20)}</button>
453
- </div>
454
- <div class="viking-search-palette-body" part="body">
455
- <slot></slot>
456
- <div class="viking-search-results-host"></div>
457
- </div>
458
- <footer class="viking-search-palette-footer" part="footer">
459
- <span class="viking-kbd">${mod}</span><span class="viking-kbd">K</span> toggle ·
460
- <span class="viking-kbd">↑</span><span class="viking-kbd">↓</span> navigate ·
461
- <span class="viking-kbd">Enter</span> open ·
462
- <span class="viking-kbd">Esc</span> close
463
- </footer>
464
- </div>
465
- </dialog>
466
- `;
467
-
468
- this.dialogEl = this.shadow.querySelector("dialog");
469
- this.inputEl = this.shadow.querySelector("input");
470
- this.resultsEl = this.shadow.querySelector(".viking-search-results-host");
471
-
472
- this.shadow
473
- .querySelector(".viking-search-palette-close")
474
- ?.addEventListener("click", () => this.closePalette());
475
-
476
- this.dialogEl?.addEventListener("keydown", (event) => {
477
- if (event.key === "Escape") {
478
- event.preventDefault();
479
- this.closePalette();
480
- }
481
- });
482
- }
483
- }
484
-
485
- export const registerVikingSearchPaletteWc = (): void => {
486
- defineCustomElement(VikingSearchPaletteWc.tag, VikingSearchPaletteWc);
487
- defineCustomElementAlias(
488
- VikingSearchPaletteWc.searchTag,
489
- VikingSearchPaletteWc,
490
- );
491
- defineCustomElementAlias(
492
- VikingSearchPaletteWc.legacyTag,
493
- VikingSearchPaletteWc,
494
- );
495
- };
@@ -1,218 +0,0 @@
1
- import {
2
- attachElementInternals,
3
- attachShadowStyles,
4
- setFormValue,
5
- } from "../core/base";
6
- import {
7
- defineCustomElement,
8
- defineCustomElementAlias,
9
- escapeHtml,
10
- HTMLElementBase,
11
- vikingWcUid,
12
- } from "../core/dom";
13
- import { VIKING_SELECT_STYLES } from "../core/styles";
14
-
15
- /**
16
- * Framework-agnostic native select Web Component with form association.
17
- * Tag: `viking-select` (legacy alias: `viking-select-wc`)
18
- *
19
- * Place `<option>` elements as light DOM children; they are mirrored into the shadow select.
20
- *
21
- * @attr label - Visible field label
22
- * @attr name - Form field name (participates in form submission via ElementInternals)
23
- * @attr value - Selected option value
24
- * @attr placeholder - Placeholder option text when no value is selected
25
- * @attr description - Helper text below the control
26
- * @attr error - Validation message; sets aria-invalid
27
- * @attr width - full | half (default: half)
28
- * @attr disabled - Disables the control
29
- * @attr required - Marks field as required
30
- *
31
- * @event viking-change - `{ detail: { value: string } }`
32
- *
33
- * @example
34
- * <viking-select-wc label="Retention" name="retention" value="30d">
35
- * <option value="7d">7 days</option>
36
- * <option value="30d">30 days</option>
37
- * </viking-select-wc>
38
- */
39
- export class VikingSelectWc extends HTMLElementBase {
40
- static readonly formAssociated = true;
41
- static readonly tag = "viking-select";
42
- static readonly legacyTag = "viking-select-wc";
43
-
44
- static get observedAttributes(): string[] {
45
- return [
46
- "label",
47
- "name",
48
- "value",
49
- "placeholder",
50
- "description",
51
- "error",
52
- "width",
53
- "disabled",
54
- "required",
55
- ];
56
- }
57
-
58
- private readonly shadow: ShadowRoot;
59
- private readonly internals: ElementInternals | null;
60
- private selectEl: HTMLSelectElement | null = null;
61
- private optionObserver: MutationObserver | null = null;
62
- private readonly controlId = vikingWcUid("viking-select");
63
-
64
- constructor() {
65
- super();
66
- this.shadow = this.attachShadow({ mode: "open" });
67
- this.internals = attachElementInternals(this);
68
- attachShadowStyles(this.shadow, VIKING_SELECT_STYLES);
69
- }
70
-
71
- connectedCallback(): void {
72
- this.render();
73
- this.syncOptions();
74
- this.observeOptions();
75
- this.selectEl?.addEventListener("change", this.onChange);
76
- }
77
-
78
- disconnectedCallback(): void {
79
- this.selectEl?.removeEventListener("change", this.onChange);
80
- this.optionObserver?.disconnect();
81
- this.optionObserver = null;
82
- }
83
-
84
- attributeChangedCallback(name: string): void {
85
- if (!this.isConnected) {
86
- return;
87
- }
88
- if (name === "value" && this.selectEl) {
89
- this.selectEl.value = this.getAttribute("value") ?? "";
90
- this.syncFormValue();
91
- return;
92
- }
93
- if (name === "error" || name === "description" || name === "label") {
94
- this.render();
95
- this.syncOptions();
96
- return;
97
- }
98
- this.render();
99
- this.syncOptions();
100
- }
101
-
102
- get value(): string {
103
- return this.selectEl?.value ?? this.getAttribute("value") ?? "";
104
- }
105
-
106
- set value(next: string) {
107
- this.setAttribute("value", next);
108
- if (this.selectEl) {
109
- this.selectEl.value = next;
110
- }
111
- this.syncFormValue();
112
- }
113
-
114
- private readonly onChange = (): void => {
115
- const value = this.selectEl?.value ?? "";
116
- this.setAttribute("value", value);
117
- this.syncFormValue();
118
- this.dispatchEvent(
119
- new CustomEvent("viking-change", {
120
- bubbles: true,
121
- composed: true,
122
- detail: { value },
123
- }),
124
- );
125
- };
126
-
127
- private syncFormValue(): void {
128
- setFormValue(this.internals, this.value);
129
- }
130
-
131
- private observeOptions(): void {
132
- this.optionObserver?.disconnect();
133
- this.optionObserver = new MutationObserver(() => this.syncOptions());
134
- this.optionObserver.observe(this, {
135
- childList: true,
136
- subtree: true,
137
- characterData: true,
138
- });
139
- }
140
-
141
- private render(): void {
142
- const label = this.getAttribute("label") ?? "";
143
- const name = this.getAttribute("name") ?? "";
144
- const disabled = this.hasAttribute("disabled");
145
- const required = this.hasAttribute("required");
146
- const error = this.getAttribute("error") ?? "";
147
- const description = this.getAttribute("description") ?? "";
148
- const describedBy = [
149
- description && `${this.controlId}-desc`,
150
- error && `${this.controlId}-error`,
151
- ]
152
- .filter(Boolean)
153
- .join(" ");
154
-
155
- this.shadow.innerHTML = `
156
- <div class="viking-field" part="field">
157
- ${label ? `<label class="viking-field-label" part="label" for="${this.controlId}">${escapeHtml(label)}</label>` : ""}
158
- <select
159
- id="${this.controlId}"
160
- class="viking-select-native"
161
- part="control"
162
- ${name ? `name="${escapeHtml(name)}"` : ""}
163
- ${disabled ? "disabled" : ""}
164
- ${required ? "required" : ""}
165
- ${error ? 'aria-invalid="true"' : ""}
166
- ${describedBy ? `aria-describedby="${describedBy}"` : ""}
167
- ></select>
168
- ${description ? `<p id="${this.controlId}-desc" class="viking-field-description" part="description">${escapeHtml(description)}</p>` : ""}
169
- ${error ? `<p id="${this.controlId}-error" class="viking-field-error" part="error" role="alert">${escapeHtml(error)}</p>` : ""}
170
- </div>
171
- `;
172
-
173
- this.selectEl = this.shadow.querySelector("select");
174
- const value = this.getAttribute("value");
175
- if (this.selectEl && value) {
176
- this.selectEl.value = value;
177
- }
178
- this.syncFormValue();
179
- }
180
-
181
- private syncOptions(): void {
182
- if (!this.selectEl) {
183
- return;
184
- }
185
-
186
- const currentValue = this.selectEl.value;
187
- this.selectEl.innerHTML = "";
188
- const options = this.querySelectorAll("option");
189
-
190
- if (options.length === 0) {
191
- const placeholder = this.getAttribute("placeholder") ?? "Select…";
192
- const opt = document.createElement("option");
193
- opt.value = "";
194
- opt.textContent = placeholder;
195
- opt.disabled = true;
196
- opt.selected = !this.getAttribute("value");
197
- this.selectEl.append(opt);
198
- return;
199
- }
200
-
201
- options.forEach((opt) => {
202
- this.selectEl?.append(opt.cloneNode(true));
203
- });
204
-
205
- const value = this.getAttribute("value");
206
- if (value) {
207
- this.selectEl.value = value;
208
- } else if (currentValue) {
209
- this.selectEl.value = currentValue;
210
- }
211
- this.syncFormValue();
212
- }
213
- }
214
-
215
- export const registerVikingSelectWc = (): void => {
216
- defineCustomElement(VikingSelectWc.tag, VikingSelectWc);
217
- defineCustomElementAlias(VikingSelectWc.legacyTag, VikingSelectWc);
218
- };