@dloizides/ui-nav 1.16.1 → 1.17.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,12 +1,93 @@
1
- import React3, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
2
- import { StyleSheet, Platform, Text, TouchableOpacity, View, TextInput, Pressable, useWindowDimensions, ActivityIndicator, ScrollView } from 'react-native';
1
+ import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
2
+ import { Platform, StyleSheet, Pressable, Text, View, TextInput, useWindowDimensions, ActivityIndicator, ScrollView } from 'react-native';
3
3
  import { useUi, UiProvider } from '@dloizides/ui-feedback';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
5
  import { Collapse } from '@dloizides/ui-motion';
5
- import { jsx, jsxs } from 'react/jsx-runtime';
6
6
  import { ModalDropdown } from '@dloizides/ui-layout';
7
7
  import { resolveAccessibleRoutes } from '@dloizides/auth-web';
8
8
 
9
9
  // src/Sidebar.tsx
10
+ var IS_WEB = Platform.OS === "web";
11
+ var FOCUS_RING_WIDTH = 2;
12
+ var FOCUS_RING_OFFSET = 2;
13
+ var HOVER_TRANSITION_MS = 150;
14
+ var TINT_TRANSITION_MS = 160;
15
+ function focusRingStyle(focused, ringColor) {
16
+ if (!IS_WEB || !focused) return void 0;
17
+ const ring = {
18
+ outlineWidth: FOCUS_RING_WIDTH,
19
+ outlineStyle: "solid",
20
+ outlineColor: ringColor,
21
+ outlineOffset: FOCUS_RING_OFFSET
22
+ };
23
+ return ring;
24
+ }
25
+ function webTransition(properties, durationMs, reducedMotion) {
26
+ if (!IS_WEB) return void 0;
27
+ const transition = {
28
+ transitionProperty: properties,
29
+ transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
30
+ };
31
+ return transition;
32
+ }
33
+ function hoverTransitionStyle(reducedMotion) {
34
+ return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
35
+ }
36
+ function tintTransitionStyle(reducedMotion) {
37
+ return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
38
+ }
39
+ function rotateTransitionStyle(reducedMotion) {
40
+ return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
41
+ }
42
+ var MIN_TARGET = 36;
43
+ var styles = StyleSheet.create({
44
+ trigger: {
45
+ flexDirection: "row",
46
+ alignItems: "center",
47
+ justifyContent: "space-between",
48
+ columnGap: 8,
49
+ paddingHorizontal: 12,
50
+ minHeight: MIN_TARGET,
51
+ borderWidth: 1,
52
+ borderRadius: 8
53
+ },
54
+ label: { fontSize: 14 },
55
+ badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
56
+ badgeText: { fontSize: 11, fontWeight: "700" }
57
+ });
58
+ var CommandPaletteTrigger = ({
59
+ label,
60
+ hint,
61
+ shortcut,
62
+ onPress,
63
+ testID
64
+ }) => {
65
+ const { theme } = useUi();
66
+ const colors = theme.colors;
67
+ const primary = theme.palette.primary["500"];
68
+ const [focused, setFocused] = React.useState(false);
69
+ return /* @__PURE__ */ jsxs(
70
+ Pressable,
71
+ {
72
+ accessibilityHint: hint,
73
+ accessibilityLabel: label,
74
+ accessibilityRole: "button",
75
+ style: [
76
+ styles.trigger,
77
+ { backgroundColor: colors.surface, borderColor: colors.border },
78
+ focusRingStyle(focused, primary)
79
+ ],
80
+ testID,
81
+ onBlur: () => setFocused(false),
82
+ onFocus: () => setFocused(true),
83
+ onPress,
84
+ children: [
85
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: colors.textSecondary }], children: label }),
86
+ /* @__PURE__ */ jsx(View, { style: [styles.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsx(Text, { style: [styles.badgeText, { color: colors.textSecondary }], children: shortcut }) })
87
+ ]
88
+ }
89
+ );
90
+ };
10
91
 
11
92
  // src/sidebarFilter.ts
12
93
  function searchTokens(query) {
@@ -50,6 +131,10 @@ var ACTIVE_ACCENT_WIDTH = 3;
50
131
  var NAV_ROW_RADIUS = 8;
51
132
  var ACTIVE_TINT_OPACITY = 0.14;
52
133
  var HOVER_TINT_OPACITY = 0.06;
134
+ function rowTintOpacity(isActive, hovered) {
135
+ if (isActive) return ACTIVE_TINT_OPACITY;
136
+ return hovered ? HOVER_TINT_OPACITY : 0;
137
+ }
53
138
  var NAV_LINK_GAP = 4;
54
139
  var navStyles = StyleSheet.create({
55
140
  // --- Sidebar ---
@@ -75,6 +160,12 @@ var navStyles = StyleSheet.create({
75
160
  sidebarSpacer: {
76
161
  flex: 1
77
162
  },
163
+ // Bottom rhythm under the ⌘K palette trigger (the narrow-viewport search
164
+ // affordance), matching the inline field's own `marginBottom` so swapping
165
+ // affordances by width keeps the same gap above the nav list.
166
+ sidebarTriggerSlot: {
167
+ marginBottom: 12
168
+ },
78
169
  // Inline-search no-match text — sits where the nav list would, muted + padded.
79
170
  sidebarEmpty: {
80
171
  fontSize: 13,
@@ -337,38 +428,6 @@ var expandableStyles = StyleSheet.create({
337
428
  var BASE_INDENT = 12;
338
429
  var NAV_ICON_SIZE = 14;
339
430
  var CHEVRON_ICON_SIZE = 12;
340
- var IS_WEB = Platform.OS === "web";
341
- var FOCUS_RING_WIDTH = 2;
342
- var FOCUS_RING_OFFSET = 2;
343
- var HOVER_TRANSITION_MS = 150;
344
- var TINT_TRANSITION_MS = 160;
345
- function focusRingStyle(focused, ringColor) {
346
- if (!IS_WEB || !focused) return void 0;
347
- const ring = {
348
- outlineWidth: FOCUS_RING_WIDTH,
349
- outlineStyle: "solid",
350
- outlineColor: ringColor,
351
- outlineOffset: FOCUS_RING_OFFSET
352
- };
353
- return ring;
354
- }
355
- function webTransition(properties, durationMs, reducedMotion) {
356
- if (!IS_WEB) return void 0;
357
- const transition = {
358
- transitionProperty: properties,
359
- transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
360
- };
361
- return transition;
362
- }
363
- function hoverTransitionStyle(reducedMotion) {
364
- return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
365
- }
366
- function tintTransitionStyle(reducedMotion) {
367
- return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
368
- }
369
- function rotateTransitionStyle(reducedMotion) {
370
- return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
371
- }
372
431
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
373
432
  function getReducedMotionQuery() {
374
433
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
@@ -422,9 +481,19 @@ function hasActiveDescendant(item, pathname) {
422
481
  if (isRouteActive(pathname, item.route)) return true;
423
482
  return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
424
483
  }
425
- function TintOverlay({ opacity, color }) {
484
+ function TintOverlay({
485
+ opacity,
486
+ color,
487
+ testID
488
+ }) {
426
489
  const reducedMotion = useReducedMotion();
427
- return /* @__PURE__ */ jsx(View, { style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)] });
490
+ return /* @__PURE__ */ jsx(
491
+ View,
492
+ {
493
+ style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)],
494
+ testID
495
+ }
496
+ );
428
497
  }
429
498
  var NavExpandableItem = ({
430
499
  item,
@@ -455,11 +524,12 @@ var NavExpandableItem = ({
455
524
  );
456
525
  const paddingStyle = useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
457
526
  const headerPaddingStyle = useMemo(() => ({ paddingLeft: indent }), [indent]);
458
- const leafTintOpacity = isActive ? ACTIVE_TINT_OPACITY : hovered ? HOVER_TINT_OPACITY : 0;
459
- const headerTintOpacity = hovered ? HOVER_TINT_OPACITY : 0;
527
+ const leafTintOpacity = rowTintOpacity(isActive, hovered);
528
+ const headerTintOpacity = rowTintOpacity(false, hovered);
529
+ const rowTestID = item.testID ?? item.key;
460
530
  if (!hasChildren)
461
531
  return /* @__PURE__ */ jsxs(
462
- TouchableOpacity,
532
+ Pressable,
463
533
  {
464
534
  accessibilityHint: navigateHint(item.label),
465
535
  accessibilityLabel: item.label,
@@ -471,14 +541,14 @@ var NavExpandableItem = ({
471
541
  isActive ? activeAccentStyle : void 0,
472
542
  focusRingStyle(focused, primaryColor)
473
543
  ],
474
- testID: item.testID ?? item.key,
544
+ testID: rowTestID,
475
545
  onBlur: () => setFocused(false),
476
546
  onFocus: () => setFocused(true),
477
547
  onPress: () => onNavigate(item.route),
478
548
  ...hoverHandlerProps(onHoverIn, onHoverOut),
479
549
  ...ariaCurrentProps(isActive),
480
550
  children: [
481
- /* @__PURE__ */ jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity }),
551
+ /* @__PURE__ */ jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity, testID: `${rowTestID}-tint` }),
482
552
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsx(View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
483
553
  /* @__PURE__ */ jsx(
484
554
  Text,
@@ -496,7 +566,7 @@ var NavExpandableItem = ({
496
566
  const chevronColor = colors.textSecondary;
497
567
  return /* @__PURE__ */ jsxs(View, { children: [
498
568
  /* @__PURE__ */ jsxs(
499
- TouchableOpacity,
569
+ Pressable,
500
570
  {
501
571
  accessibilityHint: expanded ? collapseHint : expandHint,
502
572
  accessibilityLabel: item.label,
@@ -508,14 +578,14 @@ var NavExpandableItem = ({
508
578
  headerPaddingStyle,
509
579
  focusRingStyle(focused, primaryColor)
510
580
  ],
511
- testID: item.testID ?? item.key,
581
+ testID: rowTestID,
512
582
  onBlur: () => setFocused(false),
513
583
  onFocus: () => setFocused(true),
514
584
  onPress: toggle,
515
585
  ...hoverHandlerProps(onHoverIn, onHoverOut),
516
586
  ...ariaExpandedProps(expanded),
517
587
  children: [
518
- /* @__PURE__ */ jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity }),
588
+ /* @__PURE__ */ jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity, testID: `${rowTestID}-tint` }),
519
589
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsx(View, { style: expandableStyles.iconWrapper, children: item.renderIcon(isSectionHeader ? colors.textSecondary : colors.text, NAV_ICON_SIZE) }) : null,
520
590
  /* @__PURE__ */ jsx(
521
591
  Text,
@@ -547,10 +617,16 @@ var NavExpandableItem = ({
547
617
  )) }) })
548
618
  ] });
549
619
  };
620
+
621
+ // src/searchAffordance.ts
622
+ var DEFAULT_SEARCH_BREAKPOINT = 768;
623
+ function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
624
+ return viewport >= breakpoint ? "inline" : "palette";
625
+ }
550
626
  var FIELD_MIN_HEIGHT = 38;
551
627
  var CLEAR_GLYPH = "\u2715";
552
628
  var CLEAR_MIN_TARGET = 28;
553
- var styles = StyleSheet.create({
629
+ var styles2 = StyleSheet.create({
554
630
  wrap: {
555
631
  flexDirection: "row",
556
632
  alignItems: "center",
@@ -591,7 +667,7 @@ var SidebarSearch = ({
591
667
  View,
592
668
  {
593
669
  style: [
594
- styles.wrap,
670
+ styles2.wrap,
595
671
  { backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
596
672
  focusRingStyle(focused, primary)
597
673
  ],
@@ -603,7 +679,7 @@ var SidebarSearch = ({
603
679
  accessibilityLabel: labels.placeholder,
604
680
  placeholder: labels.placeholder,
605
681
  placeholderTextColor: colors.textSecondary,
606
- style: [styles.input, { color: colors.text }],
682
+ style: [styles2.input, { color: colors.text }],
607
683
  testID,
608
684
  value,
609
685
  onBlur: () => setFocused(false),
@@ -617,10 +693,10 @@ var SidebarSearch = ({
617
693
  accessibilityHint: labels.clearHint,
618
694
  accessibilityLabel: labels.clearLabel,
619
695
  accessibilityRole: "button",
620
- style: styles.clear,
696
+ style: styles2.clear,
621
697
  testID: `${testID}-clear`,
622
698
  onPress: onClear,
623
- children: /* @__PURE__ */ jsx(Text, { style: [styles.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
699
+ children: /* @__PURE__ */ jsx(Text, { style: [styles2.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
624
700
  }
625
701
  ) : null
626
702
  ]
@@ -633,11 +709,12 @@ function isBareText(node) {
633
709
  function renderTextSlot(node, style) {
634
710
  if (node === void 0 || node === null) return node;
635
711
  if (isBareText(node)) return /* @__PURE__ */ jsx(Text, { style, children: node });
636
- return React3.Children.map(
712
+ return React.Children.map(
637
713
  node,
638
714
  (child) => isBareText(child) ? /* @__PURE__ */ jsx(Text, { style, children: child }) : child
639
715
  );
640
716
  }
717
+ var DEFAULT_TRIGGER_TEST_ID = "sidebar-command-palette-trigger";
641
718
  var Sidebar = ({
642
719
  items,
643
720
  pathname,
@@ -650,6 +727,7 @@ var Sidebar = ({
650
727
  renderChevron,
651
728
  enableInlineSearch = false,
652
729
  search,
730
+ paletteTrigger,
653
731
  searchQuery,
654
732
  onSearchChange,
655
733
  header,
@@ -658,6 +736,7 @@ var Sidebar = ({
658
736
  }) => {
659
737
  const { theme } = useUi();
660
738
  const colors = theme.colors;
739
+ const { width } = useWindowDimensions();
661
740
  const [internalQuery, setInternalQuery] = useState("");
662
741
  const controlled = searchQuery !== void 0;
663
742
  const query = controlled ? searchQuery : internalQuery;
@@ -669,9 +748,12 @@ var Sidebar = ({
669
748
  [controlled, onSearchChange]
670
749
  );
671
750
  const clearQuery = useCallback(() => setQuery(""), [setQuery]);
672
- const searchOn = enableInlineSearch && search !== void 0;
673
- const visibleItems = searchOn ? filterNavItems(items, query) : items;
674
- const showEmpty = searchOn && isFilterActive(query) && visibleItems.length === 0;
751
+ const affordance = resolveSearchAffordance(width);
752
+ const isInlineAffordance = affordance === "inline";
753
+ const showInlineSearch = enableInlineSearch && search !== void 0 && isInlineAffordance;
754
+ const showPaletteTrigger = paletteTrigger !== void 0 && !isInlineAffordance;
755
+ const visibleItems = showInlineSearch ? filterNavItems(items, query) : items;
756
+ const showEmpty = showInlineSearch && isFilterActive(query) && visibleItems.length === 0;
675
757
  return /* @__PURE__ */ jsxs(
676
758
  View,
677
759
  {
@@ -685,7 +767,7 @@ var Sidebar = ({
685
767
  ],
686
768
  children: [
687
769
  /* @__PURE__ */ jsx(Text, { accessibilityRole: "header", style: [navStyles.sidebarTitle, { color: colors.text }], children: title }),
688
- searchOn && search !== void 0 ? /* @__PURE__ */ jsx(
770
+ showInlineSearch && search !== void 0 ? /* @__PURE__ */ jsx(
689
771
  SidebarSearch,
690
772
  {
691
773
  labels: search,
@@ -695,6 +777,16 @@ var Sidebar = ({
695
777
  onClear: clearQuery
696
778
  }
697
779
  ) : null,
780
+ showPaletteTrigger && paletteTrigger !== void 0 ? /* @__PURE__ */ jsx(View, { style: navStyles.sidebarTriggerSlot, children: /* @__PURE__ */ jsx(
781
+ CommandPaletteTrigger,
782
+ {
783
+ hint: paletteTrigger.hint,
784
+ label: paletteTrigger.label,
785
+ shortcut: paletteTrigger.shortcut,
786
+ testID: paletteTrigger.testID ?? DEFAULT_TRIGGER_TEST_ID,
787
+ onPress: paletteTrigger.onOpen
788
+ }
789
+ ) }) : null,
698
790
  renderTextSlot(header, { color: colors.text }),
699
791
  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(
700
792
  NavExpandableItem,
@@ -716,12 +808,6 @@ var Sidebar = ({
716
808
  );
717
809
  };
718
810
 
719
- // src/searchAffordance.ts
720
- var DEFAULT_SEARCH_BREAKPOINT = 768;
721
- function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
722
- return viewport >= breakpoint ? "inline" : "palette";
723
- }
724
-
725
811
  // src/constants.ts
726
812
  var NAV_TEST_IDS = {
727
813
  /** displayName line in the rich account header (tappable when `onAccount` set). */
@@ -1157,7 +1243,7 @@ var NavBarInner = ({
1157
1243
  }
1158
1244
  );
1159
1245
  if (collapsed) {
1160
- return /* @__PURE__ */ jsx(View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsx(React3.Fragment, { children: renderLink(item) }, item.key)) });
1246
+ return /* @__PURE__ */ jsx(View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsx(React.Fragment, { children: renderLink(item) }, item.key)) });
1161
1247
  }
1162
1248
  const visibleItems = items.slice(0, visibleCount);
1163
1249
  const overflowItems = items.slice(visibleCount);
@@ -1223,7 +1309,7 @@ var CARD_BORDER_WIDTH = 1;
1223
1309
  var CARD_TITLE_FONT_SIZE = 16;
1224
1310
  var CARD_MESSAGE_FONT_SIZE = 14;
1225
1311
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1226
- var styles2 = StyleSheet.create({
1312
+ var styles3 = StyleSheet.create({
1227
1313
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1228
1314
  card: {
1229
1315
  padding: CARD_PADDING,
@@ -1240,9 +1326,9 @@ function MessageCard({
1240
1326
  }) {
1241
1327
  const { theme } = useUi();
1242
1328
  const colors = theme.colors;
1243
- return /* @__PURE__ */ jsxs(View, { style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1244
- /* @__PURE__ */ jsx(Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
1245
- /* @__PURE__ */ jsx(Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1329
+ return /* @__PURE__ */ jsxs(View, { style: [styles3.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1330
+ /* @__PURE__ */ jsx(Text, { style: [styles3.cardTitle, { color: accentColor }], children: message.titleText }),
1331
+ /* @__PURE__ */ jsx(Text, { style: [styles3.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1246
1332
  ] });
1247
1333
  }
1248
1334
  function useContentBody(state, children, testID) {
@@ -1254,7 +1340,7 @@ function useContentBody(state, children, testID) {
1254
1340
  if (state?.error)
1255
1341
  return /* @__PURE__ */ jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1256
1342
  if (state?.loading === true)
1257
- return /* @__PURE__ */ jsx(View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1343
+ return /* @__PURE__ */ jsx(View, { style: styles3.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1258
1344
  return children;
1259
1345
  }
1260
1346
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
@@ -1278,7 +1364,7 @@ var DEFAULT_DRAWER_LABELS = {
1278
1364
  closeLabel: "Close menu",
1279
1365
  closeHint: "Close the navigation menu"
1280
1366
  };
1281
- var styles3 = StyleSheet.create({
1367
+ var styles4 = StyleSheet.create({
1282
1368
  overlay: { ...StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1283
1369
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1284
1370
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1313,10 +1399,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1313
1399
  accessibilityHint: hint,
1314
1400
  accessibilityLabel: label,
1315
1401
  ringColor: theme.palette.primary["500"],
1316
- style: styles3.menuToggle,
1402
+ style: styles4.menuToggle,
1317
1403
  testID,
1318
1404
  onPress: onOpen,
1319
- children: /* @__PURE__ */ jsx(Text, { style: [styles3.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1405
+ children: /* @__PURE__ */ jsx(Text, { style: [styles4.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1320
1406
  }
1321
1407
  );
1322
1408
  };
@@ -1342,14 +1428,14 @@ var MobileDrawer = ({
1342
1428
  node.addEventListener("click", handleClick);
1343
1429
  return () => node.removeEventListener("click", handleClick);
1344
1430
  }, [onClose]);
1345
- return /* @__PURE__ */ jsxs(View, { style: styles3.overlay, children: [
1431
+ return /* @__PURE__ */ jsxs(View, { style: styles4.overlay, children: [
1346
1432
  /* @__PURE__ */ jsx(
1347
1433
  Pressable,
1348
1434
  {
1349
1435
  accessibilityHint: labels.closeHint,
1350
1436
  accessibilityLabel: labels.closeLabel,
1351
1437
  accessibilityRole: "button",
1352
- style: styles3.scrim,
1438
+ style: styles4.scrim,
1353
1439
  testID: scrimTestID,
1354
1440
  onPress: onClose
1355
1441
  }
@@ -1360,7 +1446,7 @@ var MobileDrawer = ({
1360
1446
  ref: panelRef,
1361
1447
  "aria-modal": true,
1362
1448
  role: "dialog",
1363
- style: [styles3.panel, { backgroundColor: theme.colors.surface }],
1449
+ style: [styles4.panel, { backgroundColor: theme.colors.surface }],
1364
1450
  testID: drawerTestID,
1365
1451
  children: renderTextSlot(sidebar, { color: theme.colors.text })
1366
1452
  }
@@ -1391,7 +1477,7 @@ function useContentMaxWidth(width) {
1391
1477
  return resolveContentMaxWidth(width, viewport);
1392
1478
  }
1393
1479
  var DEFAULT_CONTENT_PADDING = 24;
1394
- var styles4 = StyleSheet.create({
1480
+ var styles5 = StyleSheet.create({
1395
1481
  root: { flex: 1 },
1396
1482
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1397
1483
  scroll: { flex: 1 },
@@ -1448,20 +1534,20 @@ var AppShell = ({
1448
1534
  if (isUnauthenticated && gate !== void 0) gate.onRedirect();
1449
1535
  }, [isUnauthenticated, gate]);
1450
1536
  if (gate?.pending === true)
1451
- return /* @__PURE__ */ jsx(View, { style: [styles4.root, styles4.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1537
+ return /* @__PURE__ */ jsx(View, { style: [styles5.root, styles5.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsx(ActivityIndicator, { color: primary, size: "large" }) });
1452
1538
  if (isUnauthenticated) return null;
1453
- const columnStyle = maxWidth === "full" ? styles4.columnFull : [styles4.columnCapped, { maxWidth }];
1454
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles4.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1539
+ const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
1540
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1455
1541
  const scroller = /* @__PURE__ */ jsx(
1456
1542
  ScrollView,
1457
1543
  {
1458
- contentContainerStyle: [styles4.scrollContent, { padding: contentPadding }],
1459
- style: styles4.scroll,
1544
+ contentContainerStyle: [styles5.scrollContent, { padding: contentPadding }],
1545
+ style: styles5.scroll,
1460
1546
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1461
1547
  children: /* @__PURE__ */ jsx(View, { style: columnStyle, children: body })
1462
1548
  }
1463
1549
  );
1464
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: styles4.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1550
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxs(View, { style: styles5.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1465
1551
  /* @__PURE__ */ jsx(
1466
1552
  MenuToggle,
1467
1553
  {
@@ -1471,14 +1557,14 @@ var AppShell = ({
1471
1557
  onOpen: openDrawer
1472
1558
  }
1473
1559
  ),
1474
- /* @__PURE__ */ jsx(View, { style: styles4.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1560
+ /* @__PURE__ */ jsx(View, { style: styles5.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1475
1561
  ] }) : /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
1476
1562
  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;
1477
- return /* @__PURE__ */ jsxs(View, { style: [styles4.root, { backgroundColor: theme.colors.background }], testID, children: [
1563
+ return /* @__PURE__ */ jsxs(View, { style: [styles5.root, { backgroundColor: theme.colors.background }], testID, children: [
1478
1564
  headerRegion,
1479
1565
  nav !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsx(View, { style: navInnerStyle, children: renderTextSlot(nav, { color: theme.colors.text }) }) : renderTextSlot(nav, { color: theme.colors.text }) }) : null,
1480
1566
  banner !== void 0 ? /* @__PURE__ */ jsx(View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1481
- railRegion !== null ? /* @__PURE__ */ jsxs(View, { style: styles4.bodyRow, children: [
1567
+ railRegion !== null ? /* @__PURE__ */ jsxs(View, { style: styles5.bodyRow, children: [
1482
1568
  railRegion,
1483
1569
  scroller
1484
1570
  ] }) : scroller,
@@ -1601,6 +1687,7 @@ var NavShell = ({
1601
1687
  header: sideRail.header,
1602
1688
  items: sideRail.items,
1603
1689
  navigateHint,
1690
+ paletteTrigger: sideRail.paletteTrigger,
1604
1691
  pathname,
1605
1692
  regionLabel,
1606
1693
  renderChevron: sideRail.renderChevron,
@@ -1776,7 +1863,7 @@ var KEY_DOWN = "ArrowDown";
1776
1863
  var KEY_UP = "ArrowUp";
1777
1864
  var KEY_ENTER = "Enter";
1778
1865
  var KEY_ESCAPE = "Escape";
1779
- var styles5 = StyleSheet.create({
1866
+ var styles6 = StyleSheet.create({
1780
1867
  overlay: { ...StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
1781
1868
  scrim: { ...StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
1782
1869
  panel: {
@@ -1805,14 +1892,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
1805
1892
  accessibilityLabel: item.label,
1806
1893
  accessibilityRole: "button",
1807
1894
  accessibilityState: { selected },
1808
- style: [styles5.row, selected ? { backgroundColor: colors.border } : void 0],
1895
+ style: [styles6.row, selected ? { backgroundColor: colors.border } : void 0],
1809
1896
  testID: item.testID ?? item.key,
1810
1897
  onPress: () => onChoose(item),
1811
1898
  children: [
1812
1899
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
1813
- /* @__PURE__ */ jsxs(View, { style: styles5.rowText, children: [
1814
- /* @__PURE__ */ jsx(Text, { style: [styles5.rowLabel, { color: colors.text }], children: item.label }),
1815
- item.hint ? /* @__PURE__ */ jsx(Text, { style: [styles5.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1900
+ /* @__PURE__ */ jsxs(View, { style: styles6.rowText, children: [
1901
+ /* @__PURE__ */ jsx(Text, { style: [styles6.rowLabel, { color: colors.text }], children: item.label }),
1902
+ item.hint ? /* @__PURE__ */ jsx(Text, { style: [styles6.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1816
1903
  ] })
1817
1904
  ]
1818
1905
  }
@@ -1852,14 +1939,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1852
1939
  [results, selected, choose, onClose]
1853
1940
  );
1854
1941
  if (!open) return null;
1855
- return /* @__PURE__ */ jsxs(View, { style: styles5.overlay, children: [
1942
+ return /* @__PURE__ */ jsxs(View, { style: styles6.overlay, children: [
1856
1943
  /* @__PURE__ */ jsx(
1857
1944
  Pressable,
1858
1945
  {
1859
1946
  accessibilityHint: labels.closeHint,
1860
1947
  accessibilityLabel: labels.closeLabel,
1861
1948
  accessibilityRole: "button",
1862
- style: styles5.scrim,
1949
+ style: styles6.scrim,
1863
1950
  testID: `${testID ?? "command-palette"}-scrim`,
1864
1951
  onPress: onClose
1865
1952
  }
@@ -1870,7 +1957,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1870
1957
  "aria-modal": true,
1871
1958
  "aria-label": labels.regionLabel,
1872
1959
  role: "dialog",
1873
- style: [styles5.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1960
+ style: [styles6.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1874
1961
  testID: testID ?? "command-palette",
1875
1962
  children: [
1876
1963
  /* @__PURE__ */ jsx(
@@ -1880,68 +1967,19 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1880
1967
  accessibilityLabel: labels.placeholder,
1881
1968
  placeholder: labels.placeholder,
1882
1969
  placeholderTextColor: colors.textSecondary,
1883
- style: [styles5.input, { color: colors.text, borderBottomColor: colors.border }],
1970
+ style: [styles6.input, { color: colors.text, borderBottomColor: colors.border }],
1884
1971
  testID: `${testID ?? "command-palette"}-input`,
1885
1972
  value: query,
1886
1973
  onChangeText: setQuery,
1887
1974
  onKeyPress
1888
1975
  }
1889
1976
  ),
1890
- results.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles5.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", style: styles5.list, children: results.map((item, index) => /* @__PURE__ */ jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1977
+ results.length === 0 ? /* @__PURE__ */ jsx(Text, { style: [styles6.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", style: styles6.list, children: results.map((item, index) => /* @__PURE__ */ jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1891
1978
  ]
1892
1979
  }
1893
1980
  )
1894
1981
  ] });
1895
1982
  };
1896
- var MIN_TARGET = 36;
1897
- var styles6 = StyleSheet.create({
1898
- trigger: {
1899
- flexDirection: "row",
1900
- alignItems: "center",
1901
- justifyContent: "space-between",
1902
- columnGap: 8,
1903
- paddingHorizontal: 12,
1904
- minHeight: MIN_TARGET,
1905
- borderWidth: 1,
1906
- borderRadius: 8
1907
- },
1908
- label: { fontSize: 14 },
1909
- badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
1910
- badgeText: { fontSize: 11, fontWeight: "700" }
1911
- });
1912
- var CommandPaletteTrigger = ({
1913
- label,
1914
- hint,
1915
- shortcut,
1916
- onPress,
1917
- testID
1918
- }) => {
1919
- const { theme } = useUi();
1920
- const colors = theme.colors;
1921
- const primary = theme.palette.primary["500"];
1922
- const [focused, setFocused] = React3.useState(false);
1923
- return /* @__PURE__ */ jsxs(
1924
- Pressable,
1925
- {
1926
- accessibilityHint: hint,
1927
- accessibilityLabel: label,
1928
- accessibilityRole: "button",
1929
- style: [
1930
- styles6.trigger,
1931
- { backgroundColor: colors.surface, borderColor: colors.border },
1932
- focusRingStyle(focused, primary)
1933
- ],
1934
- testID,
1935
- onBlur: () => setFocused(false),
1936
- onFocus: () => setFocused(true),
1937
- onPress,
1938
- children: [
1939
- /* @__PURE__ */ jsx(Text, { style: [styles6.label, { color: colors.textSecondary }], children: label }),
1940
- /* @__PURE__ */ jsx(View, { style: [styles6.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsx(Text, { style: [styles6.badgeText, { color: colors.textSecondary }], children: shortcut }) })
1941
- ]
1942
- }
1943
- );
1944
- };
1945
1983
  var LAUNCH_KEY = "k";
1946
1984
  function useCommandPaletteHotkey(onOpen, enabled = true) {
1947
1985
  useEffect(() => {