@dloizides/ui-nav 1.7.0 → 1.8.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/ADOPTION.md +83 -0
- package/CHANGELOG.md +58 -0
- package/README.md +83 -0
- package/dist/index.d.mts +533 -79
- package/dist/index.d.ts +533 -79
- package/dist/index.js +437 -69
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +430 -72
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/ADOPTION.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Adopting `@dloizides/ui-nav` ≥ 1.8.0 — per-portal migration map
|
|
2
|
+
|
|
3
|
+
This is the map the migration agents follow to move each RN-web portal off its
|
|
4
|
+
bespoke nav/shell onto the shared package. **Only STYLE varies per app** — layout
|
|
5
|
+
structure and behaviour are shared. Nothing from the current shells is lost:
|
|
6
|
+
everything is either a component, an optional prop, or a render slot.
|
|
7
|
+
|
|
8
|
+
> Migrations are a **separate wave** — do NOT edit the apps as part of building the
|
|
9
|
+
> package. This doc tells each app which `layout`, which slots, and which
|
|
10
|
+
> components to use.
|
|
11
|
+
|
|
12
|
+
## The one decision: which `layout`?
|
|
13
|
+
|
|
14
|
+
| Portal | Current shell | `NavShell` `layout` | Notes |
|
|
15
|
+
|--------|---------------|---------------------|-------|
|
|
16
|
+
| **AML v2** (`consoleChrome` / AuthedShell / PublicShell) | top bar only (dark, radial glow) | `"top"` | `topBar.barTheme` = dark chrome; brand/user/language in `topBar.brand` / `topBar.right`; overflow "…" built-in |
|
|
17
|
+
| **Agora** (`AdminShell`) | side rail + top bar (already `AppShell`) | `"side"` | `sideRail` = sections; `header` = `<Topbar logout=… />` |
|
|
18
|
+
| **Zygos** (`ZygosShell`) | side rail + top bar (already `AppShell`) | `"side"` | as Agora, plus `Topbar.user` block |
|
|
19
|
+
| **Kefi** (`DashboardShell` / `AppHeader` / `DashboardNav`) | top bar + coral pill strip | `"top"` | `header`/`topBar` = brand + account; put `<PillNav minItems={2} />` in the `nav` slot |
|
|
20
|
+
| **Erevna** (`ProtectedLayout` + Sidebar/Topbar) | 3-tier rail + top bar | `"side"` **+** `sideRail.collapsed` | full rail → collapsed icon rail → drawer |
|
|
21
|
+
| **Katalogos** | byte-identical to erevna | `"side"` **+** `sideRail.collapsed` | same as erevna |
|
|
22
|
+
|
|
23
|
+
## Slot mapping (what fills each slot)
|
|
24
|
+
|
|
25
|
+
### AML v2 — `layout="top"`
|
|
26
|
+
- `topBar.items` ← `editionNavItems(visibleNavItems(isAdmin), selfHosted)` → `NavItem[]`
|
|
27
|
+
(keep the app-side edition/self-host filtering; pass the result).
|
|
28
|
+
- `topBar.brand` ← the `ConsoleBrand` block (brand→marketing-vs-console-home routing
|
|
29
|
+
stays in the app; `brand` is a free-form node).
|
|
30
|
+
- `topBar.right` ← `<ShellLanguageSwitcher />` + `<UserChip />` (or the "Sign in" pill
|
|
31
|
+
for anonymous `PublicShell`). Both are already `right`-slot nodes today.
|
|
32
|
+
- `topBar.barTheme` ← the navy `useNavTheme()` clone (dark bar over light page).
|
|
33
|
+
- `topBar.containerStyle` ← the 1180px cap + the radial-glow `appbarBarStyle` (a web
|
|
34
|
+
`background`/`backgroundImage` gradient cast through `ViewStyle`). **Style only.**
|
|
35
|
+
- `topBar.overflowLabel` / `collapseBelow={760}` ← as today.
|
|
36
|
+
- External-route split (`isExternalRoute` → `Linking.openURL` vs `router.navigate`)
|
|
37
|
+
stays in the app's `onNavigate`. `ToastHost` stays app-side (it is `@dloizides/ui-feedback`).
|
|
38
|
+
|
|
39
|
+
### Agora / Zygos — `layout="side"`
|
|
40
|
+
- `sideRail.items` ← the localized `NAV_SECTIONS` (`renderIcon` per item).
|
|
41
|
+
- `sideRail.title` / `regionLabel` / `navigateHint` ← as today.
|
|
42
|
+
- `header` ← `<Topbar logout={…} user={…?} />` (Zygos passes `user`; Agora does not).
|
|
43
|
+
- `mobileMenu` ← the drawer open/close labels (Agora passes them; Zygos gets English defaults).
|
|
44
|
+
- Nothing else changes — these already consume `AppShell` + `Sidebar` + `Topbar`.
|
|
45
|
+
|
|
46
|
+
### Kefi — `layout="top"` + `nav` slot
|
|
47
|
+
- `header` ← `<Topbar account={{ displayName, onLogout }} left={<Wordmark/>} logout={…} />`
|
|
48
|
+
(the rich `AccountState`; keep omitting tenant/plan to avoid the round-trip).
|
|
49
|
+
- `nav` ← `<PillNav items={accessibleNavItems(user, postLoginRouteTable, FM)} minItems={2}
|
|
50
|
+
pathname={…} onNavigate={…} regionLabel={…} />` — this folds in the coral pills AND
|
|
51
|
+
the ≥2-dashboards rule (no more app-local `MIN_DASHBOARDS_FOR_NAV`).
|
|
52
|
+
- `gate` / `state` / `banner` / `width` / `chromeAlignment` / `contentPadding` ← exactly
|
|
53
|
+
the `AppShell` props kefi already passes (unchanged).
|
|
54
|
+
- Coral colour = theme `palette.primary`; pill radius/font = theme/`containerStyle`. **Style only.**
|
|
55
|
+
|
|
56
|
+
### Erevna / Katalogos — `layout="side"` + `sideRail.collapsed`
|
|
57
|
+
- `sideRail.items` ← `toNavItems(groupSidebarItems(moduleRegistry.getSidebarItemsForRoles(roles)))`
|
|
58
|
+
(role-gated, grouped/expandable — unchanged pipeline).
|
|
59
|
+
- `sideRail.header` ← the Home shortcut; `sideRail.footer` ← `<DarkModeControl variant="segmented"
|
|
60
|
+
options={[Light, Dark, System]} value=… onChange={setDarkModePreference} />` + Logout
|
|
61
|
+
(replaces the bespoke `DarkModeToggle`). `sideRail.renderChevron` ← chevronUp/Down.
|
|
62
|
+
- `sideRail.collapsed` ← `{ items, header, footer, range: { min, max } }` → the tablet
|
|
63
|
+
**collapsed icon rail** (replaces `MobileSidebarCollapsed`). Its footer can hold the
|
|
64
|
+
`DarkModeControl variant="cycle"` (replaces `MobileDarkModeButton`).
|
|
65
|
+
- `header` ← `<Topbar left={<TenantLogo/>} language={…} notificationSlot={<SafeNotificationBell/>}
|
|
66
|
+
user={…} logout={…} account={…?} />`.
|
|
67
|
+
- `mobileMenu` ← the drawer open/close labels (the phone drawer re-hosts the full rail).
|
|
68
|
+
- `gate` (auth) / `banner` (`VerificationPendingBanner`) / `state` ← the `AppShell` props.
|
|
69
|
+
- **Still app-side (not lost, not shared):** the `!isUser` role-conditional rail hide
|
|
70
|
+
(just pass no `sideRail` for plain users), the `SkipNavLink` (compose in `children`),
|
|
71
|
+
and — if the exact right-anchored mobile *account* drawer (user+dark+lang+account) is
|
|
72
|
+
wanted verbatim — keep `MobileTopbar` in the `header` slot; the shared left drawer
|
|
73
|
+
re-hosts the rail, which covers the section-navigation case.
|
|
74
|
+
- **Style only:** scrim colour/opacity, slide offsets/durations, segmented-toggle
|
|
75
|
+
highlight, drawer panel colours → theme tokens / `containerStyle`.
|
|
76
|
+
|
|
77
|
+
## Contract reminders (unchanged)
|
|
78
|
+
|
|
79
|
+
- No FM / router / icon-set / store imports in the package. Labels are pre-localized
|
|
80
|
+
strings; icons are `renderIcon` render slots; every colour flows through the
|
|
81
|
+
`@dloizides/ui-feedback` `UiProvider` theme.
|
|
82
|
+
- `Sidebar` / `Topbar` / `NavBar` / `Nav` / `AppShell` and the role helpers keep their
|
|
83
|
+
exact 1.7.0 signatures — migrate incrementally; a partial migration compiles.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.0
|
|
4
|
+
|
|
5
|
+
The navigation shell becomes the ONE shell for every portal: a first-class
|
|
6
|
+
**top | side | both** layout, plus the capabilities harvested from every RN-web
|
|
7
|
+
portal's bespoke nav/shell so nothing is lost when they migrate. Purely additive —
|
|
8
|
+
`Sidebar`, `Topbar`, `NavBar`, `Nav`, `AppShell`, and the role helpers keep their
|
|
9
|
+
exact signatures; every new prop/component is optional.
|
|
10
|
+
|
|
11
|
+
**`NavShell` — top bar, side rail, or BOTH.** One component owns the whole page
|
|
12
|
+
composition, chosen by a single `layout` prop (`'top' | 'side' | 'both'`). It wires
|
|
13
|
+
a top bar into `AppShell`'s `header` slot and a side rail into its `sidebar` slot,
|
|
14
|
+
and inherits every `AppShell` capability (auth gate, loading/error/forbidden state
|
|
15
|
+
cards, width policy, banner, the responsive hamburger + overlay drawer). `'both'`
|
|
16
|
+
is the AML-console shape — a top bar (brand / global links + overflow "…" / user)
|
|
17
|
+
AND a side rail (sections) together — now first-class instead of hand-wired. The
|
|
18
|
+
top bar is built from a `topBar` config (a `NavBar`), the side rail from a
|
|
19
|
+
`sideRail` config (a `Sidebar`); an explicit `header` node overrides the built top
|
|
20
|
+
bar for apps that want a structured `Topbar`.
|
|
21
|
+
|
|
22
|
+
**Three-tier responsive rail (`AppShell.collapsedSidebar`).** Opts the `sidebar`
|
|
23
|
+
layout into the erevna/katalogos scheme: the full rail on desktop, an intermediate
|
|
24
|
+
persistent **collapsed (icon-only) rail** on a tablet band, and the overlay drawer
|
|
25
|
+
on a phone. Supply a `collapsedSidebar` node (and optionally `collapsedRailRange`,
|
|
26
|
+
default `{ min: 768, max: 1024 }`) — omit it and the layout stays the unchanged
|
|
27
|
+
two-tier scheme. New `CollapsedRail` component renders an icon-only rail from the
|
|
28
|
+
same `NavItem[]` (icon or first-letter glyph fallback, header/footer slots). New
|
|
29
|
+
`resolveRailMode` pure resolver + `RAIL_FULL_BREAKPOINT` / `DEFAULT_COLLAPSED_RAIL_MAX`.
|
|
30
|
+
`NavShell.sideRail.collapsed` builds it config-driven. New testID suffix
|
|
31
|
+
`-collapsed-sidebar`.
|
|
32
|
+
|
|
33
|
+
**`NavBar.barTheme` — dark bar over a light page.** An optional chrome-theme
|
|
34
|
+
override re-provisions the `@dloizides/ui-feedback` theme for the bar only (the AML
|
|
35
|
+
v1-console dark appbar over light content), re-passing the live `t` / `navigate` so
|
|
36
|
+
labels keep localizing. Forwarded via `NavShell.topBar.barTheme`. Style-only —
|
|
37
|
+
colours still flow through the theme. (Custom appbar backgrounds like AML's radial
|
|
38
|
+
glow stay expressible via `containerStyle`.)
|
|
39
|
+
|
|
40
|
+
**`PillNav` — coral cross-navigation (kefi).** A row of fully-rounded pills from a
|
|
41
|
+
`NavItem[]`, the active one wearing the accent (theme `palette.primary` = kefi's
|
|
42
|
+
coral). Folds in kefi's **visibility rule**: hides (renders `null`) unless there
|
|
43
|
+
are at least `minItems` entries (default 1; kefi passes 2).
|
|
44
|
+
|
|
45
|
+
**`DarkModeControl` — 3-way colour scheme (erevna/katalogos).** A controlled
|
|
46
|
+
Light / Dark / System control in two variants: `"segmented"` (a highlighted
|
|
47
|
+
segment row) and `"cycle"` (an icon+label button that advances to the next option
|
|
48
|
+
on press). Value-agnostic string options, pre-localized labels/hints.
|
|
49
|
+
|
|
50
|
+
**Refactor (no behaviour change).** `AppShell`'s page-state cards moved to
|
|
51
|
+
`appShellContent.tsx` (`useContentBody` / `ShellMessage` / `ShellState`) to keep
|
|
52
|
+
`AppShell` focused on layout; the shell's render tree is unchanged.
|
|
53
|
+
|
|
54
|
+
New exports: `NavShell` (+ `NavShellProps` / `NavShellLayout` / `NavShellTopBar` /
|
|
55
|
+
`NavShellSideRail` / `NavShellCollapsedRail`), `CollapsedRail` (+ `CollapsedRailProps`),
|
|
56
|
+
`PillNav` (+ `PillNavProps`), `DarkModeControl` (+ `DarkModeControlProps` /
|
|
57
|
+
`DarkModeOption` / `DarkModeVariant`), `resolveRailMode` (+ `RailMode` /
|
|
58
|
+
`CollapsedRailRange` / `ResolveRailModeArgs`), `RAIL_FULL_BREAKPOINT`,
|
|
59
|
+
`DEFAULT_COLLAPSED_RAIL_MAX`, `pillNavStyles`, `darkModeStyles`, `collapsedRailStyles`.
|
|
60
|
+
|
|
3
61
|
## 1.7.0
|
|
4
62
|
|
|
5
63
|
Two additive navigation capabilities on the horizontal bar, plus a unifying component.
|
package/README.md
CHANGED
|
@@ -101,6 +101,89 @@ import { Nav, type NavItem } from '@dloizides/ui-nav';
|
|
|
101
101
|
|
|
102
102
|
`Nav` is purely additive: `Sidebar` / `Topbar` / `NavBar` stay exported and unchanged.
|
|
103
103
|
|
|
104
|
+
### One shell, three layouts — top | side | **both** (`NavShell`)
|
|
105
|
+
|
|
106
|
+
Where `Nav` answers the single-axis question (a `NavBar` **or** a `Sidebar`),
|
|
107
|
+
`NavShell` owns the whole page composition and renders a **top bar**, a **side
|
|
108
|
+
rail**, or **BOTH together**, chosen by a single `layout` prop. It wires a top bar
|
|
109
|
+
into `AppShell`'s `header` slot and a side rail into its `sidebar` slot, and
|
|
110
|
+
inherits every `AppShell` capability (auth gate, loading/error/forbidden state
|
|
111
|
+
cards, width policy, banner, the responsive hamburger + overlay drawer).
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { NavShell, type NavItem } from '@dloizides/ui-nav';
|
|
115
|
+
|
|
116
|
+
// layout="both" — a top bar (brand / global links + overflow "…" / user) AND a
|
|
117
|
+
// side rail (sections) together (the AML-console shape), now first-class.
|
|
118
|
+
<NavShell
|
|
119
|
+
layout="both"
|
|
120
|
+
pathname={usePathname()}
|
|
121
|
+
onNavigate={(route) => router.push(route)}
|
|
122
|
+
regionLabel={FM('a11y.nav')}
|
|
123
|
+
topBar={{
|
|
124
|
+
items: globalItems, // top links (overflow-capable)
|
|
125
|
+
brand: <BrandLogo />,
|
|
126
|
+
right: <><LanguageSwitcher /><UserChip /></>,
|
|
127
|
+
overflowLabel: FM('menu.more'),
|
|
128
|
+
barTheme: darkChromeTheme, // optional: dark bar over a light page
|
|
129
|
+
}}
|
|
130
|
+
sideRail={{
|
|
131
|
+
items: sectionItems, // side sections (leaf + expandable)
|
|
132
|
+
title: FM('menu.title'),
|
|
133
|
+
footer: <><DarkModeControl … /><LogoutButton /></>,
|
|
134
|
+
collapsed: { range: { min: 768, max: 1024 } }, // optional 3-tier icon rail
|
|
135
|
+
}}
|
|
136
|
+
gate={{ pending, authenticated, onRedirect: () => router.push('/login') }}
|
|
137
|
+
state={{ loading, error, forbidden }}
|
|
138
|
+
width={{ max: 1180, wideMax: 1400, wideMinViewport: 1600 }}
|
|
139
|
+
testID="app-shell"
|
|
140
|
+
>
|
|
141
|
+
<PageContent />
|
|
142
|
+
</NavShell>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- `layout="top"` → a top bar only (`topBar`); no rail.
|
|
146
|
+
- `layout="side"` → a header + a persistent left rail of sections (`sideRail`); pass a
|
|
147
|
+
structured `<Topbar>` as `header` for the language/notification/user slots.
|
|
148
|
+
- `layout="both"` → both, together.
|
|
149
|
+
|
|
150
|
+
`NavShell` composes existing components, so it stays purely additive — `AppShell`,
|
|
151
|
+
`NavBar`, and `Sidebar` are unchanged.
|
|
152
|
+
|
|
153
|
+
### Three-tier responsive rail (`AppShell.collapsedSidebar` / `CollapsedRail`)
|
|
154
|
+
|
|
155
|
+
Supply a `collapsedSidebar` (or `NavShell`'s `sideRail.collapsed`) to opt the rail
|
|
156
|
+
into the erevna/katalogos scheme: the full rail on desktop, an intermediate
|
|
157
|
+
persistent **collapsed (icon-only) rail** on a tablet band `[min, max)` (default
|
|
158
|
+
`{ min: 768, max: 1024 }`), and the overlay drawer on a phone. Omit it and the
|
|
159
|
+
layout stays the unchanged two-tier scheme (full rail ≥768px, drawer below).
|
|
160
|
+
`CollapsedRail` renders the icon rail from the same `NavItem[]` (each item's
|
|
161
|
+
`renderIcon`, or a first-letter glyph fallback), with header/footer slots. The pure
|
|
162
|
+
`resolveRailMode` decides the tier and is directly unit-testable.
|
|
163
|
+
|
|
164
|
+
### Dark bar over a light page (`NavBar.barTheme`)
|
|
165
|
+
|
|
166
|
+
Pass `barTheme` (a `UiTheme`) to re-provision the theme for the bar **only** — a
|
|
167
|
+
dark appbar above a light workspace (the AML v1 console). The live `t` / `navigate`
|
|
168
|
+
are re-passed so labels keep localizing; every colour still flows through the theme
|
|
169
|
+
(style-only). Custom appbar backgrounds (e.g. a radial-glow gradient) stay
|
|
170
|
+
expressible via `containerStyle`.
|
|
171
|
+
|
|
172
|
+
### Cross-navigation pills (`PillNav`)
|
|
173
|
+
|
|
174
|
+
A row of fully-rounded pills from a `NavItem[]` — kefi's coral dashboard switcher.
|
|
175
|
+
The active pill wears the accent (theme `palette.primary`). Folds in kefi's rule:
|
|
176
|
+
it hides (renders `null`) unless there are at least `minItems` entries (default 1;
|
|
177
|
+
kefi passes `2`). Drop it into `AppShell`/`NavShell`'s `nav` slot.
|
|
178
|
+
|
|
179
|
+
### Colour-scheme control (`DarkModeControl`)
|
|
180
|
+
|
|
181
|
+
A controlled Light / Dark / System control (erevna/katalogos), in two variants:
|
|
182
|
+
`"segmented"` (a highlighted segment row) and `"cycle"` (an icon+label button that
|
|
183
|
+
advances to the next option per press). Options are pre-localized and value-agnostic
|
|
184
|
+
(strings), so map your own preference enum without this package importing it. Put it
|
|
185
|
+
in a `Sidebar`/`CollapsedRail` footer or a `NavBar` right slot.
|
|
186
|
+
|
|
104
187
|
An app that renders a `Sidebar` today switches to a top bar by swapping the
|
|
105
188
|
`Sidebar` element for `NavBar` (same `items` / `pathname` / `onNavigate`) — e.g.
|
|
106
189
|
the AML v2 app drops `NavBar` into the `AppShell` `header` (or `nav`) slot so its
|