@stacksjs/components 0.2.113 → 0.2.115

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.
@@ -67,6 +67,8 @@ export const activeClasses = themeStyle.item.active
67
67
  </script>
68
68
 
69
69
  <script client>
70
+ import { findActiveSidebarHref } from './route-selection'
71
+
70
72
  const emit = defineEmits()
71
73
  const sidebarUidProp = '{{ sidebarUid }}'
72
74
  const collapsedProp = {{ collapsed }}
@@ -100,12 +102,33 @@ function setActiveRow(button) {
100
102
  const classes = activeClassList()
101
103
  pane.querySelectorAll('[data-sidebar-item][data-active="true"]').forEach((el) => {
102
104
  el.removeAttribute('data-active')
105
+ el.removeAttribute('aria-current')
103
106
  el.classList.remove(...classes)
104
107
  })
105
108
  button.setAttribute('data-active', 'true')
109
+ button.setAttribute('aria-current', 'page')
106
110
  button.classList.add(...classes)
107
111
  }
108
112
 
113
+ function syncRouteSelection(url) {
114
+ const links = [...pane.querySelectorAll('a[data-sidebar-item][href]')]
115
+ let currentPath = window.location.pathname
116
+ if (url) {
117
+ try {
118
+ currentPath = new URL(url, window.location.origin).pathname
119
+ }
120
+ catch {}
121
+ }
122
+ const href = findActiveSidebarHref(currentPath, links.map(link => link.getAttribute('href') || ''))
123
+ const active = href ? links.find(link => link.getAttribute('href') === href) : null
124
+ if (active)
125
+ setActiveRow(active)
126
+ }
127
+
128
+ function onRouteChange(event) {
129
+ syncRouteSelection(event?.detail?.url)
130
+ }
131
+
109
132
  // ---------------------------------------------------------------------------
110
133
  // Disclosure — rows know their ancestors (data-parents="a/b"). Collapsing a
111
134
  // row (or section) marks every descendant with data-collapsed, which the
@@ -323,18 +346,22 @@ onMount(() => {
323
346
 
324
347
  pane.addEventListener('click', onClick)
325
348
  pane.addEventListener('keydown', onKeyDown)
349
+ window.addEventListener('stx:navigate', onRouteChange)
350
+ window.addEventListener('popstate', onRouteChange)
326
351
 
327
352
  const scrollArea = pane.querySelector('[data-sidebar-scroll]')
328
353
  const thumb = pane.querySelector('[data-sidebar-scroll-thumb]')
329
354
  if (scrollArea && thumb) attachOverlayScrollbar(scrollArea, thumb)
330
355
 
331
- // Ensure the initial active row carries the theme's highlight classes.
356
+ // Route-derived selection wins over stale server-declared state on direct
357
+ // loads, nested routes, SPA transitions, and browser history navigation.
358
+ syncRouteSelection()
332
359
  const active = pane.querySelector('[data-sidebar-item][data-active="true"]')
333
360
  if (active) active.classList.add(...activeClassList())
334
361
  syncRowVisibility()
335
362
  })
336
363
 
337
- defineExpose({ collapsed, toggle: toggleCollapse })
364
+ defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
338
365
  </script>
339
366
 
340
367
  <aside
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // @bun
2
- // ../../../../../../../tmp/stx-components-build-5MMjxD/stubs/stub-0.js
2
+ // ../../../../../../../tmp/stx-components-build-mg2saM/stubs/stub-0.js
3
3
  var stub_0_default = { __stx: true, src: "./components/CodeBlock.stx" };
4
- // ../../../../../../../tmp/stx-components-build-5MMjxD/stubs/stub-1.js
4
+ // ../../../../../../../tmp/stx-components-build-mg2saM/stubs/stub-1.js
5
5
  var stub_1_default = { __stx: true, src: "./components/Footer.stx" };
6
- // ../../../../../../../tmp/stx-components-build-5MMjxD/stubs/stub-2.js
6
+ // ../../../../../../../tmp/stx-components-build-mg2saM/stubs/stub-2.js
7
7
  var stub_2_default = { __stx: true, src: "./components/Hero.stx" };
8
- // ../../../../../../../tmp/stx-components-build-5MMjxD/stubs/stub-3.js
8
+ // ../../../../../../../tmp/stx-components-build-mg2saM/stubs/stub-3.js
9
9
  var stub_3_default = { __stx: true, src: "./components/Installation.stx" };
10
10
  // src/composables/useCopyCode.ts
11
11
  function useCopyCode(options) {
@@ -1132,7 +1132,7 @@ var RadioGroupOption_default = "./RadioGroupOption-s1d6r221.stx";
1132
1132
  // src/ui/select/Select.stx
1133
1133
  var Select_default = "./Select-1vnd0brz.stx";
1134
1134
  // src/ui/sidebar/Sidebar.stx
1135
- var Sidebar_default = "./Sidebar-gbd5ctkp.stx";
1135
+ var Sidebar_default = "./Sidebar-hjazqxv8.stx";
1136
1136
  // src/ui/sidebar/SidebarFooter.stx
1137
1137
  var SidebarFooter_default = "./SidebarFooter-0aegr99p.stx";
1138
1138
  // src/ui/sidebar/SidebarHeader.stx
@@ -0,0 +1 @@
1
+ export declare function findActiveSidebarHref(currentPath: string, hrefs: string[]): string | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/components",
3
3
  "type": "module",
4
- "version": "0.2.113",
4
+ "version": "0.2.115",
5
5
  "description": "A modern component library built with STX, featuring headwind utility classes and TypeScript syntax highlighting.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -63,8 +63,8 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@cwcss/crosswind": "^0.2.12",
66
- "@stacksjs/stx": "0.2.113",
67
- "bun-plugin-stx": "0.2.113",
66
+ "@stacksjs/stx": "0.2.115",
67
+ "bun-plugin-stx": "0.2.115",
68
68
  "ts-syntax-highlighter": "^0.2.1"
69
69
  },
70
70
  "devDependencies": {
@@ -67,6 +67,8 @@ export const activeClasses = themeStyle.item.active
67
67
  </script>
68
68
 
69
69
  <script client>
70
+ import { findActiveSidebarHref } from './route-selection'
71
+
70
72
  const emit = defineEmits()
71
73
  const sidebarUidProp = '{{ sidebarUid }}'
72
74
  const collapsedProp = {{ collapsed }}
@@ -100,12 +102,33 @@ function setActiveRow(button) {
100
102
  const classes = activeClassList()
101
103
  pane.querySelectorAll('[data-sidebar-item][data-active="true"]').forEach((el) => {
102
104
  el.removeAttribute('data-active')
105
+ el.removeAttribute('aria-current')
103
106
  el.classList.remove(...classes)
104
107
  })
105
108
  button.setAttribute('data-active', 'true')
109
+ button.setAttribute('aria-current', 'page')
106
110
  button.classList.add(...classes)
107
111
  }
108
112
 
113
+ function syncRouteSelection(url) {
114
+ const links = [...pane.querySelectorAll('a[data-sidebar-item][href]')]
115
+ let currentPath = window.location.pathname
116
+ if (url) {
117
+ try {
118
+ currentPath = new URL(url, window.location.origin).pathname
119
+ }
120
+ catch {}
121
+ }
122
+ const href = findActiveSidebarHref(currentPath, links.map(link => link.getAttribute('href') || ''))
123
+ const active = href ? links.find(link => link.getAttribute('href') === href) : null
124
+ if (active)
125
+ setActiveRow(active)
126
+ }
127
+
128
+ function onRouteChange(event) {
129
+ syncRouteSelection(event?.detail?.url)
130
+ }
131
+
109
132
  // ---------------------------------------------------------------------------
110
133
  // Disclosure — rows know their ancestors (data-parents="a/b"). Collapsing a
111
134
  // row (or section) marks every descendant with data-collapsed, which the
@@ -323,18 +346,22 @@ onMount(() => {
323
346
 
324
347
  pane.addEventListener('click', onClick)
325
348
  pane.addEventListener('keydown', onKeyDown)
349
+ window.addEventListener('stx:navigate', onRouteChange)
350
+ window.addEventListener('popstate', onRouteChange)
326
351
 
327
352
  const scrollArea = pane.querySelector('[data-sidebar-scroll]')
328
353
  const thumb = pane.querySelector('[data-sidebar-scroll-thumb]')
329
354
  if (scrollArea && thumb) attachOverlayScrollbar(scrollArea, thumb)
330
355
 
331
- // Ensure the initial active row carries the theme's highlight classes.
356
+ // Route-derived selection wins over stale server-declared state on direct
357
+ // loads, nested routes, SPA transitions, and browser history navigation.
358
+ syncRouteSelection()
332
359
  const active = pane.querySelector('[data-sidebar-item][data-active="true"]')
333
360
  if (active) active.classList.add(...activeClassList())
334
361
  syncRowVisibility()
335
362
  })
336
363
 
337
- defineExpose({ collapsed, toggle: toggleCollapse })
364
+ defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
338
365
  </script>
339
366
 
340
367
  <aside
@@ -0,0 +1,32 @@
1
+ function normalizePath(path: string): string {
2
+ const clean = (path || '/').split(/[?#]/, 1)[0] || '/'
3
+ const withSlash = clean.startsWith('/') ? clean : `/${clean}`
4
+ return withSlash.replace(/\/+$/, '') || '/'
5
+ }
6
+
7
+ function isLocalHref(href: string): boolean {
8
+ return href.startsWith('/') && !href.startsWith('//')
9
+ }
10
+
11
+ export function findActiveSidebarHref(currentPath: string, hrefs: string[]): string | null {
12
+ const current = normalizePath(currentPath)
13
+ let best: string | null = null
14
+ let bestLength = -1
15
+
16
+ for (const href of hrefs) {
17
+ if (!isLocalHref(href))
18
+ continue
19
+
20
+ const candidate = normalizePath(href)
21
+ const matches = candidate === '/'
22
+ ? current === '/'
23
+ : current === candidate || current.startsWith(`${candidate}/`)
24
+
25
+ if (matches && candidate.length > bestLength) {
26
+ best = href
27
+ bestLength = candidate.length
28
+ }
29
+ }
30
+
31
+ return best
32
+ }