@dloizides/ui-nav 1.15.0 → 1.16.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.js CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var React3 = require('react');
3
4
  var reactNative = require('react-native');
4
5
  var uiFeedback = require('@dloizides/ui-feedback');
5
- var React2 = require('react');
6
6
  var uiMotion = require('@dloizides/ui-motion');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var uiLayout = require('@dloizides/ui-layout');
@@ -10,10 +10,42 @@ var authWeb = require('@dloizides/auth-web');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
13
- var React2__default = /*#__PURE__*/_interopDefault(React2);
13
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
14
14
 
15
15
  // src/Sidebar.tsx
16
16
 
17
+ // src/sidebarFilter.ts
18
+ function searchTokens(query) {
19
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
20
+ }
21
+ function labelMatches(item, tokens) {
22
+ const label = item.label.toLowerCase();
23
+ return tokens.every((token) => label.includes(token));
24
+ }
25
+ function filterNavItems(items, query) {
26
+ const tokens = searchTokens(query);
27
+ if (tokens.length === 0) return [...items];
28
+ return filterByTokens(items, tokens);
29
+ }
30
+ function filterByTokens(items, tokens) {
31
+ const kept = [];
32
+ for (const item of items) {
33
+ const selfMatch = labelMatches(item, tokens);
34
+ const children = item.children ?? [];
35
+ if (selfMatch) {
36
+ kept.push(item);
37
+ continue;
38
+ }
39
+ if (children.length === 0) continue;
40
+ const prunedChildren = filterByTokens(children, tokens);
41
+ if (prunedChildren.length > 0) kept.push({ ...item, children: prunedChildren });
42
+ }
43
+ return kept;
44
+ }
45
+ function isFilterActive(query) {
46
+ return searchTokens(query).length > 0;
47
+ }
48
+
17
49
  // src/isRouteActive.ts
18
50
  function isRouteActive(pathname, route) {
19
51
  if (route === "/") return pathname === "/";
@@ -21,6 +53,9 @@ function isRouteActive(pathname, route) {
21
53
  }
22
54
  var ACTIVE_BORDER_RADIUS = 4;
23
55
  var ACTIVE_ACCENT_WIDTH = 3;
56
+ var NAV_ROW_RADIUS = 8;
57
+ var ACTIVE_TINT_OPACITY = 0.14;
58
+ var HOVER_TINT_OPACITY = 0.06;
24
59
  var NAV_LINK_GAP = 4;
25
60
  var navStyles = reactNative.StyleSheet.create({
26
61
  // --- Sidebar ---
@@ -46,6 +81,12 @@ var navStyles = reactNative.StyleSheet.create({
46
81
  sidebarSpacer: {
47
82
  flex: 1
48
83
  },
84
+ // Inline-search no-match text — sits where the nav list would, muted + padded.
85
+ sidebarEmpty: {
86
+ fontSize: 13,
87
+ paddingVertical: 10,
88
+ paddingHorizontal: 12
89
+ },
49
90
  // --- Topbar ---
50
91
  topbarContainer: {
51
92
  height: 64,
@@ -246,8 +287,12 @@ var collapsedRailStyles = reactNative.StyleSheet.create({
246
287
  var expandableStyles = reactNative.StyleSheet.create({
247
288
  // Every leaf reserves the accent-bar gutter (a TRANSPARENT left border of the
248
289
  // same width the active item colours in) so activation never shifts the row.
290
+ // `overflow: hidden` clips the rounded {@link tintOverlay} to the row's corners;
291
+ // `position: relative` anchors that absolutely-filled overlay to the row.
249
292
  childItem: {
250
- borderRadius: 6,
293
+ position: "relative",
294
+ overflow: "hidden",
295
+ borderRadius: NAV_ROW_RADIUS,
251
296
  flexDirection: "row",
252
297
  alignItems: "center",
253
298
  paddingVertical: 9,
@@ -258,8 +303,24 @@ var expandableStyles = reactNative.StyleSheet.create({
258
303
  childItemTextWithIcon: { fontSize: 14, marginLeft: 6 },
259
304
  chevron: { marginLeft: "auto" },
260
305
  childrenContainer: { overflow: "hidden", marginBottom: 4 },
306
+ // The hover/active brand wash: a rounded fill behind the row's icon + label,
307
+ // its opacity eased between 0 / hover / active. Colour + opacity are applied at
308
+ // render time (theme primary); this only positions and rounds it.
309
+ tintOverlay: {
310
+ position: "absolute",
311
+ top: 0,
312
+ left: 0,
313
+ right: 0,
314
+ bottom: 0,
315
+ borderRadius: NAV_ROW_RADIUS,
316
+ // `style.pointerEvents` (not the deprecated prop) so the overlay never
317
+ // intercepts the row's press/hover — RNW forwards this style key to the DOM.
318
+ pointerEvents: "none"
319
+ },
261
320
  header: {
262
- borderRadius: 6,
321
+ position: "relative",
322
+ overflow: "hidden",
323
+ borderRadius: NAV_ROW_RADIUS,
263
324
  flexDirection: "row",
264
325
  alignItems: "center",
265
326
  paddingVertical: 8
@@ -286,6 +347,7 @@ var IS_WEB = reactNative.Platform.OS === "web";
286
347
  var FOCUS_RING_WIDTH = 2;
287
348
  var FOCUS_RING_OFFSET = 2;
288
349
  var HOVER_TRANSITION_MS = 150;
350
+ var TINT_TRANSITION_MS = 160;
289
351
  function focusRingStyle(focused, ringColor) {
290
352
  if (!IS_WEB || !focused) return void 0;
291
353
  const ring = {
@@ -296,24 +358,80 @@ function focusRingStyle(focused, ringColor) {
296
358
  };
297
359
  return ring;
298
360
  }
299
- function hoverTransitionStyle(reducedMotion) {
361
+ function webTransition(properties, durationMs, reducedMotion) {
300
362
  if (!IS_WEB) return void 0;
301
363
  const transition = {
302
- transitionProperty: "color, background-color",
303
- transitionDuration: reducedMotion ? "0ms" : `${HOVER_TRANSITION_MS}ms`
364
+ transitionProperty: properties,
365
+ transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
304
366
  };
305
367
  return transition;
306
368
  }
369
+ function hoverTransitionStyle(reducedMotion) {
370
+ return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
371
+ }
372
+ function tintTransitionStyle(reducedMotion) {
373
+ return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
374
+ }
375
+ function rotateTransitionStyle(reducedMotion) {
376
+ return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
377
+ }
378
+ var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
379
+ function getReducedMotionQuery() {
380
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
381
+ return window.matchMedia(REDUCED_MOTION_QUERY);
382
+ }
383
+ function useReducedMotion() {
384
+ const [reduced, setReduced] = React3.useState(() => getReducedMotionQuery()?.matches ?? false);
385
+ React3.useEffect(() => {
386
+ const mql = getReducedMotionQuery();
387
+ if (mql === void 0 || mql.addEventListener === void 0) return void 0;
388
+ const onChange = () => setReduced(mql.matches);
389
+ mql.addEventListener("change", onChange);
390
+ return () => {
391
+ mql.removeEventListener?.("change", onChange);
392
+ };
393
+ }, []);
394
+ return reduced;
395
+ }
396
+ var CHEVRON_GLYPH = "\u203A";
397
+ var EXPANDED_ROTATION = "90deg";
398
+ var COLLAPSED_ROTATION = "0deg";
399
+ var CHEVRON_FONT_WEIGHT = "700";
400
+ var SidebarChevron = ({ expanded, color, size }) => {
401
+ const reducedMotion = useReducedMotion();
402
+ const style = {
403
+ color,
404
+ fontSize: size,
405
+ fontWeight: CHEVRON_FONT_WEIGHT,
406
+ transform: [{ rotate: expanded ? EXPANDED_ROTATION : COLLAPSED_ROTATION }]
407
+ };
408
+ return /* @__PURE__ */ jsxRuntime.jsx(
409
+ reactNative.Text,
410
+ {
411
+ accessibilityElementsHidden: true,
412
+ importantForAccessibility: "no",
413
+ style: [style, rotateTransitionStyle(reducedMotion)],
414
+ children: CHEVRON_GLYPH
415
+ }
416
+ );
417
+ };
307
418
  function ariaCurrentProps(isActive) {
308
419
  return isActive ? { "aria-current": "page" } : {};
309
420
  }
310
421
  function ariaExpandedProps(expanded) {
311
422
  return { "aria-expanded": expanded };
312
423
  }
424
+ function hoverHandlerProps(onIn, onOut) {
425
+ return IS_WEB ? { onHoverIn: onIn, onHoverOut: onOut } : {};
426
+ }
313
427
  function hasActiveDescendant(item, pathname) {
314
428
  if (isRouteActive(pathname, item.route)) return true;
315
429
  return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
316
430
  }
431
+ function TintOverlay({ opacity, color }) {
432
+ const reducedMotion = useReducedMotion();
433
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)] });
434
+ }
317
435
  var NavExpandableItem = ({
318
436
  item,
319
437
  pathname,
@@ -324,27 +442,27 @@ var NavExpandableItem = ({
324
442
  renderChevron,
325
443
  depth = 0
326
444
  }) => {
327
- const [expanded, setExpanded] = React2.useState(() => hasActiveDescendant(item, pathname));
328
- const [focused, setFocused] = React2.useState(false);
445
+ const [expanded, setExpanded] = React3.useState(() => hasActiveDescendant(item, pathname));
446
+ const [focused, setFocused] = React3.useState(false);
447
+ const [hovered, setHovered] = React3.useState(false);
329
448
  const { theme } = uiFeedback.useUi();
330
449
  const colors = theme.colors;
331
450
  const primaryColor = theme.palette.primary["500"];
332
- const toggle = React2.useCallback(() => setExpanded((v) => !v), []);
451
+ const toggle = React3.useCallback(() => setExpanded((v) => !v), []);
452
+ const onHoverIn = React3.useCallback(() => setHovered(true), []);
453
+ const onHoverOut = React3.useCallback(() => setHovered(false), []);
333
454
  const indent = depth * BASE_INDENT;
334
455
  const hasChildren = Array.isArray(item.children) && item.children.length > 0;
335
456
  const isActive = isRouteActive(pathname, item.route);
336
457
  const isSectionHeader = hasChildren && depth === 0;
337
- const activeItemStyle = React2.useMemo(
338
- () => ({
339
- backgroundColor: colors.border,
340
- borderRadius: ACTIVE_BORDER_RADIUS,
341
- borderLeftWidth: ACTIVE_ACCENT_WIDTH,
342
- borderLeftColor: primaryColor
343
- }),
344
- [colors.border, primaryColor]
458
+ const activeAccentStyle = React3.useMemo(
459
+ () => ({ borderLeftWidth: ACTIVE_ACCENT_WIDTH, borderLeftColor: primaryColor }),
460
+ [primaryColor]
345
461
  );
346
- const paddingStyle = React2.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
347
- const headerPaddingStyle = React2.useMemo(() => ({ paddingLeft: indent }), [indent]);
462
+ const paddingStyle = React3.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
463
+ const headerPaddingStyle = React3.useMemo(() => ({ paddingLeft: indent }), [indent]);
464
+ const leafTintOpacity = isActive ? ACTIVE_TINT_OPACITY : hovered ? HOVER_TINT_OPACITY : 0;
465
+ const headerTintOpacity = hovered ? HOVER_TINT_OPACITY : 0;
348
466
  if (!hasChildren)
349
467
  return /* @__PURE__ */ jsxRuntime.jsxs(
350
468
  reactNative.TouchableOpacity,
@@ -356,15 +474,17 @@ var NavExpandableItem = ({
356
474
  style: [
357
475
  expandableStyles.childItem,
358
476
  paddingStyle,
359
- isActive ? activeItemStyle : void 0,
477
+ isActive ? activeAccentStyle : void 0,
360
478
  focusRingStyle(focused, primaryColor)
361
479
  ],
362
480
  testID: item.testID ?? item.key,
363
481
  onBlur: () => setFocused(false),
364
482
  onFocus: () => setFocused(true),
365
483
  onPress: () => onNavigate(item.route),
484
+ ...hoverHandlerProps(onHoverIn, onHoverOut),
366
485
  ...ariaCurrentProps(isActive),
367
486
  children: [
487
+ /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity }),
368
488
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
369
489
  /* @__PURE__ */ jsxRuntime.jsx(
370
490
  reactNative.Text,
@@ -379,6 +499,7 @@ var NavExpandableItem = ({
379
499
  ]
380
500
  }
381
501
  );
502
+ const chevronColor = colors.textSecondary;
382
503
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { children: [
383
504
  /* @__PURE__ */ jsxRuntime.jsxs(
384
505
  reactNative.TouchableOpacity,
@@ -397,8 +518,10 @@ var NavExpandableItem = ({
397
518
  onBlur: () => setFocused(false),
398
519
  onFocus: () => setFocused(true),
399
520
  onPress: toggle,
521
+ ...hoverHandlerProps(onHoverIn, onHoverOut),
400
522
  ...ariaExpandedProps(expanded),
401
523
  children: [
524
+ /* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity }),
402
525
  typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(isSectionHeader ? colors.textSecondary : colors.text, NAV_ICON_SIZE) }) : null,
403
526
  /* @__PURE__ */ jsxRuntime.jsx(
404
527
  reactNative.Text,
@@ -410,7 +533,7 @@ var NavExpandableItem = ({
410
533
  children: item.label
411
534
  }
412
535
  ),
413
- typeof renderChevron === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.chevron, children: renderChevron(expanded, colors.textSecondary, CHEVRON_ICON_SIZE) }) : null
536
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.chevron, children: typeof renderChevron === "function" ? renderChevron(expanded, chevronColor, CHEVRON_ICON_SIZE) : /* @__PURE__ */ jsxRuntime.jsx(SidebarChevron, { color: chevronColor, expanded, size: CHEVRON_ICON_SIZE }) })
414
537
  ]
415
538
  }
416
539
  ),
@@ -430,13 +553,93 @@ var NavExpandableItem = ({
430
553
  )) }) })
431
554
  ] });
432
555
  };
556
+ var FIELD_MIN_HEIGHT = 38;
557
+ var CLEAR_GLYPH = "\u2715";
558
+ var CLEAR_MIN_TARGET = 28;
559
+ var styles = reactNative.StyleSheet.create({
560
+ wrap: {
561
+ flexDirection: "row",
562
+ alignItems: "center",
563
+ borderWidth: 1,
564
+ borderRadius: 8,
565
+ paddingLeft: 12,
566
+ paddingRight: 6,
567
+ marginBottom: 12
568
+ },
569
+ input: {
570
+ flex: 1,
571
+ fontSize: 14,
572
+ paddingVertical: 8,
573
+ minHeight: FIELD_MIN_HEIGHT
574
+ },
575
+ clear: {
576
+ minWidth: CLEAR_MIN_TARGET,
577
+ minHeight: CLEAR_MIN_TARGET,
578
+ alignItems: "center",
579
+ justifyContent: "center",
580
+ borderRadius: 6
581
+ },
582
+ clearGlyph: { fontSize: 13, fontWeight: "700" }
583
+ });
584
+ var SidebarSearch = ({
585
+ value,
586
+ onChangeText,
587
+ onClear,
588
+ labels,
589
+ testID
590
+ }) => {
591
+ const { theme } = uiFeedback.useUi();
592
+ const colors = theme.colors;
593
+ const primary = theme.palette.primary["500"];
594
+ const [focused, setFocused] = React3.useState(false);
595
+ const hasQuery = value.length > 0;
596
+ return /* @__PURE__ */ jsxRuntime.jsxs(
597
+ reactNative.View,
598
+ {
599
+ style: [
600
+ styles.wrap,
601
+ { backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
602
+ focusRingStyle(focused, primary)
603
+ ],
604
+ children: [
605
+ /* @__PURE__ */ jsxRuntime.jsx(
606
+ reactNative.TextInput,
607
+ {
608
+ accessibilityHint: labels.hint,
609
+ accessibilityLabel: labels.placeholder,
610
+ placeholder: labels.placeholder,
611
+ placeholderTextColor: colors.textSecondary,
612
+ style: [styles.input, { color: colors.text }],
613
+ testID,
614
+ value,
615
+ onBlur: () => setFocused(false),
616
+ onChangeText,
617
+ onFocus: () => setFocused(true)
618
+ }
619
+ ),
620
+ hasQuery ? /* @__PURE__ */ jsxRuntime.jsx(
621
+ reactNative.Pressable,
622
+ {
623
+ accessibilityHint: labels.clearHint,
624
+ accessibilityLabel: labels.clearLabel,
625
+ accessibilityRole: "button",
626
+ style: styles.clear,
627
+ testID: `${testID}-clear`,
628
+ onPress: onClear,
629
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
630
+ }
631
+ ) : null
632
+ ]
633
+ }
634
+ );
635
+ };
433
636
  function isBareText(node) {
434
637
  return typeof node === "string" || typeof node === "number";
435
638
  }
436
639
  function renderTextSlot(node, style) {
437
640
  if (node === void 0 || node === null) return node;
438
641
  if (isBareText(node)) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: node });
439
- return React2__default.default.Children.map(
642
+ return React3__default.default.Children.map(
440
643
  node,
441
644
  (child) => isBareText(child) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: child }) : child
442
645
  );
@@ -451,12 +654,30 @@ var Sidebar = ({
451
654
  expandHint = "",
452
655
  collapseHint = "",
453
656
  renderChevron,
657
+ enableInlineSearch = false,
658
+ search,
659
+ searchQuery,
660
+ onSearchChange,
454
661
  header,
455
662
  footer,
456
663
  containerStyle
457
664
  }) => {
458
665
  const { theme } = uiFeedback.useUi();
459
666
  const colors = theme.colors;
667
+ const [internalQuery, setInternalQuery] = React3.useState("");
668
+ const controlled = searchQuery !== void 0;
669
+ const query = controlled ? searchQuery : internalQuery;
670
+ const setQuery = React3.useCallback(
671
+ (next) => {
672
+ if (!controlled) setInternalQuery(next);
673
+ onSearchChange?.(next);
674
+ },
675
+ [controlled, onSearchChange]
676
+ );
677
+ const clearQuery = React3.useCallback(() => setQuery(""), [setQuery]);
678
+ const searchOn = enableInlineSearch && search !== void 0;
679
+ const visibleItems = searchOn ? filterNavItems(items, query) : items;
680
+ const showEmpty = searchOn && isFilterActive(query) && visibleItems.length === 0;
460
681
  return /* @__PURE__ */ jsxRuntime.jsxs(
461
682
  reactNative.View,
462
683
  {
@@ -470,8 +691,18 @@ var Sidebar = ({
470
691
  ],
471
692
  children: [
472
693
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityRole: "header", style: [navStyles.sidebarTitle, { color: colors.text }], children: title }),
694
+ searchOn && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
695
+ SidebarSearch,
696
+ {
697
+ labels: search,
698
+ testID: "sidebar-search",
699
+ value: query,
700
+ onChangeText: setQuery,
701
+ onClear: clearQuery
702
+ }
703
+ ) : null,
473
704
  renderTextSlot(header, { color: colors.text }),
474
- items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
705
+ showEmpty && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navStyles.sidebarEmpty, { color: colors.textSecondary }], testID: "sidebar-search-empty", children: search.emptyText }) : visibleItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
475
706
  NavExpandableItem,
476
707
  {
477
708
  collapseHint,
@@ -491,6 +722,12 @@ var Sidebar = ({
491
722
  );
492
723
  };
493
724
 
725
+ // src/searchAffordance.ts
726
+ var DEFAULT_SEARCH_BREAKPOINT = 768;
727
+ function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
728
+ return viewport >= breakpoint ? "inline" : "palette";
729
+ }
730
+
494
731
  // src/constants.ts
495
732
  var NAV_TEST_IDS = {
496
733
  /** displayName line in the rich account header (tappable when `onAccount` set). */
@@ -538,7 +775,7 @@ var FocusableTouchable = ({
538
775
  testID,
539
776
  children
540
777
  }) => {
541
- const [focused, setFocused] = React2.useState(false);
778
+ const [focused, setFocused] = React3.useState(false);
542
779
  return /* @__PURE__ */ jsxRuntime.jsx(
543
780
  reactNative.Pressable,
544
781
  {
@@ -691,8 +928,8 @@ var NavBarLink = ({
691
928
  navigateHint,
692
929
  onPress
693
930
  }) => {
694
- const [hovered, setHovered] = React2.useState(false);
695
- const [focused, setFocused] = React2.useState(false);
931
+ const [hovered, setHovered] = React3.useState(false);
932
+ const [focused, setFocused] = React3.useState(false);
696
933
  const isHovered = hovered && !isActive;
697
934
  const textColor = isActive ? TEXT_ON_PRIMARY2 : isHovered ? colors.hoverText : colors.rest;
698
935
  const pillStyle = isActive ? { backgroundColor: colors.activeBg } : isHovered ? { backgroundColor: colors.hoverBg } : void 0;
@@ -736,12 +973,12 @@ var NavOverflowMenu = ({
736
973
  testID,
737
974
  variant
738
975
  }) => {
739
- const options = React2.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
740
- const activeRoute = React2.useMemo(
976
+ const options = React3.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
977
+ const activeRoute = React3.useMemo(
741
978
  () => items.find((item) => isRouteActive(pathname, item.route))?.route ?? NO_ACTIVE_ROUTE,
742
979
  [items, pathname]
743
980
  );
744
- const optionTestID = React2.useMemo(() => {
981
+ const optionTestID = React3.useMemo(() => {
745
982
  const byRoute = new Map(items.map((item) => [item.route, item.testID ?? item.key]));
746
983
  return (route) => byRoute.get(route) ?? `${testID}-option-${route}`;
747
984
  }, [items, testID]);
@@ -798,13 +1035,13 @@ function resizeWidthBuffer(previous, count) {
798
1035
  return next;
799
1036
  }
800
1037
  function useNavOverflow(itemCount, gap) {
801
- const [availableWidth, setAvailableWidthState] = React2.useState(0);
802
- const [moreWidth, setMoreWidthState] = React2.useState(0);
803
- const [itemWidths, setItemWidths] = React2.useState(() => new Array(itemCount).fill(0));
804
- React2.useEffect(() => {
1038
+ const [availableWidth, setAvailableWidthState] = React3.useState(0);
1039
+ const [moreWidth, setMoreWidthState] = React3.useState(0);
1040
+ const [itemWidths, setItemWidths] = React3.useState(() => new Array(itemCount).fill(0));
1041
+ React3.useEffect(() => {
805
1042
  setItemWidths((previous) => previous.length === itemCount ? previous : resizeWidthBuffer(previous, itemCount));
806
1043
  }, [itemCount]);
807
- const setItemWidth = React2.useCallback((index, width) => {
1044
+ const setItemWidth = React3.useCallback((index, width) => {
808
1045
  setItemWidths((previous) => {
809
1046
  if (index < 0 || index >= previous.length || previous[index] === width) return previous;
810
1047
  const next = previous.slice();
@@ -812,38 +1049,20 @@ function useNavOverflow(itemCount, gap) {
812
1049
  return next;
813
1050
  });
814
1051
  }, []);
815
- const setAvailableWidth = React2.useCallback(
1052
+ const setAvailableWidth = React3.useCallback(
816
1053
  (width) => setAvailableWidthState((previous) => previous === width ? previous : width),
817
1054
  []
818
1055
  );
819
- const setMoreWidth = React2.useCallback(
1056
+ const setMoreWidth = React3.useCallback(
820
1057
  (width) => setMoreWidthState((previous) => previous === width ? previous : width),
821
1058
  []
822
1059
  );
823
- const visibleCount = React2.useMemo(
1060
+ const visibleCount = React3.useMemo(
824
1061
  () => computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }),
825
1062
  [availableWidth, itemWidths, moreWidth, gap]
826
1063
  );
827
1064
  return { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth };
828
1065
  }
829
- var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
830
- function getReducedMotionQuery() {
831
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
832
- return window.matchMedia(REDUCED_MOTION_QUERY);
833
- }
834
- function useReducedMotion() {
835
- const [reduced, setReduced] = React2.useState(() => getReducedMotionQuery()?.matches ?? false);
836
- React2.useEffect(() => {
837
- const mql = getReducedMotionQuery();
838
- if (mql === void 0 || mql.addEventListener === void 0) return void 0;
839
- const onChange = () => setReduced(mql.matches);
840
- mql.addEventListener("change", onChange);
841
- return () => {
842
- mql.removeEventListener?.("change", onChange);
843
- };
844
- }, []);
845
- return reduced;
846
- }
847
1066
  var LINKS_REGION_ID = "navbar-links-region";
848
1067
  var DEFAULT_COLLAPSE_BELOW = 760;
849
1068
  var MENU_GLYPH = "\u2630";
@@ -872,24 +1091,24 @@ var NavBarInner = ({
872
1091
  const primaryColor = theme.palette.primary["500"];
873
1092
  const { width } = reactNative.useWindowDimensions();
874
1093
  const reducedMotion = useReducedMotion();
875
- const [open, setOpen] = React2.useState(false);
876
- const [toggleFocused, setToggleFocused] = React2.useState(false);
1094
+ const [open, setOpen] = React3.useState(false);
1095
+ const [toggleFocused, setToggleFocused] = React3.useState(false);
877
1096
  const { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth } = useNavOverflow(items.length, NAV_LINK_GAP);
878
1097
  const collapsed = width < collapseBelow;
879
1098
  const showLinks = !collapsed || open;
880
- const innerCapStyle = React2.useMemo(
1099
+ const innerCapStyle = React3.useMemo(
881
1100
  () => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
882
1101
  [contentMaxWidth]
883
1102
  );
884
- const toggleMenu = React2.useCallback(() => setOpen((v) => !v), []);
885
- const handlePress = React2.useCallback(
1103
+ const toggleMenu = React3.useCallback(() => setOpen((v) => !v), []);
1104
+ const handlePress = React3.useCallback(
886
1105
  (route) => {
887
1106
  setOpen(false);
888
1107
  onNavigate(route);
889
1108
  },
890
1109
  [onNavigate]
891
1110
  );
892
- const linkColors = React2.useMemo(
1111
+ const linkColors = React3.useMemo(
893
1112
  () => ({
894
1113
  rest: colors.textSecondary,
895
1114
  hoverText: colors.text,
@@ -944,7 +1163,7 @@ var NavBarInner = ({
944
1163
  }
945
1164
  );
946
1165
  if (collapsed) {
947
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(React2__default.default.Fragment, { children: renderLink(item) }, item.key)) });
1166
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { nativeID: LINKS_REGION_ID, style: navBarStyles.linksStacked, testID: NAV_TEST_IDS.navBarLinks, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(React3__default.default.Fragment, { children: renderLink(item) }, item.key)) });
948
1167
  }
949
1168
  const visibleItems = items.slice(0, visibleCount);
950
1169
  const overflowItems = items.slice(visibleCount);
@@ -1010,7 +1229,7 @@ var CARD_BORDER_WIDTH = 1;
1010
1229
  var CARD_TITLE_FONT_SIZE = 16;
1011
1230
  var CARD_MESSAGE_FONT_SIZE = 14;
1012
1231
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1013
- var styles = reactNative.StyleSheet.create({
1232
+ var styles2 = reactNative.StyleSheet.create({
1014
1233
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1015
1234
  card: {
1016
1235
  padding: CARD_PADDING,
@@ -1027,9 +1246,9 @@ function MessageCard({
1027
1246
  }) {
1028
1247
  const { theme } = uiFeedback.useUi();
1029
1248
  const colors = theme.colors;
1030
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1031
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.cardTitle, { color: accentColor }], children: message.titleText }),
1032
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1249
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles2.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1250
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardTitle, { color: accentColor }], children: message.titleText }),
1251
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1033
1252
  ] });
1034
1253
  }
1035
1254
  function useContentBody(state, children, testID) {
@@ -1041,7 +1260,7 @@ function useContentBody(state, children, testID) {
1041
1260
  if (state?.error)
1042
1261
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1043
1262
  if (state?.loading === true)
1044
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1263
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1045
1264
  return children;
1046
1265
  }
1047
1266
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
@@ -1065,7 +1284,7 @@ var DEFAULT_DRAWER_LABELS = {
1065
1284
  closeLabel: "Close menu",
1066
1285
  closeHint: "Close the navigation menu"
1067
1286
  };
1068
- var styles2 = reactNative.StyleSheet.create({
1287
+ var styles3 = reactNative.StyleSheet.create({
1069
1288
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1070
1289
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1071
1290
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1100,10 +1319,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1100
1319
  accessibilityHint: hint,
1101
1320
  accessibilityLabel: label,
1102
1321
  ringColor: theme.palette.primary["500"],
1103
- style: styles2.menuToggle,
1322
+ style: styles3.menuToggle,
1104
1323
  testID,
1105
1324
  onPress: onOpen,
1106
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1325
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1107
1326
  }
1108
1327
  );
1109
1328
  };
@@ -1115,8 +1334,8 @@ var MobileDrawer = ({
1115
1334
  scrimTestID
1116
1335
  }) => {
1117
1336
  const { theme } = uiFeedback.useUi();
1118
- const panelRef = React2.useRef(null);
1119
- React2.useEffect(() => {
1337
+ const panelRef = React3.useRef(null);
1338
+ React3.useEffect(() => {
1120
1339
  const node = panelRef.current;
1121
1340
  if (node === null || typeof node.addEventListener !== "function") return void 0;
1122
1341
  const handleClick = (event) => {
@@ -1129,14 +1348,14 @@ var MobileDrawer = ({
1129
1348
  node.addEventListener("click", handleClick);
1130
1349
  return () => node.removeEventListener("click", handleClick);
1131
1350
  }, [onClose]);
1132
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.overlay, children: [
1351
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.overlay, children: [
1133
1352
  /* @__PURE__ */ jsxRuntime.jsx(
1134
1353
  reactNative.Pressable,
1135
1354
  {
1136
1355
  accessibilityHint: labels.closeHint,
1137
1356
  accessibilityLabel: labels.closeLabel,
1138
1357
  accessibilityRole: "button",
1139
- style: styles2.scrim,
1358
+ style: styles3.scrim,
1140
1359
  testID: scrimTestID,
1141
1360
  onPress: onClose
1142
1361
  }
@@ -1147,7 +1366,7 @@ var MobileDrawer = ({
1147
1366
  ref: panelRef,
1148
1367
  "aria-modal": true,
1149
1368
  role: "dialog",
1150
- style: [styles2.panel, { backgroundColor: theme.colors.surface }],
1369
+ style: [styles3.panel, { backgroundColor: theme.colors.surface }],
1151
1370
  testID: drawerTestID,
1152
1371
  children: renderTextSlot(sidebar, { color: theme.colors.text })
1153
1372
  }
@@ -1178,7 +1397,7 @@ function useContentMaxWidth(width) {
1178
1397
  return resolveContentMaxWidth(width, viewport);
1179
1398
  }
1180
1399
  var DEFAULT_CONTENT_PADDING = 24;
1181
- var styles3 = reactNative.StyleSheet.create({
1400
+ var styles4 = reactNative.StyleSheet.create({
1182
1401
  root: { flex: 1 },
1183
1402
  centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
1184
1403
  scroll: { flex: 1 },
@@ -1223,32 +1442,32 @@ var AppShell = ({
1223
1442
  range: railRange
1224
1443
  });
1225
1444
  const useDrawer = railMode === "drawer";
1226
- const [drawerOpen, setDrawerOpen] = React2.useState(false);
1227
- const openDrawer = React2.useCallback(() => setDrawerOpen(true), []);
1228
- const closeDrawer = React2.useCallback(() => setDrawerOpen(false), []);
1229
- React2.useEffect(() => {
1445
+ const [drawerOpen, setDrawerOpen] = React3.useState(false);
1446
+ const openDrawer = React3.useCallback(() => setDrawerOpen(true), []);
1447
+ const closeDrawer = React3.useCallback(() => setDrawerOpen(false), []);
1448
+ React3.useEffect(() => {
1230
1449
  if (!useDrawer && drawerOpen) setDrawerOpen(false);
1231
1450
  }, [useDrawer, drawerOpen]);
1232
1451
  const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
1233
1452
  const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
1234
- React2.useEffect(() => {
1453
+ React3.useEffect(() => {
1235
1454
  if (isUnauthenticated && gate !== void 0) gate.onRedirect();
1236
1455
  }, [isUnauthenticated, gate]);
1237
1456
  if (gate?.pending === true)
1238
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.root, styles3.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1457
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.root, styles4.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
1239
1458
  if (isUnauthenticated) return null;
1240
- const columnStyle = maxWidth === "full" ? styles3.columnFull : [styles3.columnCapped, { maxWidth }];
1241
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles3.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1459
+ const columnStyle = maxWidth === "full" ? styles4.columnFull : [styles4.columnCapped, { maxWidth }];
1460
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles4.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1242
1461
  const scroller = /* @__PURE__ */ jsxRuntime.jsx(
1243
1462
  reactNative.ScrollView,
1244
1463
  {
1245
- contentContainerStyle: [styles3.scrollContent, { padding: contentPadding }],
1246
- style: styles3.scroll,
1464
+ contentContainerStyle: [styles4.scrollContent, { padding: contentPadding }],
1465
+ style: styles4.scroll,
1247
1466
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1248
1467
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
1249
1468
  }
1250
1469
  );
1251
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1470
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1252
1471
  /* @__PURE__ */ jsxRuntime.jsx(
1253
1472
  MenuToggle,
1254
1473
  {
@@ -1258,14 +1477,14 @@ var AppShell = ({
1258
1477
  onOpen: openDrawer
1259
1478
  }
1260
1479
  ),
1261
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1480
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles4.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1262
1481
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
1263
1482
  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;
1264
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.root, { backgroundColor: theme.colors.background }], testID, children: [
1483
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.root, { backgroundColor: theme.colors.background }], testID, children: [
1265
1484
  headerRegion,
1266
1485
  nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: theme.colors.text }) }) : renderTextSlot(nav, { color: theme.colors.text }) }) : null,
1267
1486
  banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1268
- railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.bodyRow, children: [
1487
+ railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.bodyRow, children: [
1269
1488
  railRegion,
1270
1489
  scroller
1271
1490
  ] }) : scroller,
@@ -1483,7 +1702,7 @@ var DarkModeControl = ({
1483
1702
  const primaryColor = theme.palette.primary["500"];
1484
1703
  const currentIndex = options.findIndex((option) => option.value === value);
1485
1704
  const current = currentIndex >= 0 ? options[currentIndex] : options[0];
1486
- const advance = React2.useCallback(() => {
1705
+ const advance = React3.useCallback(() => {
1487
1706
  if (options.length === 0) return;
1488
1707
  const from = currentIndex >= 0 ? currentIndex : 0;
1489
1708
  const next = options[(from + 1) % options.length];
@@ -1559,7 +1778,7 @@ var KEY_DOWN = "ArrowDown";
1559
1778
  var KEY_UP = "ArrowUp";
1560
1779
  var KEY_ENTER = "Enter";
1561
1780
  var KEY_ESCAPE = "Escape";
1562
- var styles4 = reactNative.StyleSheet.create({
1781
+ var styles5 = reactNative.StyleSheet.create({
1563
1782
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
1564
1783
  scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
1565
1784
  panel: {
@@ -1588,14 +1807,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
1588
1807
  accessibilityLabel: item.label,
1589
1808
  accessibilityRole: "button",
1590
1809
  accessibilityState: { selected },
1591
- style: [styles4.row, selected ? { backgroundColor: colors.border } : void 0],
1810
+ style: [styles5.row, selected ? { backgroundColor: colors.border } : void 0],
1592
1811
  testID: item.testID ?? item.key,
1593
1812
  onPress: () => onChoose(item),
1594
1813
  children: [
1595
1814
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
1596
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.rowText, children: [
1597
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.rowLabel, { color: colors.text }], children: item.label }),
1598
- item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1815
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.rowText, children: [
1816
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.rowLabel, { color: colors.text }], children: item.label }),
1817
+ item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
1599
1818
  ] })
1600
1819
  ]
1601
1820
  }
@@ -1604,27 +1823,27 @@ var PaletteRow = ({ item, selected, onChoose }) => {
1604
1823
  var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1605
1824
  const { theme } = uiFeedback.useUi();
1606
1825
  const colors = theme.colors;
1607
- const [query, setQuery] = React2.useState("");
1608
- const [selected, setSelected] = React2.useState(0);
1609
- const inputRef = React2.useRef(null);
1610
- const results = React2.useMemo(() => filterCommands(items, query), [items, query]);
1611
- React2.useEffect(() => {
1826
+ const [query, setQuery] = React3.useState("");
1827
+ const [selected, setSelected] = React3.useState(0);
1828
+ const inputRef = React3.useRef(null);
1829
+ const results = React3.useMemo(() => filterCommands(items, query), [items, query]);
1830
+ React3.useEffect(() => {
1612
1831
  if (!open) return;
1613
1832
  setQuery("");
1614
1833
  setSelected(0);
1615
1834
  inputRef.current?.focus();
1616
1835
  }, [open]);
1617
- React2.useEffect(() => {
1836
+ React3.useEffect(() => {
1618
1837
  setSelected((prev) => Math.min(prev, Math.max(0, results.length - 1)));
1619
1838
  }, [results.length]);
1620
- const choose = React2.useCallback(
1839
+ const choose = React3.useCallback(
1621
1840
  (item) => {
1622
1841
  item.onSelect();
1623
1842
  onClose();
1624
1843
  },
1625
1844
  [onClose]
1626
1845
  );
1627
- const onKeyPress = React2.useCallback(
1846
+ const onKeyPress = React3.useCallback(
1628
1847
  (event) => {
1629
1848
  const key = event.nativeEvent.key;
1630
1849
  if (key === KEY_DOWN) setSelected((i) => Math.min(i + 1, results.length - 1));
@@ -1635,14 +1854,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1635
1854
  [results, selected, choose, onClose]
1636
1855
  );
1637
1856
  if (!open) return null;
1638
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
1857
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.overlay, children: [
1639
1858
  /* @__PURE__ */ jsxRuntime.jsx(
1640
1859
  reactNative.Pressable,
1641
1860
  {
1642
1861
  accessibilityHint: labels.closeHint,
1643
1862
  accessibilityLabel: labels.closeLabel,
1644
1863
  accessibilityRole: "button",
1645
- style: styles4.scrim,
1864
+ style: styles5.scrim,
1646
1865
  testID: `${testID ?? "command-palette"}-scrim`,
1647
1866
  onPress: onClose
1648
1867
  }
@@ -1653,7 +1872,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1653
1872
  "aria-modal": true,
1654
1873
  "aria-label": labels.regionLabel,
1655
1874
  role: "dialog",
1656
- style: [styles4.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1875
+ style: [styles5.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
1657
1876
  testID: testID ?? "command-palette",
1658
1877
  children: [
1659
1878
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1663,21 +1882,21 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
1663
1882
  accessibilityLabel: labels.placeholder,
1664
1883
  placeholder: labels.placeholder,
1665
1884
  placeholderTextColor: colors.textSecondary,
1666
- style: [styles4.input, { color: colors.text, borderBottomColor: colors.border }],
1885
+ style: [styles5.input, { color: colors.text, borderBottomColor: colors.border }],
1667
1886
  testID: `${testID ?? "command-palette"}-input`,
1668
1887
  value: query,
1669
1888
  onChangeText: setQuery,
1670
1889
  onKeyPress
1671
1890
  }
1672
1891
  ),
1673
- results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles4.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1892
+ results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles5.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
1674
1893
  ]
1675
1894
  }
1676
1895
  )
1677
1896
  ] });
1678
1897
  };
1679
1898
  var MIN_TARGET = 36;
1680
- var styles5 = reactNative.StyleSheet.create({
1899
+ var styles6 = reactNative.StyleSheet.create({
1681
1900
  trigger: {
1682
1901
  flexDirection: "row",
1683
1902
  alignItems: "center",
@@ -1702,7 +1921,7 @@ var CommandPaletteTrigger = ({
1702
1921
  const { theme } = uiFeedback.useUi();
1703
1922
  const colors = theme.colors;
1704
1923
  const primary = theme.palette.primary["500"];
1705
- const [focused, setFocused] = React2__default.default.useState(false);
1924
+ const [focused, setFocused] = React3__default.default.useState(false);
1706
1925
  return /* @__PURE__ */ jsxRuntime.jsxs(
1707
1926
  reactNative.Pressable,
1708
1927
  {
@@ -1710,7 +1929,7 @@ var CommandPaletteTrigger = ({
1710
1929
  accessibilityLabel: label,
1711
1930
  accessibilityRole: "button",
1712
1931
  style: [
1713
- styles5.trigger,
1932
+ styles6.trigger,
1714
1933
  { backgroundColor: colors.surface, borderColor: colors.border },
1715
1934
  focusRingStyle(focused, primary)
1716
1935
  ],
@@ -1719,15 +1938,15 @@ var CommandPaletteTrigger = ({
1719
1938
  onFocus: () => setFocused(true),
1720
1939
  onPress,
1721
1940
  children: [
1722
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.label, { color: colors.textSecondary }], children: label }),
1723
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.badgeText, { color: colors.textSecondary }], children: shortcut }) })
1941
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.label, { color: colors.textSecondary }], children: label }),
1942
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles6.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.badgeText, { color: colors.textSecondary }], children: shortcut }) })
1724
1943
  ]
1725
1944
  }
1726
1945
  );
1727
1946
  };
1728
1947
  var LAUNCH_KEY = "k";
1729
1948
  function useCommandPaletteHotkey(onOpen, enabled = true) {
1730
- React2.useEffect(() => {
1949
+ React3.useEffect(() => {
1731
1950
  if (!enabled || typeof document === "undefined") return void 0;
1732
1951
  const handler = (event) => {
1733
1952
  const isLauncher = event.key.toLowerCase() === LAUNCH_KEY && (event.metaKey || event.ctrlKey);
@@ -1751,6 +1970,7 @@ function accessibleNavItems(user, table, translate) {
1751
1970
  }
1752
1971
 
1753
1972
  exports.ACTIVE_BORDER_RADIUS = ACTIVE_BORDER_RADIUS;
1973
+ exports.ACTIVE_TINT_OPACITY = ACTIVE_TINT_OPACITY;
1754
1974
  exports.APP_SHELL_SUFFIX = APP_SHELL_SUFFIX;
1755
1975
  exports.AppShell = AppShell;
1756
1976
  exports.BASE_INDENT = BASE_INDENT;
@@ -1759,9 +1979,12 @@ exports.CollapsedRail = CollapsedRail;
1759
1979
  exports.CommandPalette = CommandPalette;
1760
1980
  exports.CommandPaletteTrigger = CommandPaletteTrigger;
1761
1981
  exports.DEFAULT_COLLAPSED_RAIL_MAX = DEFAULT_COLLAPSED_RAIL_MAX;
1982
+ exports.DEFAULT_SEARCH_BREAKPOINT = DEFAULT_SEARCH_BREAKPOINT;
1762
1983
  exports.DarkModeControl = DarkModeControl;
1984
+ exports.HOVER_TINT_OPACITY = HOVER_TINT_OPACITY;
1763
1985
  exports.NAV_ICON_SIZE = NAV_ICON_SIZE;
1764
1986
  exports.NAV_LINK_GAP = NAV_LINK_GAP;
1987
+ exports.NAV_ROW_RADIUS = NAV_ROW_RADIUS;
1765
1988
  exports.NAV_TEST_IDS = NAV_TEST_IDS;
1766
1989
  exports.Nav = Nav;
1767
1990
  exports.NavBar = NavBar;
@@ -1771,18 +1994,24 @@ exports.NavShell = NavShell;
1771
1994
  exports.PillNav = PillNav;
1772
1995
  exports.RAIL_FULL_BREAKPOINT = RAIL_FULL_BREAKPOINT;
1773
1996
  exports.Sidebar = Sidebar;
1997
+ exports.SidebarChevron = SidebarChevron;
1998
+ exports.SidebarSearch = SidebarSearch;
1774
1999
  exports.Topbar = Topbar;
1775
2000
  exports.accessibleNavItems = accessibleNavItems;
1776
2001
  exports.collapsedRailStyles = collapsedRailStyles;
1777
2002
  exports.darkModeStyles = darkModeStyles;
1778
2003
  exports.expandableStyles = expandableStyles;
1779
2004
  exports.filterCommands = filterCommands;
2005
+ exports.filterNavItems = filterNavItems;
2006
+ exports.isFilterActive = isFilterActive;
1780
2007
  exports.isRouteActive = isRouteActive;
1781
2008
  exports.navStyles = navStyles;
1782
2009
  exports.pillNavStyles = pillNavStyles;
1783
2010
  exports.resolveContentMaxWidth = resolveContentMaxWidth;
1784
2011
  exports.resolveRailMode = resolveRailMode;
2012
+ exports.resolveSearchAffordance = resolveSearchAffordance;
1785
2013
  exports.roleRoutesToNavItems = roleRoutesToNavItems;
2014
+ exports.searchTokens = searchTokens;
1786
2015
  exports.useCommandPaletteHotkey = useCommandPaletteHotkey;
1787
2016
  exports.useContentMaxWidth = useContentMaxWidth;
1788
2017
  //# sourceMappingURL=index.js.map