@dloizides/ui-nav 1.28.0 → 1.30.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/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ViewStyle, StyleProp } from 'react-native';
2
+ import { ViewStyle, ImageSourcePropType, StyleProp } from 'react-native';
3
3
  import { UiTheme } from '@dloizides/ui-feedback';
4
4
  import { DropdownVariant } from '@dloizides/ui-layout';
5
5
  import { resolveAccessibleRoutes, RoleRouteTable, RoleRoute } from '@dloizides/auth-web';
@@ -398,8 +398,7 @@ declare enum NavHoverFeedback {
398
398
  *
399
399
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
400
400
  * icon set, or store imports. Labels are pre-localized strings, icons are render
401
- * slots, and every colour is read from the theme. This is purely additive —
402
- * `Sidebar`/`Topbar` are unchanged and the new overflow labels are optional.
401
+ * slots, and every colour is read from the theme.
403
402
  */
404
403
 
405
404
  interface NavBarProps {
@@ -463,6 +462,27 @@ interface NavBarProps {
463
462
  */
464
463
  declare const NavBar: ({ barTheme, ...props }: NavBarProps) => React.ReactElement;
465
464
 
465
+ interface BrandHomeLinkProps {
466
+ route: string;
467
+ label: string;
468
+ onNavigate: (route: string) => void;
469
+ children: React.ReactNode;
470
+ }
471
+ /** The app logo as a link to the app's home route, for `NavBar` and apps with their own header. */
472
+ declare const BrandHomeLink: ({ route, label, onNavigate, children }: BrandHomeLinkProps) => React.ReactElement;
473
+
474
+ interface BrandMarkProps {
475
+ source: ImageSourcePropType;
476
+ name: string;
477
+ route: string;
478
+ label: string;
479
+ onNavigate: (route: string) => void;
480
+ size?: number;
481
+ iconOnly?: boolean;
482
+ }
483
+ /** The app icon with its name, as a link to the app's home route. */
484
+ declare const BrandMark: ({ source, name, route, label, onNavigate, size, iconOnly, }: BrandMarkProps) => React.ReactElement;
485
+
466
486
  /**
467
487
  * `Nav` — ONE component that renders EITHER the horizontal top bar (`NavBar`) or the
468
488
  * vertical rail (`Sidebar`) from the same `NavItem[]`, chosen by a single
@@ -1126,6 +1146,9 @@ declare const NAV_TEST_IDS: {
1126
1146
  readonly pillNavMenu: "pill-nav-menu";
1127
1147
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
1128
1148
  readonly topbarCenter: "topbar-center";
1149
+ readonly brandHome: "nav-brand-home";
1150
+ readonly brandMarkIcon: "nav-brand-mark-icon";
1151
+ readonly brandMarkName: "nav-brand-mark-name";
1129
1152
  };
1130
1153
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
1131
1154
  declare const APP_SHELL_SUFFIX: {
@@ -1644,4 +1667,4 @@ interface BottomTabBarProps {
1644
1667
  /** Phone tab bar; `tabFeedback` picks the press feedback, `Default` adds no motion. */
1645
1668
  declare const BottomTabBar: ({ items, pathname, onNavigate, tabFeedback, navigateHint, regionLabel, containerStyle, testID, }: BottomTabBarProps) => React.ReactElement;
1646
1669
 
1647
- export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, 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, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
1670
+ export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, BrandHomeLink, type BrandHomeLinkProps, BrandMark, type BrandMarkProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, 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, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ViewStyle, StyleProp } from 'react-native';
2
+ import { ViewStyle, ImageSourcePropType, StyleProp } from 'react-native';
3
3
  import { UiTheme } from '@dloizides/ui-feedback';
4
4
  import { DropdownVariant } from '@dloizides/ui-layout';
5
5
  import { resolveAccessibleRoutes, RoleRouteTable, RoleRoute } from '@dloizides/auth-web';
@@ -398,8 +398,7 @@ declare enum NavHoverFeedback {
398
398
  *
399
399
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
400
400
  * icon set, or store imports. Labels are pre-localized strings, icons are render
401
- * slots, and every colour is read from the theme. This is purely additive —
402
- * `Sidebar`/`Topbar` are unchanged and the new overflow labels are optional.
401
+ * slots, and every colour is read from the theme.
403
402
  */
404
403
 
405
404
  interface NavBarProps {
@@ -463,6 +462,27 @@ interface NavBarProps {
463
462
  */
464
463
  declare const NavBar: ({ barTheme, ...props }: NavBarProps) => React.ReactElement;
465
464
 
465
+ interface BrandHomeLinkProps {
466
+ route: string;
467
+ label: string;
468
+ onNavigate: (route: string) => void;
469
+ children: React.ReactNode;
470
+ }
471
+ /** The app logo as a link to the app's home route, for `NavBar` and apps with their own header. */
472
+ declare const BrandHomeLink: ({ route, label, onNavigate, children }: BrandHomeLinkProps) => React.ReactElement;
473
+
474
+ interface BrandMarkProps {
475
+ source: ImageSourcePropType;
476
+ name: string;
477
+ route: string;
478
+ label: string;
479
+ onNavigate: (route: string) => void;
480
+ size?: number;
481
+ iconOnly?: boolean;
482
+ }
483
+ /** The app icon with its name, as a link to the app's home route. */
484
+ declare const BrandMark: ({ source, name, route, label, onNavigate, size, iconOnly, }: BrandMarkProps) => React.ReactElement;
485
+
466
486
  /**
467
487
  * `Nav` — ONE component that renders EITHER the horizontal top bar (`NavBar`) or the
468
488
  * vertical rail (`Sidebar`) from the same `NavItem[]`, chosen by a single
@@ -1126,6 +1146,9 @@ declare const NAV_TEST_IDS: {
1126
1146
  readonly pillNavMenu: "pill-nav-menu";
1127
1147
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
1128
1148
  readonly topbarCenter: "topbar-center";
1149
+ readonly brandHome: "nav-brand-home";
1150
+ readonly brandMarkIcon: "nav-brand-mark-icon";
1151
+ readonly brandMarkName: "nav-brand-mark-name";
1129
1152
  };
1130
1153
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
1131
1154
  declare const APP_SHELL_SUFFIX: {
@@ -1644,4 +1667,4 @@ interface BottomTabBarProps {
1644
1667
  /** Phone tab bar; `tabFeedback` picks the press feedback, `Default` adds no motion. */
1645
1668
  declare const BottomTabBar: ({ items, pathname, onNavigate, tabFeedback, navigateHint, regionLabel, containerStyle, testID, }: BottomTabBarProps) => React.ReactElement;
1646
1669
 
1647
- export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, 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, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
1670
+ export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, BrandHomeLink, type BrandHomeLinkProps, BrandMark, type BrandMarkProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, 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, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
package/dist/index.js CHANGED
@@ -883,7 +883,10 @@ var NAV_TEST_IDS = {
883
883
  /** the compact "Current ▾" trigger of `PillNav` with `variant={PillNavVariant.Menu}`. */
884
884
  pillNavMenu: "pill-nav-menu",
885
885
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
886
- topbarCenter: "topbar-center"
886
+ topbarCenter: "topbar-center",
887
+ brandHome: "nav-brand-home",
888
+ brandMarkIcon: "nav-brand-mark-icon",
889
+ brandMarkName: "nav-brand-mark-name"
887
890
  };
888
891
  var APP_SHELL_SUFFIX = {
889
892
  content: "-content",
@@ -1390,6 +1393,7 @@ var NavBarInner = ({
1390
1393
  () => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
1391
1394
  [contentMaxWidth]
1392
1395
  );
1396
+ React.useEffect(() => setOpen(false), [pathname]);
1393
1397
  const toggleMenu = React.useCallback(() => setOpen((v) => !v), []);
1394
1398
  const handlePress = React.useCallback(
1395
1399
  (route) => {
@@ -1537,6 +1541,77 @@ var NavBar = ({ barTheme, ...props }) => {
1537
1541
  if (barTheme === void 0) return /* @__PURE__ */ jsxRuntime.jsx(NavBarInner, { ...props });
1538
1542
  return /* @__PURE__ */ jsxRuntime.jsx(uiFeedback.UiProvider, { navigate, t, theme: barTheme, children: /* @__PURE__ */ jsxRuntime.jsx(NavBarInner, { ...props }) });
1539
1543
  };
1544
+ var styles3 = reactNative.StyleSheet.create({
1545
+ hitBox: { minHeight: MIN_TOUCH_TARGET, minWidth: MIN_TOUCH_TARGET, justifyContent: "center" }
1546
+ });
1547
+ function anchorProps(route) {
1548
+ return { href: route };
1549
+ }
1550
+ function hasNewTabModifier(event) {
1551
+ const { ctrlKey, metaKey, shiftKey } = event.nativeEvent;
1552
+ return ctrlKey === true || metaKey === true || shiftKey === true;
1553
+ }
1554
+ var BrandHomeLink = ({ route, label, onNavigate, children }) => {
1555
+ const { theme } = uiFeedback.useUi();
1556
+ const [focused, setFocused] = React.useState(false);
1557
+ const handlePress = (event) => {
1558
+ if (hasNewTabModifier(event)) return;
1559
+ event.preventDefault();
1560
+ onNavigate(route);
1561
+ };
1562
+ return /* @__PURE__ */ jsxRuntime.jsx(
1563
+ reactNative.Pressable,
1564
+ {
1565
+ accessibilityLabel: label,
1566
+ accessibilityRole: "link",
1567
+ style: [styles3.hitBox, focusRingStyle(focused, theme.palette.primary["500"])],
1568
+ testID: NAV_TEST_IDS.brandHome,
1569
+ onBlur: () => setFocused(false),
1570
+ onFocus: () => setFocused(true),
1571
+ onPress: handlePress,
1572
+ ...anchorProps(route),
1573
+ children
1574
+ }
1575
+ );
1576
+ };
1577
+ var DEFAULT_ICON_SIZE = 32;
1578
+ var NAME_FONT_SIZE = 16;
1579
+ var NAME_GAP = 8;
1580
+ var styles4 = reactNative.StyleSheet.create({
1581
+ name: { flexShrink: 1, fontSize: NAME_FONT_SIZE, fontWeight: "700" },
1582
+ row: { alignItems: "center", flexDirection: "row", gap: NAME_GAP }
1583
+ });
1584
+ var BrandMark = ({
1585
+ source,
1586
+ name,
1587
+ route,
1588
+ label,
1589
+ onNavigate,
1590
+ size = DEFAULT_ICON_SIZE,
1591
+ iconOnly = false
1592
+ }) => {
1593
+ const { theme } = uiFeedback.useUi();
1594
+ return /* @__PURE__ */ jsxRuntime.jsx(BrandHomeLink, { label, route, onNavigate, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.row, children: [
1595
+ /* @__PURE__ */ jsxRuntime.jsx(
1596
+ reactNative.Image,
1597
+ {
1598
+ accessibilityElementsHidden: true,
1599
+ source,
1600
+ style: { height: size, width: size },
1601
+ testID: NAV_TEST_IDS.brandMarkIcon
1602
+ }
1603
+ ),
1604
+ iconOnly ? null : /* @__PURE__ */ jsxRuntime.jsx(
1605
+ reactNative.Text,
1606
+ {
1607
+ numberOfLines: 1,
1608
+ style: [styles4.name, { color: theme.colors.text }],
1609
+ testID: NAV_TEST_IDS.brandMarkName,
1610
+ children: name
1611
+ }
1612
+ )
1613
+ ] }) });
1614
+ };
1540
1615
  var Nav = (props) => {
1541
1616
  if (props.orientation === "vertical") {
1542
1617
  return /* @__PURE__ */ jsxRuntime.jsx(Sidebar, { ...props });
@@ -1554,7 +1629,7 @@ function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
1554
1629
  }
1555
1630
  return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
1556
1631
  }
1557
- var styles3 = reactNative.StyleSheet.create({
1632
+ var styles5 = reactNative.StyleSheet.create({
1558
1633
  band: { left: 0, position: "absolute", right: 0, top: 0 }
1559
1634
  });
1560
1635
  function readBackdrop(theme) {
@@ -1577,7 +1652,7 @@ function resolveShellBackdrop(theme, viewport, contentPadding, testID) {
1577
1652
  reactNative.View,
1578
1653
  {
1579
1654
  pointerEvents: "none",
1580
- style: [styles3.band, { backgroundColor: band.color, height: contentPadding + overlap }],
1655
+ style: [styles5.band, { backgroundColor: band.color, height: contentPadding + overlap }],
1581
1656
  testID: `${testID}${APP_SHELL_SUFFIX.band}`
1582
1657
  }
1583
1658
  );
@@ -1589,7 +1664,7 @@ var CARD_BORDER_WIDTH = 1;
1589
1664
  var CARD_TITLE_FONT_SIZE = 16;
1590
1665
  var CARD_MESSAGE_FONT_SIZE = 14;
1591
1666
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1592
- var styles4 = reactNative.StyleSheet.create({
1667
+ var styles6 = reactNative.StyleSheet.create({
1593
1668
  // Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
1594
1669
  // stably-mounted children, never a replacement for them. Replacing children here
1595
1670
  // would unmount whatever they host (e.g. a navigator), and on remount React
@@ -1621,9 +1696,9 @@ function MessageCard({
1621
1696
  }) {
1622
1697
  const { theme } = uiFeedback.useUi();
1623
1698
  const colors = theme.colors;
1624
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1625
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.cardTitle, { color: accentColor }], children: message.titleText }),
1626
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1699
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles6.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1700
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.cardTitle, { color: accentColor }], children: message.titleText }),
1701
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1627
1702
  ] });
1628
1703
  }
1629
1704
  function useContentBody(state, children, testID) {
@@ -1635,12 +1710,12 @@ function useContentBody(state, children, testID) {
1635
1710
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
1636
1711
  if (state.error)
1637
1712
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1638
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.loadingHost, children: [
1713
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.loadingHost, children: [
1639
1714
  children,
1640
1715
  state.loading === true ? /* @__PURE__ */ jsxRuntime.jsx(
1641
1716
  reactNative.View,
1642
1717
  {
1643
- style: [styles4.loadingOverlay, { backgroundColor: theme.colors.background }],
1718
+ style: [styles6.loadingOverlay, { backgroundColor: theme.colors.background }],
1644
1719
  testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
1645
1720
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
1646
1721
  }
@@ -1667,7 +1742,7 @@ var DEFAULT_DRAWER_LABELS = {
1667
1742
  closeLabel: "Close menu",
1668
1743
  closeHint: "Close the navigation menu"
1669
1744
  };
1670
- var styles5 = reactNative.StyleSheet.create({
1745
+ var styles7 = reactNative.StyleSheet.create({
1671
1746
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1672
1747
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1673
1748
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1702,10 +1777,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1702
1777
  accessibilityHint: hint,
1703
1778
  accessibilityLabel: label,
1704
1779
  ringColor: theme.palette.primary["500"],
1705
- style: styles5.menuToggle,
1780
+ style: styles7.menuToggle,
1706
1781
  testID,
1707
1782
  onPress: onOpen,
1708
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1783
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1709
1784
  }
1710
1785
  );
1711
1786
  };
@@ -1731,14 +1806,14 @@ var MobileDrawer = ({
1731
1806
  node.addEventListener("click", handleClick);
1732
1807
  return () => node.removeEventListener("click", handleClick);
1733
1808
  }, [onClose]);
1734
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.overlay, children: [
1809
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.overlay, children: [
1735
1810
  /* @__PURE__ */ jsxRuntime.jsx(
1736
1811
  reactNative.Pressable,
1737
1812
  {
1738
1813
  accessibilityHint: labels.closeHint,
1739
1814
  accessibilityLabel: labels.closeLabel,
1740
1815
  accessibilityRole: "button",
1741
- style: styles5.scrim,
1816
+ style: styles7.scrim,
1742
1817
  testID: scrimTestID,
1743
1818
  onPress: onClose
1744
1819
  }
@@ -1749,7 +1824,7 @@ var MobileDrawer = ({
1749
1824
  ref: panelRef,
1750
1825
  "aria-modal": true,
1751
1826
  role: "dialog",
1752
- style: [styles5.panel, { backgroundColor: theme.colors.surface }],
1827
+ style: [styles7.panel, { backgroundColor: theme.colors.surface }],
1753
1828
  testID: drawerTestID,
1754
1829
  children: renderTextSlot(sidebar, { color: theme.colors.text })
1755
1830
  }
@@ -1767,7 +1842,7 @@ function useContentMaxWidth(width) {
1767
1842
  return resolveContentMaxWidth(width, viewport);
1768
1843
  }
1769
1844
  var DEFAULT_CONTENT_PADDING = 24;
1770
- var styles6 = reactNative.StyleSheet.create({
1845
+ var styles8 = reactNative.StyleSheet.create({
1771
1846
  root: { flex: 1 },
1772
1847
  // Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
1773
1848
  // `children` (and any navigator they host) are never unmounted while the gate
@@ -1829,13 +1904,13 @@ var AppShell = ({
1829
1904
  }, [isUnauthenticated, gate]);
1830
1905
  const gatePending = gate?.pending === true;
1831
1906
  if (isUnauthenticated) return null;
1832
- const columnStyle = maxWidth === "full" ? styles6.columnFull : [styles6.columnCapped, { maxWidth }];
1833
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles6.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1907
+ const columnStyle = maxWidth === "full" ? styles8.columnFull : [styles8.columnCapped, { maxWidth }];
1908
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles8.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1834
1909
  const scroller = /* @__PURE__ */ jsxRuntime.jsxs(
1835
1910
  reactNative.ScrollView,
1836
1911
  {
1837
- contentContainerStyle: [styles6.scrollContent, { padding: contentPadding }],
1838
- style: styles6.scroll,
1912
+ contentContainerStyle: [styles8.scrollContent, { padding: contentPadding }],
1913
+ style: styles8.scroll,
1839
1914
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1840
1915
  children: [
1841
1916
  look.band,
@@ -1843,7 +1918,7 @@ var AppShell = ({
1843
1918
  ]
1844
1919
  }
1845
1920
  );
1846
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles6.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1921
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles8.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1847
1922
  /* @__PURE__ */ jsxRuntime.jsx(
1848
1923
  MenuToggle,
1849
1924
  {
@@ -1853,14 +1928,14 @@ var AppShell = ({
1853
1928
  onOpen: openDrawer
1854
1929
  }
1855
1930
  ),
1856
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
1931
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
1857
1932
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: look.chromeText }) });
1858
1933
  const railRegion = railMode === "full" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: renderTextSlot(sidebar, { color: theme.colors.text }) }) : railMode === "collapsed" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`, children: renderTextSlot(collapsedSidebar, { color: theme.colors.text }) }) : null;
1859
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles6.root, look.root], testID, children: [
1934
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles8.root, look.root], testID, children: [
1860
1935
  headerRegion,
1861
1936
  nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: look.chromeText }) }) : renderTextSlot(nav, { color: look.chromeText }) }) : null,
1862
1937
  banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1863
- railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.bodyRow, children: [
1938
+ railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.bodyRow, children: [
1864
1939
  railRegion,
1865
1940
  scroller
1866
1941
  ] }) : scroller,
@@ -1877,7 +1952,7 @@ var AppShell = ({
1877
1952
  gatePending ? /* @__PURE__ */ jsxRuntime.jsx(
1878
1953
  reactNative.View,
1879
1954
  {
1880
- style: [styles6.pendingOverlay, { backgroundColor: theme.colors.background }],
1955
+ style: [styles8.pendingOverlay, { backgroundColor: theme.colors.background }],
1881
1956
  testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
1882
1957
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
1883
1958
  }
@@ -2244,7 +2319,7 @@ var KEY_DOWN = "ArrowDown";
2244
2319
  var KEY_UP = "ArrowUp";
2245
2320
  var KEY_ENTER = "Enter";
2246
2321
  var KEY_ESCAPE = "Escape";
2247
- var styles7 = reactNative.StyleSheet.create({
2322
+ var styles9 = reactNative.StyleSheet.create({
2248
2323
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
2249
2324
  scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
2250
2325
  panel: {
@@ -2273,14 +2348,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
2273
2348
  accessibilityLabel: item.label,
2274
2349
  accessibilityRole: "button",
2275
2350
  accessibilityState: { selected },
2276
- style: [styles7.row, selected ? { backgroundColor: colors.border } : void 0],
2351
+ style: [styles9.row, selected ? { backgroundColor: colors.border } : void 0],
2277
2352
  testID: item.testID ?? item.key,
2278
2353
  onPress: () => onChoose(item),
2279
2354
  children: [
2280
2355
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
2281
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.rowText, children: [
2282
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.rowLabel, { color: colors.text }], children: item.label }),
2283
- item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2356
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.rowText, children: [
2357
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.rowLabel, { color: colors.text }], children: item.label }),
2358
+ item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2284
2359
  ] })
2285
2360
  ]
2286
2361
  }
@@ -2320,14 +2395,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2320
2395
  [results, selected, choose, onClose]
2321
2396
  );
2322
2397
  if (!open) return null;
2323
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.overlay, children: [
2398
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles9.overlay, children: [
2324
2399
  /* @__PURE__ */ jsxRuntime.jsx(
2325
2400
  reactNative.Pressable,
2326
2401
  {
2327
2402
  accessibilityHint: labels.closeHint,
2328
2403
  accessibilityLabel: labels.closeLabel,
2329
2404
  accessibilityRole: "button",
2330
- style: styles7.scrim,
2405
+ style: styles9.scrim,
2331
2406
  testID: `${testID ?? "command-palette"}-scrim`,
2332
2407
  onPress: onClose
2333
2408
  }
@@ -2338,7 +2413,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2338
2413
  "aria-modal": true,
2339
2414
  "aria-label": labels.regionLabel,
2340
2415
  role: "dialog",
2341
- style: [styles7.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2416
+ style: [styles9.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2342
2417
  testID: testID ?? "command-palette",
2343
2418
  children: [
2344
2419
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2348,14 +2423,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2348
2423
  accessibilityLabel: labels.placeholder,
2349
2424
  placeholder: labels.placeholder,
2350
2425
  placeholderTextColor: colors.textSecondary,
2351
- style: [styles7.input, { color: colors.text, borderBottomColor: colors.border }],
2426
+ style: [styles9.input, { color: colors.text, borderBottomColor: colors.border }],
2352
2427
  testID: `${testID ?? "command-palette"}-input`,
2353
2428
  value: query,
2354
2429
  onChangeText: setQuery,
2355
2430
  onKeyPress
2356
2431
  }
2357
2432
  ),
2358
- results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles7.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2433
+ results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles9.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2359
2434
  ]
2360
2435
  }
2361
2436
  )
@@ -2460,7 +2535,7 @@ var TAB_MIN_HEIGHT = 56;
2460
2535
  var LABEL_SIZE = 12;
2461
2536
  var LABEL_GAP = 2;
2462
2537
  var TAB_PADDING = 4;
2463
- var styles8 = reactNative.StyleSheet.create({
2538
+ var styles10 = reactNative.StyleSheet.create({
2464
2539
  tab: {
2465
2540
  flex: 1,
2466
2541
  minHeight: TAB_MIN_HEIGHT,
@@ -2497,31 +2572,31 @@ var BottomTab = ({
2497
2572
  accessibilityLabel: item.label,
2498
2573
  accessibilityRole: "link",
2499
2574
  accessibilityState: { selected: isActive },
2500
- style: [styles8.tab, { opacity: feedback.opacity }],
2575
+ style: [styles10.tab, { opacity: feedback.opacity }],
2501
2576
  testID: item.testID ?? item.key,
2502
2577
  onPress: () => onPress(item.route),
2503
2578
  onPressIn: () => setPressed(true),
2504
2579
  onPressOut: () => setPressed(false),
2505
2580
  ...ariaCurrentProps3(isActive),
2506
2581
  children: [
2507
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles8.iconWrap, { transform: [{ scale }] }], children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles10.iconWrap, { transform: [{ scale }] }], children: [
2508
2583
  feedback.pillScale === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
2509
2584
  reactNative.View,
2510
2585
  {
2511
2586
  pointerEvents: "none",
2512
- style: [styles8.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2587
+ style: [styles10.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2513
2588
  }
2514
2589
  ),
2515
2590
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ transform: feedback.iconTransform }, motion], children: item.renderIcon?.(color, ICON_SIZE) })
2516
2591
  ] }),
2517
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.label, { color }], children: item.label })
2592
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles10.label, { color }], children: item.label })
2518
2593
  ]
2519
2594
  }
2520
2595
  );
2521
2596
  };
2522
2597
  var DEFAULT_TEST_ID = "bottom-tab-bar";
2523
2598
  var SLIDER_HEIGHT = 3;
2524
- var styles9 = reactNative.StyleSheet.create({
2599
+ var styles11 = reactNative.StyleSheet.create({
2525
2600
  bar: { flexDirection: "row", borderTopWidth: reactNative.StyleSheet.hairlineWidth },
2526
2601
  slider: { position: "absolute", top: 0, height: SLIDER_HEIGHT }
2527
2602
  });
@@ -2548,14 +2623,14 @@ var BottomTabBar = ({
2548
2623
  {
2549
2624
  "aria-label": regionLabel,
2550
2625
  role: "navigation",
2551
- style: [styles9.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2626
+ style: [styles11.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2552
2627
  testID,
2553
2628
  children: [
2554
2629
  bar === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
2555
2630
  reactNative.View,
2556
2631
  {
2557
2632
  pointerEvents: "none",
2558
- style: [styles9.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2633
+ style: [styles11.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2559
2634
  }
2560
2635
  ),
2561
2636
  items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2582,6 +2657,8 @@ exports.APP_SHELL_SUFFIX = APP_SHELL_SUFFIX;
2582
2657
  exports.AppShell = AppShell;
2583
2658
  exports.BASE_INDENT = BASE_INDENT;
2584
2659
  exports.BottomTabBar = BottomTabBar;
2660
+ exports.BrandHomeLink = BrandHomeLink;
2661
+ exports.BrandMark = BrandMark;
2585
2662
  exports.CHEVRON_ICON_SIZE = CHEVRON_ICON_SIZE;
2586
2663
  exports.CollapsedRail = CollapsedRail;
2587
2664
  exports.CommandPalette = CommandPalette;