@dogsbay/elements 0.2.0-beta.114 → 0.2.0-beta.116

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 (73) hide show
  1. package/dist/{base-BtvRtAsC.d.ts → base-D1Rz70fn.d.ts} +1 -0
  2. package/dist/chunk-3AEQ6KHV.js +70 -0
  3. package/dist/chunk-3AEQ6KHV.js.map +1 -0
  4. package/dist/chunk-3FMXAKOU.js +383 -0
  5. package/dist/chunk-3FMXAKOU.js.map +1 -0
  6. package/dist/{chunk-ZLIF4UYD.js → chunk-4YPDR3W6.js} +13 -4
  7. package/dist/chunk-4YPDR3W6.js.map +1 -0
  8. package/dist/{chunk-IMW234S2.js → chunk-CLUADA3G.js} +2 -2
  9. package/dist/chunk-EGU66XCL.js +116 -0
  10. package/dist/chunk-EGU66XCL.js.map +1 -0
  11. package/dist/{chunk-THLTJ2HB.js → chunk-ETBBSA3I.js} +2 -2
  12. package/dist/chunk-FIJZRZEN.js +90 -0
  13. package/dist/chunk-FIJZRZEN.js.map +1 -0
  14. package/dist/{chunk-ABP4WEKU.js → chunk-K6DPDBAG.js} +25 -2
  15. package/dist/chunk-K6DPDBAG.js.map +1 -0
  16. package/dist/{chunk-5S2MMI3R.js → chunk-LAUNXWKJ.js} +2 -2
  17. package/dist/chunk-MAD6XJY4.js +256 -0
  18. package/dist/chunk-MAD6XJY4.js.map +1 -0
  19. package/dist/chunk-MEOIAZDF.js +140 -0
  20. package/dist/chunk-MEOIAZDF.js.map +1 -0
  21. package/dist/{chunk-WTARNLNI.js → chunk-QSRUT67Y.js} +2 -2
  22. package/dist/chunk-S62ZVVO3.js +234 -0
  23. package/dist/chunk-S62ZVVO3.js.map +1 -0
  24. package/dist/chunk-SLJG53M7.js +208 -0
  25. package/dist/chunk-SLJG53M7.js.map +1 -0
  26. package/dist/{chunk-RVJC6M2U.js → chunk-V4P3UR4I.js} +2 -2
  27. package/dist/{chunk-VJXEM5O6.js → chunk-VW7HZY2B.js} +2 -2
  28. package/dist/db-accordion.d.ts +1 -1
  29. package/dist/db-accordion.js +2 -2
  30. package/dist/db-card.d.ts +1 -1
  31. package/dist/db-card.js +2 -2
  32. package/dist/db-code-block.d.ts +1 -1
  33. package/dist/db-code-block.js +2 -2
  34. package/dist/db-collapsible.d.ts +1 -1
  35. package/dist/db-collapsible.js +2 -2
  36. package/dist/db-combobox.d.ts +70 -0
  37. package/dist/db-combobox.js +9 -0
  38. package/dist/db-combobox.js.map +1 -0
  39. package/dist/db-dialog.d.ts +62 -0
  40. package/dist/db-dialog.js +9 -0
  41. package/dist/db-dialog.js.map +1 -0
  42. package/dist/db-link-button.d.ts +1 -1
  43. package/dist/db-link-button.js +2 -2
  44. package/dist/db-nav-tree.d.ts +83 -0
  45. package/dist/db-nav-tree.js +9 -0
  46. package/dist/db-nav-tree.js.map +1 -0
  47. package/dist/db-popover.d.ts +42 -0
  48. package/dist/db-popover.js +9 -0
  49. package/dist/db-popover.js.map +1 -0
  50. package/dist/db-select.d.ts +72 -0
  51. package/dist/db-select.js +9 -0
  52. package/dist/db-select.js.map +1 -0
  53. package/dist/db-steps.d.ts +1 -1
  54. package/dist/db-steps.js +2 -2
  55. package/dist/db-tabs.d.ts +8 -1
  56. package/dist/db-tabs.js +2 -2
  57. package/dist/db-toc.d.ts +32 -0
  58. package/dist/db-toc.js +8 -0
  59. package/dist/db-toc.js.map +1 -0
  60. package/dist/db-tooltip.d.ts +48 -0
  61. package/dist/db-tooltip.js +9 -0
  62. package/dist/db-tooltip.js.map +1 -0
  63. package/dist/index.d.ts +8 -1
  64. package/dist/index.js +40 -11
  65. package/package.json +30 -2
  66. package/dist/chunk-ABP4WEKU.js.map +0 -1
  67. package/dist/chunk-ZLIF4UYD.js.map +0 -1
  68. /package/dist/{chunk-IMW234S2.js.map → chunk-CLUADA3G.js.map} +0 -0
  69. /package/dist/{chunk-THLTJ2HB.js.map → chunk-ETBBSA3I.js.map} +0 -0
  70. /package/dist/{chunk-5S2MMI3R.js.map → chunk-LAUNXWKJ.js.map} +0 -0
  71. /package/dist/{chunk-WTARNLNI.js.map → chunk-QSRUT67Y.js.map} +0 -0
  72. /package/dist/{chunk-RVJC6M2U.js.map → chunk-V4P3UR4I.js.map} +0 -0
  73. /package/dist/{chunk-VJXEM5O6.js.map → chunk-VW7HZY2B.js.map} +0 -0
@@ -0,0 +1,116 @@
1
+ import {
2
+ focusables,
3
+ hideFloating,
4
+ ownOne,
5
+ resolveAsChild,
6
+ showFloating,
7
+ uid
8
+ } from "./chunk-3AEQ6KHV.js";
9
+ import {
10
+ DogsbayElement,
11
+ ensureDefaultDisplay
12
+ } from "./chunk-K6DPDBAG.js";
13
+
14
+ // src/db-popover.ts
15
+ import { DismissController } from "@dogsbay/primitives/dismiss";
16
+ import { setupFloating } from "@dogsbay/primitives/floating";
17
+ var DbPopover = class extends DogsbayElement {
18
+ trigger = null;
19
+ content = null;
20
+ dismiss = null;
21
+ cleanupFloating = null;
22
+ _open = false;
23
+ get open() {
24
+ return this._open;
25
+ }
26
+ onTriggerClick = () => this.toggle();
27
+ onContentClick = (e) => {
28
+ const close = e.target.closest("[data-part='close']");
29
+ if (close && close.closest("db-popover") === this) this.close("close-press");
30
+ };
31
+ setup() {
32
+ this.trigger = resolveAsChild(ownOne(this, "[data-part='trigger']"));
33
+ this.content = ownOne(this, "[data-part='content']");
34
+ if (!this.trigger || !this.content) return;
35
+ const content = this.content;
36
+ content.id ||= uid("db-popover");
37
+ if (!content.hasAttribute("role")) content.setAttribute("role", "dialog");
38
+ if (!content.hasAttribute("aria-labelledby") && !content.hasAttribute("aria-label")) {
39
+ this.trigger.id ||= uid("db-popover-trigger");
40
+ content.setAttribute("aria-labelledby", this.trigger.id);
41
+ }
42
+ content.hidden = true;
43
+ this.trigger.setAttribute("aria-haspopup", "dialog");
44
+ this.trigger.setAttribute("aria-controls", content.id);
45
+ this.trigger.setAttribute("aria-expanded", "false");
46
+ this.setState("closed");
47
+ this.dismiss = new DismissController(content, { onDismiss: (reason) => this.close(reason) });
48
+ }
49
+ connect() {
50
+ this.trigger?.addEventListener("click", this.onTriggerClick);
51
+ this.content?.addEventListener("click", this.onContentClick);
52
+ }
53
+ disconnect() {
54
+ this.trigger?.removeEventListener("click", this.onTriggerClick);
55
+ this.content?.removeEventListener("click", this.onContentClick);
56
+ if (this._open) this.close("api");
57
+ }
58
+ setState(state) {
59
+ this.setAttribute("data-state", state);
60
+ this.content?.setAttribute("data-state", state);
61
+ }
62
+ placement() {
63
+ const attr = this.getAttribute("placement");
64
+ if (attr) return attr;
65
+ const side = this.content?.dataset.side || "bottom";
66
+ const align = this.content?.dataset.align || "center";
67
+ return `${side}${align !== "center" ? `-${align}` : ""}`;
68
+ }
69
+ toggle() {
70
+ if (this._open) this.close("trigger-press");
71
+ else this.show();
72
+ }
73
+ show() {
74
+ const { trigger, content } = this;
75
+ if (!trigger || !content || this._open) return;
76
+ this._open = true;
77
+ const layer = showFloating(content);
78
+ this.setState("open");
79
+ trigger.setAttribute("aria-expanded", "true");
80
+ this.cleanupFloating = setupFloating(trigger, content, {
81
+ placement: this.placement(),
82
+ offset: Number(this.getAttribute("offset") ?? 8),
83
+ strategy: layer === "top-layer" ? "fixed" : "absolute"
84
+ });
85
+ this.dismiss?.activate();
86
+ if (!this.hasAttribute("no-autofocus")) {
87
+ requestAnimationFrame(() => {
88
+ if (this._open) (focusables(content)[0] ?? content).focus({ preventScroll: true });
89
+ });
90
+ }
91
+ this.dispatchEvent(new CustomEvent("db-popover:open", { bubbles: true }));
92
+ }
93
+ close(reason = "api") {
94
+ const { trigger, content } = this;
95
+ if (!trigger || !content || !this._open) return;
96
+ this._open = false;
97
+ this.dismiss?.deactivate();
98
+ this.cleanupFloating?.();
99
+ this.cleanupFloating = null;
100
+ const hadFocus = content.contains(document.activeElement);
101
+ hideFloating(content);
102
+ this.setState("closed");
103
+ trigger.setAttribute("aria-expanded", "false");
104
+ if (reason !== "outside-press" && (hadFocus || reason === "escape-key" || reason === "close-press")) trigger.focus();
105
+ this.dispatchEvent(new CustomEvent("db-popover:close", { detail: { reason }, bubbles: true }));
106
+ }
107
+ };
108
+ ensureDefaultDisplay();
109
+ if (!customElements.get("db-popover")) {
110
+ customElements.define("db-popover", DbPopover);
111
+ }
112
+
113
+ export {
114
+ DbPopover
115
+ };
116
+ //# sourceMappingURL=chunk-EGU66XCL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db-popover.ts"],"sourcesContent":["import { DismissController } from \"@dogsbay/primitives/dismiss\";\nimport { setupFloating, type FloatingOptions } from \"@dogsbay/primitives/floating\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { focusables, hideFloating, ownOne, resolveAsChild, showFloating, uid } from \"./utils.js\";\n\ntype Placement = NonNullable<FloatingOptions[\"placement\"]>;\n\n/**\n * `<db-popover>` — Non-modal floating panel (APG \"Dialog\", non-modal).\n *\n * Same parts as the Astro Popover. The content opens in the browser's top\n * layer (Popover API) where available, so no ancestor's `overflow` clips it\n * and it needs no portal, and floating-ui positions it next to the trigger.\n *\n * Attributes:\n * placement — \"bottom\" (default), \"top-start\", … (or data-side/data-align on the content)\n * offset — gap in px (default 8)\n * no-autofocus — keep focus on the trigger when opening\n *\n * Parts: [data-part=\"trigger\"], [data-part=\"content\"], [data-part=\"close\"]\n *\n * Focus: opening moves focus to the first focusable element in the content.\n * Escape and the close part return focus to the trigger; an outside click\n * leaves focus where the user clicked.\n *\n * Events: db-popover:open, db-popover:close (detail: { reason })\n */\nexport class DbPopover extends DogsbayElement {\n private trigger: HTMLElement | null = null;\n private content: HTMLElement | null = null;\n private dismiss: DismissController | null = null;\n private cleanupFloating: (() => void) | null = null;\n private _open = false;\n\n get open(): boolean {\n return this._open;\n }\n\n private onTriggerClick = () => this.toggle();\n\n private onContentClick = (e: Event) => {\n const close = (e.target as HTMLElement).closest(\"[data-part='close']\");\n if (close && close.closest(\"db-popover\") === this) this.close(\"close-press\");\n };\n\n protected setup() {\n this.trigger = resolveAsChild(ownOne(this, \"[data-part='trigger']\"));\n this.content = ownOne(this, \"[data-part='content']\");\n if (!this.trigger || !this.content) return;\n const content = this.content;\n content.id ||= uid(\"db-popover\");\n if (!content.hasAttribute(\"role\")) content.setAttribute(\"role\", \"dialog\");\n if (!content.hasAttribute(\"aria-labelledby\") && !content.hasAttribute(\"aria-label\")) {\n this.trigger.id ||= uid(\"db-popover-trigger\");\n content.setAttribute(\"aria-labelledby\", this.trigger.id);\n }\n content.hidden = true;\n this.trigger.setAttribute(\"aria-haspopup\", \"dialog\");\n this.trigger.setAttribute(\"aria-controls\", content.id);\n this.trigger.setAttribute(\"aria-expanded\", \"false\");\n this.setState(\"closed\");\n this.dismiss = new DismissController(content, { onDismiss: (reason) => this.close(reason) });\n }\n\n protected connect() {\n this.trigger?.addEventListener(\"click\", this.onTriggerClick);\n this.content?.addEventListener(\"click\", this.onContentClick);\n }\n\n protected disconnect() {\n this.trigger?.removeEventListener(\"click\", this.onTriggerClick);\n this.content?.removeEventListener(\"click\", this.onContentClick);\n if (this._open) this.close(\"api\");\n }\n\n private setState(state: \"open\" | \"closed\") {\n this.setAttribute(\"data-state\", state);\n this.content?.setAttribute(\"data-state\", state);\n }\n\n private placement(): Placement {\n const attr = this.getAttribute(\"placement\");\n if (attr) return attr as Placement;\n const side = this.content?.dataset.side || \"bottom\";\n const align = this.content?.dataset.align || \"center\";\n return `${side}${align !== \"center\" ? `-${align}` : \"\"}` as Placement;\n }\n\n toggle() {\n if (this._open) this.close(\"trigger-press\");\n else this.show();\n }\n\n show() {\n const { trigger, content } = this;\n if (!trigger || !content || this._open) return;\n this._open = true;\n const layer = showFloating(content);\n this.setState(\"open\");\n trigger.setAttribute(\"aria-expanded\", \"true\");\n this.cleanupFloating = setupFloating(trigger, content, {\n placement: this.placement(),\n offset: Number(this.getAttribute(\"offset\") ?? 8),\n strategy: layer === \"top-layer\" ? \"fixed\" : \"absolute\",\n });\n this.dismiss?.activate();\n if (!this.hasAttribute(\"no-autofocus\")) {\n requestAnimationFrame(() => {\n if (this._open) (focusables(content)[0] ?? content).focus({ preventScroll: true });\n });\n }\n this.dispatchEvent(new CustomEvent(\"db-popover:open\", { bubbles: true }));\n }\n\n close(reason: string = \"api\") {\n const { trigger, content } = this;\n if (!trigger || !content || !this._open) return;\n this._open = false;\n this.dismiss?.deactivate();\n this.cleanupFloating?.();\n this.cleanupFloating = null;\n const hadFocus = content.contains(document.activeElement);\n hideFloating(content);\n this.setState(\"closed\");\n trigger.setAttribute(\"aria-expanded\", \"false\");\n if (reason !== \"outside-press\" && (hadFocus || reason === \"escape-key\" || reason === \"close-press\")) trigger.focus();\n this.dispatchEvent(new CustomEvent(\"db-popover:close\", { detail: { reason }, bubbles: true }));\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-popover\")) {\n customElements.define(\"db-popover\", DbPopover);\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DogsbayElement,
3
3
  ensureDefaultDisplay
4
- } from "./chunk-ABP4WEKU.js";
4
+ } from "./chunk-K6DPDBAG.js";
5
5
 
6
6
  // src/db-card.ts
7
7
  var DbCard = class extends DogsbayElement {
@@ -51,4 +51,4 @@ if (!customElements.get("db-card")) {
51
51
  export {
52
52
  DbCard
53
53
  };
54
- //# sourceMappingURL=chunk-THLTJ2HB.js.map
54
+ //# sourceMappingURL=chunk-ETBBSA3I.js.map
@@ -0,0 +1,90 @@
1
+ import {
2
+ DogsbayElement,
3
+ ensureDefaultDisplay
4
+ } from "./chunk-K6DPDBAG.js";
5
+
6
+ // src/db-toc.ts
7
+ var DbToc = class extends DogsbayElement {
8
+ links = [];
9
+ headings = [];
10
+ observer = null;
11
+ active = null;
12
+ slugOf(a) {
13
+ return a.getAttribute("data-toc-slug") ?? safeDecode(a.getAttribute("href").slice(1));
14
+ }
15
+ // Headings in a short tail never reach the observed band; at the bottom of the
16
+ // page the last one is current.
17
+ onScroll = () => {
18
+ const doc = document.documentElement;
19
+ if (window.innerHeight + window.scrollY >= doc.scrollHeight - 2 && this.headings.length) {
20
+ this.setActive(this.headings[this.headings.length - 1].id);
21
+ }
22
+ };
23
+ setup() {
24
+ this.links = Array.from(this.querySelectorAll("a[href^='#'], a[data-toc-link]"));
25
+ }
26
+ onHash = () => {
27
+ const id = safeDecode(location.hash.slice(1));
28
+ if (id && this.headings.some((h) => h.id === id)) this.setActive(id);
29
+ };
30
+ connect() {
31
+ this.headings = this.links.map((a) => document.getElementById(this.slugOf(a))).filter((h) => !!h);
32
+ if (!this.headings.length) return;
33
+ const offset = Number(this.getAttribute("offset") ?? 80);
34
+ if (typeof IntersectionObserver !== "undefined") {
35
+ this.observer = new IntersectionObserver(
36
+ (entries) => {
37
+ const hit = entries.find((e) => e.isIntersecting);
38
+ if (hit) return this.setActive(hit.target.id);
39
+ this.setActive(this.lastAbove(offset)?.id ?? null);
40
+ },
41
+ { rootMargin: `-${offset}px 0px -70% 0px`, threshold: 0 }
42
+ );
43
+ this.headings.forEach((h) => this.observer.observe(h));
44
+ }
45
+ window.addEventListener("hashchange", this.onHash);
46
+ window.addEventListener("scroll", this.onScroll, { passive: true });
47
+ this.setActive(this.lastAbove(offset)?.id ?? this.headings[0].id);
48
+ }
49
+ disconnect() {
50
+ this.observer?.disconnect();
51
+ this.observer = null;
52
+ window.removeEventListener("hashchange", this.onHash);
53
+ window.removeEventListener("scroll", this.onScroll);
54
+ }
55
+ lastAbove(offset) {
56
+ let last = null;
57
+ for (const h of this.headings) if (h.getBoundingClientRect().top <= offset + 20) last = h;
58
+ return last;
59
+ }
60
+ setActive(id) {
61
+ if (!id || id === this.active) return;
62
+ this.active = id;
63
+ for (const a of this.links) {
64
+ if (this.slugOf(a) === id) {
65
+ a.setAttribute("aria-current", "location");
66
+ a.setAttribute("data-active", "");
67
+ } else {
68
+ a.removeAttribute("aria-current");
69
+ a.removeAttribute("data-active");
70
+ }
71
+ }
72
+ this.dispatchEvent(new CustomEvent("db-toc:change", { detail: { id }, bubbles: true }));
73
+ }
74
+ };
75
+ function safeDecode(s) {
76
+ try {
77
+ return decodeURIComponent(s);
78
+ } catch {
79
+ return s;
80
+ }
81
+ }
82
+ ensureDefaultDisplay();
83
+ if (!customElements.get("db-toc")) {
84
+ customElements.define("db-toc", DbToc);
85
+ }
86
+
87
+ export {
88
+ DbToc
89
+ };
90
+ //# sourceMappingURL=chunk-FIJZRZEN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db-toc.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-toc>` — \"On this page\" links that follow the reader (scroll spy).\n *\n * Wraps plain in-page links, so it works as a list of links without\n * JavaScript. The link for the section in view gets `aria-current=\"location\"`\n * and `data-active` (the Astro TableOfContents contract).\n *\n * Links: `a[href^=\"#\"]`, or `[data-toc-link][data-toc-slug]` as in the Astro component.\n *\n * Attributes:\n * offset — px hidden under a sticky header (default 80)\n *\n * Events: db-toc:change (detail: { id })\n */\nexport class DbToc extends DogsbayElement {\n private links: HTMLAnchorElement[] = [];\n private headings: HTMLElement[] = [];\n private observer: IntersectionObserver | null = null;\n private active: string | null = null;\n\n private slugOf(a: HTMLAnchorElement): string {\n return a.getAttribute(\"data-toc-slug\") ?? safeDecode(a.getAttribute(\"href\")!.slice(1));\n }\n\n // Headings in a short tail never reach the observed band; at the bottom of the\n // page the last one is current.\n private onScroll = () => {\n const doc = document.documentElement;\n if (window.innerHeight + window.scrollY >= doc.scrollHeight - 2 && this.headings.length) {\n this.setActive(this.headings[this.headings.length - 1].id);\n }\n };\n\n protected setup() {\n this.links = Array.from(this.querySelectorAll<HTMLAnchorElement>(\"a[href^='#'], a[data-toc-link]\"));\n }\n\n private onHash = () => {\n const id = safeDecode(location.hash.slice(1));\n if (id && this.headings.some((h) => h.id === id)) this.setActive(id);\n };\n\n protected connect() {\n this.headings = this.links.map((a) => document.getElementById(this.slugOf(a))).filter((h): h is HTMLElement => !!h);\n if (!this.headings.length) return;\n const offset = Number(this.getAttribute(\"offset\") ?? 80);\n if (typeof IntersectionObserver !== \"undefined\") {\n this.observer = new IntersectionObserver(\n (entries) => {\n const hit = entries.find((e) => e.isIntersecting);\n if (hit) return this.setActive(hit.target.id);\n this.setActive(this.lastAbove(offset)?.id ?? null);\n },\n { rootMargin: `-${offset}px 0px -70% 0px`, threshold: 0 },\n );\n this.headings.forEach((h) => this.observer!.observe(h));\n }\n window.addEventListener(\"hashchange\", this.onHash);\n window.addEventListener(\"scroll\", this.onScroll, { passive: true });\n this.setActive(this.lastAbove(offset)?.id ?? this.headings[0].id);\n }\n\n protected disconnect() {\n this.observer?.disconnect();\n this.observer = null;\n window.removeEventListener(\"hashchange\", this.onHash);\n window.removeEventListener(\"scroll\", this.onScroll);\n }\n\n private lastAbove(offset: number): HTMLElement | null {\n let last: HTMLElement | null = null;\n for (const h of this.headings) if (h.getBoundingClientRect().top <= offset + 20) last = h;\n return last;\n }\n\n private setActive(id: string | null) {\n if (!id || id === this.active) return;\n this.active = id;\n for (const a of this.links) {\n if (this.slugOf(a) === id) {\n a.setAttribute(\"aria-current\", \"location\");\n a.setAttribute(\"data-active\", \"\");\n } else {\n a.removeAttribute(\"aria-current\");\n a.removeAttribute(\"data-active\");\n }\n }\n this.dispatchEvent(new CustomEvent(\"db-toc:change\", { detail: { id }, bubbles: true }));\n }\n}\n\nfunction safeDecode(s: string): string {\n try {\n return decodeURIComponent(s);\n } catch {\n return s;\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-toc\")) {\n customElements.define(\"db-toc\", DbToc);\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,22 @@
1
1
  // src/base.ts
2
2
  var DogsbayElement = class extends HTMLElement {
3
3
  _initialized = false;
4
+ _deferred = false;
4
5
  connectedCallback() {
6
+ if (!this._initialized && !this.firstElementChild && typeof document !== "undefined" && document.readyState === "loading") {
7
+ if (!this._deferred) {
8
+ this._deferred = true;
9
+ document.addEventListener(
10
+ "DOMContentLoaded",
11
+ () => {
12
+ this._deferred = false;
13
+ if (this.isConnected) this.connectedCallback();
14
+ },
15
+ { once: true }
16
+ );
17
+ }
18
+ return;
19
+ }
5
20
  if (!this._initialized) {
6
21
  this.setup();
7
22
  this._initialized = true;
@@ -29,7 +44,15 @@ var DEFAULT_DISPLAY = {
29
44
  "db-steps": "block",
30
45
  "db-code-block": "block",
31
46
  // A link styled as a button — sits in a line of prose.
32
- "db-link-button": "inline-block"
47
+ "db-link-button": "inline-block",
48
+ // A dialog host only groups its trigger and content; it takes no box of its own.
49
+ "db-dialog": "contents",
50
+ "db-popover": "inline-block",
51
+ "db-tooltip": "inline-block",
52
+ "db-select": "inline-block",
53
+ "db-combobox": "block",
54
+ "db-nav-tree": "block",
55
+ "db-toc": "block"
33
56
  };
34
57
  var STYLE_ID = "dogsbay-elements-display";
35
58
  function ensureDefaultDisplay() {
@@ -46,4 +69,4 @@ export {
46
69
  DogsbayElement,
47
70
  ensureDefaultDisplay
48
71
  };
49
- //# sourceMappingURL=chunk-ABP4WEKU.js.map
72
+ //# sourceMappingURL=chunk-K6DPDBAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base.ts"],"sourcesContent":["/**\n * Base class for Dogsbay custom elements.\n *\n * Handles the connect/disconnect lifecycle and ensures one-time setup\n * runs only once even when Obsidian's virtual scrolling removes and\n * re-adds elements to the DOM.\n */\nexport abstract class DogsbayElement extends HTMLElement {\n private _initialized = false;\n private _deferred = false;\n\n connectedCallback() {\n // A script that runs before the markup is parsed (a classic script in\n // <head>) connects each element at its start tag, before its children\n // exist. Wait for the document so setup sees the whole subtree.\n if (!this._initialized && !this.firstElementChild && typeof document !== \"undefined\" && document.readyState === \"loading\") {\n // One pending deferral: moving the element while the page loads must not\n // queue a second one (connect() would then run twice).\n if (!this._deferred) {\n this._deferred = true;\n document.addEventListener(\n \"DOMContentLoaded\",\n () => {\n this._deferred = false;\n if (this.isConnected) this.connectedCallback();\n },\n { once: true },\n );\n }\n return;\n }\n if (!this._initialized) {\n this.setup();\n this._initialized = true;\n }\n this.connect();\n }\n\n disconnectedCallback() {\n this.disconnect();\n }\n\n /** One-time setup: query children, generate IDs, set ARIA. */\n protected setup() {}\n\n /** Called on every connect: attach event listeners. */\n protected connect() {}\n\n /** Called on every disconnect: remove event listeners. */\n protected disconnect() {}\n}\n\n/**\n * Default `display` for each element.\n *\n * Custom elements have NO user-agent style, so an unstyled one is\n * `display: inline`. That is silently wrong for a container: `w-full`\n * does nothing on an inline box, and a border renders as detached\n * fragments at the start and end of the inline run rather than around\n * the content.\n *\n * `<db-tabs>` carries `border rounded-lg overflow-hidden w-full` from\n * Tabs.astro, so it showed short vertical bars above and below every\n * tab group on every generated site. The other six had the same defect\n * without a border to make it visible.\n */\nconst DEFAULT_DISPLAY: Record<string, string> = {\n \"db-tabs\": \"block\",\n \"db-accordion\": \"block\",\n \"db-collapsible\": \"block\",\n \"db-card\": \"block\",\n \"db-steps\": \"block\",\n \"db-code-block\": \"block\",\n // A link styled as a button — sits in a line of prose.\n \"db-link-button\": \"inline-block\",\n // A dialog host only groups its trigger and content; it takes no box of its own.\n \"db-dialog\": \"contents\",\n \"db-popover\": \"inline-block\",\n \"db-tooltip\": \"inline-block\",\n \"db-select\": \"inline-block\",\n \"db-combobox\": \"block\",\n \"db-nav-tree\": \"block\",\n \"db-toc\": \"block\",\n};\n\nconst STYLE_ID = \"dogsbay-elements-display\";\n\n/**\n * Give the elements a sane default `display`.\n *\n * Injected from here rather than shipped as CSS because a Dogsbay site\n * scaffolds `global.css` ONCE and copies UI components as source — so\n * neither updates when a package does. The element JS is the only part\n * of an existing site that a version bump actually reaches.\n *\n * `:where()` keeps specificity at zero, so ANY author rule or utility\n * class overrides it — this sets a default, it does not win arguments.\n *\n * Idempotent: safe to call from every element module, and Obsidian's\n * virtual scrolling re-registers freely.\n */\nexport function ensureDefaultDisplay(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(STYLE_ID)) return;\n\n const rules = Object.entries(DEFAULT_DISPLAY)\n .map(([tag, display]) => `:where(${tag}){display:${display}}`)\n .join(\"\");\n\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = rules;\n // Prepend so a later author stylesheet wins on equal specificity.\n document.head.prepend(style);\n}\n"],"mappings":";AAOO,IAAe,iBAAf,cAAsC,YAAY;AAAA,EAC/C,eAAe;AAAA,EACf,YAAY;AAAA,EAEpB,oBAAoB;AAIlB,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,qBAAqB,OAAO,aAAa,eAAe,SAAS,eAAe,WAAW;AAGzH,UAAI,CAAC,KAAK,WAAW;AACnB,aAAK,YAAY;AACjB,iBAAS;AAAA,UACP;AAAA,UACA,MAAM;AACJ,iBAAK,YAAY;AACjB,gBAAI,KAAK,YAAa,MAAK,kBAAkB;AAAA,UAC/C;AAAA,UACA,EAAE,MAAM,KAAK;AAAA,QACf;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,MAAM;AACX,WAAK,eAAe;AAAA,IACtB;AACA,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,uBAAuB;AACrB,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA,EAGU,QAAQ;AAAA,EAAC;AAAA;AAAA,EAGT,UAAU;AAAA,EAAC;AAAA;AAAA,EAGX,aAAa;AAAA,EAAC;AAC1B;AAgBA,IAAM,kBAA0C;AAAA,EAC9C,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,iBAAiB;AAAA;AAAA,EAEjB,kBAAkB;AAAA;AAAA,EAElB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAAA,EACf,UAAU;AACZ;AAEA,IAAM,WAAW;AAgBV,SAAS,uBAA6B;AAC3C,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,eAAe,QAAQ,EAAG;AAEvC,QAAM,QAAQ,OAAO,QAAQ,eAAe,EACzC,IAAI,CAAC,CAAC,KAAK,OAAO,MAAM,UAAU,GAAG,aAAa,OAAO,GAAG,EAC5D,KAAK,EAAE;AAEV,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cAAc;AAEpB,WAAS,KAAK,QAAQ,KAAK;AAC7B;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DogsbayElement,
3
3
  ensureDefaultDisplay
4
- } from "./chunk-ABP4WEKU.js";
4
+ } from "./chunk-K6DPDBAG.js";
5
5
 
6
6
  // src/db-collapsible.ts
7
7
  import { Collapsible } from "@dogsbay/primitives/collapsible";
@@ -45,4 +45,4 @@ if (!customElements.get("db-collapsible")) {
45
45
  export {
46
46
  DbCollapsible
47
47
  };
48
- //# sourceMappingURL=chunk-5S2MMI3R.js.map
48
+ //# sourceMappingURL=chunk-LAUNXWKJ.js.map
@@ -0,0 +1,256 @@
1
+ import {
2
+ isDisabled,
3
+ ownOne,
4
+ uid
5
+ } from "./chunk-3AEQ6KHV.js";
6
+ import {
7
+ DogsbayElement,
8
+ ensureDefaultDisplay
9
+ } from "./chunk-K6DPDBAG.js";
10
+
11
+ // src/db-combobox.ts
12
+ var DbCombobox = class extends DogsbayElement {
13
+ input = null;
14
+ listbox = null;
15
+ empty = null;
16
+ _open = false;
17
+ highlighted = null;
18
+ observer = null;
19
+ base = uid("db-combobox");
20
+ optionSeq = 0;
21
+ get open() {
22
+ return this._open;
23
+ }
24
+ get inline() {
25
+ return this.hasAttribute("inline");
26
+ }
27
+ get filtering() {
28
+ return (this.getAttribute("filter") ?? "contains") !== "none";
29
+ }
30
+ /** All options, in order. Read live, so page-supplied results are always current. */
31
+ options() {
32
+ if (!this.listbox) return [];
33
+ return Array.from(this.listbox.querySelectorAll("[data-part='option']")).filter((o) => o.closest("db-combobox") === this);
34
+ }
35
+ visible() {
36
+ return this.options().filter((o) => !o.hidden && !isDisabled(o));
37
+ }
38
+ setup() {
39
+ this.input = ownOne(this, "[data-part='input']");
40
+ this.listbox = ownOne(this, "[data-part='listbox']");
41
+ this.empty = ownOne(this, "[data-part='empty']");
42
+ if (!this.input || !this.listbox) return;
43
+ const { input, listbox } = this;
44
+ listbox.id ||= `${this.base}-listbox`;
45
+ listbox.setAttribute("role", "listbox");
46
+ input.setAttribute("role", "combobox");
47
+ input.setAttribute("aria-autocomplete", "list");
48
+ input.setAttribute("aria-controls", listbox.id);
49
+ input.setAttribute("autocomplete", "off");
50
+ if (!this.inline) listbox.hidden = true;
51
+ this.prepareOptions();
52
+ this.syncExpanded();
53
+ }
54
+ prepareOptions() {
55
+ this.options().forEach((o) => {
56
+ o.id ||= `${this.base}-option-${++this.optionSeq}`;
57
+ o.setAttribute("role", "option");
58
+ if (!o.hasAttribute("aria-selected")) o.setAttribute("aria-selected", "false");
59
+ if (o.matches("a[href], button")) o.setAttribute("tabindex", "-1");
60
+ o.querySelectorAll("a, button").forEach((el) => el.setAttribute("tabindex", "-1"));
61
+ if (o.parentElement && o.parentElement !== this.listbox && o.parentElement.localName === "li" && !o.parentElement.hasAttribute("role")) {
62
+ o.parentElement.setAttribute("role", "none");
63
+ }
64
+ });
65
+ }
66
+ onInput = () => {
67
+ if (this.filtering) this.filter(this.input.value);
68
+ else this.show();
69
+ };
70
+ onFocus = () => {
71
+ if (this.hasAttribute("open-on-focus") || this.filtering && this.input.value) {
72
+ if (this.filtering) this.filter(this.input.value);
73
+ else this.show();
74
+ }
75
+ };
76
+ onKey = (e) => {
77
+ const vis = this.visible();
78
+ const cur = this.highlighted ? vis.indexOf(this.highlighted) : -1;
79
+ switch (e.key) {
80
+ case "ArrowDown":
81
+ e.preventDefault();
82
+ if (!this._open) this.show();
83
+ if (vis.length) this.highlight(vis[cur < 0 ? 0 : Math.min(cur + 1, vis.length - 1)]);
84
+ break;
85
+ case "ArrowUp":
86
+ e.preventDefault();
87
+ if (!this._open) this.show();
88
+ if (vis.length) this.highlight(vis[cur < 0 ? vis.length - 1 : Math.max(cur - 1, 0)]);
89
+ break;
90
+ case "Home":
91
+ case "End":
92
+ if (this._open && this.highlighted && vis.length) {
93
+ e.preventDefault();
94
+ this.highlight(e.key === "Home" ? vis[0] : vis[vis.length - 1]);
95
+ }
96
+ break;
97
+ case "Enter":
98
+ if (this._open && this.highlighted) {
99
+ e.preventDefault();
100
+ this.choose(this.highlighted);
101
+ }
102
+ break;
103
+ case "Escape":
104
+ if (this._open && !this.inline) {
105
+ e.preventDefault();
106
+ e.stopPropagation();
107
+ this.hide();
108
+ } else if (this.input.value) {
109
+ e.preventDefault();
110
+ e.stopPropagation();
111
+ this.input.value = "";
112
+ this.input.dispatchEvent(new Event("input", { bubbles: true }));
113
+ this.hide();
114
+ }
115
+ break;
116
+ case "Tab":
117
+ if (!this.inline) this.hide();
118
+ break;
119
+ }
120
+ };
121
+ onListClick = (e) => {
122
+ const o = e.target.closest("[data-part='option']");
123
+ if (!o || o.closest("db-combobox") !== this || isDisabled(o)) return;
124
+ const link = e.target.closest("a[href]");
125
+ if (!this.choose(o, !link) && link) e.preventDefault();
126
+ };
127
+ onListPointer = (e) => {
128
+ const o = e.target.closest("[data-part='option']");
129
+ if (o && o.closest("db-combobox") === this && !isDisabled(o) && !o.hidden) this.highlight(o);
130
+ };
131
+ onListDown = (e) => e.preventDefault();
132
+ // keep focus in the input
133
+ onDocDown = (e) => {
134
+ if (this._open && !this.inline && !this.contains(e.target)) this.hide();
135
+ };
136
+ connect() {
137
+ const { input, listbox } = this;
138
+ if (!input || !listbox) return;
139
+ input.addEventListener("input", this.onInput);
140
+ input.addEventListener("focus", this.onFocus);
141
+ input.addEventListener("keydown", this.onKey);
142
+ listbox.addEventListener("click", this.onListClick);
143
+ listbox.addEventListener("pointermove", this.onListPointer);
144
+ listbox.addEventListener("mousedown", this.onListDown);
145
+ document.addEventListener("pointerdown", this.onDocDown);
146
+ if (typeof MutationObserver !== "undefined") {
147
+ this.observer = new MutationObserver(() => this.onOptionsChanged());
148
+ this.observer.observe(listbox, { childList: true, subtree: true });
149
+ }
150
+ }
151
+ disconnect() {
152
+ const { input, listbox } = this;
153
+ input?.removeEventListener("input", this.onInput);
154
+ input?.removeEventListener("focus", this.onFocus);
155
+ input?.removeEventListener("keydown", this.onKey);
156
+ listbox?.removeEventListener("click", this.onListClick);
157
+ listbox?.removeEventListener("pointermove", this.onListPointer);
158
+ listbox?.removeEventListener("mousedown", this.onListDown);
159
+ document.removeEventListener("pointerdown", this.onDocDown);
160
+ this.observer?.disconnect();
161
+ this.observer = null;
162
+ }
163
+ /** Re-scan options after the page replaced them (filter="none"). */
164
+ refresh() {
165
+ this.onOptionsChanged();
166
+ }
167
+ onOptionsChanged() {
168
+ this.prepareOptions();
169
+ if (this.highlighted && !this.options().includes(this.highlighted)) this.clearHighlight();
170
+ const vis = this.visible();
171
+ if (!this.filtering) {
172
+ if (vis.length || this.inline) this.show();
173
+ else this.hide();
174
+ if (vis.length && !this.highlighted && !this.hasAttribute("no-autohighlight")) this.highlight(vis[0]);
175
+ }
176
+ this.syncExpanded();
177
+ this.dispatchEvent(new CustomEvent("db-combobox:update", { detail: { count: vis.length }, bubbles: true }));
178
+ }
179
+ filter(query) {
180
+ const q = query.toLowerCase().trim();
181
+ let count = 0;
182
+ for (const o of this.options()) {
183
+ const hit = !q || (o.textContent ?? "").toLowerCase().includes(q) || (o.dataset.value ?? "").toLowerCase().includes(q);
184
+ o.hidden = !hit;
185
+ if (hit) count++;
186
+ }
187
+ if (this.empty) this.empty.hidden = count > 0 || !q;
188
+ this.clearHighlight();
189
+ if (count || q && this.empty) this.show();
190
+ else this.hide();
191
+ const vis = this.visible();
192
+ if (vis.length && !this.hasAttribute("no-autohighlight")) this.highlight(vis[0]);
193
+ this.dispatchEvent(new CustomEvent("db-combobox:update", { detail: { count }, bubbles: true }));
194
+ }
195
+ syncExpanded() {
196
+ if (!this.input) return;
197
+ const expanded = this.inline ? this.visible().length > 0 : this._open;
198
+ this.input.setAttribute("aria-expanded", String(expanded));
199
+ this.setAttribute("data-state", expanded ? "open" : "closed");
200
+ }
201
+ show() {
202
+ if (!this.listbox || this._open) return this.syncExpanded();
203
+ this._open = true;
204
+ this.listbox.hidden = false;
205
+ this.syncExpanded();
206
+ }
207
+ hide() {
208
+ if (!this.listbox) return;
209
+ this.clearHighlight();
210
+ if (!this._open) return this.syncExpanded();
211
+ this._open = false;
212
+ if (!this.inline) this.listbox.hidden = true;
213
+ this.syncExpanded();
214
+ }
215
+ highlight(o) {
216
+ if (!this.input) return;
217
+ this.highlighted?.removeAttribute("data-highlighted");
218
+ this.highlighted?.setAttribute("aria-selected", "false");
219
+ this.highlighted = o;
220
+ o.setAttribute("data-highlighted", "");
221
+ o.setAttribute("aria-selected", "true");
222
+ this.input.setAttribute("aria-activedescendant", o.id);
223
+ o.scrollIntoView?.({ block: "nearest" });
224
+ }
225
+ clearHighlight() {
226
+ this.highlighted?.removeAttribute("data-highlighted");
227
+ this.highlighted?.setAttribute("aria-selected", "false");
228
+ this.highlighted = null;
229
+ this.input?.removeAttribute("aria-activedescendant");
230
+ }
231
+ /** Returns false when a `db-combobox:select` listener cancelled the choice. */
232
+ choose(o, followLink = true) {
233
+ const value = o.dataset.value ?? "";
234
+ const text = o.textContent?.trim() ?? value;
235
+ const ev = new CustomEvent("db-combobox:select", { detail: { value, text, option: o }, bubbles: true, cancelable: true });
236
+ if (!this.dispatchEvent(ev)) return false;
237
+ const link = o.matches("a[href]") ? o : o.querySelector("a[href]");
238
+ if (link) {
239
+ if (followLink) link.click();
240
+ return true;
241
+ }
242
+ if (this.input) this.input.value = o.getAttribute("data-label") ?? text;
243
+ if (!this.inline) this.hide();
244
+ this.dispatchEvent(new CustomEvent("db-combobox:change", { detail: { value, text }, bubbles: true }));
245
+ return true;
246
+ }
247
+ };
248
+ ensureDefaultDisplay();
249
+ if (!customElements.get("db-combobox")) {
250
+ customElements.define("db-combobox", DbCombobox);
251
+ }
252
+
253
+ export {
254
+ DbCombobox
255
+ };
256
+ //# sourceMappingURL=chunk-MAD6XJY4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db-combobox.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { isDisabled, ownOne, uid } from \"./utils.js\";\n\n/**\n * `<db-combobox>` — Editable combobox with a listbox (APG \"Editable\n * Combobox With List Autocomplete\"). Focus stays in the input; the\n * highlighted option is exposed with aria-activedescendant.\n *\n * Parts (same contract as the Astro Combobox):\n * [data-part=\"input\"] text input, role=combobox\n * [data-part=\"listbox\"] role=listbox\n * [data-part=\"option\"] role=option, data-value, data-disabled\n * [data-part=\"empty\"] shown when a query has no matches (optional)\n *\n * Attributes:\n * filter=\"contains\" (default) the element filters its own options by text\n * filter=\"none\" the page supplies options (search results); the element\n * watches the listbox and manages highlight and ARIA\n * inline the listbox is always shown (e.g. inside a search dialog);\n * aria-expanded tracks whether it has options\n * no-autohighlight do not highlight the first option when the list changes\n *\n * Choosing an option dispatches a cancelable `db-combobox:select`\n * (detail: { value, text, option }). If not cancelled, a link option\n * (`<a href data-part=\"option\">`, the accessible shape for results that\n * navigate: ARIA allows role=option on a link, but not a link inside an\n * option) is followed; otherwise the input takes the option's text and\n * `db-combobox:change` fires. Wrap link options in `<li role=\"none\">`.\n *\n * Other events: db-combobox:update (detail: { count }) when the visible options change.\n */\nexport class DbCombobox extends DogsbayElement {\n private input: HTMLInputElement | null = null;\n private listbox: HTMLElement | null = null;\n private empty: HTMLElement | null = null;\n private _open = false;\n private highlighted: HTMLElement | null = null;\n private observer: MutationObserver | null = null;\n private base = uid(\"db-combobox\");\n private optionSeq = 0;\n\n get open(): boolean {\n return this._open;\n }\n\n private get inline(): boolean {\n return this.hasAttribute(\"inline\");\n }\n\n private get filtering(): boolean {\n return (this.getAttribute(\"filter\") ?? \"contains\") !== \"none\";\n }\n\n /** All options, in order. Read live, so page-supplied results are always current. */\n options(): HTMLElement[] {\n if (!this.listbox) return [];\n return Array.from(this.listbox.querySelectorAll<HTMLElement>(\"[data-part='option']\")).filter((o) => o.closest(\"db-combobox\") === this);\n }\n\n private visible(): HTMLElement[] {\n return this.options().filter((o) => !o.hidden && !isDisabled(o));\n }\n\n protected setup() {\n this.input = ownOne<HTMLInputElement>(this, \"[data-part='input']\");\n this.listbox = ownOne(this, \"[data-part='listbox']\");\n this.empty = ownOne(this, \"[data-part='empty']\");\n if (!this.input || !this.listbox) return;\n const { input, listbox } = this;\n listbox.id ||= `${this.base}-listbox`;\n listbox.setAttribute(\"role\", \"listbox\");\n input.setAttribute(\"role\", \"combobox\");\n input.setAttribute(\"aria-autocomplete\", \"list\");\n input.setAttribute(\"aria-controls\", listbox.id);\n input.setAttribute(\"autocomplete\", \"off\");\n if (!this.inline) listbox.hidden = true;\n this.prepareOptions();\n this.syncExpanded();\n }\n\n private prepareOptions() {\n this.options().forEach((o) => {\n // A running counter, not the index: page-supplied options come and go, and a\n // reused id would point aria-activedescendant at the wrong option.\n o.id ||= `${this.base}-option-${++this.optionSeq}`;\n o.setAttribute(\"role\", \"option\");\n if (!o.hasAttribute(\"aria-selected\")) o.setAttribute(\"aria-selected\", \"false\");\n // Options are reached through the combobox, not by Tab.\n if (o.matches(\"a[href], button\")) o.setAttribute(\"tabindex\", \"-1\");\n o.querySelectorAll(\"a, button\").forEach((el) => el.setAttribute(\"tabindex\", \"-1\"));\n if (o.parentElement && o.parentElement !== this.listbox && o.parentElement.localName === \"li\" && !o.parentElement.hasAttribute(\"role\")) {\n o.parentElement.setAttribute(\"role\", \"none\");\n }\n });\n }\n\n private onInput = () => {\n if (this.filtering) this.filter(this.input!.value);\n else this.show();\n };\n\n private onFocus = () => {\n if (this.hasAttribute(\"open-on-focus\") || (this.filtering && this.input!.value)) {\n if (this.filtering) this.filter(this.input!.value);\n else this.show();\n }\n };\n\n private onKey = (e: KeyboardEvent) => {\n const vis = this.visible();\n const cur = this.highlighted ? vis.indexOf(this.highlighted) : -1;\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n if (!this._open) this.show();\n if (vis.length) this.highlight(vis[cur < 0 ? 0 : Math.min(cur + 1, vis.length - 1)]);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n if (!this._open) this.show();\n if (vis.length) this.highlight(vis[cur < 0 ? vis.length - 1 : Math.max(cur - 1, 0)]);\n break;\n case \"Home\":\n case \"End\":\n // In a text field Home/End move the caret unless the list is being navigated.\n if (this._open && this.highlighted && vis.length) {\n e.preventDefault();\n this.highlight(e.key === \"Home\" ? vis[0] : vis[vis.length - 1]);\n }\n break;\n case \"Enter\":\n if (this._open && this.highlighted) {\n e.preventDefault();\n this.choose(this.highlighted);\n }\n break;\n case \"Escape\":\n if (this._open && !this.inline) {\n e.preventDefault();\n e.stopPropagation();\n this.hide();\n } else if (this.input!.value) {\n // Escape on a closed (or inline) list clears the field, per APG.\n e.preventDefault();\n e.stopPropagation();\n this.input!.value = \"\";\n this.input!.dispatchEvent(new Event(\"input\", { bubbles: true }));\n // Cleared and closed (APG): the input event would otherwise reopen the full list.\n this.hide();\n }\n break;\n case \"Tab\":\n if (!this.inline) this.hide();\n break;\n }\n };\n\n private onListClick = (e: MouseEvent) => {\n const o = (e.target as HTMLElement).closest<HTMLElement>(\"[data-part='option']\");\n if (!o || o.closest(\"db-combobox\") !== this || isDisabled(o)) return;\n // A real link click navigates by itself; only announce the choice, and stop the\n // navigation when a listener cancels it.\n const link = (e.target as HTMLElement).closest(\"a[href]\");\n if (!this.choose(o, !link) && link) e.preventDefault();\n };\n\n private onListPointer = (e: PointerEvent) => {\n const o = (e.target as HTMLElement).closest<HTMLElement>(\"[data-part='option']\");\n if (o && o.closest(\"db-combobox\") === this && !isDisabled(o) && !o.hidden) this.highlight(o);\n };\n\n private onListDown = (e: Event) => e.preventDefault(); // keep focus in the input\n\n private onDocDown = (e: PointerEvent) => {\n if (this._open && !this.inline && !this.contains(e.target as Node)) this.hide();\n };\n\n protected connect() {\n const { input, listbox } = this;\n if (!input || !listbox) return;\n input.addEventListener(\"input\", this.onInput);\n input.addEventListener(\"focus\", this.onFocus);\n input.addEventListener(\"keydown\", this.onKey);\n listbox.addEventListener(\"click\", this.onListClick);\n listbox.addEventListener(\"pointermove\", this.onListPointer as EventListener);\n listbox.addEventListener(\"mousedown\", this.onListDown);\n document.addEventListener(\"pointerdown\", this.onDocDown as EventListener);\n if (typeof MutationObserver !== \"undefined\") {\n this.observer = new MutationObserver(() => this.onOptionsChanged());\n this.observer.observe(listbox, { childList: true, subtree: true });\n }\n }\n\n protected disconnect() {\n const { input, listbox } = this;\n input?.removeEventListener(\"input\", this.onInput);\n input?.removeEventListener(\"focus\", this.onFocus);\n input?.removeEventListener(\"keydown\", this.onKey);\n listbox?.removeEventListener(\"click\", this.onListClick);\n listbox?.removeEventListener(\"pointermove\", this.onListPointer as EventListener);\n listbox?.removeEventListener(\"mousedown\", this.onListDown);\n document.removeEventListener(\"pointerdown\", this.onDocDown as EventListener);\n this.observer?.disconnect();\n this.observer = null;\n }\n\n /** Re-scan options after the page replaced them (filter=\"none\"). */\n refresh() {\n this.onOptionsChanged();\n }\n\n private onOptionsChanged() {\n this.prepareOptions();\n if (this.highlighted && !this.options().includes(this.highlighted)) this.clearHighlight();\n const vis = this.visible();\n if (!this.filtering) {\n if (vis.length || this.inline) this.show();\n else this.hide();\n if (vis.length && !this.highlighted && !this.hasAttribute(\"no-autohighlight\")) this.highlight(vis[0]);\n }\n this.syncExpanded();\n this.dispatchEvent(new CustomEvent(\"db-combobox:update\", { detail: { count: vis.length }, bubbles: true }));\n }\n\n private filter(query: string) {\n const q = query.toLowerCase().trim();\n let count = 0;\n for (const o of this.options()) {\n const hit = !q || (o.textContent ?? \"\").toLowerCase().includes(q) || (o.dataset.value ?? \"\").toLowerCase().includes(q);\n o.hidden = !hit;\n if (hit) count++;\n }\n if (this.empty) this.empty.hidden = count > 0 || !q;\n this.clearHighlight();\n if (count || (q && this.empty)) this.show();\n else this.hide();\n const vis = this.visible();\n if (vis.length && !this.hasAttribute(\"no-autohighlight\")) this.highlight(vis[0]);\n this.dispatchEvent(new CustomEvent(\"db-combobox:update\", { detail: { count }, bubbles: true }));\n }\n\n private syncExpanded() {\n if (!this.input) return;\n const expanded = this.inline ? this.visible().length > 0 : this._open;\n this.input.setAttribute(\"aria-expanded\", String(expanded));\n this.setAttribute(\"data-state\", expanded ? \"open\" : \"closed\");\n }\n\n show() {\n if (!this.listbox || this._open) return this.syncExpanded();\n this._open = true;\n this.listbox.hidden = false;\n this.syncExpanded();\n }\n\n hide() {\n if (!this.listbox) return;\n this.clearHighlight();\n if (!this._open) return this.syncExpanded();\n this._open = false;\n if (!this.inline) this.listbox.hidden = true;\n this.syncExpanded();\n }\n\n private highlight(o: HTMLElement) {\n if (!this.input) return;\n this.highlighted?.removeAttribute(\"data-highlighted\");\n this.highlighted?.setAttribute(\"aria-selected\", \"false\");\n this.highlighted = o;\n o.setAttribute(\"data-highlighted\", \"\");\n // In a list-autocomplete combobox the highlighted option is the selected one (APG).\n o.setAttribute(\"aria-selected\", \"true\");\n this.input.setAttribute(\"aria-activedescendant\", o.id);\n o.scrollIntoView?.({ block: \"nearest\" });\n }\n\n private clearHighlight() {\n this.highlighted?.removeAttribute(\"data-highlighted\");\n this.highlighted?.setAttribute(\"aria-selected\", \"false\");\n this.highlighted = null;\n this.input?.removeAttribute(\"aria-activedescendant\");\n }\n\n /** Returns false when a `db-combobox:select` listener cancelled the choice. */\n private choose(o: HTMLElement, followLink = true): boolean {\n const value = o.dataset.value ?? \"\";\n const text = o.textContent?.trim() ?? value;\n const ev = new CustomEvent(\"db-combobox:select\", { detail: { value, text, option: o }, bubbles: true, cancelable: true });\n if (!this.dispatchEvent(ev)) return false;\n const link = o.matches(\"a[href]\") ? (o as HTMLAnchorElement) : o.querySelector<HTMLAnchorElement>(\"a[href]\");\n if (link) {\n if (followLink) link.click();\n return true;\n }\n if (this.input) this.input.value = o.getAttribute(\"data-label\") ?? text;\n if (!this.inline) this.hide();\n this.dispatchEvent(new CustomEvent(\"db-combobox:change\", { detail: { value, text }, bubbles: true }));\n return true;\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-combobox\")) {\n customElements.define(\"db-combobox\", DbCombobox);\n}\n"],"mappings":";;;;;;;;;;;AA+BO,IAAM,aAAN,cAAyB,eAAe;AAAA,EACrC,QAAiC;AAAA,EACjC,UAA8B;AAAA,EAC9B,QAA4B;AAAA,EAC5B,QAAQ;AAAA,EACR,cAAkC;AAAA,EAClC,WAAoC;AAAA,EACpC,OAAO,IAAI,aAAa;AAAA,EACxB,YAAY;AAAA,EAEpB,IAAI,OAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAY,SAAkB;AAC5B,WAAO,KAAK,aAAa,QAAQ;AAAA,EACnC;AAAA,EAEA,IAAY,YAAqB;AAC/B,YAAQ,KAAK,aAAa,QAAQ,KAAK,gBAAgB;AAAA,EACzD;AAAA;AAAA,EAGA,UAAyB;AACvB,QAAI,CAAC,KAAK,QAAS,QAAO,CAAC;AAC3B,WAAO,MAAM,KAAK,KAAK,QAAQ,iBAA8B,sBAAsB,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,aAAa,MAAM,IAAI;AAAA,EACvI;AAAA,EAEQ,UAAyB;AAC/B,WAAO,KAAK,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;AAAA,EACjE;AAAA,EAEU,QAAQ;AAChB,SAAK,QAAQ,OAAyB,MAAM,qBAAqB;AACjE,SAAK,UAAU,OAAO,MAAM,uBAAuB;AACnD,SAAK,QAAQ,OAAO,MAAM,qBAAqB;AAC/C,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,QAAS;AAClC,UAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,YAAQ,OAAO,GAAG,KAAK,IAAI;AAC3B,YAAQ,aAAa,QAAQ,SAAS;AACtC,UAAM,aAAa,QAAQ,UAAU;AACrC,UAAM,aAAa,qBAAqB,MAAM;AAC9C,UAAM,aAAa,iBAAiB,QAAQ,EAAE;AAC9C,UAAM,aAAa,gBAAgB,KAAK;AACxC,QAAI,CAAC,KAAK,OAAQ,SAAQ,SAAS;AACnC,SAAK,eAAe;AACpB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,iBAAiB;AACvB,SAAK,QAAQ,EAAE,QAAQ,CAAC,MAAM;AAG5B,QAAE,OAAO,GAAG,KAAK,IAAI,WAAW,EAAE,KAAK,SAAS;AAChD,QAAE,aAAa,QAAQ,QAAQ;AAC/B,UAAI,CAAC,EAAE,aAAa,eAAe,EAAG,GAAE,aAAa,iBAAiB,OAAO;AAE7E,UAAI,EAAE,QAAQ,iBAAiB,EAAG,GAAE,aAAa,YAAY,IAAI;AACjE,QAAE,iBAAiB,WAAW,EAAE,QAAQ,CAAC,OAAO,GAAG,aAAa,YAAY,IAAI,CAAC;AACjF,UAAI,EAAE,iBAAiB,EAAE,kBAAkB,KAAK,WAAW,EAAE,cAAc,cAAc,QAAQ,CAAC,EAAE,cAAc,aAAa,MAAM,GAAG;AACtI,UAAE,cAAc,aAAa,QAAQ,MAAM;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,UAAU,MAAM;AACtB,QAAI,KAAK,UAAW,MAAK,OAAO,KAAK,MAAO,KAAK;AAAA,QAC5C,MAAK,KAAK;AAAA,EACjB;AAAA,EAEQ,UAAU,MAAM;AACtB,QAAI,KAAK,aAAa,eAAe,KAAM,KAAK,aAAa,KAAK,MAAO,OAAQ;AAC/E,UAAI,KAAK,UAAW,MAAK,OAAO,KAAK,MAAO,KAAK;AAAA,UAC5C,MAAK,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,QAAQ,CAAC,MAAqB;AACpC,UAAM,MAAM,KAAK,QAAQ;AACzB,UAAM,MAAM,KAAK,cAAc,IAAI,QAAQ,KAAK,WAAW,IAAI;AAC/D,YAAQ,EAAE,KAAK;AAAA,MACb,KAAK;AACH,UAAE,eAAe;AACjB,YAAI,CAAC,KAAK,MAAO,MAAK,KAAK;AAC3B,YAAI,IAAI,OAAQ,MAAK,UAAU,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;AACnF;AAAA,MACF,KAAK;AACH,UAAE,eAAe;AACjB,YAAI,CAAC,KAAK,MAAO,MAAK,KAAK;AAC3B,YAAI,IAAI,OAAQ,MAAK,UAAU,IAAI,MAAM,IAAI,IAAI,SAAS,IAAI,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC;AACnF;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AAEH,YAAI,KAAK,SAAS,KAAK,eAAe,IAAI,QAAQ;AAChD,YAAE,eAAe;AACjB,eAAK,UAAU,EAAE,QAAQ,SAAS,IAAI,CAAC,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC;AAAA,QAChE;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAK,SAAS,KAAK,aAAa;AAClC,YAAE,eAAe;AACjB,eAAK,OAAO,KAAK,WAAW;AAAA,QAC9B;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAK,SAAS,CAAC,KAAK,QAAQ;AAC9B,YAAE,eAAe;AACjB,YAAE,gBAAgB;AAClB,eAAK,KAAK;AAAA,QACZ,WAAW,KAAK,MAAO,OAAO;AAE5B,YAAE,eAAe;AACjB,YAAE,gBAAgB;AAClB,eAAK,MAAO,QAAQ;AACpB,eAAK,MAAO,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAE/D,eAAK,KAAK;AAAA,QACZ;AACA;AAAA,MACF,KAAK;AACH,YAAI,CAAC,KAAK,OAAQ,MAAK,KAAK;AAC5B;AAAA,IACJ;AAAA,EACF;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,UAAM,IAAK,EAAE,OAAuB,QAAqB,sBAAsB;AAC/E,QAAI,CAAC,KAAK,EAAE,QAAQ,aAAa,MAAM,QAAQ,WAAW,CAAC,EAAG;AAG9D,UAAM,OAAQ,EAAE,OAAuB,QAAQ,SAAS;AACxD,QAAI,CAAC,KAAK,OAAO,GAAG,CAAC,IAAI,KAAK,KAAM,GAAE,eAAe;AAAA,EACvD;AAAA,EAEQ,gBAAgB,CAAC,MAAoB;AAC3C,UAAM,IAAK,EAAE,OAAuB,QAAqB,sBAAsB;AAC/E,QAAI,KAAK,EAAE,QAAQ,aAAa,MAAM,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,OAAQ,MAAK,UAAU,CAAC;AAAA,EAC7F;AAAA,EAEQ,aAAa,CAAC,MAAa,EAAE,eAAe;AAAA;AAAA,EAE5C,YAAY,CAAC,MAAoB;AACvC,QAAI,KAAK,SAAS,CAAC,KAAK,UAAU,CAAC,KAAK,SAAS,EAAE,MAAc,EAAG,MAAK,KAAK;AAAA,EAChF;AAAA,EAEU,UAAU;AAClB,UAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,QAAI,CAAC,SAAS,CAAC,QAAS;AACxB,UAAM,iBAAiB,SAAS,KAAK,OAAO;AAC5C,UAAM,iBAAiB,SAAS,KAAK,OAAO;AAC5C,UAAM,iBAAiB,WAAW,KAAK,KAAK;AAC5C,YAAQ,iBAAiB,SAAS,KAAK,WAAW;AAClD,YAAQ,iBAAiB,eAAe,KAAK,aAA8B;AAC3E,YAAQ,iBAAiB,aAAa,KAAK,UAAU;AACrD,aAAS,iBAAiB,eAAe,KAAK,SAA0B;AACxE,QAAI,OAAO,qBAAqB,aAAa;AAC3C,WAAK,WAAW,IAAI,iBAAiB,MAAM,KAAK,iBAAiB,CAAC;AAClE,WAAK,SAAS,QAAQ,SAAS,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AAAA,IACnE;AAAA,EACF;AAAA,EAEU,aAAa;AACrB,UAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,WAAO,oBAAoB,SAAS,KAAK,OAAO;AAChD,WAAO,oBAAoB,SAAS,KAAK,OAAO;AAChD,WAAO,oBAAoB,WAAW,KAAK,KAAK;AAChD,aAAS,oBAAoB,SAAS,KAAK,WAAW;AACtD,aAAS,oBAAoB,eAAe,KAAK,aAA8B;AAC/E,aAAS,oBAAoB,aAAa,KAAK,UAAU;AACzD,aAAS,oBAAoB,eAAe,KAAK,SAA0B;AAC3E,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA,EAGA,UAAU;AACR,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEQ,mBAAmB;AACzB,SAAK,eAAe;AACpB,QAAI,KAAK,eAAe,CAAC,KAAK,QAAQ,EAAE,SAAS,KAAK,WAAW,EAAG,MAAK,eAAe;AACxF,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,CAAC,KAAK,WAAW;AACnB,UAAI,IAAI,UAAU,KAAK,OAAQ,MAAK,KAAK;AAAA,UACpC,MAAK,KAAK;AACf,UAAI,IAAI,UAAU,CAAC,KAAK,eAAe,CAAC,KAAK,aAAa,kBAAkB,EAAG,MAAK,UAAU,IAAI,CAAC,CAAC;AAAA,IACtG;AACA,SAAK,aAAa;AAClB,SAAK,cAAc,IAAI,YAAY,sBAAsB,EAAE,QAAQ,EAAE,OAAO,IAAI,OAAO,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,EAC5G;AAAA,EAEQ,OAAO,OAAe;AAC5B,UAAM,IAAI,MAAM,YAAY,EAAE,KAAK;AACnC,QAAI,QAAQ;AACZ,eAAW,KAAK,KAAK,QAAQ,GAAG;AAC9B,YAAM,MAAM,CAAC,MAAM,EAAE,eAAe,IAAI,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,SAAS,IAAI,YAAY,EAAE,SAAS,CAAC;AACrH,QAAE,SAAS,CAAC;AACZ,UAAI,IAAK;AAAA,IACX;AACA,QAAI,KAAK,MAAO,MAAK,MAAM,SAAS,QAAQ,KAAK,CAAC;AAClD,SAAK,eAAe;AACpB,QAAI,SAAU,KAAK,KAAK,MAAQ,MAAK,KAAK;AAAA,QACrC,MAAK,KAAK;AACf,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,IAAI,UAAU,CAAC,KAAK,aAAa,kBAAkB,EAAG,MAAK,UAAU,IAAI,CAAC,CAAC;AAC/E,SAAK,cAAc,IAAI,YAAY,sBAAsB,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,EAChG;AAAA,EAEQ,eAAe;AACrB,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,WAAW,KAAK,SAAS,KAAK,QAAQ,EAAE,SAAS,IAAI,KAAK;AAChE,SAAK,MAAM,aAAa,iBAAiB,OAAO,QAAQ,CAAC;AACzD,SAAK,aAAa,cAAc,WAAW,SAAS,QAAQ;AAAA,EAC9D;AAAA,EAEA,OAAO;AACL,QAAI,CAAC,KAAK,WAAW,KAAK,MAAO,QAAO,KAAK,aAAa;AAC1D,SAAK,QAAQ;AACb,SAAK,QAAQ,SAAS;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAO;AACL,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,eAAe;AACpB,QAAI,CAAC,KAAK,MAAO,QAAO,KAAK,aAAa;AAC1C,SAAK,QAAQ;AACb,QAAI,CAAC,KAAK,OAAQ,MAAK,QAAQ,SAAS;AACxC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,UAAU,GAAgB;AAChC,QAAI,CAAC,KAAK,MAAO;AACjB,SAAK,aAAa,gBAAgB,kBAAkB;AACpD,SAAK,aAAa,aAAa,iBAAiB,OAAO;AACvD,SAAK,cAAc;AACnB,MAAE,aAAa,oBAAoB,EAAE;AAErC,MAAE,aAAa,iBAAiB,MAAM;AACtC,SAAK,MAAM,aAAa,yBAAyB,EAAE,EAAE;AACrD,MAAE,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EACzC;AAAA,EAEQ,iBAAiB;AACvB,SAAK,aAAa,gBAAgB,kBAAkB;AACpD,SAAK,aAAa,aAAa,iBAAiB,OAAO;AACvD,SAAK,cAAc;AACnB,SAAK,OAAO,gBAAgB,uBAAuB;AAAA,EACrD;AAAA;AAAA,EAGQ,OAAO,GAAgB,aAAa,MAAe;AACzD,UAAM,QAAQ,EAAE,QAAQ,SAAS;AACjC,UAAM,OAAO,EAAE,aAAa,KAAK,KAAK;AACtC,UAAM,KAAK,IAAI,YAAY,sBAAsB,EAAE,QAAQ,EAAE,OAAO,MAAM,QAAQ,EAAE,GAAG,SAAS,MAAM,YAAY,KAAK,CAAC;AACxH,QAAI,CAAC,KAAK,cAAc,EAAE,EAAG,QAAO;AACpC,UAAM,OAAO,EAAE,QAAQ,SAAS,IAAK,IAA0B,EAAE,cAAiC,SAAS;AAC3G,QAAI,MAAM;AACR,UAAI,WAAY,MAAK,MAAM;AAC3B,aAAO;AAAA,IACT;AACA,QAAI,KAAK,MAAO,MAAK,MAAM,QAAQ,EAAE,aAAa,YAAY,KAAK;AACnE,QAAI,CAAC,KAAK,OAAQ,MAAK,KAAK;AAC5B,SAAK,cAAc,IAAI,YAAY,sBAAsB,EAAE,QAAQ,EAAE,OAAO,KAAK,GAAG,SAAS,KAAK,CAAC,CAAC;AACpG,WAAO;AAAA,EACT;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,aAAa,GAAG;AACtC,iBAAe,OAAO,eAAe,UAAU;AACjD;","names":[]}