@stacksjs/components 0.2.112 → 0.2.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Sidebar-bg47fqmq.stx → Sidebar-hjazqxv8.stx} +29 -18
- package/dist/index.js +7 -7
- package/dist/ui/sidebar/route-selection.d.ts +1 -0
- package/dist/ui/sidebar/themes.d.ts +0 -1
- package/package.json +4 -4
- package/src/ui/sidebar/Sidebar.stx +29 -18
- package/src/ui/sidebar/route-selection.ts +32 -0
- package/src/ui/sidebar/themes.ts +1 -12
|
@@ -31,7 +31,6 @@ export const sidebarUid = $props.id || `stx-sidebar-${Math.random().toString(36)
|
|
|
31
31
|
const themeStyle = resolveSidebarTheme(theme)
|
|
32
32
|
export const selectionModel = themeStyle.selection
|
|
33
33
|
export const hasShimmerRim = themeStyle.layers.shimmerRim === true
|
|
34
|
-
export const isFrosted = themeStyle.layers.frosted === true
|
|
35
34
|
export const tintClasses = themeStyle.layers.tint || ''
|
|
36
35
|
export const scrollAreaClasses = themeStyle.scrollArea
|
|
37
36
|
|
|
@@ -68,6 +67,8 @@ export const activeClasses = themeStyle.item.active
|
|
|
68
67
|
</script>
|
|
69
68
|
|
|
70
69
|
<script client>
|
|
70
|
+
import { findActiveSidebarHref } from './route-selection'
|
|
71
|
+
|
|
71
72
|
const emit = defineEmits()
|
|
72
73
|
const sidebarUidProp = '{{ sidebarUid }}'
|
|
73
74
|
const collapsedProp = {{ collapsed }}
|
|
@@ -101,12 +102,33 @@ function setActiveRow(button) {
|
|
|
101
102
|
const classes = activeClassList()
|
|
102
103
|
pane.querySelectorAll('[data-sidebar-item][data-active="true"]').forEach((el) => {
|
|
103
104
|
el.removeAttribute('data-active')
|
|
105
|
+
el.removeAttribute('aria-current')
|
|
104
106
|
el.classList.remove(...classes)
|
|
105
107
|
})
|
|
106
108
|
button.setAttribute('data-active', 'true')
|
|
109
|
+
button.setAttribute('aria-current', 'page')
|
|
107
110
|
button.classList.add(...classes)
|
|
108
111
|
}
|
|
109
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
|
+
|
|
110
132
|
// ---------------------------------------------------------------------------
|
|
111
133
|
// Disclosure — rows know their ancestors (data-parents="a/b"). Collapsing a
|
|
112
134
|
// row (or section) marks every descendant with data-collapsed, which the
|
|
@@ -324,18 +346,22 @@ onMount(() => {
|
|
|
324
346
|
|
|
325
347
|
pane.addEventListener('click', onClick)
|
|
326
348
|
pane.addEventListener('keydown', onKeyDown)
|
|
349
|
+
window.addEventListener('stx:navigate', onRouteChange)
|
|
350
|
+
window.addEventListener('popstate', onRouteChange)
|
|
327
351
|
|
|
328
352
|
const scrollArea = pane.querySelector('[data-sidebar-scroll]')
|
|
329
353
|
const thumb = pane.querySelector('[data-sidebar-scroll-thumb]')
|
|
330
354
|
if (scrollArea && thumb) attachOverlayScrollbar(scrollArea, thumb)
|
|
331
355
|
|
|
332
|
-
//
|
|
356
|
+
// Route-derived selection wins over stale server-declared state on direct
|
|
357
|
+
// loads, nested routes, SPA transitions, and browser history navigation.
|
|
358
|
+
syncRouteSelection()
|
|
333
359
|
const active = pane.querySelector('[data-sidebar-item][data-active="true"]')
|
|
334
360
|
if (active) active.classList.add(...activeClassList())
|
|
335
361
|
syncRowVisibility()
|
|
336
362
|
})
|
|
337
363
|
|
|
338
|
-
defineExpose({ collapsed, toggle: toggleCollapse })
|
|
364
|
+
defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
|
|
339
365
|
</script>
|
|
340
366
|
|
|
341
367
|
<aside
|
|
@@ -343,7 +369,6 @@ defineExpose({ collapsed, toggle: toggleCollapse })
|
|
|
343
369
|
data-stx-sidebar
|
|
344
370
|
data-sidebar-theme="{{ theme }}"
|
|
345
371
|
data-sidebar-selection="{{ selectionModel }}"
|
|
346
|
-
@if(isFrosted)data-sidebar-frosted@endif
|
|
347
372
|
data-window-key="true"
|
|
348
373
|
data-active-class="{{ activeClasses }}"
|
|
349
374
|
class="{{ paneClasses }}"
|
|
@@ -454,20 +479,6 @@ defineExpose({ collapsed, toggle: toggleCollapse })
|
|
|
454
479
|
.stx-sidebar-rim::after { animation: none; }
|
|
455
480
|
}
|
|
456
481
|
|
|
457
|
-
/* Frosted sidebar material. Hand-written CSS on purpose: as the utilities
|
|
458
|
-
`backdrop-blur-[50px]` / `backdrop-saturate-[180%]` the arbitrary values
|
|
459
|
-
did not survive class extraction in the stx dev server, leaving the pane
|
|
460
|
-
with `backdrop-filter: none`; an earlier `backdrop-saturate-[1.7]` lost
|
|
461
|
-
its brackets on the way to the generator, was read as a percentage, and
|
|
462
|
-
compiled to `saturate(0.017)`, which drained the colour out of the whole
|
|
463
|
-
backdrop. In a Craft window with webSidebarMaterial the native
|
|
464
|
-
NSVisualEffectView supplies the real vibrancy; this approximates it
|
|
465
|
-
everywhere else. */
|
|
466
|
-
[data-sidebar-frosted] {
|
|
467
|
-
-webkit-backdrop-filter: saturate(180%) blur(50px);
|
|
468
|
-
backdrop-filter: saturate(180%) blur(50px);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
482
|
/* ---------------------------------------------------------------------------
|
|
472
483
|
* Selection, `accent` model (macOS and its derivatives).
|
|
473
484
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
2
|
+
// ../../../../../../../tmp/stx-components-build-FWtLPS/stubs/stub-0.js
|
|
3
3
|
var stub_0_default = { __stx: true, src: "./components/CodeBlock.stx" };
|
|
4
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
4
|
+
// ../../../../../../../tmp/stx-components-build-FWtLPS/stubs/stub-1.js
|
|
5
5
|
var stub_1_default = { __stx: true, src: "./components/Footer.stx" };
|
|
6
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
6
|
+
// ../../../../../../../tmp/stx-components-build-FWtLPS/stubs/stub-2.js
|
|
7
7
|
var stub_2_default = { __stx: true, src: "./components/Hero.stx" };
|
|
8
|
-
// ../../../../../../../tmp/stx-components-build-
|
|
8
|
+
// ../../../../../../../tmp/stx-components-build-FWtLPS/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-
|
|
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
|
|
@@ -1159,13 +1159,13 @@ var macosColors = {
|
|
|
1159
1159
|
var macos = {
|
|
1160
1160
|
pane: [
|
|
1161
1161
|
"bg-[#f2f2f4]/90 dark:bg-[#1c1c1e]/88",
|
|
1162
|
+
"backdrop-blur-[50px] backdrop-saturate-[180%]",
|
|
1162
1163
|
"text-black dark:text-white",
|
|
1163
1164
|
"select-none"
|
|
1164
1165
|
].join(" "),
|
|
1165
1166
|
selection: "accent",
|
|
1166
1167
|
layers: {
|
|
1167
|
-
shimmerRim: true
|
|
1168
|
-
frosted: true
|
|
1168
|
+
shimmerRim: true
|
|
1169
1169
|
},
|
|
1170
1170
|
scrollArea: "flex-1 overflow-y-auto overflow-x-hidden px-[10px] pb-[10px]",
|
|
1171
1171
|
sectionHeader: [
|
|
@@ -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.
|
|
4
|
+
"version": "0.2.114",
|
|
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",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"prepublishOnly": "bun run build"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@cwcss/crosswind": "^0.2.
|
|
66
|
-
"@stacksjs/stx": "0.2.
|
|
67
|
-
"bun-plugin-stx": "0.2.
|
|
65
|
+
"@cwcss/crosswind": "^0.2.12",
|
|
66
|
+
"@stacksjs/stx": "0.2.114",
|
|
67
|
+
"bun-plugin-stx": "0.2.114",
|
|
68
68
|
"ts-syntax-highlighter": "^0.2.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
@@ -31,7 +31,6 @@ export const sidebarUid = $props.id || `stx-sidebar-${Math.random().toString(36)
|
|
|
31
31
|
const themeStyle = resolveSidebarTheme(theme)
|
|
32
32
|
export const selectionModel = themeStyle.selection
|
|
33
33
|
export const hasShimmerRim = themeStyle.layers.shimmerRim === true
|
|
34
|
-
export const isFrosted = themeStyle.layers.frosted === true
|
|
35
34
|
export const tintClasses = themeStyle.layers.tint || ''
|
|
36
35
|
export const scrollAreaClasses = themeStyle.scrollArea
|
|
37
36
|
|
|
@@ -68,6 +67,8 @@ export const activeClasses = themeStyle.item.active
|
|
|
68
67
|
</script>
|
|
69
68
|
|
|
70
69
|
<script client>
|
|
70
|
+
import { findActiveSidebarHref } from './route-selection'
|
|
71
|
+
|
|
71
72
|
const emit = defineEmits()
|
|
72
73
|
const sidebarUidProp = '{{ sidebarUid }}'
|
|
73
74
|
const collapsedProp = {{ collapsed }}
|
|
@@ -101,12 +102,33 @@ function setActiveRow(button) {
|
|
|
101
102
|
const classes = activeClassList()
|
|
102
103
|
pane.querySelectorAll('[data-sidebar-item][data-active="true"]').forEach((el) => {
|
|
103
104
|
el.removeAttribute('data-active')
|
|
105
|
+
el.removeAttribute('aria-current')
|
|
104
106
|
el.classList.remove(...classes)
|
|
105
107
|
})
|
|
106
108
|
button.setAttribute('data-active', 'true')
|
|
109
|
+
button.setAttribute('aria-current', 'page')
|
|
107
110
|
button.classList.add(...classes)
|
|
108
111
|
}
|
|
109
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
|
+
|
|
110
132
|
// ---------------------------------------------------------------------------
|
|
111
133
|
// Disclosure — rows know their ancestors (data-parents="a/b"). Collapsing a
|
|
112
134
|
// row (or section) marks every descendant with data-collapsed, which the
|
|
@@ -324,18 +346,22 @@ onMount(() => {
|
|
|
324
346
|
|
|
325
347
|
pane.addEventListener('click', onClick)
|
|
326
348
|
pane.addEventListener('keydown', onKeyDown)
|
|
349
|
+
window.addEventListener('stx:navigate', onRouteChange)
|
|
350
|
+
window.addEventListener('popstate', onRouteChange)
|
|
327
351
|
|
|
328
352
|
const scrollArea = pane.querySelector('[data-sidebar-scroll]')
|
|
329
353
|
const thumb = pane.querySelector('[data-sidebar-scroll-thumb]')
|
|
330
354
|
if (scrollArea && thumb) attachOverlayScrollbar(scrollArea, thumb)
|
|
331
355
|
|
|
332
|
-
//
|
|
356
|
+
// Route-derived selection wins over stale server-declared state on direct
|
|
357
|
+
// loads, nested routes, SPA transitions, and browser history navigation.
|
|
358
|
+
syncRouteSelection()
|
|
333
359
|
const active = pane.querySelector('[data-sidebar-item][data-active="true"]')
|
|
334
360
|
if (active) active.classList.add(...activeClassList())
|
|
335
361
|
syncRowVisibility()
|
|
336
362
|
})
|
|
337
363
|
|
|
338
|
-
defineExpose({ collapsed, toggle: toggleCollapse })
|
|
364
|
+
defineExpose({ collapsed, toggle: toggleCollapse, syncRouteSelection })
|
|
339
365
|
</script>
|
|
340
366
|
|
|
341
367
|
<aside
|
|
@@ -343,7 +369,6 @@ defineExpose({ collapsed, toggle: toggleCollapse })
|
|
|
343
369
|
data-stx-sidebar
|
|
344
370
|
data-sidebar-theme="{{ theme }}"
|
|
345
371
|
data-sidebar-selection="{{ selectionModel }}"
|
|
346
|
-
@if(isFrosted)data-sidebar-frosted@endif
|
|
347
372
|
data-window-key="true"
|
|
348
373
|
data-active-class="{{ activeClasses }}"
|
|
349
374
|
class="{{ paneClasses }}"
|
|
@@ -454,20 +479,6 @@ defineExpose({ collapsed, toggle: toggleCollapse })
|
|
|
454
479
|
.stx-sidebar-rim::after { animation: none; }
|
|
455
480
|
}
|
|
456
481
|
|
|
457
|
-
/* Frosted sidebar material. Hand-written CSS on purpose: as the utilities
|
|
458
|
-
`backdrop-blur-[50px]` / `backdrop-saturate-[180%]` the arbitrary values
|
|
459
|
-
did not survive class extraction in the stx dev server, leaving the pane
|
|
460
|
-
with `backdrop-filter: none`; an earlier `backdrop-saturate-[1.7]` lost
|
|
461
|
-
its brackets on the way to the generator, was read as a percentage, and
|
|
462
|
-
compiled to `saturate(0.017)`, which drained the colour out of the whole
|
|
463
|
-
backdrop. In a Craft window with webSidebarMaterial the native
|
|
464
|
-
NSVisualEffectView supplies the real vibrancy; this approximates it
|
|
465
|
-
everywhere else. */
|
|
466
|
-
[data-sidebar-frosted] {
|
|
467
|
-
-webkit-backdrop-filter: saturate(180%) blur(50px);
|
|
468
|
-
backdrop-filter: saturate(180%) blur(50px);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
482
|
/* ---------------------------------------------------------------------------
|
|
472
483
|
* Selection, `accent` model (macOS and its derivatives).
|
|
473
484
|
*
|
|
@@ -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
|
+
}
|
package/src/ui/sidebar/themes.ts
CHANGED
|
@@ -67,8 +67,6 @@ export interface SidebarTheme {
|
|
|
67
67
|
layers: {
|
|
68
68
|
/** Liquid Glass edge highlight — a bright rim that subtly shimmers. */
|
|
69
69
|
shimmerRim?: boolean
|
|
70
|
-
/** Frosted material behind the pane (a real `backdrop-filter`). */
|
|
71
|
-
frosted?: boolean
|
|
72
70
|
/** Soft tint gradient blended over the material. */
|
|
73
71
|
tint?: string
|
|
74
72
|
}
|
|
@@ -116,22 +114,13 @@ const macos: SidebarTheme = {
|
|
|
116
114
|
// hint of the backdrop through. A half-transparent pane reads as a web
|
|
117
115
|
// imitation, and over a dark page it inverts into a dark bar entirely.
|
|
118
116
|
'bg-[#f2f2f4]/90 dark:bg-[#1c1c1e]/88',
|
|
117
|
+
'backdrop-blur-[50px] backdrop-saturate-[180%]',
|
|
119
118
|
'text-black dark:text-white',
|
|
120
119
|
'select-none',
|
|
121
120
|
].join(' '),
|
|
122
121
|
selection: 'accent',
|
|
123
122
|
layers: {
|
|
124
123
|
shimmerRim: true,
|
|
125
|
-
// The blur itself is real CSS in Sidebar.stx rather than a utility.
|
|
126
|
-
// Expressed as `backdrop-blur-[50px]` / `backdrop-saturate-[180%]` the
|
|
127
|
-
// arbitrary values did not survive the stx dev server's class
|
|
128
|
-
// extraction: the pane computed `backdrop-filter: none`, and an earlier
|
|
129
|
-
// `backdrop-saturate-[1.7]` reached the generator with its brackets
|
|
130
|
-
// stripped, so it was read as a percentage and compiled to
|
|
131
|
-
// `saturate(0.017)` — draining the colour out of the whole backdrop.
|
|
132
|
-
// Crosswind parses and generates both correctly on its own, so the
|
|
133
|
-
// material is written as plain CSS to stay out of that path entirely.
|
|
134
|
-
frosted: true,
|
|
135
124
|
},
|
|
136
125
|
scrollArea: 'flex-1 overflow-y-auto overflow-x-hidden px-[10px] pb-[10px]',
|
|
137
126
|
sectionHeader: [
|