@kolkrabbi/kol-shell 0.27.0 → 0.29.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/package.json +3 -3
- package/src/AppShell.jsx +14 -0
- package/src/NavRail.jsx +33 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
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",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kolkrabbi/kol-component": "^0.
|
|
23
|
+
"@kolkrabbi/kol-component": "^0.138.0",
|
|
24
24
|
"@kolkrabbi/kol-icons": "^0.25.0",
|
|
25
25
|
"@kolkrabbi/kol-framework": "^0.35.0",
|
|
26
|
-
"@kolkrabbi/kol-theme": "^0.
|
|
26
|
+
"@kolkrabbi/kol-theme": "^0.111.0"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src",
|
package/src/AppShell.jsx
CHANGED
|
@@ -155,6 +155,20 @@ export default function AppShell({
|
|
|
155
155
|
|
|
156
156
|
/* the rail comes back on every route change */
|
|
157
157
|
useEffect(() => { setNavHidden(false) }, [currentPath])
|
|
158
|
+
|
|
159
|
+
/* THE WASH ALSO GOES ON THE ROOT (2026-08-30). It is set on the content
|
|
160
|
+
* wrapper below, which every page inherits — but a PORTALLED surface does
|
|
161
|
+
* not: `.kol-dd-panel` renders at document.body, so it read the fallback
|
|
162
|
+
* `transparent` while its own trigger, inside the shell, took the wash. The
|
|
163
|
+
* two halves of one connected control rendered at different values, which is
|
|
164
|
+
* exactly what the user saw. Anything floating over the page is still ON the
|
|
165
|
+
* page as far as this film is concerned. */
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
const root = document.documentElement
|
|
168
|
+
if (pageWash == null) { root.style.removeProperty('--kol-shell-page-wash'); return undefined }
|
|
169
|
+
root.style.setProperty('--kol-shell-page-wash', pageWash)
|
|
170
|
+
return () => root.style.removeProperty('--kol-shell-page-wash')
|
|
171
|
+
}, [pageWash])
|
|
158
172
|
/* one key toggles the rail — never while typing in a field */
|
|
159
173
|
useEffect(() => {
|
|
160
174
|
if (!railToggleKey) return undefined
|
package/src/NavRail.jsx
CHANGED
|
@@ -119,7 +119,7 @@ function useGrabEdge(ref) {
|
|
|
119
119
|
* toggles. `onSnap` reports the resting state once — the sub rows render only
|
|
120
120
|
* while open, because closed they would still take their height and push the
|
|
121
121
|
* rungs below them. */
|
|
122
|
-
function useRailDrag(railRef, grabRef, onSnap) {
|
|
122
|
+
function useRailDrag(railRef, grabRef, onSnap, snapRef) {
|
|
123
123
|
useEffect(() => {
|
|
124
124
|
const strip = grabRef.current, rail = railRef.current, root = document.documentElement
|
|
125
125
|
if (!strip || !rail) return undefined
|
|
@@ -129,6 +129,13 @@ function useRailDrag(railRef, grabRef, onSnap) {
|
|
|
129
129
|
gsap.to(root, { [RAIL_W]: `${w}px`, ...GRAB.snap, overwrite: 'auto' })
|
|
130
130
|
onSnap(w !== CLOSED)
|
|
131
131
|
}
|
|
132
|
+
/* the ONLY way out of this effect (RailSectionPressOpensRail, kol-fxr
|
|
133
|
+
* 2026-08-30). `snapTo` closes over the animation and the CLOSED/open
|
|
134
|
+
* ladder, so a section press has to reach it rather than reimplement it —
|
|
135
|
+
* a consumer setting `--kol-shell-rail-width` from outside widens the rail
|
|
136
|
+
* but leaves `railOpen` false, and the L2 rows render behind it: a wide,
|
|
137
|
+
* EMPTY rail, worse than the dead press this fixes. */
|
|
138
|
+
if (snapRef) snapRef.current = () => snapTo(openWidth())
|
|
132
139
|
const onDown = (e) => {
|
|
133
140
|
strip.setPointerCapture(e.pointerId)
|
|
134
141
|
gsap.killTweensOf(root)
|
|
@@ -159,7 +166,7 @@ function useRailDrag(railRef, grabRef, onSnap) {
|
|
|
159
166
|
strip.removeEventListener('pointerup', onUp)
|
|
160
167
|
strip.removeEventListener('pointercancel', onUp)
|
|
161
168
|
}
|
|
162
|
-
}, [railRef, grabRef, onSnap])
|
|
169
|
+
}, [railRef, grabRef, onSnap, snapRef])
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
/* one row: the rung exactly where the closed rail had it, then the label and
|
|
@@ -170,8 +177,26 @@ function IconAt({ name, size, component }) {
|
|
|
170
177
|
return <Cmp name={name} size={size} />
|
|
171
178
|
}
|
|
172
179
|
|
|
173
|
-
function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconComponent, railOpen }) {
|
|
180
|
+
function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconComponent, railOpen, onOpenRail }) {
|
|
174
181
|
const [open, setOpen] = useState(false)
|
|
182
|
+
/* A SECTION ROW IS NOT A DESTINATION (RailSectionPressOpensRail, kol-fxr
|
|
183
|
+
* 2026-08-30 — user: *"if I press effects from collapsed nav it should maybe
|
|
184
|
+
* open? currently pressing it does nothing"*).
|
|
185
|
+
*
|
|
186
|
+
* Closed, the row clips to 48px and the disclosure caret sits OUTSIDE that
|
|
187
|
+
* clip, so the only reachable control was the icon — which called
|
|
188
|
+
* `onNavigate` with a path no consumer dispatches, because a section has no
|
|
189
|
+
* action. A guaranteed dead press, in the rail's default state.
|
|
190
|
+
*
|
|
191
|
+
* Pressing it now opens the rail and expands that section. This is the
|
|
192
|
+
* behaviour `SideNav` had before the flat-rail reversal. It does not fight
|
|
193
|
+
* "nothing auto-expands" — that rule is about ARRIVING on a route; this is an
|
|
194
|
+
* explicit press. */
|
|
195
|
+
const isSection = sub?.length > 0
|
|
196
|
+
const press = () => {
|
|
197
|
+
if (isSection && !railOpen) { onOpenRail?.(); setOpen(true); return }
|
|
198
|
+
onNavigate?.(path)
|
|
199
|
+
}
|
|
175
200
|
const active = path === '/' ? currentPath === '/' : currentPath.startsWith(path)
|
|
176
201
|
return (
|
|
177
202
|
<>
|
|
@@ -190,13 +215,13 @@ function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconCompone
|
|
|
190
215
|
* and too dim on an icon rail (user: "make the color .96 on every icon
|
|
191
216
|
* in the rail") */
|
|
192
217
|
style={{ color: 'var(--kol-oq-96)' }}
|
|
193
|
-
onClick={
|
|
218
|
+
onClick={press}
|
|
194
219
|
title={label}
|
|
195
220
|
aria-label={label}
|
|
196
221
|
/>
|
|
197
222
|
<span
|
|
198
223
|
className="kol-helper-12 uppercase text-oq-96 flex-1 min-w-0 truncate cursor-pointer"
|
|
199
|
-
onClick={
|
|
224
|
+
onClick={press}
|
|
200
225
|
>
|
|
201
226
|
{label}
|
|
202
227
|
</span>
|
|
@@ -263,11 +288,12 @@ export default function NavRail({
|
|
|
263
288
|
const railRef = useRef(null)
|
|
264
289
|
const grabRef = useRef(null)
|
|
265
290
|
const [railOpen, setRailOpen] = useState(false)
|
|
291
|
+
const snapOpenRef = useRef(null)
|
|
266
292
|
useGrabEdge(grabRef)
|
|
267
|
-
useRailDrag(railRef, grabRef, setRailOpen)
|
|
293
|
+
useRailDrag(railRef, grabRef, setRailOpen, snapOpenRef)
|
|
268
294
|
if (hidden) return null
|
|
269
295
|
const row = (item) => (
|
|
270
|
-
<RailItem key={item.path} {...item} currentPath={currentPath} onNavigate={onNavigate} iconComponent={iconComponent} railOpen={railOpen} />
|
|
296
|
+
<RailItem key={item.path} {...item} currentPath={currentPath} onNavigate={onNavigate} iconComponent={iconComponent} railOpen={railOpen} onOpenRail={() => snapOpenRef.current?.()} />
|
|
271
297
|
)
|
|
272
298
|
return (
|
|
273
299
|
<div
|