@dloizides/ui-nav 1.34.0 → 1.36.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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
1
+ import React, { createContext, useState, useCallback, useMemo, useContext, useEffect, useRef } from 'react';
2
2
  import { StyleSheet, Platform, Pressable, Text, View, TextInput, useWindowDimensions, Image, ScrollView, ActivityIndicator, Animated } from 'react-native';
3
3
  import { useUi, UiProvider } from '@dloizides/ui-feedback';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -480,6 +480,18 @@ function resolveActiveRoute(items, pathname) {
480
480
  }
481
481
  return winner;
482
482
  }
483
+ var NavAccordionContext = createContext(false);
484
+ function hasActiveDescendant(item, pathname) {
485
+ if (isRouteActive(pathname, item.route)) return true;
486
+ return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
487
+ }
488
+ function initialOpenGroup(items, pathname) {
489
+ const hasChildren = (item) => (item.children ?? []).length > 0;
490
+ return items.find((item) => hasChildren(item) && hasActiveDescendant(item, pathname))?.key;
491
+ }
492
+ function nextOpenGroup(current, pressed) {
493
+ return current === pressed ? void 0 : pressed;
494
+ }
483
495
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
484
496
  function getReducedMotionQuery() {
485
497
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
@@ -529,10 +541,6 @@ function ariaExpandedProps(expanded) {
529
541
  function hoverHandlerProps(onIn, onOut) {
530
542
  return IS_WEB ? { onHoverIn: onIn, onHoverOut: onOut } : {};
531
543
  }
532
- function hasActiveDescendant(item, pathname) {
533
- if (isRouteActive(pathname, item.route)) return true;
534
- return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
535
- }
536
544
  function TintOverlay({
537
545
  opacity,
538
546
  color,
@@ -556,15 +564,21 @@ var NavExpandableItem = ({
556
564
  collapseHint,
557
565
  renderChevron,
558
566
  activeRoute,
559
- depth = 0
567
+ depth = 0,
568
+ expanded: expandedProp,
569
+ onToggle
560
570
  }) => {
561
- const [expanded, setExpanded] = useState(() => hasActiveDescendant(item, pathname));
571
+ const [internalExpanded, setExpanded] = useState(() => hasActiveDescendant(item, pathname));
572
+ const expanded = expandedProp ?? internalExpanded;
562
573
  const [focused, setFocused] = useState(false);
563
574
  const [hovered, setHovered] = useState(false);
564
575
  const { theme } = useUi();
565
576
  const colors = theme.colors;
566
577
  const primaryColor = theme.palette.primary["500"];
567
- const toggle = useCallback(() => setExpanded((v) => !v), []);
578
+ const toggle = useCallback(() => {
579
+ if (onToggle !== void 0) onToggle(item.key);
580
+ else setExpanded((v) => !v);
581
+ }, [onToggle, item.key]);
568
582
  const onHoverIn = useCallback(() => setHovered(true), []);
569
583
  const onHoverOut = useCallback(() => setHovered(false), []);
570
584
  const indent = depth * BASE_INDENT;
@@ -675,9 +689,72 @@ var DEFAULT_SEARCH_BREAKPOINT = LAYOUT_COLLAPSE_BREAKPOINT;
675
689
  function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
676
690
  return viewport >= breakpoint ? "inline" : "palette";
677
691
  }
692
+
693
+ // src/sidebarEdgeFade.ts
694
+ var EDGE_TOLERANCE = 1;
695
+ function resolveEdgeFades({ offsetY, viewportHeight, contentHeight }) {
696
+ const hiddenBelow = contentHeight - (offsetY + viewportHeight);
697
+ return { top: offsetY > EDGE_TOLERANCE, bottom: hiddenBelow > EDGE_TOLERANCE };
698
+ }
699
+ var FADE_BAND_HEIGHT = 6;
700
+ var FADE_BAND_OPACITIES = [0.9, 0.65, 0.4, 0.15];
701
+ var SCROLL_EVENT_THROTTLE_MS = 16;
702
+ var styles2 = StyleSheet.create({
703
+ area: { flex: 1, minHeight: 0 },
704
+ fade: { position: "absolute", left: 0, right: 0 },
705
+ fadeTop: { top: 0 },
706
+ fadeBottom: { bottom: 0, flexDirection: "column-reverse" },
707
+ band: { height: FADE_BAND_HEIGHT }
708
+ });
709
+ function EdgeFade({ edge, color }) {
710
+ return /* @__PURE__ */ jsx(
711
+ View,
712
+ {
713
+ pointerEvents: "none",
714
+ style: [styles2.fade, edge === "top" ? styles2.fadeTop : styles2.fadeBottom],
715
+ testID: `sidebar-fade-${edge}`,
716
+ children: FADE_BAND_OPACITIES.map((opacity) => /* @__PURE__ */ jsx(View, { style: [styles2.band, { backgroundColor: color, opacity }] }, opacity))
717
+ }
718
+ );
719
+ }
720
+ var SidebarScrollArea = ({ children }) => {
721
+ const { theme } = useUi();
722
+ const metrics = useRef({ offsetY: 0, viewportHeight: 0, contentHeight: 0 });
723
+ const [fades, setFades] = useState({ top: false, bottom: false });
724
+ const update = useCallback((next) => {
725
+ metrics.current = { ...metrics.current, ...next };
726
+ const resolved = resolveEdgeFades(metrics.current);
727
+ setFades((prev) => prev.top === resolved.top && prev.bottom === resolved.bottom ? prev : resolved);
728
+ }, []);
729
+ const onLayout = useCallback(
730
+ (e) => update({ viewportHeight: e.nativeEvent.layout.height }),
731
+ [update]
732
+ );
733
+ const onContentSizeChange = useCallback((_w, h) => update({ contentHeight: h }), [update]);
734
+ const onScroll = useCallback(
735
+ (e) => update({ offsetY: e.nativeEvent.contentOffset.y }),
736
+ [update]
737
+ );
738
+ return /* @__PURE__ */ jsxs(View, { style: styles2.area, children: [
739
+ /* @__PURE__ */ jsx(
740
+ ScrollView,
741
+ {
742
+ scrollEventThrottle: SCROLL_EVENT_THROTTLE_MS,
743
+ style: styles2.area,
744
+ testID: "sidebar-scroll",
745
+ onContentSizeChange,
746
+ onLayout,
747
+ onScroll,
748
+ children
749
+ }
750
+ ),
751
+ fades.top ? /* @__PURE__ */ jsx(EdgeFade, { color: theme.colors.surface, edge: "top" }) : null,
752
+ fades.bottom ? /* @__PURE__ */ jsx(EdgeFade, { color: theme.colors.surface, edge: "bottom" }) : null
753
+ ] });
754
+ };
678
755
  var FIELD_MIN_HEIGHT = 38;
679
756
  var CLEAR_GLYPH = "\u2715";
680
- var styles2 = StyleSheet.create({
757
+ var styles3 = StyleSheet.create({
681
758
  wrap: {
682
759
  flexDirection: "row",
683
760
  alignItems: "center",
@@ -718,7 +795,7 @@ var SidebarSearch = ({
718
795
  View,
719
796
  {
720
797
  style: [
721
- styles2.wrap,
798
+ styles3.wrap,
722
799
  { backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
723
800
  focusRingStyle(focused, primary)
724
801
  ],
@@ -730,7 +807,7 @@ var SidebarSearch = ({
730
807
  accessibilityLabel: labels.placeholder,
731
808
  placeholder: labels.placeholder,
732
809
  placeholderTextColor: colors.textSecondary,
733
- style: [styles2.input, { color: colors.text }],
810
+ style: [styles3.input, { color: colors.text }],
734
811
  testID,
735
812
  value,
736
813
  onBlur: () => setFocused(false),
@@ -744,10 +821,10 @@ var SidebarSearch = ({
744
821
  accessibilityHint: labels.clearHint,
745
822
  accessibilityLabel: labels.clearLabel,
746
823
  accessibilityRole: "button",
747
- style: styles2.clear,
824
+ style: styles3.clear,
748
825
  testID: `${testID}-clear`,
749
826
  onPress: onClear,
750
- children: /* @__PURE__ */ jsx(Text, { style: [styles2.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
827
+ children: /* @__PURE__ */ jsx(Text, { style: [styles3.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
751
828
  }
752
829
  ) : null
753
830
  ]
@@ -806,6 +883,10 @@ var Sidebar = ({
806
883
  const visibleItems = showInlineSearch ? filterNavItems(items, query) : items;
807
884
  const showEmpty = showInlineSearch && isFilterActive(query) && visibleItems.length === 0;
808
885
  const activeRoute = resolveActiveRoute(items, pathname);
886
+ const accordion = useContext(NavAccordionContext);
887
+ const [openGroup, setOpenGroup] = useState(() => initialOpenGroup(items, pathname));
888
+ const toggleGroup = useCallback((key) => setOpenGroup((current) => nextOpenGroup(current, key)), []);
889
+ const accordionProps = (item) => accordion && (item.children ?? []).length > 0 ? { expanded: openGroup === item.key, onToggle: toggleGroup } : {};
809
890
  return /* @__PURE__ */ jsxs(
810
891
  View,
811
892
  {
@@ -839,22 +920,24 @@ var Sidebar = ({
839
920
  onPress: paletteTrigger.onOpen
840
921
  }
841
922
  ) }) : null,
842
- renderTextSlot(header, { color: colors.text }),
843
- showEmpty && search !== void 0 ? /* @__PURE__ */ jsx(Text, { style: [navStyles.sidebarEmpty, { color: colors.textSecondary }], testID: "sidebar-search-empty", children: search.emptyText }) : visibleItems.map((item) => /* @__PURE__ */ jsx(
844
- NavExpandableItem,
845
- {
846
- activeRoute,
847
- collapseHint,
848
- expandHint,
849
- item,
850
- navigateHint,
851
- pathname,
852
- renderChevron,
853
- onNavigate
854
- },
855
- item.key
856
- )),
857
- /* @__PURE__ */ jsx(View, { style: navStyles.sidebarSpacer }),
923
+ /* @__PURE__ */ jsxs(SidebarScrollArea, { children: [
924
+ renderTextSlot(header, { color: colors.text }),
925
+ showEmpty && search !== void 0 ? /* @__PURE__ */ jsx(Text, { style: [navStyles.sidebarEmpty, { color: colors.textSecondary }], testID: "sidebar-search-empty", children: search.emptyText }) : visibleItems.map((item) => /* @__PURE__ */ jsx(
926
+ NavExpandableItem,
927
+ {
928
+ activeRoute,
929
+ collapseHint,
930
+ expandHint,
931
+ item,
932
+ navigateHint,
933
+ pathname,
934
+ renderChevron,
935
+ onNavigate,
936
+ ...accordionProps(item)
937
+ },
938
+ item.key
939
+ ))
940
+ ] }),
858
941
  renderTextSlot(footer, { color: colors.text })
859
942
  ]
860
943
  }
@@ -1576,7 +1659,7 @@ var NavBar = ({ barTheme, ...props }) => {
1576
1659
  if (barTheme === void 0) return /* @__PURE__ */ jsx(NavBarInner, { ...props });
1577
1660
  return /* @__PURE__ */ jsx(UiProvider, { navigate, t, theme: barTheme, children: /* @__PURE__ */ jsx(NavBarInner, { ...props }) });
1578
1661
  };
1579
- var styles3 = StyleSheet.create({
1662
+ var styles4 = StyleSheet.create({
1580
1663
  hitBox: { minHeight: MIN_TOUCH_TARGET, minWidth: MIN_TOUCH_TARGET, justifyContent: "center" }
1581
1664
  });
1582
1665
  function anchorProps(route) {
@@ -1599,7 +1682,7 @@ var BrandHomeLink = ({ route, label, onNavigate, children }) => {
1599
1682
  {
1600
1683
  accessibilityLabel: label,
1601
1684
  accessibilityRole: "link",
1602
- style: [styles3.hitBox, focusRingStyle(focused, theme.palette.primary["500"])],
1685
+ style: [styles4.hitBox, focusRingStyle(focused, theme.palette.primary["500"])],
1603
1686
  testID: NAV_TEST_IDS.brandHome,
1604
1687
  onBlur: () => setFocused(false),
1605
1688
  onFocus: () => setFocused(true),
@@ -1612,7 +1695,7 @@ var BrandHomeLink = ({ route, label, onNavigate, children }) => {
1612
1695
  var DEFAULT_ICON_SIZE = 32;
1613
1696
  var NAME_FONT_SIZE = 16;
1614
1697
  var NAME_GAP = 8;
1615
- var styles4 = StyleSheet.create({
1698
+ var styles5 = StyleSheet.create({
1616
1699
  name: { flexShrink: 1, fontSize: NAME_FONT_SIZE, fontWeight: "700" },
1617
1700
  row: { alignItems: "center", flexDirection: "row", gap: NAME_GAP }
1618
1701
  });
@@ -1626,7 +1709,7 @@ var BrandMark = ({
1626
1709
  iconOnly = false
1627
1710
  }) => {
1628
1711
  const { theme } = useUi();
1629
- return /* @__PURE__ */ jsx(BrandHomeLink, { label, route, onNavigate, children: /* @__PURE__ */ jsxs(View, { style: styles4.row, children: [
1712
+ return /* @__PURE__ */ jsx(BrandHomeLink, { label, route, onNavigate, children: /* @__PURE__ */ jsxs(View, { style: styles5.row, children: [
1630
1713
  /* @__PURE__ */ jsx(
1631
1714
  Image,
1632
1715
  {
@@ -1640,7 +1723,7 @@ var BrandMark = ({
1640
1723
  Text,
1641
1724
  {
1642
1725
  numberOfLines: 1,
1643
- style: [styles4.name, { color: theme.colors.text }],
1726
+ style: [styles5.name, { color: theme.colors.text }],
1644
1727
  testID: NAV_TEST_IDS.brandMarkName,
1645
1728
  children: name
1646
1729
  }
@@ -1653,7 +1736,7 @@ var Nav = (props) => {
1653
1736
  }
1654
1737
  return /* @__PURE__ */ jsx(NavBar, { ...props });
1655
1738
  };
1656
- var styles5 = StyleSheet.create({
1739
+ var styles6 = StyleSheet.create({
1657
1740
  band: { left: 0, position: "absolute", right: 0, top: 0 }
1658
1741
  });
1659
1742
  function readBackdrop(theme) {
@@ -1676,7 +1759,7 @@ function resolveShellBackdrop(theme, viewport, contentPadding, testID) {
1676
1759
  View,
1677
1760
  {
1678
1761
  pointerEvents: "none",
1679
- style: [styles5.band, { backgroundColor: band.color, height: contentPadding + overlap }],
1762
+ style: [styles6.band, { backgroundColor: band.color, height: contentPadding + overlap }],
1680
1763
  testID: `${testID}${APP_SHELL_SUFFIX.band}`
1681
1764
  }
1682
1765
  );
@@ -1688,7 +1771,7 @@ var CARD_BORDER_WIDTH = 1;
1688
1771
  var CARD_TITLE_FONT_SIZE = 16;
1689
1772
  var CARD_MESSAGE_FONT_SIZE = 14;
1690
1773
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1691
- var styles6 = StyleSheet.create({
1774
+ var styles7 = StyleSheet.create({
1692
1775
  // Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
1693
1776
  // stably-mounted children, never a replacement for them. Replacing children here
1694
1777
  // would unmount whatever they host (e.g. a navigator), and on remount React
@@ -1720,9 +1803,9 @@ function MessageCard({
1720
1803
  }) {
1721
1804
  const { theme } = useUi();
1722
1805
  const colors = theme.colors;
1723
- return /* @__PURE__ */ jsxs(View, { style: [styles6.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1724
- /* @__PURE__ */ jsx(Text, { style: [styles6.cardTitle, { color: accentColor }], children: message.titleText }),
1725
- /* @__PURE__ */ jsx(Text, { style: [styles6.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1806
+ return /* @__PURE__ */ jsxs(View, { style: [styles7.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1807
+ /* @__PURE__ */ jsx(Text, { style: [styles7.cardTitle, { color: accentColor }], children: message.titleText }),
1808
+ /* @__PURE__ */ jsx(Text, { style: [styles7.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1726
1809
  ] });
1727
1810
  }
1728
1811
  function useContentBody(state, children, testID) {
@@ -1734,12 +1817,12 @@ function useContentBody(state, children, testID) {
1734
1817
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
1735
1818
  if (state.error)
1736
1819
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1737
- return /* @__PURE__ */ jsxs(View, { style: styles6.loadingHost, children: [
1820
+ return /* @__PURE__ */ jsxs(View, { style: styles7.loadingHost, children: [
1738
1821
  children,
1739
1822
  state.loading === true ? /* @__PURE__ */ jsx(
1740
1823
  View,
1741
1824
  {
1742
- style: [styles6.loadingOverlay, { backgroundColor: theme.colors.background }],
1825
+ style: [styles7.loadingOverlay, { backgroundColor: theme.colors.background }],
1743
1826
  testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
1744
1827
  children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1745
1828
  }
@@ -1766,7 +1849,7 @@ var DEFAULT_DRAWER_LABELS = {
1766
1849
  closeLabel: "Close menu",
1767
1850
  closeHint: "Close the navigation menu"
1768
1851
  };
1769
- var styles7 = StyleSheet.create({
1852
+ var styles8 = StyleSheet.create({
1770
1853
  overlay: { ...StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1771
1854
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1772
1855
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1801,10 +1884,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1801
1884
  accessibilityHint: hint,
1802
1885
  accessibilityLabel: label,
1803
1886
  ringColor: theme.palette.primary["500"],
1804
- style: styles7.menuToggle,
1887
+ style: styles8.menuToggle,
1805
1888
  testID,
1806
1889
  onPress: onOpen,
1807
- children: /* @__PURE__ */ jsx(Text, { style: [styles7.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1890
+ children: /* @__PURE__ */ jsx(Text, { style: [styles8.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1808
1891
  }
1809
1892
  );
1810
1893
  };
@@ -1830,14 +1913,14 @@ var MobileDrawer = ({
1830
1913
  node.addEventListener("click", handleClick);
1831
1914
  return () => node.removeEventListener("click", handleClick);
1832
1915
  }, [onClose]);
1833
- return /* @__PURE__ */ jsxs(View, { style: styles7.overlay, children: [
1916
+ return /* @__PURE__ */ jsxs(View, { style: styles8.overlay, children: [
1834
1917
  /* @__PURE__ */ jsx(
1835
1918
  Pressable,
1836
1919
  {
1837
1920
  accessibilityHint: labels.closeHint,
1838
1921
  accessibilityLabel: labels.closeLabel,
1839
1922
  accessibilityRole: "button",
1840
- style: styles7.scrim,
1923
+ style: styles8.scrim,
1841
1924
  testID: scrimTestID,
1842
1925
  onPress: onClose
1843
1926
  }
@@ -1848,9 +1931,9 @@ var MobileDrawer = ({
1848
1931
  ref: panelRef,
1849
1932
  "aria-modal": true,
1850
1933
  role: "dialog",
1851
- style: [styles7.panel, { backgroundColor: theme.colors.surface }],
1934
+ style: [styles8.panel, { backgroundColor: theme.colors.surface }],
1852
1935
  testID: drawerTestID,
1853
- children: renderTextSlot(sidebar, { color: theme.colors.text })
1936
+ children: /* @__PURE__ */ jsx(NavAccordionContext.Provider, { value: true, children: renderTextSlot(sidebar, { color: theme.colors.text }) })
1854
1937
  }
1855
1938
  )
1856
1939
  ] });
@@ -1866,7 +1949,7 @@ function useContentMaxWidth(width) {
1866
1949
  return resolveContentMaxWidth(width, viewport);
1867
1950
  }
1868
1951
  var DEFAULT_CONTENT_PADDING = 24;
1869
- var styles8 = StyleSheet.create({
1952
+ var styles9 = StyleSheet.create({
1870
1953
  root: { flex: 1 },
1871
1954
  // Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
1872
1955
  // `children` (and any navigator they host) are never unmounted while the gate
@@ -1928,13 +2011,13 @@ var AppShell = ({
1928
2011
  }, [isUnauthenticated, gate]);
1929
2012
  const gatePending = gate?.pending === true;
1930
2013
  if (isUnauthenticated) return null;
1931
- const columnStyle = maxWidth === "full" ? styles8.columnFull : [styles8.columnCapped, { maxWidth }];
1932
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles8.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
2014
+ const columnStyle = maxWidth === "full" ? styles9.columnFull : [styles9.columnCapped, { maxWidth }];
2015
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles9.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1933
2016
  const scroller = /* @__PURE__ */ jsxs(
1934
2017
  ScrollView,
1935
2018
  {
1936
- contentContainerStyle: [styles8.scrollContent, { padding: contentPadding }],
1937
- style: styles8.scroll,
2019
+ contentContainerStyle: [styles9.scrollContent, { padding: contentPadding }],
2020
+ style: styles9.scroll,
1938
2021
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1939
2022
  children: [
1940
2023
  look.band,
@@ -1942,7 +2025,7 @@ var AppShell = ({
1942
2025
  ]
1943
2026
  }
1944
2027
  );
1945
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: [styles8.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
2028
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: [styles9.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1946
2029
  /* @__PURE__ */ jsx(
1947
2030
  MenuToggle,
1948
2031
  {
@@ -1952,14 +2035,14 @@ var AppShell = ({
1952
2035
  onOpen: openDrawer
1953
2036
  }
1954
2037
  ),
1955
- /* @__PURE__ */ jsx(View, { style: styles8.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
2038
+ /* @__PURE__ */ jsx(View, { style: styles9.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
1956
2039
  ] }) : /* @__PURE__ */ jsx(View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: look.chromeText }) });
1957
2040
  const railRegion = railMode === "full" ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: renderTextSlot(sidebar, { color: theme.colors.text }) }) : railMode === "collapsed" ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`, children: renderTextSlot(collapsedSidebar, { color: theme.colors.text }) }) : null;
1958
- return /* @__PURE__ */ jsxs(View, { style: [styles8.root, look.root], testID, children: [
2041
+ return /* @__PURE__ */ jsxs(View, { style: [styles9.root, look.root], testID, children: [
1959
2042
  headerRegion,
1960
2043
  nav !== void 0 ? /* @__PURE__ */ jsx(View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsx(View, { style: navInnerStyle, children: renderTextSlot(nav, { color: look.chromeText }) }) : renderTextSlot(nav, { color: look.chromeText }) }) : null,
1961
2044
  banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1962
- railRegion !== null ? /* @__PURE__ */ jsxs(View, { style: styles8.bodyRow, children: [
2045
+ railRegion !== null ? /* @__PURE__ */ jsxs(View, { style: styles9.bodyRow, children: [
1963
2046
  railRegion,
1964
2047
  scroller
1965
2048
  ] }) : scroller,
@@ -1976,7 +2059,7 @@ var AppShell = ({
1976
2059
  gatePending ? /* @__PURE__ */ jsx(
1977
2060
  View,
1978
2061
  {
1979
- style: [styles8.pendingOverlay, { backgroundColor: theme.colors.background }],
2062
+ style: [styles9.pendingOverlay, { backgroundColor: theme.colors.background }],
1980
2063
  testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
1981
2064
  children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" })
1982
2065
  }
@@ -2345,7 +2428,7 @@ var KEY_DOWN = "ArrowDown";
2345
2428
  var KEY_UP = "ArrowUp";
2346
2429
  var KEY_ENTER = "Enter";
2347
2430
  var KEY_ESCAPE = "Escape";
2348
- var styles9 = StyleSheet.create({
2431
+ var styles10 = StyleSheet.create({
2349
2432
  overlay: { ...StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
2350
2433
  scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
2351
2434
  panel: {
@@ -2374,14 +2457,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
2374
2457
  accessibilityLabel: item.label,
2375
2458
  accessibilityRole: "button",
2376
2459
  accessibilityState: { selected },
2377
- style: [styles9.row, selected ? { backgroundColor: colors.border } : void 0],
2460
+ style: [styles10.row, selected ? { backgroundColor: colors.border } : void 0],
2378
2461
  testID: item.testID ?? item.key,
2379
2462
  onPress: () => onChoose(item),
2380
2463
  children: [
2381
2464
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
2382
- /* @__PURE__ */ jsxs(View, { style: styles9.rowText, children: [
2383
- /* @__PURE__ */ jsx(Text, { style: [styles9.rowLabel, { color: colors.text }], children: item.label }),
2384
- item.hint ? /* @__PURE__ */ jsx(Text, { style: [styles9.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2465
+ /* @__PURE__ */ jsxs(View, { style: styles10.rowText, children: [
2466
+ /* @__PURE__ */ jsx(Text, { style: [styles10.rowLabel, { color: colors.text }], children: item.label }),
2467
+ item.hint ? /* @__PURE__ */ jsx(Text, { style: [styles10.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2385
2468
  ] })
2386
2469
  ]
2387
2470
  }
@@ -2421,14 +2504,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2421
2504
  [results, selected, choose, onClose]
2422
2505
  );
2423
2506
  if (!open) return null;
2424
- return /* @__PURE__ */ jsxs(View, { style: styles9.overlay, children: [
2507
+ return /* @__PURE__ */ jsxs(View, { style: styles10.overlay, children: [
2425
2508
  /* @__PURE__ */ jsx(
2426
2509
  Pressable,
2427
2510
  {
2428
2511
  accessibilityHint: labels.closeHint,
2429
2512
  accessibilityLabel: labels.closeLabel,
2430
2513
  accessibilityRole: "button",
2431
- style: styles9.scrim,
2514
+ style: styles10.scrim,
2432
2515
  testID: `${testID ?? "command-palette"}-scrim`,
2433
2516
  onPress: onClose
2434
2517
  }
@@ -2439,7 +2522,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2439
2522
  "aria-modal": true,
2440
2523
  "aria-label": labels.regionLabel,
2441
2524
  role: "dialog",
2442
- style: [styles9.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2525
+ style: [styles10.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2443
2526
  testID: testID ?? "command-palette",
2444
2527
  children: [
2445
2528
  /* @__PURE__ */ jsx(
@@ -2449,14 +2532,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2449
2532
  accessibilityLabel: labels.placeholder,
2450
2533
  placeholder: labels.placeholder,
2451
2534
  placeholderTextColor: colors.textSecondary,
2452
- style: [styles9.input, { color: colors.text, borderBottomColor: colors.border }],
2535
+ style: [styles10.input, { color: colors.text, borderBottomColor: colors.border }],
2453
2536
  testID: `${testID ?? "command-palette"}-input`,
2454
2537
  value: query,
2455
2538
  onChangeText: setQuery,
2456
2539
  onKeyPress
2457
2540
  }
2458
2541
  ),
2459
- results.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles9.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", style: styles9.list, children: results.map((item, index) => /* @__PURE__ */ jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2542
+ results.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles10.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", style: styles10.list, children: results.map((item, index) => /* @__PURE__ */ jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2460
2543
  ]
2461
2544
  }
2462
2545
  )
@@ -2561,7 +2644,7 @@ var TAB_MIN_HEIGHT = 56;
2561
2644
  var LABEL_SIZE = 12;
2562
2645
  var LABEL_GAP = 2;
2563
2646
  var TAB_PADDING = 4;
2564
- var styles10 = StyleSheet.create({
2647
+ var styles11 = StyleSheet.create({
2565
2648
  tab: {
2566
2649
  flex: 1,
2567
2650
  minHeight: TAB_MIN_HEIGHT,
@@ -2598,31 +2681,31 @@ var BottomTab = ({
2598
2681
  accessibilityLabel: item.label,
2599
2682
  accessibilityRole: "link",
2600
2683
  accessibilityState: { selected: isActive },
2601
- style: [styles10.tab, { opacity: feedback.opacity }],
2684
+ style: [styles11.tab, { opacity: feedback.opacity }],
2602
2685
  testID: item.testID ?? item.key,
2603
2686
  onPress: () => onPress(item.route),
2604
2687
  onPressIn: () => setPressed(true),
2605
2688
  onPressOut: () => setPressed(false),
2606
2689
  ...ariaCurrentProps3(isActive),
2607
2690
  children: [
2608
- /* @__PURE__ */ jsxs(Animated.View, { style: [styles10.iconWrap, { transform: [{ scale }] }], children: [
2691
+ /* @__PURE__ */ jsxs(Animated.View, { style: [styles11.iconWrap, { transform: [{ scale }] }], children: [
2609
2692
  feedback.pillScale === null ? null : /* @__PURE__ */ jsx(
2610
2693
  View,
2611
2694
  {
2612
2695
  pointerEvents: "none",
2613
- style: [styles10.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2696
+ style: [styles11.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2614
2697
  }
2615
2698
  ),
2616
2699
  /* @__PURE__ */ jsx(View, { style: [{ transform: feedback.iconTransform }, motion], children: item.renderIcon?.(color, ICON_SIZE) })
2617
2700
  ] }),
2618
- /* @__PURE__ */ jsx(Text, { style: [styles10.label, { color }], children: item.label })
2701
+ /* @__PURE__ */ jsx(Text, { style: [styles11.label, { color }], children: item.label })
2619
2702
  ]
2620
2703
  }
2621
2704
  );
2622
2705
  };
2623
2706
  var DEFAULT_TEST_ID = "bottom-tab-bar";
2624
2707
  var SLIDER_HEIGHT = 3;
2625
- var styles11 = StyleSheet.create({
2708
+ var styles12 = StyleSheet.create({
2626
2709
  bar: { flexDirection: "row", borderTopWidth: StyleSheet.hairlineWidth },
2627
2710
  slider: { position: "absolute", top: 0, height: SLIDER_HEIGHT }
2628
2711
  });
@@ -2649,14 +2732,14 @@ var BottomTabBar = ({
2649
2732
  {
2650
2733
  "aria-label": regionLabel,
2651
2734
  role: "navigation",
2652
- style: [styles11.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2735
+ style: [styles12.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2653
2736
  testID,
2654
2737
  children: [
2655
2738
  bar === null ? null : /* @__PURE__ */ jsx(
2656
2739
  View,
2657
2740
  {
2658
2741
  pointerEvents: "none",
2659
- style: [styles11.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2742
+ style: [styles12.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2660
2743
  }
2661
2744
  ),
2662
2745
  items.map((item) => /* @__PURE__ */ jsx(