@dloizides/ui-nav 1.2.0 → 1.4.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 CHANGED
@@ -1,22 +1,63 @@
1
- # Changelog
2
-
3
- ## 1.2.0
4
-
5
- Add `NavBar` a config-driven **horizontal top navigation bar**, the top-bar
6
- counterpart to `Sidebar`. It renders the same caller-supplied `NavItem[]` as inline
7
- links across the top (brand left slot + inline links + free-form right slot), with
8
- an active-link highlight (`aria-current="page"`) and a responsive collapse to a
9
- hamburger toggle below a configurable breakpoint (`collapseBelow`, default `760`)
10
- mirroring the v1 AML console `.global-nav`. Theme-driven (UiProvider colours),
11
- `role="navigation"` landmark, keyboard-operable links. Purely additive: `Sidebar`,
12
- `Topbar`, `AppShell`, and the role-gating helpers are unchanged. New `NAV_TEST_IDS`
13
- entries `navBarToggle` / `navBarLinks`.
14
-
15
- ## 1.0.0
16
-
17
- Initial release. `Sidebar` (leaf + expandable items, active-route highlight, header/footer
18
- slots), `Topbar` (logo/language/notification/user/account/logout slots), and the
19
- `accessibleNavItems` / `roleRoutesToNavItems` role-gating helpers (reusing
20
- `resolveAccessibleRoutes` from `@dloizides/auth-web`). Chrome + metrics ported verbatim from
21
- the byte-identical erevna-web / katalogos-web nav twins; every colour reads from the
22
- `@dloizides/ui-feedback` UiProvider theme.
1
+ # Changelog
2
+
3
+ ## 1.4.0
4
+
5
+ Add an optional **`sidebar`** slot to `AppShell` the back-office layout.
6
+
7
+ `AppShell` could only stack vertically (header nav strip banner scrolling content),
8
+ so a product with a persistent LEFT RAIL (Agora's merchant admin: products / categories /
9
+ coupons / settings) had to hand-compose `Topbar` + `Sidebar` + a `ScrollView` row and give
10
+ up the shell's auth gate and loading/error/forbidden state cards. Supplying `sidebar` now
11
+ lays the body out as a ROW — the rail on the left, the width-disciplined scrolling content
12
+ column on the right while every existing feature (gate, state cards, `width`,
13
+ `chromeAlignment`) keeps working.
14
+
15
+ The new region is exposed as `` `${testID}${APP_SHELL_SUFFIX.sidebar}` `` (`-sidebar`).
16
+
17
+ **Backwards compatible.** Omit `sidebar` and the render tree is byte-identical to 1.3.0 —
18
+ erevna / katalogos / kefi / aml-v2 are untouched. Pinned by a test asserting no `-sidebar`
19
+ region exists when the slot is omitted.
20
+
21
+ ## 1.3.0
22
+
23
+ Give the `NavBar` the v1 `.gn-links` look + a full accessibility pass (and extend the
24
+ same a11y bar to `Sidebar`/`Topbar`).
25
+
26
+ **Look & hover (NavBar).** Each link is a rounded-pill target with the v1 hover
27
+ affordance — muted at rest, and on HOVER the text brightens to full contrast while a
28
+ subtle rounded-pill background appears; the ACTIVE link is a solid accent pill. All
29
+ theme-driven (rest = `textSecondary`, hover text = `text`, hover bg = `surfaceElevated`,
30
+ active/ring = `palette.primary`). Hover/focus are web-only (`onHoverIn/Out`, `onFocus/Blur`
31
+ via `Pressable`); native stays at the rest style. The hover cross-fade is cheap CSS and is
32
+ disabled under `prefers-reduced-motion: reduce` (new `useReducedMotion`). Backwards
33
+ compatible — the hover pill is the default (aml-v2 is the only `NavBar` consumer) and the
34
+ brand/right slots, responsive hamburger collapse, and `containerStyle` width behaviour are
35
+ unchanged.
36
+
37
+ **Accessibility.** NavBar toggle now carries `aria-controls` pointing at the collapsible
38
+ links region (id via `nativeID`), keeps `aria-expanded`, and gains a themed keyboard focus
39
+ ring; links keep `aria-current="page"` + `role="link"` and gain a focus ring; link/toggle
40
+ touch targets are now ≥44×44. `Sidebar` leaves gain `aria-current="page"` + a focus ring;
41
+ `Topbar` action buttons gain a focus ring (new internal `FocusableTouchable`). Focus rings
42
+ never remove the outline without a themed replacement.
43
+
44
+ ## 1.2.0
45
+
46
+ Add `NavBar` — a config-driven **horizontal top navigation bar**, the top-bar
47
+ counterpart to `Sidebar`. It renders the same caller-supplied `NavItem[]` as inline
48
+ links across the top (brand left slot + inline links + free-form right slot), with
49
+ an active-link highlight (`aria-current="page"`) and a responsive collapse to a
50
+ hamburger toggle below a configurable breakpoint (`collapseBelow`, default `760`) —
51
+ mirroring the v1 AML console `.global-nav`. Theme-driven (UiProvider colours),
52
+ `role="navigation"` landmark, keyboard-operable links. Purely additive: `Sidebar`,
53
+ `Topbar`, `AppShell`, and the role-gating helpers are unchanged. New `NAV_TEST_IDS`
54
+ entries `navBarToggle` / `navBarLinks`.
55
+
56
+ ## 1.0.0
57
+
58
+ Initial release. `Sidebar` (leaf + expandable items, active-route highlight, header/footer
59
+ slots), `Topbar` (logo/language/notification/user/account/logout slots), and the
60
+ `accessibleNavItems` / `roleRoutesToNavItems` role-gating helpers (reusing
61
+ `resolveAccessibleRoutes` from `@dloizides/auth-web`). Chrome + metrics ported verbatim from
62
+ the byte-identical erevna-web / katalogos-web nav twins; every colour reads from the
63
+ `@dloizides/ui-feedback` UiProvider theme.
package/README.md CHANGED
@@ -74,8 +74,21 @@ import { NavBar, type NavItem } from '@dloizides/ui-nav';
74
74
  An app that renders a `Sidebar` today switches to a top bar by swapping the
75
75
  `Sidebar` element for `NavBar` (same `items` / `pathname` / `onNavigate`) — e.g.
76
76
  the AML v2 app drops `NavBar` into the `AppShell` `header` (or `nav`) slot so its
77
- links sit across the top like the rest of the console. Active links expose
78
- `aria-current="page"`; the container is a `role="navigation"` landmark.
77
+ links sit across the top like the rest of the console.
78
+
79
+ Each link is a rounded-pill target with the v1 `.gn-links` affordance: muted at
80
+ rest, brightening its text + showing a subtle pill background on **hover**, and a
81
+ solid accent pill when **active**. All colours are theme-driven (rest =
82
+ `textSecondary`, hover text = `text`, hover bg = `surfaceElevated`, active/ring =
83
+ `palette.primary`); hover/focus are web-only and the hover cross-fade honours
84
+ `prefers-reduced-motion`.
85
+
86
+ **Accessibility.** The container is a `role="navigation"` landmark named by
87
+ `regionLabel`; active links expose `aria-current="page"` + `role="link"`; the
88
+ responsive hamburger carries `role="button"`, `aria-expanded`, and `aria-controls`
89
+ pointing at the collapsible links region; every link/toggle is keyboard-operable
90
+ (Enter/Space) with a themed focus ring and a ≥44×44 touch target. `Sidebar` leaves
91
+ carry `aria-current="page"` and `Topbar` buttons a focus ring under the same bar.
79
92
 
80
93
  ### Role gating
81
94
 
package/dist/index.d.mts CHANGED
@@ -154,10 +154,17 @@ declare const Topbar: ({ left, language, notificationSlot, user, account, logout
154
154
  * collapses the links behind a hamburger toggle — mirroring the v1 AML console's
155
155
  * responsive `.global-nav`.
156
156
  *
157
+ * Look + feel: each link is a rounded-pill target with the v1 `.gn-links a`
158
+ * affordance — muted at rest, brightening text + a subtle pill background on
159
+ * HOVER, and a solid accent pill when ACTIVE. All colours are THEME-DRIVEN from
160
+ * the `@dloizides/ui-feedback` UiProvider (rest=`textSecondary`, hover text=`text`,
161
+ * hover bg=`surfaceElevated`, active=`palette.primary`, ring=`palette.primary`).
162
+ * Hover/focus are web-only (react-native-web); native stays at the rest style.
163
+ *
157
164
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
158
165
  * icon set, or store imports. Labels are pre-localized strings, icons are render
159
- * slots, and every colour is read from the `@dloizides/ui-feedback` UiProvider
160
- * theme. This is purely additive — `Sidebar`/`Topbar` are unchanged.
166
+ * slots, and every colour is read from the theme. This is purely additive —
167
+ * `Sidebar`/`Topbar` are unchanged.
161
168
  */
162
169
 
163
170
  interface NavBarProps {
@@ -211,6 +218,13 @@ declare function useContentMaxWidth(width: AppShellWidth): number | 'full';
211
218
  * (pending spinner / redirect-when-unauthenticated) and page state cards
212
219
  * (loading / error / forbidden).
213
220
  *
221
+ * A BACK-OFFICE variant is supported via the optional `sidebar` slot (added for
222
+ * Agora's merchant admin): supply a `<Sidebar>` and the body below the header /
223
+ * nav / banner becomes a ROW — a persistent left rail beside the scrolling
224
+ * content column — instead of the default single vertical stack. Omit `sidebar`
225
+ * and the render tree is byte-identical to before, so every existing consumer
226
+ * (erevna / katalogos / kefi) is untouched.
227
+ *
214
228
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
215
229
  * or store imports. The app passes wired elements, pre-localized strings, and
216
230
  * callbacks; every colour is routed through the `@dloizides/ui-feedback` theme.
@@ -226,6 +240,13 @@ interface AppShellProps {
226
240
  header: React.ReactNode;
227
241
  /** Per-app nav region (kefi coral pills / erevna sidebar+drawer). */
228
242
  nav?: React.ReactNode;
243
+ /**
244
+ * Optional persistent LEFT RAIL (a `<Sidebar>`), for back-office surfaces. When
245
+ * supplied, the region below header/nav/banner lays out as a row: the rail on the
246
+ * left, the scrolling content column on the right. Omit for the default vertical
247
+ * stack — the tree is then byte-identical to the pre-`sidebar` AppShell.
248
+ */
249
+ sidebar?: React.ReactNode;
229
250
  /** Banner slot (e.g. verification-pending). */
230
251
  banner?: React.ReactNode;
231
252
  /** Content-width policy. Defaults to full-bleed. */
@@ -254,7 +275,7 @@ interface AppShellProps {
254
275
  testID: string;
255
276
  children: React.ReactNode;
256
277
  }
257
- declare const AppShell: ({ header, nav, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
278
+ declare const AppShell: ({ header, nav, sidebar, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
258
279
 
259
280
  /**
260
281
  * Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
@@ -282,6 +303,8 @@ declare const APP_SHELL_SUFFIX: {
282
303
  readonly content: "-content";
283
304
  readonly header: "-header";
284
305
  readonly nav: "-nav";
306
+ /** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
307
+ readonly sidebar: "-sidebar";
285
308
  readonly banner: "-banner";
286
309
  readonly pending: "-pending";
287
310
  readonly loading: "-loading";
package/dist/index.d.ts CHANGED
@@ -154,10 +154,17 @@ declare const Topbar: ({ left, language, notificationSlot, user, account, logout
154
154
  * collapses the links behind a hamburger toggle — mirroring the v1 AML console's
155
155
  * responsive `.global-nav`.
156
156
  *
157
+ * Look + feel: each link is a rounded-pill target with the v1 `.gn-links a`
158
+ * affordance — muted at rest, brightening text + a subtle pill background on
159
+ * HOVER, and a solid accent pill when ACTIVE. All colours are THEME-DRIVEN from
160
+ * the `@dloizides/ui-feedback` UiProvider (rest=`textSecondary`, hover text=`text`,
161
+ * hover bg=`surfaceElevated`, active=`palette.primary`, ring=`palette.primary`).
162
+ * Hover/focus are web-only (react-native-web); native stays at the rest style.
163
+ *
157
164
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
158
165
  * icon set, or store imports. Labels are pre-localized strings, icons are render
159
- * slots, and every colour is read from the `@dloizides/ui-feedback` UiProvider
160
- * theme. This is purely additive — `Sidebar`/`Topbar` are unchanged.
166
+ * slots, and every colour is read from the theme. This is purely additive —
167
+ * `Sidebar`/`Topbar` are unchanged.
161
168
  */
162
169
 
163
170
  interface NavBarProps {
@@ -211,6 +218,13 @@ declare function useContentMaxWidth(width: AppShellWidth): number | 'full';
211
218
  * (pending spinner / redirect-when-unauthenticated) and page state cards
212
219
  * (loading / error / forbidden).
213
220
  *
221
+ * A BACK-OFFICE variant is supported via the optional `sidebar` slot (added for
222
+ * Agora's merchant admin): supply a `<Sidebar>` and the body below the header /
223
+ * nav / banner becomes a ROW — a persistent left rail beside the scrolling
224
+ * content column — instead of the default single vertical stack. Omit `sidebar`
225
+ * and the render tree is byte-identical to before, so every existing consumer
226
+ * (erevna / katalogos / kefi) is untouched.
227
+ *
214
228
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
215
229
  * or store imports. The app passes wired elements, pre-localized strings, and
216
230
  * callbacks; every colour is routed through the `@dloizides/ui-feedback` theme.
@@ -226,6 +240,13 @@ interface AppShellProps {
226
240
  header: React.ReactNode;
227
241
  /** Per-app nav region (kefi coral pills / erevna sidebar+drawer). */
228
242
  nav?: React.ReactNode;
243
+ /**
244
+ * Optional persistent LEFT RAIL (a `<Sidebar>`), for back-office surfaces. When
245
+ * supplied, the region below header/nav/banner lays out as a row: the rail on the
246
+ * left, the scrolling content column on the right. Omit for the default vertical
247
+ * stack — the tree is then byte-identical to the pre-`sidebar` AppShell.
248
+ */
249
+ sidebar?: React.ReactNode;
229
250
  /** Banner slot (e.g. verification-pending). */
230
251
  banner?: React.ReactNode;
231
252
  /** Content-width policy. Defaults to full-bleed. */
@@ -254,7 +275,7 @@ interface AppShellProps {
254
275
  testID: string;
255
276
  children: React.ReactNode;
256
277
  }
257
- declare const AppShell: ({ header, nav, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
278
+ declare const AppShell: ({ header, nav, sidebar, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
258
279
 
259
280
  /**
260
281
  * Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
@@ -282,6 +303,8 @@ declare const APP_SHELL_SUFFIX: {
282
303
  readonly content: "-content";
283
304
  readonly header: "-header";
284
305
  readonly nav: "-nav";
306
+ /** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
307
+ readonly sidebar: "-sidebar";
285
308
  readonly banner: "-banner";
286
309
  readonly pending: "-pending";
287
310
  readonly loading: "-loading";