@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/CHANGELOG.md +25 -0
- package/dist/index.d.mts +40 -4
- package/dist/index.d.ts +40 -4
- package/dist/index.js +232 -194
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +180 -142
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,99 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React = require('react');
|
|
4
4
|
var reactNative = require('react-native');
|
|
5
5
|
var uiFeedback = require('@dloizides/ui-feedback');
|
|
6
|
-
var uiMotion = require('@dloizides/ui-motion');
|
|
7
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
var uiMotion = require('@dloizides/ui-motion');
|
|
8
8
|
var uiLayout = require('@dloizides/ui-layout');
|
|
9
9
|
var authWeb = require('@dloizides/auth-web');
|
|
10
10
|
|
|
11
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
14
14
|
|
|
15
15
|
// src/Sidebar.tsx
|
|
16
|
+
var IS_WEB = reactNative.Platform.OS === "web";
|
|
17
|
+
var FOCUS_RING_WIDTH = 2;
|
|
18
|
+
var FOCUS_RING_OFFSET = 2;
|
|
19
|
+
var HOVER_TRANSITION_MS = 150;
|
|
20
|
+
var TINT_TRANSITION_MS = 160;
|
|
21
|
+
function focusRingStyle(focused, ringColor) {
|
|
22
|
+
if (!IS_WEB || !focused) return void 0;
|
|
23
|
+
const ring = {
|
|
24
|
+
outlineWidth: FOCUS_RING_WIDTH,
|
|
25
|
+
outlineStyle: "solid",
|
|
26
|
+
outlineColor: ringColor,
|
|
27
|
+
outlineOffset: FOCUS_RING_OFFSET
|
|
28
|
+
};
|
|
29
|
+
return ring;
|
|
30
|
+
}
|
|
31
|
+
function webTransition(properties, durationMs, reducedMotion) {
|
|
32
|
+
if (!IS_WEB) return void 0;
|
|
33
|
+
const transition = {
|
|
34
|
+
transitionProperty: properties,
|
|
35
|
+
transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
|
|
36
|
+
};
|
|
37
|
+
return transition;
|
|
38
|
+
}
|
|
39
|
+
function hoverTransitionStyle(reducedMotion) {
|
|
40
|
+
return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
|
|
41
|
+
}
|
|
42
|
+
function tintTransitionStyle(reducedMotion) {
|
|
43
|
+
return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
|
|
44
|
+
}
|
|
45
|
+
function rotateTransitionStyle(reducedMotion) {
|
|
46
|
+
return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
|
|
47
|
+
}
|
|
48
|
+
var MIN_TARGET = 36;
|
|
49
|
+
var styles = reactNative.StyleSheet.create({
|
|
50
|
+
trigger: {
|
|
51
|
+
flexDirection: "row",
|
|
52
|
+
alignItems: "center",
|
|
53
|
+
justifyContent: "space-between",
|
|
54
|
+
columnGap: 8,
|
|
55
|
+
paddingHorizontal: 12,
|
|
56
|
+
minHeight: MIN_TARGET,
|
|
57
|
+
borderWidth: 1,
|
|
58
|
+
borderRadius: 8
|
|
59
|
+
},
|
|
60
|
+
label: { fontSize: 14 },
|
|
61
|
+
badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
|
|
62
|
+
badgeText: { fontSize: 11, fontWeight: "700" }
|
|
63
|
+
});
|
|
64
|
+
var CommandPaletteTrigger = ({
|
|
65
|
+
label,
|
|
66
|
+
hint,
|
|
67
|
+
shortcut,
|
|
68
|
+
onPress,
|
|
69
|
+
testID
|
|
70
|
+
}) => {
|
|
71
|
+
const { theme } = uiFeedback.useUi();
|
|
72
|
+
const colors = theme.colors;
|
|
73
|
+
const primary = theme.palette.primary["500"];
|
|
74
|
+
const [focused, setFocused] = React__default.default.useState(false);
|
|
75
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
76
|
+
reactNative.Pressable,
|
|
77
|
+
{
|
|
78
|
+
accessibilityHint: hint,
|
|
79
|
+
accessibilityLabel: label,
|
|
80
|
+
accessibilityRole: "button",
|
|
81
|
+
style: [
|
|
82
|
+
styles.trigger,
|
|
83
|
+
{ backgroundColor: colors.surface, borderColor: colors.border },
|
|
84
|
+
focusRingStyle(focused, primary)
|
|
85
|
+
],
|
|
86
|
+
testID,
|
|
87
|
+
onBlur: () => setFocused(false),
|
|
88
|
+
onFocus: () => setFocused(true),
|
|
89
|
+
onPress,
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.label, { color: colors.textSecondary }], children: label }),
|
|
92
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.badgeText, { color: colors.textSecondary }], children: shortcut }) })
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
};
|
|
16
97
|
|
|
17
98
|
// src/sidebarFilter.ts
|
|
18
99
|
function searchTokens(query) {
|
|
@@ -56,6 +137,10 @@ var ACTIVE_ACCENT_WIDTH = 3;
|
|
|
56
137
|
var NAV_ROW_RADIUS = 8;
|
|
57
138
|
var ACTIVE_TINT_OPACITY = 0.14;
|
|
58
139
|
var HOVER_TINT_OPACITY = 0.06;
|
|
140
|
+
function rowTintOpacity(isActive, hovered) {
|
|
141
|
+
if (isActive) return ACTIVE_TINT_OPACITY;
|
|
142
|
+
return hovered ? HOVER_TINT_OPACITY : 0;
|
|
143
|
+
}
|
|
59
144
|
var NAV_LINK_GAP = 4;
|
|
60
145
|
var navStyles = reactNative.StyleSheet.create({
|
|
61
146
|
// --- Sidebar ---
|
|
@@ -81,6 +166,12 @@ var navStyles = reactNative.StyleSheet.create({
|
|
|
81
166
|
sidebarSpacer: {
|
|
82
167
|
flex: 1
|
|
83
168
|
},
|
|
169
|
+
// Bottom rhythm under the ⌘K palette trigger (the narrow-viewport search
|
|
170
|
+
// affordance), matching the inline field's own `marginBottom` so swapping
|
|
171
|
+
// affordances by width keeps the same gap above the nav list.
|
|
172
|
+
sidebarTriggerSlot: {
|
|
173
|
+
marginBottom: 12
|
|
174
|
+
},
|
|
84
175
|
// Inline-search no-match text — sits where the nav list would, muted + padded.
|
|
85
176
|
sidebarEmpty: {
|
|
86
177
|
fontSize: 13,
|
|
@@ -343,46 +434,14 @@ var expandableStyles = reactNative.StyleSheet.create({
|
|
|
343
434
|
var BASE_INDENT = 12;
|
|
344
435
|
var NAV_ICON_SIZE = 14;
|
|
345
436
|
var CHEVRON_ICON_SIZE = 12;
|
|
346
|
-
var IS_WEB = reactNative.Platform.OS === "web";
|
|
347
|
-
var FOCUS_RING_WIDTH = 2;
|
|
348
|
-
var FOCUS_RING_OFFSET = 2;
|
|
349
|
-
var HOVER_TRANSITION_MS = 150;
|
|
350
|
-
var TINT_TRANSITION_MS = 160;
|
|
351
|
-
function focusRingStyle(focused, ringColor) {
|
|
352
|
-
if (!IS_WEB || !focused) return void 0;
|
|
353
|
-
const ring = {
|
|
354
|
-
outlineWidth: FOCUS_RING_WIDTH,
|
|
355
|
-
outlineStyle: "solid",
|
|
356
|
-
outlineColor: ringColor,
|
|
357
|
-
outlineOffset: FOCUS_RING_OFFSET
|
|
358
|
-
};
|
|
359
|
-
return ring;
|
|
360
|
-
}
|
|
361
|
-
function webTransition(properties, durationMs, reducedMotion) {
|
|
362
|
-
if (!IS_WEB) return void 0;
|
|
363
|
-
const transition = {
|
|
364
|
-
transitionProperty: properties,
|
|
365
|
-
transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
|
|
366
|
-
};
|
|
367
|
-
return transition;
|
|
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
437
|
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
379
438
|
function getReducedMotionQuery() {
|
|
380
439
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
|
|
381
440
|
return window.matchMedia(REDUCED_MOTION_QUERY);
|
|
382
441
|
}
|
|
383
442
|
function useReducedMotion() {
|
|
384
|
-
const [reduced, setReduced] =
|
|
385
|
-
|
|
443
|
+
const [reduced, setReduced] = React.useState(() => getReducedMotionQuery()?.matches ?? false);
|
|
444
|
+
React.useEffect(() => {
|
|
386
445
|
const mql = getReducedMotionQuery();
|
|
387
446
|
if (mql === void 0 || mql.addEventListener === void 0) return void 0;
|
|
388
447
|
const onChange = () => setReduced(mql.matches);
|
|
@@ -428,9 +487,19 @@ function hasActiveDescendant(item, pathname) {
|
|
|
428
487
|
if (isRouteActive(pathname, item.route)) return true;
|
|
429
488
|
return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
|
|
430
489
|
}
|
|
431
|
-
function TintOverlay({
|
|
490
|
+
function TintOverlay({
|
|
491
|
+
opacity,
|
|
492
|
+
color,
|
|
493
|
+
testID
|
|
494
|
+
}) {
|
|
432
495
|
const reducedMotion = useReducedMotion();
|
|
433
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
496
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
497
|
+
reactNative.View,
|
|
498
|
+
{
|
|
499
|
+
style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)],
|
|
500
|
+
testID
|
|
501
|
+
}
|
|
502
|
+
);
|
|
434
503
|
}
|
|
435
504
|
var NavExpandableItem = ({
|
|
436
505
|
item,
|
|
@@ -442,30 +511,31 @@ var NavExpandableItem = ({
|
|
|
442
511
|
renderChevron,
|
|
443
512
|
depth = 0
|
|
444
513
|
}) => {
|
|
445
|
-
const [expanded, setExpanded] =
|
|
446
|
-
const [focused, setFocused] =
|
|
447
|
-
const [hovered, setHovered] =
|
|
514
|
+
const [expanded, setExpanded] = React.useState(() => hasActiveDescendant(item, pathname));
|
|
515
|
+
const [focused, setFocused] = React.useState(false);
|
|
516
|
+
const [hovered, setHovered] = React.useState(false);
|
|
448
517
|
const { theme } = uiFeedback.useUi();
|
|
449
518
|
const colors = theme.colors;
|
|
450
519
|
const primaryColor = theme.palette.primary["500"];
|
|
451
|
-
const toggle =
|
|
452
|
-
const onHoverIn =
|
|
453
|
-
const onHoverOut =
|
|
520
|
+
const toggle = React.useCallback(() => setExpanded((v) => !v), []);
|
|
521
|
+
const onHoverIn = React.useCallback(() => setHovered(true), []);
|
|
522
|
+
const onHoverOut = React.useCallback(() => setHovered(false), []);
|
|
454
523
|
const indent = depth * BASE_INDENT;
|
|
455
524
|
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
|
|
456
525
|
const isActive = isRouteActive(pathname, item.route);
|
|
457
526
|
const isSectionHeader = hasChildren && depth === 0;
|
|
458
|
-
const activeAccentStyle =
|
|
527
|
+
const activeAccentStyle = React.useMemo(
|
|
459
528
|
() => ({ borderLeftWidth: ACTIVE_ACCENT_WIDTH, borderLeftColor: primaryColor }),
|
|
460
529
|
[primaryColor]
|
|
461
530
|
);
|
|
462
|
-
const paddingStyle =
|
|
463
|
-
const headerPaddingStyle =
|
|
464
|
-
const leafTintOpacity = isActive
|
|
465
|
-
const headerTintOpacity = hovered
|
|
531
|
+
const paddingStyle = React.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
|
|
532
|
+
const headerPaddingStyle = React.useMemo(() => ({ paddingLeft: indent }), [indent]);
|
|
533
|
+
const leafTintOpacity = rowTintOpacity(isActive, hovered);
|
|
534
|
+
const headerTintOpacity = rowTintOpacity(false, hovered);
|
|
535
|
+
const rowTestID = item.testID ?? item.key;
|
|
466
536
|
if (!hasChildren)
|
|
467
537
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
468
|
-
reactNative.
|
|
538
|
+
reactNative.Pressable,
|
|
469
539
|
{
|
|
470
540
|
accessibilityHint: navigateHint(item.label),
|
|
471
541
|
accessibilityLabel: item.label,
|
|
@@ -477,14 +547,14 @@ var NavExpandableItem = ({
|
|
|
477
547
|
isActive ? activeAccentStyle : void 0,
|
|
478
548
|
focusRingStyle(focused, primaryColor)
|
|
479
549
|
],
|
|
480
|
-
testID:
|
|
550
|
+
testID: rowTestID,
|
|
481
551
|
onBlur: () => setFocused(false),
|
|
482
552
|
onFocus: () => setFocused(true),
|
|
483
553
|
onPress: () => onNavigate(item.route),
|
|
484
554
|
...hoverHandlerProps(onHoverIn, onHoverOut),
|
|
485
555
|
...ariaCurrentProps(isActive),
|
|
486
556
|
children: [
|
|
487
|
-
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity }),
|
|
557
|
+
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity, testID: `${rowTestID}-tint` }),
|
|
488
558
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
|
|
489
559
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
490
560
|
reactNative.Text,
|
|
@@ -502,7 +572,7 @@ var NavExpandableItem = ({
|
|
|
502
572
|
const chevronColor = colors.textSecondary;
|
|
503
573
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { children: [
|
|
504
574
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
505
|
-
reactNative.
|
|
575
|
+
reactNative.Pressable,
|
|
506
576
|
{
|
|
507
577
|
accessibilityHint: expanded ? collapseHint : expandHint,
|
|
508
578
|
accessibilityLabel: item.label,
|
|
@@ -514,14 +584,14 @@ var NavExpandableItem = ({
|
|
|
514
584
|
headerPaddingStyle,
|
|
515
585
|
focusRingStyle(focused, primaryColor)
|
|
516
586
|
],
|
|
517
|
-
testID:
|
|
587
|
+
testID: rowTestID,
|
|
518
588
|
onBlur: () => setFocused(false),
|
|
519
589
|
onFocus: () => setFocused(true),
|
|
520
590
|
onPress: toggle,
|
|
521
591
|
...hoverHandlerProps(onHoverIn, onHoverOut),
|
|
522
592
|
...ariaExpandedProps(expanded),
|
|
523
593
|
children: [
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity }),
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity, testID: `${rowTestID}-tint` }),
|
|
525
595
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(isSectionHeader ? colors.textSecondary : colors.text, NAV_ICON_SIZE) }) : null,
|
|
526
596
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
527
597
|
reactNative.Text,
|
|
@@ -553,10 +623,16 @@ var NavExpandableItem = ({
|
|
|
553
623
|
)) }) })
|
|
554
624
|
] });
|
|
555
625
|
};
|
|
626
|
+
|
|
627
|
+
// src/searchAffordance.ts
|
|
628
|
+
var DEFAULT_SEARCH_BREAKPOINT = 768;
|
|
629
|
+
function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
|
|
630
|
+
return viewport >= breakpoint ? "inline" : "palette";
|
|
631
|
+
}
|
|
556
632
|
var FIELD_MIN_HEIGHT = 38;
|
|
557
633
|
var CLEAR_GLYPH = "\u2715";
|
|
558
634
|
var CLEAR_MIN_TARGET = 28;
|
|
559
|
-
var
|
|
635
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
560
636
|
wrap: {
|
|
561
637
|
flexDirection: "row",
|
|
562
638
|
alignItems: "center",
|
|
@@ -591,13 +667,13 @@ var SidebarSearch = ({
|
|
|
591
667
|
const { theme } = uiFeedback.useUi();
|
|
592
668
|
const colors = theme.colors;
|
|
593
669
|
const primary = theme.palette.primary["500"];
|
|
594
|
-
const [focused, setFocused] =
|
|
670
|
+
const [focused, setFocused] = React.useState(false);
|
|
595
671
|
const hasQuery = value.length > 0;
|
|
596
672
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
597
673
|
reactNative.View,
|
|
598
674
|
{
|
|
599
675
|
style: [
|
|
600
|
-
|
|
676
|
+
styles2.wrap,
|
|
601
677
|
{ backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
|
|
602
678
|
focusRingStyle(focused, primary)
|
|
603
679
|
],
|
|
@@ -609,7 +685,7 @@ var SidebarSearch = ({
|
|
|
609
685
|
accessibilityLabel: labels.placeholder,
|
|
610
686
|
placeholder: labels.placeholder,
|
|
611
687
|
placeholderTextColor: colors.textSecondary,
|
|
612
|
-
style: [
|
|
688
|
+
style: [styles2.input, { color: colors.text }],
|
|
613
689
|
testID,
|
|
614
690
|
value,
|
|
615
691
|
onBlur: () => setFocused(false),
|
|
@@ -623,10 +699,10 @@ var SidebarSearch = ({
|
|
|
623
699
|
accessibilityHint: labels.clearHint,
|
|
624
700
|
accessibilityLabel: labels.clearLabel,
|
|
625
701
|
accessibilityRole: "button",
|
|
626
|
-
style:
|
|
702
|
+
style: styles2.clear,
|
|
627
703
|
testID: `${testID}-clear`,
|
|
628
704
|
onPress: onClear,
|
|
629
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
705
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
|
|
630
706
|
}
|
|
631
707
|
) : null
|
|
632
708
|
]
|
|
@@ -639,11 +715,12 @@ function isBareText(node) {
|
|
|
639
715
|
function renderTextSlot(node, style) {
|
|
640
716
|
if (node === void 0 || node === null) return node;
|
|
641
717
|
if (isBareText(node)) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: node });
|
|
642
|
-
return
|
|
718
|
+
return React__default.default.Children.map(
|
|
643
719
|
node,
|
|
644
720
|
(child) => isBareText(child) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: child }) : child
|
|
645
721
|
);
|
|
646
722
|
}
|
|
723
|
+
var DEFAULT_TRIGGER_TEST_ID = "sidebar-command-palette-trigger";
|
|
647
724
|
var Sidebar = ({
|
|
648
725
|
items,
|
|
649
726
|
pathname,
|
|
@@ -656,6 +733,7 @@ var Sidebar = ({
|
|
|
656
733
|
renderChevron,
|
|
657
734
|
enableInlineSearch = false,
|
|
658
735
|
search,
|
|
736
|
+
paletteTrigger,
|
|
659
737
|
searchQuery,
|
|
660
738
|
onSearchChange,
|
|
661
739
|
header,
|
|
@@ -664,20 +742,24 @@ var Sidebar = ({
|
|
|
664
742
|
}) => {
|
|
665
743
|
const { theme } = uiFeedback.useUi();
|
|
666
744
|
const colors = theme.colors;
|
|
667
|
-
const
|
|
745
|
+
const { width } = reactNative.useWindowDimensions();
|
|
746
|
+
const [internalQuery, setInternalQuery] = React.useState("");
|
|
668
747
|
const controlled = searchQuery !== void 0;
|
|
669
748
|
const query = controlled ? searchQuery : internalQuery;
|
|
670
|
-
const setQuery =
|
|
749
|
+
const setQuery = React.useCallback(
|
|
671
750
|
(next) => {
|
|
672
751
|
if (!controlled) setInternalQuery(next);
|
|
673
752
|
onSearchChange?.(next);
|
|
674
753
|
},
|
|
675
754
|
[controlled, onSearchChange]
|
|
676
755
|
);
|
|
677
|
-
const clearQuery =
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const
|
|
756
|
+
const clearQuery = React.useCallback(() => setQuery(""), [setQuery]);
|
|
757
|
+
const affordance = resolveSearchAffordance(width);
|
|
758
|
+
const isInlineAffordance = affordance === "inline";
|
|
759
|
+
const showInlineSearch = enableInlineSearch && search !== void 0 && isInlineAffordance;
|
|
760
|
+
const showPaletteTrigger = paletteTrigger !== void 0 && !isInlineAffordance;
|
|
761
|
+
const visibleItems = showInlineSearch ? filterNavItems(items, query) : items;
|
|
762
|
+
const showEmpty = showInlineSearch && isFilterActive(query) && visibleItems.length === 0;
|
|
681
763
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
682
764
|
reactNative.View,
|
|
683
765
|
{
|
|
@@ -691,7 +773,7 @@ var Sidebar = ({
|
|
|
691
773
|
],
|
|
692
774
|
children: [
|
|
693
775
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityRole: "header", style: [navStyles.sidebarTitle, { color: colors.text }], children: title }),
|
|
694
|
-
|
|
776
|
+
showInlineSearch && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
695
777
|
SidebarSearch,
|
|
696
778
|
{
|
|
697
779
|
labels: search,
|
|
@@ -701,6 +783,16 @@ var Sidebar = ({
|
|
|
701
783
|
onClear: clearQuery
|
|
702
784
|
}
|
|
703
785
|
) : null,
|
|
786
|
+
showPaletteTrigger && paletteTrigger !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navStyles.sidebarTriggerSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
787
|
+
CommandPaletteTrigger,
|
|
788
|
+
{
|
|
789
|
+
hint: paletteTrigger.hint,
|
|
790
|
+
label: paletteTrigger.label,
|
|
791
|
+
shortcut: paletteTrigger.shortcut,
|
|
792
|
+
testID: paletteTrigger.testID ?? DEFAULT_TRIGGER_TEST_ID,
|
|
793
|
+
onPress: paletteTrigger.onOpen
|
|
794
|
+
}
|
|
795
|
+
) }) : null,
|
|
704
796
|
renderTextSlot(header, { color: colors.text }),
|
|
705
797
|
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(
|
|
706
798
|
NavExpandableItem,
|
|
@@ -722,12 +814,6 @@ var Sidebar = ({
|
|
|
722
814
|
);
|
|
723
815
|
};
|
|
724
816
|
|
|
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
|
-
|
|
731
817
|
// src/constants.ts
|
|
732
818
|
var NAV_TEST_IDS = {
|
|
733
819
|
/** displayName line in the rich account header (tappable when `onAccount` set). */
|
|
@@ -775,7 +861,7 @@ var FocusableTouchable = ({
|
|
|
775
861
|
testID,
|
|
776
862
|
children
|
|
777
863
|
}) => {
|
|
778
|
-
const [focused, setFocused] =
|
|
864
|
+
const [focused, setFocused] = React.useState(false);
|
|
779
865
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
780
866
|
reactNative.Pressable,
|
|
781
867
|
{
|
|
@@ -928,8 +1014,8 @@ var NavBarLink = ({
|
|
|
928
1014
|
navigateHint,
|
|
929
1015
|
onPress
|
|
930
1016
|
}) => {
|
|
931
|
-
const [hovered, setHovered] =
|
|
932
|
-
const [focused, setFocused] =
|
|
1017
|
+
const [hovered, setHovered] = React.useState(false);
|
|
1018
|
+
const [focused, setFocused] = React.useState(false);
|
|
933
1019
|
const isHovered = hovered && !isActive;
|
|
934
1020
|
const textColor = isActive ? TEXT_ON_PRIMARY2 : isHovered ? colors.hoverText : colors.rest;
|
|
935
1021
|
const pillStyle = isActive ? { backgroundColor: colors.activeBg } : isHovered ? { backgroundColor: colors.hoverBg } : void 0;
|
|
@@ -973,12 +1059,12 @@ var NavOverflowMenu = ({
|
|
|
973
1059
|
testID,
|
|
974
1060
|
variant
|
|
975
1061
|
}) => {
|
|
976
|
-
const options =
|
|
977
|
-
const activeRoute =
|
|
1062
|
+
const options = React.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
|
|
1063
|
+
const activeRoute = React.useMemo(
|
|
978
1064
|
() => items.find((item) => isRouteActive(pathname, item.route))?.route ?? NO_ACTIVE_ROUTE,
|
|
979
1065
|
[items, pathname]
|
|
980
1066
|
);
|
|
981
|
-
const optionTestID =
|
|
1067
|
+
const optionTestID = React.useMemo(() => {
|
|
982
1068
|
const byRoute = new Map(items.map((item) => [item.route, item.testID ?? item.key]));
|
|
983
1069
|
return (route) => byRoute.get(route) ?? `${testID}-option-${route}`;
|
|
984
1070
|
}, [items, testID]);
|
|
@@ -1035,13 +1121,13 @@ function resizeWidthBuffer(previous, count) {
|
|
|
1035
1121
|
return next;
|
|
1036
1122
|
}
|
|
1037
1123
|
function useNavOverflow(itemCount, gap) {
|
|
1038
|
-
const [availableWidth, setAvailableWidthState] =
|
|
1039
|
-
const [moreWidth, setMoreWidthState] =
|
|
1040
|
-
const [itemWidths, setItemWidths] =
|
|
1041
|
-
|
|
1124
|
+
const [availableWidth, setAvailableWidthState] = React.useState(0);
|
|
1125
|
+
const [moreWidth, setMoreWidthState] = React.useState(0);
|
|
1126
|
+
const [itemWidths, setItemWidths] = React.useState(() => new Array(itemCount).fill(0));
|
|
1127
|
+
React.useEffect(() => {
|
|
1042
1128
|
setItemWidths((previous) => previous.length === itemCount ? previous : resizeWidthBuffer(previous, itemCount));
|
|
1043
1129
|
}, [itemCount]);
|
|
1044
|
-
const setItemWidth =
|
|
1130
|
+
const setItemWidth = React.useCallback((index, width) => {
|
|
1045
1131
|
setItemWidths((previous) => {
|
|
1046
1132
|
if (index < 0 || index >= previous.length || previous[index] === width) return previous;
|
|
1047
1133
|
const next = previous.slice();
|
|
@@ -1049,15 +1135,15 @@ function useNavOverflow(itemCount, gap) {
|
|
|
1049
1135
|
return next;
|
|
1050
1136
|
});
|
|
1051
1137
|
}, []);
|
|
1052
|
-
const setAvailableWidth =
|
|
1138
|
+
const setAvailableWidth = React.useCallback(
|
|
1053
1139
|
(width) => setAvailableWidthState((previous) => previous === width ? previous : width),
|
|
1054
1140
|
[]
|
|
1055
1141
|
);
|
|
1056
|
-
const setMoreWidth =
|
|
1142
|
+
const setMoreWidth = React.useCallback(
|
|
1057
1143
|
(width) => setMoreWidthState((previous) => previous === width ? previous : width),
|
|
1058
1144
|
[]
|
|
1059
1145
|
);
|
|
1060
|
-
const visibleCount =
|
|
1146
|
+
const visibleCount = React.useMemo(
|
|
1061
1147
|
() => computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }),
|
|
1062
1148
|
[availableWidth, itemWidths, moreWidth, gap]
|
|
1063
1149
|
);
|
|
@@ -1091,24 +1177,24 @@ var NavBarInner = ({
|
|
|
1091
1177
|
const primaryColor = theme.palette.primary["500"];
|
|
1092
1178
|
const { width } = reactNative.useWindowDimensions();
|
|
1093
1179
|
const reducedMotion = useReducedMotion();
|
|
1094
|
-
const [open, setOpen] =
|
|
1095
|
-
const [toggleFocused, setToggleFocused] =
|
|
1180
|
+
const [open, setOpen] = React.useState(false);
|
|
1181
|
+
const [toggleFocused, setToggleFocused] = React.useState(false);
|
|
1096
1182
|
const { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth } = useNavOverflow(items.length, NAV_LINK_GAP);
|
|
1097
1183
|
const collapsed = width < collapseBelow;
|
|
1098
1184
|
const showLinks = !collapsed || open;
|
|
1099
|
-
const innerCapStyle =
|
|
1185
|
+
const innerCapStyle = React.useMemo(
|
|
1100
1186
|
() => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
|
|
1101
1187
|
[contentMaxWidth]
|
|
1102
1188
|
);
|
|
1103
|
-
const toggleMenu =
|
|
1104
|
-
const handlePress =
|
|
1189
|
+
const toggleMenu = React.useCallback(() => setOpen((v) => !v), []);
|
|
1190
|
+
const handlePress = React.useCallback(
|
|
1105
1191
|
(route) => {
|
|
1106
1192
|
setOpen(false);
|
|
1107
1193
|
onNavigate(route);
|
|
1108
1194
|
},
|
|
1109
1195
|
[onNavigate]
|
|
1110
1196
|
);
|
|
1111
|
-
const linkColors =
|
|
1197
|
+
const linkColors = React.useMemo(
|
|
1112
1198
|
() => ({
|
|
1113
1199
|
rest: colors.textSecondary,
|
|
1114
1200
|
hoverText: colors.text,
|
|
@@ -1163,7 +1249,7 @@ var NavBarInner = ({
|
|
|
1163
1249
|
}
|
|
1164
1250
|
);
|
|
1165
1251
|
if (collapsed) {
|
|
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(
|
|
1252
|
+
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(React__default.default.Fragment, { children: renderLink(item) }, item.key)) });
|
|
1167
1253
|
}
|
|
1168
1254
|
const visibleItems = items.slice(0, visibleCount);
|
|
1169
1255
|
const overflowItems = items.slice(visibleCount);
|
|
@@ -1229,7 +1315,7 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
1229
1315
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
1230
1316
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
1231
1317
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
1232
|
-
var
|
|
1318
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
1233
1319
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
1234
1320
|
card: {
|
|
1235
1321
|
padding: CARD_PADDING,
|
|
@@ -1246,9 +1332,9 @@ function MessageCard({
|
|
|
1246
1332
|
}) {
|
|
1247
1333
|
const { theme } = uiFeedback.useUi();
|
|
1248
1334
|
const colors = theme.colors;
|
|
1249
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
1250
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1251
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1335
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
|
|
1336
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
1337
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
1252
1338
|
] });
|
|
1253
1339
|
}
|
|
1254
1340
|
function useContentBody(state, children, testID) {
|
|
@@ -1260,7 +1346,7 @@ function useContentBody(state, children, testID) {
|
|
|
1260
1346
|
if (state?.error)
|
|
1261
1347
|
return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
1262
1348
|
if (state?.loading === true)
|
|
1263
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1349
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.centerFill, testID: `${testID}${APP_SHELL_SUFFIX.loading}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
|
|
1264
1350
|
return children;
|
|
1265
1351
|
}
|
|
1266
1352
|
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
@@ -1284,7 +1370,7 @@ var DEFAULT_DRAWER_LABELS = {
|
|
|
1284
1370
|
closeLabel: "Close menu",
|
|
1285
1371
|
closeHint: "Close the navigation menu"
|
|
1286
1372
|
};
|
|
1287
|
-
var
|
|
1373
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
1288
1374
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
1289
1375
|
// The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
|
|
1290
1376
|
// (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
|
|
@@ -1319,10 +1405,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
|
1319
1405
|
accessibilityHint: hint,
|
|
1320
1406
|
accessibilityLabel: label,
|
|
1321
1407
|
ringColor: theme.palette.primary["500"],
|
|
1322
|
-
style:
|
|
1408
|
+
style: styles4.menuToggle,
|
|
1323
1409
|
testID,
|
|
1324
1410
|
onPress: onOpen,
|
|
1325
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1411
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
1326
1412
|
}
|
|
1327
1413
|
);
|
|
1328
1414
|
};
|
|
@@ -1334,8 +1420,8 @@ var MobileDrawer = ({
|
|
|
1334
1420
|
scrimTestID
|
|
1335
1421
|
}) => {
|
|
1336
1422
|
const { theme } = uiFeedback.useUi();
|
|
1337
|
-
const panelRef =
|
|
1338
|
-
|
|
1423
|
+
const panelRef = React.useRef(null);
|
|
1424
|
+
React.useEffect(() => {
|
|
1339
1425
|
const node = panelRef.current;
|
|
1340
1426
|
if (node === null || typeof node.addEventListener !== "function") return void 0;
|
|
1341
1427
|
const handleClick = (event) => {
|
|
@@ -1348,14 +1434,14 @@ var MobileDrawer = ({
|
|
|
1348
1434
|
node.addEventListener("click", handleClick);
|
|
1349
1435
|
return () => node.removeEventListener("click", handleClick);
|
|
1350
1436
|
}, [onClose]);
|
|
1351
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1437
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
|
|
1352
1438
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1353
1439
|
reactNative.Pressable,
|
|
1354
1440
|
{
|
|
1355
1441
|
accessibilityHint: labels.closeHint,
|
|
1356
1442
|
accessibilityLabel: labels.closeLabel,
|
|
1357
1443
|
accessibilityRole: "button",
|
|
1358
|
-
style:
|
|
1444
|
+
style: styles4.scrim,
|
|
1359
1445
|
testID: scrimTestID,
|
|
1360
1446
|
onPress: onClose
|
|
1361
1447
|
}
|
|
@@ -1366,7 +1452,7 @@ var MobileDrawer = ({
|
|
|
1366
1452
|
ref: panelRef,
|
|
1367
1453
|
"aria-modal": true,
|
|
1368
1454
|
role: "dialog",
|
|
1369
|
-
style: [
|
|
1455
|
+
style: [styles4.panel, { backgroundColor: theme.colors.surface }],
|
|
1370
1456
|
testID: drawerTestID,
|
|
1371
1457
|
children: renderTextSlot(sidebar, { color: theme.colors.text })
|
|
1372
1458
|
}
|
|
@@ -1397,7 +1483,7 @@ function useContentMaxWidth(width) {
|
|
|
1397
1483
|
return resolveContentMaxWidth(width, viewport);
|
|
1398
1484
|
}
|
|
1399
1485
|
var DEFAULT_CONTENT_PADDING = 24;
|
|
1400
|
-
var
|
|
1486
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
1401
1487
|
root: { flex: 1 },
|
|
1402
1488
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
1403
1489
|
scroll: { flex: 1 },
|
|
@@ -1442,32 +1528,32 @@ var AppShell = ({
|
|
|
1442
1528
|
range: railRange
|
|
1443
1529
|
});
|
|
1444
1530
|
const useDrawer = railMode === "drawer";
|
|
1445
|
-
const [drawerOpen, setDrawerOpen] =
|
|
1446
|
-
const openDrawer =
|
|
1447
|
-
const closeDrawer =
|
|
1448
|
-
|
|
1531
|
+
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
|
1532
|
+
const openDrawer = React.useCallback(() => setDrawerOpen(true), []);
|
|
1533
|
+
const closeDrawer = React.useCallback(() => setDrawerOpen(false), []);
|
|
1534
|
+
React.useEffect(() => {
|
|
1449
1535
|
if (!useDrawer && drawerOpen) setDrawerOpen(false);
|
|
1450
1536
|
}, [useDrawer, drawerOpen]);
|
|
1451
1537
|
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
1452
1538
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
1453
|
-
|
|
1539
|
+
React.useEffect(() => {
|
|
1454
1540
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
1455
1541
|
}, [isUnauthenticated, gate]);
|
|
1456
1542
|
if (gate?.pending === true)
|
|
1457
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
1543
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.root, styles5.centerFill, { backgroundColor: theme.colors.background }], testID: `${testID}${APP_SHELL_SUFFIX.pending}`, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" }) });
|
|
1458
1544
|
if (isUnauthenticated) return null;
|
|
1459
|
-
const columnStyle = maxWidth === "full" ?
|
|
1460
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
1545
|
+
const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
|
|
1546
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
1461
1547
|
const scroller = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1462
1548
|
reactNative.ScrollView,
|
|
1463
1549
|
{
|
|
1464
|
-
contentContainerStyle: [
|
|
1465
|
-
style:
|
|
1550
|
+
contentContainerStyle: [styles5.scrollContent, { padding: contentPadding }],
|
|
1551
|
+
style: styles5.scroll,
|
|
1466
1552
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
1467
1553
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
1468
1554
|
}
|
|
1469
1555
|
);
|
|
1470
|
-
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1556
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
1471
1557
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1472
1558
|
MenuToggle,
|
|
1473
1559
|
{
|
|
@@ -1477,14 +1563,14 @@ var AppShell = ({
|
|
|
1477
1563
|
onOpen: openDrawer
|
|
1478
1564
|
}
|
|
1479
1565
|
),
|
|
1480
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1566
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
|
|
1481
1567
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
|
|
1482
1568
|
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;
|
|
1483
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
1569
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles5.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
1484
1570
|
headerRegion,
|
|
1485
1571
|
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,
|
|
1486
1572
|
banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
|
|
1487
|
-
railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1573
|
+
railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.bodyRow, children: [
|
|
1488
1574
|
railRegion,
|
|
1489
1575
|
scroller
|
|
1490
1576
|
] }) : scroller,
|
|
@@ -1607,6 +1693,7 @@ var NavShell = ({
|
|
|
1607
1693
|
header: sideRail.header,
|
|
1608
1694
|
items: sideRail.items,
|
|
1609
1695
|
navigateHint,
|
|
1696
|
+
paletteTrigger: sideRail.paletteTrigger,
|
|
1610
1697
|
pathname,
|
|
1611
1698
|
regionLabel,
|
|
1612
1699
|
renderChevron: sideRail.renderChevron,
|
|
@@ -1706,7 +1793,7 @@ var DarkModeControl = ({
|
|
|
1706
1793
|
const primaryColor = theme.palette.primary["500"];
|
|
1707
1794
|
const currentIndex = options.findIndex((option) => option.value === value);
|
|
1708
1795
|
const current = currentIndex >= 0 ? options[currentIndex] : options[0];
|
|
1709
|
-
const advance =
|
|
1796
|
+
const advance = React.useCallback(() => {
|
|
1710
1797
|
if (options.length === 0) return;
|
|
1711
1798
|
const from = currentIndex >= 0 ? currentIndex : 0;
|
|
1712
1799
|
const next = options[(from + 1) % options.length];
|
|
@@ -1782,7 +1869,7 @@ var KEY_DOWN = "ArrowDown";
|
|
|
1782
1869
|
var KEY_UP = "ArrowUp";
|
|
1783
1870
|
var KEY_ENTER = "Enter";
|
|
1784
1871
|
var KEY_ESCAPE = "Escape";
|
|
1785
|
-
var
|
|
1872
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
1786
1873
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
|
|
1787
1874
|
scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
|
|
1788
1875
|
panel: {
|
|
@@ -1811,14 +1898,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
|
|
|
1811
1898
|
accessibilityLabel: item.label,
|
|
1812
1899
|
accessibilityRole: "button",
|
|
1813
1900
|
accessibilityState: { selected },
|
|
1814
|
-
style: [
|
|
1901
|
+
style: [styles6.row, selected ? { backgroundColor: colors.border } : void 0],
|
|
1815
1902
|
testID: item.testID ?? item.key,
|
|
1816
1903
|
onPress: () => onChoose(item),
|
|
1817
1904
|
children: [
|
|
1818
1905
|
typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
|
|
1819
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1820
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1821
|
-
item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1906
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.rowText, children: [
|
|
1907
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowLabel, { color: colors.text }], children: item.label }),
|
|
1908
|
+
item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
|
|
1822
1909
|
] })
|
|
1823
1910
|
]
|
|
1824
1911
|
}
|
|
@@ -1827,27 +1914,27 @@ var PaletteRow = ({ item, selected, onChoose }) => {
|
|
|
1827
1914
|
var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
1828
1915
|
const { theme } = uiFeedback.useUi();
|
|
1829
1916
|
const colors = theme.colors;
|
|
1830
|
-
const [query, setQuery] =
|
|
1831
|
-
const [selected, setSelected] =
|
|
1832
|
-
const inputRef =
|
|
1833
|
-
const results =
|
|
1834
|
-
|
|
1917
|
+
const [query, setQuery] = React.useState("");
|
|
1918
|
+
const [selected, setSelected] = React.useState(0);
|
|
1919
|
+
const inputRef = React.useRef(null);
|
|
1920
|
+
const results = React.useMemo(() => filterCommands(items, query), [items, query]);
|
|
1921
|
+
React.useEffect(() => {
|
|
1835
1922
|
if (!open) return;
|
|
1836
1923
|
setQuery("");
|
|
1837
1924
|
setSelected(0);
|
|
1838
1925
|
inputRef.current?.focus();
|
|
1839
1926
|
}, [open]);
|
|
1840
|
-
|
|
1927
|
+
React.useEffect(() => {
|
|
1841
1928
|
setSelected((prev) => Math.min(prev, Math.max(0, results.length - 1)));
|
|
1842
1929
|
}, [results.length]);
|
|
1843
|
-
const choose =
|
|
1930
|
+
const choose = React.useCallback(
|
|
1844
1931
|
(item) => {
|
|
1845
1932
|
item.onSelect();
|
|
1846
1933
|
onClose();
|
|
1847
1934
|
},
|
|
1848
1935
|
[onClose]
|
|
1849
1936
|
);
|
|
1850
|
-
const onKeyPress =
|
|
1937
|
+
const onKeyPress = React.useCallback(
|
|
1851
1938
|
(event) => {
|
|
1852
1939
|
const key = event.nativeEvent.key;
|
|
1853
1940
|
if (key === KEY_DOWN) setSelected((i) => Math.min(i + 1, results.length - 1));
|
|
@@ -1858,14 +1945,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1858
1945
|
[results, selected, choose, onClose]
|
|
1859
1946
|
);
|
|
1860
1947
|
if (!open) return null;
|
|
1861
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1948
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.overlay, children: [
|
|
1862
1949
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1863
1950
|
reactNative.Pressable,
|
|
1864
1951
|
{
|
|
1865
1952
|
accessibilityHint: labels.closeHint,
|
|
1866
1953
|
accessibilityLabel: labels.closeLabel,
|
|
1867
1954
|
accessibilityRole: "button",
|
|
1868
|
-
style:
|
|
1955
|
+
style: styles6.scrim,
|
|
1869
1956
|
testID: `${testID ?? "command-palette"}-scrim`,
|
|
1870
1957
|
onPress: onClose
|
|
1871
1958
|
}
|
|
@@ -1876,7 +1963,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1876
1963
|
"aria-modal": true,
|
|
1877
1964
|
"aria-label": labels.regionLabel,
|
|
1878
1965
|
role: "dialog",
|
|
1879
|
-
style: [
|
|
1966
|
+
style: [styles6.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
|
|
1880
1967
|
testID: testID ?? "command-palette",
|
|
1881
1968
|
children: [
|
|
1882
1969
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1886,71 +1973,22 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1886
1973
|
accessibilityLabel: labels.placeholder,
|
|
1887
1974
|
placeholder: labels.placeholder,
|
|
1888
1975
|
placeholderTextColor: colors.textSecondary,
|
|
1889
|
-
style: [
|
|
1976
|
+
style: [styles6.input, { color: colors.text, borderBottomColor: colors.border }],
|
|
1890
1977
|
testID: `${testID ?? "command-palette"}-input`,
|
|
1891
1978
|
value: query,
|
|
1892
1979
|
onChangeText: setQuery,
|
|
1893
1980
|
onKeyPress
|
|
1894
1981
|
}
|
|
1895
1982
|
),
|
|
1896
|
-
results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1983
|
+
results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles6.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
|
|
1897
1984
|
]
|
|
1898
1985
|
}
|
|
1899
1986
|
)
|
|
1900
1987
|
] });
|
|
1901
1988
|
};
|
|
1902
|
-
var MIN_TARGET = 36;
|
|
1903
|
-
var styles6 = reactNative.StyleSheet.create({
|
|
1904
|
-
trigger: {
|
|
1905
|
-
flexDirection: "row",
|
|
1906
|
-
alignItems: "center",
|
|
1907
|
-
justifyContent: "space-between",
|
|
1908
|
-
columnGap: 8,
|
|
1909
|
-
paddingHorizontal: 12,
|
|
1910
|
-
minHeight: MIN_TARGET,
|
|
1911
|
-
borderWidth: 1,
|
|
1912
|
-
borderRadius: 8
|
|
1913
|
-
},
|
|
1914
|
-
label: { fontSize: 14 },
|
|
1915
|
-
badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
|
|
1916
|
-
badgeText: { fontSize: 11, fontWeight: "700" }
|
|
1917
|
-
});
|
|
1918
|
-
var CommandPaletteTrigger = ({
|
|
1919
|
-
label,
|
|
1920
|
-
hint,
|
|
1921
|
-
shortcut,
|
|
1922
|
-
onPress,
|
|
1923
|
-
testID
|
|
1924
|
-
}) => {
|
|
1925
|
-
const { theme } = uiFeedback.useUi();
|
|
1926
|
-
const colors = theme.colors;
|
|
1927
|
-
const primary = theme.palette.primary["500"];
|
|
1928
|
-
const [focused, setFocused] = React3__default.default.useState(false);
|
|
1929
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1930
|
-
reactNative.Pressable,
|
|
1931
|
-
{
|
|
1932
|
-
accessibilityHint: hint,
|
|
1933
|
-
accessibilityLabel: label,
|
|
1934
|
-
accessibilityRole: "button",
|
|
1935
|
-
style: [
|
|
1936
|
-
styles6.trigger,
|
|
1937
|
-
{ backgroundColor: colors.surface, borderColor: colors.border },
|
|
1938
|
-
focusRingStyle(focused, primary)
|
|
1939
|
-
],
|
|
1940
|
-
testID,
|
|
1941
|
-
onBlur: () => setFocused(false),
|
|
1942
|
-
onFocus: () => setFocused(true),
|
|
1943
|
-
onPress,
|
|
1944
|
-
children: [
|
|
1945
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.label, { color: colors.textSecondary }], children: label }),
|
|
1946
|
-
/* @__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 }) })
|
|
1947
|
-
]
|
|
1948
|
-
}
|
|
1949
|
-
);
|
|
1950
|
-
};
|
|
1951
1989
|
var LAUNCH_KEY = "k";
|
|
1952
1990
|
function useCommandPaletteHotkey(onOpen, enabled = true) {
|
|
1953
|
-
|
|
1991
|
+
React.useEffect(() => {
|
|
1954
1992
|
if (!enabled || typeof document === "undefined") return void 0;
|
|
1955
1993
|
const handler = (event) => {
|
|
1956
1994
|
const isLauncher = event.key.toLowerCase() === LAUNCH_KEY && (event.metaKey || event.ctrlKey);
|