@dogsbay/elements 0.2.0-beta.12 → 0.2.0-beta.120
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.
- package/dist/{base-BtvRtAsC.d.ts → base-D1Rz70fn.d.ts} +1 -0
- package/dist/chunk-3AEQ6KHV.js +70 -0
- package/dist/chunk-3AEQ6KHV.js.map +1 -0
- package/dist/chunk-3FMXAKOU.js +383 -0
- package/dist/chunk-3FMXAKOU.js.map +1 -0
- package/dist/{chunk-LVXR7KTB.js → chunk-4YPDR3W6.js} +41 -12
- package/dist/chunk-4YPDR3W6.js.map +1 -0
- package/dist/{chunk-7M7SFLKN.js → chunk-CLUADA3G.js} +5 -3
- package/dist/chunk-CLUADA3G.js.map +1 -0
- package/dist/chunk-EGU66XCL.js +116 -0
- package/dist/chunk-EGU66XCL.js.map +1 -0
- package/dist/{chunk-7BIVQSGP.js → chunk-ETBBSA3I.js} +5 -3
- package/dist/chunk-ETBBSA3I.js.map +1 -0
- package/dist/chunk-FIJZRZEN.js +90 -0
- package/dist/chunk-FIJZRZEN.js.map +1 -0
- package/dist/chunk-K6DPDBAG.js +72 -0
- package/dist/chunk-K6DPDBAG.js.map +1 -0
- package/dist/{chunk-TTNTPXBA.js → chunk-LAUNXWKJ.js} +5 -3
- package/dist/chunk-LAUNXWKJ.js.map +1 -0
- package/dist/chunk-MAD6XJY4.js +256 -0
- package/dist/chunk-MAD6XJY4.js.map +1 -0
- package/dist/chunk-MEOIAZDF.js +140 -0
- package/dist/chunk-MEOIAZDF.js.map +1 -0
- package/dist/{chunk-LFXZQ4YV.js → chunk-QSRUT67Y.js} +5 -3
- package/dist/chunk-QSRUT67Y.js.map +1 -0
- package/dist/chunk-S62ZVVO3.js +234 -0
- package/dist/chunk-S62ZVVO3.js.map +1 -0
- package/dist/chunk-SLJG53M7.js +208 -0
- package/dist/chunk-SLJG53M7.js.map +1 -0
- package/dist/{chunk-FWAAU5UY.js → chunk-V4P3UR4I.js} +5 -3
- package/dist/chunk-V4P3UR4I.js.map +1 -0
- package/dist/{chunk-5T4OXQA2.js → chunk-VW7HZY2B.js} +6 -6
- package/dist/chunk-VW7HZY2B.js.map +1 -0
- package/dist/db-accordion.d.ts +1 -1
- package/dist/db-accordion.js +2 -2
- package/dist/db-card.d.ts +1 -1
- package/dist/db-card.js +2 -2
- package/dist/db-code-block.d.ts +1 -1
- package/dist/db-code-block.js +2 -2
- package/dist/db-collapsible.d.ts +1 -1
- package/dist/db-collapsible.js +2 -2
- package/dist/db-combobox.d.ts +70 -0
- package/dist/db-combobox.js +9 -0
- package/dist/db-combobox.js.map +1 -0
- package/dist/db-dialog.d.ts +62 -0
- package/dist/db-dialog.js +9 -0
- package/dist/db-dialog.js.map +1 -0
- package/dist/db-link-button.d.ts +1 -1
- package/dist/db-link-button.js +2 -2
- package/dist/db-nav-tree.d.ts +83 -0
- package/dist/db-nav-tree.js +9 -0
- package/dist/db-nav-tree.js.map +1 -0
- package/dist/db-popover.d.ts +42 -0
- package/dist/db-popover.js +9 -0
- package/dist/db-popover.js.map +1 -0
- package/dist/db-select.d.ts +72 -0
- package/dist/db-select.js +9 -0
- package/dist/db-select.js.map +1 -0
- package/dist/db-steps.d.ts +1 -1
- package/dist/db-steps.js +2 -2
- package/dist/db-tabs.d.ts +28 -1
- package/dist/db-tabs.js +2 -2
- package/dist/db-toc.d.ts +32 -0
- package/dist/db-toc.js +8 -0
- package/dist/db-toc.js.map +1 -0
- package/dist/db-tooltip.d.ts +48 -0
- package/dist/db-tooltip.js +9 -0
- package/dist/db-tooltip.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +40 -11
- package/package.json +34 -6
- package/dist/chunk-5T4OXQA2.js.map +0 -1
- package/dist/chunk-7BIVQSGP.js.map +0 -1
- package/dist/chunk-7M7SFLKN.js.map +0 -1
- package/dist/chunk-FWAAU5UY.js.map +0 -1
- package/dist/chunk-LFXZQ4YV.js.map +0 -1
- package/dist/chunk-LVXR7KTB.js.map +0 -1
- package/dist/chunk-TTNTPXBA.js.map +0 -1
- package/dist/chunk-XBMLE4N3.js +0 -28
- package/dist/chunk-XBMLE4N3.js.map +0 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
own,
|
|
3
|
+
uid
|
|
4
|
+
} from "./chunk-3AEQ6KHV.js";
|
|
5
|
+
import {
|
|
6
|
+
DogsbayElement,
|
|
7
|
+
ensureDefaultDisplay
|
|
8
|
+
} from "./chunk-K6DPDBAG.js";
|
|
9
|
+
|
|
10
|
+
// src/db-nav-tree.ts
|
|
11
|
+
var DbNavTree = class extends DogsbayElement {
|
|
12
|
+
/** Nav data for lazy sections. Falls back to `window[data-source]`. */
|
|
13
|
+
navData = null;
|
|
14
|
+
onClick = (e) => {
|
|
15
|
+
const toggle = e.target.closest("[data-part='toggle']");
|
|
16
|
+
if (!toggle || toggle.closest("db-nav-tree") !== this) return;
|
|
17
|
+
const item = toggle.closest("[data-part='item']");
|
|
18
|
+
if (item) this.toggle(item);
|
|
19
|
+
};
|
|
20
|
+
onKey = (e) => {
|
|
21
|
+
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
|
22
|
+
const active = document.activeElement;
|
|
23
|
+
if (!active || !this.contains(active)) return;
|
|
24
|
+
const rows = this.visibleRows();
|
|
25
|
+
const row = active.closest("[data-part='row']");
|
|
26
|
+
const i = row ? rows.indexOf(row) : -1;
|
|
27
|
+
if (i < 0) return;
|
|
28
|
+
const item = row.closest("[data-part='item']");
|
|
29
|
+
const hasGroup = !!this.groupOf(item);
|
|
30
|
+
const focusRow = (r) => {
|
|
31
|
+
if (!r) return;
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
this.primary(r)?.focus();
|
|
34
|
+
};
|
|
35
|
+
switch (e.key) {
|
|
36
|
+
case "ArrowDown":
|
|
37
|
+
return focusRow(rows[i + 1]);
|
|
38
|
+
case "ArrowUp":
|
|
39
|
+
return focusRow(rows[i - 1]);
|
|
40
|
+
case "Home":
|
|
41
|
+
return focusRow(rows[0]);
|
|
42
|
+
case "End":
|
|
43
|
+
return focusRow(rows[rows.length - 1]);
|
|
44
|
+
case "ArrowRight":
|
|
45
|
+
if (!hasGroup) return;
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
if (!this.isOpen(item)) this.setOpen(item, true);
|
|
48
|
+
else focusRow(this.visibleRows()[i + 1]);
|
|
49
|
+
return;
|
|
50
|
+
case "ArrowLeft": {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
if (hasGroup && this.isOpen(item)) return this.setOpen(item, false);
|
|
53
|
+
const parent = item.parentElement?.closest("[data-part='item']");
|
|
54
|
+
if (parent && this.contains(parent)) {
|
|
55
|
+
const pr = parent.querySelector(":scope > [data-part='row']");
|
|
56
|
+
if (pr) this.primary(pr)?.focus();
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
setup() {
|
|
63
|
+
for (const item of own(this, "[data-part='item']")) this.prepareItem(item);
|
|
64
|
+
}
|
|
65
|
+
connect() {
|
|
66
|
+
this.addEventListener("click", this.onClick);
|
|
67
|
+
this.addEventListener("keydown", this.onKey);
|
|
68
|
+
for (const g of own(this, "[data-part='group'][data-lazy]")) {
|
|
69
|
+
const item = g.closest("[data-part='item']");
|
|
70
|
+
if (item && this.isOpen(item)) this.fill(g);
|
|
71
|
+
}
|
|
72
|
+
if (this.hasAttribute("scroll-current")) requestAnimationFrame(() => this.revealCurrent());
|
|
73
|
+
}
|
|
74
|
+
disconnect() {
|
|
75
|
+
this.removeEventListener("click", this.onClick);
|
|
76
|
+
this.removeEventListener("keydown", this.onKey);
|
|
77
|
+
}
|
|
78
|
+
prepareItem(item) {
|
|
79
|
+
const group = this.groupOf(item);
|
|
80
|
+
const toggle = item.querySelector(":scope > [data-part='row'] > [data-part='toggle']");
|
|
81
|
+
if (!group || !toggle) return;
|
|
82
|
+
group.id ||= uid("db-nav-group");
|
|
83
|
+
toggle.setAttribute("aria-controls", group.id);
|
|
84
|
+
if (toggle instanceof HTMLButtonElement && !toggle.hasAttribute("type")) toggle.type = "button";
|
|
85
|
+
const open = toggle.getAttribute("aria-expanded") === "true" || item.getAttribute("data-state") === "open";
|
|
86
|
+
this.apply(item, open);
|
|
87
|
+
}
|
|
88
|
+
groupOf(item) {
|
|
89
|
+
return item.querySelector(":scope > [data-part='group']");
|
|
90
|
+
}
|
|
91
|
+
toggleOf(item) {
|
|
92
|
+
return item.querySelector(":scope > [data-part='row'] > [data-part='toggle']");
|
|
93
|
+
}
|
|
94
|
+
/** The row's main focus target: its link, or its toggle for a label-only section. */
|
|
95
|
+
primary(row) {
|
|
96
|
+
return row.querySelector(":scope > [data-part='link']") ?? row.querySelector(":scope > [data-part='toggle']");
|
|
97
|
+
}
|
|
98
|
+
visibleRows() {
|
|
99
|
+
return own(this, "[data-part='row']").filter((r) => !r.closest("[hidden]"));
|
|
100
|
+
}
|
|
101
|
+
isOpen(item) {
|
|
102
|
+
return item.getAttribute("data-state") === "open";
|
|
103
|
+
}
|
|
104
|
+
toggle(item) {
|
|
105
|
+
this.setOpen(item, !this.isOpen(item));
|
|
106
|
+
}
|
|
107
|
+
setOpen(item, open) {
|
|
108
|
+
const group = this.groupOf(item);
|
|
109
|
+
if (!group || this.isOpen(item) === open) return;
|
|
110
|
+
if (open && group.hasAttribute("data-lazy")) this.fill(group);
|
|
111
|
+
this.apply(item, open);
|
|
112
|
+
this.dispatchEvent(new CustomEvent("db-nav-tree:toggle", { detail: { item, open }, bubbles: true }));
|
|
113
|
+
}
|
|
114
|
+
apply(item, open) {
|
|
115
|
+
const group = this.groupOf(item);
|
|
116
|
+
const toggle = this.toggleOf(item);
|
|
117
|
+
if (group) group.hidden = !open;
|
|
118
|
+
toggle?.setAttribute("aria-expanded", String(open));
|
|
119
|
+
item.setAttribute("data-state", open ? "open" : "closed");
|
|
120
|
+
}
|
|
121
|
+
/** Expand every section on the path to the current page. */
|
|
122
|
+
revealCurrent() {
|
|
123
|
+
const cur = this.querySelector("[aria-current='page']");
|
|
124
|
+
if (!cur) return;
|
|
125
|
+
for (let item = cur.closest("[data-part='item']"); item && this.contains(item); item = item.parentElement?.closest("[data-part='item']") ?? null) {
|
|
126
|
+
if (this.groupOf(item) && !this.groupOf(item).contains(cur)) continue;
|
|
127
|
+
if (this.groupOf(item)) this.setOpen(item, true);
|
|
128
|
+
}
|
|
129
|
+
const scroller = scrollParent(this);
|
|
130
|
+
if (!scroller) return;
|
|
131
|
+
const r = cur.getBoundingClientRect();
|
|
132
|
+
const s = scroller.getBoundingClientRect();
|
|
133
|
+
if (r.top < s.top || r.bottom > s.bottom) scroller.scrollTop += r.top - s.top - s.height / 2 + r.height / 2;
|
|
134
|
+
}
|
|
135
|
+
data() {
|
|
136
|
+
if (this.navData) return this.navData;
|
|
137
|
+
const name = this.getAttribute("data-source");
|
|
138
|
+
const g = name ? globalThis[name] : void 0;
|
|
139
|
+
return Array.isArray(g) ? g : null;
|
|
140
|
+
}
|
|
141
|
+
/** Fill a lazy group from nav data. */
|
|
142
|
+
fill(group) {
|
|
143
|
+
const data = this.data();
|
|
144
|
+
const path = group.dataset.path;
|
|
145
|
+
if (!data || path == null) return;
|
|
146
|
+
let list = data;
|
|
147
|
+
let node;
|
|
148
|
+
for (const i of path.split(".").map(Number)) {
|
|
149
|
+
node = list?.[i];
|
|
150
|
+
list = node?.[2];
|
|
151
|
+
}
|
|
152
|
+
if (!node?.[2]) return;
|
|
153
|
+
group.replaceChildren(...this.build(node[2], path));
|
|
154
|
+
group.removeAttribute("data-lazy");
|
|
155
|
+
}
|
|
156
|
+
build(items, path) {
|
|
157
|
+
const base = this.getAttribute("base") ?? "";
|
|
158
|
+
const tmpl = this.getAttribute("toggle-label") ?? "Toggle {label}";
|
|
159
|
+
const current = location.href.split("#")[0];
|
|
160
|
+
return items.map(([label, url, kids, noCurrent], i) => {
|
|
161
|
+
const here = `${path}.${i}`;
|
|
162
|
+
const li = document.createElement("li");
|
|
163
|
+
li.setAttribute("data-part", "item");
|
|
164
|
+
li.style.setProperty("--db-level", String(here.split(".").length - 1));
|
|
165
|
+
const row = document.createElement("div");
|
|
166
|
+
row.setAttribute("data-part", "row");
|
|
167
|
+
li.appendChild(row);
|
|
168
|
+
const probe = url?.replace(/[\u0000-\u0020]/g, "") ?? "";
|
|
169
|
+
if (url != null && /^[a-z][a-z0-9+.-]*:/i.test(probe) && !/^(?:https?|mailto):/i.test(probe)) url = null;
|
|
170
|
+
const external = url != null && /^[a-z][a-z0-9+.-]*:|^\/\//i.test(probe);
|
|
171
|
+
let toggle = null;
|
|
172
|
+
if (kids?.length) {
|
|
173
|
+
toggle = document.createElement("button");
|
|
174
|
+
toggle.type = "button";
|
|
175
|
+
toggle.setAttribute("data-part", "toggle");
|
|
176
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
177
|
+
if (url != null) toggle.setAttribute("aria-label", tmpl.replace("{label}", () => label));
|
|
178
|
+
else {
|
|
179
|
+
const span = document.createElement("span");
|
|
180
|
+
span.setAttribute("data-part", "label");
|
|
181
|
+
span.textContent = label;
|
|
182
|
+
toggle.appendChild(span);
|
|
183
|
+
}
|
|
184
|
+
toggle.insertAdjacentHTML("beforeend", CHEVRON);
|
|
185
|
+
}
|
|
186
|
+
const atEnd = this.getAttribute("toggle-position") === "end";
|
|
187
|
+
if (toggle && !atEnd) row.appendChild(toggle);
|
|
188
|
+
if (url != null) {
|
|
189
|
+
const a = document.createElement("a");
|
|
190
|
+
a.setAttribute("data-part", "link");
|
|
191
|
+
a.textContent = label;
|
|
192
|
+
a.href = external ? url : base + url;
|
|
193
|
+
if (external) a.rel = "noopener";
|
|
194
|
+
if (!noCurrent && a.href.split("#")[0] === current) a.setAttribute("aria-current", "page");
|
|
195
|
+
row.appendChild(a);
|
|
196
|
+
}
|
|
197
|
+
if (url == null && !kids?.length) {
|
|
198
|
+
const span = document.createElement("span");
|
|
199
|
+
span.setAttribute("data-part", "label");
|
|
200
|
+
span.textContent = label;
|
|
201
|
+
row.appendChild(span);
|
|
202
|
+
}
|
|
203
|
+
if (toggle && atEnd) row.appendChild(toggle);
|
|
204
|
+
if (kids?.length) {
|
|
205
|
+
const ul = document.createElement("ul");
|
|
206
|
+
ul.setAttribute("data-part", "group");
|
|
207
|
+
ul.dataset.lazy = "";
|
|
208
|
+
ul.dataset.path = here;
|
|
209
|
+
ul.hidden = true;
|
|
210
|
+
li.setAttribute("data-state", "closed");
|
|
211
|
+
li.appendChild(ul);
|
|
212
|
+
this.prepareItem(li);
|
|
213
|
+
}
|
|
214
|
+
return li;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var CHEVRON = '<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>';
|
|
219
|
+
function scrollParent(el) {
|
|
220
|
+
for (let p = el.parentElement; p; p = p.parentElement) {
|
|
221
|
+
const o = getComputedStyle(p).overflowY;
|
|
222
|
+
if ((o === "auto" || o === "scroll") && p.scrollHeight > p.clientHeight) return p;
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
ensureDefaultDisplay();
|
|
227
|
+
if (!customElements.get("db-nav-tree")) {
|
|
228
|
+
customElements.define("db-nav-tree", DbNavTree);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
DbNavTree
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=chunk-S62ZVVO3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAnimations,
|
|
3
|
+
ownOne,
|
|
4
|
+
resolveAsChild,
|
|
5
|
+
uid
|
|
6
|
+
} from "./chunk-3AEQ6KHV.js";
|
|
7
|
+
import {
|
|
8
|
+
DogsbayElement,
|
|
9
|
+
ensureDefaultDisplay
|
|
10
|
+
} from "./chunk-K6DPDBAG.js";
|
|
11
|
+
|
|
12
|
+
// src/db-dialog.ts
|
|
13
|
+
import { FocusTrap } from "@dogsbay/primitives/focus-trap";
|
|
14
|
+
import { ScrollLock } from "@dogsbay/primitives/scroll-lock";
|
|
15
|
+
import { DismissController } from "@dogsbay/primitives/dismiss";
|
|
16
|
+
import { markOthersInert } from "@dogsbay/primitives/inert";
|
|
17
|
+
var DbDialog = class extends DogsbayElement {
|
|
18
|
+
content = null;
|
|
19
|
+
backdrop = null;
|
|
20
|
+
trigger = null;
|
|
21
|
+
native = false;
|
|
22
|
+
_open = false;
|
|
23
|
+
openedBy = null;
|
|
24
|
+
focusTrap = null;
|
|
25
|
+
dismiss = null;
|
|
26
|
+
cleanupInert = null;
|
|
27
|
+
get open() {
|
|
28
|
+
return this._open;
|
|
29
|
+
}
|
|
30
|
+
get dismissible() {
|
|
31
|
+
return this.getAttribute("dismissible") !== "false";
|
|
32
|
+
}
|
|
33
|
+
onTriggerClick = (e) => {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
this.toggle(e.currentTarget);
|
|
36
|
+
};
|
|
37
|
+
onHostClick = (e) => {
|
|
38
|
+
const close = e.target.closest("[data-part='close']");
|
|
39
|
+
if (close && close.closest("db-dialog") === this) this.close("close-press");
|
|
40
|
+
};
|
|
41
|
+
onDocumentClick = (e) => {
|
|
42
|
+
const ext = e.target.closest?.("[data-dialog-for]");
|
|
43
|
+
if (!ext || ext === this.trigger) return;
|
|
44
|
+
const target = ext.getAttribute("data-dialog-for");
|
|
45
|
+
if (target && (target === this.id || target === this.content?.id)) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
this.toggle(ext);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// Native <dialog>: Escape fires "cancel".
|
|
51
|
+
onCancel = (e) => {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
if (this.dismissible) this.close("escape-key");
|
|
54
|
+
};
|
|
55
|
+
// Native <dialog>: a click on the ::backdrop targets the dialog itself.
|
|
56
|
+
onNativeClick = (e) => {
|
|
57
|
+
if (!this.dismissible || e.target !== this.content || !this.content) return;
|
|
58
|
+
const r = this.content.getBoundingClientRect();
|
|
59
|
+
const inside = e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom;
|
|
60
|
+
if (!inside && (e.clientX !== 0 || e.clientY !== 0)) this.close("outside-press");
|
|
61
|
+
};
|
|
62
|
+
// Native <dialog> closed by the platform (form method="dialog").
|
|
63
|
+
onNativeClose = () => {
|
|
64
|
+
if (this._open) this.finishClose("close-press");
|
|
65
|
+
};
|
|
66
|
+
setup() {
|
|
67
|
+
this.content = ownOne(this, "[data-part='content']");
|
|
68
|
+
if (!this.content) return;
|
|
69
|
+
this.native = typeof HTMLDialogElement !== "undefined" && this.content instanceof HTMLDialogElement && typeof this.content.showModal === "function";
|
|
70
|
+
this.backdrop = ownOne(this, "[data-part='backdrop']");
|
|
71
|
+
this.trigger = resolveAsChild(ownOne(this, "[data-part='trigger']"));
|
|
72
|
+
const content = this.content;
|
|
73
|
+
if (!content.id) content.id = uid("db-dialog");
|
|
74
|
+
if (!this.native) {
|
|
75
|
+
content.setAttribute("role", content.getAttribute("role") || "dialog");
|
|
76
|
+
content.setAttribute("aria-modal", "true");
|
|
77
|
+
content.hidden = !this.hasAttribute("open");
|
|
78
|
+
if (this.backdrop) this.backdrop.hidden = true;
|
|
79
|
+
}
|
|
80
|
+
const title = content.querySelector("[data-part='title']");
|
|
81
|
+
if (title && !content.hasAttribute("aria-labelledby") && !content.hasAttribute("aria-label")) {
|
|
82
|
+
title.id ||= uid("db-dialog-title");
|
|
83
|
+
content.setAttribute("aria-labelledby", title.id);
|
|
84
|
+
}
|
|
85
|
+
const desc = content.querySelector("[data-part='description']");
|
|
86
|
+
if (desc && !content.hasAttribute("aria-describedby")) {
|
|
87
|
+
desc.id ||= uid("db-dialog-desc");
|
|
88
|
+
content.setAttribute("aria-describedby", desc.id);
|
|
89
|
+
}
|
|
90
|
+
for (const t of this.triggers()) this.prepareTrigger(t);
|
|
91
|
+
this.setState("closed");
|
|
92
|
+
if (!this.native) {
|
|
93
|
+
this.focusTrap = new FocusTrap(content);
|
|
94
|
+
this.dismiss = new DismissController(content, {
|
|
95
|
+
onDismiss: (reason) => this.dismissible && this.close(reason)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
connect() {
|
|
100
|
+
if (!this.content) return;
|
|
101
|
+
this.trigger?.addEventListener("click", this.onTriggerClick);
|
|
102
|
+
this.addEventListener("click", this.onHostClick);
|
|
103
|
+
document.addEventListener("click", this.onDocumentClick);
|
|
104
|
+
if (this.native) {
|
|
105
|
+
this.content.addEventListener("cancel", this.onCancel);
|
|
106
|
+
this.content.addEventListener("click", this.onNativeClick);
|
|
107
|
+
this.content.addEventListener("close", this.onNativeClose);
|
|
108
|
+
}
|
|
109
|
+
queueMicrotask(() => {
|
|
110
|
+
if (!this.isConnected) return;
|
|
111
|
+
for (const t of this.triggers()) if (!t.hasAttribute("aria-controls")) this.prepareTrigger(t);
|
|
112
|
+
if (this.hasAttribute("open") && !this._open) this.show();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
disconnect() {
|
|
116
|
+
this.trigger?.removeEventListener("click", this.onTriggerClick);
|
|
117
|
+
this.removeEventListener("click", this.onHostClick);
|
|
118
|
+
document.removeEventListener("click", this.onDocumentClick);
|
|
119
|
+
if (this.content && this.native) {
|
|
120
|
+
this.content.removeEventListener("cancel", this.onCancel);
|
|
121
|
+
this.content.removeEventListener("click", this.onNativeClick);
|
|
122
|
+
this.content.removeEventListener("close", this.onNativeClose);
|
|
123
|
+
}
|
|
124
|
+
if (this._open) this.finishClose("api", false);
|
|
125
|
+
}
|
|
126
|
+
/** Internal trigger plus every `[data-dialog-for]` pointing here. */
|
|
127
|
+
triggers() {
|
|
128
|
+
const out = this.trigger ? [this.trigger] : [];
|
|
129
|
+
const ids = [this.id, this.content?.id].filter(Boolean);
|
|
130
|
+
for (const id of ids) document.querySelectorAll(`[data-dialog-for="${CSS.escape(id)}"]`).forEach((el) => out.push(el));
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
prepareTrigger(t) {
|
|
134
|
+
if (!t.hasAttribute("aria-haspopup")) t.setAttribute("aria-haspopup", "dialog");
|
|
135
|
+
t.setAttribute("aria-controls", this.content.id);
|
|
136
|
+
t.setAttribute("aria-expanded", "false");
|
|
137
|
+
}
|
|
138
|
+
setState(state) {
|
|
139
|
+
this.setAttribute("data-state", state);
|
|
140
|
+
this.content?.setAttribute("data-state", state);
|
|
141
|
+
this.backdrop?.setAttribute("data-state", state);
|
|
142
|
+
}
|
|
143
|
+
toggle(opener) {
|
|
144
|
+
if (this._open) this.close("trigger-press");
|
|
145
|
+
else this.show(opener);
|
|
146
|
+
}
|
|
147
|
+
show(opener) {
|
|
148
|
+
const content = this.content;
|
|
149
|
+
if (!content || this._open) return;
|
|
150
|
+
this._open = true;
|
|
151
|
+
this.openedBy = opener ?? document.activeElement ?? this.trigger;
|
|
152
|
+
for (const t of this.triggers()) {
|
|
153
|
+
this.prepareTrigger(t);
|
|
154
|
+
t.setAttribute("aria-expanded", "true");
|
|
155
|
+
}
|
|
156
|
+
ScrollLock.activate();
|
|
157
|
+
if (this.native) {
|
|
158
|
+
const d = content;
|
|
159
|
+
if (!d.open) d.showModal();
|
|
160
|
+
} else {
|
|
161
|
+
content.hidden = false;
|
|
162
|
+
if (this.backdrop) this.backdrop.hidden = false;
|
|
163
|
+
this.cleanupInert = markOthersInert(content);
|
|
164
|
+
this.focusTrap?.activate();
|
|
165
|
+
this.dismiss?.activate();
|
|
166
|
+
}
|
|
167
|
+
this.setState("open");
|
|
168
|
+
this.dispatchEvent(new CustomEvent("db-dialog:open", { bubbles: true }));
|
|
169
|
+
}
|
|
170
|
+
close(reason = "api") {
|
|
171
|
+
if (!this._open) return;
|
|
172
|
+
this.finishClose(reason);
|
|
173
|
+
}
|
|
174
|
+
finishClose(reason, restoreFocus = true) {
|
|
175
|
+
const content = this.content;
|
|
176
|
+
this._open = false;
|
|
177
|
+
this.setState("closed");
|
|
178
|
+
for (const t of this.triggers()) t.setAttribute("aria-expanded", "false");
|
|
179
|
+
ScrollLock.deactivate();
|
|
180
|
+
if (this.native) {
|
|
181
|
+
const d = content;
|
|
182
|
+
if (d.open) d.close();
|
|
183
|
+
} else {
|
|
184
|
+
this.dismiss?.deactivate();
|
|
185
|
+
this.focusTrap?.deactivate();
|
|
186
|
+
this.cleanupInert?.();
|
|
187
|
+
this.cleanupInert = null;
|
|
188
|
+
afterAnimations(content, () => {
|
|
189
|
+
if (this._open) return;
|
|
190
|
+
content.hidden = true;
|
|
191
|
+
if (this.backdrop) this.backdrop.hidden = true;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const back = this.openedBy;
|
|
195
|
+
this.openedBy = null;
|
|
196
|
+
if (restoreFocus && back && document.contains(back)) back.focus();
|
|
197
|
+
this.dispatchEvent(new CustomEvent("db-dialog:close", { detail: { reason }, bubbles: true }));
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
ensureDefaultDisplay();
|
|
201
|
+
if (!customElements.get("db-dialog")) {
|
|
202
|
+
customElements.define("db-dialog", DbDialog);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
DbDialog
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=chunk-SLJG53M7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
DogsbayElement
|
|
3
|
-
|
|
2
|
+
DogsbayElement,
|
|
3
|
+
ensureDefaultDisplay
|
|
4
|
+
} from "./chunk-K6DPDBAG.js";
|
|
4
5
|
|
|
5
6
|
// src/db-code-block.ts
|
|
6
7
|
var DbCodeBlock = class extends DogsbayElement {
|
|
@@ -79,6 +80,7 @@ var DbCodeBlock = class extends DogsbayElement {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
};
|
|
83
|
+
ensureDefaultDisplay();
|
|
82
84
|
if (!customElements.get("db-code-block")) {
|
|
83
85
|
customElements.define("db-code-block", DbCodeBlock);
|
|
84
86
|
}
|
|
@@ -86,4 +88,4 @@ if (!customElements.get("db-code-block")) {
|
|
|
86
88
|
export {
|
|
87
89
|
DbCodeBlock
|
|
88
90
|
};
|
|
89
|
-
//# sourceMappingURL=chunk-
|
|
91
|
+
//# sourceMappingURL=chunk-V4P3UR4I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
DogsbayElement
|
|
3
|
-
|
|
2
|
+
DogsbayElement,
|
|
3
|
+
ensureDefaultDisplay
|
|
4
|
+
} from "./chunk-K6DPDBAG.js";
|
|
4
5
|
|
|
5
6
|
// src/db-steps.ts
|
|
6
7
|
var DbSteps = class extends DogsbayElement {
|
|
7
8
|
setup() {
|
|
8
9
|
this.setAttribute("role", "list");
|
|
9
|
-
const items = this.querySelectorAll(
|
|
10
|
-
":scope > [data-part='item']"
|
|
11
|
-
);
|
|
10
|
+
const items = Array.from(this.querySelectorAll("[data-part='item']")).filter((el) => el.closest("db-steps") === this);
|
|
12
11
|
for (const item of items) {
|
|
13
12
|
item.setAttribute("role", "listitem");
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
15
|
};
|
|
16
|
+
ensureDefaultDisplay();
|
|
17
17
|
if (!customElements.get("db-steps")) {
|
|
18
18
|
customElements.define("db-steps", DbSteps);
|
|
19
19
|
}
|
|
@@ -21,4 +21,4 @@ if (!customElements.get("db-steps")) {
|
|
|
21
21
|
export {
|
|
22
22
|
DbSteps
|
|
23
23
|
};
|
|
24
|
-
//# sourceMappingURL=chunk-
|
|
24
|
+
//# sourceMappingURL=chunk-VW7HZY2B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db-steps.ts"],"sourcesContent":["import { DogsbayElement, ensureDefaultDisplay } from \"./base.js\";\n\n/**\n * `<db-steps>` — Accessible step-by-step list.\n *\n * A read-only component that renders numbered steps with ARIA semantics.\n * Visual styling (numbered circles, connector lines) is handled by CSS\n * using counter(step), matching the Astro Steps component.\n *\n * Children:\n * [data-part=\"item\"] — step items\n * [data-part=\"title\"] — step title (inside item, optional)\n * [data-part=\"content\"] — step content (inside item, optional)\n */\nexport class DbSteps extends DogsbayElement {\n protected setup() {\n this.setAttribute(\"role\", \"list\");\n\n // Same scoping rule as db-tabs: allow a wrapper between the element\n // and its items, but stop at a nested <db-steps>. `:scope >` was\n // used here and silently drops every item the moment a layout\n // wrapper appears — the failure mode that hid the vertical tabs bug.\n const items = (\n Array.from(this.querySelectorAll(\"[data-part='item']\")) as HTMLElement[]\n ).filter((el) => el.closest(\"db-steps\") === this);\n\n for (const item of items) {\n item.setAttribute(\"role\", \"listitem\");\n }\n }\n}\n\nensureDefaultDisplay();\n\nif (!customElements.get(\"db-steps\")) {\n customElements.define(\"db-steps\", DbSteps);\n}\n"],"mappings":";;;;;;AAcO,IAAM,UAAN,cAAsB,eAAe;AAAA,EAChC,QAAQ;AAChB,SAAK,aAAa,QAAQ,MAAM;AAMhC,UAAM,QACJ,MAAM,KAAK,KAAK,iBAAiB,oBAAoB,CAAC,EACtD,OAAO,CAAC,OAAO,GAAG,QAAQ,UAAU,MAAM,IAAI;AAEhD,eAAW,QAAQ,OAAO;AACxB,WAAK,aAAa,QAAQ,UAAU;AAAA,IACtC;AAAA,EACF;AACF;AAEA,qBAAqB;AAErB,IAAI,CAAC,eAAe,IAAI,UAAU,GAAG;AACnC,iBAAe,OAAO,YAAY,OAAO;AAC3C;","names":[]}
|
package/dist/db-accordion.d.ts
CHANGED
package/dist/db-accordion.js
CHANGED
package/dist/db-card.d.ts
CHANGED
package/dist/db-card.js
CHANGED
package/dist/db-code-block.d.ts
CHANGED