@dloizides/ui-nav 1.7.0 → 1.9.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 +73 -0
- package/README.md +97 -0
- package/dist/index.d.mts +550 -79
- package/dist/index.d.ts +550 -79
- package/dist/index.js +445 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +438 -74
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ViewStyle } from 'react-native';
|
|
3
|
+
import { UiTheme } from '@dloizides/ui-feedback';
|
|
3
4
|
import { DropdownVariant } from '@dloizides/ui-layout';
|
|
4
5
|
import { resolveAccessibleRoutes, RoleRouteTable, RoleRoute } from '@dloizides/auth-web';
|
|
5
6
|
|
|
@@ -209,8 +210,33 @@ interface NavBarProps {
|
|
|
209
210
|
overflowHint?: string;
|
|
210
211
|
/** Extra container style overrides. */
|
|
211
212
|
containerStyle?: ViewStyle | ViewStyle[];
|
|
213
|
+
/**
|
|
214
|
+
* Optional content-column cap (px). When set, the OUTER container — which carries
|
|
215
|
+
* the background + bottom border (`barTheme`/theme surface + any `containerStyle`,
|
|
216
|
+
* e.g. the AML radial-glow appbar) — stays FULL WIDTH (full-bleed), while the INNER
|
|
217
|
+
* row (brand + links + right) is capped to this width and centred. Omit for the
|
|
218
|
+
* default where the inner spans the full container. The priority+ overflow "…" fit
|
|
219
|
+
* measures the (now capped) inner's rendered width via `onLayout`, so it keeps
|
|
220
|
+
* working against the capped column, not the full-bleed container.
|
|
221
|
+
*/
|
|
222
|
+
contentMaxWidth?: number;
|
|
223
|
+
/**
|
|
224
|
+
* Optional CHROME theme override — re-provisions the `@dloizides/ui-feedback`
|
|
225
|
+
* theme for the bar ONLY, so the top bar can be dark while the page below keeps
|
|
226
|
+
* the light workspace theme (the AML v1-console "dark appbar over light content"
|
|
227
|
+
* pattern). The live `t` (+ `navigate`) from the surrounding provider are
|
|
228
|
+
* re-passed, so nav labels keep localizing. Omit for a bar that shares the page
|
|
229
|
+
* theme. Style-only: every colour still flows through the theme — no per-app fork.
|
|
230
|
+
*/
|
|
231
|
+
barTheme?: UiTheme;
|
|
212
232
|
}
|
|
213
|
-
|
|
233
|
+
/**
|
|
234
|
+
* `NavBar` — the horizontal top bar. When `barTheme` is supplied it re-provisions
|
|
235
|
+
* the `@dloizides/ui-feedback` theme around the bar (dark bar over a light page),
|
|
236
|
+
* re-passing the live `t` / `navigate` so labels keep localizing; otherwise it
|
|
237
|
+
* renders directly against the surrounding theme (byte-identical to before).
|
|
238
|
+
*/
|
|
239
|
+
declare const NavBar: ({ barTheme, ...props }: NavBarProps) => React.ReactElement;
|
|
214
240
|
|
|
215
241
|
/**
|
|
216
242
|
* `Nav` — ONE component that renders EITHER the horizontal top bar (`NavBar`) or the
|
|
@@ -242,76 +268,24 @@ type NavProps = ({
|
|
|
242
268
|
declare const Nav: (props: NavProps) => React.ReactElement;
|
|
243
269
|
|
|
244
270
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* - rest → muted text (`colors.rest`), no background
|
|
251
|
-
* - hover → text brightens to full contrast (`colors.hoverText`) + a subtle
|
|
252
|
-
* rounded-pill background (`colors.hoverBg`, e.g. v1 `#16223a`)
|
|
253
|
-
* - active → solid accent pill (`colors.activeBg`) with white ink + aria-current
|
|
254
|
-
* - focus → a themed keyboard focus ring (`colors.ring`)
|
|
255
|
-
* Hover/focus are WEB-only (react-native-web fires `onHoverIn/Out`); on native
|
|
256
|
-
* those handlers never fire, so the link stays at its rest style.
|
|
271
|
+
* AppShell content-body helpers — the page-state cards (loading / error /
|
|
272
|
+
* forbidden) and the precedence rule that picks what renders inside the content
|
|
273
|
+
* column. Split out of `AppShell` so the shell file stays focused on layout /
|
|
274
|
+
* responsive composition. Colours are routed through the `@dloizides/ui-feedback`
|
|
275
|
+
* theme, same as the rest of `@dloizides/ui-nav`.
|
|
257
276
|
*/
|
|
258
277
|
|
|
259
|
-
/**
|
|
260
|
-
interface
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/** Brightened link text on hover. */
|
|
264
|
-
hoverText: string;
|
|
265
|
-
/** Subtle hover-pill background. */
|
|
266
|
-
hoverBg: string;
|
|
267
|
-
/** Active-pill background (accent). */
|
|
268
|
-
activeBg: string;
|
|
269
|
-
/** Focus-ring colour. */
|
|
270
|
-
ring: string;
|
|
278
|
+
/** A titled message card (error / forbidden). Colours come from the theme. */
|
|
279
|
+
interface ShellMessage {
|
|
280
|
+
titleText: string;
|
|
281
|
+
messageText: string;
|
|
271
282
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
* It REUSES the shared `@dloizides/ui-layout` `ModalDropdown` — the exact component
|
|
278
|
-
* every other menu in the app uses — so the overflow menu is portalled, keyboard
|
|
279
|
-
* navigable, dismissed on outside-click / Escape, and opens on click, looking
|
|
280
|
-
* identical to the rest of the app's menus. The overflow entries are the same
|
|
281
|
-
* `NavItem`s (label + route + active state); selecting one navigates via the same
|
|
282
|
-
* `onNavigate` the inline links use.
|
|
283
|
-
*
|
|
284
|
-
* The trigger is a rounded pill matching a `NavBarLink`: muted at rest, and — when
|
|
285
|
-
* the ACTIVE route lives in the overflow set — it wears the accent pill so the
|
|
286
|
-
* active item stays visibly reachable behind the "…". The dropdown marks that same
|
|
287
|
-
* item selected (its `value` is the active overflow route). a11y: `ModalDropdown`
|
|
288
|
-
* owns the trigger's `role="button"` + `aria-expanded` + caller-supplied label/hint,
|
|
289
|
-
* and gives the options keyboard navigation.
|
|
290
|
-
*
|
|
291
|
-
* Contract discipline (as the rest of `@dloizides/ui-nav`): no FM / router / icon
|
|
292
|
-
* set / store; the label + hint are pre-localized strings from the caller and every
|
|
293
|
-
* colour comes from the theme (via the `NavBarLinkColors` the `NavBar` computed).
|
|
294
|
-
*/
|
|
295
|
-
|
|
296
|
-
interface NavOverflowMenuProps {
|
|
297
|
-
/** The items that spilled out of the inline row. */
|
|
298
|
-
items: NavItem[];
|
|
299
|
-
/** Current active route/path — decides the active-pill + selected option. */
|
|
300
|
-
pathname: string;
|
|
301
|
-
/** Navigation callback — receives the chosen item's `route`. */
|
|
302
|
-
onNavigate: (route: string) => void;
|
|
303
|
-
/** Theme-resolved link colours (same set the inline links use). */
|
|
304
|
-
colors: NavBarLinkColors;
|
|
305
|
-
/** Localized visible + accessible label for the trigger (e.g. "More"). */
|
|
306
|
-
label: string;
|
|
307
|
-
/** Localized accessibility hint for the trigger. */
|
|
308
|
-
hint: string;
|
|
309
|
-
/** testID for the trigger (options default to `` `${testID}-option-${route}` ``). */
|
|
310
|
-
testID: string;
|
|
311
|
-
/** Force a dropdown variant (default: responsive — inline menu on desktop). */
|
|
312
|
-
variant?: DropdownVariant;
|
|
283
|
+
/** Page-state model — a loading spinner card, or a titled error / forbidden card. */
|
|
284
|
+
interface ShellState {
|
|
285
|
+
loading?: boolean;
|
|
286
|
+
error?: ShellMessage | null;
|
|
287
|
+
forbidden?: ShellMessage | null;
|
|
313
288
|
}
|
|
314
|
-
declare const NavOverflowMenu: ({ items, pathname, onNavigate, colors, label, hint, testID, variant, }: NavOverflowMenuProps) => React.ReactElement;
|
|
315
289
|
|
|
316
290
|
/**
|
|
317
291
|
* MobileDrawer — the below-the-breakpoint face of `AppShell`'s back-office
|
|
@@ -340,6 +314,50 @@ interface DrawerLabels {
|
|
|
340
314
|
closeHint: string;
|
|
341
315
|
}
|
|
342
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Rail responsive mode — the pure (viewport → which rail face) rule behind
|
|
319
|
+
* `AppShell`'s optional THREE-tier layout, split out so it is directly
|
|
320
|
+
* unit-testable.
|
|
321
|
+
*
|
|
322
|
+
* Two-tier (default, back-compat): a persistent full rail at/above
|
|
323
|
+
* {@link RAIL_FULL_BREAKPOINT}, an overlay drawer below it. This is what every
|
|
324
|
+
* existing `AppShell` `sidebar` consumer already gets.
|
|
325
|
+
*
|
|
326
|
+
* Three-tier (opt-in — supply a `collapsedSidebar`): a persistent full rail on
|
|
327
|
+
* desktop, an intermediate persistent COLLAPSED (icon-only) rail on tablet, and
|
|
328
|
+
* the overlay drawer on a phone — the erevna / katalogos responsive scheme.
|
|
329
|
+
*/
|
|
330
|
+
/** At/above this viewport width the persistent FULL rail shows (two-tier boundary). */
|
|
331
|
+
declare const RAIL_FULL_BREAKPOINT = 768;
|
|
332
|
+
/** Default upper bound of the collapsed-rail band (full rail at/above this). */
|
|
333
|
+
declare const DEFAULT_COLLAPSED_RAIL_MAX = 1024;
|
|
334
|
+
/** Which face the rail takes at a given viewport. */
|
|
335
|
+
type RailMode = 'none' | 'full' | 'collapsed' | 'drawer';
|
|
336
|
+
/**
|
|
337
|
+
* Viewport band in which the intermediate COLLAPSED icon-rail shows: `[min, max)`.
|
|
338
|
+
* At/above `max` the full rail shows; below `min` the overlay drawer shows.
|
|
339
|
+
*/
|
|
340
|
+
interface CollapsedRailRange {
|
|
341
|
+
min: number;
|
|
342
|
+
max: number;
|
|
343
|
+
}
|
|
344
|
+
interface ResolveRailModeArgs {
|
|
345
|
+
viewport: number;
|
|
346
|
+
/** Whether a `sidebar` (full rail) node was supplied at all. */
|
|
347
|
+
hasSidebar: boolean;
|
|
348
|
+
/** Whether an intermediate `collapsedSidebar` node was supplied (enables 3-tier). */
|
|
349
|
+
hasCollapsed: boolean;
|
|
350
|
+
/** The collapsed-rail band; only consulted in 3-tier mode. */
|
|
351
|
+
range: CollapsedRailRange;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Resolve the rail mode. No sidebar ⇒ `'none'`. With a `collapsedSidebar`
|
|
355
|
+
* (3-tier): full at/above `range.max`, collapsed in `[range.min, range.max)`,
|
|
356
|
+
* drawer below `range.min`. Without one (2-tier, unchanged): full at/above
|
|
357
|
+
* {@link RAIL_FULL_BREAKPOINT}, drawer below.
|
|
358
|
+
*/
|
|
359
|
+
declare function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }: ResolveRailModeArgs): RailMode;
|
|
360
|
+
|
|
343
361
|
/** Content-width policy: a capped column, optionally wider past a viewport breakpoint, or full-bleed. */
|
|
344
362
|
type AppShellWidth = {
|
|
345
363
|
max: number;
|
|
@@ -375,11 +393,6 @@ declare function useContentMaxWidth(width: AppShellWidth): number | 'full';
|
|
|
375
393
|
* callbacks; every colour is routed through the `@dloizides/ui-feedback` theme.
|
|
376
394
|
*/
|
|
377
395
|
|
|
378
|
-
/** A titled message card (error / forbidden). Colours come from the theme. */
|
|
379
|
-
interface ShellMessage {
|
|
380
|
-
titleText: string;
|
|
381
|
-
messageText: string;
|
|
382
|
-
}
|
|
383
396
|
interface AppShellProps {
|
|
384
397
|
/** App-supplied wired header (Topbar / AppHeader), rendered full-bleed. */
|
|
385
398
|
header: React.ReactNode;
|
|
@@ -396,6 +409,21 @@ interface AppShellProps {
|
|
|
396
409
|
* takes the full width. Desktop (≥768px) is unchanged. No effect without `sidebar`.
|
|
397
410
|
*/
|
|
398
411
|
sidebar?: React.ReactNode;
|
|
412
|
+
/**
|
|
413
|
+
* Optional intermediate COLLAPSED (icon-only) rail — opts the `sidebar` layout
|
|
414
|
+
* into a THREE-tier responsive scheme (erevna / katalogos): the full `sidebar`
|
|
415
|
+
* on desktop, this collapsed rail on a tablet-width band, and the overlay drawer
|
|
416
|
+
* (hosting the full `sidebar`) on a phone. Omit it and the layout stays the
|
|
417
|
+
* unchanged two-tier scheme (full rail ≥768px, drawer below). No effect without
|
|
418
|
+
* `sidebar`. Exposed as `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`.
|
|
419
|
+
*/
|
|
420
|
+
collapsedSidebar?: React.ReactNode;
|
|
421
|
+
/**
|
|
422
|
+
* The viewport band `[min, max)` in which `collapsedSidebar` shows: at/above
|
|
423
|
+
* `max` the full rail shows, below `min` the drawer shows. Defaults to
|
|
424
|
+
* `{ min: 768, max: 1024 }`. Only consulted when `collapsedSidebar` is supplied.
|
|
425
|
+
*/
|
|
426
|
+
collapsedRailRange?: CollapsedRailRange;
|
|
399
427
|
/**
|
|
400
428
|
* Pre-localized labels for the mobile nav drawer (only used in the `sidebar`
|
|
401
429
|
* layout, below the breakpoint): the hamburger's open label/hint and the scrim's
|
|
@@ -421,16 +449,367 @@ interface AppShellProps {
|
|
|
421
449
|
onRedirect: () => void;
|
|
422
450
|
};
|
|
423
451
|
/** Page state — loading spinner card, or a titled error / forbidden card. */
|
|
424
|
-
state?:
|
|
425
|
-
loading?: boolean;
|
|
426
|
-
error?: ShellMessage | null;
|
|
427
|
-
forbidden?: ShellMessage | null;
|
|
428
|
-
};
|
|
452
|
+
state?: ShellState;
|
|
429
453
|
/** Root testID; the content region is exposed as `${testID}-content`. */
|
|
430
454
|
testID: string;
|
|
431
455
|
children: React.ReactNode;
|
|
432
456
|
}
|
|
433
|
-
declare const AppShell: ({ header, nav, sidebar, mobileMenu, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
457
|
+
declare const AppShell: ({ header, nav, sidebar, collapsedSidebar, collapsedRailRange, mobileMenu, banner, width, contentPadding, chromeAlignment, gate, state, testID, children, }: AppShellProps) => React.ReactElement | null;
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* NavShell — the ONE first-class navigation shell that renders a **top bar**, a
|
|
461
|
+
* **side rail**, or **BOTH together**, chosen by a single `layout` prop
|
|
462
|
+
* (`'top' | 'side' | 'both'`). It is the shell-level answer to `Nav`'s
|
|
463
|
+
* single-axis question: where `Nav` returns one element (a `NavBar` OR a
|
|
464
|
+
* `Sidebar`), `NavShell` owns the whole page composition — it wires a top bar
|
|
465
|
+
* into `AppShell`'s `header` slot and a side rail into `AppShell`'s `sidebar`
|
|
466
|
+
* slot, and inherits every `AppShell` capability (auth gate, loading / error /
|
|
467
|
+
* forbidden state cards, width discipline, banner, the responsive
|
|
468
|
+
* hamburger + overlay drawer for the rail).
|
|
469
|
+
*
|
|
470
|
+
* layout="top" → a horizontal top bar only (brand + links + overflow "…" + right).
|
|
471
|
+
* layout="side" → a header + a persistent left rail of sections (the erevna / katalogos shape).
|
|
472
|
+
* layout="both" → a top bar (brand / global links + overflow "…" / user) AND a side rail
|
|
473
|
+
* (sections), together — the AML-console shape, now first-class.
|
|
474
|
+
*
|
|
475
|
+
* "both" is inherently a shell composition (a top region + a side region + a
|
|
476
|
+
* content column), which a single-element `Nav` cannot own; `NavShell` is that
|
|
477
|
+
* composition, so no app hand-wires `Topbar` + `Sidebar` + a `ScrollView` row
|
|
478
|
+
* again. Customizability is STYLE-only: every colour flows through the
|
|
479
|
+
* `@dloizides/ui-feedback` UiProvider theme, and brand / user / language /
|
|
480
|
+
* notification / section content are render slots. Structure + behaviour are
|
|
481
|
+
* shared.
|
|
482
|
+
*
|
|
483
|
+
* Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
|
|
484
|
+
* icon-set, or store imports. Labels are pre-localized strings, icons are render
|
|
485
|
+
* slots, and every colour is read from the theme.
|
|
486
|
+
*/
|
|
487
|
+
|
|
488
|
+
/** Which regions the shell renders: a top bar, a side rail, or both together. */
|
|
489
|
+
type NavShellLayout = 'top' | 'side' | 'both';
|
|
490
|
+
/**
|
|
491
|
+
* Config for the TOP bar (rendered as a `NavBar` in `AppShell`'s header slot).
|
|
492
|
+
* Used when `layout` is `'top'` or `'both'`. Every field is optional except the
|
|
493
|
+
* items — an empty `items` array renders a bar with just the brand + right slots
|
|
494
|
+
* (a plain top bar with no nav links), which is the natural `'side'`-mode header.
|
|
495
|
+
*/
|
|
496
|
+
interface NavShellTopBar {
|
|
497
|
+
/** Top-bar nav entries (global links). Empty ⇒ brand + right only. */
|
|
498
|
+
items: NavItem[];
|
|
499
|
+
/** Left slot — typically the tenant brand / logo. */
|
|
500
|
+
brand?: React.ReactNode;
|
|
501
|
+
/** Right slot — free-form (language toggle, notification bell, user chip, logout). */
|
|
502
|
+
right?: React.ReactNode;
|
|
503
|
+
/** Collapse the links behind a hamburger below this viewport width (default 760). */
|
|
504
|
+
collapseBelow?: number;
|
|
505
|
+
/** Localized visible + accessible label for the "More ▾" overflow trigger (default "More"). */
|
|
506
|
+
overflowLabel?: string;
|
|
507
|
+
/** Localized accessibility hint for the overflow trigger. */
|
|
508
|
+
overflowHint?: string;
|
|
509
|
+
/** Localized accessibility label for the responsive menu toggle. */
|
|
510
|
+
menuLabel?: string;
|
|
511
|
+
/** Localized accessibility hint for the responsive menu toggle. */
|
|
512
|
+
menuHint?: string;
|
|
513
|
+
/** Optional custom glyph renderer for the responsive toggle; defaults to ☰. */
|
|
514
|
+
renderMenuIcon?: (color: string, open: boolean) => React.ReactNode;
|
|
515
|
+
/**
|
|
516
|
+
* Extra container style overrides (e.g. an app-specific appbar background —
|
|
517
|
+
* including a web `background`/`backgroundImage` gradient cast through
|
|
518
|
+
* `ViewStyle`, the AML radial-glow appbar). Style-only.
|
|
519
|
+
*/
|
|
520
|
+
containerStyle?: ViewStyle | ViewStyle[];
|
|
521
|
+
/**
|
|
522
|
+
* Optional content-column cap (px) for the top bar. When set, the bar's
|
|
523
|
+
* background + bottom border (from `containerStyle` / `barTheme`) stay FULL WIDTH
|
|
524
|
+
* (full-bleed) while the inner brand + links + right row is capped to this width
|
|
525
|
+
* and centred — the AML v1-console appbar shape. Forwarded to `NavBar.contentMaxWidth`.
|
|
526
|
+
*/
|
|
527
|
+
contentMaxWidth?: number;
|
|
528
|
+
/**
|
|
529
|
+
* Optional CHROME theme override for the top bar only (dark bar over a light
|
|
530
|
+
* page). Forwarded to `NavBar.barTheme`. Style-only — see `NavBar`.
|
|
531
|
+
*/
|
|
532
|
+
barTheme?: UiTheme;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Config for the intermediate COLLAPSED (icon-only) rail — opts the side layout
|
|
536
|
+
* into a THREE-tier responsive scheme (full rail → collapsed rail → drawer). When
|
|
537
|
+
* present, NavShell builds a `<CollapsedRail>` and hands it to `AppShell` for the
|
|
538
|
+
* tablet band; omit it and the layout stays two-tier.
|
|
539
|
+
*/
|
|
540
|
+
interface NavShellCollapsedRail {
|
|
541
|
+
/** Items for the icon rail. Defaults to the full rail's `items`. */
|
|
542
|
+
items?: NavItem[];
|
|
543
|
+
/** Optional header slot (e.g. a home shortcut). */
|
|
544
|
+
header?: React.ReactNode;
|
|
545
|
+
/** Optional footer slot (dark-mode toggle, logout). */
|
|
546
|
+
footer?: React.ReactNode;
|
|
547
|
+
/** The `[min, max)` viewport band for the collapsed rail (default `{ min: 768, max: 1024 }`). */
|
|
548
|
+
range?: CollapsedRailRange;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Config for the SIDE rail (rendered as a `Sidebar` in `AppShell`'s sidebar
|
|
552
|
+
* slot). Used when `layout` is `'side'` or `'both'`.
|
|
553
|
+
*/
|
|
554
|
+
interface NavShellSideRail {
|
|
555
|
+
/** Side-rail nav entries (sections — leaf + expandable). */
|
|
556
|
+
items: NavItem[];
|
|
557
|
+
/** Localized menu title (heading). */
|
|
558
|
+
title: string;
|
|
559
|
+
/** Optional header slot rendered above the items (e.g. a Home shortcut). */
|
|
560
|
+
header?: React.ReactNode;
|
|
561
|
+
/** Optional footer slot rendered after a flex spacer (dark-mode toggle, logout). */
|
|
562
|
+
footer?: React.ReactNode;
|
|
563
|
+
/** a11y hint shown when an expandable section is collapsed. */
|
|
564
|
+
expandHint?: string;
|
|
565
|
+
/** a11y hint shown when an expandable section is expanded. */
|
|
566
|
+
collapseHint?: string;
|
|
567
|
+
/** Optional chevron renderer for expandable sections. */
|
|
568
|
+
renderChevron?: (expanded: boolean, color: string, size: number) => React.ReactNode;
|
|
569
|
+
/** Extra container style overrides. */
|
|
570
|
+
containerStyle?: ViewStyle | ViewStyle[];
|
|
571
|
+
/** Optional collapsed (icon-only) rail config → the intermediate tablet tier. */
|
|
572
|
+
collapsed?: NavShellCollapsedRail;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* `AppShell` props NavShell forwards straight through. It composes the `header`
|
|
576
|
+
* and `sidebar` (+ `collapsedSidebar`) regions itself, so those are handled here;
|
|
577
|
+
* everything else — including the optional `nav` strip (e.g. a `PillNav`), the
|
|
578
|
+
* auth gate, state cards, banner, and width policy — passes through untouched.
|
|
579
|
+
*/
|
|
580
|
+
type ForwardedShellProps = Omit<AppShellProps, 'header' | 'sidebar' | 'children'>;
|
|
581
|
+
interface NavShellProps extends ForwardedShellProps {
|
|
582
|
+
/** Which regions to render: a top bar, a side rail, or both. */
|
|
583
|
+
layout: NavShellLayout;
|
|
584
|
+
/** Current active route/path — drives active highlighting on both regions. */
|
|
585
|
+
pathname: string;
|
|
586
|
+
/** Navigation callback — receives a `NavItem.route`. */
|
|
587
|
+
onNavigate: (route: string) => void;
|
|
588
|
+
/** Localized accessibility label for the navigation landmark(s). */
|
|
589
|
+
regionLabel: string;
|
|
590
|
+
/** a11y hint for a nav item, given its label. Defaults to the label. */
|
|
591
|
+
navigateHint?: (label: string) => string;
|
|
592
|
+
/**
|
|
593
|
+
* Top-bar config → a `NavBar` in the header slot. Required for `'top'` /
|
|
594
|
+
* `'both'`; ignored for `'side'` unless supplied (a `'side'` app may still pass
|
|
595
|
+
* a `topBar` with empty `items` for its brand + user header).
|
|
596
|
+
*/
|
|
597
|
+
topBar?: NavShellTopBar;
|
|
598
|
+
/**
|
|
599
|
+
* Escape hatch: a ready-made header node (e.g. an app-composed `<Topbar>`).
|
|
600
|
+
* When supplied it REPLACES the `topBar`-built `NavBar` — used by `'side'`
|
|
601
|
+
* apps that want the structured `Topbar` (language / notification / user
|
|
602
|
+
* slots) instead of a `NavBar` header.
|
|
603
|
+
*/
|
|
604
|
+
header?: React.ReactNode;
|
|
605
|
+
/**
|
|
606
|
+
* Side-rail config → a `Sidebar` in the sidebar slot. Required for `'side'` /
|
|
607
|
+
* `'both'`; ignored for `'top'`.
|
|
608
|
+
*/
|
|
609
|
+
sideRail?: NavShellSideRail;
|
|
610
|
+
children: React.ReactNode;
|
|
611
|
+
}
|
|
612
|
+
declare const NavShell: ({ layout, pathname, onNavigate, regionLabel, navigateHint, topBar, header, sideRail, children, collapsedSidebar: collapsedSidebarProp, collapsedRailRange: collapsedRailRangeProp, ...shellProps }: NavShellProps) => React.ReactElement | null;
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* CollapsedRail — the icon-only vertical rail for the intermediate (tablet) tier
|
|
616
|
+
* of `AppShell`'s three-tier responsive layout (the erevna / katalogos collapsed
|
|
617
|
+
* rail). It renders the SAME caller-supplied `NavItem[]` as `Sidebar`, but as a
|
|
618
|
+
* narrow strip of icon buttons: each item shows its `renderIcon` (or a first-letter
|
|
619
|
+
* glyph fallback), labelled for assistive tech, with the active route highlighted.
|
|
620
|
+
* Optional `header` / `footer` slots carry app chrome (a home shortcut, a
|
|
621
|
+
* dark-mode toggle, logout) top and bottom.
|
|
622
|
+
*
|
|
623
|
+
* A collapsed rail cannot expand a section inline, so pressing ANY item — leaf or
|
|
624
|
+
* parent — navigates to its `route`; an app that wants a parent to open the full
|
|
625
|
+
* drawer instead can special-case that in its `onNavigate`.
|
|
626
|
+
*
|
|
627
|
+
* Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
|
|
628
|
+
* icon-set, or store imports. Labels are pre-localized strings, icons are render
|
|
629
|
+
* slots, and every colour is read from the `@dloizides/ui-feedback` theme.
|
|
630
|
+
*/
|
|
631
|
+
|
|
632
|
+
interface CollapsedRailProps {
|
|
633
|
+
/** Nav entries — the same role-filtered `NavItem[]` the full `Sidebar` renders. */
|
|
634
|
+
items: NavItem[];
|
|
635
|
+
/** Current active route/path. */
|
|
636
|
+
pathname: string;
|
|
637
|
+
/** Navigation callback — receives a `NavItem.route`. */
|
|
638
|
+
onNavigate: (route: string) => void;
|
|
639
|
+
/** Localized accessibility label for the navigation landmark. */
|
|
640
|
+
regionLabel: string;
|
|
641
|
+
/** a11y hint for an item, given its label. Defaults to the label. */
|
|
642
|
+
navigateHint?: (label: string) => string;
|
|
643
|
+
/** Optional header slot rendered above the items (e.g. a home shortcut). */
|
|
644
|
+
header?: React.ReactNode;
|
|
645
|
+
/** Optional footer slot rendered after a flex spacer (dark-mode toggle, logout). */
|
|
646
|
+
footer?: React.ReactNode;
|
|
647
|
+
/** Extra container style overrides. */
|
|
648
|
+
containerStyle?: ViewStyle | ViewStyle[];
|
|
649
|
+
}
|
|
650
|
+
declare const CollapsedRail: ({ items, pathname, onNavigate, regionLabel, navigateHint, header, footer, containerStyle, }: CollapsedRailProps) => React.ReactElement;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* PillNav — a horizontal row of fully-rounded "pills", the cross-navigation
|
|
654
|
+
* switcher promoted from kefi's coral dashboard nav. It renders the same
|
|
655
|
+
* caller-supplied `NavItem[]` as the other nav components, as pills that wrap,
|
|
656
|
+
* with the active route wearing the accent pill.
|
|
657
|
+
*
|
|
658
|
+
* It also folds in kefi's **visibility rule**: the whole nav hides (renders
|
|
659
|
+
* `null`) unless there are at least `minItems` entries — a single destination is
|
|
660
|
+
* not worth a switcher. Default `minItems` is 1 (always show when there is at
|
|
661
|
+
* least one item); kefi passes `2`.
|
|
662
|
+
*
|
|
663
|
+
* "Coral pills" is STYLE, not structure: the accent colour is the theme
|
|
664
|
+
* `palette.primary` (kefi's coral), the resting pill is `surfaceElevated`, and
|
|
665
|
+
* `containerStyle` / theme tokens carry any per-app tweak — no layout fork.
|
|
666
|
+
*
|
|
667
|
+
* Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
|
|
668
|
+
* icon-set, or store imports. Labels are pre-localized strings, icons are render
|
|
669
|
+
* slots, and every colour is read from the `@dloizides/ui-feedback` theme.
|
|
670
|
+
*/
|
|
671
|
+
|
|
672
|
+
interface PillNavProps {
|
|
673
|
+
/** Nav entries — the cross-navigation destinations. */
|
|
674
|
+
items: NavItem[];
|
|
675
|
+
/** Current active route/path. */
|
|
676
|
+
pathname: string;
|
|
677
|
+
/** Navigation callback — receives a `NavItem.route`. */
|
|
678
|
+
onNavigate: (route: string) => void;
|
|
679
|
+
/** Localized accessibility label for the navigation landmark. */
|
|
680
|
+
regionLabel: string;
|
|
681
|
+
/** a11y hint for a pill, given its label. Defaults to the label. */
|
|
682
|
+
navigateHint?: (label: string) => string;
|
|
683
|
+
/**
|
|
684
|
+
* Hide the whole switcher unless there are at least this many items. Default 1
|
|
685
|
+
* (show when non-empty); kefi passes 2 (a lone dashboard needs no switcher).
|
|
686
|
+
*/
|
|
687
|
+
minItems?: number;
|
|
688
|
+
/** Extra container style overrides. */
|
|
689
|
+
containerStyle?: ViewStyle | ViewStyle[];
|
|
690
|
+
}
|
|
691
|
+
declare const PillNav: ({ items, pathname, onNavigate, regionLabel, navigateHint, minItems, containerStyle, }: PillNavProps) => React.ReactElement | null;
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* DarkModeControl — the shared colour-scheme control promoted from erevna /
|
|
695
|
+
* katalogos (their Light / Dark / System toggle + the icon-only "cycle" button).
|
|
696
|
+
* It is a CONTROLLED component: the app owns the current `value` and applies the
|
|
697
|
+
* chosen scheme; this component only renders the choices and reports changes.
|
|
698
|
+
*
|
|
699
|
+
* Two variants encode the two behaviours the apps shipped:
|
|
700
|
+
* - `"segmented"` (default): a row of segments (Light / Dark / System), the
|
|
701
|
+
* active one highlighted — the desktop control.
|
|
702
|
+
* - `"cycle"`: a single icon+label button that advances to the NEXT option on
|
|
703
|
+
* each press (wrapping) — the compact mobile control.
|
|
704
|
+
*
|
|
705
|
+
* The options are pre-localized (`label` + `hint`) and value-agnostic (a string),
|
|
706
|
+
* so the app maps its own `DarkModePreference` enum to strings without this
|
|
707
|
+
* package importing it. Every colour flows through the `@dloizides/ui-feedback`
|
|
708
|
+
* theme; STYLE is the only per-app variation.
|
|
709
|
+
*/
|
|
710
|
+
|
|
711
|
+
/** Which control shape to render. */
|
|
712
|
+
type DarkModeVariant = 'segmented' | 'cycle';
|
|
713
|
+
/** One pre-localized colour-scheme choice (e.g. Light / Dark / System). */
|
|
714
|
+
interface DarkModeOption {
|
|
715
|
+
/** App-defined value (its own preference enum mapped to a string). */
|
|
716
|
+
value: string;
|
|
717
|
+
/** Localized visible + accessible label. */
|
|
718
|
+
label: string;
|
|
719
|
+
/** Localized accessibility hint. */
|
|
720
|
+
hint: string;
|
|
721
|
+
/** Optional leading icon. Receives the resolved colour + size. */
|
|
722
|
+
renderIcon?: (color: string, size: number) => React.ReactNode;
|
|
723
|
+
}
|
|
724
|
+
interface DarkModeControlProps {
|
|
725
|
+
/** Current selected value — must match one of `options[].value`. */
|
|
726
|
+
value: string;
|
|
727
|
+
/** The pre-localized choices (typically Light / Dark / System). */
|
|
728
|
+
options: DarkModeOption[];
|
|
729
|
+
/** Change callback — receives the newly-selected value. */
|
|
730
|
+
onChange: (value: string) => void;
|
|
731
|
+
/** Localized accessibility label for the control group. */
|
|
732
|
+
regionLabel: string;
|
|
733
|
+
/** Control shape — a segmented row (default) or a single cycling button. */
|
|
734
|
+
variant?: DarkModeVariant;
|
|
735
|
+
/** testID for the control; segments are `${testID}-${value}`. */
|
|
736
|
+
testID?: string;
|
|
737
|
+
/** Extra container style overrides. */
|
|
738
|
+
containerStyle?: ViewStyle | ViewStyle[];
|
|
739
|
+
}
|
|
740
|
+
declare const DarkModeControl: ({ value, options, onChange, regionLabel, variant, testID, containerStyle, }: DarkModeControlProps) => React.ReactElement | null;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* NavBarLink — a SINGLE horizontal `NavBar` link, extracted so each link owns its
|
|
744
|
+
* own hover/focus state (React hooks can't be per-item inside a map otherwise).
|
|
745
|
+
*
|
|
746
|
+
* It renders the v1 `.gn-links a` affordance, THEME-DRIVEN (no hard-coded colours
|
|
747
|
+
* beyond white ink on the accent pill):
|
|
748
|
+
* - rest → muted text (`colors.rest`), no background
|
|
749
|
+
* - hover → text brightens to full contrast (`colors.hoverText`) + a subtle
|
|
750
|
+
* rounded-pill background (`colors.hoverBg`, e.g. v1 `#16223a`)
|
|
751
|
+
* - active → solid accent pill (`colors.activeBg`) with white ink + aria-current
|
|
752
|
+
* - focus → a themed keyboard focus ring (`colors.ring`)
|
|
753
|
+
* Hover/focus are WEB-only (react-native-web fires `onHoverIn/Out`); on native
|
|
754
|
+
* those handlers never fire, so the link stays at its rest style.
|
|
755
|
+
*/
|
|
756
|
+
|
|
757
|
+
/** Theme-resolved colours a link needs, computed once by `NavBar`. */
|
|
758
|
+
interface NavBarLinkColors {
|
|
759
|
+
/** Muted link text at rest. */
|
|
760
|
+
rest: string;
|
|
761
|
+
/** Brightened link text on hover. */
|
|
762
|
+
hoverText: string;
|
|
763
|
+
/** Subtle hover-pill background. */
|
|
764
|
+
hoverBg: string;
|
|
765
|
+
/** Active-pill background (accent). */
|
|
766
|
+
activeBg: string;
|
|
767
|
+
/** Focus-ring colour. */
|
|
768
|
+
ring: string;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* NavOverflowMenu — the "More ▾" trigger + dropdown that holds the `NavBar` items
|
|
773
|
+
* which don't fit inline (the priority+ overflow set).
|
|
774
|
+
*
|
|
775
|
+
* It REUSES the shared `@dloizides/ui-layout` `ModalDropdown` — the exact component
|
|
776
|
+
* every other menu in the app uses — so the overflow menu is portalled, keyboard
|
|
777
|
+
* navigable, dismissed on outside-click / Escape, and opens on click, looking
|
|
778
|
+
* identical to the rest of the app's menus. The overflow entries are the same
|
|
779
|
+
* `NavItem`s (label + route + active state); selecting one navigates via the same
|
|
780
|
+
* `onNavigate` the inline links use.
|
|
781
|
+
*
|
|
782
|
+
* The trigger is a rounded pill matching a `NavBarLink`: muted at rest, and — when
|
|
783
|
+
* the ACTIVE route lives in the overflow set — it wears the accent pill so the
|
|
784
|
+
* active item stays visibly reachable behind the "…". The dropdown marks that same
|
|
785
|
+
* item selected (its `value` is the active overflow route). a11y: `ModalDropdown`
|
|
786
|
+
* owns the trigger's `role="button"` + `aria-expanded` + caller-supplied label/hint,
|
|
787
|
+
* and gives the options keyboard navigation.
|
|
788
|
+
*
|
|
789
|
+
* Contract discipline (as the rest of `@dloizides/ui-nav`): no FM / router / icon
|
|
790
|
+
* set / store; the label + hint are pre-localized strings from the caller and every
|
|
791
|
+
* colour comes from the theme (via the `NavBarLinkColors` the `NavBar` computed).
|
|
792
|
+
*/
|
|
793
|
+
|
|
794
|
+
interface NavOverflowMenuProps {
|
|
795
|
+
/** The items that spilled out of the inline row. */
|
|
796
|
+
items: NavItem[];
|
|
797
|
+
/** Current active route/path — decides the active-pill + selected option. */
|
|
798
|
+
pathname: string;
|
|
799
|
+
/** Navigation callback — receives the chosen item's `route`. */
|
|
800
|
+
onNavigate: (route: string) => void;
|
|
801
|
+
/** Theme-resolved link colours (same set the inline links use). */
|
|
802
|
+
colors: NavBarLinkColors;
|
|
803
|
+
/** Localized visible + accessible label for the trigger (e.g. "More"). */
|
|
804
|
+
label: string;
|
|
805
|
+
/** Localized accessibility hint for the trigger. */
|
|
806
|
+
hint: string;
|
|
807
|
+
/** testID for the trigger (options default to `` `${testID}-option-${route}` ``). */
|
|
808
|
+
testID: string;
|
|
809
|
+
/** Force a dropdown variant (default: responsive — inline menu on desktop). */
|
|
810
|
+
variant?: DropdownVariant;
|
|
811
|
+
}
|
|
812
|
+
declare const NavOverflowMenu: ({ items, pathname, onNavigate, colors, label, hint, testID, variant, }: NavOverflowMenuProps) => React.ReactElement;
|
|
434
813
|
|
|
435
814
|
/**
|
|
436
815
|
* Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
|
|
@@ -462,6 +841,8 @@ declare const APP_SHELL_SUFFIX: {
|
|
|
462
841
|
readonly nav: "-nav";
|
|
463
842
|
/** The persistent left rail of the back-office layout (only when `sidebar` is supplied). */
|
|
464
843
|
readonly sidebar: "-sidebar";
|
|
844
|
+
/** The intermediate persistent COLLAPSED (icon-only) rail (3-tier layout, tablet band). */
|
|
845
|
+
readonly collapsedSidebar: "-collapsed-sidebar";
|
|
465
846
|
/** Mobile hamburger that opens the nav drawer (back-office layout, below the breakpoint). */
|
|
466
847
|
readonly menuToggle: "-menu-toggle";
|
|
467
848
|
/** The mobile nav drawer panel (holds the `sidebar` slot when opened on a narrow viewport). */
|
|
@@ -605,6 +986,96 @@ declare const navStyles: {
|
|
|
605
986
|
fontWeight: "700";
|
|
606
987
|
};
|
|
607
988
|
};
|
|
989
|
+
declare const pillNavStyles: {
|
|
990
|
+
container: {
|
|
991
|
+
flexDirection: "row";
|
|
992
|
+
flexWrap: "wrap";
|
|
993
|
+
alignItems: "center";
|
|
994
|
+
columnGap: number;
|
|
995
|
+
rowGap: number;
|
|
996
|
+
paddingVertical: number;
|
|
997
|
+
};
|
|
998
|
+
pill: {
|
|
999
|
+
paddingHorizontal: number;
|
|
1000
|
+
paddingVertical: number;
|
|
1001
|
+
borderRadius: number;
|
|
1002
|
+
minHeight: number;
|
|
1003
|
+
flexDirection: "row";
|
|
1004
|
+
alignItems: "center";
|
|
1005
|
+
columnGap: number;
|
|
1006
|
+
justifyContent: "center";
|
|
1007
|
+
};
|
|
1008
|
+
pillText: {
|
|
1009
|
+
fontSize: number;
|
|
1010
|
+
fontWeight: "600";
|
|
1011
|
+
};
|
|
1012
|
+
};
|
|
1013
|
+
declare const darkModeStyles: {
|
|
1014
|
+
segmentRow: {
|
|
1015
|
+
flexDirection: "row";
|
|
1016
|
+
alignItems: "center";
|
|
1017
|
+
borderWidth: number;
|
|
1018
|
+
borderRadius: number;
|
|
1019
|
+
overflow: "hidden";
|
|
1020
|
+
alignSelf: "flex-start";
|
|
1021
|
+
};
|
|
1022
|
+
segment: {
|
|
1023
|
+
paddingHorizontal: number;
|
|
1024
|
+
paddingVertical: number;
|
|
1025
|
+
minHeight: number;
|
|
1026
|
+
flexDirection: "row";
|
|
1027
|
+
alignItems: "center";
|
|
1028
|
+
columnGap: number;
|
|
1029
|
+
justifyContent: "center";
|
|
1030
|
+
};
|
|
1031
|
+
segmentText: {
|
|
1032
|
+
fontSize: number;
|
|
1033
|
+
fontWeight: "600";
|
|
1034
|
+
};
|
|
1035
|
+
cycle: {
|
|
1036
|
+
paddingHorizontal: number;
|
|
1037
|
+
paddingVertical: number;
|
|
1038
|
+
minHeight: number;
|
|
1039
|
+
minWidth: number;
|
|
1040
|
+
borderRadius: number;
|
|
1041
|
+
flexDirection: "row";
|
|
1042
|
+
alignItems: "center";
|
|
1043
|
+
columnGap: number;
|
|
1044
|
+
justifyContent: "center";
|
|
1045
|
+
};
|
|
1046
|
+
cycleText: {
|
|
1047
|
+
fontSize: number;
|
|
1048
|
+
fontWeight: "600";
|
|
1049
|
+
};
|
|
1050
|
+
};
|
|
1051
|
+
declare const collapsedRailStyles: {
|
|
1052
|
+
container: {
|
|
1053
|
+
width: number;
|
|
1054
|
+
paddingTop: number;
|
|
1055
|
+
paddingHorizontal: number;
|
|
1056
|
+
borderRightWidth: number;
|
|
1057
|
+
height: "100%";
|
|
1058
|
+
alignItems: "center";
|
|
1059
|
+
};
|
|
1060
|
+
item: {
|
|
1061
|
+
width: number;
|
|
1062
|
+
height: number;
|
|
1063
|
+
marginVertical: number;
|
|
1064
|
+
borderRadius: number;
|
|
1065
|
+
alignItems: "center";
|
|
1066
|
+
justifyContent: "center";
|
|
1067
|
+
};
|
|
1068
|
+
glyph: {
|
|
1069
|
+
fontSize: number;
|
|
1070
|
+
fontWeight: "700";
|
|
1071
|
+
};
|
|
1072
|
+
spacer: {
|
|
1073
|
+
flex: number;
|
|
1074
|
+
};
|
|
1075
|
+
slot: {
|
|
1076
|
+
alignItems: "center";
|
|
1077
|
+
};
|
|
1078
|
+
};
|
|
608
1079
|
declare const expandableStyles: {
|
|
609
1080
|
childItem: {
|
|
610
1081
|
borderRadius: number;
|
|
@@ -648,4 +1119,4 @@ declare const NAV_ICON_SIZE = 14;
|
|
|
648
1119
|
/** Chevron icon size for expandable sections. */
|
|
649
1120
|
declare const CHEVRON_ICON_SIZE = 12;
|
|
650
1121
|
|
|
651
|
-
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_LINK_GAP, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, type NavUser, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, expandableStyles, isRouteActive, navStyles, resolveContentMaxWidth, roleRoutesToNavItems, useContentMaxWidth };
|
|
1122
|
+
export { ACTIVE_BORDER_RADIUS, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, DEFAULT_COLLAPSED_RAIL_MAX, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, NavShell, type NavShellCollapsedRail, type NavShellLayout, type NavShellProps, type NavShellSideRail, type NavShellTopBar, type NavUser, PillNav, type PillNavProps, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type ShellMessage, Sidebar, type SidebarProps, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, isRouteActive, navStyles, pillNavStyles, resolveContentMaxWidth, resolveRailMode, roleRoutesToNavItems, useContentMaxWidth };
|