@countermeasure-platform/web-components 1.3.3-dev.33.1 → 1.3.4-dev.35.1
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/README.md +31 -0
- package/dist/component-D5sRm1fq.js +389 -0
- package/dist/component-D5sRm1fq.js.map +1 -0
- package/dist/components/index.js +27 -27
- package/dist/icons/index.d.ts +12 -0
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/index.js +12 -0
- package/dist/icons/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +127 -126
- package/dist/layout/app-shell.d.ts +50 -3
- package/dist/layout/app-shell.d.ts.map +1 -1
- package/dist/layout/app-shell.js +142 -13
- package/dist/layout/app-shell.js.map +1 -1
- package/dist/layout/core-app-chrome.d.ts +81 -0
- package/dist/layout/core-app-chrome.d.ts.map +1 -0
- package/dist/layout/core-app-chrome.js +349 -0
- package/dist/layout/core-app-chrome.js.map +1 -0
- package/dist/layout/index.d.ts +4 -2
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +88 -87
- package/dist/layout/index.js.map +1 -1
- package/dist/react/primitives/badge.d.ts +1 -1
- package/dist/react/primitives/button.d.ts +2 -2
- package/dist/react/primitives/copy-button.d.ts +1 -1
- package/dist/react/primitives/stat-card.d.ts +1 -1
- package/dist/react/primitives/toggle.d.ts +1 -1
- package/dist/react/sidebar.d.ts +4 -4
- package/dist/react/sidebar.d.ts.map +1 -1
- package/dist/react/sidebar.js +18 -16
- package/dist/react/sidebar.js.map +1 -1
- package/dist/sidebar/component.d.ts +24 -2
- package/dist/sidebar/component.d.ts.map +1 -1
- package/dist/sidebar/enhance.d.ts +8 -0
- package/dist/sidebar/enhance.d.ts.map +1 -1
- package/dist/sidebar/index.d.ts +3 -0
- package/dist/sidebar/index.d.ts.map +1 -1
- package/dist/sidebar/index.js +81 -28
- package/dist/sidebar/index.js.map +1 -1
- package/dist/sidebar/types.d.ts +126 -4
- package/dist/sidebar/types.d.ts.map +1 -1
- package/dist/styles/layout.css +252 -0
- package/dist/styles/sidebar.css +313 -5
- package/package.json +6 -1
- package/src/icons/icons.test.ts +9 -0
- package/src/icons/index.ts +12 -0
- package/src/index.ts +11 -0
- package/src/layout/app-shell.test.ts +204 -0
- package/src/layout/app-shell.ts +362 -3
- package/src/layout/core-app-chrome.test.ts +507 -0
- package/src/layout/core-app-chrome.ts +662 -0
- package/src/layout/index.ts +36 -2
- package/src/react/sidebar.test.tsx +104 -3
- package/src/react/sidebar.tsx +26 -4
- package/src/sidebar/component.test.ts +395 -1
- package/src/sidebar/component.ts +661 -86
- package/src/sidebar/enhance.ts +118 -0
- package/src/sidebar/index.ts +144 -0
- package/src/sidebar/types.ts +143 -4
- package/src/styles/layout.css +252 -0
- package/src/styles/sidebar.css +313 -5
- package/dist/component-Bxhxf21c.js +0 -167
- package/dist/component-Bxhxf21c.js.map +0 -1
package/dist/layout/index.js
CHANGED
|
@@ -1,66 +1,67 @@
|
|
|
1
1
|
import { n as e } from "../sanitize-uWpY18N9.js";
|
|
2
2
|
import { t } from "../navigation-DihI8dfX.js";
|
|
3
|
-
import { AppShell as n,
|
|
4
|
-
import {
|
|
3
|
+
import { AppShell as n, TopMenuBar as r, createAppShell as i, createProductShell as a, createTopMenuBar as o } from "./app-shell.js";
|
|
4
|
+
import { CORE_APP_ROUTE_LABELS as s, createCoreAppChromePreset as c, createCoreAppSidebarConfig as l, createCoreAppSidebarSections as u, createCoreAppTopbarActions as d, createCoreAppTopbarConfig as f, getCoreAppRouteLabel as p, mountCoreAppChrome as m, mountCoreAppChromeFromDom as h } from "./core-app-chrome.js";
|
|
5
|
+
import { PageHeader as g, createPageHeader as _ } from "./page-header.js";
|
|
5
6
|
//#region src/layout/index.ts
|
|
6
|
-
var
|
|
7
|
+
var v = () => {
|
|
7
8
|
if (typeof window > "u") return null;
|
|
8
9
|
try {
|
|
9
10
|
return window.localStorage;
|
|
10
11
|
} catch {
|
|
11
12
|
return console.warn("Unable to access localStorage"), null;
|
|
12
13
|
}
|
|
13
|
-
},
|
|
14
|
+
}, y = (e) => {
|
|
14
15
|
try {
|
|
15
16
|
return new URL(e, window.location.href).origin === window.location.origin;
|
|
16
17
|
} catch {
|
|
17
18
|
return !1;
|
|
18
19
|
}
|
|
19
|
-
},
|
|
20
|
+
}, b = {
|
|
20
21
|
storageKey: "cmm-sidebar-state",
|
|
21
22
|
desktopBreakpoint: "(min-width: 1024px)",
|
|
22
23
|
collapsedClass: "cmm-shell--sidebar-collapsed",
|
|
23
24
|
openClass: "cmm-shell--sidebar-open"
|
|
24
25
|
};
|
|
25
|
-
function
|
|
26
|
+
function x(e = {}) {
|
|
26
27
|
let t = {
|
|
27
|
-
...
|
|
28
|
+
...b,
|
|
28
29
|
...e
|
|
29
30
|
}, n = document.querySelector("[data-shell-layout]"), r = document.querySelector("[data-shell-sidebar]");
|
|
30
31
|
if (!n || !r) return () => {};
|
|
31
|
-
let i =
|
|
32
|
-
r.id =
|
|
33
|
-
let
|
|
32
|
+
let i = v(), a = window.matchMedia(t.desktopBreakpoint), o = Array.from(document.querySelectorAll("[data-sidebar-toggle]")), s = Array.from(document.querySelectorAll("[data-sidebar-dismiss]")), c = r.id || "shell-sidebar";
|
|
33
|
+
r.id = c;
|
|
34
|
+
let l = () => i?.getItem(t.storageKey) === "collapsed", u = () => {
|
|
34
35
|
let e = a.matches ? !n.classList.contains(t.collapsedClass) : n.classList.contains(t.openClass);
|
|
35
|
-
|
|
36
|
-
t.setAttribute("aria-expanded", e.toString()), t.setAttribute("aria-controls",
|
|
36
|
+
o.forEach((t) => {
|
|
37
|
+
t.setAttribute("aria-expanded", e.toString()), t.setAttribute("aria-controls", c);
|
|
37
38
|
});
|
|
38
|
-
},
|
|
39
|
-
n.classList.toggle(t.collapsedClass, e), r && a.matches && i?.setItem(t.storageKey, e ? "collapsed" : "expanded"),
|
|
40
|
-
},
|
|
41
|
-
n.classList.toggle(t.openClass, e),
|
|
39
|
+
}, d = (e, r = !0) => {
|
|
40
|
+
n.classList.toggle(t.collapsedClass, e), r && a.matches && i?.setItem(t.storageKey, e ? "collapsed" : "expanded"), u();
|
|
41
|
+
}, f = (e) => {
|
|
42
|
+
n.classList.toggle(t.openClass, e), u();
|
|
43
|
+
}, p = () => {
|
|
44
|
+
a.matches ? (f(!1), d(l(), !1)) : (f(!1), d(!1, !1));
|
|
45
|
+
}, m = () => {
|
|
46
|
+
a.matches ? d(!n.classList.contains(t.collapsedClass)) : f(!n.classList.contains(t.openClass));
|
|
42
47
|
}, h = () => {
|
|
43
|
-
|
|
44
|
-
}, g = () => {
|
|
45
|
-
|
|
46
|
-
}, _ = () => {
|
|
47
|
-
m(!1);
|
|
48
|
-
}, v = (e) => {
|
|
49
|
-
e.key === "Escape" && !a.matches && m(!1);
|
|
48
|
+
f(!1);
|
|
49
|
+
}, g = (e) => {
|
|
50
|
+
e.key === "Escape" && !a.matches && f(!1);
|
|
50
51
|
};
|
|
51
|
-
return
|
|
52
|
-
e.addEventListener("click",
|
|
53
|
-
}),
|
|
54
|
-
e.addEventListener("click",
|
|
55
|
-
}), document.addEventListener("keydown",
|
|
56
|
-
|
|
57
|
-
e.removeEventListener("click",
|
|
58
|
-
}),
|
|
59
|
-
e.removeEventListener("click",
|
|
60
|
-
}), document.removeEventListener("keydown",
|
|
52
|
+
return o.forEach((e) => {
|
|
53
|
+
e.addEventListener("click", m);
|
|
54
|
+
}), s.forEach((e) => {
|
|
55
|
+
e.addEventListener("click", h);
|
|
56
|
+
}), document.addEventListener("keydown", g), a.addEventListener("change", p), p(), () => {
|
|
57
|
+
o.forEach((e) => {
|
|
58
|
+
e.removeEventListener("click", m);
|
|
59
|
+
}), s.forEach((e) => {
|
|
60
|
+
e.removeEventListener("click", h);
|
|
61
|
+
}), document.removeEventListener("keydown", g), a.removeEventListener("change", p);
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
|
-
function
|
|
64
|
+
function S(e = {}) {
|
|
64
65
|
let t = e.triggerSelector ?? "[data-user-trigger]", n = e.dropdownSelector ?? "[data-user-dropdown]", r = document.querySelector("[data-user-menu]"), i = document.querySelector(t), a = document.querySelector(n);
|
|
65
66
|
if (!r || !i || !a) return () => {};
|
|
66
67
|
let o = (e) => {
|
|
@@ -83,17 +84,17 @@ function u(e = {}) {
|
|
|
83
84
|
i.removeEventListener("click", c), document.removeEventListener("click", l), document.removeEventListener("keydown", u), a.removeEventListener("keydown", f), i.removeEventListener("keydown", p);
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
|
-
var
|
|
87
|
-
e.classList.toggle("hidden", !t), e.classList.toggle(
|
|
88
|
-
},
|
|
87
|
+
var C = (e) => e.dataset.collapsibleOpen === "true" || !e.classList.contains("hidden"), w = "cmm-collapsible--hidden", T = (e, t) => {
|
|
88
|
+
e.classList.toggle("hidden", !t), e.classList.toggle(w, !t), e.dataset.collapsibleOpen = t.toString(), e.setAttribute("aria-hidden", t ? "false" : "true");
|
|
89
|
+
}, E = (e, t) => {
|
|
89
90
|
document.querySelectorAll(`[data-toggle-target='${e}']`).forEach((e) => {
|
|
90
91
|
e.setAttribute("aria-expanded", t.toString());
|
|
91
92
|
});
|
|
92
93
|
};
|
|
93
|
-
function
|
|
94
|
+
function D(e = {}) {
|
|
94
95
|
let { scrollIntoView: t = !0, scrollBehavior: n = "smooth", hiddenClass: r = "cmm-collapsible--hidden" } = e;
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
w = r, document.querySelectorAll("[data-collapsible]").forEach((e) => {
|
|
97
|
+
T(e, e.dataset.collapsibleOpen === "true" || !e.classList.contains("hidden"));
|
|
97
98
|
});
|
|
98
99
|
let i = [];
|
|
99
100
|
document.querySelectorAll("[data-toggle-target]").forEach((e) => {
|
|
@@ -102,11 +103,11 @@ function h(e = {}) {
|
|
|
102
103
|
let a = document.querySelector(r);
|
|
103
104
|
if (!a) return;
|
|
104
105
|
let o = a.id || r.replace(/^#/, "");
|
|
105
|
-
e.setAttribute("aria-controls", o), e.setAttribute("aria-expanded",
|
|
106
|
+
e.setAttribute("aria-controls", o), e.setAttribute("aria-expanded", C(a).toString());
|
|
106
107
|
let s = (e) => {
|
|
107
108
|
e.preventDefault();
|
|
108
|
-
let i = !
|
|
109
|
-
|
|
109
|
+
let i = !C(a);
|
|
110
|
+
T(a, i), E(r, i), i && t && a.scrollIntoView({
|
|
110
111
|
behavior: n,
|
|
111
112
|
block: "start"
|
|
112
113
|
});
|
|
@@ -121,7 +122,7 @@ function h(e = {}) {
|
|
|
121
122
|
let n = document.querySelector(t);
|
|
122
123
|
if (!n) return;
|
|
123
124
|
let r = (e) => {
|
|
124
|
-
e.preventDefault(),
|
|
125
|
+
e.preventDefault(), T(n, !1), E(t, !1);
|
|
125
126
|
};
|
|
126
127
|
e.addEventListener("click", r), i.push({
|
|
127
128
|
element: e,
|
|
@@ -131,7 +132,7 @@ function h(e = {}) {
|
|
|
131
132
|
let a = window.location.hash;
|
|
132
133
|
if (a) {
|
|
133
134
|
let e = document.querySelector(a);
|
|
134
|
-
e?.dataset.collapsible !== void 0 && (
|
|
135
|
+
e?.dataset.collapsible !== void 0 && (T(e, !0), E(a, !0));
|
|
135
136
|
}
|
|
136
137
|
return () => {
|
|
137
138
|
i.forEach(({ element: e, handler: t }) => {
|
|
@@ -139,17 +140,17 @@ function h(e = {}) {
|
|
|
139
140
|
});
|
|
140
141
|
};
|
|
141
142
|
}
|
|
142
|
-
var
|
|
143
|
-
function
|
|
143
|
+
var O = "\n <div class=\"cmm-sheet__loading\">\n <div class=\"cmm-spinner\"></div>\n </div>\n", k = "\n <div class=\"cmm-sheet__error\">\n <p>Failed to load content. Please try the full page.</p>\n </div>\n";
|
|
144
|
+
function A(t, n) {
|
|
144
145
|
t.replaceChildren(e(n, { allowSvg: !0 }));
|
|
145
146
|
}
|
|
146
|
-
function
|
|
147
|
-
let { openClass: n = "cmm-sheet--open", hiddenClass: r = "cmm-sheet--hidden", backdropVisibleClass: i = "cmm-sheet__backdrop--visible", backdropHiddenClass: a = "cmm-sheet__backdrop--hidden", spinnerHtml: o =
|
|
147
|
+
function j(e = {}) {
|
|
148
|
+
let { openClass: n = "cmm-sheet--open", hiddenClass: r = "cmm-sheet--hidden", backdropVisibleClass: i = "cmm-sheet__backdrop--visible", backdropHiddenClass: a = "cmm-sheet__backdrop--hidden", spinnerHtml: o = O, errorHtml: s = k, allowCrossOriginFetch: c = !1 } = e, l = /* @__PURE__ */ new Map();
|
|
148
149
|
if (document.querySelectorAll("[data-sheet]").forEach((e) => {
|
|
149
150
|
let t = e.dataset.sheet;
|
|
150
151
|
if (!t) return;
|
|
151
152
|
let n = document.querySelector(`[data-sheet-backdrop='${t}']`), r = e.querySelector("[data-sheet-content]");
|
|
152
|
-
!n || !r ||
|
|
153
|
+
!n || !r || l.set(t, {
|
|
153
154
|
key: t,
|
|
154
155
|
sheet: e,
|
|
155
156
|
backdrop: n,
|
|
@@ -158,46 +159,46 @@ function y(e = {}) {
|
|
|
158
159
|
expandLink: e.querySelector("[data-sheet-expand]"),
|
|
159
160
|
focusSelector: e.dataset.sheetFocus
|
|
160
161
|
});
|
|
161
|
-
}),
|
|
162
|
-
let
|
|
162
|
+
}), l.size === 0) return () => {};
|
|
163
|
+
let u = null, d = (e, t) => {
|
|
163
164
|
e.sheet.classList.toggle(n, t), e.sheet.classList.toggle(r, !t), e.backdrop.classList.toggle(i, t), e.backdrop.classList.toggle(a, !t), e.sheet.setAttribute("aria-hidden", t ? "false" : "true");
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
let r = n?.dataset.sheetUrl ?? e.sheet.dataset.sheetUrl, i = n?.dataset.sheetFocus ?? e.focusSelector, a = n?.dataset.sheetFullpage ?? e.sheet.dataset.sheetFullpage,
|
|
169
|
-
if (e.fullPageLink && a && (e.fullPageLink.href = a), e.expandLink &&
|
|
170
|
-
|
|
165
|
+
}, f = (e) => {
|
|
166
|
+
u?.key === e.key && (u = null), d(e, !1);
|
|
167
|
+
}, p = async (e, n) => {
|
|
168
|
+
u && u.key !== e.key && f(u), u = e, d(e, !0);
|
|
169
|
+
let r = n?.dataset.sheetUrl ?? e.sheet.dataset.sheetUrl, i = n?.dataset.sheetFocus ?? e.focusSelector, a = n?.dataset.sheetFullpage ?? e.sheet.dataset.sheetFullpage, l = n?.dataset.sheetExpand ?? e.sheet.dataset.sheetExpand;
|
|
170
|
+
if (e.fullPageLink && a && (e.fullPageLink.href = a), e.expandLink && l && (e.expandLink.href = l), !r || !c && !y(r)) {
|
|
171
|
+
A(e.content, s);
|
|
171
172
|
return;
|
|
172
173
|
}
|
|
173
|
-
|
|
174
|
+
A(e.content, o);
|
|
174
175
|
try {
|
|
175
176
|
let n = await fetch(r);
|
|
176
177
|
if (n.ok) {
|
|
177
178
|
let t = await n.text();
|
|
178
|
-
|
|
179
|
-
} else a ? t(a) :
|
|
179
|
+
A(e.content, t), i && e.content.querySelector(i)?.focus();
|
|
180
|
+
} else a ? t(a) : A(e.content, s);
|
|
180
181
|
} catch {
|
|
181
|
-
a ? t(a) :
|
|
182
|
+
a ? t(a) : A(e.content, s);
|
|
182
183
|
}
|
|
183
|
-
},
|
|
184
|
-
|
|
184
|
+
}, m = [];
|
|
185
|
+
l.forEach((e) => {
|
|
185
186
|
let t = (t) => {
|
|
186
|
-
t.target.closest("[data-sheet-close]") && (t.preventDefault(),
|
|
187
|
+
t.target.closest("[data-sheet-close]") && (t.preventDefault(), f(e));
|
|
187
188
|
}, n = () => {
|
|
188
|
-
|
|
189
|
+
f(e);
|
|
189
190
|
}, r = () => {
|
|
190
|
-
|
|
191
|
+
f(e);
|
|
191
192
|
};
|
|
192
|
-
e.sheet.addEventListener("click", t), e.backdrop.addEventListener("click", n),
|
|
193
|
+
e.sheet.addEventListener("click", t), e.backdrop.addEventListener("click", n), m.push({
|
|
193
194
|
element: e.sheet,
|
|
194
195
|
event: "click",
|
|
195
196
|
handler: t
|
|
196
|
-
}),
|
|
197
|
+
}), m.push({
|
|
197
198
|
element: e.backdrop,
|
|
198
199
|
event: "click",
|
|
199
200
|
handler: n
|
|
200
|
-
}), e.expandLink && (e.expandLink.addEventListener("click", r),
|
|
201
|
+
}), e.expandLink && (e.expandLink.addEventListener("click", r), m.push({
|
|
201
202
|
element: e.expandLink,
|
|
202
203
|
event: "click",
|
|
203
204
|
handler: r
|
|
@@ -205,27 +206,27 @@ function y(e = {}) {
|
|
|
205
206
|
}), document.querySelectorAll("[data-sheet-trigger]").forEach((e) => {
|
|
206
207
|
let t = e.dataset.sheetTrigger;
|
|
207
208
|
if (!t) return;
|
|
208
|
-
let n =
|
|
209
|
+
let n = l.get(t);
|
|
209
210
|
if (!n) return;
|
|
210
211
|
let r = (t) => {
|
|
211
|
-
t.preventDefault(),
|
|
212
|
+
t.preventDefault(), p(n, e);
|
|
212
213
|
};
|
|
213
|
-
e.addEventListener("click", r),
|
|
214
|
+
e.addEventListener("click", r), m.push({
|
|
214
215
|
element: e,
|
|
215
216
|
event: "click",
|
|
216
217
|
handler: r
|
|
217
218
|
});
|
|
218
219
|
});
|
|
219
|
-
let
|
|
220
|
-
e.key === "Escape" &&
|
|
220
|
+
let h = (e) => {
|
|
221
|
+
e.key === "Escape" && u && f(u);
|
|
221
222
|
};
|
|
222
|
-
return document.addEventListener("keydown",
|
|
223
|
-
|
|
223
|
+
return document.addEventListener("keydown", h), () => {
|
|
224
|
+
m.forEach(({ element: e, event: t, handler: n }) => {
|
|
224
225
|
e.removeEventListener(t, n);
|
|
225
|
-
}), document.removeEventListener("keydown",
|
|
226
|
+
}), document.removeEventListener("keydown", h);
|
|
226
227
|
};
|
|
227
228
|
}
|
|
228
|
-
function
|
|
229
|
+
function M() {
|
|
229
230
|
let e = document.querySelector("[data-nav-list]");
|
|
230
231
|
if (!e) return () => {};
|
|
231
232
|
let t = () => Array.from(e.querySelectorAll("[data-nav-item]")), n = (e) => {
|
|
@@ -236,13 +237,13 @@ function b() {
|
|
|
236
237
|
e.removeEventListener("keydown", n);
|
|
237
238
|
};
|
|
238
239
|
}
|
|
239
|
-
function
|
|
240
|
+
function N(e = {}) {
|
|
240
241
|
let t = [
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
x(e.sidebar),
|
|
243
|
+
S(e.userMenu),
|
|
244
|
+
D(e.collapsibles),
|
|
245
|
+
j(e.sheets),
|
|
246
|
+
M()
|
|
246
247
|
];
|
|
247
248
|
return () => {
|
|
248
249
|
t.forEach((e) => {
|
|
@@ -251,6 +252,6 @@ function x(e = {}) {
|
|
|
251
252
|
};
|
|
252
253
|
}
|
|
253
254
|
//#endregion
|
|
254
|
-
export { n as AppShell,
|
|
255
|
+
export { n as AppShell, s as CORE_APP_ROUTE_LABELS, g as PageHeader, r as TopMenuBar, i as createAppShell, c as createCoreAppChromePreset, l as createCoreAppSidebarConfig, u as createCoreAppSidebarSections, d as createCoreAppTopbarActions, f as createCoreAppTopbarConfig, _ as createPageHeader, a as createProductShell, o as createTopMenuBar, p as getCoreAppRouteLabel, N as initLayout, m as mountCoreAppChrome, h as mountCoreAppChromeFromDom, D as setupCollapsibles, M as setupNavKeyboard, j as setupSheets, x as setupSidebar, S as setupUserMenu };
|
|
255
256
|
|
|
256
257
|
//# sourceMappingURL=index.js.map
|
package/dist/layout/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/layout/index.ts"],"sourcesContent":["/**\n * Layout Components\n * @countermeasure/web-components\n *\n * Shared layout utilities for sidebar, user menu, collapsibles, and sheets.\n */\n\nimport { createSafeMarkupFragment } from '../utils/sanitize'\nimport { navigateDocumentTo } from '../utils/navigation'\nexport { AppShell, createAppShell } from './app-shell'\nexport type { AppShellConfig } from './app-shell'\nexport { PageHeader, createPageHeader } from './page-header'\nexport type { PageHeaderConfig, PageHeaderMetadata } from './page-header'\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface SidebarConfig {\n storageKey?: string\n desktopBreakpoint?: string\n collapsedClass?: string\n openClass?: string\n}\n\nexport interface UserMenuConfig {\n triggerSelector?: string\n dropdownSelector?: string\n dropdownItemSelector?: string\n}\n\nexport interface CollapsibleConfig {\n scrollIntoView?: boolean\n scrollBehavior?: ScrollBehavior\n hiddenClass?: string\n}\n\nexport interface SheetConfig {\n key: string\n sheet: HTMLElement\n backdrop: HTMLElement\n content: HTMLElement\n fullPageLink?: HTMLAnchorElement | null | undefined\n expandLink?: HTMLAnchorElement | null | undefined\n focusSelector?: string | undefined\n}\n\nexport interface SheetSetupConfig {\n openClass?: string\n hiddenClass?: string\n backdropVisibleClass?: string\n backdropHiddenClass?: string\n spinnerHtml?: string\n errorHtml?: string\n /** Allow fetching sheet content from non-same-origin URLs. Default: false. */\n allowCrossOriginFetch?: boolean\n}\n\n// ============================================================================\n// STORAGE HELPERS\n// ============================================================================\n\nconst resolveStorage = (): Storage | null => {\n if (typeof window === 'undefined') {\n return null\n }\n try {\n return window.localStorage\n } catch {\n console.warn('Unable to access localStorage')\n return null\n }\n}\n\nconst isSameOriginUrl = (url: string): boolean => {\n try {\n return new URL(url, window.location.href).origin === window.location.origin\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// SIDEBAR\n// ============================================================================\n\nconst DEFAULT_SIDEBAR_CONFIG: Required<SidebarConfig> = {\n storageKey: 'cmm-sidebar-state',\n desktopBreakpoint: '(min-width: 1024px)',\n collapsedClass: 'cmm-shell--sidebar-collapsed',\n openClass: 'cmm-shell--sidebar-open',\n}\n\n/**\n * Setup responsive sidebar with collapse/expand functionality.\n */\nexport function setupSidebar(config: SidebarConfig = {}): () => void {\n const options = { ...DEFAULT_SIDEBAR_CONFIG, ...config }\n const layout = document.querySelector<HTMLElement>('[data-shell-layout]')\n const sidebar = document.querySelector<HTMLElement>('[data-shell-sidebar]')\n\n if (!layout || !sidebar) {\n return () => {\n /* noop */\n }\n }\n\n const storage = resolveStorage()\n const mediaQuery = window.matchMedia(options.desktopBreakpoint)\n const toggles = Array.from(document.querySelectorAll<HTMLElement>('[data-sidebar-toggle]'))\n const dismissors = Array.from(document.querySelectorAll<HTMLElement>('[data-sidebar-dismiss]'))\n\n const sidebarId = sidebar.id || 'shell-sidebar'\n sidebar.id = sidebarId\n\n const readStoredCollapsed = () => storage?.getItem(options.storageKey) === 'collapsed'\n\n const syncToggleAria = () => {\n const expanded = mediaQuery.matches\n ? !layout.classList.contains(options.collapsedClass)\n : layout.classList.contains(options.openClass)\n\n toggles.forEach(toggle => {\n toggle.setAttribute('aria-expanded', expanded.toString())\n toggle.setAttribute('aria-controls', sidebarId)\n })\n }\n\n const applyCollapsed = (collapsed: boolean, persist = true) => {\n layout.classList.toggle(options.collapsedClass, collapsed)\n if (persist && mediaQuery.matches) {\n storage?.setItem(options.storageKey, collapsed ? 'collapsed' : 'expanded')\n }\n syncToggleAria()\n }\n\n const setOpen = (open: boolean) => {\n layout.classList.toggle(options.openClass, open)\n syncToggleAria()\n }\n\n const syncForViewport = () => {\n if (mediaQuery.matches) {\n setOpen(false)\n applyCollapsed(readStoredCollapsed(), false)\n } else {\n setOpen(false)\n applyCollapsed(false, false)\n }\n }\n\n const handleToggleClick = () => {\n if (mediaQuery.matches) {\n const collapsed = layout.classList.contains(options.collapsedClass)\n applyCollapsed(!collapsed)\n } else {\n const open = layout.classList.contains(options.openClass)\n setOpen(!open)\n }\n }\n\n const handleDismissClick = () => {\n setOpen(false)\n }\n\n const handleKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !mediaQuery.matches) {\n setOpen(false)\n }\n }\n\n // Attach listeners\n toggles.forEach(toggle => {\n toggle.addEventListener('click', handleToggleClick)\n })\n dismissors.forEach(dismiss => {\n dismiss.addEventListener('click', handleDismissClick)\n })\n document.addEventListener('keydown', handleKeydown)\n mediaQuery.addEventListener('change', syncForViewport)\n\n // Initialize\n syncForViewport()\n\n // Return cleanup function\n return () => {\n toggles.forEach(toggle => {\n toggle.removeEventListener('click', handleToggleClick)\n })\n dismissors.forEach(dismiss => {\n dismiss.removeEventListener('click', handleDismissClick)\n })\n document.removeEventListener('keydown', handleKeydown)\n mediaQuery.removeEventListener('change', syncForViewport)\n }\n}\n\n// ============================================================================\n// USER MENU\n// ============================================================================\n\n/**\n * Setup user menu dropdown with keyboard navigation.\n */\nexport function setupUserMenu(config: UserMenuConfig = {}): () => void {\n const triggerSelector = config.triggerSelector ?? '[data-user-trigger]'\n const dropdownSelector = config.dropdownSelector ?? '[data-user-dropdown]'\n\n const userMenu = document.querySelector<HTMLElement>('[data-user-menu]')\n const trigger = document.querySelector<HTMLButtonElement>(triggerSelector)\n const dropdown = document.querySelector<HTMLElement>(dropdownSelector)\n\n if (!userMenu || !trigger || !dropdown) {\n return () => {\n /* noop */\n }\n }\n\n const setExpanded = (expanded: boolean) => {\n trigger.setAttribute('aria-expanded', expanded.toString())\n userMenu.setAttribute('aria-expanded', expanded.toString())\n }\n\n const isExpanded = () => trigger.getAttribute('aria-expanded') === 'true'\n\n const handleTriggerClick = (event: Event) => {\n event.stopPropagation()\n setExpanded(!isExpanded())\n }\n\n const handleDocumentClick = (event: Event) => {\n if (!userMenu.contains(event.target as Node)) {\n setExpanded(false)\n }\n }\n\n const handleKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isExpanded()) {\n setExpanded(false)\n trigger.focus()\n }\n }\n\n const dropdownItemSelector = config.dropdownItemSelector ?? '.cmm-user-menu__dropdown-item'\n\n const handleDropdownKeydown = (event: KeyboardEvent) => {\n const items = Array.from(dropdown.querySelectorAll<HTMLAnchorElement>(dropdownItemSelector))\n const currentIndex = items.indexOf(document.activeElement as HTMLAnchorElement)\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0\n items[nextIndex]?.focus()\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1\n items[prevIndex]?.focus()\n } else if (event.key === 'Tab' && !event.shiftKey && currentIndex === items.length - 1) {\n setExpanded(false)\n } else if (event.key === 'Tab' && event.shiftKey && currentIndex === 0) {\n setExpanded(false)\n }\n }\n\n const handleTriggerKeydown = (event: KeyboardEvent) => {\n if (\n (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') &&\n !isExpanded()\n ) {\n event.preventDefault()\n setExpanded(true)\n setTimeout(() => {\n const firstItem = dropdown.querySelector<HTMLAnchorElement>(dropdownItemSelector)\n firstItem?.focus()\n }, 50)\n }\n }\n\n // Attach listeners\n trigger.addEventListener('click', handleTriggerClick)\n document.addEventListener('click', handleDocumentClick)\n document.addEventListener('keydown', handleKeydown)\n dropdown.addEventListener('keydown', handleDropdownKeydown)\n trigger.addEventListener('keydown', handleTriggerKeydown)\n\n // Return cleanup function\n return () => {\n trigger.removeEventListener('click', handleTriggerClick)\n document.removeEventListener('click', handleDocumentClick)\n document.removeEventListener('keydown', handleKeydown)\n dropdown.removeEventListener('keydown', handleDropdownKeydown)\n trigger.removeEventListener('keydown', handleTriggerKeydown)\n }\n}\n\n// ============================================================================\n// COLLAPSIBLES\n// ============================================================================\n\nconst isCollapsibleExpanded = (section: HTMLElement) =>\n section.dataset.collapsibleOpen === 'true' || !section.classList.contains('hidden')\n\nlet collapsibleHiddenClass = 'cmm-collapsible--hidden'\n\nconst updateCollapsibleState = (section: HTMLElement, expanded: boolean) => {\n section.classList.toggle('hidden', !expanded)\n section.classList.toggle(collapsibleHiddenClass, !expanded)\n section.dataset.collapsibleOpen = expanded.toString()\n section.setAttribute('aria-hidden', expanded ? 'false' : 'true')\n}\n\nconst syncCollapsibleToggles = (selector: string, expanded: boolean) => {\n document.querySelectorAll<HTMLElement>(`[data-toggle-target='${selector}']`).forEach(toggle => {\n toggle.setAttribute('aria-expanded', expanded.toString())\n })\n}\n\n/**\n * Setup collapsible sections with toggle buttons.\n */\nexport function setupCollapsibles(config: CollapsibleConfig = {}): () => void {\n const {\n scrollIntoView = true,\n scrollBehavior = 'smooth',\n hiddenClass = 'cmm-collapsible--hidden',\n } = config\n collapsibleHiddenClass = hiddenClass\n\n // Initialize all collapsible sections\n document.querySelectorAll<HTMLElement>('[data-collapsible]').forEach(section => {\n const expanded =\n section.dataset.collapsibleOpen === 'true' || !section.classList.contains('hidden')\n updateCollapsibleState(section, expanded)\n })\n\n const handlers: { element: HTMLElement; handler: (e: Event) => void }[] = []\n\n // Setup toggle triggers\n document.querySelectorAll<HTMLElement>('[data-toggle-target]').forEach(toggle => {\n const selector = toggle.dataset.toggleTarget\n if (!selector) {\n return\n }\n\n const target = document.querySelector<HTMLElement>(selector)\n if (!target) {\n return\n }\n\n const ariaTarget = target.id || selector.replace(/^#/, '')\n toggle.setAttribute('aria-controls', ariaTarget)\n toggle.setAttribute('aria-expanded', isCollapsibleExpanded(target).toString())\n\n const handler = (event: Event) => {\n event.preventDefault()\n const expanded = !isCollapsibleExpanded(target)\n updateCollapsibleState(target, expanded)\n syncCollapsibleToggles(selector, expanded)\n\n if (expanded && scrollIntoView) {\n target.scrollIntoView({ behavior: scrollBehavior, block: 'start' })\n }\n }\n\n toggle.addEventListener('click', handler)\n handlers.push({ element: toggle, handler })\n })\n\n // Setup collapse triggers (close only)\n document.querySelectorAll<HTMLElement>('[data-collapse-target]').forEach(toggle => {\n const selector = toggle.dataset.collapseTarget\n if (!selector) {\n return\n }\n\n const target = document.querySelector<HTMLElement>(selector)\n if (!target) {\n return\n }\n\n const handler = (event: Event) => {\n event.preventDefault()\n updateCollapsibleState(target, false)\n syncCollapsibleToggles(selector, false)\n }\n\n toggle.addEventListener('click', handler)\n handlers.push({ element: toggle, handler })\n })\n\n // Handle URL hash for deep linking\n const hash = window.location.hash\n if (hash) {\n const target = document.querySelector<HTMLElement>(hash)\n if (target?.dataset.collapsible !== undefined) {\n updateCollapsibleState(target, true)\n syncCollapsibleToggles(hash, true)\n }\n }\n\n // Return cleanup function\n return () => {\n handlers.forEach(({ element, handler }) => {\n element.removeEventListener('click', handler)\n })\n }\n}\n\n// ============================================================================\n// SHEETS / SIDE PANELS\n// ============================================================================\n\nconst SHEET_SPINNER_HTML = `\n <div class=\"cmm-sheet__loading\">\n <div class=\"cmm-spinner\"></div>\n </div>\n`\n\nconst SHEET_ERROR_HTML = `\n <div class=\"cmm-sheet__error\">\n <p>Failed to load content. Please try the full page.</p>\n </div>\n`\n\nfunction replaceSheetMarkup(element: HTMLElement, markup: string): void {\n element.replaceChildren(createSafeMarkupFragment(markup, { allowSvg: true }))\n}\n\n/**\n * Setup side sheet/drawer components.\n */\nexport function setupSheets(setupConfig: SheetSetupConfig = {}): () => void {\n const {\n openClass = 'cmm-sheet--open',\n hiddenClass = 'cmm-sheet--hidden',\n backdropVisibleClass = 'cmm-sheet__backdrop--visible',\n backdropHiddenClass = 'cmm-sheet__backdrop--hidden',\n spinnerHtml: spinnerMarkup = SHEET_SPINNER_HTML,\n errorHtml: errorMarkup = SHEET_ERROR_HTML,\n allowCrossOriginFetch = false,\n } = setupConfig\n\n const sheetMap = new Map<string, SheetConfig>()\n\n document.querySelectorAll<HTMLElement>('[data-sheet]').forEach(sheet => {\n const key = sheet.dataset.sheet\n if (!key) {\n return\n }\n\n const backdrop = document.querySelector<HTMLElement>(`[data-sheet-backdrop='${key}']`)\n const content = sheet.querySelector<HTMLElement>('[data-sheet-content]')\n if (!backdrop || !content) {\n return\n }\n\n sheetMap.set(key, {\n key,\n sheet,\n backdrop,\n content,\n fullPageLink: sheet.querySelector<HTMLAnchorElement>('[data-sheet-fullpage]'),\n expandLink: sheet.querySelector<HTMLAnchorElement>('[data-sheet-expand]'),\n focusSelector: sheet.dataset.sheetFocus,\n })\n })\n\n if (sheetMap.size === 0) {\n return () => {\n /* noop */\n }\n }\n\n let activeSheet: SheetConfig | null = null\n\n const setOpenState = (config: SheetConfig, open: boolean) => {\n config.sheet.classList.toggle(openClass, open)\n config.sheet.classList.toggle(hiddenClass, !open)\n config.backdrop.classList.toggle(backdropVisibleClass, open)\n config.backdrop.classList.toggle(backdropHiddenClass, !open)\n config.sheet.setAttribute('aria-hidden', open ? 'false' : 'true')\n }\n\n const closeSheet = (config: SheetConfig) => {\n if (activeSheet?.key === config.key) {\n activeSheet = null\n }\n setOpenState(config, false)\n }\n\n const openSheet = async (config: SheetConfig, trigger?: HTMLElement) => {\n if (activeSheet && activeSheet.key !== config.key) {\n closeSheet(activeSheet)\n }\n\n activeSheet = config\n setOpenState(config, true)\n\n const url = trigger?.dataset.sheetUrl ?? config.sheet.dataset.sheetUrl\n const focusSelector = trigger?.dataset.sheetFocus ?? config.focusSelector\n const fullPageHref = trigger?.dataset.sheetFullpage ?? config.sheet.dataset.sheetFullpage\n const expandHref = trigger?.dataset.sheetExpand ?? config.sheet.dataset.sheetExpand\n\n if (config.fullPageLink && fullPageHref) {\n config.fullPageLink.href = fullPageHref\n }\n\n if (config.expandLink && expandHref) {\n config.expandLink.href = expandHref\n }\n\n if (!url || (!allowCrossOriginFetch && !isSameOriginUrl(url))) {\n replaceSheetMarkup(config.content, errorMarkup)\n return\n }\n\n replaceSheetMarkup(config.content, spinnerMarkup)\n\n try {\n const response = await fetch(url)\n if (response.ok) {\n const markup = await response.text()\n replaceSheetMarkup(config.content, markup)\n if (focusSelector) {\n const focusTarget = config.content.querySelector<HTMLElement>(focusSelector)\n focusTarget?.focus()\n }\n } else if (fullPageHref) {\n navigateDocumentTo(fullPageHref)\n } else {\n replaceSheetMarkup(config.content, errorMarkup)\n }\n } catch {\n if (fullPageHref) {\n navigateDocumentTo(fullPageHref)\n } else {\n replaceSheetMarkup(config.content, errorMarkup)\n }\n }\n }\n\n const handlers: { element: HTMLElement; event: string; handler: (e: Event) => void }[] = []\n\n sheetMap.forEach(config => {\n const closeHandler = (event: Event) => {\n const target = event.target as HTMLElement\n if (target.closest('[data-sheet-close]')) {\n event.preventDefault()\n closeSheet(config)\n }\n }\n\n const backdropHandler = () => {\n closeSheet(config)\n }\n\n const expandHandler = () => {\n closeSheet(config)\n }\n\n config.sheet.addEventListener('click', closeHandler)\n config.backdrop.addEventListener('click', backdropHandler)\n handlers.push({ element: config.sheet, event: 'click', handler: closeHandler })\n handlers.push({ element: config.backdrop, event: 'click', handler: backdropHandler })\n\n if (config.expandLink) {\n config.expandLink.addEventListener('click', expandHandler)\n handlers.push({ element: config.expandLink, event: 'click', handler: expandHandler })\n }\n })\n\n // Setup triggers\n document.querySelectorAll<HTMLElement>('[data-sheet-trigger]').forEach(trigger => {\n const key = trigger.dataset.sheetTrigger\n if (!key) {\n return\n }\n\n const config = sheetMap.get(key)\n if (!config) {\n return\n }\n\n const handler = (event: Event) => {\n event.preventDefault()\n void openSheet(config, trigger)\n }\n\n trigger.addEventListener('click', handler)\n handlers.push({ element: trigger, event: 'click', handler })\n })\n\n // Escape key handler\n const escapeHandler = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && activeSheet) {\n closeSheet(activeSheet)\n }\n }\n\n document.addEventListener('keydown', escapeHandler)\n\n // Return cleanup function\n return () => {\n handlers.forEach(({ element, event, handler }) => {\n element.removeEventListener(event, handler)\n })\n document.removeEventListener('keydown', escapeHandler)\n }\n}\n\n// ============================================================================\n// NAVIGATION KEYBOARD SUPPORT\n// ============================================================================\n\n/**\n * Setup keyboard navigation for sidebar nav items.\n */\nexport function setupNavKeyboard(): () => void {\n const navList = document.querySelector<HTMLElement>('[data-nav-list]')\n if (!navList) {\n return () => {\n /* noop */\n }\n }\n\n const getNavItems = () =>\n Array.from(navList.querySelectorAll<HTMLAnchorElement>('[data-nav-item]'))\n\n const handler = (event: KeyboardEvent) => {\n const items = getNavItems()\n const currentIndex = items.indexOf(document.activeElement as HTMLAnchorElement)\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0\n items[nextIndex]?.focus()\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1\n items[prevIndex]?.focus()\n } else if (event.key === 'Home') {\n event.preventDefault()\n items[0]?.focus()\n } else if (event.key === 'End') {\n event.preventDefault()\n items[items.length - 1]?.focus()\n }\n }\n\n navList.addEventListener('keydown', handler)\n\n return () => {\n navList.removeEventListener('keydown', handler)\n }\n}\n\n// ============================================================================\n// BOOTSTRAP\n// ============================================================================\n\n/**\n * Initialize all layout components.\n */\nexport function initLayout(\n config: {\n sidebar?: SidebarConfig\n userMenu?: UserMenuConfig\n collapsibles?: CollapsibleConfig\n sheets?: SheetSetupConfig\n } = {}\n): () => void {\n const cleanups = [\n setupSidebar(config.sidebar),\n setupUserMenu(config.userMenu),\n setupCollapsibles(config.collapsibles),\n setupSheets(config.sheets),\n setupNavKeyboard(),\n ]\n\n return () => {\n cleanups.forEach(cleanup => {\n cleanup()\n })\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/layout/index.ts"],"sourcesContent":["/**\n * Layout Components\n * @countermeasure/web-components\n *\n * Shared layout utilities for sidebar, user menu, collapsibles, and sheets.\n */\n\nimport { createSafeMarkupFragment } from '../utils/sanitize'\nimport { navigateDocumentTo } from '../utils/navigation'\nexport {\n AppShell,\n TopMenuBar,\n createAppShell,\n createProductShell,\n createTopMenuBar,\n} from './app-shell'\nexport type {\n AppShellConfig,\n TopMenuBarAction,\n TopMenuBarActionInput,\n TopMenuBarBreadcrumb,\n TopMenuBarConfig,\n TopMenuBarUser,\n} from './app-shell'\nexport {\n CORE_APP_ROUTE_LABELS,\n createCoreAppChromePreset,\n createCoreAppSidebarConfig,\n createCoreAppSidebarSections,\n createCoreAppTopbarActions,\n createCoreAppTopbarConfig,\n getCoreAppRouteLabel,\n mountCoreAppChrome,\n mountCoreAppChromeFromDom,\n} from './core-app-chrome'\nexport type {\n CoreAppChromePreset,\n CoreAppChromeDomMountOptions,\n CoreAppChromeMount,\n CoreAppChromeMountOptions,\n CoreAppChromePresetOptions,\n CoreAppRouteLabel,\n CoreAppSidebarPresetOptions,\n CoreAppTopbarPresetOptions,\n} from './core-app-chrome'\nexport { PageHeader, createPageHeader } from './page-header'\nexport type { PageHeaderConfig, PageHeaderMetadata } from './page-header'\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface SidebarConfig {\n storageKey?: string\n desktopBreakpoint?: string\n collapsedClass?: string\n openClass?: string\n}\n\nexport interface UserMenuConfig {\n triggerSelector?: string\n dropdownSelector?: string\n dropdownItemSelector?: string\n}\n\nexport interface CollapsibleConfig {\n scrollIntoView?: boolean\n scrollBehavior?: ScrollBehavior\n hiddenClass?: string\n}\n\nexport interface SheetConfig {\n key: string\n sheet: HTMLElement\n backdrop: HTMLElement\n content: HTMLElement\n fullPageLink?: HTMLAnchorElement | null | undefined\n expandLink?: HTMLAnchorElement | null | undefined\n focusSelector?: string | undefined\n}\n\nexport interface SheetSetupConfig {\n openClass?: string\n hiddenClass?: string\n backdropVisibleClass?: string\n backdropHiddenClass?: string\n spinnerHtml?: string\n errorHtml?: string\n /** Allow fetching sheet content from non-same-origin URLs. Default: false. */\n allowCrossOriginFetch?: boolean\n}\n\n// ============================================================================\n// STORAGE HELPERS\n// ============================================================================\n\nconst resolveStorage = (): Storage | null => {\n if (typeof window === 'undefined') {\n return null\n }\n try {\n return window.localStorage\n } catch {\n console.warn('Unable to access localStorage')\n return null\n }\n}\n\nconst isSameOriginUrl = (url: string): boolean => {\n try {\n return new URL(url, window.location.href).origin === window.location.origin\n } catch {\n return false\n }\n}\n\n// ============================================================================\n// SIDEBAR\n// ============================================================================\n\nconst DEFAULT_SIDEBAR_CONFIG: Required<SidebarConfig> = {\n storageKey: 'cmm-sidebar-state',\n desktopBreakpoint: '(min-width: 1024px)',\n collapsedClass: 'cmm-shell--sidebar-collapsed',\n openClass: 'cmm-shell--sidebar-open',\n}\n\n/**\n * Setup responsive sidebar with collapse/expand functionality.\n */\nexport function setupSidebar(config: SidebarConfig = {}): () => void {\n const options = { ...DEFAULT_SIDEBAR_CONFIG, ...config }\n const layout = document.querySelector<HTMLElement>('[data-shell-layout]')\n const sidebar = document.querySelector<HTMLElement>('[data-shell-sidebar]')\n\n if (!layout || !sidebar) {\n return () => {\n /* noop */\n }\n }\n\n const storage = resolveStorage()\n const mediaQuery = window.matchMedia(options.desktopBreakpoint)\n const toggles = Array.from(document.querySelectorAll<HTMLElement>('[data-sidebar-toggle]'))\n const dismissors = Array.from(document.querySelectorAll<HTMLElement>('[data-sidebar-dismiss]'))\n\n const sidebarId = sidebar.id || 'shell-sidebar'\n sidebar.id = sidebarId\n\n const readStoredCollapsed = () => storage?.getItem(options.storageKey) === 'collapsed'\n\n const syncToggleAria = () => {\n const expanded = mediaQuery.matches\n ? !layout.classList.contains(options.collapsedClass)\n : layout.classList.contains(options.openClass)\n\n toggles.forEach(toggle => {\n toggle.setAttribute('aria-expanded', expanded.toString())\n toggle.setAttribute('aria-controls', sidebarId)\n })\n }\n\n const applyCollapsed = (collapsed: boolean, persist = true) => {\n layout.classList.toggle(options.collapsedClass, collapsed)\n if (persist && mediaQuery.matches) {\n storage?.setItem(options.storageKey, collapsed ? 'collapsed' : 'expanded')\n }\n syncToggleAria()\n }\n\n const setOpen = (open: boolean) => {\n layout.classList.toggle(options.openClass, open)\n syncToggleAria()\n }\n\n const syncForViewport = () => {\n if (mediaQuery.matches) {\n setOpen(false)\n applyCollapsed(readStoredCollapsed(), false)\n } else {\n setOpen(false)\n applyCollapsed(false, false)\n }\n }\n\n const handleToggleClick = () => {\n if (mediaQuery.matches) {\n const collapsed = layout.classList.contains(options.collapsedClass)\n applyCollapsed(!collapsed)\n } else {\n const open = layout.classList.contains(options.openClass)\n setOpen(!open)\n }\n }\n\n const handleDismissClick = () => {\n setOpen(false)\n }\n\n const handleKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !mediaQuery.matches) {\n setOpen(false)\n }\n }\n\n // Attach listeners\n toggles.forEach(toggle => {\n toggle.addEventListener('click', handleToggleClick)\n })\n dismissors.forEach(dismiss => {\n dismiss.addEventListener('click', handleDismissClick)\n })\n document.addEventListener('keydown', handleKeydown)\n mediaQuery.addEventListener('change', syncForViewport)\n\n // Initialize\n syncForViewport()\n\n // Return cleanup function\n return () => {\n toggles.forEach(toggle => {\n toggle.removeEventListener('click', handleToggleClick)\n })\n dismissors.forEach(dismiss => {\n dismiss.removeEventListener('click', handleDismissClick)\n })\n document.removeEventListener('keydown', handleKeydown)\n mediaQuery.removeEventListener('change', syncForViewport)\n }\n}\n\n// ============================================================================\n// USER MENU\n// ============================================================================\n\n/**\n * Setup user menu dropdown with keyboard navigation.\n */\nexport function setupUserMenu(config: UserMenuConfig = {}): () => void {\n const triggerSelector = config.triggerSelector ?? '[data-user-trigger]'\n const dropdownSelector = config.dropdownSelector ?? '[data-user-dropdown]'\n\n const userMenu = document.querySelector<HTMLElement>('[data-user-menu]')\n const trigger = document.querySelector<HTMLButtonElement>(triggerSelector)\n const dropdown = document.querySelector<HTMLElement>(dropdownSelector)\n\n if (!userMenu || !trigger || !dropdown) {\n return () => {\n /* noop */\n }\n }\n\n const setExpanded = (expanded: boolean) => {\n trigger.setAttribute('aria-expanded', expanded.toString())\n userMenu.setAttribute('aria-expanded', expanded.toString())\n }\n\n const isExpanded = () => trigger.getAttribute('aria-expanded') === 'true'\n\n const handleTriggerClick = (event: Event) => {\n event.stopPropagation()\n setExpanded(!isExpanded())\n }\n\n const handleDocumentClick = (event: Event) => {\n if (!userMenu.contains(event.target as Node)) {\n setExpanded(false)\n }\n }\n\n const handleKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isExpanded()) {\n setExpanded(false)\n trigger.focus()\n }\n }\n\n const dropdownItemSelector = config.dropdownItemSelector ?? '.cmm-user-menu__dropdown-item'\n\n const handleDropdownKeydown = (event: KeyboardEvent) => {\n const items = Array.from(dropdown.querySelectorAll<HTMLAnchorElement>(dropdownItemSelector))\n const currentIndex = items.indexOf(document.activeElement as HTMLAnchorElement)\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0\n items[nextIndex]?.focus()\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1\n items[prevIndex]?.focus()\n } else if (event.key === 'Tab' && !event.shiftKey && currentIndex === items.length - 1) {\n setExpanded(false)\n } else if (event.key === 'Tab' && event.shiftKey && currentIndex === 0) {\n setExpanded(false)\n }\n }\n\n const handleTriggerKeydown = (event: KeyboardEvent) => {\n if (\n (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') &&\n !isExpanded()\n ) {\n event.preventDefault()\n setExpanded(true)\n setTimeout(() => {\n const firstItem = dropdown.querySelector<HTMLAnchorElement>(dropdownItemSelector)\n firstItem?.focus()\n }, 50)\n }\n }\n\n // Attach listeners\n trigger.addEventListener('click', handleTriggerClick)\n document.addEventListener('click', handleDocumentClick)\n document.addEventListener('keydown', handleKeydown)\n dropdown.addEventListener('keydown', handleDropdownKeydown)\n trigger.addEventListener('keydown', handleTriggerKeydown)\n\n // Return cleanup function\n return () => {\n trigger.removeEventListener('click', handleTriggerClick)\n document.removeEventListener('click', handleDocumentClick)\n document.removeEventListener('keydown', handleKeydown)\n dropdown.removeEventListener('keydown', handleDropdownKeydown)\n trigger.removeEventListener('keydown', handleTriggerKeydown)\n }\n}\n\n// ============================================================================\n// COLLAPSIBLES\n// ============================================================================\n\nconst isCollapsibleExpanded = (section: HTMLElement) =>\n section.dataset.collapsibleOpen === 'true' || !section.classList.contains('hidden')\n\nlet collapsibleHiddenClass = 'cmm-collapsible--hidden'\n\nconst updateCollapsibleState = (section: HTMLElement, expanded: boolean) => {\n section.classList.toggle('hidden', !expanded)\n section.classList.toggle(collapsibleHiddenClass, !expanded)\n section.dataset.collapsibleOpen = expanded.toString()\n section.setAttribute('aria-hidden', expanded ? 'false' : 'true')\n}\n\nconst syncCollapsibleToggles = (selector: string, expanded: boolean) => {\n document.querySelectorAll<HTMLElement>(`[data-toggle-target='${selector}']`).forEach(toggle => {\n toggle.setAttribute('aria-expanded', expanded.toString())\n })\n}\n\n/**\n * Setup collapsible sections with toggle buttons.\n */\nexport function setupCollapsibles(config: CollapsibleConfig = {}): () => void {\n const {\n scrollIntoView = true,\n scrollBehavior = 'smooth',\n hiddenClass = 'cmm-collapsible--hidden',\n } = config\n collapsibleHiddenClass = hiddenClass\n\n // Initialize all collapsible sections\n document.querySelectorAll<HTMLElement>('[data-collapsible]').forEach(section => {\n const expanded =\n section.dataset.collapsibleOpen === 'true' || !section.classList.contains('hidden')\n updateCollapsibleState(section, expanded)\n })\n\n const handlers: { element: HTMLElement; handler: (e: Event) => void }[] = []\n\n // Setup toggle triggers\n document.querySelectorAll<HTMLElement>('[data-toggle-target]').forEach(toggle => {\n const selector = toggle.dataset.toggleTarget\n if (!selector) {\n return\n }\n\n const target = document.querySelector<HTMLElement>(selector)\n if (!target) {\n return\n }\n\n const ariaTarget = target.id || selector.replace(/^#/, '')\n toggle.setAttribute('aria-controls', ariaTarget)\n toggle.setAttribute('aria-expanded', isCollapsibleExpanded(target).toString())\n\n const handler = (event: Event) => {\n event.preventDefault()\n const expanded = !isCollapsibleExpanded(target)\n updateCollapsibleState(target, expanded)\n syncCollapsibleToggles(selector, expanded)\n\n if (expanded && scrollIntoView) {\n target.scrollIntoView({ behavior: scrollBehavior, block: 'start' })\n }\n }\n\n toggle.addEventListener('click', handler)\n handlers.push({ element: toggle, handler })\n })\n\n // Setup collapse triggers (close only)\n document.querySelectorAll<HTMLElement>('[data-collapse-target]').forEach(toggle => {\n const selector = toggle.dataset.collapseTarget\n if (!selector) {\n return\n }\n\n const target = document.querySelector<HTMLElement>(selector)\n if (!target) {\n return\n }\n\n const handler = (event: Event) => {\n event.preventDefault()\n updateCollapsibleState(target, false)\n syncCollapsibleToggles(selector, false)\n }\n\n toggle.addEventListener('click', handler)\n handlers.push({ element: toggle, handler })\n })\n\n // Handle URL hash for deep linking\n const hash = window.location.hash\n if (hash) {\n const target = document.querySelector<HTMLElement>(hash)\n if (target?.dataset.collapsible !== undefined) {\n updateCollapsibleState(target, true)\n syncCollapsibleToggles(hash, true)\n }\n }\n\n // Return cleanup function\n return () => {\n handlers.forEach(({ element, handler }) => {\n element.removeEventListener('click', handler)\n })\n }\n}\n\n// ============================================================================\n// SHEETS / SIDE PANELS\n// ============================================================================\n\nconst SHEET_SPINNER_HTML = `\n <div class=\"cmm-sheet__loading\">\n <div class=\"cmm-spinner\"></div>\n </div>\n`\n\nconst SHEET_ERROR_HTML = `\n <div class=\"cmm-sheet__error\">\n <p>Failed to load content. Please try the full page.</p>\n </div>\n`\n\nfunction replaceSheetMarkup(element: HTMLElement, markup: string): void {\n element.replaceChildren(createSafeMarkupFragment(markup, { allowSvg: true }))\n}\n\n/**\n * Setup side sheet/drawer components.\n */\nexport function setupSheets(setupConfig: SheetSetupConfig = {}): () => void {\n const {\n openClass = 'cmm-sheet--open',\n hiddenClass = 'cmm-sheet--hidden',\n backdropVisibleClass = 'cmm-sheet__backdrop--visible',\n backdropHiddenClass = 'cmm-sheet__backdrop--hidden',\n spinnerHtml: spinnerMarkup = SHEET_SPINNER_HTML,\n errorHtml: errorMarkup = SHEET_ERROR_HTML,\n allowCrossOriginFetch = false,\n } = setupConfig\n\n const sheetMap = new Map<string, SheetConfig>()\n\n document.querySelectorAll<HTMLElement>('[data-sheet]').forEach(sheet => {\n const key = sheet.dataset.sheet\n if (!key) {\n return\n }\n\n const backdrop = document.querySelector<HTMLElement>(`[data-sheet-backdrop='${key}']`)\n const content = sheet.querySelector<HTMLElement>('[data-sheet-content]')\n if (!backdrop || !content) {\n return\n }\n\n sheetMap.set(key, {\n key,\n sheet,\n backdrop,\n content,\n fullPageLink: sheet.querySelector<HTMLAnchorElement>('[data-sheet-fullpage]'),\n expandLink: sheet.querySelector<HTMLAnchorElement>('[data-sheet-expand]'),\n focusSelector: sheet.dataset.sheetFocus,\n })\n })\n\n if (sheetMap.size === 0) {\n return () => {\n /* noop */\n }\n }\n\n let activeSheet: SheetConfig | null = null\n\n const setOpenState = (config: SheetConfig, open: boolean) => {\n config.sheet.classList.toggle(openClass, open)\n config.sheet.classList.toggle(hiddenClass, !open)\n config.backdrop.classList.toggle(backdropVisibleClass, open)\n config.backdrop.classList.toggle(backdropHiddenClass, !open)\n config.sheet.setAttribute('aria-hidden', open ? 'false' : 'true')\n }\n\n const closeSheet = (config: SheetConfig) => {\n if (activeSheet?.key === config.key) {\n activeSheet = null\n }\n setOpenState(config, false)\n }\n\n const openSheet = async (config: SheetConfig, trigger?: HTMLElement) => {\n if (activeSheet && activeSheet.key !== config.key) {\n closeSheet(activeSheet)\n }\n\n activeSheet = config\n setOpenState(config, true)\n\n const url = trigger?.dataset.sheetUrl ?? config.sheet.dataset.sheetUrl\n const focusSelector = trigger?.dataset.sheetFocus ?? config.focusSelector\n const fullPageHref = trigger?.dataset.sheetFullpage ?? config.sheet.dataset.sheetFullpage\n const expandHref = trigger?.dataset.sheetExpand ?? config.sheet.dataset.sheetExpand\n\n if (config.fullPageLink && fullPageHref) {\n config.fullPageLink.href = fullPageHref\n }\n\n if (config.expandLink && expandHref) {\n config.expandLink.href = expandHref\n }\n\n if (!url || (!allowCrossOriginFetch && !isSameOriginUrl(url))) {\n replaceSheetMarkup(config.content, errorMarkup)\n return\n }\n\n replaceSheetMarkup(config.content, spinnerMarkup)\n\n try {\n const response = await fetch(url)\n if (response.ok) {\n const markup = await response.text()\n replaceSheetMarkup(config.content, markup)\n if (focusSelector) {\n const focusTarget = config.content.querySelector<HTMLElement>(focusSelector)\n focusTarget?.focus()\n }\n } else if (fullPageHref) {\n navigateDocumentTo(fullPageHref)\n } else {\n replaceSheetMarkup(config.content, errorMarkup)\n }\n } catch {\n if (fullPageHref) {\n navigateDocumentTo(fullPageHref)\n } else {\n replaceSheetMarkup(config.content, errorMarkup)\n }\n }\n }\n\n const handlers: { element: HTMLElement; event: string; handler: (e: Event) => void }[] = []\n\n sheetMap.forEach(config => {\n const closeHandler = (event: Event) => {\n const target = event.target as HTMLElement\n if (target.closest('[data-sheet-close]')) {\n event.preventDefault()\n closeSheet(config)\n }\n }\n\n const backdropHandler = () => {\n closeSheet(config)\n }\n\n const expandHandler = () => {\n closeSheet(config)\n }\n\n config.sheet.addEventListener('click', closeHandler)\n config.backdrop.addEventListener('click', backdropHandler)\n handlers.push({ element: config.sheet, event: 'click', handler: closeHandler })\n handlers.push({ element: config.backdrop, event: 'click', handler: backdropHandler })\n\n if (config.expandLink) {\n config.expandLink.addEventListener('click', expandHandler)\n handlers.push({ element: config.expandLink, event: 'click', handler: expandHandler })\n }\n })\n\n // Setup triggers\n document.querySelectorAll<HTMLElement>('[data-sheet-trigger]').forEach(trigger => {\n const key = trigger.dataset.sheetTrigger\n if (!key) {\n return\n }\n\n const config = sheetMap.get(key)\n if (!config) {\n return\n }\n\n const handler = (event: Event) => {\n event.preventDefault()\n void openSheet(config, trigger)\n }\n\n trigger.addEventListener('click', handler)\n handlers.push({ element: trigger, event: 'click', handler })\n })\n\n // Escape key handler\n const escapeHandler = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && activeSheet) {\n closeSheet(activeSheet)\n }\n }\n\n document.addEventListener('keydown', escapeHandler)\n\n // Return cleanup function\n return () => {\n handlers.forEach(({ element, event, handler }) => {\n element.removeEventListener(event, handler)\n })\n document.removeEventListener('keydown', escapeHandler)\n }\n}\n\n// ============================================================================\n// NAVIGATION KEYBOARD SUPPORT\n// ============================================================================\n\n/**\n * Setup keyboard navigation for sidebar nav items.\n */\nexport function setupNavKeyboard(): () => void {\n const navList = document.querySelector<HTMLElement>('[data-nav-list]')\n if (!navList) {\n return () => {\n /* noop */\n }\n }\n\n const getNavItems = () =>\n Array.from(navList.querySelectorAll<HTMLAnchorElement>('[data-nav-item]'))\n\n const handler = (event: KeyboardEvent) => {\n const items = getNavItems()\n const currentIndex = items.indexOf(document.activeElement as HTMLAnchorElement)\n\n if (event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0\n items[nextIndex]?.focus()\n } else if (event.key === 'ArrowUp') {\n event.preventDefault()\n const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1\n items[prevIndex]?.focus()\n } else if (event.key === 'Home') {\n event.preventDefault()\n items[0]?.focus()\n } else if (event.key === 'End') {\n event.preventDefault()\n items[items.length - 1]?.focus()\n }\n }\n\n navList.addEventListener('keydown', handler)\n\n return () => {\n navList.removeEventListener('keydown', handler)\n }\n}\n\n// ============================================================================\n// BOOTSTRAP\n// ============================================================================\n\n/**\n * Initialize all layout components.\n */\nexport function initLayout(\n config: {\n sidebar?: SidebarConfig\n userMenu?: UserMenuConfig\n collapsibles?: CollapsibleConfig\n sheets?: SheetSetupConfig\n } = {}\n): () => void {\n const cleanups = [\n setupSidebar(config.sidebar),\n setupUserMenu(config.userMenu),\n setupCollapsibles(config.collapsibles),\n setupSheets(config.sheets),\n setupNavKeyboard(),\n ]\n\n return () => {\n cleanups.forEach(cleanup => {\n cleanup()\n })\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { VariantProps } from 'class-variance-authority';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "glass" | "orange" | "cyan" | "violet" | "rose" | "emerald" | "
|
|
4
|
+
variant?: "glass" | "orange" | "cyan" | "violet" | "rose" | "emerald" | "default" | "amber" | "outline" | "destructive" | "secondary" | "blue" | "live" | null | undefined;
|
|
5
5
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
6
6
|
export type BadgeSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
7
7
|
export type BadgeStatus = 'active' | 'inactive' | 'pending' | 'error';
|
|
@@ -9,8 +9,8 @@ import * as React from 'react';
|
|
|
9
9
|
* `glass-primary` — see CVA mapping below.
|
|
10
10
|
*/
|
|
11
11
|
declare const buttonVariants: (props?: ({
|
|
12
|
-
variant?: "glass" | "link" | "
|
|
13
|
-
size?: "default" | "
|
|
12
|
+
variant?: "glass" | "link" | "default" | "primary" | "outline" | "ghost" | "destructive" | "secondary" | "glass-primary" | null | undefined;
|
|
13
|
+
size?: "default" | "icon" | "sm" | "lg" | "icon-sm" | "icon-lg" | null | undefined;
|
|
14
14
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
15
15
|
export type ButtonTone = 'glass' | 'shadcn';
|
|
16
16
|
export type ButtonProps = React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
|
|
@@ -2,7 +2,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
declare const copyButtonVariants: (props?: ({
|
|
4
4
|
variant?: "default" | "ghost" | null | undefined;
|
|
5
|
-
size?: "default" | "
|
|
5
|
+
size?: "default" | "icon" | "sm" | "icon-sm" | null | undefined;
|
|
6
6
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
7
|
export interface CopyButtonProps extends Omit<React.ComponentProps<'button'>, 'children'>, VariantProps<typeof copyButtonVariants> {
|
|
8
8
|
/** The text to copy to clipboard */
|
|
@@ -6,7 +6,7 @@ export interface TrendConfig {
|
|
|
6
6
|
label?: string;
|
|
7
7
|
}
|
|
8
8
|
declare const trendVariants: (props?: ({
|
|
9
|
-
direction?: "
|
|
9
|
+
direction?: "neutral" | "up" | "down" | null | undefined;
|
|
10
10
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
11
11
|
export interface StatCardProps extends React.ComponentProps<'div'> {
|
|
12
12
|
label: string;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import * as TogglePrimitive from '@radix-ui/react-toggle';
|
|
4
4
|
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
|
|
5
5
|
declare const toggleVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "default" | "outline" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | null | undefined;
|
|
8
8
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
9
9
|
declare function Toggle({ className, variant, size, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): import("react/jsx-runtime").JSX.Element;
|
package/dist/react/sidebar.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SidebarComponentConfig, SidebarNavItem, SidebarSection, SidebarUser, SidebarFooterAction } from '../sidebar/types';
|
|
1
|
+
import { SidebarComponentConfig, SidebarBadgeConfig, SidebarBadgeTone, SidebarNavItem, SidebarScopeConfig, SidebarScopeOption, SidebarSection, SidebarUser, SidebarFooterAction } from '../sidebar/types';
|
|
2
2
|
/**
|
|
3
3
|
* Sidebar React wrapper — Lane A imperative wrap over src/sidebar/component.ts
|
|
4
4
|
*
|
|
@@ -6,8 +6,8 @@ import { SidebarComponentConfig, SidebarNavItem, SidebarSection, SidebarUser, Si
|
|
|
6
6
|
* import { Sidebar } from '@countermeasure-platform/web-components/react/sidebar'
|
|
7
7
|
*
|
|
8
8
|
* The wrapper renders a host div then mounts `SidebarComponent` into it on
|
|
9
|
-
* mount. Prop changes to `activeItemId` and controlled `open`
|
|
10
|
-
* the class's public API.
|
|
9
|
+
* mount. Prop changes to `activeItemId`, `pathname`, and controlled `open`
|
|
10
|
+
* are synced via the class's public API.
|
|
11
11
|
*/
|
|
12
12
|
import * as React from 'react';
|
|
13
13
|
export interface SidebarProps extends Omit<SidebarComponentConfig, 'container' | 'onNavigate' | 'onCollapse'> {
|
|
@@ -19,7 +19,7 @@ export interface SidebarProps extends Omit<SidebarComponentConfig, 'container' |
|
|
|
19
19
|
onCollapse?: (collapsed: boolean) => void;
|
|
20
20
|
className?: string;
|
|
21
21
|
}
|
|
22
|
-
export type { SidebarNavItem, SidebarSection, SidebarUser,
|
|
22
|
+
export type { SidebarBadgeConfig, SidebarBadgeTone, SidebarFooterAction, SidebarNavItem, SidebarScopeConfig, SidebarScopeOption, SidebarSection, SidebarUser, };
|
|
23
23
|
export interface SidebarCollapseButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
24
24
|
collapsed?: boolean;
|
|
25
25
|
edge?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../src/react/sidebar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,sBAAsB,EACtB,WAAW,GAAG,YAAY,GAAG,YAAY,CAC1C;IACC,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3C,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,YAAY,
|
|
1
|
+
{"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../src/react/sidebar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,sBAAsB,EACtB,WAAW,GAAG,YAAY,GAAG,YAAY,CAC1C;IACC,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3C,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,GACZ,CAAA;AAED,MAAM,WAAW,0BAA2B,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAC/F,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,QAAA,MAAM,OAAO;YAAW,YAAY;;CAsDnC,CAAA;AAwBD,QAAA,MAAM,qBAAqB;qBAAoB,0BAA0B;;CA2BxE,CAAA;AAID,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAA"}
|
package/dist/react/sidebar.js
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
import { t as e } from "../component-
|
|
1
|
+
import { t as e } from "../component-D5sRm1fq.js";
|
|
2
2
|
import { t } from "../utils-BC9GT6Pr.js";
|
|
3
3
|
import * as n from "react";
|
|
4
4
|
import { jsx as r } from "react/jsx-runtime";
|
|
5
5
|
//#region src/react/sidebar.tsx
|
|
6
6
|
var i = (i) => {
|
|
7
|
-
let { activeItemId: a,
|
|
8
|
-
return
|
|
9
|
-
if (typeof document > "u" || !
|
|
7
|
+
let { activeItemId: a, pathname: o, onNavigate: s, onCollapse: c, className: l, open: u, ...d } = i, f = n.useRef(null), p = n.useRef(null), m = n.useRef(s), h = n.useRef(c);
|
|
8
|
+
return m.current = s, h.current = c, n.useEffect(() => {
|
|
9
|
+
if (typeof document > "u" || !f.current) return;
|
|
10
10
|
let t = {
|
|
11
|
-
...
|
|
12
|
-
container:
|
|
11
|
+
...d,
|
|
12
|
+
container: f.current,
|
|
13
13
|
onNavigate: (e) => {
|
|
14
|
-
|
|
14
|
+
m.current?.(e);
|
|
15
15
|
},
|
|
16
16
|
onCollapse: (e) => {
|
|
17
|
-
|
|
17
|
+
h.current?.(e);
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
return
|
|
21
|
-
|
|
20
|
+
return u !== void 0 && (t.open = u), o !== void 0 && (t.pathname = o), p.current = new e(t), () => {
|
|
21
|
+
p.current?.destroy(), p.current = null;
|
|
22
22
|
};
|
|
23
23
|
}, []), n.useEffect(() => {
|
|
24
|
-
|
|
25
|
-
}, [
|
|
26
|
-
!
|
|
27
|
-
}, [a]),
|
|
28
|
-
|
|
24
|
+
p.current && p.current.setOpen(u === !0);
|
|
25
|
+
}, [u]), n.useEffect(() => {
|
|
26
|
+
!p.current || a === void 0 || p.current.setActive(a);
|
|
27
|
+
}, [a]), n.useEffect(() => {
|
|
28
|
+
!p.current || a !== void 0 || p.current.setPathname(o);
|
|
29
|
+
}, [a, o]), /* @__PURE__ */ r("div", {
|
|
30
|
+
ref: f,
|
|
29
31
|
"data-slot": "sidebar",
|
|
30
|
-
className: t(
|
|
32
|
+
className: t(l)
|
|
31
33
|
});
|
|
32
34
|
};
|
|
33
35
|
i.displayName = "Sidebar";
|