@avesh.k/prism-sidebar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Initial enterprise-ready release of `@your-org/prism-sidebar`.
6
+ - Includes framework-agnostic web component, tests, docs, and CI templates.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # @your-org/prism-sidebar
2
+
3
+ Framework-agnostic Prism sidebar package powered by native Web Components.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @your-org/prism-sidebar
9
+ ```
10
+
11
+ ## Registration
12
+
13
+ ```ts
14
+ import { registerPrismSidebar } from '@your-org/prism-sidebar'
15
+ registerPrismSidebar()
16
+ ```
17
+
18
+ ## Attributes
19
+
20
+ - `token`
21
+ - `api-base-url`
22
+ - `active-path`
23
+ - `target` (`_self | _blank | _top`)
24
+ - `use-shadow-dom`
25
+ - `aria-label`
26
+ - `collapsed`
27
+ - `auto-navigate`
28
+
29
+ ## JS Properties
30
+
31
+ - `token`
32
+ - `apiBaseUrl`
33
+ - `activePath`
34
+ - `target`
35
+ - `useShadowDom`
36
+ - `ariaLabel`
37
+ - `collapsed`
38
+ - `autoNavigate`
39
+
40
+ ## Events
41
+
42
+ - `sidebar-loaded`
43
+ - `sidebar-error`
44
+ - `sidebar-item-click`
45
+
46
+ ## Methods
47
+
48
+ - `refresh()`
49
+ - `setActivePath(path)`
50
+ - `setConfig(partialConfig)`
51
+
52
+ ## Plain HTML
53
+
54
+ ```html
55
+ <script type="module">
56
+ import { registerPrismSidebar } from '@your-org/prism-sidebar'
57
+ registerPrismSidebar()
58
+ </script>
59
+ <prism-sidebar token="abc" api-base-url="https://api.example.com/menu"></prism-sidebar>
60
+ ```
61
+
62
+ ## React
63
+
64
+ ```tsx
65
+ import { useEffect } from 'react'
66
+ import { registerPrismSidebar } from '@your-org/prism-sidebar'
67
+
68
+ export function App() {
69
+ useEffect(() => {
70
+ registerPrismSidebar()
71
+ }, [])
72
+ return <prism-sidebar token="abc" api-base-url="https://api.example.com/menu" />
73
+ }
74
+ ```
75
+
76
+ ## Angular
77
+
78
+ Register once in bootstrap and consume directly:
79
+
80
+ ```html
81
+ <prism-sidebar [attr.token]="token" [attr.api-base-url]="apiBaseUrl"></prism-sidebar>
82
+ ```
83
+
84
+ ## SolidJS
85
+
86
+ ```tsx
87
+ import { onMount } from 'solid-js'
88
+ import { registerPrismSidebar } from '@your-org/prism-sidebar'
89
+
90
+ export default function App() {
91
+ onMount(() => registerPrismSidebar())
92
+ return <prism-sidebar token="abc" api-base-url="https://api.example.com/menu" />
93
+ }
94
+ ```
95
+
96
+ ## iframe-safe activePath
97
+
98
+ Pass `active-path` from the host application to avoid iframe routing mismatch. This is the recommended integration pattern in nested iframe environments.
99
+
100
+ ## Testing
101
+
102
+ ```bash
103
+ npm run test
104
+ npm run test:e2e
105
+ ```
106
+
107
+ ## Publishing
108
+
109
+ Set `.npmrc` for internal registry and publish with:
110
+
111
+ ```bash
112
+ npm run build
113
+ npm publish
114
+ ```
115
+
116
+ ## Versioning
117
+
118
+ Use semantic versioning: patch for fixes, minor for backward-compatible features, major for breaking changes.
@@ -0,0 +1,544 @@
1
+ function P(e) {
2
+ if (!e || typeof e != "object") return !1;
3
+ const t = e;
4
+ return typeof t.id < "u" && typeof t.name == "string";
5
+ }
6
+ function m(e) {
7
+ return e.filter((t) => P(t)).map((t) => {
8
+ const r = {
9
+ ...t,
10
+ id: String(t.id)
11
+ };
12
+ return Array.isArray(t.children) && (r.children = m(t.children)), r;
13
+ });
14
+ }
15
+ function I(e) {
16
+ if (Array.isArray(e)) return m(e);
17
+ if (!e || typeof e != "object") return [];
18
+ const t = e;
19
+ return Array.isArray(t.data?.menu) ? m(t.data.menu) : Array.isArray(t.menu) ? m(t.menu) : [];
20
+ }
21
+ async function E(e, t) {
22
+ if (!e.apiBaseUrl) throw new Error("Missing apiBaseUrl");
23
+ if (!e.token) throw new Error("Missing token");
24
+ const r = {
25
+ headers: {
26
+ Authorization: `Bearer ${e.token}`
27
+ }
28
+ };
29
+ t && (r.signal = t);
30
+ const i = await fetch(e.apiBaseUrl, r);
31
+ if (!i.ok)
32
+ throw new Error(`Menu API failed with ${i.status}`);
33
+ let o;
34
+ try {
35
+ o = await i.json();
36
+ } catch {
37
+ throw new Error("Menu API returned non-JSON payload");
38
+ }
39
+ return I(o);
40
+ }
41
+ function $(e) {
42
+ return {
43
+ role: "navigation",
44
+ "aria-label": e
45
+ };
46
+ }
47
+ function z(e) {
48
+ return e ? "page" : null;
49
+ }
50
+ function M(e) {
51
+ return e.key === "Enter" || e.key === " ";
52
+ }
53
+ function A(e) {
54
+ const t = e.trim();
55
+ return t && t.replace(/\/+$/, "") || "/";
56
+ }
57
+ function U(e) {
58
+ return e.split("?")[0]?.split("#")[0] ?? e;
59
+ }
60
+ function h(e, t) {
61
+ if (typeof e == "boolean") return e;
62
+ if (e == null || e === "") return t;
63
+ const r = String(e).toLowerCase();
64
+ return !["0", "false", "no", "off"].includes(r);
65
+ }
66
+ const n = {
67
+ token: "",
68
+ apiBaseUrl: "",
69
+ activePath: "",
70
+ target: "_self",
71
+ useShadowDom: !0,
72
+ ariaLabel: "Sidebar navigation",
73
+ collapsed: !0,
74
+ autoNavigate: !0,
75
+ width: "240px",
76
+ title: "P.R.I.S.M.",
77
+ subtitle: "Purplle Retail Intelligence & Sales Management",
78
+ homeUrl: "/",
79
+ logoutText: "Logout",
80
+ logoutPath: "/sentinel/logout",
81
+ error: ""
82
+ }, B = ["_self", "_blank", "_top"];
83
+ function L(e) {
84
+ const t = {
85
+ target: C(e.getAttribute("target"))
86
+ }, r = e.getAttribute("token");
87
+ r !== null && (t.token = r);
88
+ const i = e.getAttribute("api-base-url");
89
+ i !== null && (t.apiBaseUrl = i);
90
+ const o = e.getAttribute("active-path");
91
+ o !== null && (t.activePath = o);
92
+ const s = e.getAttribute("aria-label");
93
+ s !== null && (t.ariaLabel = s);
94
+ const l = e.getAttribute("use-shadow-dom");
95
+ l !== null && (t.useShadowDom = h(l, !0));
96
+ const d = e.getAttribute("collapsed");
97
+ d !== null && (t.collapsed = h(d, !1));
98
+ const a = e.getAttribute("auto-navigate");
99
+ a !== null && (t.autoNavigate = h(a, !0));
100
+ const c = e.getAttribute("width");
101
+ c !== null && (t.width = c);
102
+ const u = e.getAttribute("title");
103
+ u !== null && (t.title = u);
104
+ const g = e.getAttribute("subtitle");
105
+ g !== null && (t.subtitle = g);
106
+ const f = e.getAttribute("home-url");
107
+ f !== null && (t.homeUrl = f);
108
+ const p = e.getAttribute("logout-text");
109
+ p !== null && (t.logoutText = p);
110
+ const w = e.getAttribute("logout-path");
111
+ w !== null && (t.logoutPath = w);
112
+ const x = e.getAttribute("error");
113
+ return x !== null && (t.error = x), t;
114
+ }
115
+ function k(e) {
116
+ const t = C(e.target);
117
+ return {
118
+ token: e.token?.trim() ?? n.token,
119
+ apiBaseUrl: e.apiBaseUrl?.replace(/\/+$/, "") ?? n.apiBaseUrl,
120
+ activePath: e.activePath ? A(e.activePath) : n.activePath,
121
+ target: t,
122
+ useShadowDom: h(e.useShadowDom, n.useShadowDom),
123
+ ariaLabel: e.ariaLabel?.trim() || n.ariaLabel,
124
+ collapsed: h(e.collapsed, n.collapsed),
125
+ autoNavigate: h(e.autoNavigate, n.autoNavigate),
126
+ width: e.width?.trim() || n.width,
127
+ title: e.title?.trim() || n.title,
128
+ subtitle: e.subtitle?.trim() || n.subtitle,
129
+ homeUrl: e.homeUrl?.trim() || n.homeUrl,
130
+ logoutText: e.logoutText?.trim() || n.logoutText,
131
+ logoutPath: e.logoutPath?.trim() || n.logoutPath,
132
+ error: e.error?.trim() || n.error
133
+ };
134
+ }
135
+ function b(e, t) {
136
+ const r = { ...e };
137
+ for (const i of Object.keys(t)) {
138
+ const o = t[i];
139
+ typeof o > "u" || (r[i] = o);
140
+ }
141
+ return k(r);
142
+ }
143
+ function C(e) {
144
+ if (!e) return n.target;
145
+ const t = e;
146
+ return B.includes(t) ? t : n.target;
147
+ }
148
+ const v = {
149
+ loaded: "sidebar-loaded",
150
+ error: "sidebar-error",
151
+ itemClick: "sidebar-item-click"
152
+ };
153
+ function T(e, t) {
154
+ e.dispatchEvent(new CustomEvent(v.loaded, { detail: t, bubbles: !0, composed: !0 }));
155
+ }
156
+ function R(e, t) {
157
+ e.dispatchEvent(new CustomEvent(v.error, { detail: t, bubbles: !0, composed: !0 }));
158
+ }
159
+ function H(e, t) {
160
+ e.dispatchEvent(new CustomEvent(v.itemClick, { detail: t, bubbles: !0, composed: !0 }));
161
+ }
162
+ function y(e) {
163
+ return A(U(e));
164
+ }
165
+ function N(e, t) {
166
+ const r = y(t || "/"), i = /* @__PURE__ */ new Set();
167
+ let o = null;
168
+ const s = (l, d) => {
169
+ for (const a of l) {
170
+ const c = String(a.id), u = a.path ? y(a.path) : "", g = u && u === r, f = Array.isArray(a.children) ? s(a.children, [...d, c]) : !1;
171
+ if (g || f) {
172
+ o = g ? c : o;
173
+ for (const p of d)
174
+ i.add(p);
175
+ return f && i.add(c), !0;
176
+ }
177
+ }
178
+ return !1;
179
+ };
180
+ return s(e, []), { activeId: o, expandedIds: i };
181
+ }
182
+ function D(e) {
183
+ const t = String(e || "").trim();
184
+ return t || "#";
185
+ }
186
+ function _(e, t) {
187
+ if (!t.autoNavigate) return;
188
+ const r = D(e);
189
+ if (r !== "#") {
190
+ if (t.target === "_blank") {
191
+ window.open(r, "_blank", "noopener,noreferrer");
192
+ return;
193
+ }
194
+ if (t.target === "_top")
195
+ try {
196
+ if (window.top) {
197
+ window.top.location.href = r;
198
+ return;
199
+ }
200
+ } catch {
201
+ window.location.href = r;
202
+ return;
203
+ }
204
+ window.location.href = r;
205
+ }
206
+ }
207
+ function F() {
208
+ return {
209
+ loading: !1,
210
+ menu: [],
211
+ error: "",
212
+ activeId: null,
213
+ expandedIds: /* @__PURE__ */ new Set()
214
+ };
215
+ }
216
+ const V = ':host{--prism-sidebar-width: 240px;--prism-sidebar-bg: linear-gradient(rgb(156,0,173),rgb(89,0,99));--prism-sidebar-text-color: #ffffff;--prism-sidebar-border-color: rgba(255,255,255,.15);--prism-sidebar-hover-bg: rgba(255,255,255,.12);--prism-sidebar-active-bg: rgba(255,255,255,.1);--prism-sidebar-indent: 14px;--prism-sidebar-font-family: Inter,sans-serif,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial;line-height:1.5;display:block}.prism-sidebar{position:fixed;left:0;top:0;bottom:0;z-index:1600;width:var(--prism-sidebar-width);min-width:var(--prism-sidebar-width);box-sizing:border-box;background:var(--prism-sidebar-bg);color:var(--prism-sidebar-text-color);font-family:var(--prism-sidebar-font-family);padding:20px;height:100vh;overflow:hidden;display:flex;flex-direction:column;gap:12px}.prism-brand{text-align:center;color:inherit;text-decoration:none;font-size:30px;line-height:36px;font-weight:600;letter-spacing:2px;padding:0 10px;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.prism-subtitle{text-align:center;font-size:12px;line-height:16px;padding:0 10px;margin-bottom:16px}.prism-nav{flex:1 1 0%;display:flex;flex-direction:column;gap:4px;overflow-y:auto;-ms-overflow-style:none;scrollbar-width:none}.prism-nav::-webkit-scrollbar{display:none}.prism-footer{margin-top:8px;padding-top:8px;border-top:1px solid var(--prism-sidebar-border-color)}.prism-logout{width:100%;border:0;background:transparent;color:inherit;display:flex;align-items:center;gap:10px;text-align:left;font-size:12.5px;line-height:17px;font-weight:600;padding:8px 12px;border-radius:6px;cursor:pointer}.prism-state,.prism-empty,.prism-error{padding:8px 10px;font-size:14px}.prism-error{color:#ffd0d0}.prism-group{margin-bottom:4px}.prism-group-title{width:100%;background:transparent;border:0;color:#fffffff2;display:flex;align-items:center;justify-content:space-between;text-align:left;padding:10px 12px;border-radius:6px;font-size:13px;line-height:18px;font-weight:600;cursor:pointer}.prism-group-title:hover{background:var(--prism-sidebar-hover-bg)}.prism-brand:hover{opacity:.9}.prism-logout:hover{background:var(--prism-sidebar-hover-bg);color:#fff}.prism-children{margin-left:12px;margin-top:4px}.prism-item{width:100%;border:0;background:transparent;color:#fffc;display:flex;align-items:center;gap:10px;font-size:12.5px;line-height:17px;font-weight:500;padding:8px 12px;border-radius:6px;cursor:pointer}.prism-item:hover{color:#fff}.prism-item-icon{display:flex;flex-shrink:0;font-size:16px}.prism-chevron{display:flex;flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.prism-chevron-open{transform:rotate(180deg)}.prism-item.active{background:var(--prism-sidebar-active-bg);font-weight:600}.prism-item:focus-visible,.prism-group-title:focus-visible,.prism-brand:focus-visible,.prism-logout:focus-visible{outline:2px solid #ffffff;outline-offset:2px}';
217
+ class j extends HTMLElement {
218
+ static observedAttributes = [
219
+ "token",
220
+ "api-base-url",
221
+ "active-path",
222
+ "target",
223
+ "use-shadow-dom",
224
+ "aria-label",
225
+ "collapsed",
226
+ "auto-navigate",
227
+ "width",
228
+ "title",
229
+ "subtitle",
230
+ "home-url",
231
+ "logout-text",
232
+ "logout-path",
233
+ "error"
234
+ ];
235
+ config = k({});
236
+ state = F();
237
+ renderRoot = null;
238
+ abortController = null;
239
+ refreshSequence = 0;
240
+ get token() {
241
+ return this.config.token;
242
+ }
243
+ set token(t) {
244
+ this.setConfig({ token: t });
245
+ }
246
+ get apiBaseUrl() {
247
+ return this.config.apiBaseUrl;
248
+ }
249
+ set apiBaseUrl(t) {
250
+ this.setConfig({ apiBaseUrl: t });
251
+ }
252
+ get activePath() {
253
+ return this.config.activePath;
254
+ }
255
+ set activePath(t) {
256
+ this.setActivePath(t);
257
+ }
258
+ get target() {
259
+ return this.config.target;
260
+ }
261
+ set target(t) {
262
+ this.setConfig({ target: t });
263
+ }
264
+ get useShadowDom() {
265
+ return this.config.useShadowDom;
266
+ }
267
+ set useShadowDom(t) {
268
+ this.setConfig({ useShadowDom: t });
269
+ }
270
+ get ariaLabel() {
271
+ return this.config.ariaLabel;
272
+ }
273
+ set ariaLabel(t) {
274
+ this.setConfig({ ariaLabel: t });
275
+ }
276
+ get collapsed() {
277
+ return this.config.collapsed;
278
+ }
279
+ set collapsed(t) {
280
+ this.setConfig({ collapsed: t });
281
+ }
282
+ get autoNavigate() {
283
+ return this.config.autoNavigate;
284
+ }
285
+ set autoNavigate(t) {
286
+ this.setConfig({ autoNavigate: t });
287
+ }
288
+ get width() {
289
+ return this.config.width;
290
+ }
291
+ set width(t) {
292
+ this.setConfig({ width: t });
293
+ }
294
+ get title() {
295
+ return this.config.title;
296
+ }
297
+ set title(t) {
298
+ this.setConfig({ title: t });
299
+ }
300
+ get subtitle() {
301
+ return this.config.subtitle;
302
+ }
303
+ set subtitle(t) {
304
+ this.setConfig({ subtitle: t });
305
+ }
306
+ get homeUrl() {
307
+ return this.config.homeUrl;
308
+ }
309
+ set homeUrl(t) {
310
+ this.setConfig({ homeUrl: t });
311
+ }
312
+ get logoutText() {
313
+ return this.config.logoutText;
314
+ }
315
+ set logoutText(t) {
316
+ this.setConfig({ logoutText: t });
317
+ }
318
+ get logoutPath() {
319
+ return this.config.logoutPath;
320
+ }
321
+ set logoutPath(t) {
322
+ this.setConfig({ logoutPath: t });
323
+ }
324
+ get error() {
325
+ return this.config.error;
326
+ }
327
+ set error(t) {
328
+ this.setConfig({ error: t });
329
+ }
330
+ connectedCallback() {
331
+ this.syncConfigFromAttributes(), this.ensureRenderRoot(), this.render(), this.refresh().catch(() => {
332
+ });
333
+ }
334
+ disconnectedCallback() {
335
+ this.abortController && this.abortController.abort();
336
+ }
337
+ attributeChangedCallback(t, r, i) {
338
+ if (r !== i) {
339
+ if (this.syncConfigFromAttributes(), t === "use-shadow-dom" && this.ensureRenderRoot(), this.render(), t === "token" || t === "api-base-url") {
340
+ this.refresh().catch(() => {
341
+ });
342
+ return;
343
+ }
344
+ t === "active-path" && (this.applyMatchState(), this.render());
345
+ }
346
+ }
347
+ async refresh() {
348
+ const t = ++this.refreshSequence;
349
+ this.state.loading = !0, this.state.error = "", this.render(), this.abortController && this.abortController.abort(), this.abortController = new AbortController();
350
+ try {
351
+ const r = await E(this.config, this.abortController.signal);
352
+ if (t !== this.refreshSequence) return;
353
+ this.state.menu = r, this.state.loading = !1, this.state.error = "", this.applyMatchState(), this.render(), T(this, { menu: r });
354
+ } catch (r) {
355
+ if (t !== this.refreshSequence || r instanceof Error && r.name === "AbortError") return;
356
+ const i = r instanceof Error ? r.message : "Failed to load menu";
357
+ this.state.loading = !1, this.state.menu = [], this.state.error = i, this.render(), R(this, { message: i });
358
+ }
359
+ }
360
+ setActivePath(t) {
361
+ this.config = b(this.config, { activePath: t }), this.applyMatchState(), this.render();
362
+ }
363
+ setConfig(t) {
364
+ const r = this.config.useShadowDom;
365
+ this.config = b(this.config, t), r !== this.config.useShadowDom && this.ensureRenderRoot(), this.applyMatchState(), this.render();
366
+ }
367
+ syncConfigFromAttributes() {
368
+ this.config = b(this.config, L(this));
369
+ }
370
+ ensureRenderRoot() {
371
+ if (this.config.useShadowDom) {
372
+ this.renderRoot = this.shadowRoot ?? this.attachShadow({ mode: "open" });
373
+ return;
374
+ }
375
+ this.renderRoot = this;
376
+ }
377
+ applyMatchState() {
378
+ const t = this.config.activePath || window.location.pathname, { activeId: r, expandedIds: i } = N(this.state.menu, t);
379
+ this.state.activeId = r, this.state.expandedIds = i;
380
+ }
381
+ resolveItemState(t) {
382
+ const r = String(t.id);
383
+ return {
384
+ active: this.state.activeId === r,
385
+ expanded: this.state.expandedIds.has(r)
386
+ };
387
+ }
388
+ render() {
389
+ if (!this.renderRoot) return;
390
+ this.style.setProperty("width", this.config.width), this.style.setProperty("min-width", this.config.width), this.style.setProperty("--prism-sidebar-width", this.config.width);
391
+ const t = $(this.config.ariaLabel), r = `
392
+ <style>${V}</style>
393
+ <aside class="prism-sidebar" style="--prism-sidebar-width: ${this.config.width};">
394
+ ${this.renderHeader()}
395
+ <nav class="prism-nav" role="${t.role}" aria-label="${t["aria-label"]}">
396
+ ${this.renderBody(this.state.menu)}
397
+ </nav>
398
+ ${this.renderFooter()}
399
+ </aside>
400
+ `;
401
+ this.renderRoot.innerHTML = r, this.bindEvents();
402
+ }
403
+ renderHeader() {
404
+ return `
405
+ <a href="${this.escapeAttribute(this.config.homeUrl)}" class="prism-brand" data-action="home">
406
+ ${this.escapeHtml(this.config.title)}
407
+ </a>
408
+ <div class="prism-subtitle">${this.escapeHtml(this.config.subtitle)}</div>
409
+ `;
410
+ }
411
+ renderFooter() {
412
+ return `
413
+ <div class="prism-footer">
414
+ <button type="button" class="prism-logout" data-action="logout">
415
+ <span class="prism-item-icon">${this.getLogoutSvg()}</span>
416
+ ${this.escapeHtml(this.config.logoutText)}
417
+ </button>
418
+ </div>
419
+ `;
420
+ }
421
+ renderBody(t) {
422
+ const r = this.config.error;
423
+ return this.state.loading ? '<div class="prism-state">Loading...</div>' : r ? `<div class="prism-error">${r}</div>` : t.length ? t.map((i) => this.renderNode(i)).join("") : '<div class="prism-empty">No menu available</div>';
424
+ }
425
+ renderNode(t) {
426
+ const r = this.resolveItemState(t);
427
+ if (!!t.children?.length) {
428
+ const s = r.expanded || !this.config.collapsed;
429
+ return `
430
+ <div class="prism-group">
431
+ <button type="button" class="prism-group-title" data-action="toggle" data-id="${t.id}" aria-expanded="${s}">
432
+ <span>${t.name}</span>
433
+ <span class="prism-chevron ${s ? "prism-chevron-open" : ""}">${this.getChevronSvg()}</span>
434
+ </button>
435
+ <div class="prism-children" ${s ? "" : "hidden"}>
436
+ ${(t.children ?? []).map((l) => this.renderNode(l)).join("")}
437
+ </div>
438
+ </div>
439
+ `;
440
+ }
441
+ const o = z(r.active);
442
+ return `
443
+ <button
444
+ type="button"
445
+ class="prism-item ${r.active ? "active" : ""}"
446
+ data-action="navigate"
447
+ data-id="${t.id}"
448
+ data-path="${t.path ?? ""}"
449
+ ${o ? `aria-current="${o}"` : ""}
450
+ >
451
+ <span class="prism-item-icon">${this.getAppstoreSvg()}</span>
452
+ ${t.name}
453
+ </button>
454
+ `;
455
+ }
456
+ bindEvents() {
457
+ this.renderRoot && this.renderRoot.querySelectorAll("[data-action]").forEach((t) => {
458
+ t.addEventListener("click", (r) => this.onActionClick(r)), t.addEventListener("keydown", (r) => {
459
+ M(r) && (r.preventDefault(), this.onActionClick(r));
460
+ });
461
+ });
462
+ }
463
+ onActionClick(t) {
464
+ const r = t.currentTarget;
465
+ if (!(r instanceof HTMLElement)) return;
466
+ const i = r.dataset.action;
467
+ if (i === "home") {
468
+ this.navigateWithoutGuard(this.config.homeUrl);
469
+ return;
470
+ }
471
+ if (i === "logout") {
472
+ this.navigateWithoutGuard(this.config.logoutPath);
473
+ return;
474
+ }
475
+ const o = r.dataset.id;
476
+ if (!o) return;
477
+ if (i === "toggle") {
478
+ this.state.expandedIds.has(o) ? this.state.expandedIds.delete(o) : this.state.expandedIds.add(o), this.render();
479
+ return;
480
+ }
481
+ if (i !== "navigate") return;
482
+ const s = this.findMenuItemById(o, this.state.menu);
483
+ !s || !s.path || (this.state.activeId = o, this.render(), H(this, {
484
+ item: s,
485
+ resolvedPath: s.path,
486
+ target: this.config.target
487
+ }), _(s.path, this.config));
488
+ }
489
+ navigateWithoutGuard(t) {
490
+ const r = String(t || "").trim();
491
+ if (!(!r || r === "#")) {
492
+ if (this.config.target === "_blank") {
493
+ window.open(r, "_blank", "noopener,noreferrer");
494
+ return;
495
+ }
496
+ if (this.config.target === "_top")
497
+ try {
498
+ if (window.top) {
499
+ window.top.location.href = r;
500
+ return;
501
+ }
502
+ } catch {
503
+ window.location.href = r;
504
+ return;
505
+ }
506
+ window.location.href = r;
507
+ }
508
+ }
509
+ escapeHtml(t) {
510
+ return String(t || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
511
+ }
512
+ escapeAttribute(t) {
513
+ return this.escapeHtml(t).replace(/"/g, "&quot;");
514
+ }
515
+ getAppstoreSvg() {
516
+ return '<span class="anticon anticon-appstore"><svg viewBox="64 64 896 896" width="1em" height="1em" fill="currentColor"><path d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"></path></svg></span>';
517
+ }
518
+ getLogoutSvg() {
519
+ return '<span class="anticon anticon-logout"><svg viewBox="64 64 896 896" focusable="false" data-icon="logout" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z"></path></svg></span>';
520
+ }
521
+ getChevronSvg() {
522
+ return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';
523
+ }
524
+ findMenuItemById(t, r) {
525
+ for (const i of r) {
526
+ if (String(i.id) === t) return i;
527
+ if (i.children?.length) {
528
+ const o = this.findMenuItemById(t, i.children);
529
+ if (o) return o;
530
+ }
531
+ }
532
+ return null;
533
+ }
534
+ }
535
+ const S = "prism-sidebar";
536
+ function q() {
537
+ customElements.get(S) || customElements.define(S, j);
538
+ }
539
+ export {
540
+ S as PRISM_SIDEBAR_TAG_NAME,
541
+ j as PrismSidebarElement,
542
+ q as registerPrismSidebar
543
+ };
544
+ //# sourceMappingURL=prism-sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-sidebar.js","sources":["../src/core/api.ts","../src/core/a11y.ts","../src/core/utils.ts","../src/core/config.ts","../src/core/events.ts","../src/core/matcher.ts","../src/core/navigation.ts","../src/core/state.ts","../src/components/prism-sidebar.ts","../src/register.ts"],"sourcesContent":["import type { SidebarConfig } from '../types/config'\nimport type { SidebarMenuApiResponse, SidebarMenuItem } from '../types/menu'\n\nfunction isMenuItem(value: unknown): value is SidebarMenuItem {\n if (!value || typeof value !== 'object') return false\n const item = value as Partial<SidebarMenuItem>\n return typeof item.id !== 'undefined' && typeof item.name === 'string'\n}\n\nfunction sanitizeItems(items: unknown[]): SidebarMenuItem[] {\n return items\n .filter((item): item is SidebarMenuItem => isMenuItem(item))\n .map((item) => {\n const sanitized: SidebarMenuItem = {\n ...item,\n id: String(item.id)\n }\n if (Array.isArray(item.children)) {\n sanitized.children = sanitizeItems(item.children)\n }\n return sanitized\n })\n}\n\nexport function extractMenu(payload: unknown): SidebarMenuItem[] {\n if (Array.isArray(payload)) return sanitizeItems(payload)\n if (!payload || typeof payload !== 'object') return []\n const response = payload as SidebarMenuApiResponse\n if (Array.isArray(response.data?.menu)) return sanitizeItems(response.data.menu)\n if (Array.isArray(response.menu)) return sanitizeItems(response.menu)\n return []\n}\n\nexport async function fetchSidebarMenu(config: SidebarConfig, signal?: AbortSignal): Promise<SidebarMenuItem[]> {\n if (!config.apiBaseUrl) throw new Error('Missing apiBaseUrl')\n if (!config.token) throw new Error('Missing token')\n const requestInit: RequestInit = {\n headers: {\n Authorization: `Bearer ${config.token}`\n }\n }\n if (signal) requestInit.signal = signal\n const response = await fetch(config.apiBaseUrl, requestInit)\n if (!response.ok) {\n throw new Error(`Menu API failed with ${response.status}`)\n }\n let payload: unknown\n try {\n payload = await response.json()\n } catch {\n throw new Error('Menu API returned non-JSON payload')\n }\n return extractMenu(payload)\n}\n","export function getNavigationContainerAttributes(ariaLabel: string): Record<string, string> {\n return {\n role: 'navigation',\n 'aria-label': ariaLabel\n }\n}\n\nexport function getAriaCurrent(isActive: boolean): string | null {\n return isActive ? 'page' : null\n}\n\nexport function isActivationKey(event: KeyboardEvent): boolean {\n return event.key === 'Enter' || event.key === ' '\n}\n","export function normalizeSlash(value: string): string {\n const stripped = value.trim()\n if (!stripped) return '/'\n const noTrailingSlash = stripped.replace(/\\/+$/, '')\n return noTrailingSlash || '/'\n}\n\nexport function stripQueryAndHash(path: string): string {\n return path.split('?')[0]?.split('#')[0] ?? path\n}\n\nexport function toBoolean(value: string | boolean | null | undefined, fallback: boolean): boolean {\n if (typeof value === 'boolean') return value\n if (value === null || value === undefined || value === '') return fallback\n const normalized = String(value).toLowerCase()\n return !['0', 'false', 'no', 'off'].includes(normalized)\n}\n","import type { SidebarConfig, SidebarConfigInput, SidebarNavigationTarget } from '../types/config'\nimport { normalizeSlash, toBoolean } from './utils'\n\nconst DEFAULT_CONFIG: SidebarConfig = {\n token: '',\n apiBaseUrl: '',\n activePath: '',\n target: '_self',\n useShadowDom: true,\n ariaLabel: 'Sidebar navigation',\n collapsed: true,\n autoNavigate: true,\n width: '240px',\n title: 'P.R.I.S.M.',\n subtitle: 'Purplle Retail Intelligence & Sales Management',\n homeUrl: '/',\n logoutText: 'Logout',\n logoutPath: '/sentinel/logout',\n error: ''\n}\n\nconst VALID_TARGETS: SidebarNavigationTarget[] = ['_self', '_blank', '_top']\n\nexport function parseConfigFromAttributes(element: HTMLElement): SidebarConfigInput {\n const config: SidebarConfigInput = {\n target: parseTarget(element.getAttribute('target'))\n }\n const token = element.getAttribute('token')\n if (token !== null) config.token = token\n const apiBaseUrl = element.getAttribute('api-base-url')\n if (apiBaseUrl !== null) config.apiBaseUrl = apiBaseUrl\n const activePath = element.getAttribute('active-path')\n if (activePath !== null) config.activePath = activePath\n const ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel !== null) config.ariaLabel = ariaLabel\n const useShadowDom = element.getAttribute('use-shadow-dom')\n if (useShadowDom !== null) config.useShadowDom = toBoolean(useShadowDom, true)\n const collapsed = element.getAttribute('collapsed')\n if (collapsed !== null) config.collapsed = toBoolean(collapsed, false)\n const autoNavigate = element.getAttribute('auto-navigate')\n if (autoNavigate !== null) config.autoNavigate = toBoolean(autoNavigate, true)\n const width = element.getAttribute('width')\n if (width !== null) config.width = width\n const title = element.getAttribute('title')\n if (title !== null) config.title = title\n const subtitle = element.getAttribute('subtitle')\n if (subtitle !== null) config.subtitle = subtitle\n const homeUrl = element.getAttribute('home-url')\n if (homeUrl !== null) config.homeUrl = homeUrl\n const logoutText = element.getAttribute('logout-text')\n if (logoutText !== null) config.logoutText = logoutText\n const logoutPath = element.getAttribute('logout-path')\n if (logoutPath !== null) config.logoutPath = logoutPath\n const error = element.getAttribute('error')\n if (error !== null) config.error = error\n return config\n}\n\nexport function normalizeConfig(input: SidebarConfigInput): SidebarConfig {\n const target = parseTarget(input.target)\n return {\n token: input.token?.trim() ?? DEFAULT_CONFIG.token,\n apiBaseUrl: input.apiBaseUrl?.replace(/\\/+$/, '') ?? DEFAULT_CONFIG.apiBaseUrl,\n activePath: input.activePath ? normalizeSlash(input.activePath) : DEFAULT_CONFIG.activePath,\n target,\n useShadowDom: toBoolean(input.useShadowDom, DEFAULT_CONFIG.useShadowDom),\n ariaLabel: input.ariaLabel?.trim() || DEFAULT_CONFIG.ariaLabel,\n collapsed: toBoolean(input.collapsed, DEFAULT_CONFIG.collapsed),\n autoNavigate: toBoolean(input.autoNavigate, DEFAULT_CONFIG.autoNavigate),\n width: input.width?.trim() || DEFAULT_CONFIG.width,\n title: input.title?.trim() || DEFAULT_CONFIG.title,\n subtitle: input.subtitle?.trim() || DEFAULT_CONFIG.subtitle,\n homeUrl: input.homeUrl?.trim() || DEFAULT_CONFIG.homeUrl,\n logoutText: input.logoutText?.trim() || DEFAULT_CONFIG.logoutText,\n logoutPath: input.logoutPath?.trim() || DEFAULT_CONFIG.logoutPath,\n error: input.error?.trim() || DEFAULT_CONFIG.error\n }\n}\n\nexport function mergeConfig(base: SidebarConfig, partial: SidebarConfigInput): SidebarConfig {\n const next = { ...base } as Record<string, unknown>\n for (const key of Object.keys(partial) as Array<keyof SidebarConfigInput>) {\n const value = partial[key]\n if (typeof value === 'undefined') continue\n next[key as string] = value\n }\n return normalizeConfig(next as SidebarConfigInput)\n}\n\nfunction parseTarget(value: string | null | undefined): SidebarNavigationTarget {\n if (!value) return DEFAULT_CONFIG.target\n const candidate = value as SidebarNavigationTarget\n return VALID_TARGETS.includes(candidate) ? candidate : DEFAULT_CONFIG.target\n}\n","import type {\n SidebarErrorEventDetail,\n SidebarItemClickEventDetail,\n SidebarLoadedEventDetail\n} from '../types/events'\n\nexport const SIDEBAR_EVENTS = {\n loaded: 'sidebar-loaded',\n error: 'sidebar-error',\n itemClick: 'sidebar-item-click'\n} as const\n\nexport function emitSidebarLoaded(target: HTMLElement, detail: SidebarLoadedEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.loaded, { detail, bubbles: true, composed: true }))\n}\n\nexport function emitSidebarError(target: HTMLElement, detail: SidebarErrorEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.error, { detail, bubbles: true, composed: true }))\n}\n\nexport function emitSidebarItemClick(target: HTMLElement, detail: SidebarItemClickEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.itemClick, { detail, bubbles: true, composed: true }))\n}\n","import type { SidebarMenuItem } from '../types/menu'\nimport { normalizeSlash, stripQueryAndHash } from './utils'\n\nexport interface SidebarMatchResult {\n activeId: string | null\n expandedIds: Set<string>\n}\n\nexport function normalizePath(path: string): string {\n return normalizeSlash(stripQueryAndHash(path))\n}\n\nexport function matchActivePath(menu: SidebarMenuItem[], activePath: string): SidebarMatchResult {\n const normalizedActivePath = normalizePath(activePath || '/')\n const expandedIds = new Set<string>()\n let activeId: string | null = null\n\n const walk = (items: SidebarMenuItem[], ancestors: string[]): boolean => {\n for (const item of items) {\n const currentId = String(item.id)\n const normalizedNodePath = item.path ? normalizePath(item.path) : ''\n const directMatch = normalizedNodePath && normalizedNodePath === normalizedActivePath\n const childMatch = Array.isArray(item.children) ? walk(item.children, [...ancestors, currentId]) : false\n if (directMatch || childMatch) {\n activeId = directMatch ? currentId : activeId\n for (const parentId of ancestors) {\n expandedIds.add(parentId)\n }\n if (childMatch) expandedIds.add(currentId)\n return true\n }\n }\n return false\n }\n\n walk(menu, [])\n return { activeId, expandedIds }\n}\n","import type { SidebarConfig } from '../types/config'\n\nexport function resolveNavigationPath(path: string): string {\n const clean = String(path || '').trim()\n if (!clean) return '#'\n return clean\n}\n\nexport function navigateToPath(path: string, config: SidebarConfig): void {\n if (!config.autoNavigate) return\n const resolvedPath = resolveNavigationPath(path)\n if (resolvedPath === '#') return\n if (config.target === '_blank') {\n window.open(resolvedPath, '_blank', 'noopener,noreferrer')\n return\n }\n if (config.target === '_top') {\n try {\n if (window.top) {\n window.top.location.href = resolvedPath\n return\n }\n } catch {\n window.location.href = resolvedPath\n return\n }\n }\n window.location.href = resolvedPath\n}\n","import type { SidebarMenuItem } from '../types/menu'\n\nexport interface SidebarState {\n loading: boolean\n menu: SidebarMenuItem[]\n error: string\n activeId: string | null\n expandedIds: Set<string>\n}\n\nexport function createInitialState(): SidebarState {\n return {\n loading: false,\n menu: [],\n error: '',\n activeId: null,\n expandedIds: new Set<string>()\n }\n}\n","import type { SidebarConfig, SidebarConfigInput } from '../types/config'\nimport type { SidebarMenuItem } from '../types/menu'\nimport { fetchSidebarMenu } from '../core/api'\nimport { getAriaCurrent, getNavigationContainerAttributes, isActivationKey } from '../core/a11y'\nimport { mergeConfig, normalizeConfig, parseConfigFromAttributes } from '../core/config'\nimport { emitSidebarError, emitSidebarItemClick, emitSidebarLoaded } from '../core/events'\nimport { matchActivePath } from '../core/matcher'\nimport { navigateToPath } from '../core/navigation'\nimport { createInitialState } from '../core/state'\nimport sidebarStyles from '../styles/sidebar.css?inline'\n\ntype SidebarRenderRoot = ShadowRoot | HTMLElement\n\nexport class PrismSidebarElement extends HTMLElement {\n static observedAttributes = [\n 'token',\n 'api-base-url',\n 'active-path',\n 'target',\n 'use-shadow-dom',\n 'aria-label',\n 'collapsed',\n 'auto-navigate',\n 'width',\n 'title',\n 'subtitle',\n 'home-url',\n 'logout-text',\n 'logout-path',\n 'error'\n ]\n\n private config: SidebarConfig = normalizeConfig({})\n private state = createInitialState()\n private renderRoot: SidebarRenderRoot | null = null\n private abortController: AbortController | null = null\n private refreshSequence = 0\n\n get token(): string { return this.config.token }\n set token(value: string) { this.setConfig({ token: value }) }\n get apiBaseUrl(): string { return this.config.apiBaseUrl }\n set apiBaseUrl(value: string) { this.setConfig({ apiBaseUrl: value }) }\n get activePath(): string { return this.config.activePath }\n set activePath(value: string) { this.setActivePath(value) }\n get target(): SidebarConfig['target'] { return this.config.target }\n set target(value: SidebarConfig['target']) { this.setConfig({ target: value }) }\n get useShadowDom(): boolean { return this.config.useShadowDom }\n set useShadowDom(value: boolean) { this.setConfig({ useShadowDom: value }) }\n override get ariaLabel(): string { return this.config.ariaLabel }\n override set ariaLabel(value: string) { this.setConfig({ ariaLabel: value }) }\n get collapsed(): boolean { return this.config.collapsed }\n set collapsed(value: boolean) { this.setConfig({ collapsed: value }) }\n get autoNavigate(): boolean { return this.config.autoNavigate }\n set autoNavigate(value: boolean) { this.setConfig({ autoNavigate: value }) }\n get width(): string { return this.config.width }\n set width(value: string) { this.setConfig({ width: value }) }\n override get title(): string { return this.config.title }\n override set title(value: string) { this.setConfig({ title: value }) }\n get subtitle(): string { return this.config.subtitle }\n set subtitle(value: string) { this.setConfig({ subtitle: value }) }\n get homeUrl(): string { return this.config.homeUrl }\n set homeUrl(value: string) { this.setConfig({ homeUrl: value }) }\n get logoutText(): string { return this.config.logoutText }\n set logoutText(value: string) { this.setConfig({ logoutText: value }) }\n get logoutPath(): string { return this.config.logoutPath }\n set logoutPath(value: string) { this.setConfig({ logoutPath: value }) }\n get error(): string { return this.config.error }\n set error(value: string) { this.setConfig({ error: value }) }\n\n connectedCallback(): void {\n this.syncConfigFromAttributes()\n this.ensureRenderRoot()\n this.render()\n this.refresh().catch(() => undefined)\n }\n\n disconnectedCallback(): void {\n if (this.abortController) this.abortController.abort()\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return\n this.syncConfigFromAttributes()\n if (name === 'use-shadow-dom') {\n this.ensureRenderRoot()\n }\n this.render()\n if (name === 'token' || name === 'api-base-url') {\n this.refresh().catch(() => undefined)\n return\n }\n if (name === 'active-path') {\n this.applyMatchState()\n this.render()\n }\n }\n\n async refresh(): Promise<void> {\n const refreshSequence = ++this.refreshSequence\n this.state.loading = true\n this.state.error = ''\n this.render()\n if (this.abortController) this.abortController.abort()\n this.abortController = new AbortController()\n try {\n const menu = await fetchSidebarMenu(this.config, this.abortController.signal)\n if (refreshSequence !== this.refreshSequence) return\n this.state.menu = menu\n this.state.loading = false\n this.state.error = ''\n this.applyMatchState()\n this.render()\n emitSidebarLoaded(this, { menu })\n } catch (error) {\n if (refreshSequence !== this.refreshSequence) return\n if (error instanceof Error && error.name === 'AbortError') return\n const message = error instanceof Error ? error.message : 'Failed to load menu'\n this.state.loading = false\n this.state.menu = []\n this.state.error = message\n this.render()\n emitSidebarError(this, { message })\n }\n }\n\n setActivePath(path: string): void {\n this.config = mergeConfig(this.config, { activePath: path })\n this.applyMatchState()\n this.render()\n }\n\n setConfig(partialConfig: SidebarConfigInput): void {\n const previousUseShadowDom = this.config.useShadowDom\n this.config = mergeConfig(this.config, partialConfig)\n if (previousUseShadowDom !== this.config.useShadowDom) {\n this.ensureRenderRoot()\n }\n this.applyMatchState()\n this.render()\n }\n\n private syncConfigFromAttributes(): void {\n this.config = mergeConfig(this.config, parseConfigFromAttributes(this))\n }\n\n private ensureRenderRoot(): void {\n if (this.config.useShadowDom) {\n this.renderRoot = this.shadowRoot ?? this.attachShadow({ mode: 'open' })\n return\n }\n this.renderRoot = this\n }\n\n private applyMatchState(): void {\n const activePath = this.config.activePath || window.location.pathname\n const { activeId, expandedIds } = matchActivePath(this.state.menu, activePath)\n this.state.activeId = activeId\n this.state.expandedIds = expandedIds\n }\n\n private resolveItemState(item: SidebarMenuItem): { active: boolean; expanded: boolean } {\n const id = String(item.id)\n return {\n active: this.state.activeId === id,\n expanded: this.state.expandedIds.has(id)\n }\n }\n\n private render(): void {\n if (!this.renderRoot) return\n this.style.setProperty('width', this.config.width)\n this.style.setProperty('min-width', this.config.width)\n this.style.setProperty('--prism-sidebar-width', this.config.width)\n const navAttrs = getNavigationContainerAttributes(this.config.ariaLabel)\n const shell = `\n <style>${sidebarStyles}</style>\n <aside class=\"prism-sidebar\" style=\"--prism-sidebar-width: ${this.config.width};\">\n ${this.renderHeader()}\n <nav class=\"prism-nav\" role=\"${navAttrs.role}\" aria-label=\"${navAttrs['aria-label']}\">\n ${this.renderBody(this.state.menu)}\n </nav>\n ${this.renderFooter()}\n </aside>\n `\n this.renderRoot.innerHTML = shell\n this.bindEvents()\n }\n\n private renderHeader(): string {\n return `\n <a href=\"${this.escapeAttribute(this.config.homeUrl)}\" class=\"prism-brand\" data-action=\"home\">\n ${this.escapeHtml(this.config.title)}\n </a>\n <div class=\"prism-subtitle\">${this.escapeHtml(this.config.subtitle)}</div>\n `\n }\n\n private renderFooter(): string {\n return `\n <div class=\"prism-footer\">\n <button type=\"button\" class=\"prism-logout\" data-action=\"logout\">\n <span class=\"prism-item-icon\">${this.getLogoutSvg()}</span>\n ${this.escapeHtml(this.config.logoutText)}\n </button>\n </div>\n `\n }\n\n private renderBody(menu: SidebarMenuItem[]): string {\n const errorMessage = this.config.error\n if (this.state.loading) return '<div class=\"prism-state\">Loading...</div>'\n if (errorMessage) return `<div class=\"prism-error\">${errorMessage}</div>`\n if (!menu.length) return '<div class=\"prism-empty\">No menu available</div>'\n return menu.map((item) => this.renderNode(item)).join('')\n }\n\n private renderNode(item: SidebarMenuItem): string {\n const state = this.resolveItemState(item)\n const hasChildren = Boolean(item.children?.length)\n if (hasChildren) {\n const expanded = state.expanded || !this.config.collapsed\n return `\n <div class=\"prism-group\">\n <button type=\"button\" class=\"prism-group-title\" data-action=\"toggle\" data-id=\"${item.id}\" aria-expanded=\"${expanded}\">\n <span>${item.name}</span>\n <span class=\"prism-chevron ${expanded ? 'prism-chevron-open' : ''}\">${this.getChevronSvg()}</span>\n </button>\n <div class=\"prism-children\" ${expanded ? '' : 'hidden'}>\n ${(item.children ?? []).map((childItem) => this.renderNode(childItem)).join('')}\n </div>\n </div>\n `\n }\n const ariaCurrent = getAriaCurrent(state.active)\n return `\n <button\n type=\"button\"\n class=\"prism-item ${state.active ? 'active' : ''}\"\n data-action=\"navigate\"\n data-id=\"${item.id}\"\n data-path=\"${item.path ?? ''}\"\n ${ariaCurrent ? `aria-current=\"${ariaCurrent}\"` : ''}\n >\n <span class=\"prism-item-icon\">${this.getAppstoreSvg()}</span>\n ${item.name}\n </button>\n `\n }\n\n private bindEvents(): void {\n if (!this.renderRoot) return\n this.renderRoot.querySelectorAll<HTMLElement>('[data-action]').forEach((node) => {\n node.addEventListener('click', (event) => this.onActionClick(event))\n node.addEventListener('keydown', (event) => {\n if (!isActivationKey(event)) return\n event.preventDefault()\n this.onActionClick(event)\n })\n })\n }\n\n private onActionClick(event: Event): void {\n const target = event.currentTarget\n if (!(target instanceof HTMLElement)) return\n const action = target.dataset.action\n if (action === 'home') {\n this.navigateWithoutGuard(this.config.homeUrl)\n return\n }\n if (action === 'logout') {\n this.navigateWithoutGuard(this.config.logoutPath)\n return\n }\n const id = target.dataset.id\n if (!id) return\n if (action === 'toggle') {\n if (this.state.expandedIds.has(id)) this.state.expandedIds.delete(id)\n else this.state.expandedIds.add(id)\n this.render()\n return\n }\n if (action !== 'navigate') return\n const clickedItem = this.findMenuItemById(id, this.state.menu)\n if (!clickedItem || !clickedItem.path) return\n this.state.activeId = id\n this.render()\n emitSidebarItemClick(this, {\n item: clickedItem,\n resolvedPath: clickedItem.path,\n target: this.config.target\n })\n navigateToPath(clickedItem.path, this.config)\n }\n\n private navigateWithoutGuard(path: string): void {\n const resolvedPath = String(path || '').trim()\n if (!resolvedPath || resolvedPath === '#') return\n if (this.config.target === '_blank') {\n window.open(resolvedPath, '_blank', 'noopener,noreferrer')\n return\n }\n if (this.config.target === '_top') {\n try {\n if (window.top) {\n window.top.location.href = resolvedPath\n return\n }\n } catch {\n window.location.href = resolvedPath\n return\n }\n }\n window.location.href = resolvedPath\n }\n\n private escapeHtml(value: string): string {\n return String(value || '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n }\n\n private escapeAttribute(value: string): string {\n return this.escapeHtml(value).replace(/\"/g, '&quot;')\n }\n\n private getAppstoreSvg(): string {\n return '<span class=\"anticon anticon-appstore\"><svg viewBox=\"64 64 896 896\" width=\"1em\" height=\"1em\" fill=\"currentColor\"><path d=\"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z\"></path></svg></span>'\n }\n\n private getLogoutSvg(): string {\n return '<span class=\"anticon anticon-logout\"><svg viewBox=\"64 64 896 896\" focusable=\"false\" data-icon=\"logout\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z\"></path></svg></span>'\n }\n\n private getChevronSvg(): string {\n return '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>'\n }\n\n private findMenuItemById(id: string, menu: SidebarMenuItem[]): SidebarMenuItem | null {\n for (const item of menu) {\n if (String(item.id) === id) return item\n if (item.children?.length) {\n const found = this.findMenuItemById(id, item.children)\n if (found) return found\n }\n }\n return null\n }\n}\n","import { PrismSidebarElement } from './components/prism-sidebar'\n\nexport const PRISM_SIDEBAR_TAG_NAME = 'prism-sidebar'\n\nexport function registerPrismSidebar(): void {\n if (customElements.get(PRISM_SIDEBAR_TAG_NAME)) return\n customElements.define(PRISM_SIDEBAR_TAG_NAME, PrismSidebarElement)\n}\n"],"names":["isMenuItem","value","item","sanitizeItems","items","sanitized","extractMenu","payload","response","fetchSidebarMenu","config","signal","requestInit","getNavigationContainerAttributes","ariaLabel","getAriaCurrent","isActive","isActivationKey","event","normalizeSlash","stripped","stripQueryAndHash","path","toBoolean","fallback","normalized","DEFAULT_CONFIG","VALID_TARGETS","parseConfigFromAttributes","element","parseTarget","token","apiBaseUrl","activePath","useShadowDom","collapsed","autoNavigate","width","title","subtitle","homeUrl","logoutText","logoutPath","error","normalizeConfig","input","target","mergeConfig","base","partial","next","key","candidate","SIDEBAR_EVENTS","emitSidebarLoaded","detail","emitSidebarError","emitSidebarItemClick","normalizePath","matchActivePath","menu","normalizedActivePath","expandedIds","activeId","walk","ancestors","currentId","normalizedNodePath","directMatch","childMatch","parentId","resolveNavigationPath","clean","navigateToPath","resolvedPath","createInitialState","PrismSidebarElement","name","oldValue","newValue","refreshSequence","message","partialConfig","previousUseShadowDom","id","navAttrs","shell","sidebarStyles","errorMessage","state","expanded","childItem","ariaCurrent","node","action","clickedItem","found","PRISM_SIDEBAR_TAG_NAME","registerPrismSidebar"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ (function(a,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(a=typeof globalThis<"u"?globalThis:a||self,u(a.PrismSidebar={}))})(this,(function(a){"use strict";function u(e){if(!e||typeof e!="object")return!1;const t=e;return typeof t.id<"u"&&typeof t.name=="string"}function b(e){return e.filter(t=>u(t)).map(t=>{const i={...t,id:String(t.id)};return Array.isArray(t.children)&&(i.children=b(t.children)),i})}function M(e){if(Array.isArray(e))return b(e);if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.data?.menu)?b(t.data.menu):Array.isArray(t.menu)?b(t.menu):[]}async function $(e,t){if(!e.apiBaseUrl)throw new Error("Missing apiBaseUrl");if(!e.token)throw new Error("Missing token");const i={headers:{Authorization:`Bearer ${e.token}`}};t&&(i.signal=t);const r=await fetch(e.apiBaseUrl,i);if(!r.ok)throw new Error(`Menu API failed with ${r.status}`);let o;try{o=await r.json()}catch{throw new Error("Menu API returned non-JSON payload")}return M(o)}function z(e){return{role:"navigation","aria-label":e}}function B(e){return e?"page":null}function T(e){return e.key==="Enter"||e.key===" "}function S(e){const t=e.trim();return t&&t.replace(/\/+$/,"")||"/"}function U(e){return e.split("?")[0]?.split("#")[0]??e}function c(e,t){if(typeof e=="boolean")return e;if(e==null||e==="")return t;const i=String(e).toLowerCase();return!["0","false","no","off"].includes(i)}const n={token:"",apiBaseUrl:"",activePath:"",target:"_self",useShadowDom:!0,ariaLabel:"Sidebar navigation",collapsed:!0,autoNavigate:!0,width:"240px",title:"P.R.I.S.M.",subtitle:"Purplle Retail Intelligence & Sales Management",homeUrl:"/",logoutText:"Logout",logoutPath:"/sentinel/logout",error:""},L=["_self","_blank","_top"];function R(e){const t={target:k(e.getAttribute("target"))},i=e.getAttribute("token");i!==null&&(t.token=i);const r=e.getAttribute("api-base-url");r!==null&&(t.apiBaseUrl=r);const o=e.getAttribute("active-path");o!==null&&(t.activePath=o);const s=e.getAttribute("aria-label");s!==null&&(t.ariaLabel=s);const d=e.getAttribute("use-shadow-dom");d!==null&&(t.useShadowDom=c(d,!0));const f=e.getAttribute("collapsed");f!==null&&(t.collapsed=c(f,!1));const l=e.getAttribute("auto-navigate");l!==null&&(t.autoNavigate=c(l,!0));const h=e.getAttribute("width");h!==null&&(t.width=h);const g=e.getAttribute("title");g!==null&&(t.title=g);const p=e.getAttribute("subtitle");p!==null&&(t.subtitle=p);const m=e.getAttribute("home-url");m!==null&&(t.homeUrl=m);const v=e.getAttribute("logout-text");v!==null&&(t.logoutText=v);const I=e.getAttribute("logout-path");I!==null&&(t.logoutPath=I);const E=e.getAttribute("error");return E!==null&&(t.error=E),t}function A(e){const t=k(e.target);return{token:e.token?.trim()??n.token,apiBaseUrl:e.apiBaseUrl?.replace(/\/+$/,"")??n.apiBaseUrl,activePath:e.activePath?S(e.activePath):n.activePath,target:t,useShadowDom:c(e.useShadowDom,n.useShadowDom),ariaLabel:e.ariaLabel?.trim()||n.ariaLabel,collapsed:c(e.collapsed,n.collapsed),autoNavigate:c(e.autoNavigate,n.autoNavigate),width:e.width?.trim()||n.width,title:e.title?.trim()||n.title,subtitle:e.subtitle?.trim()||n.subtitle,homeUrl:e.homeUrl?.trim()||n.homeUrl,logoutText:e.logoutText?.trim()||n.logoutText,logoutPath:e.logoutPath?.trim()||n.logoutPath,error:e.error?.trim()||n.error}}function w(e,t){const i={...e};for(const r of Object.keys(t)){const o=t[r];typeof o>"u"||(i[r]=o)}return A(i)}function k(e){if(!e)return n.target;const t=e;return L.includes(t)?t:n.target}const x={loaded:"sidebar-loaded",error:"sidebar-error",itemClick:"sidebar-item-click"};function N(e,t){e.dispatchEvent(new CustomEvent(x.loaded,{detail:t,bubbles:!0,composed:!0}))}function H(e,t){e.dispatchEvent(new CustomEvent(x.error,{detail:t,bubbles:!0,composed:!0}))}function D(e,t){e.dispatchEvent(new CustomEvent(x.itemClick,{detail:t,bubbles:!0,composed:!0}))}function C(e){return S(U(e))}function _(e,t){const i=C(t||"/"),r=new Set;let o=null;const s=(d,f)=>{for(const l of d){const h=String(l.id),g=l.path?C(l.path):"",p=g&&g===i,m=Array.isArray(l.children)?s(l.children,[...f,h]):!1;if(p||m){o=p?h:o;for(const v of f)r.add(v);return m&&r.add(h),!0}}return!1};return s(e,[]),{activeId:o,expandedIds:r}}function j(e){const t=String(e||"").trim();return t||"#"}function F(e,t){if(!t.autoNavigate)return;const i=j(e);if(i!=="#"){if(t.target==="_blank"){window.open(i,"_blank","noopener,noreferrer");return}if(t.target==="_top")try{if(window.top){window.top.location.href=i;return}}catch{window.location.href=i;return}window.location.href=i}}function V(){return{loading:!1,menu:[],error:"",activeId:null,expandedIds:new Set}}const q=':host{--prism-sidebar-width: 240px;--prism-sidebar-bg: linear-gradient(rgb(156,0,173),rgb(89,0,99));--prism-sidebar-text-color: #ffffff;--prism-sidebar-border-color: rgba(255,255,255,.15);--prism-sidebar-hover-bg: rgba(255,255,255,.12);--prism-sidebar-active-bg: rgba(255,255,255,.1);--prism-sidebar-indent: 14px;--prism-sidebar-font-family: Inter,sans-serif,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial;line-height:1.5;display:block}.prism-sidebar{position:fixed;left:0;top:0;bottom:0;z-index:1600;width:var(--prism-sidebar-width);min-width:var(--prism-sidebar-width);box-sizing:border-box;background:var(--prism-sidebar-bg);color:var(--prism-sidebar-text-color);font-family:var(--prism-sidebar-font-family);padding:20px;height:100vh;overflow:hidden;display:flex;flex-direction:column;gap:12px}.prism-brand{text-align:center;color:inherit;text-decoration:none;font-size:30px;line-height:36px;font-weight:600;letter-spacing:2px;padding:0 10px;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.prism-subtitle{text-align:center;font-size:12px;line-height:16px;padding:0 10px;margin-bottom:16px}.prism-nav{flex:1 1 0%;display:flex;flex-direction:column;gap:4px;overflow-y:auto;-ms-overflow-style:none;scrollbar-width:none}.prism-nav::-webkit-scrollbar{display:none}.prism-footer{margin-top:8px;padding-top:8px;border-top:1px solid var(--prism-sidebar-border-color)}.prism-logout{width:100%;border:0;background:transparent;color:inherit;display:flex;align-items:center;gap:10px;text-align:left;font-size:12.5px;line-height:17px;font-weight:600;padding:8px 12px;border-radius:6px;cursor:pointer}.prism-state,.prism-empty,.prism-error{padding:8px 10px;font-size:14px}.prism-error{color:#ffd0d0}.prism-group{margin-bottom:4px}.prism-group-title{width:100%;background:transparent;border:0;color:#fffffff2;display:flex;align-items:center;justify-content:space-between;text-align:left;padding:10px 12px;border-radius:6px;font-size:13px;line-height:18px;font-weight:600;cursor:pointer}.prism-group-title:hover{background:var(--prism-sidebar-hover-bg)}.prism-brand:hover{opacity:.9}.prism-logout:hover{background:var(--prism-sidebar-hover-bg);color:#fff}.prism-children{margin-left:12px;margin-top:4px}.prism-item{width:100%;border:0;background:transparent;color:#fffc;display:flex;align-items:center;gap:10px;font-size:12.5px;line-height:17px;font-weight:500;padding:8px 12px;border-radius:6px;cursor:pointer}.prism-item:hover{color:#fff}.prism-item-icon{display:flex;flex-shrink:0;font-size:16px}.prism-chevron{display:flex;flex-shrink:0;transition:transform .2s cubic-bezier(.4,0,.2,1)}.prism-chevron-open{transform:rotate(180deg)}.prism-item.active{background:var(--prism-sidebar-active-bg);font-weight:600}.prism-item:focus-visible,.prism-group-title:focus-visible,.prism-brand:focus-visible,.prism-logout:focus-visible{outline:2px solid #ffffff;outline-offset:2px}';class P extends HTMLElement{static observedAttributes=["token","api-base-url","active-path","target","use-shadow-dom","aria-label","collapsed","auto-navigate","width","title","subtitle","home-url","logout-text","logout-path","error"];config=A({});state=V();renderRoot=null;abortController=null;refreshSequence=0;get token(){return this.config.token}set token(t){this.setConfig({token:t})}get apiBaseUrl(){return this.config.apiBaseUrl}set apiBaseUrl(t){this.setConfig({apiBaseUrl:t})}get activePath(){return this.config.activePath}set activePath(t){this.setActivePath(t)}get target(){return this.config.target}set target(t){this.setConfig({target:t})}get useShadowDom(){return this.config.useShadowDom}set useShadowDom(t){this.setConfig({useShadowDom:t})}get ariaLabel(){return this.config.ariaLabel}set ariaLabel(t){this.setConfig({ariaLabel:t})}get collapsed(){return this.config.collapsed}set collapsed(t){this.setConfig({collapsed:t})}get autoNavigate(){return this.config.autoNavigate}set autoNavigate(t){this.setConfig({autoNavigate:t})}get width(){return this.config.width}set width(t){this.setConfig({width:t})}get title(){return this.config.title}set title(t){this.setConfig({title:t})}get subtitle(){return this.config.subtitle}set subtitle(t){this.setConfig({subtitle:t})}get homeUrl(){return this.config.homeUrl}set homeUrl(t){this.setConfig({homeUrl:t})}get logoutText(){return this.config.logoutText}set logoutText(t){this.setConfig({logoutText:t})}get logoutPath(){return this.config.logoutPath}set logoutPath(t){this.setConfig({logoutPath:t})}get error(){return this.config.error}set error(t){this.setConfig({error:t})}connectedCallback(){this.syncConfigFromAttributes(),this.ensureRenderRoot(),this.render(),this.refresh().catch(()=>{})}disconnectedCallback(){this.abortController&&this.abortController.abort()}attributeChangedCallback(t,i,r){if(i!==r){if(this.syncConfigFromAttributes(),t==="use-shadow-dom"&&this.ensureRenderRoot(),this.render(),t==="token"||t==="api-base-url"){this.refresh().catch(()=>{});return}t==="active-path"&&(this.applyMatchState(),this.render())}}async refresh(){const t=++this.refreshSequence;this.state.loading=!0,this.state.error="",this.render(),this.abortController&&this.abortController.abort(),this.abortController=new AbortController;try{const i=await $(this.config,this.abortController.signal);if(t!==this.refreshSequence)return;this.state.menu=i,this.state.loading=!1,this.state.error="",this.applyMatchState(),this.render(),N(this,{menu:i})}catch(i){if(t!==this.refreshSequence||i instanceof Error&&i.name==="AbortError")return;const r=i instanceof Error?i.message:"Failed to load menu";this.state.loading=!1,this.state.menu=[],this.state.error=r,this.render(),H(this,{message:r})}}setActivePath(t){this.config=w(this.config,{activePath:t}),this.applyMatchState(),this.render()}setConfig(t){const i=this.config.useShadowDom;this.config=w(this.config,t),i!==this.config.useShadowDom&&this.ensureRenderRoot(),this.applyMatchState(),this.render()}syncConfigFromAttributes(){this.config=w(this.config,R(this))}ensureRenderRoot(){if(this.config.useShadowDom){this.renderRoot=this.shadowRoot??this.attachShadow({mode:"open"});return}this.renderRoot=this}applyMatchState(){const t=this.config.activePath||window.location.pathname,{activeId:i,expandedIds:r}=_(this.state.menu,t);this.state.activeId=i,this.state.expandedIds=r}resolveItemState(t){const i=String(t.id);return{active:this.state.activeId===i,expanded:this.state.expandedIds.has(i)}}render(){if(!this.renderRoot)return;this.style.setProperty("width",this.config.width),this.style.setProperty("min-width",this.config.width),this.style.setProperty("--prism-sidebar-width",this.config.width);const t=z(this.config.ariaLabel),i=`
2
+ <style>${q}</style>
3
+ <aside class="prism-sidebar" style="--prism-sidebar-width: ${this.config.width};">
4
+ ${this.renderHeader()}
5
+ <nav class="prism-nav" role="${t.role}" aria-label="${t["aria-label"]}">
6
+ ${this.renderBody(this.state.menu)}
7
+ </nav>
8
+ ${this.renderFooter()}
9
+ </aside>
10
+ `;this.renderRoot.innerHTML=i,this.bindEvents()}renderHeader(){return`
11
+ <a href="${this.escapeAttribute(this.config.homeUrl)}" class="prism-brand" data-action="home">
12
+ ${this.escapeHtml(this.config.title)}
13
+ </a>
14
+ <div class="prism-subtitle">${this.escapeHtml(this.config.subtitle)}</div>
15
+ `}renderFooter(){return`
16
+ <div class="prism-footer">
17
+ <button type="button" class="prism-logout" data-action="logout">
18
+ <span class="prism-item-icon">${this.getLogoutSvg()}</span>
19
+ ${this.escapeHtml(this.config.logoutText)}
20
+ </button>
21
+ </div>
22
+ `}renderBody(t){const i=this.config.error;return this.state.loading?'<div class="prism-state">Loading...</div>':i?`<div class="prism-error">${i}</div>`:t.length?t.map(r=>this.renderNode(r)).join(""):'<div class="prism-empty">No menu available</div>'}renderNode(t){const i=this.resolveItemState(t);if(!!t.children?.length){const s=i.expanded||!this.config.collapsed;return`
23
+ <div class="prism-group">
24
+ <button type="button" class="prism-group-title" data-action="toggle" data-id="${t.id}" aria-expanded="${s}">
25
+ <span>${t.name}</span>
26
+ <span class="prism-chevron ${s?"prism-chevron-open":""}">${this.getChevronSvg()}</span>
27
+ </button>
28
+ <div class="prism-children" ${s?"":"hidden"}>
29
+ ${(t.children??[]).map(d=>this.renderNode(d)).join("")}
30
+ </div>
31
+ </div>
32
+ `}const o=B(i.active);return`
33
+ <button
34
+ type="button"
35
+ class="prism-item ${i.active?"active":""}"
36
+ data-action="navigate"
37
+ data-id="${t.id}"
38
+ data-path="${t.path??""}"
39
+ ${o?`aria-current="${o}"`:""}
40
+ >
41
+ <span class="prism-item-icon">${this.getAppstoreSvg()}</span>
42
+ ${t.name}
43
+ </button>
44
+ `}bindEvents(){this.renderRoot&&this.renderRoot.querySelectorAll("[data-action]").forEach(t=>{t.addEventListener("click",i=>this.onActionClick(i)),t.addEventListener("keydown",i=>{T(i)&&(i.preventDefault(),this.onActionClick(i))})})}onActionClick(t){const i=t.currentTarget;if(!(i instanceof HTMLElement))return;const r=i.dataset.action;if(r==="home"){this.navigateWithoutGuard(this.config.homeUrl);return}if(r==="logout"){this.navigateWithoutGuard(this.config.logoutPath);return}const o=i.dataset.id;if(!o)return;if(r==="toggle"){this.state.expandedIds.has(o)?this.state.expandedIds.delete(o):this.state.expandedIds.add(o),this.render();return}if(r!=="navigate")return;const s=this.findMenuItemById(o,this.state.menu);!s||!s.path||(this.state.activeId=o,this.render(),D(this,{item:s,resolvedPath:s.path,target:this.config.target}),F(s.path,this.config))}navigateWithoutGuard(t){const i=String(t||"").trim();if(!(!i||i==="#")){if(this.config.target==="_blank"){window.open(i,"_blank","noopener,noreferrer");return}if(this.config.target==="_top")try{if(window.top){window.top.location.href=i;return}}catch{window.location.href=i;return}window.location.href=i}}escapeHtml(t){return String(t||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}escapeAttribute(t){return this.escapeHtml(t).replace(/"/g,"&quot;")}getAppstoreSvg(){return'<span class="anticon anticon-appstore"><svg viewBox="64 64 896 896" width="1em" height="1em" fill="currentColor"><path d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"></path></svg></span>'}getLogoutSvg(){return'<span class="anticon anticon-logout"><svg viewBox="64 64 896 896" focusable="false" data-icon="logout" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z"></path></svg></span>'}getChevronSvg(){return'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>'}findMenuItemById(t,i){for(const r of i){if(String(r.id)===t)return r;if(r.children?.length){const o=this.findMenuItemById(t,r.children);if(o)return o}}return null}}const y="prism-sidebar";function G(){customElements.get(y)||customElements.define(y,P)}a.PRISM_SIDEBAR_TAG_NAME=y,a.PrismSidebarElement=P,a.registerPrismSidebar=G,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
45
+ //# sourceMappingURL=prism-sidebar.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-sidebar.umd.js","sources":["../src/core/api.ts","../src/core/a11y.ts","../src/core/utils.ts","../src/core/config.ts","../src/core/events.ts","../src/core/matcher.ts","../src/core/navigation.ts","../src/core/state.ts","../src/components/prism-sidebar.ts","../src/register.ts"],"sourcesContent":["import type { SidebarConfig } from '../types/config'\nimport type { SidebarMenuApiResponse, SidebarMenuItem } from '../types/menu'\n\nfunction isMenuItem(value: unknown): value is SidebarMenuItem {\n if (!value || typeof value !== 'object') return false\n const item = value as Partial<SidebarMenuItem>\n return typeof item.id !== 'undefined' && typeof item.name === 'string'\n}\n\nfunction sanitizeItems(items: unknown[]): SidebarMenuItem[] {\n return items\n .filter((item): item is SidebarMenuItem => isMenuItem(item))\n .map((item) => {\n const sanitized: SidebarMenuItem = {\n ...item,\n id: String(item.id)\n }\n if (Array.isArray(item.children)) {\n sanitized.children = sanitizeItems(item.children)\n }\n return sanitized\n })\n}\n\nexport function extractMenu(payload: unknown): SidebarMenuItem[] {\n if (Array.isArray(payload)) return sanitizeItems(payload)\n if (!payload || typeof payload !== 'object') return []\n const response = payload as SidebarMenuApiResponse\n if (Array.isArray(response.data?.menu)) return sanitizeItems(response.data.menu)\n if (Array.isArray(response.menu)) return sanitizeItems(response.menu)\n return []\n}\n\nexport async function fetchSidebarMenu(config: SidebarConfig, signal?: AbortSignal): Promise<SidebarMenuItem[]> {\n if (!config.apiBaseUrl) throw new Error('Missing apiBaseUrl')\n if (!config.token) throw new Error('Missing token')\n const requestInit: RequestInit = {\n headers: {\n Authorization: `Bearer ${config.token}`\n }\n }\n if (signal) requestInit.signal = signal\n const response = await fetch(config.apiBaseUrl, requestInit)\n if (!response.ok) {\n throw new Error(`Menu API failed with ${response.status}`)\n }\n let payload: unknown\n try {\n payload = await response.json()\n } catch {\n throw new Error('Menu API returned non-JSON payload')\n }\n return extractMenu(payload)\n}\n","export function getNavigationContainerAttributes(ariaLabel: string): Record<string, string> {\n return {\n role: 'navigation',\n 'aria-label': ariaLabel\n }\n}\n\nexport function getAriaCurrent(isActive: boolean): string | null {\n return isActive ? 'page' : null\n}\n\nexport function isActivationKey(event: KeyboardEvent): boolean {\n return event.key === 'Enter' || event.key === ' '\n}\n","export function normalizeSlash(value: string): string {\n const stripped = value.trim()\n if (!stripped) return '/'\n const noTrailingSlash = stripped.replace(/\\/+$/, '')\n return noTrailingSlash || '/'\n}\n\nexport function stripQueryAndHash(path: string): string {\n return path.split('?')[0]?.split('#')[0] ?? path\n}\n\nexport function toBoolean(value: string | boolean | null | undefined, fallback: boolean): boolean {\n if (typeof value === 'boolean') return value\n if (value === null || value === undefined || value === '') return fallback\n const normalized = String(value).toLowerCase()\n return !['0', 'false', 'no', 'off'].includes(normalized)\n}\n","import type { SidebarConfig, SidebarConfigInput, SidebarNavigationTarget } from '../types/config'\nimport { normalizeSlash, toBoolean } from './utils'\n\nconst DEFAULT_CONFIG: SidebarConfig = {\n token: '',\n apiBaseUrl: '',\n activePath: '',\n target: '_self',\n useShadowDom: true,\n ariaLabel: 'Sidebar navigation',\n collapsed: true,\n autoNavigate: true,\n width: '240px',\n title: 'P.R.I.S.M.',\n subtitle: 'Purplle Retail Intelligence & Sales Management',\n homeUrl: '/',\n logoutText: 'Logout',\n logoutPath: '/sentinel/logout',\n error: ''\n}\n\nconst VALID_TARGETS: SidebarNavigationTarget[] = ['_self', '_blank', '_top']\n\nexport function parseConfigFromAttributes(element: HTMLElement): SidebarConfigInput {\n const config: SidebarConfigInput = {\n target: parseTarget(element.getAttribute('target'))\n }\n const token = element.getAttribute('token')\n if (token !== null) config.token = token\n const apiBaseUrl = element.getAttribute('api-base-url')\n if (apiBaseUrl !== null) config.apiBaseUrl = apiBaseUrl\n const activePath = element.getAttribute('active-path')\n if (activePath !== null) config.activePath = activePath\n const ariaLabel = element.getAttribute('aria-label')\n if (ariaLabel !== null) config.ariaLabel = ariaLabel\n const useShadowDom = element.getAttribute('use-shadow-dom')\n if (useShadowDom !== null) config.useShadowDom = toBoolean(useShadowDom, true)\n const collapsed = element.getAttribute('collapsed')\n if (collapsed !== null) config.collapsed = toBoolean(collapsed, false)\n const autoNavigate = element.getAttribute('auto-navigate')\n if (autoNavigate !== null) config.autoNavigate = toBoolean(autoNavigate, true)\n const width = element.getAttribute('width')\n if (width !== null) config.width = width\n const title = element.getAttribute('title')\n if (title !== null) config.title = title\n const subtitle = element.getAttribute('subtitle')\n if (subtitle !== null) config.subtitle = subtitle\n const homeUrl = element.getAttribute('home-url')\n if (homeUrl !== null) config.homeUrl = homeUrl\n const logoutText = element.getAttribute('logout-text')\n if (logoutText !== null) config.logoutText = logoutText\n const logoutPath = element.getAttribute('logout-path')\n if (logoutPath !== null) config.logoutPath = logoutPath\n const error = element.getAttribute('error')\n if (error !== null) config.error = error\n return config\n}\n\nexport function normalizeConfig(input: SidebarConfigInput): SidebarConfig {\n const target = parseTarget(input.target)\n return {\n token: input.token?.trim() ?? DEFAULT_CONFIG.token,\n apiBaseUrl: input.apiBaseUrl?.replace(/\\/+$/, '') ?? DEFAULT_CONFIG.apiBaseUrl,\n activePath: input.activePath ? normalizeSlash(input.activePath) : DEFAULT_CONFIG.activePath,\n target,\n useShadowDom: toBoolean(input.useShadowDom, DEFAULT_CONFIG.useShadowDom),\n ariaLabel: input.ariaLabel?.trim() || DEFAULT_CONFIG.ariaLabel,\n collapsed: toBoolean(input.collapsed, DEFAULT_CONFIG.collapsed),\n autoNavigate: toBoolean(input.autoNavigate, DEFAULT_CONFIG.autoNavigate),\n width: input.width?.trim() || DEFAULT_CONFIG.width,\n title: input.title?.trim() || DEFAULT_CONFIG.title,\n subtitle: input.subtitle?.trim() || DEFAULT_CONFIG.subtitle,\n homeUrl: input.homeUrl?.trim() || DEFAULT_CONFIG.homeUrl,\n logoutText: input.logoutText?.trim() || DEFAULT_CONFIG.logoutText,\n logoutPath: input.logoutPath?.trim() || DEFAULT_CONFIG.logoutPath,\n error: input.error?.trim() || DEFAULT_CONFIG.error\n }\n}\n\nexport function mergeConfig(base: SidebarConfig, partial: SidebarConfigInput): SidebarConfig {\n const next = { ...base } as Record<string, unknown>\n for (const key of Object.keys(partial) as Array<keyof SidebarConfigInput>) {\n const value = partial[key]\n if (typeof value === 'undefined') continue\n next[key as string] = value\n }\n return normalizeConfig(next as SidebarConfigInput)\n}\n\nfunction parseTarget(value: string | null | undefined): SidebarNavigationTarget {\n if (!value) return DEFAULT_CONFIG.target\n const candidate = value as SidebarNavigationTarget\n return VALID_TARGETS.includes(candidate) ? candidate : DEFAULT_CONFIG.target\n}\n","import type {\n SidebarErrorEventDetail,\n SidebarItemClickEventDetail,\n SidebarLoadedEventDetail\n} from '../types/events'\n\nexport const SIDEBAR_EVENTS = {\n loaded: 'sidebar-loaded',\n error: 'sidebar-error',\n itemClick: 'sidebar-item-click'\n} as const\n\nexport function emitSidebarLoaded(target: HTMLElement, detail: SidebarLoadedEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.loaded, { detail, bubbles: true, composed: true }))\n}\n\nexport function emitSidebarError(target: HTMLElement, detail: SidebarErrorEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.error, { detail, bubbles: true, composed: true }))\n}\n\nexport function emitSidebarItemClick(target: HTMLElement, detail: SidebarItemClickEventDetail): void {\n target.dispatchEvent(new CustomEvent(SIDEBAR_EVENTS.itemClick, { detail, bubbles: true, composed: true }))\n}\n","import type { SidebarMenuItem } from '../types/menu'\nimport { normalizeSlash, stripQueryAndHash } from './utils'\n\nexport interface SidebarMatchResult {\n activeId: string | null\n expandedIds: Set<string>\n}\n\nexport function normalizePath(path: string): string {\n return normalizeSlash(stripQueryAndHash(path))\n}\n\nexport function matchActivePath(menu: SidebarMenuItem[], activePath: string): SidebarMatchResult {\n const normalizedActivePath = normalizePath(activePath || '/')\n const expandedIds = new Set<string>()\n let activeId: string | null = null\n\n const walk = (items: SidebarMenuItem[], ancestors: string[]): boolean => {\n for (const item of items) {\n const currentId = String(item.id)\n const normalizedNodePath = item.path ? normalizePath(item.path) : ''\n const directMatch = normalizedNodePath && normalizedNodePath === normalizedActivePath\n const childMatch = Array.isArray(item.children) ? walk(item.children, [...ancestors, currentId]) : false\n if (directMatch || childMatch) {\n activeId = directMatch ? currentId : activeId\n for (const parentId of ancestors) {\n expandedIds.add(parentId)\n }\n if (childMatch) expandedIds.add(currentId)\n return true\n }\n }\n return false\n }\n\n walk(menu, [])\n return { activeId, expandedIds }\n}\n","import type { SidebarConfig } from '../types/config'\n\nexport function resolveNavigationPath(path: string): string {\n const clean = String(path || '').trim()\n if (!clean) return '#'\n return clean\n}\n\nexport function navigateToPath(path: string, config: SidebarConfig): void {\n if (!config.autoNavigate) return\n const resolvedPath = resolveNavigationPath(path)\n if (resolvedPath === '#') return\n if (config.target === '_blank') {\n window.open(resolvedPath, '_blank', 'noopener,noreferrer')\n return\n }\n if (config.target === '_top') {\n try {\n if (window.top) {\n window.top.location.href = resolvedPath\n return\n }\n } catch {\n window.location.href = resolvedPath\n return\n }\n }\n window.location.href = resolvedPath\n}\n","import type { SidebarMenuItem } from '../types/menu'\n\nexport interface SidebarState {\n loading: boolean\n menu: SidebarMenuItem[]\n error: string\n activeId: string | null\n expandedIds: Set<string>\n}\n\nexport function createInitialState(): SidebarState {\n return {\n loading: false,\n menu: [],\n error: '',\n activeId: null,\n expandedIds: new Set<string>()\n }\n}\n","import type { SidebarConfig, SidebarConfigInput } from '../types/config'\nimport type { SidebarMenuItem } from '../types/menu'\nimport { fetchSidebarMenu } from '../core/api'\nimport { getAriaCurrent, getNavigationContainerAttributes, isActivationKey } from '../core/a11y'\nimport { mergeConfig, normalizeConfig, parseConfigFromAttributes } from '../core/config'\nimport { emitSidebarError, emitSidebarItemClick, emitSidebarLoaded } from '../core/events'\nimport { matchActivePath } from '../core/matcher'\nimport { navigateToPath } from '../core/navigation'\nimport { createInitialState } from '../core/state'\nimport sidebarStyles from '../styles/sidebar.css?inline'\n\ntype SidebarRenderRoot = ShadowRoot | HTMLElement\n\nexport class PrismSidebarElement extends HTMLElement {\n static observedAttributes = [\n 'token',\n 'api-base-url',\n 'active-path',\n 'target',\n 'use-shadow-dom',\n 'aria-label',\n 'collapsed',\n 'auto-navigate',\n 'width',\n 'title',\n 'subtitle',\n 'home-url',\n 'logout-text',\n 'logout-path',\n 'error'\n ]\n\n private config: SidebarConfig = normalizeConfig({})\n private state = createInitialState()\n private renderRoot: SidebarRenderRoot | null = null\n private abortController: AbortController | null = null\n private refreshSequence = 0\n\n get token(): string { return this.config.token }\n set token(value: string) { this.setConfig({ token: value }) }\n get apiBaseUrl(): string { return this.config.apiBaseUrl }\n set apiBaseUrl(value: string) { this.setConfig({ apiBaseUrl: value }) }\n get activePath(): string { return this.config.activePath }\n set activePath(value: string) { this.setActivePath(value) }\n get target(): SidebarConfig['target'] { return this.config.target }\n set target(value: SidebarConfig['target']) { this.setConfig({ target: value }) }\n get useShadowDom(): boolean { return this.config.useShadowDom }\n set useShadowDom(value: boolean) { this.setConfig({ useShadowDom: value }) }\n override get ariaLabel(): string { return this.config.ariaLabel }\n override set ariaLabel(value: string) { this.setConfig({ ariaLabel: value }) }\n get collapsed(): boolean { return this.config.collapsed }\n set collapsed(value: boolean) { this.setConfig({ collapsed: value }) }\n get autoNavigate(): boolean { return this.config.autoNavigate }\n set autoNavigate(value: boolean) { this.setConfig({ autoNavigate: value }) }\n get width(): string { return this.config.width }\n set width(value: string) { this.setConfig({ width: value }) }\n override get title(): string { return this.config.title }\n override set title(value: string) { this.setConfig({ title: value }) }\n get subtitle(): string { return this.config.subtitle }\n set subtitle(value: string) { this.setConfig({ subtitle: value }) }\n get homeUrl(): string { return this.config.homeUrl }\n set homeUrl(value: string) { this.setConfig({ homeUrl: value }) }\n get logoutText(): string { return this.config.logoutText }\n set logoutText(value: string) { this.setConfig({ logoutText: value }) }\n get logoutPath(): string { return this.config.logoutPath }\n set logoutPath(value: string) { this.setConfig({ logoutPath: value }) }\n get error(): string { return this.config.error }\n set error(value: string) { this.setConfig({ error: value }) }\n\n connectedCallback(): void {\n this.syncConfigFromAttributes()\n this.ensureRenderRoot()\n this.render()\n this.refresh().catch(() => undefined)\n }\n\n disconnectedCallback(): void {\n if (this.abortController) this.abortController.abort()\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue) return\n this.syncConfigFromAttributes()\n if (name === 'use-shadow-dom') {\n this.ensureRenderRoot()\n }\n this.render()\n if (name === 'token' || name === 'api-base-url') {\n this.refresh().catch(() => undefined)\n return\n }\n if (name === 'active-path') {\n this.applyMatchState()\n this.render()\n }\n }\n\n async refresh(): Promise<void> {\n const refreshSequence = ++this.refreshSequence\n this.state.loading = true\n this.state.error = ''\n this.render()\n if (this.abortController) this.abortController.abort()\n this.abortController = new AbortController()\n try {\n const menu = await fetchSidebarMenu(this.config, this.abortController.signal)\n if (refreshSequence !== this.refreshSequence) return\n this.state.menu = menu\n this.state.loading = false\n this.state.error = ''\n this.applyMatchState()\n this.render()\n emitSidebarLoaded(this, { menu })\n } catch (error) {\n if (refreshSequence !== this.refreshSequence) return\n if (error instanceof Error && error.name === 'AbortError') return\n const message = error instanceof Error ? error.message : 'Failed to load menu'\n this.state.loading = false\n this.state.menu = []\n this.state.error = message\n this.render()\n emitSidebarError(this, { message })\n }\n }\n\n setActivePath(path: string): void {\n this.config = mergeConfig(this.config, { activePath: path })\n this.applyMatchState()\n this.render()\n }\n\n setConfig(partialConfig: SidebarConfigInput): void {\n const previousUseShadowDom = this.config.useShadowDom\n this.config = mergeConfig(this.config, partialConfig)\n if (previousUseShadowDom !== this.config.useShadowDom) {\n this.ensureRenderRoot()\n }\n this.applyMatchState()\n this.render()\n }\n\n private syncConfigFromAttributes(): void {\n this.config = mergeConfig(this.config, parseConfigFromAttributes(this))\n }\n\n private ensureRenderRoot(): void {\n if (this.config.useShadowDom) {\n this.renderRoot = this.shadowRoot ?? this.attachShadow({ mode: 'open' })\n return\n }\n this.renderRoot = this\n }\n\n private applyMatchState(): void {\n const activePath = this.config.activePath || window.location.pathname\n const { activeId, expandedIds } = matchActivePath(this.state.menu, activePath)\n this.state.activeId = activeId\n this.state.expandedIds = expandedIds\n }\n\n private resolveItemState(item: SidebarMenuItem): { active: boolean; expanded: boolean } {\n const id = String(item.id)\n return {\n active: this.state.activeId === id,\n expanded: this.state.expandedIds.has(id)\n }\n }\n\n private render(): void {\n if (!this.renderRoot) return\n this.style.setProperty('width', this.config.width)\n this.style.setProperty('min-width', this.config.width)\n this.style.setProperty('--prism-sidebar-width', this.config.width)\n const navAttrs = getNavigationContainerAttributes(this.config.ariaLabel)\n const shell = `\n <style>${sidebarStyles}</style>\n <aside class=\"prism-sidebar\" style=\"--prism-sidebar-width: ${this.config.width};\">\n ${this.renderHeader()}\n <nav class=\"prism-nav\" role=\"${navAttrs.role}\" aria-label=\"${navAttrs['aria-label']}\">\n ${this.renderBody(this.state.menu)}\n </nav>\n ${this.renderFooter()}\n </aside>\n `\n this.renderRoot.innerHTML = shell\n this.bindEvents()\n }\n\n private renderHeader(): string {\n return `\n <a href=\"${this.escapeAttribute(this.config.homeUrl)}\" class=\"prism-brand\" data-action=\"home\">\n ${this.escapeHtml(this.config.title)}\n </a>\n <div class=\"prism-subtitle\">${this.escapeHtml(this.config.subtitle)}</div>\n `\n }\n\n private renderFooter(): string {\n return `\n <div class=\"prism-footer\">\n <button type=\"button\" class=\"prism-logout\" data-action=\"logout\">\n <span class=\"prism-item-icon\">${this.getLogoutSvg()}</span>\n ${this.escapeHtml(this.config.logoutText)}\n </button>\n </div>\n `\n }\n\n private renderBody(menu: SidebarMenuItem[]): string {\n const errorMessage = this.config.error\n if (this.state.loading) return '<div class=\"prism-state\">Loading...</div>'\n if (errorMessage) return `<div class=\"prism-error\">${errorMessage}</div>`\n if (!menu.length) return '<div class=\"prism-empty\">No menu available</div>'\n return menu.map((item) => this.renderNode(item)).join('')\n }\n\n private renderNode(item: SidebarMenuItem): string {\n const state = this.resolveItemState(item)\n const hasChildren = Boolean(item.children?.length)\n if (hasChildren) {\n const expanded = state.expanded || !this.config.collapsed\n return `\n <div class=\"prism-group\">\n <button type=\"button\" class=\"prism-group-title\" data-action=\"toggle\" data-id=\"${item.id}\" aria-expanded=\"${expanded}\">\n <span>${item.name}</span>\n <span class=\"prism-chevron ${expanded ? 'prism-chevron-open' : ''}\">${this.getChevronSvg()}</span>\n </button>\n <div class=\"prism-children\" ${expanded ? '' : 'hidden'}>\n ${(item.children ?? []).map((childItem) => this.renderNode(childItem)).join('')}\n </div>\n </div>\n `\n }\n const ariaCurrent = getAriaCurrent(state.active)\n return `\n <button\n type=\"button\"\n class=\"prism-item ${state.active ? 'active' : ''}\"\n data-action=\"navigate\"\n data-id=\"${item.id}\"\n data-path=\"${item.path ?? ''}\"\n ${ariaCurrent ? `aria-current=\"${ariaCurrent}\"` : ''}\n >\n <span class=\"prism-item-icon\">${this.getAppstoreSvg()}</span>\n ${item.name}\n </button>\n `\n }\n\n private bindEvents(): void {\n if (!this.renderRoot) return\n this.renderRoot.querySelectorAll<HTMLElement>('[data-action]').forEach((node) => {\n node.addEventListener('click', (event) => this.onActionClick(event))\n node.addEventListener('keydown', (event) => {\n if (!isActivationKey(event)) return\n event.preventDefault()\n this.onActionClick(event)\n })\n })\n }\n\n private onActionClick(event: Event): void {\n const target = event.currentTarget\n if (!(target instanceof HTMLElement)) return\n const action = target.dataset.action\n if (action === 'home') {\n this.navigateWithoutGuard(this.config.homeUrl)\n return\n }\n if (action === 'logout') {\n this.navigateWithoutGuard(this.config.logoutPath)\n return\n }\n const id = target.dataset.id\n if (!id) return\n if (action === 'toggle') {\n if (this.state.expandedIds.has(id)) this.state.expandedIds.delete(id)\n else this.state.expandedIds.add(id)\n this.render()\n return\n }\n if (action !== 'navigate') return\n const clickedItem = this.findMenuItemById(id, this.state.menu)\n if (!clickedItem || !clickedItem.path) return\n this.state.activeId = id\n this.render()\n emitSidebarItemClick(this, {\n item: clickedItem,\n resolvedPath: clickedItem.path,\n target: this.config.target\n })\n navigateToPath(clickedItem.path, this.config)\n }\n\n private navigateWithoutGuard(path: string): void {\n const resolvedPath = String(path || '').trim()\n if (!resolvedPath || resolvedPath === '#') return\n if (this.config.target === '_blank') {\n window.open(resolvedPath, '_blank', 'noopener,noreferrer')\n return\n }\n if (this.config.target === '_top') {\n try {\n if (window.top) {\n window.top.location.href = resolvedPath\n return\n }\n } catch {\n window.location.href = resolvedPath\n return\n }\n }\n window.location.href = resolvedPath\n }\n\n private escapeHtml(value: string): string {\n return String(value || '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n }\n\n private escapeAttribute(value: string): string {\n return this.escapeHtml(value).replace(/\"/g, '&quot;')\n }\n\n private getAppstoreSvg(): string {\n return '<span class=\"anticon anticon-appstore\"><svg viewBox=\"64 64 896 896\" width=\"1em\" height=\"1em\" fill=\"currentColor\"><path d=\"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z\"></path></svg></span>'\n }\n\n private getLogoutSvg(): string {\n return '<span class=\"anticon anticon-logout\"><svg viewBox=\"64 64 896 896\" focusable=\"false\" data-icon=\"logout\" width=\"1em\" height=\"1em\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z\"></path></svg></span>'\n }\n\n private getChevronSvg(): string {\n return '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>'\n }\n\n private findMenuItemById(id: string, menu: SidebarMenuItem[]): SidebarMenuItem | null {\n for (const item of menu) {\n if (String(item.id) === id) return item\n if (item.children?.length) {\n const found = this.findMenuItemById(id, item.children)\n if (found) return found\n }\n }\n return null\n }\n}\n","import { PrismSidebarElement } from './components/prism-sidebar'\n\nexport const PRISM_SIDEBAR_TAG_NAME = 'prism-sidebar'\n\nexport function registerPrismSidebar(): void {\n if (customElements.get(PRISM_SIDEBAR_TAG_NAME)) return\n customElements.define(PRISM_SIDEBAR_TAG_NAME, PrismSidebarElement)\n}\n"],"names":["isMenuItem","value","item","sanitizeItems","items","sanitized","extractMenu","payload","response","fetchSidebarMenu","config","signal","requestInit","getNavigationContainerAttributes","ariaLabel","getAriaCurrent","isActive","isActivationKey","event","normalizeSlash","stripped","stripQueryAndHash","path","toBoolean","fallback","normalized","DEFAULT_CONFIG","VALID_TARGETS","parseConfigFromAttributes","element","parseTarget","token","apiBaseUrl","activePath","useShadowDom","collapsed","autoNavigate","width","title","subtitle","homeUrl","logoutText","logoutPath","error","normalizeConfig","input","target","mergeConfig","base","partial","next","key","candidate","SIDEBAR_EVENTS","emitSidebarLoaded","detail","emitSidebarError","emitSidebarItemClick","normalizePath","matchActivePath","menu","normalizedActivePath","expandedIds","activeId","walk","ancestors","currentId","normalizedNodePath","directMatch","childMatch","parentId","resolveNavigationPath","clean","navigateToPath","resolvedPath","createInitialState","PrismSidebarElement","name","oldValue","newValue","refreshSequence","message","partialConfig","previousUseShadowDom","id","navAttrs","shell","sidebarStyles","errorMessage","state","expanded","childItem","ariaCurrent","node","action","clickedItem","found","PRISM_SIDEBAR_TAG_NAME","registerPrismSidebar"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@avesh.k/prism-sidebar",
3
+ "version": "1.0.0",
4
+ "description": "Framework-agnostic Prism sidebar web component package",
5
+ "type": "module",
6
+ "main": "./dist/prism-sidebar.umd.js",
7
+ "module": "./dist/prism-sidebar.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/prism-sidebar.js",
13
+ "require": "./dist/prism-sidebar.umd.js"
14
+ },
15
+ "./register": {
16
+ "types": "./dist/register.d.ts",
17
+ "import": "./dist/register.js"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "CHANGELOG.md"
25
+ ],
26
+ "sideEffects": [
27
+ "**/*.css"
28
+ ],
29
+ "scripts": {
30
+ "dev": "vite",
31
+ "build": "tsc -p tsconfig.build.json && vite build",
32
+ "preview": "vite preview",
33
+ "lint": "eslint .",
34
+ "format": "prettier --write .",
35
+ "test": "vitest run",
36
+ "test:unit": "vitest run src/test/unit",
37
+ "test:e2e": "playwright test",
38
+ "typecheck": "tsc -p tsconfig.json --noEmit"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/your-org/prism-sidebar.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/your-org/prism-sidebar/issues"
46
+ },
47
+ "homepage": "https://github.com/your-org/prism-sidebar#readme",
48
+ "license": "UNLICENSED",
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "devDependencies": {
56
+ "@eslint/js": "^9.0.0",
57
+ "@playwright/test": "^1.54.0",
58
+ "@types/node": "^24.0.0",
59
+ "eslint": "^9.0.0",
60
+ "eslint-config-prettier": "^10.1.5",
61
+ "globals": "^16.0.0",
62
+ "jsdom": "^26.0.0",
63
+ "prettier": "^3.6.0",
64
+ "typescript": "^5.8.0",
65
+ "typescript-eslint": "^8.0.0",
66
+ "vite": "^7.0.0",
67
+ "vitest": "^3.0.0"
68
+ }
69
+ }