@kolkrabbi/kol-shell 0.61.0 → 0.62.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 +5 -5
- package/src/AppShell.jsx +5 -0
- package/src/NavRail.jsx +28 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The application shell",
|
|
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.
|
|
24
|
-
"@kolkrabbi/kol-
|
|
25
|
-
"@kolkrabbi/kol-
|
|
26
|
-
"@kolkrabbi/kol-
|
|
23
|
+
"@kolkrabbi/kol-component": "^0.240.0",
|
|
24
|
+
"@kolkrabbi/kol-icons": "^0.33.1",
|
|
25
|
+
"@kolkrabbi/kol-theme": "^0.166.0",
|
|
26
|
+
"@kolkrabbi/kol-framework": "^0.49.0"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src",
|
package/src/AppShell.jsx
CHANGED
|
@@ -43,6 +43,9 @@ import { TABBAR_H, MastheadContext } from '@kolkrabbi/kol-component'
|
|
|
43
43
|
* `/settings` while already there returns to the last page), and the theme
|
|
44
44
|
* toggle lives on the settings page, not in the rail (user, 2026-08-28).
|
|
45
45
|
* @param {boolean} [props.railFade=true] the rail's list fades out at its foot (`NavRail fade`); `false` opts out
|
|
46
|
+
* @param {'fold'|'enter'} [props.railSections='fold'] how a section row opens: `fold` unfolds it in place (the
|
|
47
|
+
* accordion), `enter` replaces the list with its rows under a Back row
|
|
48
|
+
* (`NavRail sections`, 2026-10-06 — labs' catalog)
|
|
46
49
|
* @param {string} props.railToggleKey a key that toggles the rail (e.g. '\\') — ignored while typing in a field;
|
|
47
50
|
* the rail comes back on every `currentPath` change (ShellHomeSystem, 2026-08-27)
|
|
48
51
|
* @param {'shell'|'bare'|'overlay'|'drawer'|'bar'} props.touch the touch-primary policy (default 'shell' = the rail regardless):
|
|
@@ -132,6 +135,7 @@ export default function AppShell({
|
|
|
132
135
|
iconComponent,
|
|
133
136
|
railComponent: Rail = NavRail,
|
|
134
137
|
railFade = true,
|
|
138
|
+
railSections = 'fold',
|
|
135
139
|
railToggleKey,
|
|
136
140
|
touch = 'shell',
|
|
137
141
|
appName,
|
|
@@ -373,6 +377,7 @@ export default function AppShell({
|
|
|
373
377
|
iconComponent={iconComponent}
|
|
374
378
|
drawer={drawer}
|
|
375
379
|
fade={railFade}
|
|
380
|
+
sections={railSections}
|
|
376
381
|
/>
|
|
377
382
|
)}
|
|
378
383
|
{/* THE BACK OF THE BACK — surface-primary, always, in every app; the
|
package/src/NavRail.jsx
CHANGED
|
@@ -167,7 +167,7 @@ function IconAt({ name, size, component }) {
|
|
|
167
167
|
return <Cmp name={name} size={size} />
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconComponent, railOpen, onOpenRail }) {
|
|
170
|
+
function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconComponent, railOpen, onOpenRail, onEnter }) {
|
|
171
171
|
const [open, setOpen] = useState(false)
|
|
172
172
|
/* A SECTION ROW IS NOT A DESTINATION (RailSectionPressOpensRail, kol-fxr
|
|
173
173
|
* 2026-08-30 — user: *"if I press effects from collapsed nav it should maybe
|
|
@@ -183,8 +183,13 @@ function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconCompone
|
|
|
183
183
|
* "nothing auto-expands" — that rule is about ARRIVING on a route; this is an
|
|
184
184
|
* explicit press. */
|
|
185
185
|
const isSection = sub?.length > 0
|
|
186
|
+
/* …AND IN AN OPEN RAIL THE SAME PRESS FOLDS AND UNFOLDS IT (2026-10-05, labs on a phone). Open,
|
|
187
|
+
* it still fell through to `onNavigate` with that path no consumer dispatches: dead at a desk,
|
|
188
|
+
* and worse on a touch drawer, where AppShell closes the drawer on every navigate — pressing a
|
|
189
|
+
* section's name shut the nav and loaded nothing. */
|
|
186
190
|
const press = () => {
|
|
187
|
-
if (isSection && !railOpen)
|
|
191
|
+
if (isSection && onEnter) { if (!railOpen) onOpenRail?.(); onEnter(); return }
|
|
192
|
+
if (isSection) { if (!railOpen) onOpenRail?.(); setOpen((v) => !railOpen || !v); return }
|
|
188
193
|
onNavigate?.(path)
|
|
189
194
|
}
|
|
190
195
|
const active = path === '/' ? currentPath === '/' : currentPath.startsWith(path)
|
|
@@ -221,7 +226,7 @@ function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconCompone
|
|
|
221
226
|
{sub?.length > 0 && (
|
|
222
227
|
<Tooltip label={label} placement="right" triggerClassName="inline-flex shrink-0">
|
|
223
228
|
<Button
|
|
224
|
-
iconOnly={open ? 'chevron-down' : 'chevron-right'}
|
|
229
|
+
iconOnly={onEnter ? 'chevron-right' : open ? 'chevron-down' : 'chevron-right'}
|
|
225
230
|
iconSize={16}
|
|
226
231
|
iconComponent={iconComponent}
|
|
227
232
|
variant="nav"
|
|
@@ -230,14 +235,14 @@ function RailItem({ icon, path, label, sub, currentPath, onNavigate, iconCompone
|
|
|
230
235
|
size="md"
|
|
231
236
|
className="shrink-0"
|
|
232
237
|
style={{ color: 'var(--kol-oq-96)' }}
|
|
233
|
-
aria-expanded={open}
|
|
234
|
-
onClick={() => setOpen((v) => !v)}
|
|
238
|
+
aria-expanded={onEnter ? undefined : open}
|
|
239
|
+
onClick={onEnter ? press : () => setOpen((v) => !v)}
|
|
235
240
|
aria-label={`${label} sub categories`}
|
|
236
241
|
/>
|
|
237
242
|
</Tooltip>
|
|
238
243
|
)}
|
|
239
244
|
</div>
|
|
240
|
-
{railOpen && open && sub.map((s) => {
|
|
245
|
+
{railOpen && open && !onEnter && sub.map((s) => {
|
|
241
246
|
const on = s.path === '/' ? currentPath === '/' : currentPath.startsWith(s.path)
|
|
242
247
|
return (
|
|
243
248
|
<Tooltip key={s.path} label={s.label} placement="right" asChild>
|
|
@@ -282,18 +287,28 @@ export default function NavRail({
|
|
|
282
287
|
hidden = false,
|
|
283
288
|
drawer = false,
|
|
284
289
|
fade = true,
|
|
290
|
+
sections = 'fold',
|
|
285
291
|
}) {
|
|
286
292
|
const railRef = useRef(null)
|
|
287
293
|
const grabRef = useRef(null)
|
|
288
294
|
const [railOpen, setRailOpen] = useState(false)
|
|
295
|
+
/* ENTER MODE (`sections="enter"`, 2026-10-06 — the user, on labs: "having a 'enter' mode in the
|
|
296
|
+
* left sidebar instead of collapse folder?"): pressing a section replaces the list with that
|
|
297
|
+
* section's rows under a Back row, instead of unfolding it in place. `fold` is the accordion
|
|
298
|
+
* every rail had and keeps. Per consumer, not a law: labs' catalog is five sections of up to
|
|
299
|
+
* ten rows each, which is where an accordion stops reading. */
|
|
300
|
+
const [entered, setEntered] = useState(null)
|
|
289
301
|
const snapOpenRef = useRef(null)
|
|
290
302
|
/* the grab strip is not rendered in drawer mode, so the hook's ref stays null
|
|
291
303
|
* and it no-ops on its own — no second argument needed */
|
|
292
304
|
useGrabEdge(grabRef)
|
|
293
305
|
useRailDrag(railRef, grabRef, setRailOpen, snapOpenRef, !drawer)
|
|
294
306
|
if (hidden) return null
|
|
307
|
+
const enterMode = sections === 'enter'
|
|
308
|
+
const section = enterMode && entered ? items.find((i) => i.path === entered) : null
|
|
295
309
|
const row = (item) => (
|
|
296
|
-
<RailItem key={item.path} {...item} currentPath={currentPath} onNavigate={onNavigate} iconComponent={iconComponent} railOpen={drawer || railOpen} onOpenRail={() => snapOpenRef.current?.()}
|
|
310
|
+
<RailItem key={item.path} {...item} currentPath={currentPath} onNavigate={onNavigate} iconComponent={iconComponent} railOpen={drawer || railOpen} onOpenRail={() => snapOpenRef.current?.()}
|
|
311
|
+
onEnter={enterMode ? () => setEntered(item.path) : undefined} />
|
|
297
312
|
)
|
|
298
313
|
return (
|
|
299
314
|
<div
|
|
@@ -349,7 +364,12 @@ export default function NavRail({
|
|
|
349
364
|
* bottom rows — what the bare spacer used to hold — scrolls when the rows outrun the window,
|
|
350
365
|
* and fades at its foot unless `fade={false}`. Same column, same gap, as the rail's own. */}
|
|
351
366
|
<div className={`kol-shell-rail-list flex min-h-0 w-full flex-1 flex-col items-start gap-2 overflow-y-auto overflow-x-hidden ${fade ? 'kol-shell-rail-list--fade' : ''}`.trim()}>
|
|
352
|
-
{
|
|
367
|
+
{section ? (
|
|
368
|
+
<>
|
|
369
|
+
<RailItem icon="arrow-left" path={section.path} label={section.label} currentPath="" onNavigate={() => setEntered(null)} iconComponent={iconComponent} railOpen={drawer || railOpen} />
|
|
370
|
+
{section.sub.map((s) => <RailItem key={s.path} {...s} currentPath={currentPath} onNavigate={onNavigate} iconComponent={iconComponent} railOpen={drawer || railOpen} />)}
|
|
371
|
+
</>
|
|
372
|
+
) : items.map(row)}
|
|
353
373
|
</div>
|
|
354
374
|
{/* the rule runs the full rail width, out past the px-2 */}
|
|
355
375
|
{bottomItems.length > 0 && <div className="self-stretch -mx-2 border-t border-fg-08" />}
|