@dloizides/ui-nav 1.1.1 → 1.3.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,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0
4
+
5
+ Give the `NavBar` the v1 `.gn-links` look + a full accessibility pass (and extend the
6
+ same a11y bar to `Sidebar`/`Topbar`).
7
+
8
+ **Look & hover (NavBar).** Each link is a rounded-pill target with the v1 hover
9
+ affordance — muted at rest, and on HOVER the text brightens to full contrast while a
10
+ subtle rounded-pill background appears; the ACTIVE link is a solid accent pill. All
11
+ theme-driven (rest = `textSecondary`, hover text = `text`, hover bg = `surfaceElevated`,
12
+ active/ring = `palette.primary`). Hover/focus are web-only (`onHoverIn/Out`, `onFocus/Blur`
13
+ via `Pressable`); native stays at the rest style. The hover cross-fade is cheap CSS and is
14
+ disabled under `prefers-reduced-motion: reduce` (new `useReducedMotion`). Backwards
15
+ compatible — the hover pill is the default (aml-v2 is the only `NavBar` consumer) and the
16
+ brand/right slots, responsive hamburger collapse, and `containerStyle` width behaviour are
17
+ unchanged.
18
+
19
+ **Accessibility.** NavBar toggle now carries `aria-controls` pointing at the collapsible
20
+ links region (id via `nativeID`), keeps `aria-expanded`, and gains a themed keyboard focus
21
+ ring; links keep `aria-current="page"` + `role="link"` and gain a focus ring; link/toggle
22
+ touch targets are now ≥44×44. `Sidebar` leaves gain `aria-current="page"` + a focus ring;
23
+ `Topbar` action buttons gain a focus ring (new internal `FocusableTouchable`). Focus rings
24
+ never remove the outline without a themed replacement.
25
+
26
+ ## 1.2.0
27
+
28
+ Add `NavBar` — a config-driven **horizontal top navigation bar**, the top-bar
29
+ counterpart to `Sidebar`. It renders the same caller-supplied `NavItem[]` as inline
30
+ links across the top (brand left slot + inline links + free-form right slot), with
31
+ an active-link highlight (`aria-current="page"`) and a responsive collapse to a
32
+ hamburger toggle below a configurable breakpoint (`collapseBelow`, default `760`) —
33
+ mirroring the v1 AML console `.global-nav`. Theme-driven (UiProvider colours),
34
+ `role="navigation"` landmark, keyboard-operable links. Purely additive: `Sidebar`,
35
+ `Topbar`, `AppShell`, and the role-gating helpers are unchanged. New `NAV_TEST_IDS`
36
+ entries `navBarToggle` / `navBarLinks`.
37
+
3
38
  ## 1.0.0
4
39
 
5
40
  Initial release. `Sidebar` (leaf + expandable items, active-route highlight, header/footer
package/README.md CHANGED
@@ -46,6 +46,50 @@ const items: NavItem[] = buildItems(); // your role-filtered / grouped items
46
46
  />
47
47
  ```
48
48
 
49
+ ### Horizontal top bar (`NavBar`)
50
+
51
+ Prefer the nav links **on top** (a horizontal menu bar) instead of the left rail?
52
+ `NavBar` renders the SAME `NavItem[]` model horizontally — brand slot on the left,
53
+ inline links, a free-form right slot (auth / user / logout) — and collapses the
54
+ links behind a hamburger below a configurable breakpoint (default `760`, matching
55
+ the v1 AML console `.global-nav`). It is purely additive; `Sidebar` is unchanged.
56
+
57
+ ```tsx
58
+ import { NavBar, type NavItem } from '@dloizides/ui-nav';
59
+
60
+ <NavBar
61
+ items={items} // same role-filtered NavItem[] as Sidebar
62
+ pathname={usePathname()}
63
+ onNavigate={(route) => router.push(route)}
64
+ regionLabel={FM('accessibility.navigationRegion')}
65
+ navigateHint={(label) => FM('menu.navigateToHint', label)}
66
+ brand={<BrandLogo />} // left slot
67
+ right={<AuthLink />} // right slot (auth / user / logout)
68
+ menuLabel={FM('menu.toggle')} // a11y label for the hamburger
69
+ menuHint={FM('menu.toggleHint')}
70
+ collapseBelow={760} // hamburger under this viewport width
71
+ />
72
+ ```
73
+
74
+ An app that renders a `Sidebar` today switches to a top bar by swapping the
75
+ `Sidebar` element for `NavBar` (same `items` / `pathname` / `onNavigate`) — e.g.
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.
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.
92
+
49
93
  ### Role gating
50
94
 
51
95
  `accessibleNavItems(user, roleRouteTable, translate)` builds the `NavItem[]` a user's roles
package/dist/index.d.mts CHANGED
@@ -145,6 +145,56 @@ interface TopbarProps {
145
145
  }
146
146
  declare const Topbar: ({ left, language, notificationSlot, user, account, logout, containerStyle, }: TopbarProps) => React.ReactElement;
147
147
 
148
+ /**
149
+ * NavBar — the config-driven HORIZONTAL top navigation bar. It is the top-bar
150
+ * counterpart to `Sidebar`: it renders the SAME caller-supplied `NavItem[]`
151
+ * (already role-filtered / localized by the app) as inline links across the top
152
+ * instead of a left rail, with an optional brand slot on the left and a free-form
153
+ * right slot (auth link / user / logout). Below a configurable breakpoint it
154
+ * collapses the links behind a hamburger toggle — mirroring the v1 AML console's
155
+ * responsive `.global-nav`.
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
+ *
164
+ * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
165
+ * icon set, or store imports. Labels are pre-localized strings, icons are render
166
+ * slots, and every colour is read from the theme. This is purely additive —
167
+ * `Sidebar`/`Topbar` are unchanged.
168
+ */
169
+
170
+ interface NavBarProps {
171
+ /** Nav entries — already role-filtered / grouped by the app. */
172
+ items: NavItem[];
173
+ /** Current active route/path. */
174
+ pathname: string;
175
+ /** Navigation callback — receives a `NavItem.route`. */
176
+ onNavigate: (route: string) => void;
177
+ /** Localized accessibility label for the navigation landmark. */
178
+ regionLabel: string;
179
+ /** a11y hint for a link, given its label. Defaults to the label. */
180
+ navigateHint?: (label: string) => string;
181
+ /** Left slot — typically the brand / tenant logo. */
182
+ brand?: React.ReactNode;
183
+ /** Right slot — free-form (auth link, user block, logout). */
184
+ right?: React.ReactNode;
185
+ /** Localized accessibility label for the responsive menu toggle. */
186
+ menuLabel?: string;
187
+ /** Localized accessibility hint for the responsive menu toggle. */
188
+ menuHint?: string;
189
+ /** Optional custom glyph renderer for the toggle; defaults to ☰. */
190
+ renderMenuIcon?: (color: string, open: boolean) => React.ReactNode;
191
+ /** Collapse the links behind a hamburger below this viewport width (default 760). */
192
+ collapseBelow?: number;
193
+ /** Extra container style overrides. */
194
+ containerStyle?: ViewStyle | ViewStyle[];
195
+ }
196
+ declare const NavBar: ({ items, pathname, onNavigate, regionLabel, navigateHint, brand, right, menuLabel, menuHint, renderMenuIcon, collapseBelow, containerStyle, }: NavBarProps) => React.ReactElement;
197
+
148
198
  /** Content-width policy: a capped column, optionally wider past a viewport breakpoint, or full-bleed. */
149
199
  type AppShellWidth = {
150
200
  max: number;
@@ -229,6 +279,10 @@ declare const NAV_TEST_IDS: {
229
279
  readonly accountPlan: "topbar-account-plan";
230
280
  /** default testID for the rich header's upgrade action (overridable per-action). */
231
281
  readonly accountUpgrade: "topbar-account-upgrade";
282
+ /** responsive hamburger toggle in the horizontal NavBar (shown below the breakpoint). */
283
+ readonly navBarToggle: "navbar-toggle";
284
+ /** links container in the horizontal NavBar. */
285
+ readonly navBarLinks: "navbar-links";
232
286
  };
233
287
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
234
288
  declare const APP_SHELL_SUFFIX: {
@@ -408,4 +462,4 @@ declare const NAV_ICON_SIZE = 14;
408
462
  /** Chevron icon size for expandable sections. */
409
463
  declare const CHEVRON_ICON_SIZE = 12;
410
464
 
411
- export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, CHEVRON_ICON_SIZE, NAV_ICON_SIZE, NAV_TEST_IDS, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavUser, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };
465
+ export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, CHEVRON_ICON_SIZE, NAV_ICON_SIZE, NAV_TEST_IDS, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavUser, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };
package/dist/index.d.ts CHANGED
@@ -145,6 +145,56 @@ interface TopbarProps {
145
145
  }
146
146
  declare const Topbar: ({ left, language, notificationSlot, user, account, logout, containerStyle, }: TopbarProps) => React.ReactElement;
147
147
 
148
+ /**
149
+ * NavBar — the config-driven HORIZONTAL top navigation bar. It is the top-bar
150
+ * counterpart to `Sidebar`: it renders the SAME caller-supplied `NavItem[]`
151
+ * (already role-filtered / localized by the app) as inline links across the top
152
+ * instead of a left rail, with an optional brand slot on the left and a free-form
153
+ * right slot (auth link / user / logout). Below a configurable breakpoint it
154
+ * collapses the links behind a hamburger toggle — mirroring the v1 AML console's
155
+ * responsive `.global-nav`.
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
+ *
164
+ * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
165
+ * icon set, or store imports. Labels are pre-localized strings, icons are render
166
+ * slots, and every colour is read from the theme. This is purely additive —
167
+ * `Sidebar`/`Topbar` are unchanged.
168
+ */
169
+
170
+ interface NavBarProps {
171
+ /** Nav entries — already role-filtered / grouped by the app. */
172
+ items: NavItem[];
173
+ /** Current active route/path. */
174
+ pathname: string;
175
+ /** Navigation callback — receives a `NavItem.route`. */
176
+ onNavigate: (route: string) => void;
177
+ /** Localized accessibility label for the navigation landmark. */
178
+ regionLabel: string;
179
+ /** a11y hint for a link, given its label. Defaults to the label. */
180
+ navigateHint?: (label: string) => string;
181
+ /** Left slot — typically the brand / tenant logo. */
182
+ brand?: React.ReactNode;
183
+ /** Right slot — free-form (auth link, user block, logout). */
184
+ right?: React.ReactNode;
185
+ /** Localized accessibility label for the responsive menu toggle. */
186
+ menuLabel?: string;
187
+ /** Localized accessibility hint for the responsive menu toggle. */
188
+ menuHint?: string;
189
+ /** Optional custom glyph renderer for the toggle; defaults to ☰. */
190
+ renderMenuIcon?: (color: string, open: boolean) => React.ReactNode;
191
+ /** Collapse the links behind a hamburger below this viewport width (default 760). */
192
+ collapseBelow?: number;
193
+ /** Extra container style overrides. */
194
+ containerStyle?: ViewStyle | ViewStyle[];
195
+ }
196
+ declare const NavBar: ({ items, pathname, onNavigate, regionLabel, navigateHint, brand, right, menuLabel, menuHint, renderMenuIcon, collapseBelow, containerStyle, }: NavBarProps) => React.ReactElement;
197
+
148
198
  /** Content-width policy: a capped column, optionally wider past a viewport breakpoint, or full-bleed. */
149
199
  type AppShellWidth = {
150
200
  max: number;
@@ -229,6 +279,10 @@ declare const NAV_TEST_IDS: {
229
279
  readonly accountPlan: "topbar-account-plan";
230
280
  /** default testID for the rich header's upgrade action (overridable per-action). */
231
281
  readonly accountUpgrade: "topbar-account-upgrade";
282
+ /** responsive hamburger toggle in the horizontal NavBar (shown below the breakpoint). */
283
+ readonly navBarToggle: "navbar-toggle";
284
+ /** links container in the horizontal NavBar. */
285
+ readonly navBarLinks: "navbar-links";
232
286
  };
233
287
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
234
288
  declare const APP_SHELL_SUFFIX: {
@@ -408,4 +462,4 @@ declare const NAV_ICON_SIZE = 14;
408
462
  /** Chevron icon size for expandable sections. */
409
463
  declare const CHEVRON_ICON_SIZE = 12;
410
464
 
411
- export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, CHEVRON_ICON_SIZE, NAV_ICON_SIZE, NAV_TEST_IDS, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavUser, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };
465
+ export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, CHEVRON_ICON_SIZE, NAV_ICON_SIZE, NAV_TEST_IDS, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavUser, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };