@dogsbay/elements 0.2.0-beta.121 → 0.2.0-beta.123

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 (67) hide show
  1. package/dist/{chunk-S62ZVVO3.js → chunk-2ZMWJ74J.js} +2 -3
  2. package/dist/{chunk-CLUADA3G.js → chunk-5KHESKQR.js} +1 -2
  3. package/dist/{chunk-LAUNXWKJ.js → chunk-5WY3DHVB.js} +1 -2
  4. package/dist/{chunk-EGU66XCL.js → chunk-6QAYHFIQ.js} +2 -3
  5. package/dist/{chunk-4YPDR3W6.js → chunk-6ZKFQBCF.js} +1 -2
  6. package/dist/{chunk-VW7HZY2B.js → chunk-AVQ7KRRE.js} +1 -2
  7. package/dist/{chunk-V4P3UR4I.js → chunk-CD3G6HKT.js} +1 -2
  8. package/dist/{chunk-FIJZRZEN.js → chunk-EPHDSTM7.js} +4 -9
  9. package/dist/{chunk-MAD6XJY4.js → chunk-GC5HL7FU.js} +2 -3
  10. package/dist/{chunk-3FMXAKOU.js → chunk-HMNRE3FZ.js} +2 -3
  11. package/dist/{chunk-MEOIAZDF.js → chunk-HMV62CBN.js} +2 -3
  12. package/dist/{chunk-ETBBSA3I.js → chunk-JRAYNKQ6.js} +1 -2
  13. package/dist/chunk-KFY2MTR3.js +190 -0
  14. package/dist/{chunk-3AEQ6KHV.js → chunk-KXCPLIQD.js} +9 -2
  15. package/dist/{chunk-K6DPDBAG.js → chunk-MZDGSTI4.js} +1 -1
  16. package/dist/{chunk-SLJG53M7.js → chunk-RZ3KZOM3.js} +2 -3
  17. package/dist/{chunk-QSRUT67Y.js → chunk-US4H4ZPZ.js} +1 -2
  18. package/dist/db-accordion.js +2 -3
  19. package/dist/db-card.js +2 -3
  20. package/dist/db-code-block.js +2 -3
  21. package/dist/db-collapsible.js +2 -3
  22. package/dist/db-combobox.js +3 -4
  23. package/dist/db-details-sheet.d.ts +53 -0
  24. package/dist/db-details-sheet.js +8 -0
  25. package/dist/db-dialog.js +3 -4
  26. package/dist/db-link-button.js +2 -3
  27. package/dist/db-nav-tree.js +3 -4
  28. package/dist/db-popover.js +3 -4
  29. package/dist/db-select.js +3 -4
  30. package/dist/db-steps.js +2 -3
  31. package/dist/db-tabs.js +2 -3
  32. package/dist/db-toc.js +3 -3
  33. package/dist/db-tooltip.js +3 -4
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +22 -19
  36. package/package.json +6 -2
  37. package/dist/chunk-3AEQ6KHV.js.map +0 -1
  38. package/dist/chunk-3FMXAKOU.js.map +0 -1
  39. package/dist/chunk-4YPDR3W6.js.map +0 -1
  40. package/dist/chunk-CLUADA3G.js.map +0 -1
  41. package/dist/chunk-EGU66XCL.js.map +0 -1
  42. package/dist/chunk-ETBBSA3I.js.map +0 -1
  43. package/dist/chunk-FIJZRZEN.js.map +0 -1
  44. package/dist/chunk-K6DPDBAG.js.map +0 -1
  45. package/dist/chunk-LAUNXWKJ.js.map +0 -1
  46. package/dist/chunk-MAD6XJY4.js.map +0 -1
  47. package/dist/chunk-MEOIAZDF.js.map +0 -1
  48. package/dist/chunk-QSRUT67Y.js.map +0 -1
  49. package/dist/chunk-S62ZVVO3.js.map +0 -1
  50. package/dist/chunk-SLJG53M7.js.map +0 -1
  51. package/dist/chunk-V4P3UR4I.js.map +0 -1
  52. package/dist/chunk-VW7HZY2B.js.map +0 -1
  53. package/dist/db-accordion.js.map +0 -1
  54. package/dist/db-card.js.map +0 -1
  55. package/dist/db-code-block.js.map +0 -1
  56. package/dist/db-collapsible.js.map +0 -1
  57. package/dist/db-combobox.js.map +0 -1
  58. package/dist/db-dialog.js.map +0 -1
  59. package/dist/db-link-button.js.map +0 -1
  60. package/dist/db-nav-tree.js.map +0 -1
  61. package/dist/db-popover.js.map +0 -1
  62. package/dist/db-select.js.map +0 -1
  63. package/dist/db-steps.js.map +0 -1
  64. package/dist/db-tabs.js.map +0 -1
  65. package/dist/db-toc.js.map +0 -1
  66. package/dist/db-tooltip.js.map +0 -1
  67. package/dist/index.js.map +0 -1
package/dist/db-select.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  DbSelect
3
- } from "./chunk-3FMXAKOU.js";
4
- import "./chunk-3AEQ6KHV.js";
5
- import "./chunk-K6DPDBAG.js";
3
+ } from "./chunk-HMNRE3FZ.js";
4
+ import "./chunk-KXCPLIQD.js";
5
+ import "./chunk-MZDGSTI4.js";
6
6
  export {
7
7
  DbSelect
8
8
  };
9
- //# sourceMappingURL=db-select.js.map
package/dist/db-steps.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  DbSteps
3
- } from "./chunk-VW7HZY2B.js";
4
- import "./chunk-K6DPDBAG.js";
3
+ } from "./chunk-AVQ7KRRE.js";
4
+ import "./chunk-MZDGSTI4.js";
5
5
  export {
6
6
  DbSteps
7
7
  };
8
- //# sourceMappingURL=db-steps.js.map
package/dist/db-tabs.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  DbTabs
3
- } from "./chunk-4YPDR3W6.js";
4
- import "./chunk-K6DPDBAG.js";
3
+ } from "./chunk-6ZKFQBCF.js";
4
+ import "./chunk-MZDGSTI4.js";
5
5
  export {
6
6
  DbTabs
7
7
  };
8
- //# sourceMappingURL=db-tabs.js.map
package/dist/db-toc.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  DbToc
3
- } from "./chunk-FIJZRZEN.js";
4
- import "./chunk-K6DPDBAG.js";
3
+ } from "./chunk-EPHDSTM7.js";
4
+ import "./chunk-KXCPLIQD.js";
5
+ import "./chunk-MZDGSTI4.js";
5
6
  export {
6
7
  DbToc
7
8
  };
8
- //# sourceMappingURL=db-toc.js.map
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  DbTooltip
3
- } from "./chunk-MEOIAZDF.js";
4
- import "./chunk-3AEQ6KHV.js";
5
- import "./chunk-K6DPDBAG.js";
3
+ } from "./chunk-HMV62CBN.js";
4
+ import "./chunk-KXCPLIQD.js";
5
+ import "./chunk-MZDGSTI4.js";
6
6
  export {
7
7
  DbTooltip
8
8
  };
9
- //# sourceMappingURL=db-tooltip.js.map
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { D as DogsbayElement } from './base-D1Rz70fn.js';
2
2
  export { DbTabs } from './db-tabs.js';
3
3
  export { DbCollapsible } from './db-collapsible.js';
4
4
  export { DbAccordion } from './db-accordion.js';
5
+ export { DbDetailsSheet } from './db-details-sheet.js';
5
6
  export { DbSteps } from './db-steps.js';
6
7
  export { DbCodeBlock } from './db-code-block.js';
7
8
  export { DbCard } from './db-card.js';
package/dist/index.js CHANGED
@@ -1,55 +1,59 @@
1
+ import {
2
+ DbNavTree
3
+ } from "./chunk-2ZMWJ74J.js";
1
4
  import {
2
5
  DbPopover
3
- } from "./chunk-EGU66XCL.js";
6
+ } from "./chunk-6QAYHFIQ.js";
4
7
  import {
5
8
  DbSelect
6
- } from "./chunk-3FMXAKOU.js";
9
+ } from "./chunk-HMNRE3FZ.js";
7
10
  import {
8
11
  DbSteps
9
- } from "./chunk-VW7HZY2B.js";
12
+ } from "./chunk-AVQ7KRRE.js";
10
13
  import {
11
14
  DbTabs
12
- } from "./chunk-4YPDR3W6.js";
15
+ } from "./chunk-6ZKFQBCF.js";
13
16
  import {
14
17
  DbToc
15
- } from "./chunk-FIJZRZEN.js";
18
+ } from "./chunk-EPHDSTM7.js";
16
19
  import {
17
20
  DbTooltip
18
- } from "./chunk-MEOIAZDF.js";
21
+ } from "./chunk-HMV62CBN.js";
19
22
  import {
20
23
  DbAccordion
21
- } from "./chunk-CLUADA3G.js";
24
+ } from "./chunk-5KHESKQR.js";
22
25
  import {
23
26
  DbCard
24
- } from "./chunk-ETBBSA3I.js";
27
+ } from "./chunk-JRAYNKQ6.js";
25
28
  import {
26
29
  DbCodeBlock
27
- } from "./chunk-V4P3UR4I.js";
30
+ } from "./chunk-CD3G6HKT.js";
28
31
  import {
29
32
  DbCollapsible
30
- } from "./chunk-LAUNXWKJ.js";
33
+ } from "./chunk-5WY3DHVB.js";
31
34
  import {
32
35
  DbCombobox
33
- } from "./chunk-MAD6XJY4.js";
36
+ } from "./chunk-GC5HL7FU.js";
37
+ import {
38
+ DbDetailsSheet
39
+ } from "./chunk-KFY2MTR3.js";
34
40
  import {
35
41
  DbDialog
36
- } from "./chunk-SLJG53M7.js";
42
+ } from "./chunk-RZ3KZOM3.js";
43
+ import "./chunk-KXCPLIQD.js";
37
44
  import {
38
45
  DbLinkButton
39
- } from "./chunk-QSRUT67Y.js";
40
- import {
41
- DbNavTree
42
- } from "./chunk-S62ZVVO3.js";
43
- import "./chunk-3AEQ6KHV.js";
46
+ } from "./chunk-US4H4ZPZ.js";
44
47
  import {
45
48
  DogsbayElement
46
- } from "./chunk-K6DPDBAG.js";
49
+ } from "./chunk-MZDGSTI4.js";
47
50
  export {
48
51
  DbAccordion,
49
52
  DbCard,
50
53
  DbCodeBlock,
51
54
  DbCollapsible,
52
55
  DbCombobox,
56
+ DbDetailsSheet,
53
57
  DbDialog,
54
58
  DbLinkButton,
55
59
  DbNavTree,
@@ -61,4 +65,3 @@ export {
61
65
  DbTooltip,
62
66
  DogsbayElement
63
67
  };
64
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/elements",
3
- "version": "0.2.0-beta.121",
3
+ "version": "0.2.0-beta.123",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -21,6 +21,10 @@
21
21
  "types": "./dist/db-accordion.d.ts",
22
22
  "import": "./dist/db-accordion.js"
23
23
  },
24
+ "./db-details-sheet": {
25
+ "types": "./dist/db-details-sheet.d.ts",
26
+ "import": "./dist/db-details-sheet.js"
27
+ },
24
28
  "./db-steps": {
25
29
  "types": "./dist/db-steps.d.ts",
26
30
  "import": "./dist/db-steps.js"
@@ -70,7 +74,7 @@
70
74
  "dist"
71
75
  ],
72
76
  "dependencies": {
73
- "@dogsbay/primitives": "0.2.0-beta.121"
77
+ "@dogsbay/primitives": "0.2.0-beta.123"
74
78
  },
75
79
  "devDependencies": {
76
80
  "happy-dom": "^20.10.6",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils.ts"],"sourcesContent":["/**\n * Shared helpers for the elements. Nothing here touches the DOM at\n * import time, so modules stay safe to import in non-browser tooling.\n */\n\nlet counter = 0;\n\n/** A document-unique id with a readable prefix. */\nexport function uid(prefix: string): string {\n return `${prefix}-${++counter}`;\n}\n\n/**\n * Descendants of `host` matching `selector` that belong to THIS element,\n * not to a nested element of the same tag. Wrappers are allowed.\n */\nexport function own<T extends HTMLElement = HTMLElement>(host: HTMLElement, selector: string): T[] {\n const tag = host.localName;\n return (Array.from(host.querySelectorAll(selector)) as T[]).filter((el) => el.closest(tag) === host);\n}\n\n/** First own descendant matching `selector`, or null. */\nexport function ownOne<T extends HTMLElement = HTMLElement>(host: HTMLElement, selector: string): T | null {\n return own<T>(host, selector)[0] ?? null;\n}\n\n/**\n * The Astro components' `asChild` pattern: a wrapper `<span data-part=\"x\"\n * data-as-child>` stands in for its first child, which receives the part.\n */\nexport function resolveAsChild(el: HTMLElement | null): HTMLElement | null {\n if (!el || !el.hasAttribute(\"data-as-child\")) return el;\n const child = el.firstElementChild as HTMLElement | null;\n if (!child) return el;\n const part = el.getAttribute(\"data-part\");\n if (part) child.setAttribute(\"data-part\", part);\n return child;\n}\n\nexport function isDisabled(el: Element): boolean {\n return el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\" || el.getAttribute(\"data-disabled\") === \"true\";\n}\n\n/**\n * True when the browser supports the Popover API, which puts floating\n * content in the top layer: never clipped by `overflow`, always above\n * other stacking contexts, and no portal needed.\n */\nexport function supportsPopover(): boolean {\n return typeof HTMLElement !== \"undefined\" && typeof (HTMLElement.prototype as { showPopover?: unknown }).showPopover === \"function\";\n}\n\n/** Show floating content, in the top layer when the browser allows it. */\nexport function showFloating(el: HTMLElement): \"top-layer\" | \"inline\" {\n el.hidden = false;\n if (!supportsPopover()) return \"inline\";\n if (!el.hasAttribute(\"popover\")) el.setAttribute(\"popover\", \"manual\");\n // The UA stylesheet centres popovers in the viewport; floating-ui positions them instead.\n el.style.inset = \"auto\";\n el.style.margin = \"0\";\n try {\n if (!el.matches(\":popover-open\")) el.showPopover();\n } catch {\n return \"inline\";\n }\n return \"top-layer\";\n}\n\nexport function hideFloating(el: HTMLElement): void {\n if (supportsPopover()) {\n try {\n if (el.matches(\":popover-open\")) el.hidePopover();\n } catch {\n /* not a popover */\n }\n }\n el.hidden = true;\n}\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\n/** Focusable descendants in document order, skipping hidden ones. */\nexport function focusables(root: HTMLElement): HTMLElement[] {\n return (Array.from(root.querySelectorAll(FOCUSABLE)) as HTMLElement[]).filter((el) => !el.closest(\"[hidden]\") && !isDisabled(el));\n}\n\n/** Wait for running CSS animations on `el` (exit animations), then run `done`. */\nexport function afterAnimations(el: HTMLElement, done: () => void): void {\n const anims = typeof el.getAnimations === \"function\" ? el.getAnimations() : [];\n if (!anims.length) return done();\n Promise.all(anims.map((a) => a.finished)).then(done, done);\n}\n"],"mappings":";AAKA,IAAI,UAAU;AAGP,SAAS,IAAI,QAAwB;AAC1C,SAAO,GAAG,MAAM,IAAI,EAAE,OAAO;AAC/B;AAMO,SAAS,IAAyC,MAAmB,UAAuB;AACjG,QAAM,MAAM,KAAK;AACjB,SAAQ,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC,EAAU,OAAO,CAAC,OAAO,GAAG,QAAQ,GAAG,MAAM,IAAI;AACrG;AAGO,SAAS,OAA4C,MAAmB,UAA4B;AACzG,SAAO,IAAO,MAAM,QAAQ,EAAE,CAAC,KAAK;AACtC;AAMO,SAAS,eAAe,IAA4C;AACzE,MAAI,CAAC,MAAM,CAAC,GAAG,aAAa,eAAe,EAAG,QAAO;AACrD,QAAM,QAAQ,GAAG;AACjB,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,GAAG,aAAa,WAAW;AACxC,MAAI,KAAM,OAAM,aAAa,aAAa,IAAI;AAC9C,SAAO;AACT;AAEO,SAAS,WAAW,IAAsB;AAC/C,SAAO,GAAG,aAAa,UAAU,KAAK,GAAG,aAAa,eAAe,MAAM,UAAU,GAAG,aAAa,eAAe,MAAM;AAC5H;AAOO,SAAS,kBAA2B;AACzC,SAAO,OAAO,gBAAgB,eAAe,OAAQ,YAAY,UAAwC,gBAAgB;AAC3H;AAGO,SAAS,aAAa,IAAyC;AACpE,KAAG,SAAS;AACZ,MAAI,CAAC,gBAAgB,EAAG,QAAO;AAC/B,MAAI,CAAC,GAAG,aAAa,SAAS,EAAG,IAAG,aAAa,WAAW,QAAQ;AAEpE,KAAG,MAAM,QAAQ;AACjB,KAAG,MAAM,SAAS;AAClB,MAAI;AACF,QAAI,CAAC,GAAG,QAAQ,eAAe,EAAG,IAAG,YAAY;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,aAAa,IAAuB;AAClD,MAAI,gBAAgB,GAAG;AACrB,QAAI;AACF,UAAI,GAAG,QAAQ,eAAe,EAAG,IAAG,YAAY;AAAA,IAClD,QAAQ;AAAA,IAER;AAAA,EACF;AACA,KAAG,SAAS;AACd;AAEA,IAAM,YACJ;AAGK,SAAS,WAAW,MAAkC;AAC3D,SAAQ,MAAM,KAAK,KAAK,iBAAiB,SAAS,CAAC,EAAoB,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,UAAU,KAAK,CAAC,WAAW,EAAE,CAAC;AAClI;AAGO,SAAS,gBAAgB,IAAiB,MAAwB;AACvE,QAAM,QAAQ,OAAO,GAAG,kBAAkB,aAAa,GAAG,cAAc,IAAI,CAAC;AAC7E,MAAI,CAAC,MAAM,OAAQ,QAAO,KAAK;AAC/B,UAAQ,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,MAAM,IAAI;AAC3D;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-select.ts"],"sourcesContent":["import { DismissController } from \"@dogsbay/primitives/dismiss\";\nimport { setupFloating } from \"@dogsbay/primitives/floating\";\nimport { Typeahead } from \"@dogsbay/primitives/typeahead\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { hideFloating, isDisabled, own, ownOne, showFloating, uid } from \"./utils.js\";\n\n/**\n * `<db-select>` — Select-only combobox (APG \"Select-Only Combobox\").\n *\n * Two ways to use it:\n *\n * 1. The Astro Select parts (same contract as `ui/src/select`):\n * [data-part=\"trigger\"] button, role=combobox (focus stays here)\n * [data-part=\"value\"] shows the selected label (data-placeholder)\n * [data-part=\"content\"] role=listbox\n * [data-part=\"item\"] role=option, data-value, aria-disabled\n * [data-part=\"group\"] / [data-part=\"label\"]\n *\n * 2. Progressive enhancement of a native `<select>` inside the element.\n * The element builds the parts from its options, keeps the native\n * select in sync (hidden, still submitted with forms) and dispatches a\n * native `change` on it, so existing listeners keep working. With no\n * JavaScript the native select is what the user gets.\n *\n * The listbox opens in the top layer where supported. Focus never leaves\n * the trigger: the highlighted option is exposed with aria-activedescendant.\n *\n * Attributes: name, default-value, required, disabled\n * Events: db-select:change (detail: { value })\n */\nexport class DbSelect extends DogsbayElement {\n private trigger: HTMLElement | null = null;\n private content: HTMLElement | null = null;\n private valueEl: HTMLElement | null = null;\n private items: HTMLElement[] = [];\n private nativeSelect: HTMLSelectElement | null = null;\n private hiddenInput: HTMLInputElement | null = null;\n private labelEl: HTMLLabelElement | null = null;\n\n private onLabelClick = (e: Event) => {\n if ((e.target as HTMLElement).closest(\"[data-part='trigger']\")) return;\n e.preventDefault();\n this.trigger?.focus();\n };\n private dismiss: DismissController | null = null;\n private typeahead: Typeahead | null = null;\n private cleanupFloating: (() => void) | null = null;\n private _open = false;\n private _value = \"\";\n private highlighted = -1;\n\n get open(): boolean {\n return this._open;\n }\n\n get value(): string {\n return this._value;\n }\n\n set value(v: string) {\n const item = this.items.find((i) => i.dataset.value === v);\n if (item) this.select(item, false);\n }\n\n /** Disabled when the host or the enhanced native select is. */\n private get isDisabled(): boolean {\n return this.hasAttribute(\"disabled\") || !!this.nativeSelect?.disabled;\n }\n\n private syncing = false;\n\n /** Re-read the native select's value (after script changes it or a form reset). */\n refresh() {\n const sel = this.nativeSelect;\n if (!sel) return;\n const item = this.items.find((i) => i.dataset.value === sel.value);\n if (item) this.select(item, false);\n this.trigger?.setAttribute(\"aria-disabled\", String(this.isDisabled));\n }\n\n private onNativeChange = () => {\n if (!this.syncing) this.refresh();\n };\n\n private onFormReset = () => setTimeout(() => this.refresh());\n\n private enabled(): HTMLElement[] {\n return this.items.filter((i) => !isDisabled(i));\n }\n\n protected setup() {\n this.nativeSelect = ownOne<HTMLSelectElement>(this, \"select\");\n if (this.nativeSelect && !ownOne(this, \"[data-part='trigger']\")) this.buildFromNative(this.nativeSelect);\n\n this.trigger = ownOne(this, \"[data-part='trigger']\");\n this.content = ownOne(this, \"[data-part='content']\");\n this.valueEl = ownOne(this, \"[data-part='value']\");\n if (!this.trigger || !this.content) return;\n const { trigger, content } = this;\n this.items = own(this, \"[data-part='item']\");\n\n const base = uid(\"db-select\");\n content.id ||= `${base}-listbox`;\n content.setAttribute(\"role\", \"listbox\");\n content.hidden = true;\n trigger.setAttribute(\"role\", \"combobox\");\n trigger.setAttribute(\"aria-haspopup\", \"listbox\");\n trigger.setAttribute(\"aria-expanded\", \"false\");\n trigger.setAttribute(\"aria-controls\", content.id);\n if (trigger instanceof HTMLButtonElement && !trigger.hasAttribute(\"type\")) trigger.type = \"button\";\n if (this.isDisabled) trigger.setAttribute(\"aria-disabled\", \"true\");\n this.items.forEach((item, i) => {\n item.id ||= `${base}-item-${i}`;\n item.setAttribute(\"role\", \"option\");\n item.setAttribute(\"aria-selected\", \"false\");\n });\n own(this, \"[data-part='group']\").forEach((group) => {\n group.setAttribute(\"role\", \"group\");\n const label = group.querySelector<HTMLElement>(\"[data-part='label']\");\n if (label) {\n label.id ||= uid(\"db-select-label\");\n group.setAttribute(\"aria-labelledby\", label.id);\n }\n });\n\n const name = this.getAttribute(\"name\");\n if (name && !this.nativeSelect) {\n this.hiddenInput = document.createElement(\"input\");\n this.hiddenInput.type = \"hidden\";\n this.hiddenInput.name = name;\n if (this.hasAttribute(\"required\")) this.hiddenInput.required = true;\n this.appendChild(this.hiddenInput);\n }\n\n this.typeahead = new Typeahead({\n getItemCount: () => this.enabled().length,\n getItemText: (i) => this.enabled()[i]?.textContent?.trim() ?? \"\",\n onMatch: (i) => {\n const item = this.enabled()[i];\n if (!item) return;\n if (this._open) this.highlight(this.items.indexOf(item));\n else this.select(item);\n },\n });\n this.dismiss = new DismissController(content, { onDismiss: (reason) => this.close(reason !== \"outside-press\") });\n\n const initial = this.nativeSelect?.value ?? this.getAttribute(\"default-value\") ?? \"\";\n const initialItem = this.items.find((i) => i.dataset.value === initial);\n if (initialItem && isDisabled(initialItem)) {\n // A disabled placeholder option (\"Choose…\") is shown, not selected.\n if (this.valueEl) {\n this.valueEl.textContent = initialItem.textContent?.trim() ?? \"\";\n this.valueEl.setAttribute(\"data-placeholder\", \"\");\n }\n } else if (initialItem) this.select(initialItem, false);\n this.setState(\"closed\");\n }\n\n /** Build the trigger and listbox from a native <select>, and hide it. */\n private buildFromNative(sel: HTMLSelectElement) {\n const trigger = document.createElement(\"button\");\n trigger.type = \"button\";\n trigger.setAttribute(\"data-part\", \"trigger\");\n trigger.className = \"db-select-trigger\";\n // Carry the native select's accessible name over to the trigger.\n const label = sel.getAttribute(\"aria-label\");\n let labelledBy = sel.getAttribute(\"aria-labelledby\");\n const forLabel = !labelledBy && sel.id ? document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(sel.id)}\"]`) : null;\n if (forLabel) labelledBy = forLabel.id ||= uid(\"db-select-for\");\n if (label) trigger.setAttribute(\"aria-label\", label);\n if (labelledBy) trigger.setAttribute(\"aria-labelledby\", labelledBy);\n // A <label> wrapping the select: name the trigger with the label's own text (not the\n // options'), and send label clicks to the trigger rather than the hidden select.\n const wrap = sel.closest(\"label\");\n if (wrap) {\n this.labelEl = wrap;\n if (!label && !labelledBy) {\n const text = Array.from(wrap.childNodes)\n .filter((n) => !(n instanceof Element && (n === sel || n.contains(sel) || n.localName === \"db-select\")))\n .map((n) => n.textContent ?? \"\")\n .join(\" \")\n .replace(/\\s+/g, \" \")\n .trim();\n if (text) trigger.setAttribute(\"aria-label\", text);\n }\n } else if (forLabel) this.labelEl = forLabel;\n const value = document.createElement(\"span\");\n value.setAttribute(\"data-part\", \"value\");\n value.className = \"db-select-value\";\n const chevron = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n chevron.setAttribute(\"viewBox\", \"0 0 24 24\");\n chevron.setAttribute(\"aria-hidden\", \"true\");\n chevron.setAttribute(\"class\", \"db-select-chevron\");\n chevron.innerHTML = '<path d=\"m6 9 6 6 6-6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>';\n trigger.append(value, chevron);\n\n const list = document.createElement(\"div\");\n list.setAttribute(\"data-part\", \"content\");\n list.className = \"db-select-content\";\n const addOption = (o: HTMLOptionElement, parent: HTMLElement) => {\n const item = document.createElement(\"div\");\n item.setAttribute(\"data-part\", \"item\");\n item.className = \"db-select-item\";\n item.dataset.value = o.value;\n for (const [k, v] of Object.entries(o.dataset)) if (v != null) item.dataset[k] = v;\n if (o.disabled) item.setAttribute(\"aria-disabled\", \"true\");\n item.textContent = o.textContent ?? o.value;\n parent.appendChild(item);\n };\n for (const child of Array.from(sel.children)) {\n if (child instanceof HTMLOptGroupElement) {\n const group = document.createElement(\"div\");\n group.setAttribute(\"data-part\", \"group\");\n const gl = document.createElement(\"div\");\n gl.setAttribute(\"data-part\", \"label\");\n gl.className = \"db-select-label\";\n gl.textContent = child.label;\n group.appendChild(gl);\n for (const o of Array.from(child.children)) if (o instanceof HTMLOptionElement) addOption(o, group);\n list.appendChild(group);\n } else if (child instanceof HTMLOptionElement) {\n addOption(child, list);\n }\n }\n sel.hidden = true;\n sel.tabIndex = -1;\n sel.setAttribute(\"aria-hidden\", \"true\");\n this.append(trigger, list);\n }\n\n private onTriggerClick = () => {\n if (this.isDisabled) return;\n if (this._open) this.close();\n else this.show();\n };\n\n // Firefox fires click on keyup of Space even after keydown was cancelled; cancel it\n // there too so Space does not toggle twice.\n private onTriggerKeyUp = (e: KeyboardEvent) => {\n if (e.key === \" \") e.preventDefault();\n };\n\n private onTriggerKey = (e: KeyboardEvent) => {\n if (this.isDisabled) return;\n const enabled = this.enabled();\n if (!this._open) {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\" || e.key === \"Enter\" || e.key === \" \" || (e.altKey && e.key === \"ArrowDown\")) {\n e.preventDefault();\n this.show();\n if (e.key === \"ArrowUp\" && enabled.length) this.highlight(this.items.indexOf(enabled[enabled.length - 1]));\n } else if (e.key === \"Home\" || e.key === \"End\") {\n e.preventDefault();\n this.show();\n const t = e.key === \"Home\" ? enabled[0] : enabled[enabled.length - 1];\n if (t) this.highlight(this.items.indexOf(t));\n } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n this.typeahead?.handle(e.key);\n }\n return;\n }\n if (!enabled.length) return;\n const cur = this.highlighted >= 0 ? enabled.indexOf(this.items[this.highlighted]) : -1;\n const go = (i: number) => {\n e.preventDefault();\n this.highlight(this.items.indexOf(enabled[i]));\n };\n switch (e.key) {\n case \"ArrowDown\":\n return go(Math.min(cur + 1, enabled.length - 1));\n case \"ArrowUp\":\n if (e.altKey) {\n e.preventDefault();\n if (this.highlighted >= 0) this.select(this.items[this.highlighted]);\n return this.close();\n }\n return go(Math.max(cur - 1, 0));\n case \"Home\":\n return go(0);\n case \"End\":\n return go(enabled.length - 1);\n case \"PageDown\":\n return go(Math.min(cur + 10, enabled.length - 1));\n case \"PageUp\":\n return go(Math.max(cur - 10, 0));\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (this.highlighted >= 0) this.select(this.items[this.highlighted]);\n return this.close();\n case \"Tab\":\n if (this.highlighted >= 0) this.select(this.items[this.highlighted]);\n return this.close(false);\n default:\n if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) this.typeahead?.handle(e.key);\n }\n };\n\n private onListClick = (e: Event) => {\n const item = (e.target as HTMLElement).closest<HTMLElement>(\"[data-part='item']\");\n if (!item || !this.items.includes(item) || isDisabled(item)) return;\n this.select(item);\n this.close();\n };\n\n private onListPointer = (e: PointerEvent) => {\n const item = (e.target as HTMLElement).closest<HTMLElement>(\"[data-part='item']\");\n if (item && this.items.includes(item) && !isDisabled(item)) this.highlight(this.items.indexOf(item));\n };\n\n protected connect() {\n this.trigger?.addEventListener(\"click\", this.onTriggerClick);\n this.trigger?.addEventListener(\"keydown\", this.onTriggerKey);\n this.trigger?.addEventListener(\"keyup\", this.onTriggerKeyUp);\n this.nativeSelect?.addEventListener(\"change\", this.onNativeChange);\n this.nativeSelect?.form?.addEventListener(\"reset\", this.onFormReset);\n this.labelEl?.addEventListener(\"click\", this.onLabelClick);\n this.content?.addEventListener(\"click\", this.onListClick);\n this.content?.addEventListener(\"pointermove\", this.onListPointer as EventListener);\n // Keep focus on the trigger when an option is pressed.\n this.content?.addEventListener(\"mousedown\", this.preventBlur);\n }\n\n protected disconnect() {\n this.trigger?.removeEventListener(\"click\", this.onTriggerClick);\n this.trigger?.removeEventListener(\"keydown\", this.onTriggerKey);\n this.trigger?.removeEventListener(\"keyup\", this.onTriggerKeyUp);\n this.nativeSelect?.removeEventListener(\"change\", this.onNativeChange);\n this.nativeSelect?.form?.removeEventListener(\"reset\", this.onFormReset);\n this.labelEl?.removeEventListener(\"click\", this.onLabelClick);\n this.content?.removeEventListener(\"click\", this.onListClick);\n this.content?.removeEventListener(\"pointermove\", this.onListPointer as EventListener);\n this.content?.removeEventListener(\"mousedown\", this.preventBlur);\n if (this._open) this.close(false);\n }\n\n private preventBlur = (e: Event) => e.preventDefault();\n\n private setState(state: \"open\" | \"closed\") {\n this.setAttribute(\"data-state\", state);\n this.content?.setAttribute(\"data-state\", state);\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: \"bottom-start\",\n offset: 4,\n matchWidth: !this.hasAttribute(\"auto-width\"),\n strategy: layer === \"top-layer\" ? \"fixed\" : \"absolute\",\n });\n const selected = this.items.find((i) => i.dataset.value === this._value && !isDisabled(i));\n const first = selected ?? this.enabled()[0];\n if (first) this.highlight(this.items.indexOf(first));\n this.dismiss?.activate();\n this.typeahead?.reset();\n trigger.focus();\n }\n\n /** Close the listbox. `refocus` is false for outside presses, which keep focus where the user clicked. */\n close(refocus = true) {\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 hideFloating(content);\n this.setState(\"closed\");\n trigger.setAttribute(\"aria-expanded\", \"false\");\n trigger.removeAttribute(\"aria-activedescendant\");\n this.items.forEach((i) => i.removeAttribute(\"data-highlighted\"));\n this.highlighted = -1;\n this.typeahead?.reset();\n if (refocus) trigger.focus();\n }\n\n private highlight(index: number) {\n const item = this.items[index];\n const content = this.content;\n if (!item || !content || !this.trigger) return;\n if (this.highlighted >= 0) this.items[this.highlighted]?.removeAttribute(\"data-highlighted\");\n this.highlighted = index;\n item.setAttribute(\"data-highlighted\", \"\");\n this.trigger.setAttribute(\"aria-activedescendant\", item.id);\n const top = item.offsetTop;\n const bottom = top + item.offsetHeight;\n if (top < content.scrollTop) content.scrollTop = top;\n else if (bottom > content.scrollTop + content.clientHeight) content.scrollTop = bottom - content.clientHeight;\n }\n\n private select(item: HTMLElement, notify = true) {\n if (isDisabled(item)) return;\n const value = item.dataset.value ?? \"\";\n const changed = value !== this._value;\n this._value = value;\n for (const i of this.items) {\n const on = i === item;\n i.setAttribute(\"aria-selected\", String(on));\n if (on) i.setAttribute(\"data-state\", \"checked\");\n else i.removeAttribute(\"data-state\");\n }\n if (this.valueEl) {\n this.valueEl.textContent = item.textContent?.trim() ?? \"\";\n this.valueEl.removeAttribute(\"data-placeholder\");\n }\n if (this.hiddenInput) this.hiddenInput.value = value;\n if (!notify || !changed) {\n if (this.nativeSelect) this.nativeSelect.value = value;\n return;\n }\n if (this.nativeSelect) {\n this.nativeSelect.value = value;\n this.syncing = true;\n try {\n this.nativeSelect.dispatchEvent(new Event(\"input\", { bubbles: true }));\n this.nativeSelect.dispatchEvent(new Event(\"change\", { bubbles: true }));\n } finally {\n this.syncing = false;\n }\n }\n this.dispatchEvent(new CustomEvent(\"db-select:change\", { detail: { value }, bubbles: true }));\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-select\")) {\n customElements.define(\"db-select\", DbSelect);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-tabs.ts"],"sourcesContent":["import { CompositeNavigation } from \"@dogsbay/primitives/composite\";\nimport type { Orientation } from \"@dogsbay/primitives/composite\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\nlet idCounter = 0;\nfunction uniqueId(prefix: string) {\n return `${prefix}-${++idCounter}`;\n}\n\n/**\n * `<db-tabs>` — Accessible tabbed interface.\n *\n * Attributes:\n * default-value — initial selected tab value\n * orientation — \"horizontal\" (default) or \"vertical\"\n * sync-key — sync selection across instances via localStorage\n *\n * Children:\n * [data-part=\"list\"] — tablist container\n * [data-part=\"trigger\"] — tab buttons (must have data-value)\n * [data-part=\"content\"] — tab panels (must have data-value)\n *\n * Events:\n * db-tabs:change — fired on selection change, detail: { value }\n */\nexport class DbTabs extends DogsbayElement {\n private list!: HTMLElement;\n private triggers: HTMLElement[] = [];\n private panels: HTMLElement[] = [];\n private nav: CompositeNavigation | null = null;\n private value = \"\";\n private syncKey: string | null = null;\n private orientation: Orientation = \"horizontal\";\n\n private handleClick = (e: Event) => {\n const trigger = (e.target as HTMLElement).closest(\n \"[data-part='trigger']\",\n ) as HTMLElement | null;\n if (!trigger || trigger.hasAttribute(\"disabled\")) return;\n const value = trigger.dataset.value;\n if (value) this.select(value);\n };\n\n /**\n * A synced group follows only a value it HAS. Groups sharing a key need\n * not share a tab set (a web-only snippet group beside an all-SDK one), and\n * selecting a value with no trigger here left the group with every panel\n * hidden and no tab selected.\n */\n private has(value: string): boolean {\n return this.triggers.some((t) => t.dataset.value === value);\n }\n\n private handleStorage = (e: StorageEvent) => {\n if (!this.syncKey || e.key !== `db-tabs-sync:${this.syncKey}`) return;\n const newValue = e.newValue;\n if (newValue && this.value !== newValue && this.has(newValue)) {\n this.select(newValue);\n }\n };\n\n private handleSyncEvent = ((e: CustomEvent) => {\n const newValue = e.detail.value;\n if (newValue && this.value !== newValue && this.has(newValue)) {\n this.select(newValue);\n }\n }) as EventListener;\n\n /**\n * Descendants belonging to THIS tab group.\n *\n * Not `:scope >`, and not a bare descendant query — both were wrong\n * here, in opposite directions:\n *\n * - Panels used `:scope > [data-part='content']`, so any wrapper\n * between the element and its panels hid them. The vertical example\n * wraps its list and panels in a flex row to sit them side by side,\n * so `panels` was EMPTY: triggers moved `aria-selected`, and the\n * content never changed. Nothing threw, and the server-rendered\n * initial state looked right, so it read as working.\n * - Triggers used a bare descendant query, which would reach into a\n * NESTED tab group and drive the wrong panels.\n *\n * `closest(\"db-tabs\") === this` allows arbitrary wrappers while\n * stopping at a nested group, which is a real case — Obsidian\n * alternates fence characters by depth precisely so tabs can nest.\n */\n private own(selector: string): HTMLElement[] {\n return (\n Array.from(this.querySelectorAll(selector)) as HTMLElement[]\n ).filter((el) => el.closest(\"db-tabs\") === this);\n }\n\n protected setup() {\n const list = this.own(\"[data-part='list']\")[0] ?? null;\n if (!list) return;\n this.list = list;\n\n this.triggers = this.own(\"[data-part='trigger']\");\n this.panels = this.own(\"[data-part='content']\");\n\n this.orientation =\n (this.getAttribute(\"orientation\") as Orientation) || \"horizontal\";\n this.syncKey = this.getAttribute(\"sync-key\") || null;\n\n // Set orientation on tablist\n list.setAttribute(\"role\", \"tablist\");\n list.setAttribute(\"aria-orientation\", this.orientation);\n\n // Generate IDs and link triggers ↔ panels\n this.triggers.forEach((trigger) => {\n const value = trigger.dataset.value!;\n trigger.setAttribute(\"role\", \"tab\");\n const triggerId = trigger.id || uniqueId(\"db-tab\");\n trigger.id = triggerId;\n\n const panel = this.panels.find((p) => p.dataset.value === value);\n if (panel) {\n panel.setAttribute(\"role\", \"tabpanel\");\n const panelId = panel.id || uniqueId(\"db-tabpanel\");\n panel.id = panelId;\n trigger.setAttribute(\"aria-controls\", panelId);\n panel.setAttribute(\"aria-labelledby\", triggerId);\n }\n });\n\n // Set up composite navigation\n this.nav = new CompositeNavigation(list, \"[data-part='trigger']\", {\n orientation: this.orientation,\n loop: true,\n homeEnd: true,\n onHighlightedIndexChange: () => {\n const focused = document.activeElement as HTMLElement;\n const value = focused?.dataset?.value;\n if (value) this.select(value);\n },\n });\n\n this.nav.activate();\n\n // Determine initial value: hash > localStorage > default-value > first trigger\n const defaultValue =\n this.getAttribute(\"default-value\") ||\n this.triggers[0]?.dataset.value ||\n \"\";\n\n let initialValue = defaultValue;\n if (this.syncKey) {\n const stored = localStorage.getItem(`db-tabs-sync:${this.syncKey}`);\n if (stored && this.triggers.some((t) => t.dataset.value === stored)) {\n initialValue = stored;\n }\n }\n const hash = window.location.hash.slice(1);\n if (hash && this.triggers.some((t) => t.dataset.value === hash)) {\n initialValue = hash;\n }\n\n this.select(initialValue);\n }\n\n protected connect() {\n this.list?.addEventListener(\"click\", this.handleClick);\n if (this.syncKey) {\n window.addEventListener(\"storage\", this.handleStorage);\n document.addEventListener(\n `db-tabs-sync:${this.syncKey}`,\n this.handleSyncEvent,\n );\n }\n }\n\n protected disconnect() {\n this.list?.removeEventListener(\"click\", this.handleClick);\n if (this.syncKey) {\n window.removeEventListener(\"storage\", this.handleStorage);\n document.removeEventListener(\n `db-tabs-sync:${this.syncKey}`,\n this.handleSyncEvent,\n );\n }\n }\n\n select(value: string) {\n if (this.value === value) return;\n this.value = value;\n\n // Update triggers\n this.triggers.forEach((trigger) => {\n const isSelected = trigger.dataset.value === value;\n trigger.setAttribute(\"aria-selected\", String(isSelected));\n trigger.setAttribute(\"tabindex\", isSelected ? \"0\" : \"-1\");\n if (isSelected) {\n trigger.setAttribute(\"data-state\", \"active\");\n } else {\n trigger.removeAttribute(\"data-state\");\n }\n });\n\n // Update panels\n this.panels.forEach((panel) => {\n const isActive = panel.dataset.value === value;\n panel.hidden = !isActive;\n if (isActive) {\n panel.setAttribute(\"data-state\", \"active\");\n if (!panel.hasAttribute(\"tabindex\")) {\n panel.setAttribute(\"tabindex\", \"0\");\n }\n // Lazy init: upgrade any nested db-tabs that haven't connected yet\n // (browser handles this automatically via connectedCallback)\n } else {\n panel.removeAttribute(\"data-state\");\n }\n });\n\n // Sync to localStorage and other instances\n if (this.syncKey) {\n localStorage.setItem(`db-tabs-sync:${this.syncKey}`, value);\n document.dispatchEvent(\n new CustomEvent(`db-tabs-sync:${this.syncKey}`, {\n detail: { value, source: this },\n }),\n );\n }\n\n // Dispatch change event\n this.dispatchEvent(\n new CustomEvent(\"db-tabs:change\", {\n detail: { value },\n bubbles: true,\n }),\n );\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-tabs\")) {\n customElements.define(\"db-tabs\", DbTabs);\n}\n"],"mappings":";;;;;;AAAA,SAAS,2BAA2B;AAIpC,IAAI,YAAY;AAChB,SAAS,SAAS,QAAgB;AAChC,SAAO,GAAG,MAAM,IAAI,EAAE,SAAS;AACjC;AAkBO,IAAM,SAAN,cAAqB,eAAe;AAAA,EACjC;AAAA,EACA,WAA0B,CAAC;AAAA,EAC3B,SAAwB,CAAC;AAAA,EACzB,MAAkC;AAAA,EAClC,QAAQ;AAAA,EACR,UAAyB;AAAA,EACzB,cAA2B;AAAA,EAE3B,cAAc,CAAC,MAAa;AAClC,UAAM,UAAW,EAAE,OAAuB;AAAA,MACxC;AAAA,IACF;AACA,QAAI,CAAC,WAAW,QAAQ,aAAa,UAAU,EAAG;AAClD,UAAM,QAAQ,QAAQ,QAAQ;AAC9B,QAAI,MAAO,MAAK,OAAO,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,IAAI,OAAwB;AAClC,WAAO,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU,KAAK;AAAA,EAC5D;AAAA,EAEQ,gBAAgB,CAAC,MAAoB;AAC3C,QAAI,CAAC,KAAK,WAAW,EAAE,QAAQ,gBAAgB,KAAK,OAAO,GAAI;AAC/D,UAAM,WAAW,EAAE;AACnB,QAAI,YAAY,KAAK,UAAU,YAAY,KAAK,IAAI,QAAQ,GAAG;AAC7D,WAAK,OAAO,QAAQ;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,mBAAmB,CAAC,MAAmB;AAC7C,UAAM,WAAW,EAAE,OAAO;AAC1B,QAAI,YAAY,KAAK,UAAU,YAAY,KAAK,IAAI,QAAQ,GAAG;AAC7D,WAAK,OAAO,QAAQ;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBQ,IAAI,UAAiC;AAC3C,WACE,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC,EAC1C,OAAO,CAAC,OAAO,GAAG,QAAQ,SAAS,MAAM,IAAI;AAAA,EACjD;AAAA,EAEU,QAAQ;AAChB,UAAM,OAAO,KAAK,IAAI,oBAAoB,EAAE,CAAC,KAAK;AAClD,QAAI,CAAC,KAAM;AACX,SAAK,OAAO;AAEZ,SAAK,WAAW,KAAK,IAAI,uBAAuB;AAChD,SAAK,SAAS,KAAK,IAAI,uBAAuB;AAE9C,SAAK,cACF,KAAK,aAAa,aAAa,KAAqB;AACvD,SAAK,UAAU,KAAK,aAAa,UAAU,KAAK;AAGhD,SAAK,aAAa,QAAQ,SAAS;AACnC,SAAK,aAAa,oBAAoB,KAAK,WAAW;AAGtD,SAAK,SAAS,QAAQ,CAAC,YAAY;AACjC,YAAM,QAAQ,QAAQ,QAAQ;AAC9B,cAAQ,aAAa,QAAQ,KAAK;AAClC,YAAM,YAAY,QAAQ,MAAM,SAAS,QAAQ;AACjD,cAAQ,KAAK;AAEb,YAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU,KAAK;AAC/D,UAAI,OAAO;AACT,cAAM,aAAa,QAAQ,UAAU;AACrC,cAAM,UAAU,MAAM,MAAM,SAAS,aAAa;AAClD,cAAM,KAAK;AACX,gBAAQ,aAAa,iBAAiB,OAAO;AAC7C,cAAM,aAAa,mBAAmB,SAAS;AAAA,MACjD;AAAA,IACF,CAAC;AAGD,SAAK,MAAM,IAAI,oBAAoB,MAAM,yBAAyB;AAAA,MAChE,aAAa,KAAK;AAAA,MAClB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,0BAA0B,MAAM;AAC9B,cAAM,UAAU,SAAS;AACzB,cAAM,QAAQ,SAAS,SAAS;AAChC,YAAI,MAAO,MAAK,OAAO,KAAK;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,SAAK,IAAI,SAAS;AAGlB,UAAM,eACJ,KAAK,aAAa,eAAe,KACjC,KAAK,SAAS,CAAC,GAAG,QAAQ,SAC1B;AAEF,QAAI,eAAe;AACnB,QAAI,KAAK,SAAS;AAChB,YAAM,SAAS,aAAa,QAAQ,gBAAgB,KAAK,OAAO,EAAE;AAClE,UAAI,UAAU,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU,MAAM,GAAG;AACnE,uBAAe;AAAA,MACjB;AAAA,IACF;AACA,UAAM,OAAO,OAAO,SAAS,KAAK,MAAM,CAAC;AACzC,QAAI,QAAQ,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU,IAAI,GAAG;AAC/D,qBAAe;AAAA,IACjB;AAEA,SAAK,OAAO,YAAY;AAAA,EAC1B;AAAA,EAEU,UAAU;AAClB,SAAK,MAAM,iBAAiB,SAAS,KAAK,WAAW;AACrD,QAAI,KAAK,SAAS;AAChB,aAAO,iBAAiB,WAAW,KAAK,aAAa;AACrD,eAAS;AAAA,QACP,gBAAgB,KAAK,OAAO;AAAA,QAC5B,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA,EAEU,aAAa;AACrB,SAAK,MAAM,oBAAoB,SAAS,KAAK,WAAW;AACxD,QAAI,KAAK,SAAS;AAChB,aAAO,oBAAoB,WAAW,KAAK,aAAa;AACxD,eAAS;AAAA,QACP,gBAAgB,KAAK,OAAO;AAAA,QAC5B,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,OAAe;AACpB,QAAI,KAAK,UAAU,MAAO;AAC1B,SAAK,QAAQ;AAGb,SAAK,SAAS,QAAQ,CAAC,YAAY;AACjC,YAAM,aAAa,QAAQ,QAAQ,UAAU;AAC7C,cAAQ,aAAa,iBAAiB,OAAO,UAAU,CAAC;AACxD,cAAQ,aAAa,YAAY,aAAa,MAAM,IAAI;AACxD,UAAI,YAAY;AACd,gBAAQ,aAAa,cAAc,QAAQ;AAAA,MAC7C,OAAO;AACL,gBAAQ,gBAAgB,YAAY;AAAA,MACtC;AAAA,IACF,CAAC;AAGD,SAAK,OAAO,QAAQ,CAAC,UAAU;AAC7B,YAAM,WAAW,MAAM,QAAQ,UAAU;AACzC,YAAM,SAAS,CAAC;AAChB,UAAI,UAAU;AACZ,cAAM,aAAa,cAAc,QAAQ;AACzC,YAAI,CAAC,MAAM,aAAa,UAAU,GAAG;AACnC,gBAAM,aAAa,YAAY,GAAG;AAAA,QACpC;AAAA,MAGF,OAAO;AACL,cAAM,gBAAgB,YAAY;AAAA,MACpC;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,SAAS;AAChB,mBAAa,QAAQ,gBAAgB,KAAK,OAAO,IAAI,KAAK;AAC1D,eAAS;AAAA,QACP,IAAI,YAAY,gBAAgB,KAAK,OAAO,IAAI;AAAA,UAC9C,QAAQ,EAAE,OAAO,QAAQ,KAAK;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,IACF;AAGA,SAAK;AAAA,MACH,IAAI,YAAY,kBAAkB;AAAA,QAChC,QAAQ,EAAE,MAAM;AAAA,QAChB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,SAAS,GAAG;AAClC,iBAAe,OAAO,WAAW,MAAM;AACzC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-accordion.ts"],"sourcesContent":["import { CompositeNavigation } from \"@dogsbay/primitives/composite\";\nimport { Collapsible } from \"@dogsbay/primitives/collapsible\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\ninterface AccordionItem {\n el: HTMLElement;\n value: string;\n collapsible: Collapsible;\n disabled: boolean;\n}\n\n/**\n * `<db-accordion>` — Accessible accordion with single/multiple mode.\n *\n * Attributes:\n * type — \"single\" (default) or \"multiple\"\n * default-value — comma-separated values to open initially\n *\n * Children:\n * [data-part=\"item\"] with data-value — accordion items\n * [data-part=\"trigger\"] — toggle button (inside item)\n * [data-part=\"content\"] — collapsible panel (inside item)\n *\n * Events:\n * db-accordion:change — detail: { value, open, openValues }\n */\nexport class DbAccordion extends DogsbayElement {\n private items: AccordionItem[] = [];\n private nav: CompositeNavigation | null = null;\n private openValues = new Set<string>();\n private type: \"single\" | \"multiple\" = \"single\";\n\n protected setup() {\n this.type =\n (this.getAttribute(\"type\") as \"single\" | \"multiple\") || \"single\";\n const defaultStr = this.getAttribute(\"default-value\") || \"\";\n const defaultValues = defaultStr ? defaultStr.split(\",\") : [];\n\n const itemEls = Array.from(\n this.querySelectorAll(\"[data-part='item']\"),\n ) as HTMLElement[];\n\n this.items = itemEls\n .map((el) => {\n const trigger = el.querySelector<HTMLElement>(\"[data-part='trigger']\");\n const content = el.querySelector<HTMLElement>(\"[data-part='content']\");\n if (!trigger || !content) return null;\n\n const value = el.dataset.value || \"\";\n const disabled = el.hasAttribute(\"data-disabled\");\n\n if (disabled) {\n trigger.setAttribute(\"disabled\", \"\");\n trigger.setAttribute(\"aria-disabled\", \"true\");\n }\n\n const collapsible = new Collapsible(trigger, content, {\n onToggle: (open) => {\n if (open) {\n this.onItemExpanded(value);\n } else {\n this.onItemCollapsed(value);\n }\n },\n });\n\n return { el, value, collapsible, disabled };\n })\n .filter(Boolean) as AccordionItem[];\n\n // Keyboard navigation on triggers\n this.nav = new CompositeNavigation(this, \"[data-part='trigger']\", {\n orientation: \"vertical\",\n loop: true,\n homeEnd: true,\n });\n\n this.nav.activate();\n\n // Set initial tabindex for roving\n this.items.forEach((item, i) => {\n item.collapsible.trigger.setAttribute(\"tabindex\", i === 0 ? \"0\" : \"-1\");\n });\n\n // Set initial open state\n defaultValues.forEach((value) => {\n const item = this.items.find((i) => i.value === value && !i.disabled);\n if (item) {\n item.collapsible.expand();\n }\n });\n }\n\n private onItemExpanded(value: string) {\n // In single mode, collapse all others\n if (this.type === \"single\") {\n for (const openValue of this.openValues) {\n if (openValue !== value) {\n const other = this.items.find((i) => i.value === openValue);\n if (other) other.collapsible.collapse();\n }\n }\n }\n\n this.openValues.add(value);\n const item = this.items.find((i) => i.value === value);\n if (item) item.el.setAttribute(\"data-state\", \"open\");\n\n this.dispatchEvent(\n new CustomEvent(\"db-accordion:change\", {\n detail: { value, open: true, openValues: [...this.openValues] },\n bubbles: true,\n }),\n );\n }\n\n private onItemCollapsed(value: string) {\n this.openValues.delete(value);\n const item = this.items.find((i) => i.value === value);\n if (item) item.el.removeAttribute(\"data-state\");\n\n this.dispatchEvent(\n new CustomEvent(\"db-accordion:change\", {\n detail: { value, open: false, openValues: [...this.openValues] },\n bubbles: true,\n }),\n );\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-accordion\")) {\n customElements.define(\"db-accordion\", DbAccordion);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"sources":["../src/db-card.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-card>` — Accessible content card with optional link behavior.\n *\n * Attributes:\n * href — if present, the card is clickable and navigates on click/Enter/Space\n *\n * Children:\n * [data-part=\"icon\"] — icon (optional)\n * [data-part=\"title\"] — card title (optional)\n * [data-part=\"description\"] — card description (optional)\n * [data-part=\"content\"] — card body content (optional)\n *\n * Events:\n * db-card:navigate — fired when a linked card is activated, detail: { href }\n * The host environment (Obsidian plugin or Astro) handles\n * actual navigation by listening for this event.\n */\nexport class DbCard extends DogsbayElement {\n private href: string | null = null;\n\n private handleClick = () => {\n if (this.href) this.navigate();\n };\n\n private handleKeydown = (e: KeyboardEvent) => {\n if (!this.href) return;\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n this.navigate();\n }\n };\n\n protected setup() {\n this.href = this.getAttribute(\"href\");\n\n if (this.href) {\n this.setAttribute(\"role\", \"link\");\n this.setAttribute(\"tabindex\", \"0\");\n this.setAttribute(\"data-interactive\", \"\");\n }\n }\n\n protected connect() {\n if (this.href) {\n this.addEventListener(\"click\", this.handleClick);\n this.addEventListener(\"keydown\", this.handleKeydown as EventListener);\n }\n }\n\n protected disconnect() {\n this.removeEventListener(\"click\", this.handleClick);\n this.removeEventListener(\"keydown\", this.handleKeydown as EventListener);\n }\n\n private navigate() {\n this.dispatchEvent(\n new CustomEvent(\"db-card:navigate\", {\n detail: { href: this.href },\n bubbles: true,\n }),\n );\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-card\")) {\n customElements.define(\"db-card\", DbCard);\n}\n"],"mappings":";;;;;;AAmBO,IAAM,SAAN,cAAqB,eAAe;AAAA,EACjC,OAAsB;AAAA,EAEtB,cAAc,MAAM;AAC1B,QAAI,KAAK,KAAM,MAAK,SAAS;AAAA,EAC/B;AAAA,EAEQ,gBAAgB,CAAC,MAAqB;AAC5C,QAAI,CAAC,KAAK,KAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,QAAE,eAAe;AACjB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEU,QAAQ;AAChB,SAAK,OAAO,KAAK,aAAa,MAAM;AAEpC,QAAI,KAAK,MAAM;AACb,WAAK,aAAa,QAAQ,MAAM;AAChC,WAAK,aAAa,YAAY,GAAG;AACjC,WAAK,aAAa,oBAAoB,EAAE;AAAA,IAC1C;AAAA,EACF;AAAA,EAEU,UAAU;AAClB,QAAI,KAAK,MAAM;AACb,WAAK,iBAAiB,SAAS,KAAK,WAAW;AAC/C,WAAK,iBAAiB,WAAW,KAAK,aAA8B;AAAA,IACtE;AAAA,EACF;AAAA,EAEU,aAAa;AACrB,SAAK,oBAAoB,SAAS,KAAK,WAAW;AAClD,SAAK,oBAAoB,WAAW,KAAK,aAA8B;AAAA,EACzE;AAAA,EAEQ,WAAW;AACjB,SAAK;AAAA,MACH,IAAI,YAAY,oBAAoB;AAAA,QAClC,QAAQ,EAAE,MAAM,KAAK,KAAK;AAAA,QAC1B,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,SAAS,GAAG;AAClC,iBAAe,OAAO,WAAW,MAAM;AACzC;","names":[]}
@@ -1 +0,0 @@
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":";;;;;;AAgBO,IAAM,QAAN,cAAoB,eAAe;AAAA,EAChC,QAA6B,CAAC;AAAA,EAC9B,WAA0B,CAAC;AAAA,EAC3B,WAAwC;AAAA,EACxC,SAAwB;AAAA,EAExB,OAAO,GAA8B;AAC3C,WAAO,EAAE,aAAa,eAAe,KAAK,WAAW,EAAE,aAAa,MAAM,EAAG,MAAM,CAAC,CAAC;AAAA,EACvF;AAAA;AAAA;AAAA,EAIQ,WAAW,MAAM;AACvB,UAAM,MAAM,SAAS;AACrB,QAAI,OAAO,cAAc,OAAO,WAAW,IAAI,eAAe,KAAK,KAAK,SAAS,QAAQ;AACvF,WAAK,UAAU,KAAK,SAAS,KAAK,SAAS,SAAS,CAAC,EAAE,EAAE;AAAA,IAC3D;AAAA,EACF;AAAA,EAEU,QAAQ;AAChB,SAAK,QAAQ,MAAM,KAAK,KAAK,iBAAoC,gCAAgC,CAAC;AAAA,EACpG;AAAA,EAEQ,SAAS,MAAM;AACrB,UAAM,KAAK,WAAW,SAAS,KAAK,MAAM,CAAC,CAAC;AAC5C,QAAI,MAAM,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,EAAG,MAAK,UAAU,EAAE;AAAA,EACrE;AAAA,EAEU,UAAU;AAClB,SAAK,WAAW,KAAK,MAAM,IAAI,CAAC,MAAM,SAAS,eAAe,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,MAAwB,CAAC,CAAC,CAAC;AAClH,QAAI,CAAC,KAAK,SAAS,OAAQ;AAC3B,UAAM,SAAS,OAAO,KAAK,aAAa,QAAQ,KAAK,EAAE;AACvD,QAAI,OAAO,yBAAyB,aAAa;AAC/C,WAAK,WAAW,IAAI;AAAA,QAClB,CAAC,YAAY;AACX,gBAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,cAAc;AAChD,cAAI,IAAK,QAAO,KAAK,UAAU,IAAI,OAAO,EAAE;AAC5C,eAAK,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,IAAI;AAAA,QACnD;AAAA,QACA,EAAE,YAAY,IAAI,MAAM,mBAAmB,WAAW,EAAE;AAAA,MAC1D;AACA,WAAK,SAAS,QAAQ,CAAC,MAAM,KAAK,SAAU,QAAQ,CAAC,CAAC;AAAA,IACxD;AACA,WAAO,iBAAiB,cAAc,KAAK,MAAM;AACjD,WAAO,iBAAiB,UAAU,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAClE,SAAK,UAAU,KAAK,UAAU,MAAM,GAAG,MAAM,KAAK,SAAS,CAAC,EAAE,EAAE;AAAA,EAClE;AAAA,EAEU,aAAa;AACrB,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW;AAChB,WAAO,oBAAoB,cAAc,KAAK,MAAM;AACpD,WAAO,oBAAoB,UAAU,KAAK,QAAQ;AAAA,EACpD;AAAA,EAEQ,UAAU,QAAoC;AACpD,QAAI,OAA2B;AAC/B,eAAW,KAAK,KAAK,SAAU,KAAI,EAAE,sBAAsB,EAAE,OAAO,SAAS,GAAI,QAAO;AACxF,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,IAAmB;AACnC,QAAI,CAAC,MAAM,OAAO,KAAK,OAAQ;AAC/B,SAAK,SAAS;AACd,eAAW,KAAK,KAAK,OAAO;AAC1B,UAAI,KAAK,OAAO,CAAC,MAAM,IAAI;AACzB,UAAE,aAAa,gBAAgB,UAAU;AACzC,UAAE,aAAa,eAAe,EAAE;AAAA,MAClC,OAAO;AACL,UAAE,gBAAgB,cAAc;AAChC,UAAE,gBAAgB,aAAa;AAAA,MACjC;AAAA,IACF;AACA,SAAK,cAAc,IAAI,YAAY,iBAAiB,EAAE,QAAQ,EAAE,GAAG,GAAG,SAAS,KAAK,CAAC,CAAC;AAAA,EACxF;AACF;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI;AACF,WAAO,mBAAmB,CAAC;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,QAAQ,GAAG;AACjC,iBAAe,OAAO,UAAU,KAAK;AACvC;","names":[]}
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"sources":["../src/db-collapsible.ts"],"sourcesContent":["import { Collapsible } from \"@dogsbay/primitives/collapsible\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-collapsible>` — Accessible expand/collapse panel.\n *\n * Attributes:\n * default-open — start expanded\n * disabled — prevent toggling\n *\n * Children:\n * [data-part=\"trigger\"] — toggle button\n * [data-part=\"content\"] — collapsible panel\n *\n * Events:\n * db-collapsible:toggle — fired on state change, detail: { open }\n */\nexport class DbCollapsible extends DogsbayElement {\n private collapsible: Collapsible | null = null;\n\n protected setup() {\n const trigger = this.querySelector<HTMLElement>(\"[data-part='trigger']\");\n const content = this.querySelector<HTMLElement>(\"[data-part='content']\");\n if (!trigger || !content) return;\n\n const disabled = this.hasAttribute(\"disabled\");\n if (disabled) {\n trigger.setAttribute(\"disabled\", \"\");\n trigger.setAttribute(\"aria-disabled\", \"true\");\n }\n\n this.collapsible = new Collapsible(trigger, content, {\n onToggle: (open) => {\n this.setAttribute(\"data-state\", open ? \"open\" : \"closed\");\n content.setAttribute(\"data-state\", open ? \"open\" : \"closed\");\n this.dispatchEvent(\n new CustomEvent(\"db-collapsible:toggle\", {\n detail: { open },\n bubbles: true,\n }),\n );\n },\n });\n\n if (this.hasAttribute(\"default-open\")) {\n this.collapsible.expand();\n content.setAttribute(\"data-state\", \"open\");\n } else {\n this.setAttribute(\"data-state\", \"closed\");\n content.setAttribute(\"data-state\", \"closed\");\n }\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-collapsible\")) {\n customElements.define(\"db-collapsible\", DbCollapsible);\n}\n"],"mappings":";;;;;;AAAA,SAAS,mBAAmB;AAiBrB,IAAM,gBAAN,cAA4B,eAAe;AAAA,EACxC,cAAkC;AAAA,EAEhC,QAAQ;AAChB,UAAM,UAAU,KAAK,cAA2B,uBAAuB;AACvE,UAAM,UAAU,KAAK,cAA2B,uBAAuB;AACvE,QAAI,CAAC,WAAW,CAAC,QAAS;AAE1B,UAAM,WAAW,KAAK,aAAa,UAAU;AAC7C,QAAI,UAAU;AACZ,cAAQ,aAAa,YAAY,EAAE;AACnC,cAAQ,aAAa,iBAAiB,MAAM;AAAA,IAC9C;AAEA,SAAK,cAAc,IAAI,YAAY,SAAS,SAAS;AAAA,MACnD,UAAU,CAAC,SAAS;AAClB,aAAK,aAAa,cAAc,OAAO,SAAS,QAAQ;AACxD,gBAAQ,aAAa,cAAc,OAAO,SAAS,QAAQ;AAC3D,aAAK;AAAA,UACH,IAAI,YAAY,yBAAyB;AAAA,YACvC,QAAQ,EAAE,KAAK;AAAA,YACf,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,KAAK,aAAa,cAAc,GAAG;AACrC,WAAK,YAAY,OAAO;AACxB,cAAQ,aAAa,cAAc,MAAM;AAAA,IAC3C,OAAO;AACL,WAAK,aAAa,cAAc,QAAQ;AACxC,cAAQ,aAAa,cAAc,QAAQ;AAAA,IAC7C;AAAA,EACF;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,gBAAgB,GAAG;AACzC,iBAAe,OAAO,kBAAkB,aAAa;AACvD;","names":[]}
@@ -1 +0,0 @@
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":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-tooltip.ts"],"sourcesContent":["import { setupFloating, type FloatingOptions } from \"@dogsbay/primitives/floating\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { hideFloating, ownOne, resolveAsChild, showFloating, uid } from \"./utils.js\";\n\ntype Placement = NonNullable<FloatingOptions[\"placement\"]>;\n\n/**\n * `<db-tooltip>` — Short description shown on hover and keyboard focus\n * (APG \"Tooltip\"). The trigger is described by the content\n * (aria-describedby), so screen readers announce it without showing it.\n *\n * WCAG 1.4.13 (content on hover or focus):\n * - hoverable: the pointer can move from the trigger onto the tooltip\n * without it closing (`close-delay`, default 100 ms);\n * - dismissible: Escape hides it, whether it opened from hover or focus;\n * - persistent: it stays until the pointer leaves, focus moves or Escape.\n *\n * Attributes:\n * delay — ms before showing on hover (default 200)\n * close-delay — ms grace period on leave (default 100)\n * placement — \"top\" (default), or data-side on the content\n *\n * Parts: [data-part=\"trigger\"] (`data-as-child` supported), [data-part=\"content\"]\n *\n * Events: db-tooltip:open, db-tooltip:close\n */\nexport class DbTooltip extends DogsbayElement {\n private trigger: HTMLElement | null = null;\n private content: HTMLElement | null = null;\n private cleanupFloating: (() => void) | null = null;\n private showTimer: ReturnType<typeof setTimeout> | null = null;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n private _open = false;\n\n get open(): boolean {\n return this._open;\n }\n\n private num(name: string, fallback: number): number {\n const v = Number(this.getAttribute(name));\n return Number.isFinite(v) && this.hasAttribute(name) ? v : fallback;\n }\n\n private onEnter = (e: PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n this.clearTimers();\n if (!this._open) this.showTimer = setTimeout(() => this.show(), this.num(\"delay\", 200));\n };\n\n private onLeave = (e: PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n this.clearTimers();\n this.hideTimer = setTimeout(() => this.hide(), this.num(\"close-delay\", 100));\n };\n\n private onContentEnter = () => this.clearTimers();\n\n // Keyboard focus only: a mouse press focuses the button too, and would reopen\n // the tooltip the press just closed.\n private onFocus = () => {\n let keyboard = true;\n try {\n keyboard = this.trigger!.matches(\":focus-visible\");\n } catch {\n /* :focus-visible unsupported: treat as keyboard */\n }\n if (!keyboard) return;\n this.clearTimers();\n this.show();\n };\n\n private onBlur = () => this.hide();\n\n private onPress = () => this.hide();\n\n // Capture phase, consumed: Escape dismisses only the tooltip, not a popover or\n // dialog around its trigger (WCAG 1.4.13, APG tooltip).\n private onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && this._open) {\n e.preventDefault();\n e.stopImmediatePropagation();\n this.hide();\n }\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 this.content.id ||= uid(\"db-tooltip\");\n this.content.setAttribute(\"role\", \"tooltip\");\n this.content.hidden = true;\n const described = (this.trigger.getAttribute(\"aria-describedby\") ?? \"\").split(/\\s+/).filter(Boolean);\n if (!described.includes(this.content.id)) this.trigger.setAttribute(\"aria-describedby\", [...described, this.content.id].join(\" \"));\n this.setState(\"closed\");\n }\n\n protected connect() {\n const { trigger, content } = this;\n if (!trigger || !content) return;\n trigger.addEventListener(\"pointerenter\", this.onEnter);\n trigger.addEventListener(\"pointerleave\", this.onLeave);\n trigger.addEventListener(\"focus\", this.onFocus);\n trigger.addEventListener(\"blur\", this.onBlur);\n trigger.addEventListener(\"pointerdown\", this.onPress);\n content.addEventListener(\"pointerenter\", this.onContentEnter);\n content.addEventListener(\"pointerleave\", this.onLeave);\n }\n\n protected disconnect() {\n const { trigger, content } = this;\n trigger?.removeEventListener(\"pointerenter\", this.onEnter);\n trigger?.removeEventListener(\"pointerleave\", this.onLeave);\n trigger?.removeEventListener(\"focus\", this.onFocus);\n trigger?.removeEventListener(\"blur\", this.onBlur);\n trigger?.removeEventListener(\"pointerdown\", this.onPress);\n content?.removeEventListener(\"pointerenter\", this.onContentEnter);\n content?.removeEventListener(\"pointerleave\", this.onLeave);\n this.hide();\n }\n\n private clearTimers() {\n if (this.showTimer) clearTimeout(this.showTimer);\n if (this.hideTimer) clearTimeout(this.hideTimer);\n this.showTimer = this.hideTimer = null;\n }\n\n private setState(state: \"open\" | \"closed\") {\n this.setAttribute(\"data-state\", state);\n this.content?.setAttribute(\"data-state\", state);\n }\n\n show() {\n const { trigger, content } = this;\n if (!trigger || !content || this._open) return;\n this._open = true;\n this.clearTimers();\n const layer = showFloating(content);\n this.setState(\"open\");\n this.cleanupFloating = setupFloating(trigger, content, {\n placement: (this.getAttribute(\"placement\") || content.dataset.side || \"top\") as Placement,\n offset: 8,\n strategy: layer === \"top-layer\" ? \"fixed\" : \"absolute\",\n });\n document.addEventListener(\"keydown\", this.onKey, true);\n this.dispatchEvent(new CustomEvent(\"db-tooltip:open\", { bubbles: true }));\n }\n\n hide() {\n this.clearTimers();\n if (!this._open || !this.content) return;\n this._open = false;\n document.removeEventListener(\"keydown\", this.onKey, true);\n this.cleanupFloating?.();\n this.cleanupFloating = null;\n hideFloating(this.content);\n this.setState(\"closed\");\n this.dispatchEvent(new CustomEvent(\"db-tooltip:close\", { bubbles: true }));\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-tooltip\")) {\n customElements.define(\"db-tooltip\", DbTooltip);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-link-button.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-link-button>` — Styled link that looks like a button.\n *\n * Attributes:\n * href — target URL (required)\n * variant — \"primary\" | \"secondary\" | \"outline\" (default: \"primary\")\n *\n * Light DOM: renders as an <a> tag with button styling.\n * Works in both Astro (matches Button component) and Obsidian.\n *\n * In Astro, the format-astro serializer maps link-button TreeNode to\n * the Button.astro component with href prop.\n *\n * In Obsidian, the plugin emits <db-link-button> which this element\n * auto-upgrades with styling and keyboard navigation.\n */\nexport class DbLinkButton extends DogsbayElement {\n private anchor: HTMLAnchorElement | null = null;\n\n protected setup() {\n const href = this.getAttribute(\"href\") || \"#\";\n const variant = this.getAttribute(\"variant\") || \"primary\";\n const text = this.textContent?.trim() || href;\n\n // Create the anchor element\n this.anchor = document.createElement(\"a\");\n this.anchor.href = href;\n this.anchor.textContent = text;\n this.anchor.className = \"db-link-button-anchor\";\n this.anchor.dataset.variant = variant;\n\n // External links open in new tab\n if (href.startsWith(\"http\")) {\n this.anchor.target = \"_blank\";\n this.anchor.rel = \"noopener noreferrer\";\n\n // Add external icon\n const icon = document.createElement(\"span\");\n icon.className = \"db-link-button-icon\";\n icon.setAttribute(\"aria-hidden\", \"true\");\n icon.textContent = \"↗\";\n this.anchor.appendChild(icon);\n }\n\n this.textContent = \"\";\n this.appendChild(this.anchor);\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-link-button\")) {\n customElements.define(\"db-link-button\", DbLinkButton);\n}\n"],"mappings":";;;;;;AAkBO,IAAM,eAAN,cAA2B,eAAe;AAAA,EACvC,SAAmC;AAAA,EAEjC,QAAQ;AAChB,UAAM,OAAO,KAAK,aAAa,MAAM,KAAK;AAC1C,UAAM,UAAU,KAAK,aAAa,SAAS,KAAK;AAChD,UAAM,OAAO,KAAK,aAAa,KAAK,KAAK;AAGzC,SAAK,SAAS,SAAS,cAAc,GAAG;AACxC,SAAK,OAAO,OAAO;AACnB,SAAK,OAAO,cAAc;AAC1B,SAAK,OAAO,YAAY;AACxB,SAAK,OAAO,QAAQ,UAAU;AAG9B,QAAI,KAAK,WAAW,MAAM,GAAG;AAC3B,WAAK,OAAO,SAAS;AACrB,WAAK,OAAO,MAAM;AAGlB,YAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,WAAK,YAAY;AACjB,WAAK,aAAa,eAAe,MAAM;AACvC,WAAK,cAAc;AACnB,WAAK,OAAO,YAAY,IAAI;AAAA,IAC9B;AAEA,SAAK,cAAc;AACnB,SAAK,YAAY,KAAK,MAAM;AAAA,EAC9B;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,gBAAgB,GAAG;AACzC,iBAAe,OAAO,kBAAkB,YAAY;AACtD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-nav-tree.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { own, uid } from \"./utils.js\";\n\n/**\n * [label, url, children?, noCurrent?]. `noCurrent` (1): never mark this link\n * as the current page even when its URL matches — an alias (a link to a page\n * that lives in another section) or a #section link. The server-rendered\n * tree already leaves those unmarked; lazily built groups must agree.\n */\nexport type NavData = [string, string | null, NavData[]?, 1?];\n\n/**\n * `<db-nav-tree>` — Site navigation tree (APG \"Disclosure Navigation\").\n *\n * A section that is also a page gets a real link AND a separate toggle\n * button as siblings — never a link inside `<summary>`, which is nested\n * interactive content (axe `nested-interactive`). A section with no page\n * is a single toggle button carrying the label. Every link stays in the\n * normal Tab order; arrow keys are an extra shortcut, not a replacement.\n *\n * Server-rendered markup works without JavaScript: links navigate, and the\n * branch to the current page is rendered open.\n *\n * <db-nav-tree>\n * <ul data-part=\"list\">\n * <li data-part=\"item\" data-state=\"open\">\n * <div data-part=\"row\">\n * <button data-part=\"toggle\" type=\"button\" aria-expanded=\"true\" aria-controls=\"g1\" aria-label=\"Toggle Guides\"></button>\n * <a data-part=\"link\" href=\"guides/index.html\">Guides</a>\n * </div>\n * <ul data-part=\"group\" id=\"g1\">…</ul>\n * </li>\n * <li data-part=\"item\"><div data-part=\"row\"><a data-part=\"link\" href=\"faq.html\" aria-current=\"page\">FAQ</a></div></li>\n * </ul>\n * </db-nav-tree>\n *\n * Lazy sections: a group with `data-lazy` and `data-path=\"0.3\"` is filled\n * from `navData` (or the global named by `data-source`) the first time it\n * opens. URLs in the data are prefixed with the `base` attribute.\n *\n * Attributes:\n * base — prefix for data URLs (e.g. \"../../\")\n * data-source — name of a window global holding NavData[]\n * toggle-label — accessible name template, default \"Toggle {label}\"\n * scroll-current — scroll the current page's link into view on connect\n * Built items carry `style=\"--db-level:N\"` (0 at the top) so CSS can indent\n * them; server markup should set the same.\n *\n * toggle-position — \"start\" (default) or \"end\": where lazily built rows put\n * the toggle relative to the link. Match the server markup\n * so DOM (and Tab) order follows the visual order.\n *\n * Keyboard (in addition to Tab): ArrowDown/ArrowUp move between visible\n * rows, ArrowRight opens a section or moves into it, ArrowLeft closes it or\n * moves to its parent, Home/End go to the first/last row.\n *\n * Events: db-nav-tree:toggle (detail: { item, open })\n */\nexport class DbNavTree extends DogsbayElement {\n /** Nav data for lazy sections. Falls back to `window[data-source]`. */\n navData: NavData[] | null = null;\n\n private onClick = (e: Event) => {\n const toggle = (e.target as HTMLElement).closest<HTMLElement>(\"[data-part='toggle']\");\n if (!toggle || toggle.closest(\"db-nav-tree\") !== this) return;\n const item = toggle.closest<HTMLElement>(\"[data-part='item']\");\n if (item) this.toggle(item);\n };\n\n private onKey = (e: KeyboardEvent) => {\n if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;\n const active = document.activeElement as HTMLElement | null;\n if (!active || !this.contains(active)) return;\n const rows = this.visibleRows();\n const row = active.closest<HTMLElement>(\"[data-part='row']\");\n const i = row ? rows.indexOf(row) : -1;\n if (i < 0) return;\n const item = row!.closest<HTMLElement>(\"[data-part='item']\")!;\n const hasGroup = !!this.groupOf(item);\n const focusRow = (r: HTMLElement | undefined) => {\n if (!r) return;\n e.preventDefault();\n this.primary(r)?.focus();\n };\n switch (e.key) {\n case \"ArrowDown\":\n return focusRow(rows[i + 1]);\n case \"ArrowUp\":\n return focusRow(rows[i - 1]);\n case \"Home\":\n return focusRow(rows[0]);\n case \"End\":\n return focusRow(rows[rows.length - 1]);\n case \"ArrowRight\":\n if (!hasGroup) return;\n e.preventDefault();\n if (!this.isOpen(item)) this.setOpen(item, true);\n else focusRow(this.visibleRows()[i + 1]);\n return;\n case \"ArrowLeft\": {\n e.preventDefault();\n if (hasGroup && this.isOpen(item)) return this.setOpen(item, false);\n const parent = item.parentElement?.closest<HTMLElement>(\"[data-part='item']\");\n if (parent && this.contains(parent)) {\n const pr = parent.querySelector<HTMLElement>(\":scope > [data-part='row']\");\n if (pr) this.primary(pr)?.focus();\n }\n return;\n }\n }\n };\n\n protected setup() {\n for (const item of own(this, \"[data-part='item']\")) this.prepareItem(item);\n }\n\n protected connect() {\n this.addEventListener(\"click\", this.onClick);\n this.addEventListener(\"keydown\", this.onKey);\n // Sections rendered open with lazy content fill now.\n for (const g of own(this, \"[data-part='group'][data-lazy]\")) {\n const item = g.closest<HTMLElement>(\"[data-part='item']\");\n if (item && this.isOpen(item)) this.fill(g);\n }\n if (this.hasAttribute(\"scroll-current\")) requestAnimationFrame(() => this.revealCurrent());\n }\n\n protected disconnect() {\n this.removeEventListener(\"click\", this.onClick);\n this.removeEventListener(\"keydown\", this.onKey);\n }\n\n private prepareItem(item: HTMLElement) {\n const group = this.groupOf(item);\n const toggle = item.querySelector<HTMLElement>(\":scope > [data-part='row'] > [data-part='toggle']\");\n if (!group || !toggle) return;\n group.id ||= uid(\"db-nav-group\");\n toggle.setAttribute(\"aria-controls\", group.id);\n if (toggle instanceof HTMLButtonElement && !toggle.hasAttribute(\"type\")) toggle.type = \"button\";\n const open = toggle.getAttribute(\"aria-expanded\") === \"true\" || item.getAttribute(\"data-state\") === \"open\";\n this.apply(item, open);\n }\n\n private groupOf(item: HTMLElement): HTMLElement | null {\n return item.querySelector<HTMLElement>(\":scope > [data-part='group']\");\n }\n\n private toggleOf(item: HTMLElement): HTMLElement | null {\n return item.querySelector<HTMLElement>(\":scope > [data-part='row'] > [data-part='toggle']\");\n }\n\n /** The row's main focus target: its link, or its toggle for a label-only section. */\n private primary(row: HTMLElement): HTMLElement | null {\n return row.querySelector<HTMLElement>(\":scope > [data-part='link']\") ?? row.querySelector<HTMLElement>(\":scope > [data-part='toggle']\");\n }\n\n private visibleRows(): HTMLElement[] {\n return own(this, \"[data-part='row']\").filter((r) => !r.closest(\"[hidden]\"));\n }\n\n isOpen(item: HTMLElement): boolean {\n return item.getAttribute(\"data-state\") === \"open\";\n }\n\n toggle(item: HTMLElement) {\n this.setOpen(item, !this.isOpen(item));\n }\n\n setOpen(item: HTMLElement, open: boolean) {\n const group = this.groupOf(item);\n if (!group || this.isOpen(item) === open) return;\n if (open && group.hasAttribute(\"data-lazy\")) this.fill(group);\n this.apply(item, open);\n this.dispatchEvent(new CustomEvent(\"db-nav-tree:toggle\", { detail: { item, open }, bubbles: true }));\n }\n\n private apply(item: HTMLElement, open: boolean) {\n const group = this.groupOf(item);\n const toggle = this.toggleOf(item);\n if (group) group.hidden = !open;\n toggle?.setAttribute(\"aria-expanded\", String(open));\n item.setAttribute(\"data-state\", open ? \"open\" : \"closed\");\n }\n\n /** Expand every section on the path to the current page. */\n revealCurrent() {\n const cur = this.querySelector<HTMLElement>(\"[aria-current='page']\");\n if (!cur) return;\n for (let item = cur.closest<HTMLElement>(\"[data-part='item']\"); item && this.contains(item); item = item.parentElement?.closest<HTMLElement>(\"[data-part='item']\") ?? null) {\n if (this.groupOf(item) && !this.groupOf(item)!.contains(cur)) continue;\n if (this.groupOf(item)) this.setOpen(item, true);\n }\n // Scroll only the nearest scrolling ancestor, and only when the link is out of view.\n const scroller = scrollParent(this);\n if (!scroller) return;\n const r = cur.getBoundingClientRect();\n const s = scroller.getBoundingClientRect();\n if (r.top < s.top || r.bottom > s.bottom) scroller.scrollTop += r.top - s.top - s.height / 2 + r.height / 2;\n }\n\n private data(): NavData[] | null {\n if (this.navData) return this.navData;\n const name = this.getAttribute(\"data-source\");\n const g = name ? (globalThis as Record<string, unknown>)[name] : undefined;\n return Array.isArray(g) ? (g as NavData[]) : null;\n }\n\n /** Fill a lazy group from nav data. */\n private fill(group: HTMLElement) {\n const data = this.data();\n const path = group.dataset.path;\n if (!data || path == null) return;\n let list: NavData[] | undefined = data;\n let node: NavData | undefined;\n for (const i of path.split(\".\").map(Number)) {\n node = list?.[i];\n list = node?.[2];\n }\n if (!node?.[2]) return;\n group.replaceChildren(...this.build(node[2], path));\n group.removeAttribute(\"data-lazy\");\n }\n\n private build(items: NavData[], path: string): HTMLElement[] {\n const base = this.getAttribute(\"base\") ?? \"\";\n const tmpl = this.getAttribute(\"toggle-label\") ?? \"Toggle {label}\";\n const current = location.href.split(\"#\")[0];\n return items.map(([label, url, kids, noCurrent], i) => {\n const here = `${path}.${i}`;\n const li = document.createElement(\"li\");\n li.setAttribute(\"data-part\", \"item\");\n // Depth for styling (indentation), matching server-rendered items.\n li.style.setProperty(\"--db-level\", String(here.split(\".\").length - 1));\n const row = document.createElement(\"div\");\n row.setAttribute(\"data-part\", \"row\");\n li.appendChild(row);\n // Nav data comes from a project's own TOC files; a `javascript:` (or any\n // scheme but http(s)/mailto) entry renders as text, never as a link.\n // Tested without tabs, newlines and other controls, which browsers\n // strip from a URL (`java\\tscript:` is javascript:).\n const probe = url?.replace(/[\\u0000-\\u0020]/g, \"\") ?? \"\";\n if (url != null && /^[a-z][a-z0-9+.-]*:/i.test(probe) && !/^(?:https?|mailto):/i.test(probe)) url = null;\n const external = url != null && /^[a-z][a-z0-9+.-]*:|^\\/\\//i.test(probe);\n let toggle: HTMLButtonElement | null = null;\n if (kids?.length) {\n toggle = document.createElement(\"button\");\n toggle.type = \"button\";\n toggle.setAttribute(\"data-part\", \"toggle\");\n toggle.setAttribute(\"aria-expanded\", \"false\");\n if (url != null) toggle.setAttribute(\"aria-label\", tmpl.replace(\"{label}\", () => label));\n else {\n const span = document.createElement(\"span\");\n span.setAttribute(\"data-part\", \"label\");\n span.textContent = label;\n toggle.appendChild(span);\n }\n toggle.insertAdjacentHTML(\"beforeend\", CHEVRON);\n }\n const atEnd = this.getAttribute(\"toggle-position\") === \"end\";\n if (toggle && !atEnd) row.appendChild(toggle);\n if (url != null) {\n const a = document.createElement(\"a\");\n a.setAttribute(\"data-part\", \"link\");\n a.textContent = label;\n a.href = external ? url : base + url;\n if (external) a.rel = \"noopener\";\n if (!noCurrent && a.href.split(\"#\")[0] === current) a.setAttribute(\"aria-current\", \"page\");\n row.appendChild(a);\n }\n if (url == null && !kids?.length) {\n // An entry with no usable link (dropped above) keeps its label.\n const span = document.createElement(\"span\");\n span.setAttribute(\"data-part\", \"label\");\n span.textContent = label;\n row.appendChild(span);\n }\n if (toggle && atEnd) row.appendChild(toggle);\n if (kids?.length) {\n const ul = document.createElement(\"ul\");\n ul.setAttribute(\"data-part\", \"group\");\n ul.dataset.lazy = \"\";\n ul.dataset.path = here;\n ul.hidden = true;\n li.setAttribute(\"data-state\", \"closed\");\n li.appendChild(ul);\n this.prepareItem(li);\n }\n return li;\n });\n }\n}\n\nconst CHEVRON =\n '<svg data-part=\"chevron\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m9 18 6-6-6-6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>';\n\nfunction scrollParent(el: HTMLElement): HTMLElement | null {\n for (let p = el.parentElement; p; p = p.parentElement) {\n const o = getComputedStyle(p).overflowY;\n if ((o === \"auto\" || o === \"scroll\") && p.scrollHeight > p.clientHeight) return p;\n }\n return null;\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-nav-tree\")) {\n customElements.define(\"db-nav-tree\", DbNavTree);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-dialog.ts"],"sourcesContent":["import { FocusTrap } from \"@dogsbay/primitives/focus-trap\";\nimport { ScrollLock } from \"@dogsbay/primitives/scroll-lock\";\nimport { DismissController } from \"@dogsbay/primitives/dismiss\";\nimport { markOthersInert } from \"@dogsbay/primitives/inert\";\nimport { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\nimport { afterAnimations, ownOne, resolveAsChild, uid } from \"./utils.js\";\n\nexport type DialogCloseReason = \"escape-key\" | \"outside-press\" | \"close-press\" | \"trigger-press\" | \"api\";\n\n/**\n * `<db-dialog>` — Modal dialog (WAI-ARIA APG \"Dialog (Modal)\").\n *\n * Same parts as the Astro Dialog. When `[data-part=\"content\"]` is a native\n * `<dialog>`, it opens with `showModal()`: the browser puts it in the top\n * layer, makes the rest of the page inert and handles Escape. Otherwise the\n * content is a positioned element and the primitives do that work\n * (FocusTrap, markOthersInert, ScrollLock, DismissController). Content is\n * never moved out of the element, so nested elements keep working.\n *\n * Attributes:\n * open — open on connect\n * dismissible=\"false\" — ignore Escape and outside clicks\n *\n * Parts:\n * [data-part=\"trigger\"] — opens (optional; `data-as-child` supported)\n * [data-part=\"content\"] — `<dialog>` (recommended) or any element\n * [data-part=\"backdrop\"] — overlay for non-native content\n * [data-part=\"title\"] — becomes aria-labelledby\n * [data-part=\"description\"] — becomes aria-describedby\n * [data-part=\"close\"] — closes\n *\n * External triggers: any element with `data-dialog-for=\"<id>\"`, where the id\n * is the element's or the content's, opens it from anywhere on the page.\n *\n * Events: db-dialog:open, db-dialog:close (detail: { reason })\n */\nexport class DbDialog extends DogsbayElement {\n content: HTMLElement | null = null;\n private backdrop: HTMLElement | null = null;\n private trigger: HTMLElement | null = null;\n private native = false;\n private _open = false;\n private openedBy: HTMLElement | null = null;\n private focusTrap: FocusTrap | null = null;\n private dismiss: DismissController | null = null;\n private cleanupInert: (() => void) | null = null;\n\n get open(): boolean {\n return this._open;\n }\n\n private get dismissible(): boolean {\n return this.getAttribute(\"dismissible\") !== \"false\";\n }\n\n private onTriggerClick = (e: Event) => {\n e.preventDefault();\n this.toggle(e.currentTarget as HTMLElement);\n };\n\n private onHostClick = (e: Event) => {\n const close = (e.target as HTMLElement).closest(\"[data-part='close']\");\n if (close && close.closest(\"db-dialog\") === this) this.close(\"close-press\");\n };\n\n private onDocumentClick = (e: Event) => {\n const ext = (e.target as HTMLElement).closest?.(\"[data-dialog-for]\") as HTMLElement | null;\n if (!ext || ext === this.trigger) return;\n const target = ext.getAttribute(\"data-dialog-for\");\n if (target && (target === this.id || target === this.content?.id)) {\n e.preventDefault();\n this.toggle(ext);\n }\n };\n\n // Native <dialog>: Escape fires \"cancel\".\n private onCancel = (e: Event) => {\n e.preventDefault();\n if (this.dismissible) this.close(\"escape-key\");\n };\n\n // Native <dialog>: a click on the ::backdrop targets the dialog itself.\n private onNativeClick = (e: MouseEvent) => {\n if (!this.dismissible || e.target !== this.content || !this.content) return;\n const r = this.content.getBoundingClientRect();\n const inside = e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom;\n if (!inside && (e.clientX !== 0 || e.clientY !== 0)) this.close(\"outside-press\");\n };\n\n // Native <dialog> closed by the platform (form method=\"dialog\").\n private onNativeClose = () => {\n if (this._open) this.finishClose(\"close-press\");\n };\n\n protected setup() {\n this.content = ownOne(this, \"[data-part='content']\");\n if (!this.content) return;\n this.native = typeof HTMLDialogElement !== \"undefined\" && this.content instanceof HTMLDialogElement && typeof this.content.showModal === \"function\";\n this.backdrop = ownOne(this, \"[data-part='backdrop']\");\n this.trigger = resolveAsChild(ownOne(this, \"[data-part='trigger']\"));\n\n const content = this.content;\n if (!content.id) content.id = uid(\"db-dialog\");\n if (!this.native) {\n content.setAttribute(\"role\", content.getAttribute(\"role\") || \"dialog\");\n content.setAttribute(\"aria-modal\", \"true\");\n content.hidden = !this.hasAttribute(\"open\");\n if (this.backdrop) this.backdrop.hidden = true;\n }\n\n const title = content.querySelector<HTMLElement>(\"[data-part='title']\");\n if (title && !content.hasAttribute(\"aria-labelledby\") && !content.hasAttribute(\"aria-label\")) {\n title.id ||= uid(\"db-dialog-title\");\n content.setAttribute(\"aria-labelledby\", title.id);\n }\n const desc = content.querySelector<HTMLElement>(\"[data-part='description']\");\n if (desc && !content.hasAttribute(\"aria-describedby\")) {\n desc.id ||= uid(\"db-dialog-desc\");\n content.setAttribute(\"aria-describedby\", desc.id);\n }\n\n for (const t of this.triggers()) this.prepareTrigger(t);\n this.setState(\"closed\");\n\n if (!this.native) {\n this.focusTrap = new FocusTrap(content);\n this.dismiss = new DismissController(content, {\n onDismiss: (reason) => this.dismissible && this.close(reason),\n });\n }\n }\n\n protected connect() {\n if (!this.content) return;\n this.trigger?.addEventListener(\"click\", this.onTriggerClick);\n this.addEventListener(\"click\", this.onHostClick);\n document.addEventListener(\"click\", this.onDocumentClick);\n if (this.native) {\n this.content.addEventListener(\"cancel\", this.onCancel);\n this.content.addEventListener(\"click\", this.onNativeClick as EventListener);\n this.content.addEventListener(\"close\", this.onNativeClose);\n }\n // External triggers may come later in the document than the dialog.\n queueMicrotask(() => {\n if (!this.isConnected) return;\n for (const t of this.triggers()) if (!t.hasAttribute(\"aria-controls\")) this.prepareTrigger(t);\n if (this.hasAttribute(\"open\") && !this._open) this.show();\n });\n }\n\n protected disconnect() {\n this.trigger?.removeEventListener(\"click\", this.onTriggerClick);\n this.removeEventListener(\"click\", this.onHostClick);\n document.removeEventListener(\"click\", this.onDocumentClick);\n if (this.content && this.native) {\n this.content.removeEventListener(\"cancel\", this.onCancel);\n this.content.removeEventListener(\"click\", this.onNativeClick as EventListener);\n this.content.removeEventListener(\"close\", this.onNativeClose);\n }\n if (this._open) this.finishClose(\"api\", false);\n }\n\n /** Internal trigger plus every `[data-dialog-for]` pointing here. */\n private triggers(): HTMLElement[] {\n const out: HTMLElement[] = this.trigger ? [this.trigger] : [];\n const ids = [this.id, this.content?.id].filter(Boolean);\n for (const id of ids) document.querySelectorAll<HTMLElement>(`[data-dialog-for=\"${CSS.escape(id!)}\"]`).forEach((el) => out.push(el));\n return out;\n }\n\n private prepareTrigger(t: HTMLElement) {\n if (!t.hasAttribute(\"aria-haspopup\")) t.setAttribute(\"aria-haspopup\", \"dialog\");\n t.setAttribute(\"aria-controls\", this.content!.id);\n t.setAttribute(\"aria-expanded\", \"false\");\n }\n\n private setState(state: \"open\" | \"closed\") {\n this.setAttribute(\"data-state\", state);\n this.content?.setAttribute(\"data-state\", state);\n this.backdrop?.setAttribute(\"data-state\", state);\n }\n\n toggle(opener?: HTMLElement) {\n if (this._open) this.close(\"trigger-press\");\n else this.show(opener);\n }\n\n show(opener?: HTMLElement) {\n const content = this.content;\n if (!content || this._open) return;\n this._open = true;\n this.openedBy = opener ?? (document.activeElement as HTMLElement | null) ?? this.trigger;\n for (const t of this.triggers()) {\n this.prepareTrigger(t);\n t.setAttribute(\"aria-expanded\", \"true\");\n }\n ScrollLock.activate();\n if (this.native) {\n const d = content as HTMLDialogElement;\n if (!d.open) d.showModal();\n } else {\n content.hidden = false;\n if (this.backdrop) this.backdrop.hidden = false;\n this.cleanupInert = markOthersInert(content);\n this.focusTrap?.activate();\n this.dismiss?.activate();\n }\n this.setState(\"open\");\n this.dispatchEvent(new CustomEvent(\"db-dialog:open\", { bubbles: true }));\n }\n\n close(reason: DialogCloseReason = \"api\") {\n if (!this._open) return;\n this.finishClose(reason);\n }\n\n private finishClose(reason: DialogCloseReason, restoreFocus = true) {\n const content = this.content!;\n this._open = false;\n this.setState(\"closed\");\n for (const t of this.triggers()) t.setAttribute(\"aria-expanded\", \"false\");\n ScrollLock.deactivate();\n if (this.native) {\n const d = content as HTMLDialogElement;\n if (d.open) d.close();\n } else {\n this.dismiss?.deactivate();\n this.focusTrap?.deactivate();\n this.cleanupInert?.();\n this.cleanupInert = null;\n afterAnimations(content, () => {\n if (this._open) return;\n content.hidden = true;\n if (this.backdrop) this.backdrop.hidden = true;\n });\n }\n const back = this.openedBy;\n this.openedBy = null;\n if (restoreFocus && back && document.contains(back)) back.focus();\n this.dispatchEvent(new CustomEvent(\"db-dialog:close\", { detail: { reason }, bubbles: true }));\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-dialog\")) {\n customElements.define(\"db-dialog\", DbDialog);\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db-code-block.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-code-block>` — Code block with copy-to-clipboard.\n *\n * Attributes:\n * data-copy-text — raw code text to copy (preferred over parsing DOM)\n *\n * Children:\n * [data-part=\"header\"] — header bar (optional)\n * [data-part=\"label\"] — title/language label (inside header, optional)\n * [data-part=\"copy\"] — copy button\n * [data-part=\"copy-icon\"] — copy icon SVG (inside copy button)\n * [data-part=\"check-icon\"] — check icon SVG (inside copy button, starts hidden)\n * [data-part=\"code\"] — code content container\n *\n * Events:\n * db-code-block:copied — fired after successful copy, detail: { text }\n */\nexport class DbCodeBlock extends DogsbayElement {\n private copyBtn: HTMLElement | null = null;\n private copyIcon: HTMLElement | null = null;\n private checkIcon: HTMLElement | null = null;\n private liveRegion: HTMLElement | null = null;\n private resetTimeout: ReturnType<typeof setTimeout> | null = null;\n\n private handleCopy = async () => {\n const text = this.getCopyText();\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n this.showCopiedFeedback();\n\n this.dispatchEvent(\n new CustomEvent(\"db-code-block:copied\", {\n detail: { text },\n bubbles: true,\n }),\n );\n } catch {\n // Clipboard API unavailable or denied — fail silently\n }\n };\n\n protected setup() {\n this.copyBtn = this.querySelector(\"[data-part='copy']\");\n this.copyIcon = this.querySelector(\"[data-part='copy-icon']\");\n this.checkIcon = this.querySelector(\"[data-part='check-icon']\");\n\n // Ensure copy button has accessible label\n if (this.copyBtn && !this.copyBtn.hasAttribute(\"aria-label\")) {\n this.copyBtn.setAttribute(\"aria-label\", \"Copy code\");\n }\n\n // Create aria-live region for screen reader announcements\n this.liveRegion = document.createElement(\"span\");\n this.liveRegion.setAttribute(\"aria-live\", \"polite\");\n this.liveRegion.setAttribute(\"role\", \"status\");\n this.liveRegion.className = \"sr-only\";\n this.liveRegion.style.position = \"absolute\";\n this.liveRegion.style.width = \"1px\";\n this.liveRegion.style.height = \"1px\";\n this.liveRegion.style.overflow = \"hidden\";\n this.liveRegion.style.clip = \"rect(0, 0, 0, 0)\";\n this.liveRegion.style.whiteSpace = \"nowrap\";\n this.appendChild(this.liveRegion);\n }\n\n protected connect() {\n this.copyBtn?.addEventListener(\"click\", this.handleCopy);\n }\n\n protected disconnect() {\n this.copyBtn?.removeEventListener(\"click\", this.handleCopy);\n this.clearResetTimeout();\n }\n\n private getCopyText(): string {\n // Prefer explicit attribute (set by the processor with raw code)\n const attrText = this.getAttribute(\"data-copy-text\");\n if (attrText) return attrText;\n\n // Fallback: extract text from the code container\n const codeEl = this.querySelector(\"[data-part='code'] code\") as HTMLElement | null;\n if (codeEl) return codeEl.textContent ?? \"\";\n\n const codeContainer = this.querySelector(\"[data-part='code']\") as HTMLElement | null;\n return codeContainer?.textContent ?? \"\";\n }\n\n private showCopiedFeedback() {\n this.clearResetTimeout();\n\n // Swap icons\n if (this.copyIcon) this.copyIcon.hidden = true;\n if (this.checkIcon) this.checkIcon.hidden = false;\n\n // Visual state\n this.setAttribute(\"data-state\", \"copied\");\n\n // Screen reader announcement\n if (this.liveRegion) this.liveRegion.textContent = \"Copied!\";\n\n // Revert after 2 seconds\n this.resetTimeout = setTimeout(() => {\n if (this.copyIcon) this.copyIcon.hidden = false;\n if (this.checkIcon) this.checkIcon.hidden = true;\n this.removeAttribute(\"data-state\");\n if (this.liveRegion) this.liveRegion.textContent = \"\";\n this.resetTimeout = null;\n }, 2000);\n }\n\n private clearResetTimeout() {\n if (this.resetTimeout !== null) {\n clearTimeout(this.resetTimeout);\n this.resetTimeout = null;\n }\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-code-block\")) {\n customElements.define(\"db-code-block\", DbCodeBlock);\n}\n"],"mappings":";;;;;;AAmBO,IAAM,cAAN,cAA0B,eAAe;AAAA,EACtC,UAA8B;AAAA,EAC9B,WAA+B;AAAA,EAC/B,YAAgC;AAAA,EAChC,aAAiC;AAAA,EACjC,eAAqD;AAAA,EAErD,aAAa,YAAY;AAC/B,UAAM,OAAO,KAAK,YAAY;AAC9B,QAAI,CAAC,KAAM;AAEX,QAAI;AACF,YAAM,UAAU,UAAU,UAAU,IAAI;AACxC,WAAK,mBAAmB;AAExB,WAAK;AAAA,QACH,IAAI,YAAY,wBAAwB;AAAA,UACtC,QAAQ,EAAE,KAAK;AAAA,UACf,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEU,QAAQ;AAChB,SAAK,UAAU,KAAK,cAAc,oBAAoB;AACtD,SAAK,WAAW,KAAK,cAAc,yBAAyB;AAC5D,SAAK,YAAY,KAAK,cAAc,0BAA0B;AAG9D,QAAI,KAAK,WAAW,CAAC,KAAK,QAAQ,aAAa,YAAY,GAAG;AAC5D,WAAK,QAAQ,aAAa,cAAc,WAAW;AAAA,IACrD;AAGA,SAAK,aAAa,SAAS,cAAc,MAAM;AAC/C,SAAK,WAAW,aAAa,aAAa,QAAQ;AAClD,SAAK,WAAW,aAAa,QAAQ,QAAQ;AAC7C,SAAK,WAAW,YAAY;AAC5B,SAAK,WAAW,MAAM,WAAW;AACjC,SAAK,WAAW,MAAM,QAAQ;AAC9B,SAAK,WAAW,MAAM,SAAS;AAC/B,SAAK,WAAW,MAAM,WAAW;AACjC,SAAK,WAAW,MAAM,OAAO;AAC7B,SAAK,WAAW,MAAM,aAAa;AACnC,SAAK,YAAY,KAAK,UAAU;AAAA,EAClC;AAAA,EAEU,UAAU;AAClB,SAAK,SAAS,iBAAiB,SAAS,KAAK,UAAU;AAAA,EACzD;AAAA,EAEU,aAAa;AACrB,SAAK,SAAS,oBAAoB,SAAS,KAAK,UAAU;AAC1D,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEQ,cAAsB;AAE5B,UAAM,WAAW,KAAK,aAAa,gBAAgB;AACnD,QAAI,SAAU,QAAO;AAGrB,UAAM,SAAS,KAAK,cAAc,yBAAyB;AAC3D,QAAI,OAAQ,QAAO,OAAO,eAAe;AAEzC,UAAM,gBAAgB,KAAK,cAAc,oBAAoB;AAC7D,WAAO,eAAe,eAAe;AAAA,EACvC;AAAA,EAEQ,qBAAqB;AAC3B,SAAK,kBAAkB;AAGvB,QAAI,KAAK,SAAU,MAAK,SAAS,SAAS;AAC1C,QAAI,KAAK,UAAW,MAAK,UAAU,SAAS;AAG5C,SAAK,aAAa,cAAc,QAAQ;AAGxC,QAAI,KAAK,WAAY,MAAK,WAAW,cAAc;AAGnD,SAAK,eAAe,WAAW,MAAM;AACnC,UAAI,KAAK,SAAU,MAAK,SAAS,SAAS;AAC1C,UAAI,KAAK,UAAW,MAAK,UAAU,SAAS;AAC5C,WAAK,gBAAgB,YAAY;AACjC,UAAI,KAAK,WAAY,MAAK,WAAW,cAAc;AACnD,WAAK,eAAe;AAAA,IACtB,GAAG,GAAI;AAAA,EACT;AAAA,EAEQ,oBAAoB;AAC1B,QAAI,KAAK,iBAAiB,MAAM;AAC9B,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,eAAe,GAAG;AACxC,iBAAe,OAAO,iBAAiB,WAAW;AACpD;","names":[]}