@kolkrabbi/kol-shell 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/AppShell.jsx +23 -10
- package/src/NavRail.jsx +46 -60
- package/src/index.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KOL application shell — fixed 48px NavRail + AppShell layout root, PageShell/PageHeader scaffolds, ContentFilters catalog organism, GridCard, SettingsScaffold, WalkthroughPanel, ShortcutsOverlay. App chrome (kol-framework owns site chrome). Nav items, content, shortcuts and settings are consumer-injected. Sits above @kolkrabbi/kol-{theme,component,framework}.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"@kolkrabbi/kol-component": ">=0.108.0",
|
|
16
|
-
"@kolkrabbi/kol-framework": ">=0.
|
|
16
|
+
"@kolkrabbi/kol-framework": ">=0.33.0",
|
|
17
17
|
"@kolkrabbi/kol-icons": ">=0.16.0",
|
|
18
|
-
"@kolkrabbi/kol-theme": ">=0.
|
|
18
|
+
"@kolkrabbi/kol-theme": ">=0.80.0",
|
|
19
19
|
"react": "^18.3.0 || ^19.0.0",
|
|
20
20
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kolkrabbi/kol-
|
|
24
|
-
"@kolkrabbi/kol-framework": "^0.30.0",
|
|
23
|
+
"@kolkrabbi/kol-framework": "^0.33.0",
|
|
25
24
|
"@kolkrabbi/kol-icons": "^0.24.0",
|
|
26
|
-
"@kolkrabbi/kol-
|
|
25
|
+
"@kolkrabbi/kol-component": "^0.118.3",
|
|
26
|
+
"@kolkrabbi/kol-theme": "^0.80.0"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src",
|
package/src/AppShell.jsx
CHANGED
|
@@ -4,17 +4,25 @@ import { NavHiddenContext } from './navHidden.js'
|
|
|
4
4
|
import TouchDeviceOverlay, { useTouchPrimary } from './TouchDeviceOverlay.jsx'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* AppShell — layout root:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* AppShell — layout root: the rail + the content column. No header, no
|
|
8
|
+
* footer. Router-agnostic: render your router's element (e.g. `<Outlet/>`) as
|
|
9
|
+
* children and wire `currentPath`/`onNavigate` from your router at the call
|
|
10
|
+
* site.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* THE RAIL IS A SIDENAV (RailSideNavPixelParity, kol-fxr 2026-08-28 — user:
|
|
13
|
+
* "same component both states super nice"): since 0.13.0 the root is the
|
|
14
|
+
* brand grid, `.kol-brand-layout` (kol-framework.css — `--kol-sidenav-w` ×
|
|
15
|
+
* `minmax(0, 1fr)`, the collapsed column on the `data-sidenav` stamp, the
|
|
16
|
+
* ≤1024 forced rail and the ≤767 drawer), and `NavRail` mounts kol-framework's
|
|
17
|
+
* `SideNav` collapsed in it. The grab edge opens it to the brand sidebar and
|
|
18
|
+
* the drag is the transition; the content column follows the grid, so the old
|
|
19
|
+
* fixed rail + `margin-left` offset is gone. `--kol-shell-rail-width`
|
|
20
|
+
* (kol-theme ≥0.80.0) is LIVE — the sidenav's width in its current state —
|
|
21
|
+
* for consumers that offset a fixed element by it; hidden rail = `0px`.
|
|
14
22
|
*
|
|
15
23
|
* @param {Array} props.items nav items `{ icon, path, label }`
|
|
16
24
|
* @param {Array} props.bottomItems items pinned below the theme toggle
|
|
17
|
-
* @param {Object} props.logomark `{ svgUrl, title }` —
|
|
25
|
+
* @param {Object} props.logomark `{ svgUrl, title }` — the rail's header mark, at the top in both states, navigates to '/'
|
|
18
26
|
* @param {string} props.currentPath the router's current pathname
|
|
19
27
|
* @param {Function} props.onNavigate `(path) => void`
|
|
20
28
|
* @param {ElementType} props.iconComponent icon renderer seam (see Button)
|
|
@@ -101,8 +109,13 @@ export default function AppShell({
|
|
|
101
109
|
|
|
102
110
|
return (
|
|
103
111
|
<NavHiddenContext.Provider value={{ navHidden, setNavHidden }}>
|
|
104
|
-
{/* `kol-app-shell` = the app tier: neutral ::selection (kol-theme)
|
|
105
|
-
|
|
112
|
+
{/* `kol-app-shell` = the app tier: neutral ::selection (kol-theme).
|
|
113
|
+
* `kol-brand-layout` = the sidenav grid; a hidden rail collapses the
|
|
114
|
+
* grid to the content column and zeroes the live width token. */}
|
|
115
|
+
<div
|
|
116
|
+
className="kol-app-shell kol-brand-layout min-h-dvh bg-surface-primary"
|
|
117
|
+
style={navHidden ? { gridTemplateColumns: 'minmax(0, 1fr)', '--kol-shell-rail-width': '0px' } : undefined}
|
|
118
|
+
>
|
|
106
119
|
{touch === 'overlay' && <TouchDeviceOverlay appName={appName} />}
|
|
107
120
|
{!navHidden && (
|
|
108
121
|
<NavRail
|
|
@@ -117,7 +130,7 @@ export default function AppShell({
|
|
|
117
130
|
)}
|
|
118
131
|
{/* THE BACK OF THE BACK — surface-primary, always, in every app; the
|
|
119
132
|
* page paints its wash over it (ShellPageWash). */}
|
|
120
|
-
<div className="bg-surface-primary" style={{
|
|
133
|
+
<div className="bg-surface-primary min-w-0" style={{ '--kol-shell-page-wash': pageWash }}>
|
|
121
134
|
{children}
|
|
122
135
|
</div>
|
|
123
136
|
</div>
|
package/src/NavRail.jsx
CHANGED
|
@@ -1,35 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ThemeToggle } from '@kolkrabbi/kol-framework'
|
|
1
|
+
import { SideNav } from '@kolkrabbi/kol-framework'
|
|
3
2
|
import Logomark from './Logomark.jsx'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* NavRail —
|
|
7
|
-
*
|
|
5
|
+
* NavRail — AppShell's rail, which IS kol-framework's `SideNav`
|
|
6
|
+
* (RailSideNavPixelParity, kol-fxr 2026-08-28 — user, on the brand sidebar's
|
|
7
|
+
* drag: "same component both states super nice"). Until 0.13.0 this was its
|
|
8
|
+
* own fixed 48px icon rail, and the first app to run it beside a collapsed
|
|
9
|
+
* SideNav on adjacent routes measured every icon moving on the route change
|
|
10
|
+
* (48 vs 56 wide, 20 vs 16px glyphs, 40 vs 38 pitch, first glyph at y 76 vs
|
|
11
|
+
* 26). There is no second component now: this file maps the shell's flat
|
|
12
|
+
* `{ icon, path, label }` items onto the sidenav's tree and mounts it
|
|
13
|
+
* collapsed — the pill-marked grab edge opens it to the brand sidebar, the
|
|
14
|
+
* drag is the transition, and it stays a rail while it navigates
|
|
15
|
+
* (`expandOnSelect={false}`).
|
|
8
16
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
17
|
+
* The bottom, specified once: theme slot → `bottomItems` (pinned rows) →
|
|
18
|
+
* no footer (an app rail never carried the site's wordmark link). The
|
|
19
|
+
* `logomark` is the sidenav's `header` — at the TOP, both states, where every
|
|
20
|
+
* rail in the estate has carried it (RailLogomarkAtTop, 2026-08-28 — user, on
|
|
21
|
+
* 0.13.0's footer placement: "why did you move the logo from top to bottom?
|
|
22
|
+
* … I've never seen that before so you are the first"). Its `title` is the
|
|
23
|
+
* label beside the mark in the open rail; collapsed, the mark alone.
|
|
16
24
|
*/
|
|
17
|
-
|
|
18
|
-
function RailItem({ icon, path, label, active, onNavigate, iconComponent }) {
|
|
19
|
-
return (
|
|
20
|
-
<Button
|
|
21
|
-
iconOnly={icon}
|
|
22
|
-
iconSize={20}
|
|
23
|
-
variant="nav"
|
|
24
|
-
size="md"
|
|
25
|
-
aria-current={active ? 'page' : undefined}
|
|
26
|
-
onClick={() => onNavigate?.(path)}
|
|
27
|
-
title={label}
|
|
28
|
-
iconComponent={iconComponent}
|
|
29
|
-
/>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
25
|
export default function NavRail({
|
|
34
26
|
items = [],
|
|
35
27
|
bottomItems = [],
|
|
@@ -41,42 +33,36 @@ export default function NavRail({
|
|
|
41
33
|
hidden = false,
|
|
42
34
|
}) {
|
|
43
35
|
if (hidden) return null
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
const node = (item) => ({ id: item.path, label: item.label, icon: item.icon, to: item.path })
|
|
37
|
+
/* the shell's route match: '/' exact, anything else by prefix */
|
|
38
|
+
const isActive = (to) => (to === '/' ? currentPath === '/' : currentPath.startsWith(to))
|
|
47
39
|
return (
|
|
48
|
-
<
|
|
49
|
-
{
|
|
50
|
-
|
|
40
|
+
<SideNav
|
|
41
|
+
navTree={items.map(node)}
|
|
42
|
+
bottomItems={bottomItems.map(node)}
|
|
43
|
+
currentPath={currentPath}
|
|
44
|
+
isActive={isActive}
|
|
45
|
+
onNavigate={(e, to) => onNavigate?.(to)}
|
|
46
|
+
iconComponent={iconComponent}
|
|
47
|
+
themeToggle={themeToggle}
|
|
48
|
+
expandOnSelect={false}
|
|
49
|
+
defaultCollapsed
|
|
50
|
+
hairline
|
|
51
|
+
footer={false}
|
|
52
|
+
header={logomark ? (
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
className="flex items-center gap-3 w-full bg-transparent border-0 p-0 cursor-pointer text-emphasis no-underline text-left"
|
|
51
56
|
onClick={() => onNavigate?.('/')}
|
|
52
|
-
className="text-oq-96"
|
|
53
|
-
style={{ cursor: 'pointer', marginBottom: 16, paddingTop: 4 }}
|
|
54
57
|
title={logomark.title}
|
|
58
|
+
aria-label={logomark.title}
|
|
55
59
|
>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
active={isActive(item.path)}
|
|
64
|
-
onNavigate={onNavigate}
|
|
65
|
-
iconComponent={iconComponent}
|
|
66
|
-
/>
|
|
67
|
-
))}
|
|
68
|
-
<div style={{ flex: 1 }} />
|
|
69
|
-
{themeToggle && <ThemeToggle label={false} style={{ color: 'var(--kol-oq-96)' }} />}
|
|
70
|
-
{bottomItems.map((item) => (
|
|
71
|
-
<RailItem
|
|
72
|
-
key={item.path}
|
|
73
|
-
{...item}
|
|
74
|
-
active={isActive(item.path)}
|
|
75
|
-
onNavigate={onNavigate}
|
|
76
|
-
iconComponent={iconComponent}
|
|
77
|
-
/>
|
|
78
|
-
))}
|
|
79
|
-
<div style={{ height: 8 }} />
|
|
80
|
-
</div>
|
|
60
|
+
<span className="kol-sidenav-hop-icon inline-flex items-center justify-center w-5 h-5 shrink-0" aria-hidden="true">
|
|
61
|
+
<Logomark svgUrl={logomark.svgUrl} size={20} />
|
|
62
|
+
</span>
|
|
63
|
+
{logomark.title && <span className="kol-sidenav-hop-label kol-helper-12 flex-1 min-w-0 truncate">{logomark.title}</span>}
|
|
64
|
+
</button>
|
|
65
|
+
) : undefined}
|
|
66
|
+
/>
|
|
81
67
|
)
|
|
82
68
|
}
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Lifted 2026-08-14 from the hand-copied twins in kol-monitor ("Monitor") and
|
|
5
5
|
* kol-mirror ("Hall of Mirrors") — AppShellSet lobby brief. App chrome, not
|
|
6
6
|
* site chrome: kol-framework's SideNav/footer/heroes are the site register;
|
|
7
|
-
* this is the
|
|
7
|
+
* this is the rail (a collapsed kol-framework SideNav since 0.13.0 —
|
|
8
|
+
* RailSideNavPixelParity) + the page scaffolds an application is built from.
|
|
8
9
|
*
|
|
9
10
|
* Router-agnostic: no react-router dependency — pass `currentPath` +
|
|
10
11
|
* `onNavigate` and render your router's element as children.
|