@dloizides/ui-nav 1.16.1 → 1.18.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 +54 -0
- package/dist/index.d.mts +114 -40
- package/dist/index.d.ts +114 -40
- package/dist/index.js +267 -200
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -149
- 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) {
|
|
@@ -47,15 +128,40 @@ function isFilterActive(query) {
|
|
|
47
128
|
}
|
|
48
129
|
|
|
49
130
|
// src/isRouteActive.ts
|
|
50
|
-
function isRouteActive(pathname, route) {
|
|
131
|
+
function isRouteActive(pathname, route, exact = false) {
|
|
132
|
+
if (exact) return pathname === route;
|
|
51
133
|
if (route === "/") return pathname === "/";
|
|
52
134
|
return pathname === route || pathname.startsWith(`${route}/`);
|
|
53
135
|
}
|
|
136
|
+
function leafRoutes(items) {
|
|
137
|
+
return items.flatMap((item) => {
|
|
138
|
+
const children = item.children ?? [];
|
|
139
|
+
const isGroup = children.length > 0;
|
|
140
|
+
return isGroup ? leafRoutes(children) : [{ route: item.route, exact: item.exact === true }];
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function resolveActiveRoute(items, pathname) {
|
|
144
|
+
let winner;
|
|
145
|
+
let winnerLength = -1;
|
|
146
|
+
for (const leaf of leafRoutes(items)) {
|
|
147
|
+
const matches = isRouteActive(pathname, leaf.route, leaf.exact);
|
|
148
|
+
const isLonger = leaf.route.length > winnerLength;
|
|
149
|
+
if (matches && isLonger) {
|
|
150
|
+
winner = leaf.route;
|
|
151
|
+
winnerLength = leaf.route.length;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return winner;
|
|
155
|
+
}
|
|
54
156
|
var ACTIVE_BORDER_RADIUS = 4;
|
|
55
157
|
var ACTIVE_ACCENT_WIDTH = 3;
|
|
56
158
|
var NAV_ROW_RADIUS = 8;
|
|
57
159
|
var ACTIVE_TINT_OPACITY = 0.14;
|
|
58
160
|
var HOVER_TINT_OPACITY = 0.06;
|
|
161
|
+
function rowTintOpacity(isActive, hovered) {
|
|
162
|
+
if (isActive) return ACTIVE_TINT_OPACITY;
|
|
163
|
+
return hovered ? HOVER_TINT_OPACITY : 0;
|
|
164
|
+
}
|
|
59
165
|
var NAV_LINK_GAP = 4;
|
|
60
166
|
var navStyles = reactNative.StyleSheet.create({
|
|
61
167
|
// --- Sidebar ---
|
|
@@ -81,6 +187,12 @@ var navStyles = reactNative.StyleSheet.create({
|
|
|
81
187
|
sidebarSpacer: {
|
|
82
188
|
flex: 1
|
|
83
189
|
},
|
|
190
|
+
// Bottom rhythm under the ⌘K palette trigger (the narrow-viewport search
|
|
191
|
+
// affordance), matching the inline field's own `marginBottom` so swapping
|
|
192
|
+
// affordances by width keeps the same gap above the nav list.
|
|
193
|
+
sidebarTriggerSlot: {
|
|
194
|
+
marginBottom: 12
|
|
195
|
+
},
|
|
84
196
|
// Inline-search no-match text — sits where the nav list would, muted + padded.
|
|
85
197
|
sidebarEmpty: {
|
|
86
198
|
fontSize: 13,
|
|
@@ -343,46 +455,14 @@ var expandableStyles = reactNative.StyleSheet.create({
|
|
|
343
455
|
var BASE_INDENT = 12;
|
|
344
456
|
var NAV_ICON_SIZE = 14;
|
|
345
457
|
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
458
|
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
379
459
|
function getReducedMotionQuery() {
|
|
380
460
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
|
|
381
461
|
return window.matchMedia(REDUCED_MOTION_QUERY);
|
|
382
462
|
}
|
|
383
463
|
function useReducedMotion() {
|
|
384
|
-
const [reduced, setReduced] =
|
|
385
|
-
|
|
464
|
+
const [reduced, setReduced] = React.useState(() => getReducedMotionQuery()?.matches ?? false);
|
|
465
|
+
React.useEffect(() => {
|
|
386
466
|
const mql = getReducedMotionQuery();
|
|
387
467
|
if (mql === void 0 || mql.addEventListener === void 0) return void 0;
|
|
388
468
|
const onChange = () => setReduced(mql.matches);
|
|
@@ -428,9 +508,19 @@ function hasActiveDescendant(item, pathname) {
|
|
|
428
508
|
if (isRouteActive(pathname, item.route)) return true;
|
|
429
509
|
return (item.children ?? []).some((child) => hasActiveDescendant(child, pathname));
|
|
430
510
|
}
|
|
431
|
-
function TintOverlay({
|
|
511
|
+
function TintOverlay({
|
|
512
|
+
opacity,
|
|
513
|
+
color,
|
|
514
|
+
testID
|
|
515
|
+
}) {
|
|
432
516
|
const reducedMotion = useReducedMotion();
|
|
433
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
517
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
518
|
+
reactNative.View,
|
|
519
|
+
{
|
|
520
|
+
style: [expandableStyles.tintOverlay, { backgroundColor: color, opacity }, tintTransitionStyle(reducedMotion)],
|
|
521
|
+
testID
|
|
522
|
+
}
|
|
523
|
+
);
|
|
434
524
|
}
|
|
435
525
|
var NavExpandableItem = ({
|
|
436
526
|
item,
|
|
@@ -440,32 +530,34 @@ var NavExpandableItem = ({
|
|
|
440
530
|
expandHint,
|
|
441
531
|
collapseHint,
|
|
442
532
|
renderChevron,
|
|
533
|
+
activeRoute,
|
|
443
534
|
depth = 0
|
|
444
535
|
}) => {
|
|
445
|
-
const [expanded, setExpanded] =
|
|
446
|
-
const [focused, setFocused] =
|
|
447
|
-
const [hovered, setHovered] =
|
|
536
|
+
const [expanded, setExpanded] = React.useState(() => hasActiveDescendant(item, pathname));
|
|
537
|
+
const [focused, setFocused] = React.useState(false);
|
|
538
|
+
const [hovered, setHovered] = React.useState(false);
|
|
448
539
|
const { theme } = uiFeedback.useUi();
|
|
449
540
|
const colors = theme.colors;
|
|
450
541
|
const primaryColor = theme.palette.primary["500"];
|
|
451
|
-
const toggle =
|
|
452
|
-
const onHoverIn =
|
|
453
|
-
const onHoverOut =
|
|
542
|
+
const toggle = React.useCallback(() => setExpanded((v) => !v), []);
|
|
543
|
+
const onHoverIn = React.useCallback(() => setHovered(true), []);
|
|
544
|
+
const onHoverOut = React.useCallback(() => setHovered(false), []);
|
|
454
545
|
const indent = depth * BASE_INDENT;
|
|
455
546
|
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
|
|
456
|
-
const isActive = isRouteActive(pathname, item.route);
|
|
547
|
+
const isActive = activeRoute !== void 0 ? item.route === activeRoute : isRouteActive(pathname, item.route, item.exact === true);
|
|
457
548
|
const isSectionHeader = hasChildren && depth === 0;
|
|
458
|
-
const activeAccentStyle =
|
|
549
|
+
const activeAccentStyle = React.useMemo(
|
|
459
550
|
() => ({ borderLeftWidth: ACTIVE_ACCENT_WIDTH, borderLeftColor: primaryColor }),
|
|
460
551
|
[primaryColor]
|
|
461
552
|
);
|
|
462
|
-
const paddingStyle =
|
|
463
|
-
const headerPaddingStyle =
|
|
464
|
-
const leafTintOpacity = isActive
|
|
465
|
-
const headerTintOpacity = hovered
|
|
553
|
+
const paddingStyle = React.useMemo(() => ({ paddingLeft: indent + BASE_INDENT }), [indent]);
|
|
554
|
+
const headerPaddingStyle = React.useMemo(() => ({ paddingLeft: indent }), [indent]);
|
|
555
|
+
const leafTintOpacity = rowTintOpacity(isActive, hovered);
|
|
556
|
+
const headerTintOpacity = rowTintOpacity(false, hovered);
|
|
557
|
+
const rowTestID = item.testID ?? item.key;
|
|
466
558
|
if (!hasChildren)
|
|
467
559
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
468
|
-
reactNative.
|
|
560
|
+
reactNative.Pressable,
|
|
469
561
|
{
|
|
470
562
|
accessibilityHint: navigateHint(item.label),
|
|
471
563
|
accessibilityLabel: item.label,
|
|
@@ -477,14 +569,14 @@ var NavExpandableItem = ({
|
|
|
477
569
|
isActive ? activeAccentStyle : void 0,
|
|
478
570
|
focusRingStyle(focused, primaryColor)
|
|
479
571
|
],
|
|
480
|
-
testID:
|
|
572
|
+
testID: rowTestID,
|
|
481
573
|
onBlur: () => setFocused(false),
|
|
482
574
|
onFocus: () => setFocused(true),
|
|
483
575
|
onPress: () => onNavigate(item.route),
|
|
484
576
|
...hoverHandlerProps(onHoverIn, onHoverOut),
|
|
485
577
|
...ariaCurrentProps(isActive),
|
|
486
578
|
children: [
|
|
487
|
-
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity }),
|
|
579
|
+
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: leafTintOpacity, testID: `${rowTestID}-tint` }),
|
|
488
580
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.iconWrapper, children: item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) }) : null,
|
|
489
581
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
490
582
|
reactNative.Text,
|
|
@@ -502,7 +594,7 @@ var NavExpandableItem = ({
|
|
|
502
594
|
const chevronColor = colors.textSecondary;
|
|
503
595
|
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { children: [
|
|
504
596
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
505
|
-
reactNative.
|
|
597
|
+
reactNative.Pressable,
|
|
506
598
|
{
|
|
507
599
|
accessibilityHint: expanded ? collapseHint : expandHint,
|
|
508
600
|
accessibilityLabel: item.label,
|
|
@@ -514,14 +606,14 @@ var NavExpandableItem = ({
|
|
|
514
606
|
headerPaddingStyle,
|
|
515
607
|
focusRingStyle(focused, primaryColor)
|
|
516
608
|
],
|
|
517
|
-
testID:
|
|
609
|
+
testID: rowTestID,
|
|
518
610
|
onBlur: () => setFocused(false),
|
|
519
611
|
onFocus: () => setFocused(true),
|
|
520
612
|
onPress: toggle,
|
|
521
613
|
...hoverHandlerProps(onHoverIn, onHoverOut),
|
|
522
614
|
...ariaExpandedProps(expanded),
|
|
523
615
|
children: [
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(TintOverlay, { color: primaryColor, opacity: headerTintOpacity, testID: `${rowTestID}-tint` }),
|
|
525
617
|
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
618
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
527
619
|
reactNative.Text,
|
|
@@ -540,6 +632,7 @@ var NavExpandableItem = ({
|
|
|
540
632
|
/* @__PURE__ */ jsxRuntime.jsx(uiMotion.Collapse, { open: expanded, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: expandableStyles.childrenContainer, children: item.children?.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
541
633
|
NavExpandableItem,
|
|
542
634
|
{
|
|
635
|
+
activeRoute,
|
|
543
636
|
collapseHint,
|
|
544
637
|
depth: depth + 1,
|
|
545
638
|
expandHint,
|
|
@@ -553,10 +646,16 @@ var NavExpandableItem = ({
|
|
|
553
646
|
)) }) })
|
|
554
647
|
] });
|
|
555
648
|
};
|
|
649
|
+
|
|
650
|
+
// src/searchAffordance.ts
|
|
651
|
+
var DEFAULT_SEARCH_BREAKPOINT = 768;
|
|
652
|
+
function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
|
|
653
|
+
return viewport >= breakpoint ? "inline" : "palette";
|
|
654
|
+
}
|
|
556
655
|
var FIELD_MIN_HEIGHT = 38;
|
|
557
656
|
var CLEAR_GLYPH = "\u2715";
|
|
558
657
|
var CLEAR_MIN_TARGET = 28;
|
|
559
|
-
var
|
|
658
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
560
659
|
wrap: {
|
|
561
660
|
flexDirection: "row",
|
|
562
661
|
alignItems: "center",
|
|
@@ -591,13 +690,13 @@ var SidebarSearch = ({
|
|
|
591
690
|
const { theme } = uiFeedback.useUi();
|
|
592
691
|
const colors = theme.colors;
|
|
593
692
|
const primary = theme.palette.primary["500"];
|
|
594
|
-
const [focused, setFocused] =
|
|
693
|
+
const [focused, setFocused] = React.useState(false);
|
|
595
694
|
const hasQuery = value.length > 0;
|
|
596
695
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
597
696
|
reactNative.View,
|
|
598
697
|
{
|
|
599
698
|
style: [
|
|
600
|
-
|
|
699
|
+
styles2.wrap,
|
|
601
700
|
{ backgroundColor: colors.surface, borderColor: focused ? primary : colors.border },
|
|
602
701
|
focusRingStyle(focused, primary)
|
|
603
702
|
],
|
|
@@ -609,7 +708,7 @@ var SidebarSearch = ({
|
|
|
609
708
|
accessibilityLabel: labels.placeholder,
|
|
610
709
|
placeholder: labels.placeholder,
|
|
611
710
|
placeholderTextColor: colors.textSecondary,
|
|
612
|
-
style: [
|
|
711
|
+
style: [styles2.input, { color: colors.text }],
|
|
613
712
|
testID,
|
|
614
713
|
value,
|
|
615
714
|
onBlur: () => setFocused(false),
|
|
@@ -623,10 +722,10 @@ var SidebarSearch = ({
|
|
|
623
722
|
accessibilityHint: labels.clearHint,
|
|
624
723
|
accessibilityLabel: labels.clearLabel,
|
|
625
724
|
accessibilityRole: "button",
|
|
626
|
-
style:
|
|
725
|
+
style: styles2.clear,
|
|
627
726
|
testID: `${testID}-clear`,
|
|
628
727
|
onPress: onClear,
|
|
629
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
728
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.clearGlyph, { color: colors.textSecondary }], children: CLEAR_GLYPH })
|
|
630
729
|
}
|
|
631
730
|
) : null
|
|
632
731
|
]
|
|
@@ -639,11 +738,12 @@ function isBareText(node) {
|
|
|
639
738
|
function renderTextSlot(node, style) {
|
|
640
739
|
if (node === void 0 || node === null) return node;
|
|
641
740
|
if (isBareText(node)) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: node });
|
|
642
|
-
return
|
|
741
|
+
return React__default.default.Children.map(
|
|
643
742
|
node,
|
|
644
743
|
(child) => isBareText(child) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style, children: child }) : child
|
|
645
744
|
);
|
|
646
745
|
}
|
|
746
|
+
var DEFAULT_TRIGGER_TEST_ID = "sidebar-command-palette-trigger";
|
|
647
747
|
var Sidebar = ({
|
|
648
748
|
items,
|
|
649
749
|
pathname,
|
|
@@ -656,6 +756,7 @@ var Sidebar = ({
|
|
|
656
756
|
renderChevron,
|
|
657
757
|
enableInlineSearch = false,
|
|
658
758
|
search,
|
|
759
|
+
paletteTrigger,
|
|
659
760
|
searchQuery,
|
|
660
761
|
onSearchChange,
|
|
661
762
|
header,
|
|
@@ -664,20 +765,25 @@ var Sidebar = ({
|
|
|
664
765
|
}) => {
|
|
665
766
|
const { theme } = uiFeedback.useUi();
|
|
666
767
|
const colors = theme.colors;
|
|
667
|
-
const
|
|
768
|
+
const { width } = reactNative.useWindowDimensions();
|
|
769
|
+
const [internalQuery, setInternalQuery] = React.useState("");
|
|
668
770
|
const controlled = searchQuery !== void 0;
|
|
669
771
|
const query = controlled ? searchQuery : internalQuery;
|
|
670
|
-
const setQuery =
|
|
772
|
+
const setQuery = React.useCallback(
|
|
671
773
|
(next) => {
|
|
672
774
|
if (!controlled) setInternalQuery(next);
|
|
673
775
|
onSearchChange?.(next);
|
|
674
776
|
},
|
|
675
777
|
[controlled, onSearchChange]
|
|
676
778
|
);
|
|
677
|
-
const clearQuery =
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const
|
|
779
|
+
const clearQuery = React.useCallback(() => setQuery(""), [setQuery]);
|
|
780
|
+
const affordance = resolveSearchAffordance(width);
|
|
781
|
+
const isInlineAffordance = affordance === "inline";
|
|
782
|
+
const showInlineSearch = enableInlineSearch && search !== void 0 && isInlineAffordance;
|
|
783
|
+
const showPaletteTrigger = paletteTrigger !== void 0 && !isInlineAffordance;
|
|
784
|
+
const visibleItems = showInlineSearch ? filterNavItems(items, query) : items;
|
|
785
|
+
const showEmpty = showInlineSearch && isFilterActive(query) && visibleItems.length === 0;
|
|
786
|
+
const activeRoute = resolveActiveRoute(items, pathname);
|
|
681
787
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
682
788
|
reactNative.View,
|
|
683
789
|
{
|
|
@@ -691,7 +797,7 @@ var Sidebar = ({
|
|
|
691
797
|
],
|
|
692
798
|
children: [
|
|
693
799
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityRole: "header", style: [navStyles.sidebarTitle, { color: colors.text }], children: title }),
|
|
694
|
-
|
|
800
|
+
showInlineSearch && search !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
695
801
|
SidebarSearch,
|
|
696
802
|
{
|
|
697
803
|
labels: search,
|
|
@@ -701,10 +807,21 @@ var Sidebar = ({
|
|
|
701
807
|
onClear: clearQuery
|
|
702
808
|
}
|
|
703
809
|
) : null,
|
|
810
|
+
showPaletteTrigger && paletteTrigger !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navStyles.sidebarTriggerSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
811
|
+
CommandPaletteTrigger,
|
|
812
|
+
{
|
|
813
|
+
hint: paletteTrigger.hint,
|
|
814
|
+
label: paletteTrigger.label,
|
|
815
|
+
shortcut: paletteTrigger.shortcut,
|
|
816
|
+
testID: paletteTrigger.testID ?? DEFAULT_TRIGGER_TEST_ID,
|
|
817
|
+
onPress: paletteTrigger.onOpen
|
|
818
|
+
}
|
|
819
|
+
) }) : null,
|
|
704
820
|
renderTextSlot(header, { color: colors.text }),
|
|
705
821
|
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
822
|
NavExpandableItem,
|
|
707
823
|
{
|
|
824
|
+
activeRoute,
|
|
708
825
|
collapseHint,
|
|
709
826
|
expandHint,
|
|
710
827
|
item,
|
|
@@ -722,12 +839,6 @@ var Sidebar = ({
|
|
|
722
839
|
);
|
|
723
840
|
};
|
|
724
841
|
|
|
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
842
|
// src/constants.ts
|
|
732
843
|
var NAV_TEST_IDS = {
|
|
733
844
|
/** displayName line in the rich account header (tappable when `onAccount` set). */
|
|
@@ -775,7 +886,7 @@ var FocusableTouchable = ({
|
|
|
775
886
|
testID,
|
|
776
887
|
children
|
|
777
888
|
}) => {
|
|
778
|
-
const [focused, setFocused] =
|
|
889
|
+
const [focused, setFocused] = React.useState(false);
|
|
779
890
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
780
891
|
reactNative.Pressable,
|
|
781
892
|
{
|
|
@@ -928,8 +1039,8 @@ var NavBarLink = ({
|
|
|
928
1039
|
navigateHint,
|
|
929
1040
|
onPress
|
|
930
1041
|
}) => {
|
|
931
|
-
const [hovered, setHovered] =
|
|
932
|
-
const [focused, setFocused] =
|
|
1042
|
+
const [hovered, setHovered] = React.useState(false);
|
|
1043
|
+
const [focused, setFocused] = React.useState(false);
|
|
933
1044
|
const isHovered = hovered && !isActive;
|
|
934
1045
|
const textColor = isActive ? TEXT_ON_PRIMARY2 : isHovered ? colors.hoverText : colors.rest;
|
|
935
1046
|
const pillStyle = isActive ? { backgroundColor: colors.activeBg } : isHovered ? { backgroundColor: colors.hoverBg } : void 0;
|
|
@@ -973,12 +1084,12 @@ var NavOverflowMenu = ({
|
|
|
973
1084
|
testID,
|
|
974
1085
|
variant
|
|
975
1086
|
}) => {
|
|
976
|
-
const options =
|
|
977
|
-
const activeRoute =
|
|
978
|
-
() => items
|
|
1087
|
+
const options = React.useMemo(() => items.map((item) => ({ label: item.label, value: item.route })), [items]);
|
|
1088
|
+
const activeRoute = React.useMemo(
|
|
1089
|
+
() => resolveActiveRoute(items, pathname) ?? NO_ACTIVE_ROUTE,
|
|
979
1090
|
[items, pathname]
|
|
980
1091
|
);
|
|
981
|
-
const optionTestID =
|
|
1092
|
+
const optionTestID = React.useMemo(() => {
|
|
982
1093
|
const byRoute = new Map(items.map((item) => [item.route, item.testID ?? item.key]));
|
|
983
1094
|
return (route) => byRoute.get(route) ?? `${testID}-option-${route}`;
|
|
984
1095
|
}, [items, testID]);
|
|
@@ -1035,13 +1146,13 @@ function resizeWidthBuffer(previous, count) {
|
|
|
1035
1146
|
return next;
|
|
1036
1147
|
}
|
|
1037
1148
|
function useNavOverflow(itemCount, gap) {
|
|
1038
|
-
const [availableWidth, setAvailableWidthState] =
|
|
1039
|
-
const [moreWidth, setMoreWidthState] =
|
|
1040
|
-
const [itemWidths, setItemWidths] =
|
|
1041
|
-
|
|
1149
|
+
const [availableWidth, setAvailableWidthState] = React.useState(0);
|
|
1150
|
+
const [moreWidth, setMoreWidthState] = React.useState(0);
|
|
1151
|
+
const [itemWidths, setItemWidths] = React.useState(() => new Array(itemCount).fill(0));
|
|
1152
|
+
React.useEffect(() => {
|
|
1042
1153
|
setItemWidths((previous) => previous.length === itemCount ? previous : resizeWidthBuffer(previous, itemCount));
|
|
1043
1154
|
}, [itemCount]);
|
|
1044
|
-
const setItemWidth =
|
|
1155
|
+
const setItemWidth = React.useCallback((index, width) => {
|
|
1045
1156
|
setItemWidths((previous) => {
|
|
1046
1157
|
if (index < 0 || index >= previous.length || previous[index] === width) return previous;
|
|
1047
1158
|
const next = previous.slice();
|
|
@@ -1049,15 +1160,15 @@ function useNavOverflow(itemCount, gap) {
|
|
|
1049
1160
|
return next;
|
|
1050
1161
|
});
|
|
1051
1162
|
}, []);
|
|
1052
|
-
const setAvailableWidth =
|
|
1163
|
+
const setAvailableWidth = React.useCallback(
|
|
1053
1164
|
(width) => setAvailableWidthState((previous) => previous === width ? previous : width),
|
|
1054
1165
|
[]
|
|
1055
1166
|
);
|
|
1056
|
-
const setMoreWidth =
|
|
1167
|
+
const setMoreWidth = React.useCallback(
|
|
1057
1168
|
(width) => setMoreWidthState((previous) => previous === width ? previous : width),
|
|
1058
1169
|
[]
|
|
1059
1170
|
);
|
|
1060
|
-
const visibleCount =
|
|
1171
|
+
const visibleCount = React.useMemo(
|
|
1061
1172
|
() => computeVisibleCount({ availableWidth, itemWidths, moreWidth, gap }),
|
|
1062
1173
|
[availableWidth, itemWidths, moreWidth, gap]
|
|
1063
1174
|
);
|
|
@@ -1091,24 +1202,25 @@ var NavBarInner = ({
|
|
|
1091
1202
|
const primaryColor = theme.palette.primary["500"];
|
|
1092
1203
|
const { width } = reactNative.useWindowDimensions();
|
|
1093
1204
|
const reducedMotion = useReducedMotion();
|
|
1094
|
-
const [open, setOpen] =
|
|
1095
|
-
const [toggleFocused, setToggleFocused] =
|
|
1205
|
+
const [open, setOpen] = React.useState(false);
|
|
1206
|
+
const [toggleFocused, setToggleFocused] = React.useState(false);
|
|
1096
1207
|
const { visibleCount, setAvailableWidth, setItemWidth, setMoreWidth } = useNavOverflow(items.length, NAV_LINK_GAP);
|
|
1097
1208
|
const collapsed = width < collapseBelow;
|
|
1098
1209
|
const showLinks = !collapsed || open;
|
|
1099
|
-
const innerCapStyle =
|
|
1210
|
+
const innerCapStyle = React.useMemo(
|
|
1100
1211
|
() => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
|
|
1101
1212
|
[contentMaxWidth]
|
|
1102
1213
|
);
|
|
1103
|
-
const toggleMenu =
|
|
1104
|
-
const handlePress =
|
|
1214
|
+
const toggleMenu = React.useCallback(() => setOpen((v) => !v), []);
|
|
1215
|
+
const handlePress = React.useCallback(
|
|
1105
1216
|
(route) => {
|
|
1106
1217
|
setOpen(false);
|
|
1107
1218
|
onNavigate(route);
|
|
1108
1219
|
},
|
|
1109
1220
|
[onNavigate]
|
|
1110
1221
|
);
|
|
1111
|
-
const
|
|
1222
|
+
const activeRoute = resolveActiveRoute(items, pathname);
|
|
1223
|
+
const linkColors = React.useMemo(
|
|
1112
1224
|
() => ({
|
|
1113
1225
|
rest: colors.textSecondary,
|
|
1114
1226
|
hoverText: colors.text,
|
|
@@ -1155,7 +1267,7 @@ var NavBarInner = ({
|
|
|
1155
1267
|
collapsed,
|
|
1156
1268
|
colors: linkColors,
|
|
1157
1269
|
iconSize: NAV_ICON_SIZE,
|
|
1158
|
-
isActive:
|
|
1270
|
+
isActive: item.route === activeRoute,
|
|
1159
1271
|
item,
|
|
1160
1272
|
navigateHint,
|
|
1161
1273
|
reducedMotion,
|
|
@@ -1163,7 +1275,7 @@ var NavBarInner = ({
|
|
|
1163
1275
|
}
|
|
1164
1276
|
);
|
|
1165
1277
|
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(
|
|
1278
|
+
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
1279
|
}
|
|
1168
1280
|
const visibleItems = items.slice(0, visibleCount);
|
|
1169
1281
|
const overflowItems = items.slice(visibleCount);
|
|
@@ -1229,7 +1341,7 @@ var CARD_BORDER_WIDTH = 1;
|
|
|
1229
1341
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
1230
1342
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
1231
1343
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
1232
|
-
var
|
|
1344
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
1233
1345
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
1234
1346
|
card: {
|
|
1235
1347
|
padding: CARD_PADDING,
|
|
@@ -1246,9 +1358,9 @@ function MessageCard({
|
|
|
1246
1358
|
}) {
|
|
1247
1359
|
const { theme } = uiFeedback.useUi();
|
|
1248
1360
|
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: [
|
|
1361
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
|
|
1362
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
1363
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
1252
1364
|
] });
|
|
1253
1365
|
}
|
|
1254
1366
|
function useContentBody(state, children, testID) {
|
|
@@ -1260,7 +1372,7 @@ function useContentBody(state, children, testID) {
|
|
|
1260
1372
|
if (state?.error)
|
|
1261
1373
|
return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
1262
1374
|
if (state?.loading === true)
|
|
1263
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1375
|
+
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
1376
|
return children;
|
|
1265
1377
|
}
|
|
1266
1378
|
var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
|
|
@@ -1284,7 +1396,7 @@ var DEFAULT_DRAWER_LABELS = {
|
|
|
1284
1396
|
closeLabel: "Close menu",
|
|
1285
1397
|
closeHint: "Close the navigation menu"
|
|
1286
1398
|
};
|
|
1287
|
-
var
|
|
1399
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
1288
1400
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
1289
1401
|
// The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
|
|
1290
1402
|
// (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
|
|
@@ -1319,10 +1431,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
|
1319
1431
|
accessibilityHint: hint,
|
|
1320
1432
|
accessibilityLabel: label,
|
|
1321
1433
|
ringColor: theme.palette.primary["500"],
|
|
1322
|
-
style:
|
|
1434
|
+
style: styles4.menuToggle,
|
|
1323
1435
|
testID,
|
|
1324
1436
|
onPress: onOpen,
|
|
1325
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1437
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
1326
1438
|
}
|
|
1327
1439
|
);
|
|
1328
1440
|
};
|
|
@@ -1334,8 +1446,8 @@ var MobileDrawer = ({
|
|
|
1334
1446
|
scrimTestID
|
|
1335
1447
|
}) => {
|
|
1336
1448
|
const { theme } = uiFeedback.useUi();
|
|
1337
|
-
const panelRef =
|
|
1338
|
-
|
|
1449
|
+
const panelRef = React.useRef(null);
|
|
1450
|
+
React.useEffect(() => {
|
|
1339
1451
|
const node = panelRef.current;
|
|
1340
1452
|
if (node === null || typeof node.addEventListener !== "function") return void 0;
|
|
1341
1453
|
const handleClick = (event) => {
|
|
@@ -1348,14 +1460,14 @@ var MobileDrawer = ({
|
|
|
1348
1460
|
node.addEventListener("click", handleClick);
|
|
1349
1461
|
return () => node.removeEventListener("click", handleClick);
|
|
1350
1462
|
}, [onClose]);
|
|
1351
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
|
|
1352
1464
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1353
1465
|
reactNative.Pressable,
|
|
1354
1466
|
{
|
|
1355
1467
|
accessibilityHint: labels.closeHint,
|
|
1356
1468
|
accessibilityLabel: labels.closeLabel,
|
|
1357
1469
|
accessibilityRole: "button",
|
|
1358
|
-
style:
|
|
1470
|
+
style: styles4.scrim,
|
|
1359
1471
|
testID: scrimTestID,
|
|
1360
1472
|
onPress: onClose
|
|
1361
1473
|
}
|
|
@@ -1366,7 +1478,7 @@ var MobileDrawer = ({
|
|
|
1366
1478
|
ref: panelRef,
|
|
1367
1479
|
"aria-modal": true,
|
|
1368
1480
|
role: "dialog",
|
|
1369
|
-
style: [
|
|
1481
|
+
style: [styles4.panel, { backgroundColor: theme.colors.surface }],
|
|
1370
1482
|
testID: drawerTestID,
|
|
1371
1483
|
children: renderTextSlot(sidebar, { color: theme.colors.text })
|
|
1372
1484
|
}
|
|
@@ -1397,7 +1509,7 @@ function useContentMaxWidth(width) {
|
|
|
1397
1509
|
return resolveContentMaxWidth(width, viewport);
|
|
1398
1510
|
}
|
|
1399
1511
|
var DEFAULT_CONTENT_PADDING = 24;
|
|
1400
|
-
var
|
|
1512
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
1401
1513
|
root: { flex: 1 },
|
|
1402
1514
|
centerFill: { flex: 1, justifyContent: "center", alignItems: "center" },
|
|
1403
1515
|
scroll: { flex: 1 },
|
|
@@ -1442,32 +1554,32 @@ var AppShell = ({
|
|
|
1442
1554
|
range: railRange
|
|
1443
1555
|
});
|
|
1444
1556
|
const useDrawer = railMode === "drawer";
|
|
1445
|
-
const [drawerOpen, setDrawerOpen] =
|
|
1446
|
-
const openDrawer =
|
|
1447
|
-
const closeDrawer =
|
|
1448
|
-
|
|
1557
|
+
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
|
1558
|
+
const openDrawer = React.useCallback(() => setDrawerOpen(true), []);
|
|
1559
|
+
const closeDrawer = React.useCallback(() => setDrawerOpen(false), []);
|
|
1560
|
+
React.useEffect(() => {
|
|
1449
1561
|
if (!useDrawer && drawerOpen) setDrawerOpen(false);
|
|
1450
1562
|
}, [useDrawer, drawerOpen]);
|
|
1451
1563
|
const drawerLabels = { ...DEFAULT_DRAWER_LABELS, ...mobileMenu };
|
|
1452
1564
|
const isUnauthenticated = gate !== void 0 && !gate.pending && !gate.authenticated;
|
|
1453
|
-
|
|
1565
|
+
React.useEffect(() => {
|
|
1454
1566
|
if (isUnauthenticated && gate !== void 0) gate.onRedirect();
|
|
1455
1567
|
}, [isUnauthenticated, gate]);
|
|
1456
1568
|
if (gate?.pending === true)
|
|
1457
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
1569
|
+
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
1570
|
if (isUnauthenticated) return null;
|
|
1459
|
-
const columnStyle = maxWidth === "full" ?
|
|
1460
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
1571
|
+
const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
|
|
1572
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
1461
1573
|
const scroller = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1462
1574
|
reactNative.ScrollView,
|
|
1463
1575
|
{
|
|
1464
|
-
contentContainerStyle: [
|
|
1465
|
-
style:
|
|
1576
|
+
contentContainerStyle: [styles5.scrollContent, { padding: contentPadding }],
|
|
1577
|
+
style: styles5.scroll,
|
|
1466
1578
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
1467
1579
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
1468
1580
|
}
|
|
1469
1581
|
);
|
|
1470
|
-
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1582
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
1471
1583
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1472
1584
|
MenuToggle,
|
|
1473
1585
|
{
|
|
@@ -1477,14 +1589,14 @@ var AppShell = ({
|
|
|
1477
1589
|
onOpen: openDrawer
|
|
1478
1590
|
}
|
|
1479
1591
|
),
|
|
1480
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1592
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
|
|
1481
1593
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
|
|
1482
1594
|
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: [
|
|
1595
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles5.root, { backgroundColor: theme.colors.background }], testID, children: [
|
|
1484
1596
|
headerRegion,
|
|
1485
1597
|
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
1598
|
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:
|
|
1599
|
+
railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.bodyRow, children: [
|
|
1488
1600
|
railRegion,
|
|
1489
1601
|
scroller
|
|
1490
1602
|
] }) : scroller,
|
|
@@ -1517,6 +1629,7 @@ var CollapsedRail = ({
|
|
|
1517
1629
|
const { theme } = uiFeedback.useUi();
|
|
1518
1630
|
const colors = theme.colors;
|
|
1519
1631
|
const primaryColor = theme.palette.primary["500"];
|
|
1632
|
+
const activeRoute = resolveActiveRoute(items, pathname);
|
|
1520
1633
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1521
1634
|
reactNative.View,
|
|
1522
1635
|
{
|
|
@@ -1531,7 +1644,7 @@ var CollapsedRail = ({
|
|
|
1531
1644
|
children: [
|
|
1532
1645
|
header !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: collapsedRailStyles.slot, children: renderTextSlot(header, { color: colors.text }) }) : null,
|
|
1533
1646
|
items.map((item) => {
|
|
1534
|
-
const active =
|
|
1647
|
+
const active = item.route === activeRoute;
|
|
1535
1648
|
const iconColor = active ? primaryColor : colors.textSecondary;
|
|
1536
1649
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1537
1650
|
FocusableTouchable,
|
|
@@ -1607,6 +1720,7 @@ var NavShell = ({
|
|
|
1607
1720
|
header: sideRail.header,
|
|
1608
1721
|
items: sideRail.items,
|
|
1609
1722
|
navigateHint,
|
|
1723
|
+
paletteTrigger: sideRail.paletteTrigger,
|
|
1610
1724
|
pathname,
|
|
1611
1725
|
regionLabel,
|
|
1612
1726
|
renderChevron: sideRail.renderChevron,
|
|
@@ -1658,6 +1772,7 @@ var PillNav = ({
|
|
|
1658
1772
|
const colors = theme.colors;
|
|
1659
1773
|
const primaryColor = theme.palette.primary["500"];
|
|
1660
1774
|
if (items.length < minItems) return null;
|
|
1775
|
+
const activeRoute = resolveActiveRoute(items, pathname);
|
|
1661
1776
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1662
1777
|
reactNative.View,
|
|
1663
1778
|
{
|
|
@@ -1666,7 +1781,7 @@ var PillNav = ({
|
|
|
1666
1781
|
role: "navigation",
|
|
1667
1782
|
style: [pillNavStyles.container, containerStyle],
|
|
1668
1783
|
children: items.map((item) => {
|
|
1669
|
-
const active =
|
|
1784
|
+
const active = item.route === activeRoute;
|
|
1670
1785
|
const textColor = active ? TEXT_ON_PRIMARY4 : colors.textSecondary;
|
|
1671
1786
|
const pillStyle = active ? { backgroundColor: primaryColor } : { backgroundColor: colors.surfaceElevated };
|
|
1672
1787
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1706,7 +1821,7 @@ var DarkModeControl = ({
|
|
|
1706
1821
|
const primaryColor = theme.palette.primary["500"];
|
|
1707
1822
|
const currentIndex = options.findIndex((option) => option.value === value);
|
|
1708
1823
|
const current = currentIndex >= 0 ? options[currentIndex] : options[0];
|
|
1709
|
-
const advance =
|
|
1824
|
+
const advance = React.useCallback(() => {
|
|
1710
1825
|
if (options.length === 0) return;
|
|
1711
1826
|
const from = currentIndex >= 0 ? currentIndex : 0;
|
|
1712
1827
|
const next = options[(from + 1) % options.length];
|
|
@@ -1782,7 +1897,7 @@ var KEY_DOWN = "ArrowDown";
|
|
|
1782
1897
|
var KEY_UP = "ArrowUp";
|
|
1783
1898
|
var KEY_ENTER = "Enter";
|
|
1784
1899
|
var KEY_ESCAPE = "Escape";
|
|
1785
|
-
var
|
|
1900
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
1786
1901
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
|
|
1787
1902
|
scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
|
|
1788
1903
|
panel: {
|
|
@@ -1811,14 +1926,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
|
|
|
1811
1926
|
accessibilityLabel: item.label,
|
|
1812
1927
|
accessibilityRole: "button",
|
|
1813
1928
|
accessibilityState: { selected },
|
|
1814
|
-
style: [
|
|
1929
|
+
style: [styles6.row, selected ? { backgroundColor: colors.border } : void 0],
|
|
1815
1930
|
testID: item.testID ?? item.key,
|
|
1816
1931
|
onPress: () => onChoose(item),
|
|
1817
1932
|
children: [
|
|
1818
1933
|
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: [
|
|
1934
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.rowText, children: [
|
|
1935
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowLabel, { color: colors.text }], children: item.label }),
|
|
1936
|
+
item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
|
|
1822
1937
|
] })
|
|
1823
1938
|
]
|
|
1824
1939
|
}
|
|
@@ -1827,27 +1942,27 @@ var PaletteRow = ({ item, selected, onChoose }) => {
|
|
|
1827
1942
|
var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
1828
1943
|
const { theme } = uiFeedback.useUi();
|
|
1829
1944
|
const colors = theme.colors;
|
|
1830
|
-
const [query, setQuery] =
|
|
1831
|
-
const [selected, setSelected] =
|
|
1832
|
-
const inputRef =
|
|
1833
|
-
const results =
|
|
1834
|
-
|
|
1945
|
+
const [query, setQuery] = React.useState("");
|
|
1946
|
+
const [selected, setSelected] = React.useState(0);
|
|
1947
|
+
const inputRef = React.useRef(null);
|
|
1948
|
+
const results = React.useMemo(() => filterCommands(items, query), [items, query]);
|
|
1949
|
+
React.useEffect(() => {
|
|
1835
1950
|
if (!open) return;
|
|
1836
1951
|
setQuery("");
|
|
1837
1952
|
setSelected(0);
|
|
1838
1953
|
inputRef.current?.focus();
|
|
1839
1954
|
}, [open]);
|
|
1840
|
-
|
|
1955
|
+
React.useEffect(() => {
|
|
1841
1956
|
setSelected((prev) => Math.min(prev, Math.max(0, results.length - 1)));
|
|
1842
1957
|
}, [results.length]);
|
|
1843
|
-
const choose =
|
|
1958
|
+
const choose = React.useCallback(
|
|
1844
1959
|
(item) => {
|
|
1845
1960
|
item.onSelect();
|
|
1846
1961
|
onClose();
|
|
1847
1962
|
},
|
|
1848
1963
|
[onClose]
|
|
1849
1964
|
);
|
|
1850
|
-
const onKeyPress =
|
|
1965
|
+
const onKeyPress = React.useCallback(
|
|
1851
1966
|
(event) => {
|
|
1852
1967
|
const key = event.nativeEvent.key;
|
|
1853
1968
|
if (key === KEY_DOWN) setSelected((i) => Math.min(i + 1, results.length - 1));
|
|
@@ -1858,14 +1973,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1858
1973
|
[results, selected, choose, onClose]
|
|
1859
1974
|
);
|
|
1860
1975
|
if (!open) return null;
|
|
1861
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1976
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.overlay, children: [
|
|
1862
1977
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1863
1978
|
reactNative.Pressable,
|
|
1864
1979
|
{
|
|
1865
1980
|
accessibilityHint: labels.closeHint,
|
|
1866
1981
|
accessibilityLabel: labels.closeLabel,
|
|
1867
1982
|
accessibilityRole: "button",
|
|
1868
|
-
style:
|
|
1983
|
+
style: styles6.scrim,
|
|
1869
1984
|
testID: `${testID ?? "command-palette"}-scrim`,
|
|
1870
1985
|
onPress: onClose
|
|
1871
1986
|
}
|
|
@@ -1876,7 +1991,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1876
1991
|
"aria-modal": true,
|
|
1877
1992
|
"aria-label": labels.regionLabel,
|
|
1878
1993
|
role: "dialog",
|
|
1879
|
-
style: [
|
|
1994
|
+
style: [styles6.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
|
|
1880
1995
|
testID: testID ?? "command-palette",
|
|
1881
1996
|
children: [
|
|
1882
1997
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1886,71 +2001,22 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
1886
2001
|
accessibilityLabel: labels.placeholder,
|
|
1887
2002
|
placeholder: labels.placeholder,
|
|
1888
2003
|
placeholderTextColor: colors.textSecondary,
|
|
1889
|
-
style: [
|
|
2004
|
+
style: [styles6.input, { color: colors.text, borderBottomColor: colors.border }],
|
|
1890
2005
|
testID: `${testID ?? "command-palette"}-input`,
|
|
1891
2006
|
value: query,
|
|
1892
2007
|
onChangeText: setQuery,
|
|
1893
2008
|
onKeyPress
|
|
1894
2009
|
}
|
|
1895
2010
|
),
|
|
1896
|
-
results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
2011
|
+
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
2012
|
]
|
|
1898
2013
|
}
|
|
1899
2014
|
)
|
|
1900
2015
|
] });
|
|
1901
2016
|
};
|
|
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
2017
|
var LAUNCH_KEY = "k";
|
|
1952
2018
|
function useCommandPaletteHotkey(onOpen, enabled = true) {
|
|
1953
|
-
|
|
2019
|
+
React.useEffect(() => {
|
|
1954
2020
|
if (!enabled || typeof document === "undefined") return void 0;
|
|
1955
2021
|
const handler = (event) => {
|
|
1956
2022
|
const isLauncher = event.key.toLowerCase() === LAUNCH_KEY && (event.metaKey || event.ctrlKey);
|
|
@@ -2011,6 +2077,7 @@ exports.isFilterActive = isFilterActive;
|
|
|
2011
2077
|
exports.isRouteActive = isRouteActive;
|
|
2012
2078
|
exports.navStyles = navStyles;
|
|
2013
2079
|
exports.pillNavStyles = pillNavStyles;
|
|
2080
|
+
exports.resolveActiveRoute = resolveActiveRoute;
|
|
2014
2081
|
exports.resolveContentMaxWidth = resolveContentMaxWidth;
|
|
2015
2082
|
exports.resolveRailMode = resolveRailMode;
|
|
2016
2083
|
exports.resolveSearchAffordance = resolveSearchAffordance;
|