@dloizides/ui-nav 1.38.0 → 1.39.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/CHANGELOG.md +560 -556
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +12 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,556 +1,560 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 1.38.
|
|
4
|
-
|
|
5
|
-
- NAV-BRAND-1
|
|
6
|
-
|
|
7
|
-
## 1.
|
|
8
|
-
|
|
9
|
-
- NAV-BRAND-1 "
|
|
10
|
-
|
|
11
|
-
## 1.
|
|
12
|
-
|
|
13
|
-
- NAV-
|
|
14
|
-
|
|
15
|
-
## 1.
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
## 1.
|
|
20
|
-
|
|
21
|
-
- INVOICE-5 "Edit invoices before sending, shared background and menu hover" (AC-UI-
|
|
22
|
-
|
|
23
|
-
## 1.
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
## 1.26.
|
|
28
|
-
|
|
29
|
-
- KEFI-LAUNCH-1 "Do every open Kefi task before the public launch":
|
|
30
|
-
|
|
31
|
-
## 1.
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
## 1.
|
|
36
|
-
|
|
37
|
-
- `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
## 1.23.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`children`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
`
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
`
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
(`enableInlineSearch
|
|
168
|
-
|
|
169
|
-
(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
- **
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
|
|
189
|
-
`
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
`
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
the panel
|
|
249
|
-
|
|
250
|
-
- **
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
- **
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
- **
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
>
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
`
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
`
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
`
|
|
422
|
-
`
|
|
423
|
-
|
|
424
|
-
`
|
|
425
|
-
`
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
`
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
`
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
`
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
region
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.38.1
|
|
4
|
+
|
|
5
|
+
- NAV-BRAND-1 "Wider sidebar, logo and name in the top bar" AC-UI-2: a `full` phone drawer takes its width and height from the window (`useWindowDimensions`), not from the shell root, so a sibling below the shell (FinReg `BuildInfoFooter`) no longer leaves it 24 px short. The default 280 px drawer is unchanged.
|
|
6
|
+
|
|
7
|
+
## 1.38.0
|
|
8
|
+
|
|
9
|
+
- NAV-BRAND-1 D-NAVB-3 "One brand options prop": `NavShell` takes one `brand={{ placement, railWidth, drawerHeader, drawerWidth }}` prop instead of the four 1.37.0 props; `Sidebar.width` removed (the shell sets the rail width through `containerStyle`). The top-bar brand cell carries the top bar background and bottom border, so the header line runs over the rail.
|
|
10
|
+
|
|
11
|
+
## 1.37.0
|
|
12
|
+
|
|
13
|
+
- NAV-BRAND-1 "Wider sidebar, logo and name in the top bar": `NavShell` gains `brandPlacement`, `sidebarWidth`, `drawerHeader`, `drawerWidth` (defaults = today); `Sidebar.width`; `AppShell.mobileMenu` takes `header` / `width` / `sidebar`. Exports `SIDEBAR_WIDTH_PX`, `NAV_BRAND_DEFAULTS`.
|
|
14
|
+
|
|
15
|
+
## 1.35.0
|
|
16
|
+
|
|
17
|
+
- NAV-SCROLL-1 "Opening sidebar groups never hides the items below" (AC-UI-1, AC-UI-2): `Sidebar` items (header slot + list) scroll in a `ScrollView` between the pinned title/search and the pinned footer, with a `theme.colors.surface` fade on each edge that hides items. Inside `MobileDrawer` opening a top-level group closes the other (through `NavAccordionContext`, which `MobileDrawer` provides; no new `Sidebar` prop). `NavExpandableItem` takes optional controlled `expanded` + `onToggle`. No prop changes for existing consumers.
|
|
18
|
+
|
|
19
|
+
## 1.28.0
|
|
20
|
+
|
|
21
|
+
- INVOICE-5 "Edit invoices before sending, shared background and menu hover" (AC-UI-3, AC-UI-6): `AppShell` draws `theme.backdrop` (the `@dloizides/theme-web` 1.5.0 `resolveBackdrop` shape, typed here as `AppShellBackdrop`): its colour and web image on the root, and for `horizon` a brand band behind header and nav that the content overlaps by `overlap.compact` (< 768 px) or `overlap.wide`. No `theme.backdrop` keeps today's flat background.
|
|
22
|
+
|
|
23
|
+
## 1.27.0
|
|
24
|
+
|
|
25
|
+
- INVOICE-5 "Edit invoices before sending, shared background and menu hover" (AC-UI-2, AC-UI-6): `NavBar` takes `hoverFeedback` (`NavHoverFeedback`: `default`, `tint-pill`, `underline`, `lift`, `sliding-highlight`); the new phone `BottomTabBar` takes `tabFeedback` (`TabFeedback`: `default`, `indicator-pill`, `dim-spring`, `sliding-bar`, `icon-lift`). Timings come from the `@dloizides/ui-motion` tokens (<= 300 ms); reduced motion keeps every option still. `default` keeps today's look, so existing apps do not change.
|
|
26
|
+
|
|
27
|
+
## 1.26.2
|
|
28
|
+
|
|
29
|
+
- KEFI-LAUNCH-1 "Do every open Kefi task before the public launch": a long account name no longer overlaps the `Topbar` center slot at 360 px. The center slot keeps its content width (`flexShrink: 0`); only the account name gives way. The overlap dates from 1.24.0, when the slot was added.
|
|
30
|
+
|
|
31
|
+
## 1.26.1
|
|
32
|
+
|
|
33
|
+
- KEFI-LAUNCH-1 "Do every open Kefi task before the public launch": the `Topbar` left slot no longer wraps beside a long account name at 360 px. The left slot keeps its width (`flexShrink: 0`) and the account name truncates to one line.
|
|
34
|
+
|
|
35
|
+
## 1.25.0
|
|
36
|
+
|
|
37
|
+
- `PillNav` / `PillNavMenu` get an optional `menuHeading`: a visible, non-focusable heading at the top of the open `Menu` that also names it (needs `@dloizides/ui-layout` >= 1.30.0) (KEFI-EDITOR-HEADER-1 "Compact the landing editor header").
|
|
38
|
+
|
|
39
|
+
## 1.24.0
|
|
40
|
+
|
|
41
|
+
- `Topbar` gets an optional `center` slot between `left` and the actions (testID `topbar-center`); callers that do not pass it render as before (KEFI-EDITOR-HEADER-1 "Compact the landing editor header").
|
|
42
|
+
- `PillNav` gets `variant={PillNavVariant.Menu}`: one compact "Current ▾" trigger (44px pill) opening a menu of the same items through the shared `ModalDropdown` (role menu, arrow keys / Home / End / Enter / Escape, highlight starts on the current item). New optional props `menuHint`, `menuTestID`, `dropdownVariant`; new exports `PillNavVariant`, `resolvePillMenuLabel`. Default stays `Pills`.
|
|
43
|
+
|
|
44
|
+
## 1.23.2
|
|
45
|
+
|
|
46
|
+
- Topbar account-name button and language toggle get a 44x44 minimum hit box (KEFI-HEADER-HITBOX-1 "kefi-web header account control reaches 44x44").
|
|
47
|
+
|
|
48
|
+
## 1.23.1
|
|
49
|
+
|
|
50
|
+
**Overlay z-indexes come from the shared `@dloizides/design-tokens` `Z_INDEX` scale (UI-ZINDEX-SCALE-1 "Shared overlay z-index scale").**
|
|
51
|
+
|
|
52
|
+
- `MobileDrawer` overlay uses `Z_INDEX.drawer`; `CommandPalette` overlay uses `Z_INDEX.palette`.
|
|
53
|
+
- Values are unchanged (50 / 60), so stacking behaviour is identical. The drawer's scrim/panel
|
|
54
|
+
order (1 / 2) is local to the overlay and stays as is.
|
|
55
|
+
- New dependency: `@dloizides/design-tokens` `^1.13.0`.
|
|
56
|
+
|
|
57
|
+
## 1.23.0
|
|
58
|
+
|
|
59
|
+
**The NavBar's stacked mobile menu now scrolls within the viewport (SHOWCASE-1).**
|
|
60
|
+
|
|
61
|
+
Below `collapseBelow` the open hamburger menu grew to its natural height (734px with ~15 items on
|
|
62
|
+
ui.dloizides.com at 360x640). Portals set `body { overflow: hidden }`, so the last items sat below
|
|
63
|
+
the fold and neither wheel nor touch could reach them — in every portal with more than ~10 items.
|
|
64
|
+
|
|
65
|
+
- The stacked links region is now a `ScrollView` capped to `windowHeight - closedBarHeight -
|
|
66
|
+
stackedRightSlot - chrome` (pure `computeStackedMenuMaxHeight`, unit-tested; floor of three 44px
|
|
67
|
+
rows). Rows keep their 44px minimum; nothing depends on hover.
|
|
68
|
+
|
|
69
|
+
## 1.20.0
|
|
70
|
+
|
|
71
|
+
**The auth-pending and page-loading spinners now OVERLAY stably-mounted children instead of
|
|
72
|
+
replacing them — closing a latent deep-link-collapse trap in the shared shell.**
|
|
73
|
+
|
|
74
|
+
`AppShell`'s `gate.pending` branch and `appShellContent`'s `state.loading` branch each `return`ed a
|
|
75
|
+
full-screen spinner *instead of* `children`. Replacing the subtree unmounts whatever `children` host
|
|
76
|
+
— in a portal that is a navigator/`<Slot/>` — and on remount React Navigation re-derives its default
|
|
77
|
+
route, so a deep-linked sub-route silently collapses to its default on every reload. (This is the
|
|
78
|
+
exact class of bug that hit kefi-web's organizer portal, fixed there in its `OnboardingGate`; the
|
|
79
|
+
same shape lived dormant here because no consumer passes `gate=`/`state=` yet.)
|
|
80
|
+
|
|
81
|
+
- **`AppShell` (`gate.pending`)** now renders the shell tree normally and paints the pending spinner
|
|
82
|
+
as an opaque `position:absolute` overlay on top, so `children` mount once and are never torn down
|
|
83
|
+
across `pending → authenticated` (proven by a "same child instance, no remount" test).
|
|
84
|
+
- **`appShellContent` (`state.loading`)** likewise overlays the loading spinner over mounted
|
|
85
|
+
`children` rather than swapping them out. The `loadingHost` wrapper is gated on `state` being
|
|
86
|
+
DEFINED, not on `loading`'s live value — so the element type at the mount point is invariant across
|
|
87
|
+
a `loading` true↔false toggle and `children` are reconciled in place, never remounted on the edge
|
|
88
|
+
(also proven by a "same child instance, no remount" test). Consumers that pass no `state` render
|
|
89
|
+
`children` bare, byte-identical to 1.19.0. Error / forbidden cards are TERMINAL and still replace;
|
|
90
|
+
unauthenticated still returns null + `onRedirect`.
|
|
91
|
+
- **Rule encoded in the docblock:** a *transitional* status overlays; only a *terminal* state
|
|
92
|
+
replaces. Corollary for consumers — `children` mount beneath the pending overlay, so gate data
|
|
93
|
+
FETCHES on auth state (`enabled: isAuthenticated`), not on mount. No public API changed; behavior
|
|
94
|
+
is unchanged for any consumer not passing `gate`/`state`.
|
|
95
|
+
|
|
96
|
+
## 1.19.0
|
|
97
|
+
|
|
98
|
+
**The "More ▾" overflow menu now opens at a readable width instead of cramping onto the trigger's tiny footprint.**
|
|
99
|
+
|
|
100
|
+
`NavOverflowMenu` rendered the shared `@dloizides/ui-layout` `ModalDropdown` without a `menuMinWidth`,
|
|
101
|
+
so the popover matched the width of the compact "More ▾" pill (~its own label). Option labels
|
|
102
|
+
("Settings", "API docs") wrapped onto two lines, and the resulting tall rows overflowed the menu's
|
|
103
|
+
max-height — so the lower items scrolled out of view and read as missing.
|
|
104
|
+
|
|
105
|
+
- **`NavOverflowMenu`** now passes a 200px `menuMinWidth` floor, so the overflow menu opens wide
|
|
106
|
+
enough to show each label on one line. `ModalDropdown`/`InlineMenu` clamp a floored menu inside the
|
|
107
|
+
viewport (ui-layout ≥ 1.24.0, now the peer floor), so the extra width never pushes it off the right
|
|
108
|
+
edge. No public API changed.
|
|
109
|
+
|
|
110
|
+
## 1.18.0
|
|
111
|
+
|
|
112
|
+
Root-cause fix for sidebar leaf co-activation. Behavioral/additive — no breaking API.
|
|
113
|
+
|
|
114
|
+
- **Fix (two sidebar leaves showed `aria-current="page"` at once).** On the live finreg
|
|
115
|
+
`/accounting/journals`, BOTH "Chart of Accounts" (route `/accounting`) and "Journals"
|
|
116
|
+
(route `/accounting/journals`) were marked active, because the active predicate was purely
|
|
117
|
+
prefix-based (`pathname === route || pathname.startsWith(route + '/')`) — a module-home leaf
|
|
118
|
+
routed at `/accounting` is a prefix of every `/accounting/*` sibling, so it co-activated on all
|
|
119
|
+
of them. (Second occurrence of this class; the first, CRM Contacts at `/crm`, was worked around
|
|
120
|
+
by moving the route.) Now resolution is **most-specific (longest) match wins**: among a nav
|
|
121
|
+
model's LEAF items, at most ONE is active for any path — the leaf whose route is the longest
|
|
122
|
+
prefix match. On `/accounting/journals` only Journals is active; on `/accounting` exactly only
|
|
123
|
+
Chart of Accounts is. For nav models with no parent/child route overlap (the common case)
|
|
124
|
+
behaviour is unchanged.
|
|
125
|
+
- **New export** `resolveActiveRoute(items, pathname)`: flattens the tree to its leaf routes and
|
|
126
|
+
returns the single longest matching route (honouring per-leaf `exact`), or `undefined`.
|
|
127
|
+
- `Sidebar` resolves the winning route once over the full leaf set and passes `activeRoute` to
|
|
128
|
+
each row; `NavExpandableItem` marks a leaf active iff `item.route === activeRoute` (falls back
|
|
129
|
+
to the per-item predicate when used standalone — backward compatible). Group HEADER expansion
|
|
130
|
+
(`hasActiveDescendant`) is a separate concern and is unchanged.
|
|
131
|
+
- The same winner-based resolution now backs `NavBar`, `PillNav`, `CollapsedRail` and
|
|
132
|
+
`NavOverflowMenu`, so the latent prefix co-activation cannot resurface in the horizontal navs.
|
|
133
|
+
- **Additive API**: new optional `NavItem.exact?: boolean` — a leaf active ONLY on its exact
|
|
134
|
+
path (never a nested one). A complementary escape hatch; the longest-match default is the real
|
|
135
|
+
fix, so no consumer needs to set it.
|
|
136
|
+
- `isRouteActive(pathname, route, exact?)` gains an optional third arg; the two-arg call is
|
|
137
|
+
unchanged.
|
|
138
|
+
|
|
139
|
+
## 1.17.0
|
|
140
|
+
|
|
141
|
+
Two live-defect fixes to the shared sidebar chrome. Behavioral/additive — no breaking API.
|
|
142
|
+
|
|
143
|
+
- **Fix (two search bars on desktop → mutually exclusive by affordance).** 1.16.1 shipped the
|
|
144
|
+
desktop inline `SidebarSearch` field, but the ⌘K `CommandPaletteTrigger` could be present at
|
|
145
|
+
the same time, so a wide viewport showed BOTH. `Sidebar` now owns both affordances and renders
|
|
146
|
+
exactly ONE, chosen by its own viewport width via `resolveSearchAffordance`:
|
|
147
|
+
- inline affordance (≥768) → the inline field ONLY; the ⌘K trigger is suppressed.
|
|
148
|
+
- palette affordance (<768) → the ⌘K trigger ONLY; the inline field is suppressed.
|
|
149
|
+
- The ⌘K keyboard shortcut (`useCommandPaletteHotkey`) is untouched and stays bound at every width.
|
|
150
|
+
- **Additive API**: new `Sidebar.paletteTrigger?: SidebarPaletteTriggerConfig`
|
|
151
|
+
(`{ label, hint, shortcut, onOpen, testID? }`), forwarded from `NavShellSideRail.paletteTrigger`.
|
|
152
|
+
Consumers that wire neither, or only the inline field, are unchanged (the inline field simply
|
|
153
|
+
no longer renders below 768, where a persistent field would steal vertical space).
|
|
154
|
+
|
|
155
|
+
- **Fix (hover wash never appeared on RN-web).** The nav rows were `TouchableOpacity`, which in
|
|
156
|
+
react-native-web has NO hover support and silently drops `onHoverIn`/`onHoverOut` — so the row's
|
|
157
|
+
`hovered` state never toggled and the primary-tinted overlay stayed at opacity 0. The leaf and
|
|
158
|
+
group-header rows in `NavExpandableItem` are now `Pressable`, whose `useHover` responder actually
|
|
159
|
+
drives `hovered`, lifting the token-tinted overlay to the hover wash (`HOVER_TINT_OPACITY`, ~160ms
|
|
160
|
+
reduced-motion-gated transition) — distinct from and lighter than the active tint. Token-only
|
|
161
|
+
(colours via the theme; no raw hex/rgb/hsl). aria wiring (`aria-current` / `aria-expanded`, which
|
|
162
|
+
`MobileDrawer` depends on) is preserved across the switch.
|
|
163
|
+
|
|
164
|
+
## 1.16.1
|
|
165
|
+
|
|
166
|
+
- **Fix (NavShell forwards inline-search props to the inner `Sidebar`).** 1.16.0 added
|
|
167
|
+
desktop inline search to `Sidebar` (`enableInlineSearch`, `search`, controlled
|
|
168
|
+
`searchQuery` / `onSearchChange`), but all seven portals consume the higher-level
|
|
169
|
+
`NavShell` (via `sideRail`), which did **not** forward those props — so inline search was
|
|
170
|
+
unreachable by every real consumer. `NavShellSideRail` now carries the same four fields
|
|
171
|
+
(`enableInlineSearch?`, `search?: SidebarSearchConfig`, `searchQuery?`, `onSearchChange?`)
|
|
172
|
+
and threads them onto the `<Sidebar>` it builds. Desktop-vs-mobile resolution is unchanged
|
|
173
|
+
(inline ≥768, ⌘K palette <768 via `resolveSearchAffordance`).
|
|
174
|
+
- **Purely additive / backward compatible**: with the fields unset, `NavShell` renders
|
|
175
|
+
exactly as 1.16.0. No breaking change; no styling touched.
|
|
176
|
+
|
|
177
|
+
## 1.16.0
|
|
178
|
+
|
|
179
|
+
- **Feature (docs-style sidebar rail — hover wash, tinted active pill, rotating chevron).**
|
|
180
|
+
The `Sidebar` / `NavExpandableItem` rows now read like the aml-screening docs navigation:
|
|
181
|
+
each row carries a PRIMARY-TINTED overlay whose opacity eases `0 → hover → active` as a
|
|
182
|
+
smooth ~160ms CSS cross-fade on web (gated by `prefers-reduced-motion`, omitted on native).
|
|
183
|
+
- **Hover** lights a row with a subtle brand wash (`HOVER_TINT_OPACITY` = 0.06); **active**
|
|
184
|
+
deepens it to a soft brand pill (`ACTIVE_TINT_OPACITY` = 0.14) over the transparent
|
|
185
|
+
left-accent gutter every leaf already reserved, so the active leaf shows a tinted pill + a
|
|
186
|
+
coloured left bar + brand-coloured label — no layout shift, all from `theme.palette.primary`.
|
|
187
|
+
- **Rotating chevron.** Expandable groups now get a built-in `SidebarChevron` (a `›` glyph
|
|
188
|
+
that rotates to `⌄` on expand, tweened on web) when the caller supplies no `renderChevron`.
|
|
189
|
+
A caller-supplied `renderChevron` still wins, so existing icon-set chevrons are unaffected.
|
|
190
|
+
- Row corner radius softened to `NAV_ROW_RADIUS` (8px) with the rounded overlay clipped to it.
|
|
191
|
+
- No public API change to existing props; hover uses RNW's `onHoverIn`/`onHoverOut` (no-op on
|
|
192
|
+
native). New exports: `SidebarChevron`, `NAV_ROW_RADIUS`, `ACTIVE_TINT_OPACITY`,
|
|
193
|
+
`HOVER_TINT_OPACITY`.
|
|
194
|
+
|
|
195
|
+
- **Feature (desktop INLINE sidebar search — opt-in — with the ⌘K palette kept for mobile).**
|
|
196
|
+
The owner found the ⌘K command-palette popup inconvenient on desktop. `Sidebar` now accepts
|
|
197
|
+
an opt-in persistent inline search field at the top of the rail that narrows the nav list
|
|
198
|
+
LIVE as you type.
|
|
199
|
+
- **New `Sidebar` props (all additive, default OFF):** `enableInlineSearch` + a pre-localized
|
|
200
|
+
`search: SidebarSearchConfig` (`placeholder` / `hint` / `clearLabel` / `clearHint` /
|
|
201
|
+
`emptyText`), plus optional controlled `searchQuery` + `onSearchChange` (the sidebar owns the
|
|
202
|
+
state when omitted). With the flag off the render tree is byte-identical to 1.15 — the mobile
|
|
203
|
+
drawer, which should keep using the ⌘K palette, is untouched.
|
|
204
|
+
- **New exports:** `SidebarSearch` (the field), `filterNavItems` / `isFilterActive` /
|
|
205
|
+
`searchTokens` (the pure tree-prune, same all-tokens-must-match semantics as `filterCommands`
|
|
206
|
+
so inline + ⌘K rank identically), and `resolveSearchAffordance(viewport)` +
|
|
207
|
+
`DEFAULT_SEARCH_BREAKPOINT` — the pure `'inline'` (≥768px) vs `'palette'` (<768px) rule so a
|
|
208
|
+
shell can wire desktop-inline / mobile-palette from one function. The ⌘K hotkey
|
|
209
|
+
(`useCommandPaletteHotkey`) stays bound at every width.
|
|
210
|
+
|
|
211
|
+
## 1.15.0
|
|
212
|
+
|
|
213
|
+
- **Feature (an expandable nav group now ANIMATES its expand/collapse instead of snapping).**
|
|
214
|
+
`NavExpandableItem` used to conditionally render its children (`{expanded ? … : null}`),
|
|
215
|
+
so a sidebar/drawer group's leaves appeared and vanished INSTANTLY on toggle — a hard,
|
|
216
|
+
jarring cut with no transition. The group's children are now wrapped in the
|
|
217
|
+
`@dloizides/ui-motion` `<Collapse open={expanded}>`, which measures the children's natural
|
|
218
|
+
height and animates a real `Animated.Value` height on WEB and native alike (the height code
|
|
219
|
+
path RN's native-only `LayoutAnimation` never gave the web build), collapsing to instant
|
|
220
|
+
under `prefers-reduced-motion`.
|
|
221
|
+
- **Collapsed leaves stay non-interactive.** Per the `Collapse` contract the children remain
|
|
222
|
+
MOUNTED at all times (their state and a11y focus order are preserved); when collapsed they
|
|
223
|
+
are clipped to zero height under `overflow: hidden`, so a collapsed group's leaves are
|
|
224
|
+
neither visible nor tappable in a real browser even though they are in the tree. This is a
|
|
225
|
+
deliberate change from the prior "absent from the DOM when collapsed" behaviour.
|
|
226
|
+
- **`aria-expanded` and the mobile-drawer contract are intact.** The group header still emits
|
|
227
|
+
`aria-expanded` (the `ariaExpandedProps` spread is unchanged), so `MobileDrawer` still tells
|
|
228
|
+
a group TOGGLE apart from a navigating LEAF and does not dismiss the drawer when a group is
|
|
229
|
+
expanded on mobile. The active-leaf accent bar, docs-style section-header styling, chevron
|
|
230
|
+
slot, testIDs, and all a11y roles/labels/hints are unchanged.
|
|
231
|
+
- **No public API change.** `NavExpandableItemProps` / `NavItem` are untouched — the animation
|
|
232
|
+
is derived from the `expanded` state the component already tracked. `@dloizides/ui-motion`
|
|
233
|
+
(`^1.0.1`) is added as a runtime dependency; all 7 portals gain the animated group on their
|
|
234
|
+
next bump for free. Consumers with no expandable groups are visually unaffected.
|
|
235
|
+
|
|
236
|
+
## 1.13.1
|
|
237
|
+
|
|
238
|
+
- **Fix (mobile drawer: a sub-item / leaf tap dismissed the drawer WITHOUT navigating).**
|
|
239
|
+
On a phone-width viewport, tapping a leaf inside the open `MobileDrawer` closed the drawer
|
|
240
|
+
but the route never changed — verified live on finreg via Playwright (real Chromium, real
|
|
241
|
+
pointer events); every jsdom unit test stayed green because it wired `onClose` to an inert
|
|
242
|
+
`jest.fn()` that never unmounts anything.
|
|
243
|
+
- **Root cause.** `MobileDrawer` closes on a nav press via a NATIVE
|
|
244
|
+
`panel.addEventListener('click', …)`. react-native-web fires a leaf's `onPress` from its
|
|
245
|
+
`onClick`, dispatched through REACT's synthetic event system whose root listener sits on the
|
|
246
|
+
app-root container — an ANCESTOR of the panel. So on a real click the native panel listener
|
|
247
|
+
runs FIRST (during the DOM bubble), and closing synchronously there ran `setDrawerOpen(false)`,
|
|
248
|
+
unmounting the panel and tearing the leaf out of the tree BEFORE React could dispatch its
|
|
249
|
+
`onPress`. The navigation call never ran.
|
|
250
|
+
- **Fix.** The drawer's close is now DEFERRED to the next macrotask (`setTimeout(…, 0)`), so
|
|
251
|
+
React finishes dispatching the tapped leaf's `onPress` (the navigation) for the current click;
|
|
252
|
+
the panel then unmounts on the following tick, after the route has already changed. Scrim tap
|
|
253
|
+
and group-header toggle are unchanged.
|
|
254
|
+
- **No API change.** All 7 portals get the fix on their next bump.
|
|
255
|
+
|
|
256
|
+
## 1.11.0
|
|
257
|
+
|
|
258
|
+
- **Feature (an expandable nav group now auto-opens when the current route is inside it).**
|
|
259
|
+
`NavExpandableItem` used to mount every group collapsed (`useState(false)`), so on a fresh
|
|
260
|
+
page load the group containing the active route was closed and its active leaf was hidden.
|
|
261
|
+
A persistent-rail console that groups its nav by module (e.g. finreg-web's Payments / CRM /
|
|
262
|
+
Accounting rail) needs the active module's group open on load, the others collapsed — with
|
|
263
|
+
no way for the caller to express that, since expansion state was internal and uncontrolled.
|
|
264
|
+
- The group now initializes its expansion from whether the current route is inside it:
|
|
265
|
+
`useState(() => hasActiveDescendant(item, pathname))`, where `hasActiveDescendant` is true
|
|
266
|
+
when the item's own route is active (`isRouteActive`) or any descendant's is (recursive, so
|
|
267
|
+
it holds at any nesting depth). The group of the active route mounts EXPANDED (active leaf
|
|
268
|
+
visible + highlighted); a group with no active descendant mounts COLLAPSED — exactly the
|
|
269
|
+
prior behaviour.
|
|
270
|
+
- **No API change.** `NavItem` is unchanged — the behaviour is derived from the `pathname`
|
|
271
|
+
the component already receives. Consumers with no expandable groups, or whose groups hold no
|
|
272
|
+
active route, are byte-for-byte unaffected; all 7 portals gain the auto-open on their next
|
|
273
|
+
bump for free.
|
|
274
|
+
- Guarded by `src/navExpandableItem.test.tsx` (RED-first): the two "mounts EXPANDED" assertions
|
|
275
|
+
FAIL against the pre-change `useState(false)` (the active group's child is absent from the DOM)
|
|
276
|
+
and pass after; the "mounts COLLAPSED" + "header always renders" assertions lock the
|
|
277
|
+
backward-compatible half.
|
|
278
|
+
|
|
279
|
+
## 1.10.2
|
|
280
|
+
|
|
281
|
+
- **Fix (the mobile nav DRAWER was not clickable — a tap on a nav item did not navigate, it
|
|
282
|
+
just closed the drawer / did nothing, in all seven portals below 768px).**
|
|
283
|
+
`MobileDrawer` rendered a full-bleed scrim `Pressable` (`StyleSheet.absoluteFillObject`) and a
|
|
284
|
+
panel `View` with `{ position: 'absolute', top: 0, bottom: 0, left: 0 }` — **no width and no
|
|
285
|
+
zIndex**. Reproduced structurally: the panel's computed `width` was empty and its `zIndex` was
|
|
286
|
+
`0`; the scrim's computed `left`/`right` were both `0px` at `zIndex` `0`. So the scrim FULLY
|
|
287
|
+
overlapped the panel at the SAME stacking level, and on react-native-web the equal-zIndex scrim
|
|
288
|
+
won the pointer (the estate's known "RN-web Pressable swallows a sibling/child press" trap):
|
|
289
|
+
the tap closed the drawer and the nav-item's `TouchableOpacity` `onPress` (→ the app's router)
|
|
290
|
+
never fired.
|
|
291
|
+
- Fixed once, in the kit: the panel now has an explicit drawer **width** (`280px`), a solid
|
|
292
|
+
surface background, and a `zIndex` **above** the scrim; the scrim is anchored BESIDE the panel
|
|
293
|
+
(`left: DRAWER_WIDTH`) so its tap-to-close hit area never overlaps the panel. Nav-item taps
|
|
294
|
+
reach the `Sidebar` leaves; taps beside the drawer still close it.
|
|
295
|
+
- Guarded by `src/mobileDrawer.test.tsx` (RED-first): three structural assertions read the
|
|
296
|
+
resolved geometry via `getComputedStyle` and FAIL against the pre-fix panel/scrim (no width,
|
|
297
|
+
equal zIndex, overlapping scrim); two behavioural assertions lock the contract (nav-item tap
|
|
298
|
+
navigates + closes; scrim tap only closes).
|
|
299
|
+
- **No API change.** Consumers need no edit — bump the dependency and the drawer works.
|
|
300
|
+
|
|
301
|
+
## 1.10.1
|
|
302
|
+
|
|
303
|
+
- **Fix (a plain-string slot logged `console.error` on EVERY render, in all seven portals).**
|
|
304
|
+
Every caller-supplied slot in the kit is typed `React.ReactNode`, which legitimately includes
|
|
305
|
+
`string` — and callers pass exactly that (`brand: FM('app.name')`, a plain localized string).
|
|
306
|
+
The slot was dropped straight into a `<View>`, and react-native-web refuses to render a bare
|
|
307
|
+
text node there: `Unexpected text node: <s>. A text node cannot be a child of a <View>.`
|
|
308
|
+
Nothing in the type system objected, so the defect only ever surfaced as browser console
|
|
309
|
+
noise — which is why it survived to 1.10.0 across the whole fleet.
|
|
310
|
+
- Fixed once, in the kit, via the new internal `renderTextSlot` helper: a `string`/`number`
|
|
311
|
+
slot is wrapped in `<Text>` (routed through the theme's `colors.text`, so it honours dark
|
|
312
|
+
mode rather than defaulting to black); an element slot passes through **untouched**, so
|
|
313
|
+
every existing caller renders byte-identically. Array slots have each string member wrapped
|
|
314
|
+
individually, keys preserved.
|
|
315
|
+
- Applied to **all 12 slot render sites**, not just the reported one — `NavBar` (`brand`,
|
|
316
|
+
`right`), `Topbar` (`left`, `notificationSlot`), `Sidebar` (`header`, `footer`),
|
|
317
|
+
`CollapsedRail` (`header`, `footer`), `AppShell` (`header`, `nav`, `sidebar`,
|
|
318
|
+
`collapsedSidebar`, `banner`) and `MobileDrawer` (`sidebar`).
|
|
319
|
+
- Guarded by `src/rawTextSlots.test.tsx`, which spies on `console.error` and asserts silence.
|
|
320
|
+
Unit tests that only assert "the slot rendered" pass against the bug; this one does not.
|
|
321
|
+
- **No API change.** Consumers need no edit — the `<Text>` wrapper they were expected to
|
|
322
|
+
remember is now the kit's job.
|
|
323
|
+
|
|
324
|
+
## 1.10.0
|
|
325
|
+
|
|
326
|
+
- **Fix (`layout` was not authoritative — `layout="side"` rendered a top bar anyway).**
|
|
327
|
+
`wantsTopBar` carried an extra `(layout === 'side' && hasTopBar)` clause, so passing a
|
|
328
|
+
`topBar` **config** with `layout="side"` mounted the bar regardless — making `'side'` and
|
|
329
|
+
`'both'` render **pixel-identically** and `layout` unobservable. `layout` now decides which
|
|
330
|
+
REGIONS exist; `topBar` is only the CONTENT of the top region. A `'side'` app that also wants
|
|
331
|
+
a bar asks for `layout="both"`, which is what that combination always meant.
|
|
332
|
+
- Found by `apps/ui-showcase`, which renders every orientation side by side — a product app
|
|
333
|
+
picks ONE orientation and never sees the other, which is why this survived to 1.9.0.
|
|
334
|
+
- **Not breaking in this fleet:** no app passed `layout="side"` + `topBar`. The four `'side'`
|
|
335
|
+
portals (erevna, katalogos, zygos, agora) pass a `header` NODE, not a `topBar` config.
|
|
336
|
+
- The `hasHeader` clause is **deliberately kept**: `header` is the documented escape hatch by
|
|
337
|
+
which a `'side'` app supplies its own ready-made header (the structured `Topbar` with
|
|
338
|
+
language / notification / user slots). It is an explicit "render THIS node", not a config
|
|
339
|
+
whose presence is silently read as an orientation change.
|
|
340
|
+
- **a11y: `AccountState.accountHint`** — the account-name button's `accessibilityHint` was
|
|
341
|
+
hardcoded to `displayName`, byte-identical to its `accessibilityLabel`, so a screen reader
|
|
342
|
+
announced the name twice and never said what pressing it does — and the consumer had no way
|
|
343
|
+
to correct it. The sibling `upgrade?: TopbarAction` on the same interface has always carried
|
|
344
|
+
a `hint`; this closes that inconsistency. Falls back to `displayName`, so existing callers
|
|
345
|
+
are unaffected.
|
|
346
|
+
- **a11y: `AccountState.accountTestID`** — the account-name button's testID was hardcoded, with
|
|
347
|
+
no override, unlike `TopbarAction.testID?` / `NavItem.testID?` elsewhere in the package.
|
|
348
|
+
- **a11y: `Topbar.language.testID`** — the inline `language` prop type declared no `testID`, so
|
|
349
|
+
the repo's testID+label+hint standard was **structurally unsatisfiable** for that one button
|
|
350
|
+
while every other Topbar action (all `TopbarAction`) accepted one.
|
|
351
|
+
|
|
352
|
+
> Note for consumers: this react-native-web build does **not** forward `accessibilityHint` to
|
|
353
|
+
> the DOM at all — rendered buttons carry `aria-label` but no hint attribute. The hint is a
|
|
354
|
+
> NATIVE-only guarantee today. That is pre-existing and unchanged here, but it means DOM-level
|
|
355
|
+
> tests cannot distinguish a threaded hint from a dropped one; `topbarA11y.test.tsx` therefore
|
|
356
|
+
> pins the prop threading directly.
|
|
357
|
+
|
|
358
|
+
## 1.9.0
|
|
359
|
+
|
|
360
|
+
**`NavBar.contentMaxWidth` — a full-bleed bar with a centred content column.**
|
|
361
|
+
An optional content-column cap (px), forwarded via `NavShell.topBar.contentMaxWidth`.
|
|
362
|
+
When set, the OUTER `NavBar` container — which carries the bar background + bottom
|
|
363
|
+
border (from `barTheme` / theme surface + any `containerStyle`, e.g. the AML
|
|
364
|
+
radial-glow appbar) — stays FULL WIDTH (full-bleed), while the INNER row (brand +
|
|
365
|
+
links + right) is capped to `contentMaxWidth` and centred. This restores the v1-console
|
|
366
|
+
`.ui-appbar` (full-bleed) + `.ui-appbar-inner { max-width; margin: 0 auto }` shape:
|
|
367
|
+
previously an app had to put the cap on the container's `containerStyle`, which also
|
|
368
|
+
capped the dark background and left light gutters on wide viewports. Omit it and the
|
|
369
|
+
inner spans the full container (unchanged — byte-identical to before). The priority+
|
|
370
|
+
overflow "…" fit measures the capped inner's rendered width, so it keeps working
|
|
371
|
+
against the content column, not the full-bleed container. Purely additive.
|
|
372
|
+
|
|
373
|
+
## 1.8.0
|
|
374
|
+
|
|
375
|
+
The navigation shell becomes the ONE shell for every portal: a first-class
|
|
376
|
+
**top | side | both** layout, plus the capabilities harvested from every RN-web
|
|
377
|
+
portal's bespoke nav/shell so nothing is lost when they migrate. Purely additive —
|
|
378
|
+
`Sidebar`, `Topbar`, `NavBar`, `Nav`, `AppShell`, and the role helpers keep their
|
|
379
|
+
exact signatures; every new prop/component is optional.
|
|
380
|
+
|
|
381
|
+
**`NavShell` — top bar, side rail, or BOTH.** One component owns the whole page
|
|
382
|
+
composition, chosen by a single `layout` prop (`'top' | 'side' | 'both'`). It wires
|
|
383
|
+
a top bar into `AppShell`'s `header` slot and a side rail into its `sidebar` slot,
|
|
384
|
+
and inherits every `AppShell` capability (auth gate, loading/error/forbidden state
|
|
385
|
+
cards, width policy, banner, the responsive hamburger + overlay drawer). `'both'`
|
|
386
|
+
is the AML-console shape — a top bar (brand / global links + overflow "…" / user)
|
|
387
|
+
AND a side rail (sections) together — now first-class instead of hand-wired. The
|
|
388
|
+
top bar is built from a `topBar` config (a `NavBar`), the side rail from a
|
|
389
|
+
`sideRail` config (a `Sidebar`); an explicit `header` node overrides the built top
|
|
390
|
+
bar for apps that want a structured `Topbar`.
|
|
391
|
+
|
|
392
|
+
**Three-tier responsive rail (`AppShell.collapsedSidebar`).** Opts the `sidebar`
|
|
393
|
+
layout into the erevna/katalogos scheme: the full rail on desktop, an intermediate
|
|
394
|
+
persistent **collapsed (icon-only) rail** on a tablet band, and the overlay drawer
|
|
395
|
+
on a phone. Supply a `collapsedSidebar` node (and optionally `collapsedRailRange`,
|
|
396
|
+
default `{ min: 768, max: 1024 }`) — omit it and the layout stays the unchanged
|
|
397
|
+
two-tier scheme. New `CollapsedRail` component renders an icon-only rail from the
|
|
398
|
+
same `NavItem[]` (icon or first-letter glyph fallback, header/footer slots). New
|
|
399
|
+
`resolveRailMode` pure resolver + `RAIL_FULL_BREAKPOINT` / `DEFAULT_COLLAPSED_RAIL_MAX`.
|
|
400
|
+
`NavShell.sideRail.collapsed` builds it config-driven. New testID suffix
|
|
401
|
+
`-collapsed-sidebar`.
|
|
402
|
+
|
|
403
|
+
**`NavBar.barTheme` — dark bar over a light page.** An optional chrome-theme
|
|
404
|
+
override re-provisions the `@dloizides/ui-feedback` theme for the bar only (the AML
|
|
405
|
+
v1-console dark appbar over light content), re-passing the live `t` / `navigate` so
|
|
406
|
+
labels keep localizing. Forwarded via `NavShell.topBar.barTheme`. Style-only —
|
|
407
|
+
colours still flow through the theme. (Custom appbar backgrounds like AML's radial
|
|
408
|
+
glow stay expressible via `containerStyle`.)
|
|
409
|
+
|
|
410
|
+
**`PillNav` — coral cross-navigation (kefi).** A row of fully-rounded pills from a
|
|
411
|
+
`NavItem[]`, the active one wearing the accent (theme `palette.primary` = kefi's
|
|
412
|
+
coral). Folds in kefi's **visibility rule**: hides (renders `null`) unless there
|
|
413
|
+
are at least `minItems` entries (default 1; kefi passes 2).
|
|
414
|
+
|
|
415
|
+
**`DarkModeControl` — 3-way colour scheme (erevna/katalogos).** A controlled
|
|
416
|
+
Light / Dark / System control in two variants: `"segmented"` (a highlighted
|
|
417
|
+
segment row) and `"cycle"` (an icon+label button that advances to the next option
|
|
418
|
+
on press). Value-agnostic string options, pre-localized labels/hints.
|
|
419
|
+
|
|
420
|
+
**Refactor (no behaviour change).** `AppShell`'s page-state cards moved to
|
|
421
|
+
`appShellContent.tsx` (`useContentBody` / `ShellMessage` / `ShellState`) to keep
|
|
422
|
+
`AppShell` focused on layout; the shell's render tree is unchanged.
|
|
423
|
+
|
|
424
|
+
New exports: `NavShell` (+ `NavShellProps` / `NavShellLayout` / `NavShellTopBar` /
|
|
425
|
+
`NavShellSideRail` / `NavShellCollapsedRail`), `CollapsedRail` (+ `CollapsedRailProps`),
|
|
426
|
+
`PillNav` (+ `PillNavProps`), `DarkModeControl` (+ `DarkModeControlProps` /
|
|
427
|
+
`DarkModeOption` / `DarkModeVariant`), `resolveRailMode` (+ `RailMode` /
|
|
428
|
+
`CollapsedRailRange` / `ResolveRailModeArgs`), `RAIL_FULL_BREAKPOINT`,
|
|
429
|
+
`DEFAULT_COLLAPSED_RAIL_MAX`, `pillNavStyles`, `darkModeStyles`, `collapsedRailStyles`.
|
|
430
|
+
|
|
431
|
+
## 1.7.0
|
|
432
|
+
|
|
433
|
+
Two additive navigation capabilities on the horizontal bar, plus a unifying component.
|
|
434
|
+
|
|
435
|
+
**Priority+ overflow ("…" menu) on `NavBar`.** Above the `collapseBelow` breakpoint the
|
|
436
|
+
inline links sit on ONE non-wrapping row; when they don't all fit, a measure pass
|
|
437
|
+
(`useNavOverflow` + the pure `computeVisibleCount`) keeps the leading items that fit inline
|
|
438
|
+
and collapses the rest behind a **"More ▾"** trigger — the shared `@dloizides/ui-layout`
|
|
439
|
+
`ModalDropdown`, so the overflow menu is portalled, keyboard-navigable, and dismissed on
|
|
440
|
+
outside-click / Escape like every other menu. No item is ever cut off; the surplus simply
|
|
441
|
+
moves into the dropdown. When the active route lives in the overflow set the trigger wears
|
|
442
|
+
the accent pill so it stays visibly reachable. BELOW `collapseBelow` the whole set still
|
|
443
|
+
collapses into the responsive hamburger drawer (the two mechanisms never overlap). Two new
|
|
444
|
+
optional props localize the trigger: `overflowLabel` (visible + accessible, default `"More"`)
|
|
445
|
+
and `overflowHint`. New `NAV_TEST_IDS.navBarOverflow` (`navbar-overflow`).
|
|
446
|
+
|
|
447
|
+
**`Nav` — one component, two orientations.** `<Nav orientation="horizontal" />` renders the
|
|
448
|
+
`NavBar`; `<Nav orientation="vertical" />` renders the `Sidebar` — from the SAME `NavItem[]`,
|
|
449
|
+
so an app flips between a top bar and a side rail without swapping components. `orientation`
|
|
450
|
+
defaults to `"horizontal"`; the remaining props are a discriminated union of the delegate's
|
|
451
|
+
props (TypeScript enforces the right prop set per orientation). Vertical nav is the existing
|
|
452
|
+
`Sidebar` (stacked, vertically scrolling — no overflow menu needed).
|
|
453
|
+
|
|
454
|
+
**Backwards compatible.** Purely additive — `Sidebar`, `Topbar`, `NavBar`, `AppShell`, and
|
|
455
|
+
the role-gating helpers keep their exact signatures; the new overflow props are optional
|
|
456
|
+
(default `"More"` / `""`) so aml-v2 (the only `NavBar` consumer) and every `Sidebar`/`Topbar`
|
|
457
|
+
consumer (erevna / katalogos / kefi / agora) are untouched. New exports (`Nav`, `NavProps`,
|
|
458
|
+
`NavOrientation`, `NavOverflowMenu`, `NavOverflowMenuProps`, `NAV_LINK_GAP`) are additive.
|
|
459
|
+
|
|
460
|
+
## 1.6.1
|
|
461
|
+
|
|
462
|
+
Keep the expanded links on ONE row — the row must be `nowrap` so `flex-shrink` distributes
|
|
463
|
+
negative space (a `wrap` row drops the right slot onto a 2nd line instead of shrinking the
|
|
464
|
+
links). Renames the internal expanded links style to `linksRow` (grow + shrink-to-0,
|
|
465
|
+
`minWidth:0`, `overflow:hidden`) with a per-link `linkCell` (never shrinks, so each link keeps
|
|
466
|
+
its natural measured width). Brand + right stay pinned (`flexShrink:0`).
|
|
467
|
+
|
|
468
|
+
## 1.6.0
|
|
469
|
+
|
|
470
|
+
Keep the bar to ONE row — horizontal-scroll / clip the links instead of wrapping the bar
|
|
471
|
+
onto a second line when brand + links + right slot overflow the available width.
|
|
472
|
+
|
|
473
|
+
## 1.5.0
|
|
474
|
+
|
|
475
|
+
Make the `AppShell` back-office `sidebar` layout **responsive** — a mobile drawer.
|
|
476
|
+
|
|
477
|
+
The `sidebar` slot (1.4.0) always rendered a persistent fixed-width left rail in a
|
|
478
|
+
`flexDirection:'row'` body with no width breakpoint, so below ~768px the content column
|
|
479
|
+
starved (headings wrapped a character per line, inputs truncated) — the merchant admin was
|
|
480
|
+
unusable on a phone. Now `AppShell` reads `useWindowDimensions`:
|
|
481
|
+
|
|
482
|
+
- **Desktop (≥768px): IDENTICAL to 1.4.0** — the persistent rail + row layout, untouched.
|
|
483
|
+
Purely additive; no consuming product's desktop layout changes.
|
|
484
|
+
- **Below 768px:** the rail is not rendered beside the content; instead a **hamburger**
|
|
485
|
+
(in the header) opens the same `Sidebar` as an overlay **drawer** (a tap-to-close scrim +
|
|
486
|
+
a left panel) and the content takes the full width. The drawer closes on a scrim tap and
|
|
487
|
+
on a nav-item press (web: a bubbled click on a `role="button"` / link inside the panel).
|
|
488
|
+
|
|
489
|
+
Accessibility: the hamburger has a testID + `accessibilityLabel` + `accessibilityHint` and a
|
|
490
|
+
themed focus ring; the drawer panel is `role="dialog"` `aria-modal`; the scrim is a labelled
|
|
491
|
+
button. New optional prop `mobileMenu?: Partial<DrawerLabels>` supplies pre-localized
|
|
492
|
+
open/close labels (English defaults otherwise — this package has no i18n). New testID
|
|
493
|
+
suffixes: `-menu-toggle`, `-drawer`, `-drawer-scrim`.
|
|
494
|
+
|
|
495
|
+
**Backwards compatible.** No breaking prop changes — consumers pass the same `sidebar` slot.
|
|
496
|
+
Omit `sidebar` and the tree is byte-identical to 1.4.0 (erevna / katalogos / kefi untouched).
|
|
497
|
+
Only a `sidebar` consumer on a narrow viewport sees the drawer. Pinned by tests asserting the
|
|
498
|
+
rail is present on desktop and absent (hamburger present) on a narrow viewport.
|
|
499
|
+
|
|
500
|
+
## 1.4.0
|
|
501
|
+
|
|
502
|
+
Add an optional **`sidebar`** slot to `AppShell` — the back-office layout.
|
|
503
|
+
|
|
504
|
+
`AppShell` could only stack vertically (header → nav strip → banner → scrolling content),
|
|
505
|
+
so a product with a persistent LEFT RAIL (Agora's merchant admin: products / categories /
|
|
506
|
+
coupons / settings) had to hand-compose `Topbar` + `Sidebar` + a `ScrollView` row and give
|
|
507
|
+
up the shell's auth gate and loading/error/forbidden state cards. Supplying `sidebar` now
|
|
508
|
+
lays the body out as a ROW — the rail on the left, the width-disciplined scrolling content
|
|
509
|
+
column on the right — while every existing feature (gate, state cards, `width`,
|
|
510
|
+
`chromeAlignment`) keeps working.
|
|
511
|
+
|
|
512
|
+
The new region is exposed as `` `${testID}${APP_SHELL_SUFFIX.sidebar}` `` (`-sidebar`).
|
|
513
|
+
|
|
514
|
+
**Backwards compatible.** Omit `sidebar` and the render tree is byte-identical to 1.3.0 —
|
|
515
|
+
erevna / katalogos / kefi / aml-v2 are untouched. Pinned by a test asserting no `-sidebar`
|
|
516
|
+
region exists when the slot is omitted.
|
|
517
|
+
|
|
518
|
+
## 1.3.0
|
|
519
|
+
|
|
520
|
+
Give the `NavBar` the v1 `.gn-links` look + a full accessibility pass (and extend the
|
|
521
|
+
same a11y bar to `Sidebar`/`Topbar`).
|
|
522
|
+
|
|
523
|
+
**Look & hover (NavBar).** Each link is a rounded-pill target with the v1 hover
|
|
524
|
+
affordance — muted at rest, and on HOVER the text brightens to full contrast while a
|
|
525
|
+
subtle rounded-pill background appears; the ACTIVE link is a solid accent pill. All
|
|
526
|
+
theme-driven (rest = `textSecondary`, hover text = `text`, hover bg = `surfaceElevated`,
|
|
527
|
+
active/ring = `palette.primary`). Hover/focus are web-only (`onHoverIn/Out`, `onFocus/Blur`
|
|
528
|
+
via `Pressable`); native stays at the rest style. The hover cross-fade is cheap CSS and is
|
|
529
|
+
disabled under `prefers-reduced-motion: reduce` (new `useReducedMotion`). Backwards
|
|
530
|
+
compatible — the hover pill is the default (aml-v2 is the only `NavBar` consumer) and the
|
|
531
|
+
brand/right slots, responsive hamburger collapse, and `containerStyle` width behaviour are
|
|
532
|
+
unchanged.
|
|
533
|
+
|
|
534
|
+
**Accessibility.** NavBar toggle now carries `aria-controls` pointing at the collapsible
|
|
535
|
+
links region (id via `nativeID`), keeps `aria-expanded`, and gains a themed keyboard focus
|
|
536
|
+
ring; links keep `aria-current="page"` + `role="link"` and gain a focus ring; link/toggle
|
|
537
|
+
touch targets are now ≥44×44. `Sidebar` leaves gain `aria-current="page"` + a focus ring;
|
|
538
|
+
`Topbar` action buttons gain a focus ring (new internal `FocusableTouchable`). Focus rings
|
|
539
|
+
never remove the outline without a themed replacement.
|
|
540
|
+
|
|
541
|
+
## 1.2.0
|
|
542
|
+
|
|
543
|
+
Add `NavBar` — a config-driven **horizontal top navigation bar**, the top-bar
|
|
544
|
+
counterpart to `Sidebar`. It renders the same caller-supplied `NavItem[]` as inline
|
|
545
|
+
links across the top (brand left slot + inline links + free-form right slot), with
|
|
546
|
+
an active-link highlight (`aria-current="page"`) and a responsive collapse to a
|
|
547
|
+
hamburger toggle below a configurable breakpoint (`collapseBelow`, default `760`) —
|
|
548
|
+
mirroring the v1 AML console `.global-nav`. Theme-driven (UiProvider colours),
|
|
549
|
+
`role="navigation"` landmark, keyboard-operable links. Purely additive: `Sidebar`,
|
|
550
|
+
`Topbar`, `AppShell`, and the role-gating helpers are unchanged. New `NAV_TEST_IDS`
|
|
551
|
+
entries `navBarToggle` / `navBarLinks`.
|
|
552
|
+
|
|
553
|
+
## 1.0.0
|
|
554
|
+
|
|
555
|
+
Initial release. `Sidebar` (leaf + expandable items, active-route highlight, header/footer
|
|
556
|
+
slots), `Topbar` (logo/language/notification/user/account/logout slots), and the
|
|
557
|
+
`accessibleNavItems` / `roleRoutesToNavItems` role-gating helpers (reusing
|
|
558
|
+
`resolveAccessibleRoutes` from `@dloizides/auth-web`). Chrome + metrics ported verbatim from
|
|
559
|
+
the byte-identical erevna-web / katalogos-web nav twins; every colour reads from the
|
|
560
|
+
`@dloizides/ui-feedback` UiProvider theme.
|