@ds-gn/ui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +4 -4
  2. package/custom-elements.json +5527 -2136
  3. package/dist/common/controllers/OverlayDismissController.d.ts +52 -0
  4. package/dist/common/controllers/OverlayPositionController.d.ts +168 -0
  5. package/dist/common/controllers/RovingFocusController.d.ts +99 -0
  6. package/dist/common/controllers/SectionEntranceController.d.ts +37 -0
  7. package/dist/common/helpers/field-control.d.ts +35 -0
  8. package/dist/common/helpers/listbox-navigation.d.ts +50 -0
  9. package/dist/common/props/size/size.props.d.ts +11 -0
  10. package/dist/common/styles/overlay-panel.styles.d.ts +55 -0
  11. package/dist/components/Button/src/Button.d.ts +20 -0
  12. package/dist/components/Button/src/Button.js +28 -0
  13. package/dist/components/Container/src/Container.d.ts +10 -0
  14. package/dist/components/Container/src/Container.js +148 -23
  15. package/dist/components/DropdownMenu/src/DropdownMenu.d.ts +150 -0
  16. package/dist/components/DropdownMenu/src/DropdownMenu.js +1550 -0
  17. package/dist/components/DropdownMenu/src/DropdownMenu.playground.d.ts +2 -0
  18. package/dist/components/DropdownMenu/src/DropdownMenu.props.d.ts +47 -0
  19. package/dist/components/DropdownMenu/src/DropdownMenu.props.js +54 -0
  20. package/dist/components/DropdownMenu/src/DropdownMenu.styles.d.ts +20 -0
  21. package/dist/components/Field/src/Field.d.ts +39 -0
  22. package/dist/components/Field/src/Field.js +203 -0
  23. package/dist/components/Field/src/Field.playground.d.ts +2 -0
  24. package/dist/components/Field/src/Field.styles.d.ts +6 -0
  25. package/dist/components/Icon/src/Icon.d.ts +5 -0
  26. package/dist/components/Icon/src/Icon.js +3 -0
  27. package/dist/components/Icon/src/icons/arrow-down.d.ts +1 -0
  28. package/dist/components/Icon/src/icons/arrow-down.js +6 -0
  29. package/dist/components/Icon/src/icons/arrow-up.d.ts +1 -0
  30. package/dist/components/Icon/src/icons/arrow-up.js +6 -0
  31. package/dist/components/Icon/src/icons/chevrons-up-down.d.ts +1 -0
  32. package/dist/components/Icon/src/icons/chevrons-up-down.js +6 -0
  33. package/dist/components/Icon/src/icons/cookie.d.ts +1 -0
  34. package/dist/components/Icon/src/icons/cookie.js +6 -0
  35. package/dist/components/Icon/src/icons/icon-names.d.ts +1 -1
  36. package/dist/components/Icon/src/icons/icon-names.js +4 -0
  37. package/dist/components/Icon/src/icons/index.d.ts +4 -0
  38. package/dist/components/Icon/src/icons/index.js +4 -0
  39. package/dist/components/Input/src/Input.d.ts +37 -0
  40. package/dist/components/Input/src/Input.js +300 -143
  41. package/dist/components/Input/src/Input.props.d.ts +2 -1
  42. package/dist/components/Input/src/Input.props.js +12 -6
  43. package/dist/components/Input/src/Input.styles.d.ts +12 -4
  44. package/dist/components/Modal/src/Modal.d.ts +1 -0
  45. package/dist/components/Modal/src/Modal.js +1 -0
  46. package/dist/components/Nav/src/Nav.js +2 -1
  47. package/dist/components/NavLink/src/NavLink.d.ts +14 -30
  48. package/dist/components/NavLink/src/NavLink.js +201 -65
  49. package/dist/components/Select/src/Select.d.ts +157 -0
  50. package/dist/components/Select/src/Select.js +1766 -0
  51. package/dist/components/Select/src/Select.playground.d.ts +2 -0
  52. package/dist/components/Select/src/Select.props.d.ts +19 -0
  53. package/dist/components/Select/src/Select.props.js +40 -0
  54. package/dist/components/Select/src/Select.styles.d.ts +30 -0
  55. package/dist/components/Table/src/Table.d.ts +93 -0
  56. package/dist/components/Table/src/Table.js +1105 -0
  57. package/dist/components/Table/src/Table.playground.d.ts +2 -0
  58. package/dist/components/Table/src/Table.props.d.ts +53 -0
  59. package/dist/components/Table/src/Table.props.js +18 -0
  60. package/dist/components/Table/src/Table.styles.d.ts +33 -0
  61. package/dist/components/Toaster/src/Toaster.d.ts +174 -0
  62. package/dist/components/Toaster/src/Toaster.js +1489 -0
  63. package/dist/components/Toaster/src/Toaster.playground.d.ts +2 -0
  64. package/dist/components/Toaster/src/Toaster.props.d.ts +14 -0
  65. package/dist/components/Toaster/src/Toaster.props.js +81 -0
  66. package/dist/components/Toaster/src/Toaster.styles.d.ts +52 -0
  67. package/dist/components/Toaster/src/toast.d.ts +115 -0
  68. package/dist/components/Toaster/src/toast.js +74 -0
  69. package/dist/playground/types.d.ts +27 -0
  70. package/dist/playground-manifest.d.ts +3 -2
  71. package/dist/playground-manifest.js +507 -7
  72. package/dist/react/DropdownMenu.d.ts +8 -0
  73. package/dist/react/DropdownMenu.js +13 -0
  74. package/dist/react/Field.d.ts +6 -0
  75. package/dist/react/Field.js +13 -0
  76. package/dist/react/Select.d.ts +8 -0
  77. package/dist/react/Select.js +13 -0
  78. package/dist/react/Table.d.ts +8 -0
  79. package/dist/react/Table.js +13 -0
  80. package/dist/react/Toaster.d.ts +8 -0
  81. package/dist/react/Toaster.js +13 -0
  82. package/dist/react/index.d.ts +6 -0
  83. package/dist/react/index.js +6 -0
  84. package/package.json +52 -2
@@ -0,0 +1,1550 @@
1
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
4
+ r = Reflect.decorate(decorators, target, key, desc);
5
+ else
6
+ for (var i = decorators.length - 1;i >= 0; i--)
7
+ if (d = decorators[i])
8
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+
12
+ // components/DropdownMenu/src/DropdownMenu.ts
13
+ import { MotionController } from "@ds-gn/motion/lit";
14
+ import { html as html2, nothing } from "lit";
15
+ import { property, query, state } from "lit/decorators.js";
16
+
17
+ // common/Base/Base.ts
18
+ import { html, LitElement } from "lit";
19
+
20
+ // common/helpers/define-element.ts
21
+ var defineElement = (tag, ctor) => {
22
+ if (typeof customElements === "undefined")
23
+ return;
24
+ if (!customElements.get(tag))
25
+ customElements.define(tag, ctor);
26
+ };
27
+
28
+ // common/Base/Base.css
29
+ import { css } from "lit";
30
+ var styles = css`/* Base reset in @layer base ensures it comes before utilities in Tailwind v4 cascade
31
+ This prevents the reset from overriding component utility classes */
32
+ @layer base {
33
+ :host {
34
+ display: block;
35
+ box-sizing: border-box;
36
+ line-height: 1.5;
37
+ -webkit-text-size-adjust: 100%;
38
+ tab-size: 4;
39
+ font-family: var(
40
+ --default-font-family,
41
+ var(--font-sans, ui-sans-serif, system-ui, sans-serif)
42
+ );
43
+ }
44
+
45
+ *,
46
+ *::before,
47
+ *::after {
48
+ box-sizing: inherit;
49
+ margin: 0;
50
+ padding: 0;
51
+ border: 0 solid;
52
+ }
53
+
54
+ :host h1,
55
+ :host h2,
56
+ :host h3,
57
+ :host h4,
58
+ :host h5,
59
+ :host h6 {
60
+ font-size: inherit;
61
+ font-weight: inherit;
62
+ margin: 0;
63
+ padding: 0;
64
+ }
65
+ }
66
+ `;
67
+ var Base_default = styles;
68
+
69
+ // common/Base/Base.ts
70
+ class Base extends LitElement {
71
+ static styles = Base_default;
72
+ updateTheme(changedProperties, surface) {
73
+ if (changedProperties.has("surface")) {
74
+ if (surface) {
75
+ this.setAttribute("data-theme", surface);
76
+ } else {
77
+ this.removeAttribute("data-theme");
78
+ }
79
+ }
80
+ }
81
+ emit(name, detail, options = {}) {
82
+ return this.dispatchEvent(new CustomEvent(name, {
83
+ detail,
84
+ bubbles: true,
85
+ composed: true,
86
+ cancelable: options.cancelable ?? false
87
+ }));
88
+ }
89
+ render() {
90
+ return html`<slot></slot>`;
91
+ }
92
+ }
93
+ defineElement("dsgn-base", Base);
94
+
95
+ // common/controllers/OverlayDismissController.ts
96
+ class OverlayDismissController {
97
+ constructor(host, options) {
98
+ this.listening = false;
99
+ this.wantsOutside = false;
100
+ this.handleDocumentPointerDown = (event) => {
101
+ if (!event.composedPath().includes(this.host)) {
102
+ this.options.onDismiss("outside");
103
+ }
104
+ };
105
+ this.host = host;
106
+ this.options = options;
107
+ host.addController(this);
108
+ }
109
+ start() {
110
+ this.wantsOutside = true;
111
+ this.listen();
112
+ }
113
+ stop() {
114
+ this.wantsOutside = false;
115
+ this.unlisten();
116
+ }
117
+ handleKeydown(event) {
118
+ if (event.key !== "Escape" || !this.options.isOpen()) {
119
+ return false;
120
+ }
121
+ this.options.onDismiss("escape");
122
+ this.options.returnFocusTo?.()?.focus();
123
+ return true;
124
+ }
125
+ hostConnected() {
126
+ if (this.wantsOutside) {
127
+ this.listen();
128
+ }
129
+ }
130
+ hostDisconnected() {
131
+ this.unlisten();
132
+ }
133
+ listen() {
134
+ if (this.listening || typeof document === "undefined") {
135
+ return;
136
+ }
137
+ this.listening = true;
138
+ document.addEventListener("pointerdown", this.handleDocumentPointerDown);
139
+ }
140
+ unlisten() {
141
+ if (!this.listening) {
142
+ return;
143
+ }
144
+ this.listening = false;
145
+ document.removeEventListener("pointerdown", this.handleDocumentPointerDown);
146
+ }
147
+ }
148
+
149
+ // common/helpers/layout-change.ts
150
+ var listenersByTarget = new Map;
151
+ var observer;
152
+ var lastDocumentWidth;
153
+ var handleEntries = (entries) => {
154
+ const notify = new Set;
155
+ for (const entry of entries) {
156
+ if (entry.target !== document.documentElement) {
157
+ for (const listener of listenersByTarget.get(entry.target) ?? []) {
158
+ notify.add(listener);
159
+ }
160
+ continue;
161
+ }
162
+ const { width } = entry.contentRect;
163
+ if (width === lastDocumentWidth) {
164
+ continue;
165
+ }
166
+ lastDocumentWidth = width;
167
+ for (const listeners of listenersByTarget.values()) {
168
+ for (const listener of listeners) {
169
+ notify.add(listener);
170
+ }
171
+ }
172
+ }
173
+ for (const listener of notify) {
174
+ listener();
175
+ }
176
+ };
177
+ var observeLayoutChanges = (target, listener) => {
178
+ if (typeof ResizeObserver === "undefined" || typeof document === "undefined") {
179
+ return () => {};
180
+ }
181
+ if (!observer) {
182
+ lastDocumentWidth = undefined;
183
+ observer = new ResizeObserver(handleEntries);
184
+ observer.observe(document.documentElement);
185
+ }
186
+ let listeners = listenersByTarget.get(target);
187
+ if (!listeners) {
188
+ listeners = new Set;
189
+ listenersByTarget.set(target, listeners);
190
+ observer.observe(target);
191
+ }
192
+ listeners.add(listener);
193
+ return () => {
194
+ const current = listenersByTarget.get(target);
195
+ if (!current?.delete(listener)) {
196
+ return;
197
+ }
198
+ if (current.size === 0) {
199
+ listenersByTarget.delete(target);
200
+ observer?.unobserve(target);
201
+ }
202
+ if (listenersByTarget.size === 0) {
203
+ observer?.disconnect();
204
+ observer = undefined;
205
+ lastDocumentWidth = undefined;
206
+ }
207
+ };
208
+ };
209
+
210
+ // common/controllers/OverlayPositionController.ts
211
+ var overlayViewportGutter = 16;
212
+ var computeOverlayAvailableHeight = (viewport, viewportHeight, gutter = overlayViewportGutter) => Math.max(0, viewportHeight - (viewport.top ?? viewport.bottom ?? 0) - gutter);
213
+ var computeViewportPosition = (anchor, { placement, shift }, viewportHeight, offset = 0) => {
214
+ const below = placement.startsWith("bottom");
215
+ return {
216
+ left: anchor.left + shift,
217
+ top: below ? anchor.bottom + offset : null,
218
+ bottom: below ? null : viewportHeight - anchor.top + offset,
219
+ anchorWidth: anchor.width
220
+ };
221
+ };
222
+ var computeOverlayShift = (anchor, overlayWidth, viewportWidth, gutter = overlayViewportGutter, align = "start") => {
223
+ const viewportRight = viewportWidth - gutter;
224
+ const startFits = anchor.left + overlayWidth <= viewportRight;
225
+ const endAligned = anchor.width - overlayWidth;
226
+ const endFits = anchor.left + endAligned >= gutter;
227
+ if (!overlayWidth) {
228
+ return 0;
229
+ }
230
+ if (align === "end") {
231
+ if (endFits)
232
+ return endAligned;
233
+ return startFits ? 0 : viewportRight - overlayWidth - anchor.left;
234
+ }
235
+ if (startFits)
236
+ return 0;
237
+ return endFits ? endAligned : gutter - anchor.left;
238
+ };
239
+ var computeOverlayPlacement = (anchor, overlayHeight, viewportHeight, {
240
+ gutter = overlayViewportGutter,
241
+ offset = 0,
242
+ align = "start"
243
+ } = {}) => {
244
+ const below = viewportHeight - gutter - anchor.bottom - offset;
245
+ const above = anchor.top - gutter - offset;
246
+ const flips = !!overlayHeight && overlayHeight > below && (overlayHeight <= above || above > below);
247
+ return `${flips ? "top" : "bottom"}-${align}`;
248
+ };
249
+ var sizeOf = (overlay) => {
250
+ if (overlay.offsetWidth) {
251
+ return { width: overlay.offsetWidth, height: overlay.offsetHeight };
252
+ }
253
+ const { width, height } = overlay.getBoundingClientRect();
254
+ return { width, height };
255
+ };
256
+ var measureOverlay = (overlay) => {
257
+ const open = sizeOf(overlay);
258
+ if (open.width) {
259
+ return open;
260
+ }
261
+ const inline = overlay.style.cssText;
262
+ overlay.style.display = "block";
263
+ overlay.style.visibility = "hidden";
264
+ const size = sizeOf(overlay);
265
+ overlay.style.cssText = inline;
266
+ return size;
267
+ };
268
+
269
+ class OverlayPositionController {
270
+ constructor(host, options) {
271
+ this.wantsUpdates = false;
272
+ this.update = () => {
273
+ const anchor = this.options.anchor?.() ?? this.host;
274
+ const overlay = this.options.overlay();
275
+ if (!anchor || !overlay) {
276
+ return;
277
+ }
278
+ const anchorRect = anchor.getBoundingClientRect();
279
+ this.options.beforeMeasure?.(anchorRect);
280
+ const { width, height } = measureOverlay(overlay);
281
+ const gutter = this.options.gutter ?? overlayViewportGutter;
282
+ const offset = this.options.offset ?? 0;
283
+ const align = this.options.align?.() ?? "start";
284
+ const shift = computeOverlayShift(anchorRect, width, window.innerWidth, gutter, align);
285
+ const placement = this.options.flip ? computeOverlayPlacement(anchorRect, height, window.innerHeight, {
286
+ gutter,
287
+ offset,
288
+ align
289
+ }) : `bottom-${align}`;
290
+ this._position = {
291
+ shift,
292
+ placement,
293
+ viewport: computeViewportPosition(anchorRect, { placement, shift }, window.innerHeight, offset)
294
+ };
295
+ this.options.apply(this._position);
296
+ };
297
+ this.scheduleUpdate = () => {
298
+ if (this.frame !== undefined) {
299
+ return;
300
+ }
301
+ this.frame = requestAnimationFrame(() => {
302
+ this.frame = undefined;
303
+ this.update();
304
+ });
305
+ };
306
+ this.host = host;
307
+ this.options = options;
308
+ host.addController(this);
309
+ }
310
+ get position() {
311
+ return this._position;
312
+ }
313
+ start() {
314
+ this.wantsUpdates = true;
315
+ this.subscribe();
316
+ this.update();
317
+ }
318
+ stop() {
319
+ this.wantsUpdates = false;
320
+ this.unsubscribeAll();
321
+ this._position = undefined;
322
+ this.options.clear?.();
323
+ }
324
+ hostConnected() {
325
+ if (this.wantsUpdates) {
326
+ this.subscribe();
327
+ }
328
+ }
329
+ hostDisconnected() {
330
+ this.unsubscribeAll();
331
+ }
332
+ subscribe() {
333
+ if (this.unsubscribe) {
334
+ return;
335
+ }
336
+ const stopLayout = observeLayoutChanges(this.host, this.update);
337
+ if (!this.options.trackViewport || typeof window === "undefined") {
338
+ this.unsubscribe = stopLayout;
339
+ return;
340
+ }
341
+ const listenerOptions = { capture: true, passive: true };
342
+ window.addEventListener("scroll", this.scheduleUpdate, listenerOptions);
343
+ window.addEventListener("resize", this.scheduleUpdate, listenerOptions);
344
+ this.unsubscribe = () => {
345
+ stopLayout();
346
+ window.removeEventListener("scroll", this.scheduleUpdate, true);
347
+ window.removeEventListener("resize", this.scheduleUpdate, true);
348
+ };
349
+ }
350
+ unsubscribeAll() {
351
+ this.unsubscribe?.();
352
+ this.unsubscribe = undefined;
353
+ if (this.frame !== undefined) {
354
+ cancelAnimationFrame(this.frame);
355
+ this.frame = undefined;
356
+ }
357
+ }
358
+ }
359
+
360
+ // common/helpers/listbox-navigation.ts
361
+ var isPrintableKey = (event) => event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
362
+ var typeaheadTimeout = 500;
363
+
364
+ class TypeaheadBuffer {
365
+ constructor(timeout = typeaheadTimeout) {
366
+ this.timeout = timeout;
367
+ this.buffer = "";
368
+ }
369
+ get active() {
370
+ return this.buffer.length > 0;
371
+ }
372
+ search(items, char, from, labelOf, { cycle = true } = {}) {
373
+ this.buffer += char.toLowerCase();
374
+ clearTimeout(this.timer);
375
+ this.timer = setTimeout(() => this.clear(), this.timeout);
376
+ const repeated = [...this.buffer].every((key) => key === this.buffer[0]);
377
+ const search = repeated ? this.buffer[0] : this.buffer;
378
+ const start = cycle && search.length === 1 ? from + 1 : Math.max(from, 0);
379
+ for (let n = 0;n < items.length; n++) {
380
+ const i = (start + n) % items.length;
381
+ const item = items[i];
382
+ if (!item.disabled && labelOf(item).toLowerCase().startsWith(search)) {
383
+ return i;
384
+ }
385
+ }
386
+ return -1;
387
+ }
388
+ clear() {
389
+ clearTimeout(this.timer);
390
+ this.timer = undefined;
391
+ this.buffer = "";
392
+ }
393
+ }
394
+
395
+ // common/controllers/RovingFocusController.ts
396
+ var isSeparator = (item) => item.getAttribute("role") === "separator";
397
+ var textOf = (item) => item.textContent?.trim() ?? "";
398
+ var previousKeys = {
399
+ vertical: ["ArrowUp"],
400
+ horizontal: ["ArrowLeft"],
401
+ both: ["ArrowUp", "ArrowLeft"]
402
+ };
403
+ var nextKeys = {
404
+ vertical: ["ArrowDown"],
405
+ horizontal: ["ArrowRight"],
406
+ both: ["ArrowDown", "ArrowRight"]
407
+ };
408
+
409
+ class RovingFocusController {
410
+ constructor(host, options) {
411
+ this.current = null;
412
+ this.handleFocusIn = (event) => {
413
+ const target = event.composedPath()[0];
414
+ if (target instanceof HTMLElement && this.navigable().includes(target)) {
415
+ this.setCurrent(target);
416
+ }
417
+ };
418
+ this.host = host;
419
+ this.options = options;
420
+ this.typeahead = new TypeaheadBuffer(options.typeaheadTimeout ?? typeaheadTimeout);
421
+ host.addController(this);
422
+ }
423
+ get activeIndex() {
424
+ return this.current ? this.options.items().indexOf(this.current) : -1;
425
+ }
426
+ get activeItem() {
427
+ return this.current;
428
+ }
429
+ get typeaheadActive() {
430
+ return this.typeahead.active;
431
+ }
432
+ focusFirst() {
433
+ return this.focusAt(this.navigable()[0]);
434
+ }
435
+ focusLast() {
436
+ const navigable = this.navigable();
437
+ return this.focusAt(navigable[navigable.length - 1]);
438
+ }
439
+ focusItem(item) {
440
+ return this.navigable().includes(item) ? this.focusAt(item) : null;
441
+ }
442
+ syncTabStops() {
443
+ const navigable = this.navigable();
444
+ const stop = this.current && navigable.includes(this.current) ? this.current : navigable[0] ?? null;
445
+ for (const item of this.options.items()) {
446
+ if (navigable.includes(item))
447
+ item.tabIndex = item === stop ? 0 : -1;
448
+ else
449
+ item.removeAttribute("tabindex");
450
+ }
451
+ }
452
+ reset() {
453
+ this.current = null;
454
+ this.typeahead.clear();
455
+ }
456
+ handleKeydown(event) {
457
+ const navigable = this.navigable();
458
+ if (!navigable.length) {
459
+ return false;
460
+ }
461
+ if (this.isTypeaheadKey(event)) {
462
+ const from = this.current ? navigable.indexOf(this.current) : -1;
463
+ const labelOf = this.options.labelOf ?? textOf;
464
+ const match = this.typeahead.search(navigable.map((item) => ({ item, disabled: false })), event.key, from, ({ item }) => labelOf(item));
465
+ if (match >= 0) {
466
+ this.focusAt(navigable[match]);
467
+ }
468
+ return true;
469
+ }
470
+ const target = this.targetFor(event.key, navigable);
471
+ if (target === undefined) {
472
+ return false;
473
+ }
474
+ if (target) {
475
+ this.focusAt(target);
476
+ }
477
+ return true;
478
+ }
479
+ hostConnected() {
480
+ this.host.addEventListener("focusin", this.handleFocusIn);
481
+ }
482
+ hostDisconnected() {
483
+ this.host.removeEventListener("focusin", this.handleFocusIn);
484
+ this.typeahead.clear();
485
+ }
486
+ navigable() {
487
+ const isSkipped = this.options.isSkipped ?? isSeparator;
488
+ return this.options.items().filter((item) => !isSkipped(item));
489
+ }
490
+ isTypeaheadKey(event) {
491
+ return (this.options.typeahead ?? true) && isPrintableKey(event) && (event.key !== " " || this.typeahead.active);
492
+ }
493
+ targetFor(key, navigable) {
494
+ const orientation = this.options.orientation ?? "vertical";
495
+ if (key === "Home")
496
+ return navigable[0];
497
+ if (key === "End")
498
+ return navigable[navigable.length - 1];
499
+ if (nextKeys[orientation].includes(key))
500
+ return this.step(navigable, 1);
501
+ if (previousKeys[orientation].includes(key)) {
502
+ return this.step(navigable, -1);
503
+ }
504
+ return;
505
+ }
506
+ step(navigable, dir) {
507
+ const from = this.current ? navigable.indexOf(this.current) : -1;
508
+ const outside = dir === 1 ? -1 : navigable.length;
509
+ const next = (from < 0 ? outside : from) + dir;
510
+ if (next >= 0 && next < navigable.length)
511
+ return navigable[next];
512
+ if (!(this.options.wrap ?? true))
513
+ return null;
514
+ return navigable[(next + navigable.length) % navigable.length];
515
+ }
516
+ focusAt(item) {
517
+ if (!item) {
518
+ return null;
519
+ }
520
+ this.setCurrent(item);
521
+ item.focus();
522
+ return item;
523
+ }
524
+ setCurrent(item) {
525
+ const changed = item !== this.current;
526
+ this.current = item;
527
+ this.syncTabStops();
528
+ if (changed) {
529
+ this.options.onFocusChange?.(item, this.activeIndex);
530
+ }
531
+ }
532
+ }
533
+
534
+ // common/helpers/enum-validator.ts
535
+ var enumValidator = (propName, enumSet, candidateValue) => {
536
+ if (!enumSet.has(candidateValue)) {
537
+ throw new Error(`Invalid prop ${propName} of value ${candidateValue}. Expected one of: ${Array.from(enumSet).join(", ")}`);
538
+ }
539
+ };
540
+
541
+ // common/helpers/enum-creator.ts
542
+ var enumCreator = (acceptableValues, propName, type, other = {}) => {
543
+ const acceptableValuesSet = new Set(acceptableValues);
544
+ return {
545
+ type,
546
+ hasChanged(newValue, oldValue) {
547
+ if (newValue !== oldValue) {
548
+ enumValidator(propName, acceptableValuesSet, newValue);
549
+ return true;
550
+ }
551
+ return false;
552
+ },
553
+ ...other
554
+ };
555
+ };
556
+
557
+ // common/props/surface/surface.props.ts
558
+ var surfaceValues = {
559
+ LIGHT: "light",
560
+ DARK: "dark"
561
+ };
562
+ var surfaceProps = enumCreator(Object.values(surfaceValues), "surface", String, { reflect: true });
563
+
564
+ // common/styles/overlay-panel.styles.ts
565
+ var overlayPositionProperties = {
566
+ shift: "--dsgn-overlay-shift",
567
+ left: "--dsgn-overlay-left",
568
+ top: "--dsgn-overlay-top",
569
+ bottom: "--dsgn-overlay-bottom",
570
+ width: "--dsgn-overlay-width",
571
+ maxHeight: "--dsgn-overlay-max-height"
572
+ };
573
+ var overlayPanelOffset = 4;
574
+ var originClasses = {
575
+ "bottom-start": "origin-top-left",
576
+ "top-start": "origin-bottom-left",
577
+ "bottom-end": "origin-top-right",
578
+ "top-end": "origin-bottom-right"
579
+ };
580
+ var anchoredPlacementClasses = {
581
+ "bottom-start": "top-[calc(100%+4px)]",
582
+ "top-start": "bottom-[calc(100%+4px)]",
583
+ "bottom-end": "top-[calc(100%+4px)]",
584
+ "top-end": "bottom-[calc(100%+4px)]"
585
+ };
586
+ var anchoredClasses = (placement) => [
587
+ "absolute",
588
+ "z-50",
589
+ "left-[var(--dsgn-overlay-shift,0px)]",
590
+ anchoredPlacementClasses[placement]
591
+ ];
592
+ var topLayerClasses = [
593
+ "fixed",
594
+ "inset-auto",
595
+ "m-0",
596
+ "left-[var(--dsgn-overlay-left,0px)]",
597
+ "top-[var(--dsgn-overlay-top,auto)]",
598
+ "bottom-[var(--dsgn-overlay-bottom,auto)]"
599
+ ];
600
+ var surfaceClasses = [
601
+ "box-border",
602
+ "flex",
603
+ "flex-col",
604
+ "gap-0.5",
605
+ "p-1",
606
+ "rounded-lg",
607
+ "border",
608
+ "border-popover-border",
609
+ "bg-popover-bg",
610
+ "[box-shadow:var(--shadow-popover)]",
611
+ "outline-none"
612
+ ];
613
+ var overlayPanelClasses = ({ visible, open, topLayer }, placement) => [
614
+ ...topLayer ? topLayerClasses : anchoredClasses(placement),
615
+ ...surfaceClasses,
616
+ originClasses[placement],
617
+ ...open ? [] : ["pointer-events-none"],
618
+ ...visible ? [] : ["hidden"]
619
+ ];
620
+ var overlayItemClasses = ({
621
+ active,
622
+ disabled,
623
+ tone
624
+ }) => {
625
+ const text = tone?.text ?? "text-input-text";
626
+ const fill = tone?.activeFill ?? "bg-surface-subtle";
627
+ return [
628
+ "flex",
629
+ "items-center",
630
+ "shrink-0",
631
+ "pl-2",
632
+ "pr-2.5",
633
+ "rounded-md",
634
+ "font-sans",
635
+ "select-none",
636
+ ...disabled ? ["text-input-text-disabled", "cursor-default"] : [text, "cursor-pointer"],
637
+ ...active ? [fill] : []
638
+ ];
639
+ };
640
+
641
+ // components/DropdownMenu/src/DropdownMenu.ts
642
+ import { Icon } from "../../Icon/src/Icon.js";
643
+
644
+ // components/DropdownMenu/src/DropdownMenu.css
645
+ import { css as css2 } from "lit";
646
+ var styles2 = css2`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
647
+ @layer properties;
648
+ @layer theme {
649
+ :root, :host {
650
+ --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
651
+ 'Noto Color Emoji';
652
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
653
+ monospace;
654
+ --spacing: 0.25rem;
655
+ --text-xs: 0.75rem;
656
+ --text-xs--line-height: calc(1 / 0.75);
657
+ --text-sm: 0.875rem;
658
+ --text-sm--line-height: calc(1.25 / 0.875);
659
+ --radius-md: 0.375rem;
660
+ --radius-lg: 0.5rem;
661
+ --color-error-strong: inherit;
662
+ --color-input-text: inherit;
663
+ --color-input-text-disabled: inherit;
664
+ --color-popover-bg: inherit;
665
+ --color-popover-border: inherit;
666
+ --color-surface-subtle: inherit;
667
+ --color-text-secondary: inherit;
668
+ --shadow-popover: inherit;
669
+ }
670
+ }
671
+ @layer utilities {
672
+ .pointer-events-none {
673
+ pointer-events: none;
674
+ }
675
+ .visible {
676
+ visibility: visible;
677
+ }
678
+ .absolute {
679
+ position: absolute;
680
+ }
681
+ .fixed {
682
+ position: fixed;
683
+ }
684
+ .relative {
685
+ position: relative;
686
+ }
687
+ .static {
688
+ position: static;
689
+ }
690
+ .inset-auto {
691
+ inset: auto;
692
+ }
693
+ .top-\\[calc\\(100\\%\\+4px\\)\\] {
694
+ top: calc(100% + 4px);
695
+ }
696
+ .top-\\[var\\(--dsgn-overlay-top\\,auto\\)\\] {
697
+ top: var(--dsgn-overlay-top,auto);
698
+ }
699
+ .bottom-\\[calc\\(100\\%\\+4px\\)\\] {
700
+ bottom: calc(100% + 4px);
701
+ }
702
+ .bottom-\\[var\\(--dsgn-overlay-bottom\\,auto\\)\\] {
703
+ bottom: var(--dsgn-overlay-bottom,auto);
704
+ }
705
+ .left-\\[var\\(--dsgn-overlay-left\\,0px\\)\\] {
706
+ left: var(--dsgn-overlay-left,0px);
707
+ }
708
+ .left-\\[var\\(--dsgn-overlay-shift\\,0px\\)\\] {
709
+ left: var(--dsgn-overlay-shift,0px);
710
+ }
711
+ .z-50 {
712
+ z-index: 50;
713
+ }
714
+ .m-0 {
715
+ margin: calc(var(--spacing) * 0);
716
+ }
717
+ .m-1 {
718
+ margin: calc(var(--spacing) * 1);
719
+ }
720
+ .ml-auto {
721
+ margin-left: auto;
722
+ }
723
+ .box-border {
724
+ box-sizing: border-box;
725
+ }
726
+ .flex {
727
+ display: flex;
728
+ }
729
+ .hidden {
730
+ display: none;
731
+ }
732
+ .inline-flex {
733
+ display: inline-flex;
734
+ }
735
+ .size-3\\.5 {
736
+ width: calc(var(--spacing) * 3.5);
737
+ height: calc(var(--spacing) * 3.5);
738
+ }
739
+ .size-4 {
740
+ width: calc(var(--spacing) * 4);
741
+ height: calc(var(--spacing) * 4);
742
+ }
743
+ .size-\\[18px\\] {
744
+ width: 18px;
745
+ height: 18px;
746
+ }
747
+ .h-7 {
748
+ height: calc(var(--spacing) * 7);
749
+ }
750
+ .h-9 {
751
+ height: calc(var(--spacing) * 9);
752
+ }
753
+ .h-11 {
754
+ height: calc(var(--spacing) * 11);
755
+ }
756
+ .h-px {
757
+ height: 1px;
758
+ }
759
+ .max-h-\\[min\\(calc\\(100vh-32px\\)\\,var\\(--dsgn-overlay-max-height\\,100vh\\)\\)\\] {
760
+ max-height: min(calc(100vh - 32px), var(--dsgn-overlay-max-height,100vh));
761
+ }
762
+ .w-max {
763
+ width: max-content;
764
+ }
765
+ .max-w-\\[calc\\(100vw-32px\\)\\] {
766
+ max-width: calc(100vw - 32px);
767
+ }
768
+ .min-w-\\[232px\\] {
769
+ min-width: 232px;
770
+ }
771
+ .shrink-0 {
772
+ flex-shrink: 0;
773
+ }
774
+ .grow {
775
+ flex-grow: 1;
776
+ }
777
+ .origin-bottom-left {
778
+ transform-origin: 0 100%;
779
+ }
780
+ .origin-bottom-right {
781
+ transform-origin: 100% 100%;
782
+ }
783
+ .origin-top-left {
784
+ transform-origin: 0 0;
785
+ }
786
+ .origin-top-right {
787
+ transform-origin: 100% 0;
788
+ }
789
+ .transform {
790
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
791
+ }
792
+ .cursor-default {
793
+ cursor: default;
794
+ }
795
+ .cursor-pointer {
796
+ cursor: pointer;
797
+ }
798
+ .flex-col {
799
+ flex-direction: column;
800
+ }
801
+ .items-center {
802
+ align-items: center;
803
+ }
804
+ .gap-0\\.5 {
805
+ gap: calc(var(--spacing) * 0.5);
806
+ }
807
+ .gap-2 {
808
+ gap: calc(var(--spacing) * 2);
809
+ }
810
+ .gap-2\\.5 {
811
+ gap: calc(var(--spacing) * 2.5);
812
+ }
813
+ .truncate {
814
+ overflow: hidden;
815
+ text-overflow: ellipsis;
816
+ white-space: nowrap;
817
+ }
818
+ .overflow-y-auto {
819
+ overflow-y: auto;
820
+ }
821
+ .rounded-lg {
822
+ border-radius: var(--radius-lg);
823
+ }
824
+ .rounded-md {
825
+ border-radius: var(--radius-md);
826
+ }
827
+ .border {
828
+ border-style: var(--tw-border-style);
829
+ border-width: 1px;
830
+ }
831
+ .border-popover-border {
832
+ border-color: var(--color-popover-border);
833
+ }
834
+ .bg-error-strong\\/15 {
835
+ background-color: color-mix(in srgb, inherit 15%, transparent);
836
+ @supports (color: color-mix(in lab, red, red)) {
837
+ background-color: color-mix(in oklab, var(--color-error-strong) 15%, transparent);
838
+ }
839
+ }
840
+ .bg-popover-bg {
841
+ background-color: var(--color-popover-bg);
842
+ }
843
+ .bg-popover-border {
844
+ background-color: var(--color-popover-border);
845
+ }
846
+ .bg-surface-subtle {
847
+ background-color: var(--color-surface-subtle);
848
+ }
849
+ .fill-none {
850
+ fill: none;
851
+ }
852
+ .stroke-current {
853
+ stroke: currentcolor;
854
+ }
855
+ .stroke-\\[1\\.75\\] {
856
+ stroke-width: 1.75;
857
+ }
858
+ .p-1 {
859
+ padding: calc(var(--spacing) * 1);
860
+ }
861
+ .pr-2\\.5 {
862
+ padding-right: calc(var(--spacing) * 2.5);
863
+ }
864
+ .pl-2 {
865
+ padding-left: calc(var(--spacing) * 2);
866
+ }
867
+ .pl-4 {
868
+ padding-left: calc(var(--spacing) * 4);
869
+ }
870
+ .font-mono {
871
+ font-family: var(--font-mono);
872
+ }
873
+ .font-sans {
874
+ font-family: var(--font-sans);
875
+ }
876
+ .text-sm {
877
+ font-size: var(--text-sm);
878
+ line-height: var(--tw-leading, var(--text-sm--line-height));
879
+ }
880
+ .text-xs {
881
+ font-size: var(--text-xs);
882
+ line-height: var(--tw-leading, var(--text-xs--line-height));
883
+ }
884
+ .text-\\[13px\\] {
885
+ font-size: 13px;
886
+ }
887
+ .text-\\[15px\\] {
888
+ font-size: 15px;
889
+ }
890
+ .whitespace-nowrap {
891
+ white-space: nowrap;
892
+ }
893
+ .text-error-strong {
894
+ color: var(--color-error-strong);
895
+ }
896
+ .text-input-text {
897
+ color: var(--color-input-text);
898
+ }
899
+ .text-input-text-disabled {
900
+ color: var(--color-input-text-disabled);
901
+ }
902
+ .text-text-secondary {
903
+ color: var(--color-text-secondary);
904
+ }
905
+ .shadow {
906
+ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
907
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
908
+ }
909
+ .shadow-popover {
910
+ --tw-shadow: inherit;
911
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
912
+ }
913
+ .\\[box-shadow\\:var\\(--shadow-popover\\)\\] {
914
+ box-shadow: var(--shadow-popover);
915
+ }
916
+ .outline-none {
917
+ --tw-outline-style: none;
918
+ outline-style: none;
919
+ }
920
+ .select-none {
921
+ -webkit-user-select: none;
922
+ user-select: none;
923
+ }
924
+ }
925
+ @property --dsgn-border-sweep-angle {
926
+ syntax: '<angle>';
927
+ inherits: false;
928
+ initial-value: 0deg;
929
+ }
930
+ @property --tw-rotate-x {
931
+ syntax: "*";
932
+ inherits: false;
933
+ }
934
+ @property --tw-rotate-y {
935
+ syntax: "*";
936
+ inherits: false;
937
+ }
938
+ @property --tw-rotate-z {
939
+ syntax: "*";
940
+ inherits: false;
941
+ }
942
+ @property --tw-skew-x {
943
+ syntax: "*";
944
+ inherits: false;
945
+ }
946
+ @property --tw-skew-y {
947
+ syntax: "*";
948
+ inherits: false;
949
+ }
950
+ @property --tw-border-style {
951
+ syntax: "*";
952
+ inherits: false;
953
+ initial-value: solid;
954
+ }
955
+ @property --tw-shadow {
956
+ syntax: "*";
957
+ inherits: false;
958
+ initial-value: 0 0 #0000;
959
+ }
960
+ @property --tw-shadow-color {
961
+ syntax: "*";
962
+ inherits: false;
963
+ }
964
+ @property --tw-shadow-alpha {
965
+ syntax: "<percentage>";
966
+ inherits: false;
967
+ initial-value: 100%;
968
+ }
969
+ @property --tw-inset-shadow {
970
+ syntax: "*";
971
+ inherits: false;
972
+ initial-value: 0 0 #0000;
973
+ }
974
+ @property --tw-inset-shadow-color {
975
+ syntax: "*";
976
+ inherits: false;
977
+ }
978
+ @property --tw-inset-shadow-alpha {
979
+ syntax: "<percentage>";
980
+ inherits: false;
981
+ initial-value: 100%;
982
+ }
983
+ @property --tw-ring-color {
984
+ syntax: "*";
985
+ inherits: false;
986
+ }
987
+ @property --tw-ring-shadow {
988
+ syntax: "*";
989
+ inherits: false;
990
+ initial-value: 0 0 #0000;
991
+ }
992
+ @property --tw-inset-ring-color {
993
+ syntax: "*";
994
+ inherits: false;
995
+ }
996
+ @property --tw-inset-ring-shadow {
997
+ syntax: "*";
998
+ inherits: false;
999
+ initial-value: 0 0 #0000;
1000
+ }
1001
+ @property --tw-ring-inset {
1002
+ syntax: "*";
1003
+ inherits: false;
1004
+ }
1005
+ @property --tw-ring-offset-width {
1006
+ syntax: "<length>";
1007
+ inherits: false;
1008
+ initial-value: 0px;
1009
+ }
1010
+ @property --tw-ring-offset-color {
1011
+ syntax: "*";
1012
+ inherits: false;
1013
+ initial-value: #fff;
1014
+ }
1015
+ @property --tw-ring-offset-shadow {
1016
+ syntax: "*";
1017
+ inherits: false;
1018
+ initial-value: 0 0 #0000;
1019
+ }
1020
+ @layer properties {
1021
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
1022
+ *, ::before, ::after, ::backdrop {
1023
+ --tw-rotate-x: initial;
1024
+ --tw-rotate-y: initial;
1025
+ --tw-rotate-z: initial;
1026
+ --tw-skew-x: initial;
1027
+ --tw-skew-y: initial;
1028
+ --tw-border-style: solid;
1029
+ --tw-shadow: 0 0 #0000;
1030
+ --tw-shadow-color: initial;
1031
+ --tw-shadow-alpha: 100%;
1032
+ --tw-inset-shadow: 0 0 #0000;
1033
+ --tw-inset-shadow-color: initial;
1034
+ --tw-inset-shadow-alpha: 100%;
1035
+ --tw-ring-color: initial;
1036
+ --tw-ring-shadow: 0 0 #0000;
1037
+ --tw-inset-ring-color: initial;
1038
+ --tw-inset-ring-shadow: 0 0 #0000;
1039
+ --tw-ring-inset: initial;
1040
+ --tw-ring-offset-width: 0px;
1041
+ --tw-ring-offset-color: #fff;
1042
+ --tw-ring-offset-shadow: 0 0 #0000;
1043
+ --dsgn-border-sweep-angle: 0deg;
1044
+ }
1045
+ }
1046
+ }
1047
+ `;
1048
+ var DropdownMenu_default = styles2;
1049
+
1050
+ // common/props/size/size.props.ts
1051
+ var sizeValues = {
1052
+ SM: "sm",
1053
+ MD: "md",
1054
+ LG: "lg"
1055
+ };
1056
+ var sizeProps = enumCreator(Object.values(sizeValues), "size", String, {
1057
+ reflect: true
1058
+ });
1059
+
1060
+ // components/DropdownMenu/src/DropdownMenu.props.ts
1061
+ var dropdownMenuItemVariantValues = {
1062
+ DEFAULT: "default",
1063
+ DANGER: "danger"
1064
+ };
1065
+ var isDropdownMenuSeparator = (entry) => ("separator" in entry) && entry.separator === true;
1066
+ var dropdownMenuAlignValues = {
1067
+ START: "start",
1068
+ END: "end"
1069
+ };
1070
+ var dropdownMenuAlignProps = enumCreator(Object.values(dropdownMenuAlignValues), "align", String, { reflect: true });
1071
+ var dropdownMenuSizeValues = sizeValues;
1072
+ var dropdownMenuSizeProps = sizeProps;
1073
+
1074
+ // ../../node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
1075
+ function r(e) {
1076
+ var t, f, n = "";
1077
+ if (typeof e == "string" || typeof e == "number")
1078
+ n += e;
1079
+ else if (typeof e == "object")
1080
+ if (Array.isArray(e)) {
1081
+ var o = e.length;
1082
+ for (t = 0;t < o; t++)
1083
+ e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
1084
+ } else
1085
+ for (f in e)
1086
+ e[f] && (n && (n += " "), n += f);
1087
+ return n;
1088
+ }
1089
+ function clsx() {
1090
+ for (var e, t, f = 0, n = "", o = arguments.length;f < o; f++)
1091
+ (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
1092
+ return n;
1093
+ }
1094
+ var clsx_default = clsx;
1095
+
1096
+ // components/DropdownMenu/src/DropdownMenu.styles.ts
1097
+ var anchorClasses = () => clsx_default("relative", "inline-flex");
1098
+ var menuClasses = (panel, placement) => clsx_default(overlayPanelClasses(panel, placement), "w-max", "min-w-[232px]", "max-w-[calc(100vw-32px)]", "max-h-[min(calc(100vh-32px),var(--dsgn-overlay-max-height,100vh))]", "overflow-y-auto");
1099
+ var itemSizeClasses = {
1100
+ [dropdownMenuSizeValues.SM]: ["h-7", "gap-2", "text-[13px]"],
1101
+ [dropdownMenuSizeValues.MD]: ["h-9", "gap-2.5", "text-sm"],
1102
+ [dropdownMenuSizeValues.LG]: ["h-11", "gap-2.5", "text-[15px]"]
1103
+ };
1104
+ var dangerTone = {
1105
+ text: "text-error-strong",
1106
+ activeFill: "bg-error-strong/15"
1107
+ };
1108
+ var itemClasses = (size, {
1109
+ active,
1110
+ disabled,
1111
+ danger
1112
+ }) => clsx_default(overlayItemClasses({
1113
+ active,
1114
+ disabled,
1115
+ tone: danger ? dangerTone : undefined
1116
+ }), itemSizeClasses[size], "outline-none", "whitespace-nowrap");
1117
+ var iconSizeClasses = {
1118
+ [dropdownMenuSizeValues.SM]: "size-3.5",
1119
+ [dropdownMenuSizeValues.MD]: "size-4",
1120
+ [dropdownMenuSizeValues.LG]: "size-[18px]"
1121
+ };
1122
+ var itemIconClasses = (size, plain) => clsx_default(iconSizeClasses[size], "shrink-0", "fill-none", "stroke-current", "stroke-[1.75]", plain && "text-text-secondary");
1123
+ var itemLabelClasses = () => clsx_default("truncate");
1124
+ var itemShortcutClasses = (plain) => clsx_default("ml-auto", "pl-4", "shrink-0", "font-mono", "text-xs", plain && "text-text-secondary");
1125
+ var separatorClasses = () => clsx_default("shrink-0", "h-px", "m-1", "bg-popover-border");
1126
+
1127
+ // components/DropdownMenu/src/DropdownMenu.ts
1128
+ var supportsPopover = () => typeof HTMLElement !== "undefined" && ("popover" in HTMLElement.prototype);
1129
+ var px = (value) => value === null ? "auto" : `${value}px`;
1130
+ var coarsePointerQuery = "(pointer: coarse)";
1131
+ var matchCoarsePointer = () => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(coarsePointerQuery) : undefined;
1132
+ var openMenus = new Set;
1133
+
1134
+ class DropdownMenu extends Base {
1135
+ constructor() {
1136
+ super(...arguments);
1137
+ this.surface = surfaceValues.LIGHT;
1138
+ this.size = dropdownMenuSizeValues.MD;
1139
+ this.items = [];
1140
+ this.align = dropdownMenuAlignValues.START;
1141
+ this.open = false;
1142
+ this.label = "";
1143
+ this._visible = false;
1144
+ this._placement = "bottom-start";
1145
+ this._activeIndex = -1;
1146
+ this._coarse = false;
1147
+ this._triggerName = "";
1148
+ this._trigger = null;
1149
+ this._focusOnOpen = "first";
1150
+ this._returnFocus = false;
1151
+ this._motion = new MotionController(this);
1152
+ this._exitRun = 0;
1153
+ this._topLayer = supportsPopover();
1154
+ this._inTopLayer = false;
1155
+ this._menuScrollTop = 0;
1156
+ this._position = new OverlayPositionController(this, {
1157
+ anchor: () => this._trigger,
1158
+ overlay: () => this._menu,
1159
+ flip: true,
1160
+ trackViewport: true,
1161
+ offset: overlayPanelOffset,
1162
+ align: () => this.align,
1163
+ beforeMeasure: () => {
1164
+ this._menuScrollTop = this._menu?.scrollTop ?? 0;
1165
+ this.style.removeProperty(overlayPositionProperties.maxHeight);
1166
+ },
1167
+ apply: ({ shift, placement, viewport }) => {
1168
+ this.style.setProperty(overlayPositionProperties.shift, `${shift}px`);
1169
+ this.style.setProperty(overlayPositionProperties.left, px(viewport.left));
1170
+ this.style.setProperty(overlayPositionProperties.top, px(viewport.top));
1171
+ this.style.setProperty(overlayPositionProperties.bottom, px(viewport.bottom));
1172
+ this.style.setProperty(overlayPositionProperties.maxHeight, `${computeOverlayAvailableHeight(viewport, window.innerHeight)}px`);
1173
+ if (this._menu)
1174
+ this._menu.scrollTop = this._menuScrollTop;
1175
+ this._placement = placement;
1176
+ },
1177
+ clear: () => {
1178
+ for (const name of Object.values(overlayPositionProperties)) {
1179
+ this.style.removeProperty(name);
1180
+ }
1181
+ this._placement = `bottom-${this.align}`;
1182
+ }
1183
+ });
1184
+ this._dismiss = new OverlayDismissController(this, {
1185
+ isOpen: () => this.open,
1186
+ onDismiss: () => this._close(false),
1187
+ returnFocusTo: () => this._trigger
1188
+ });
1189
+ this._roving = new RovingFocusController(this, {
1190
+ items: () => this._itemElements(),
1191
+ labelOf: (item) => this._entryAt(item)?.label ?? "",
1192
+ onFocusChange: (item) => {
1193
+ this._activeIndex = Number(item.dataset.index);
1194
+ }
1195
+ });
1196
+ this._resolveTrigger = () => {
1197
+ const slot = this.renderRoot.querySelector('slot[name="trigger"]');
1198
+ const trigger = slot?.assignedElements({ flatten: true })[0] ?? null;
1199
+ if (trigger === this._trigger)
1200
+ return;
1201
+ this._trigger = trigger;
1202
+ if (this.open)
1203
+ this._triggerName = this._readTriggerName();
1204
+ this._syncTrigger();
1205
+ };
1206
+ this._handleTriggerClick = (event) => {
1207
+ if (!this._fromTrigger(event) || this._triggerUnavailable(event))
1208
+ return;
1209
+ if (this.open)
1210
+ this._close(true);
1211
+ else
1212
+ this.show("first");
1213
+ };
1214
+ this._handleTriggerKeydown = (event) => {
1215
+ if (!this._fromTrigger(event) || this._triggerUnavailable(event))
1216
+ return;
1217
+ const opens = {
1218
+ Enter: "first",
1219
+ " ": "first",
1220
+ ArrowDown: "first",
1221
+ ArrowUp: "last"
1222
+ };
1223
+ const focus = opens[event.key];
1224
+ if (!focus)
1225
+ return;
1226
+ event.preventDefault();
1227
+ this.show(focus);
1228
+ };
1229
+ this._handleTriggerKeyup = (event) => {
1230
+ if (event.key === " " && this._fromTrigger(event))
1231
+ event.preventDefault();
1232
+ };
1233
+ this._handleFocusOut = (event) => {
1234
+ if (!this.open)
1235
+ return;
1236
+ const next = event.relatedTarget;
1237
+ const inside = next === this || this.contains(next) || this.renderRoot.contains(next);
1238
+ if (next && inside)
1239
+ return;
1240
+ this._close(false);
1241
+ };
1242
+ this._handleCoarseChange = () => {
1243
+ this._coarse = !!this._coarseQuery?.matches;
1244
+ };
1245
+ }
1246
+ static styles = [Base.styles, DropdownMenu_default];
1247
+ get _effectiveSize() {
1248
+ return this._coarse ? dropdownMenuSizeValues.LG : this.size;
1249
+ }
1250
+ _itemElements() {
1251
+ return this._menu ? [...this._menu.querySelectorAll("[data-index]")] : [];
1252
+ }
1253
+ _entryAt(element) {
1254
+ const entry = this.items[Number(element.dataset.index)];
1255
+ return entry && !isDropdownMenuSeparator(entry) ? entry : undefined;
1256
+ }
1257
+ show(focus = "first") {
1258
+ this._focusOnOpen = focus;
1259
+ this.open = true;
1260
+ }
1261
+ hide() {
1262
+ this._close(false);
1263
+ }
1264
+ _close(returnFocus) {
1265
+ if (!this.open)
1266
+ return;
1267
+ this._returnFocus = returnFocus;
1268
+ this.open = false;
1269
+ }
1270
+ _activate(element) {
1271
+ const item = this._entryAt(element);
1272
+ if (!item || item.disabled)
1273
+ return;
1274
+ this.emit("dsgn-select", { value: item.value });
1275
+ this._close(true);
1276
+ }
1277
+ _readTriggerName() {
1278
+ return this._trigger?.getAttribute("aria-label") || this._trigger?.textContent?.trim() || "";
1279
+ }
1280
+ _syncTrigger() {
1281
+ const trigger = this._trigger;
1282
+ if (!trigger)
1283
+ return;
1284
+ const expanded = this.open ? "true" : "false";
1285
+ const declared = trigger.constructor.elementProperties;
1286
+ if (declared?.has("ariaExpanded")) {
1287
+ Object.assign(trigger, { ariaHasPopup: "menu", ariaExpanded: expanded });
1288
+ } else {
1289
+ trigger.setAttribute("aria-haspopup", "menu");
1290
+ trigger.setAttribute("aria-expanded", expanded);
1291
+ }
1292
+ }
1293
+ _fromTrigger(event) {
1294
+ return !!this._trigger && event.composedPath().includes(this._trigger);
1295
+ }
1296
+ _triggerUnavailable(event) {
1297
+ if (this._trigger?.loading)
1298
+ return true;
1299
+ const path = event.composedPath();
1300
+ return path.slice(0, path.indexOf(this._trigger) + 1).some((element) => element instanceof HTMLElement && (element.disabled || element.getAttribute("aria-disabled") === "true"));
1301
+ }
1302
+ _handleMenuKeydown(event) {
1303
+ if (this._dismiss.handleKeydown(event)) {
1304
+ event.preventDefault();
1305
+ event.stopPropagation();
1306
+ return;
1307
+ }
1308
+ if (event.key === "Tab") {
1309
+ this._close(false);
1310
+ return;
1311
+ }
1312
+ if (event.key === "Enter" || event.key === " ") {
1313
+ event.preventDefault();
1314
+ const item = this._roving.activeItem;
1315
+ if (item)
1316
+ this._activate(item);
1317
+ return;
1318
+ }
1319
+ if (this._roving.handleKeydown(event)) {
1320
+ event.preventDefault();
1321
+ }
1322
+ }
1323
+ _handleItemPointerMove(event) {
1324
+ const item = event.currentTarget;
1325
+ if (this._roving.activeItem !== item)
1326
+ this._roving.focusItem(item);
1327
+ }
1328
+ _handleItemClick(event) {
1329
+ this._activate(event.currentTarget);
1330
+ }
1331
+ connectedCallback() {
1332
+ super.connectedCallback();
1333
+ this._coarseQuery = matchCoarsePointer();
1334
+ this._coarse = !!this._coarseQuery?.matches;
1335
+ this._coarseQuery?.addEventListener?.("change", this._handleCoarseChange);
1336
+ this.addEventListener("focusout", this._handleFocusOut);
1337
+ this.addEventListener("click", this._handleTriggerClick);
1338
+ this.addEventListener("keydown", this._handleTriggerKeydown);
1339
+ this.addEventListener("keyup", this._handleTriggerKeyup);
1340
+ }
1341
+ disconnectedCallback() {
1342
+ this._coarseQuery?.removeEventListener?.("change", this._handleCoarseChange);
1343
+ this.removeEventListener("focusout", this._handleFocusOut);
1344
+ this.removeEventListener("click", this._handleTriggerClick);
1345
+ this.removeEventListener("keydown", this._handleTriggerKeydown);
1346
+ this.removeEventListener("keyup", this._handleTriggerKeyup);
1347
+ openMenus.delete(this);
1348
+ if (this.open || this._visible) {
1349
+ this._exitRun++;
1350
+ this.open = false;
1351
+ this._inTopLayer = false;
1352
+ this._visible = false;
1353
+ this._activeIndex = -1;
1354
+ this._roving.reset();
1355
+ }
1356
+ super.disconnectedCallback();
1357
+ }
1358
+ firstUpdated() {
1359
+ this._resolveTrigger();
1360
+ }
1361
+ willUpdate(changedProperties) {
1362
+ if (changedProperties.has("open") && this.open) {
1363
+ this._visible = true;
1364
+ this._triggerName = this._readTriggerName();
1365
+ }
1366
+ if (changedProperties.has("align") && !this.open) {
1367
+ this._placement = `bottom-${this.align}`;
1368
+ }
1369
+ }
1370
+ updated(changedProperties) {
1371
+ super.updated(changedProperties);
1372
+ this.updateTheme(changedProperties, this.surface);
1373
+ this._roving.syncTabStops();
1374
+ if (changedProperties.has("open")) {
1375
+ this._syncTrigger();
1376
+ if (this.open || changedProperties.get("open") === true) {
1377
+ this._handleOpenChange();
1378
+ }
1379
+ }
1380
+ }
1381
+ _showMenu(menu) {
1382
+ if (!this._topLayer || this._inTopLayer)
1383
+ return;
1384
+ menu.showPopover();
1385
+ this._inTopLayer = true;
1386
+ }
1387
+ _hideMenu(menu) {
1388
+ if (!this._inTopLayer)
1389
+ return;
1390
+ this._inTopLayer = false;
1391
+ menu?.hidePopover();
1392
+ }
1393
+ _handleOpenChange() {
1394
+ const menu = this._menu;
1395
+ if (this.open) {
1396
+ for (const other of openMenus)
1397
+ if (other !== this)
1398
+ other.hide();
1399
+ openMenus.add(this);
1400
+ this._exitRun++;
1401
+ this._dismiss.start();
1402
+ if (menu)
1403
+ this._showMenu(menu);
1404
+ this._position.start();
1405
+ if (this._focusOnOpen === "last")
1406
+ this._roving.focusLast();
1407
+ else
1408
+ this._roving.focusFirst();
1409
+ this._focusOnOpen = "first";
1410
+ if (menu) {
1411
+ this._motion.enter(menu, "scaleIn", { reducedMotion: "jump" });
1412
+ }
1413
+ return;
1414
+ }
1415
+ openMenus.delete(this);
1416
+ this._dismiss.stop();
1417
+ this._activeIndex = -1;
1418
+ this._roving.reset();
1419
+ if (this._returnFocus)
1420
+ this._trigger?.focus();
1421
+ this._returnFocus = false;
1422
+ this._exit(menu);
1423
+ }
1424
+ _exit(menu) {
1425
+ if (!this._visible)
1426
+ return;
1427
+ const run = ++this._exitRun;
1428
+ const hide = () => {
1429
+ if (run !== this._exitRun)
1430
+ return;
1431
+ this._hideMenu(menu);
1432
+ this._visible = false;
1433
+ this._position.stop();
1434
+ };
1435
+ if (menu) {
1436
+ this._motion.exit(menu, "scaleIn", { reducedMotion: "jump" }).then(hide);
1437
+ } else {
1438
+ hide();
1439
+ }
1440
+ }
1441
+ _renderIcon(name, plain) {
1442
+ const icon = name ? Icon.resolve(name) : undefined;
1443
+ if (!icon)
1444
+ return nothing;
1445
+ return html2`<svg
1446
+ part="item-icon"
1447
+ class="${itemIconClasses(this._effectiveSize, plain)}"
1448
+ viewBox="0 0 24 24"
1449
+ stroke-linecap="round"
1450
+ stroke-linejoin="round"
1451
+ aria-hidden="true"
1452
+ >
1453
+ ${icon}
1454
+ </svg>`;
1455
+ }
1456
+ _renderEntry(entry, index) {
1457
+ if (isDropdownMenuSeparator(entry)) {
1458
+ return html2`<div
1459
+ part="separator"
1460
+ role="separator"
1461
+ data-index="${index}"
1462
+ class="${separatorClasses()}"
1463
+ ></div>`;
1464
+ }
1465
+ const disabled = !!entry.disabled;
1466
+ const danger = entry.variant === dropdownMenuItemVariantValues.DANGER;
1467
+ const plain = !disabled && !danger;
1468
+ return html2`<div
1469
+ part="item"
1470
+ role="menuitem"
1471
+ tabindex="-1"
1472
+ data-index="${index}"
1473
+ class="${itemClasses(this._effectiveSize, {
1474
+ active: index === this._activeIndex,
1475
+ disabled,
1476
+ danger
1477
+ })}"
1478
+ aria-disabled="${disabled ? "true" : nothing}"
1479
+ @click=${this._handleItemClick}
1480
+ @pointermove=${this._handleItemPointerMove}
1481
+ >
1482
+ ${this._renderIcon(entry.icon, plain)}
1483
+ <span class="${itemLabelClasses()}">${entry.label}</span>
1484
+ ${entry.shortcut ? html2`<span part="item-shortcut" class="${itemShortcutClasses(plain)}"
1485
+ >${entry.shortcut}</span
1486
+ >` : nothing}
1487
+ </div>`;
1488
+ }
1489
+ render() {
1490
+ return html2`<div class="${anchorClasses()}">
1491
+ <slot name="trigger" @slotchange=${this._resolveTrigger}></slot>
1492
+ <div
1493
+ id="menu"
1494
+ part="menu"
1495
+ role="menu"
1496
+ popover=${this._topLayer ? "manual" : nothing}
1497
+ class="${menuClasses({
1498
+ visible: this._visible,
1499
+ open: this.open,
1500
+ topLayer: this._topLayer
1501
+ }, this._placement)}"
1502
+ aria-label="${this.label || this._triggerName || nothing}"
1503
+ aria-orientation="vertical"
1504
+ @keydown=${this._handleMenuKeydown}
1505
+ >
1506
+ ${this.items.map((entry, index) => this._renderEntry(entry, index))}
1507
+ </div>
1508
+ </div>`;
1509
+ }
1510
+ }
1511
+ __legacyDecorateClassTS([
1512
+ property({ ...surfaceProps })
1513
+ ], DropdownMenu.prototype, "surface", undefined);
1514
+ __legacyDecorateClassTS([
1515
+ property({ ...dropdownMenuSizeProps })
1516
+ ], DropdownMenu.prototype, "size", undefined);
1517
+ __legacyDecorateClassTS([
1518
+ property({ type: Array })
1519
+ ], DropdownMenu.prototype, "items", undefined);
1520
+ __legacyDecorateClassTS([
1521
+ property({ ...dropdownMenuAlignProps })
1522
+ ], DropdownMenu.prototype, "align", undefined);
1523
+ __legacyDecorateClassTS([
1524
+ property({ type: Boolean, reflect: true })
1525
+ ], DropdownMenu.prototype, "open", undefined);
1526
+ __legacyDecorateClassTS([
1527
+ property({ type: String })
1528
+ ], DropdownMenu.prototype, "label", undefined);
1529
+ __legacyDecorateClassTS([
1530
+ state()
1531
+ ], DropdownMenu.prototype, "_visible", undefined);
1532
+ __legacyDecorateClassTS([
1533
+ state()
1534
+ ], DropdownMenu.prototype, "_placement", undefined);
1535
+ __legacyDecorateClassTS([
1536
+ state()
1537
+ ], DropdownMenu.prototype, "_activeIndex", undefined);
1538
+ __legacyDecorateClassTS([
1539
+ state()
1540
+ ], DropdownMenu.prototype, "_coarse", undefined);
1541
+ __legacyDecorateClassTS([
1542
+ state()
1543
+ ], DropdownMenu.prototype, "_triggerName", undefined);
1544
+ __legacyDecorateClassTS([
1545
+ query("#menu")
1546
+ ], DropdownMenu.prototype, "_menu", undefined);
1547
+ defineElement("dsgn-dropdown-menu", DropdownMenu);
1548
+ export {
1549
+ DropdownMenu
1550
+ };