@dloizides/ui-nav 1.26.2 → 1.28.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 +8 -0
- package/dist/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +418 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -62
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -903,7 +903,8 @@ var APP_SHELL_SUFFIX = {
|
|
|
903
903
|
pending: "-pending",
|
|
904
904
|
loading: "-loading",
|
|
905
905
|
error: "-error",
|
|
906
|
-
forbidden: "-forbidden"
|
|
906
|
+
forbidden: "-forbidden",
|
|
907
|
+
band: "-backdrop-band"
|
|
907
908
|
};
|
|
908
909
|
var FocusableTouchable = ({
|
|
909
910
|
onPress,
|
|
@@ -1057,10 +1058,88 @@ var Topbar = ({
|
|
|
1057
1058
|
}
|
|
1058
1059
|
);
|
|
1059
1060
|
};
|
|
1061
|
+
|
|
1062
|
+
// src/NavHoverFeedback.ts
|
|
1063
|
+
var NavHoverFeedback = /* @__PURE__ */ ((NavHoverFeedback2) => {
|
|
1064
|
+
NavHoverFeedback2["Default"] = "default";
|
|
1065
|
+
NavHoverFeedback2["TintPill"] = "tint-pill";
|
|
1066
|
+
NavHoverFeedback2["Underline"] = "underline";
|
|
1067
|
+
NavHoverFeedback2["Lift"] = "lift";
|
|
1068
|
+
NavHoverFeedback2["SlidingHighlight"] = "sliding-highlight";
|
|
1069
|
+
return NavHoverFeedback2;
|
|
1070
|
+
})(NavHoverFeedback || {});
|
|
1071
|
+
|
|
1072
|
+
// src/feedback/navLinkFeedback.ts
|
|
1073
|
+
var DEFAULT_FADE_MS = 150;
|
|
1074
|
+
var PRESS_SCALE = 0.97;
|
|
1075
|
+
var PRESS_NUDGE_PX = 1;
|
|
1076
|
+
var LIFT_PX = -2;
|
|
1077
|
+
var PILL_KINDS = /* @__PURE__ */ new Set([
|
|
1078
|
+
"default" /* Default */,
|
|
1079
|
+
"tint-pill" /* TintPill */,
|
|
1080
|
+
"lift" /* Lift */
|
|
1081
|
+
]);
|
|
1082
|
+
function pillBackground(kind, isHovered, active, colors) {
|
|
1083
|
+
if (!PILL_KINDS.has(kind)) return void 0;
|
|
1084
|
+
if (active) return colors.activeBg;
|
|
1085
|
+
return isHovered ? colors.hoverBg : void 0;
|
|
1086
|
+
}
|
|
1087
|
+
function linkTextColor(kind, isHovered, active, colors) {
|
|
1088
|
+
if (active) return PILL_KINDS.has(kind) ? colors.onPrimary : colors.activeBg;
|
|
1089
|
+
return isHovered ? colors.hoverText : colors.rest;
|
|
1090
|
+
}
|
|
1091
|
+
function pressTransform(kind, pressed, isHovered) {
|
|
1092
|
+
if (kind === "tint-pill" /* TintPill */) return pressed ? [{ scale: PRESS_SCALE }] : void 0;
|
|
1093
|
+
if (kind === "underline" /* Underline */) return pressed ? [{ translateY: PRESS_NUDGE_PX }] : void 0;
|
|
1094
|
+
if (kind !== "lift" /* Lift */) return void 0;
|
|
1095
|
+
if (pressed) return [{ translateY: 0 }];
|
|
1096
|
+
return isHovered ? [{ translateY: LIFT_PX }] : void 0;
|
|
1097
|
+
}
|
|
1098
|
+
function feedbackDuration(kind) {
|
|
1099
|
+
if (kind === "default" /* Default */) return DEFAULT_FADE_MS;
|
|
1100
|
+
return kind === "tint-pill" /* TintPill */ ? uiMotion.motionDurations.fast : uiMotion.motionDurations.base;
|
|
1101
|
+
}
|
|
1102
|
+
function resolveLinkFeedback(kind, state, colors, reducedMotion) {
|
|
1103
|
+
const isHovered = state.hovered && !state.active;
|
|
1104
|
+
const underlined = state.active || isHovered;
|
|
1105
|
+
return {
|
|
1106
|
+
background: pillBackground(kind, isHovered, state.active, colors),
|
|
1107
|
+
textColor: linkTextColor(kind, isHovered, state.active, colors),
|
|
1108
|
+
transform: reducedMotion ? void 0 : pressTransform(kind, state.pressed, isHovered),
|
|
1109
|
+
shadow: kind === "lift" /* Lift */ && isHovered,
|
|
1110
|
+
underline: kind === "underline" /* Underline */ ? { color: colors.activeBg, scale: underlined ? 1 : 0 } : null,
|
|
1111
|
+
durationMs: reducedMotion ? uiMotion.motionDurations.instant : feedbackDuration(kind)
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1060
1114
|
var TEXT_ON_PRIMARY2 = "#ffffff";
|
|
1061
1115
|
function ariaCurrentProps2(isActive) {
|
|
1062
1116
|
return isActive ? { "aria-current": "page" } : {};
|
|
1063
1117
|
}
|
|
1118
|
+
var FEEDBACK_PROPERTIES = "color, background-color, transform, box-shadow";
|
|
1119
|
+
var UNDERLINE_INSET = 14;
|
|
1120
|
+
var UNDERLINE_HEIGHT = 2;
|
|
1121
|
+
var SHADOW_OPACITY = 0.18;
|
|
1122
|
+
var SHADOW_RADIUS = 6;
|
|
1123
|
+
var SHADOW_OFFSET = { width: 0, height: 2 };
|
|
1124
|
+
function feedbackStyle(kind, feedback, shadowColor, reducedMotion) {
|
|
1125
|
+
const background = feedback.background === void 0 ? {} : { backgroundColor: feedback.background };
|
|
1126
|
+
const shadow = feedback.shadow ? { shadowColor, shadowOpacity: SHADOW_OPACITY, shadowRadius: SHADOW_RADIUS, shadowOffset: SHADOW_OFFSET } : {};
|
|
1127
|
+
const transition = kind === "default" /* Default */ ? hoverTransitionStyle(reducedMotion) : webTransition(FEEDBACK_PROPERTIES, feedback.durationMs, reducedMotion);
|
|
1128
|
+
return [background, shadow, { transform: feedback.transform }, transition ?? {}];
|
|
1129
|
+
}
|
|
1130
|
+
function underlineStyle(feedback, reducedMotion) {
|
|
1131
|
+
if (feedback.underline === null) return null;
|
|
1132
|
+
const line = {
|
|
1133
|
+
position: "absolute",
|
|
1134
|
+
left: UNDERLINE_INSET,
|
|
1135
|
+
right: UNDERLINE_INSET,
|
|
1136
|
+
bottom: 0,
|
|
1137
|
+
height: UNDERLINE_HEIGHT,
|
|
1138
|
+
backgroundColor: feedback.underline.color,
|
|
1139
|
+
transform: [{ scaleX: feedback.underline.scale }]
|
|
1140
|
+
};
|
|
1141
|
+
return [line, webTransition("transform", feedback.durationMs, reducedMotion) ?? {}];
|
|
1142
|
+
}
|
|
1064
1143
|
var NavBarLink = ({
|
|
1065
1144
|
item,
|
|
1066
1145
|
isActive,
|
|
@@ -1069,13 +1148,20 @@ var NavBarLink = ({
|
|
|
1069
1148
|
reducedMotion,
|
|
1070
1149
|
colors,
|
|
1071
1150
|
navigateHint,
|
|
1072
|
-
onPress
|
|
1151
|
+
onPress,
|
|
1152
|
+
hoverFeedback = "default" /* Default */,
|
|
1153
|
+
onHoverChange
|
|
1073
1154
|
}) => {
|
|
1074
1155
|
const [hovered, setHovered] = React.useState(false);
|
|
1075
1156
|
const [focused, setFocused] = React.useState(false);
|
|
1076
|
-
const
|
|
1077
|
-
const
|
|
1078
|
-
const
|
|
1157
|
+
const linkColors = { ...colors, onPrimary: TEXT_ON_PRIMARY2 };
|
|
1158
|
+
const resolve = (pressed) => resolveLinkFeedback(hoverFeedback, { hovered, pressed, active: isActive }, linkColors, reducedMotion);
|
|
1159
|
+
const textColor = resolve(false).textColor;
|
|
1160
|
+
const underline = underlineStyle(resolve(false), reducedMotion);
|
|
1161
|
+
const setHover = (next) => {
|
|
1162
|
+
setHovered(next);
|
|
1163
|
+
onHoverChange?.(item.key, next);
|
|
1164
|
+
};
|
|
1079
1165
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1080
1166
|
reactNative.Pressable,
|
|
1081
1167
|
{
|
|
@@ -1083,27 +1169,61 @@ var NavBarLink = ({
|
|
|
1083
1169
|
accessibilityLabel: item.label,
|
|
1084
1170
|
accessibilityRole: "link",
|
|
1085
1171
|
accessibilityState: { selected: isActive },
|
|
1086
|
-
style: [
|
|
1172
|
+
style: ({ pressed }) => [
|
|
1087
1173
|
navBarStyles.link,
|
|
1088
1174
|
collapsed ? navBarStyles.linkStacked : void 0,
|
|
1089
|
-
|
|
1090
|
-
hoverTransitionStyle(reducedMotion),
|
|
1175
|
+
...feedbackStyle(hoverFeedback, resolve(pressed), colors.rest, reducedMotion),
|
|
1091
1176
|
focusRingStyle(focused, colors.ring)
|
|
1092
1177
|
],
|
|
1093
1178
|
testID: item.testID ?? item.key,
|
|
1094
1179
|
onBlur: () => setFocused(false),
|
|
1095
1180
|
onFocus: () => setFocused(true),
|
|
1096
|
-
onHoverIn: () =>
|
|
1097
|
-
onHoverOut: () =>
|
|
1181
|
+
onHoverIn: () => setHover(true),
|
|
1182
|
+
onHoverOut: () => setHover(false),
|
|
1098
1183
|
onPress: () => onPress(item.route),
|
|
1099
1184
|
...ariaCurrentProps2(isActive),
|
|
1100
1185
|
children: [
|
|
1101
1186
|
typeof item.renderIcon === "function" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navBarStyles.linkIcon, children: item.renderIcon(textColor, iconSize) }) : null,
|
|
1187
|
+
underline === null ? null : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { pointerEvents: "none", style: underline }),
|
|
1102
1188
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [navBarStyles.linkText, { color: textColor }], children: item.label })
|
|
1103
1189
|
]
|
|
1104
1190
|
}
|
|
1105
1191
|
);
|
|
1106
1192
|
};
|
|
1193
|
+
var HIGHLIGHT_RADIUS = 8;
|
|
1194
|
+
var SLIDE_PROPERTIES = "left, top, width, height";
|
|
1195
|
+
function slideHighlightRect(layouts, activeKey, hoveredKey) {
|
|
1196
|
+
const hoveredRect = hoveredKey === null ? void 0 : layouts[hoveredKey];
|
|
1197
|
+
const activeRect = activeKey === void 0 ? void 0 : layouts[activeKey];
|
|
1198
|
+
return hoveredRect ?? activeRect ?? null;
|
|
1199
|
+
}
|
|
1200
|
+
function useSlidingHighlight(enabled, activeKey, color, reducedMotion) {
|
|
1201
|
+
const [layouts, setLayouts] = React.useState({});
|
|
1202
|
+
const [hoveredKey, setHoveredKey] = React.useState(null);
|
|
1203
|
+
const measure = React.useCallback(
|
|
1204
|
+
(key, layout) => {
|
|
1205
|
+
if (enabled) setLayouts((prev) => ({ ...prev, [key]: layout }));
|
|
1206
|
+
},
|
|
1207
|
+
[enabled]
|
|
1208
|
+
);
|
|
1209
|
+
const hover = React.useCallback((key, hovered) => {
|
|
1210
|
+
setHoveredKey((prev) => hovered ? key : prev === key ? null : prev);
|
|
1211
|
+
}, []);
|
|
1212
|
+
const rect = enabled ? slideHighlightRect(layouts, activeKey, hoveredKey) : null;
|
|
1213
|
+
const style = rect === null ? null : [
|
|
1214
|
+
{
|
|
1215
|
+
position: "absolute",
|
|
1216
|
+
left: rect.x,
|
|
1217
|
+
top: rect.y,
|
|
1218
|
+
width: rect.width,
|
|
1219
|
+
height: rect.height,
|
|
1220
|
+
borderRadius: HIGHLIGHT_RADIUS,
|
|
1221
|
+
backgroundColor: color
|
|
1222
|
+
},
|
|
1223
|
+
webTransition(SLIDE_PROPERTIES, uiMotion.motionDurations.base, reducedMotion)
|
|
1224
|
+
];
|
|
1225
|
+
return { style, measure, hover };
|
|
1226
|
+
}
|
|
1107
1227
|
var TEXT_ON_PRIMARY3 = "#ffffff";
|
|
1108
1228
|
var NO_ACTIVE_ROUTE = "";
|
|
1109
1229
|
var OVERFLOW_MENU_MIN_WIDTH = 200;
|
|
@@ -1244,7 +1364,8 @@ var NavBarInner = ({
|
|
|
1244
1364
|
overflowLabel = "More",
|
|
1245
1365
|
overflowHint = "",
|
|
1246
1366
|
containerStyle,
|
|
1247
|
-
contentMaxWidth
|
|
1367
|
+
contentMaxWidth,
|
|
1368
|
+
hoverFeedback = "default" /* Default */
|
|
1248
1369
|
}) => {
|
|
1249
1370
|
const { theme } = uiFeedback.useUi();
|
|
1250
1371
|
const colors = theme.colors;
|
|
@@ -1278,6 +1399,12 @@ var NavBarInner = ({
|
|
|
1278
1399
|
[onNavigate]
|
|
1279
1400
|
);
|
|
1280
1401
|
const activeRoute = resolveActiveRoute(items, pathname);
|
|
1402
|
+
const slide = useSlidingHighlight(
|
|
1403
|
+
hoverFeedback === "sliding-highlight" /* SlidingHighlight */,
|
|
1404
|
+
items.find((item) => item.route === activeRoute)?.key,
|
|
1405
|
+
colors.surfaceElevated,
|
|
1406
|
+
reducedMotion
|
|
1407
|
+
);
|
|
1281
1408
|
const linkColors = React.useMemo(
|
|
1282
1409
|
() => ({
|
|
1283
1410
|
rest: colors.textSecondary,
|
|
@@ -1327,9 +1454,11 @@ var NavBarInner = ({
|
|
|
1327
1454
|
colors: linkColors,
|
|
1328
1455
|
iconSize: NAV_ICON_SIZE,
|
|
1329
1456
|
isActive: item.route === activeRoute,
|
|
1457
|
+
hoverFeedback,
|
|
1330
1458
|
item,
|
|
1331
1459
|
navigateHint,
|
|
1332
1460
|
reducedMotion,
|
|
1461
|
+
onHoverChange: slide.hover,
|
|
1333
1462
|
onPress: handlePress
|
|
1334
1463
|
}
|
|
1335
1464
|
);
|
|
@@ -1355,11 +1484,15 @@ var NavBarInner = ({
|
|
|
1355
1484
|
testID: NAV_TEST_IDS.navBarLinks,
|
|
1356
1485
|
onLayout: (event) => setAvailableWidth(event.nativeEvent.layout.width),
|
|
1357
1486
|
children: [
|
|
1487
|
+
slide.style === null ? null : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { pointerEvents: "none", style: slide.style }),
|
|
1358
1488
|
visibleItems.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1359
1489
|
reactNative.View,
|
|
1360
1490
|
{
|
|
1361
1491
|
style: navBarStyles.linkCell,
|
|
1362
|
-
onLayout: (event) =>
|
|
1492
|
+
onLayout: (event) => {
|
|
1493
|
+
setItemWidth(index, event.nativeEvent.layout.width);
|
|
1494
|
+
slide.measure(item.key, event.nativeEvent.layout);
|
|
1495
|
+
},
|
|
1363
1496
|
children: renderLink(item)
|
|
1364
1497
|
},
|
|
1365
1498
|
item.key
|
|
@@ -1410,13 +1543,53 @@ var Nav = (props) => {
|
|
|
1410
1543
|
}
|
|
1411
1544
|
return /* @__PURE__ */ jsxRuntime.jsx(NavBar, { ...props });
|
|
1412
1545
|
};
|
|
1546
|
+
var RAIL_FULL_BREAKPOINT = uiLayout.LAYOUT_COLLAPSE_BREAKPOINT;
|
|
1547
|
+
var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
|
|
1548
|
+
function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
|
|
1549
|
+
if (!hasSidebar) return "none";
|
|
1550
|
+
if (hasCollapsed) {
|
|
1551
|
+
if (viewport >= range.max) return "full";
|
|
1552
|
+
if (viewport >= range.min) return "collapsed";
|
|
1553
|
+
return "drawer";
|
|
1554
|
+
}
|
|
1555
|
+
return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
|
|
1556
|
+
}
|
|
1557
|
+
var styles3 = reactNative.StyleSheet.create({
|
|
1558
|
+
band: { left: 0, position: "absolute", right: 0, top: 0 }
|
|
1559
|
+
});
|
|
1560
|
+
function readBackdrop(theme) {
|
|
1561
|
+
return theme.backdrop;
|
|
1562
|
+
}
|
|
1563
|
+
function rootStyle(backdrop, fallback) {
|
|
1564
|
+
if (backdrop === void 0) return { backgroundColor: fallback };
|
|
1565
|
+
const base = { backgroundColor: backdrop.color };
|
|
1566
|
+
if (!IS_WEB || backdrop.image === null) return base;
|
|
1567
|
+
const web = { backgroundImage: backdrop.image, backgroundSize: backdrop.imageSize ?? "auto" };
|
|
1568
|
+
return { ...base, ...web };
|
|
1569
|
+
}
|
|
1570
|
+
function resolveShellBackdrop(theme, viewport, contentPadding, testID) {
|
|
1571
|
+
const backdrop = readBackdrop(theme);
|
|
1572
|
+
const band = backdrop?.band ?? null;
|
|
1573
|
+
const root = rootStyle(backdrop, theme.colors.background);
|
|
1574
|
+
if (band === null) return { root, chrome: void 0, chromeText: theme.colors.text, band: null };
|
|
1575
|
+
const overlap = viewport < RAIL_FULL_BREAKPOINT ? band.overlap.compact : band.overlap.wide;
|
|
1576
|
+
const element = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1577
|
+
reactNative.View,
|
|
1578
|
+
{
|
|
1579
|
+
pointerEvents: "none",
|
|
1580
|
+
style: [styles3.band, { backgroundColor: band.color, height: contentPadding + overlap }],
|
|
1581
|
+
testID: `${testID}${APP_SHELL_SUFFIX.band}`
|
|
1582
|
+
}
|
|
1583
|
+
);
|
|
1584
|
+
return { root, chrome: { backgroundColor: band.color }, chromeText: band.text, band: element };
|
|
1585
|
+
}
|
|
1413
1586
|
var CARD_PADDING = 20;
|
|
1414
1587
|
var CARD_BORDER_RADIUS = 12;
|
|
1415
1588
|
var CARD_BORDER_WIDTH = 1;
|
|
1416
1589
|
var CARD_TITLE_FONT_SIZE = 16;
|
|
1417
1590
|
var CARD_MESSAGE_FONT_SIZE = 14;
|
|
1418
1591
|
var CARD_TITLE_MARGIN_BOTTOM = 8;
|
|
1419
|
-
var
|
|
1592
|
+
var styles4 = reactNative.StyleSheet.create({
|
|
1420
1593
|
// Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
|
|
1421
1594
|
// stably-mounted children, never a replacement for them. Replacing children here
|
|
1422
1595
|
// would unmount whatever they host (e.g. a navigator), and on remount React
|
|
@@ -1448,9 +1621,9 @@ function MessageCard({
|
|
|
1448
1621
|
}) {
|
|
1449
1622
|
const { theme } = uiFeedback.useUi();
|
|
1450
1623
|
const colors = theme.colors;
|
|
1451
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
1452
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1453
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1624
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles4.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
|
|
1625
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.cardTitle, { color: accentColor }], children: message.titleText }),
|
|
1626
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.cardMessage, { color: colors.textSecondary }], children: message.messageText })
|
|
1454
1627
|
] });
|
|
1455
1628
|
}
|
|
1456
1629
|
function useContentBody(state, children, testID) {
|
|
@@ -1462,12 +1635,12 @@ function useContentBody(state, children, testID) {
|
|
|
1462
1635
|
return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
|
|
1463
1636
|
if (state.error)
|
|
1464
1637
|
return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
|
|
1465
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1638
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.loadingHost, children: [
|
|
1466
1639
|
children,
|
|
1467
1640
|
state.loading === true ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1468
1641
|
reactNative.View,
|
|
1469
1642
|
{
|
|
1470
|
-
style: [
|
|
1643
|
+
style: [styles4.loadingOverlay, { backgroundColor: theme.colors.background }],
|
|
1471
1644
|
testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
|
|
1472
1645
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
|
|
1473
1646
|
}
|
|
@@ -1494,7 +1667,7 @@ var DEFAULT_DRAWER_LABELS = {
|
|
|
1494
1667
|
closeLabel: "Close menu",
|
|
1495
1668
|
closeHint: "Close the navigation menu"
|
|
1496
1669
|
};
|
|
1497
|
-
var
|
|
1670
|
+
var styles5 = reactNative.StyleSheet.create({
|
|
1498
1671
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
|
|
1499
1672
|
// The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
|
|
1500
1673
|
// (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
|
|
@@ -1529,10 +1702,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
|
|
|
1529
1702
|
accessibilityHint: hint,
|
|
1530
1703
|
accessibilityLabel: label,
|
|
1531
1704
|
ringColor: theme.palette.primary["500"],
|
|
1532
|
-
style:
|
|
1705
|
+
style: styles5.menuToggle,
|
|
1533
1706
|
testID,
|
|
1534
1707
|
onPress: onOpen,
|
|
1535
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
1708
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
|
|
1536
1709
|
}
|
|
1537
1710
|
);
|
|
1538
1711
|
};
|
|
@@ -1558,14 +1731,14 @@ var MobileDrawer = ({
|
|
|
1558
1731
|
node.addEventListener("click", handleClick);
|
|
1559
1732
|
return () => node.removeEventListener("click", handleClick);
|
|
1560
1733
|
}, [onClose]);
|
|
1561
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1734
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.overlay, children: [
|
|
1562
1735
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1563
1736
|
reactNative.Pressable,
|
|
1564
1737
|
{
|
|
1565
1738
|
accessibilityHint: labels.closeHint,
|
|
1566
1739
|
accessibilityLabel: labels.closeLabel,
|
|
1567
1740
|
accessibilityRole: "button",
|
|
1568
|
-
style:
|
|
1741
|
+
style: styles5.scrim,
|
|
1569
1742
|
testID: scrimTestID,
|
|
1570
1743
|
onPress: onClose
|
|
1571
1744
|
}
|
|
@@ -1576,24 +1749,13 @@ var MobileDrawer = ({
|
|
|
1576
1749
|
ref: panelRef,
|
|
1577
1750
|
"aria-modal": true,
|
|
1578
1751
|
role: "dialog",
|
|
1579
|
-
style: [
|
|
1752
|
+
style: [styles5.panel, { backgroundColor: theme.colors.surface }],
|
|
1580
1753
|
testID: drawerTestID,
|
|
1581
1754
|
children: renderTextSlot(sidebar, { color: theme.colors.text })
|
|
1582
1755
|
}
|
|
1583
1756
|
)
|
|
1584
1757
|
] });
|
|
1585
1758
|
};
|
|
1586
|
-
var RAIL_FULL_BREAKPOINT = uiLayout.LAYOUT_COLLAPSE_BREAKPOINT;
|
|
1587
|
-
var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
|
|
1588
|
-
function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
|
|
1589
|
-
if (!hasSidebar) return "none";
|
|
1590
|
-
if (hasCollapsed) {
|
|
1591
|
-
if (viewport >= range.max) return "full";
|
|
1592
|
-
if (viewport >= range.min) return "collapsed";
|
|
1593
|
-
return "drawer";
|
|
1594
|
-
}
|
|
1595
|
-
return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
|
|
1596
|
-
}
|
|
1597
1759
|
function resolveContentMaxWidth(width, viewport) {
|
|
1598
1760
|
if (width === "full") return "full";
|
|
1599
1761
|
const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
|
|
@@ -1605,7 +1767,7 @@ function useContentMaxWidth(width) {
|
|
|
1605
1767
|
return resolveContentMaxWidth(width, viewport);
|
|
1606
1768
|
}
|
|
1607
1769
|
var DEFAULT_CONTENT_PADDING = 24;
|
|
1608
|
-
var
|
|
1770
|
+
var styles6 = reactNative.StyleSheet.create({
|
|
1609
1771
|
root: { flex: 1 },
|
|
1610
1772
|
// Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
|
|
1611
1773
|
// `children` (and any navigator they host) are never unmounted while the gate
|
|
@@ -1645,6 +1807,7 @@ var AppShell = ({
|
|
|
1645
1807
|
const maxWidth = useContentMaxWidth(width);
|
|
1646
1808
|
const body = useContentBody(state, children, testID);
|
|
1647
1809
|
const { width: viewportWidth } = reactNative.useWindowDimensions();
|
|
1810
|
+
const look = resolveShellBackdrop(theme, viewportWidth, contentPadding, testID);
|
|
1648
1811
|
const railRange = collapsedRailRange ?? { min: RAIL_FULL_BREAKPOINT, max: DEFAULT_COLLAPSED_RAIL_MAX };
|
|
1649
1812
|
const railMode = resolveRailMode({
|
|
1650
1813
|
viewport: viewportWidth,
|
|
@@ -1666,18 +1829,21 @@ var AppShell = ({
|
|
|
1666
1829
|
}, [isUnauthenticated, gate]);
|
|
1667
1830
|
const gatePending = gate?.pending === true;
|
|
1668
1831
|
if (isUnauthenticated) return null;
|
|
1669
|
-
const columnStyle = maxWidth === "full" ?
|
|
1670
|
-
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [
|
|
1671
|
-
const scroller = /* @__PURE__ */ jsxRuntime.
|
|
1832
|
+
const columnStyle = maxWidth === "full" ? styles6.columnFull : [styles6.columnCapped, { maxWidth }];
|
|
1833
|
+
const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles6.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
|
|
1834
|
+
const scroller = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1672
1835
|
reactNative.ScrollView,
|
|
1673
1836
|
{
|
|
1674
|
-
contentContainerStyle: [
|
|
1675
|
-
style:
|
|
1837
|
+
contentContainerStyle: [styles6.scrollContent, { padding: contentPadding }],
|
|
1838
|
+
style: styles6.scroll,
|
|
1676
1839
|
testID: `${testID}${APP_SHELL_SUFFIX.content}`,
|
|
1677
|
-
children:
|
|
1840
|
+
children: [
|
|
1841
|
+
look.band,
|
|
1842
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
|
|
1843
|
+
]
|
|
1678
1844
|
}
|
|
1679
1845
|
);
|
|
1680
|
-
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1846
|
+
const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles6.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
|
|
1681
1847
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1682
1848
|
MenuToggle,
|
|
1683
1849
|
{
|
|
@@ -1687,14 +1853,14 @@ var AppShell = ({
|
|
|
1687
1853
|
onOpen: openDrawer
|
|
1688
1854
|
}
|
|
1689
1855
|
),
|
|
1690
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
1691
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color:
|
|
1856
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
|
|
1857
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: look.chromeText }) });
|
|
1692
1858
|
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;
|
|
1693
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [
|
|
1859
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles6.root, look.root], testID, children: [
|
|
1694
1860
|
headerRegion,
|
|
1695
|
-
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:
|
|
1861
|
+
nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: look.chromeText }) }) : renderTextSlot(nav, { color: look.chromeText }) }) : null,
|
|
1696
1862
|
banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
|
|
1697
|
-
railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
1863
|
+
railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.bodyRow, children: [
|
|
1698
1864
|
railRegion,
|
|
1699
1865
|
scroller
|
|
1700
1866
|
] }) : scroller,
|
|
@@ -1711,7 +1877,7 @@ var AppShell = ({
|
|
|
1711
1877
|
gatePending ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1712
1878
|
reactNative.View,
|
|
1713
1879
|
{
|
|
1714
|
-
style: [
|
|
1880
|
+
style: [styles6.pendingOverlay, { backgroundColor: theme.colors.background }],
|
|
1715
1881
|
testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
|
|
1716
1882
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
|
|
1717
1883
|
}
|
|
@@ -2078,7 +2244,7 @@ var KEY_DOWN = "ArrowDown";
|
|
|
2078
2244
|
var KEY_UP = "ArrowUp";
|
|
2079
2245
|
var KEY_ENTER = "Enter";
|
|
2080
2246
|
var KEY_ESCAPE = "Escape";
|
|
2081
|
-
var
|
|
2247
|
+
var styles7 = reactNative.StyleSheet.create({
|
|
2082
2248
|
overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
|
|
2083
2249
|
scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
|
|
2084
2250
|
panel: {
|
|
@@ -2107,14 +2273,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
|
|
|
2107
2273
|
accessibilityLabel: item.label,
|
|
2108
2274
|
accessibilityRole: "button",
|
|
2109
2275
|
accessibilityState: { selected },
|
|
2110
|
-
style: [
|
|
2276
|
+
style: [styles7.row, selected ? { backgroundColor: colors.border } : void 0],
|
|
2111
2277
|
testID: item.testID ?? item.key,
|
|
2112
2278
|
onPress: () => onChoose(item),
|
|
2113
2279
|
children: [
|
|
2114
2280
|
typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
|
|
2115
|
-
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
2116
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
2117
|
-
item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
2281
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.rowText, children: [
|
|
2282
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.rowLabel, { color: colors.text }], children: item.label }),
|
|
2283
|
+
item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
|
|
2118
2284
|
] })
|
|
2119
2285
|
]
|
|
2120
2286
|
}
|
|
@@ -2154,14 +2320,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
2154
2320
|
[results, selected, choose, onClose]
|
|
2155
2321
|
);
|
|
2156
2322
|
if (!open) return null;
|
|
2157
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style:
|
|
2323
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.overlay, children: [
|
|
2158
2324
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2159
2325
|
reactNative.Pressable,
|
|
2160
2326
|
{
|
|
2161
2327
|
accessibilityHint: labels.closeHint,
|
|
2162
2328
|
accessibilityLabel: labels.closeLabel,
|
|
2163
2329
|
accessibilityRole: "button",
|
|
2164
|
-
style:
|
|
2330
|
+
style: styles7.scrim,
|
|
2165
2331
|
testID: `${testID ?? "command-palette"}-scrim`,
|
|
2166
2332
|
onPress: onClose
|
|
2167
2333
|
}
|
|
@@ -2172,7 +2338,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
2172
2338
|
"aria-modal": true,
|
|
2173
2339
|
"aria-label": labels.regionLabel,
|
|
2174
2340
|
role: "dialog",
|
|
2175
|
-
style: [
|
|
2341
|
+
style: [styles7.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
|
|
2176
2342
|
testID: testID ?? "command-palette",
|
|
2177
2343
|
children: [
|
|
2178
2344
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2182,14 +2348,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
|
|
|
2182
2348
|
accessibilityLabel: labels.placeholder,
|
|
2183
2349
|
placeholder: labels.placeholder,
|
|
2184
2350
|
placeholderTextColor: colors.textSecondary,
|
|
2185
|
-
style: [
|
|
2351
|
+
style: [styles7.input, { color: colors.text, borderBottomColor: colors.border }],
|
|
2186
2352
|
testID: `${testID ?? "command-palette"}-input`,
|
|
2187
2353
|
value: query,
|
|
2188
2354
|
onChangeText: setQuery,
|
|
2189
2355
|
onKeyPress
|
|
2190
2356
|
}
|
|
2191
2357
|
),
|
|
2192
|
-
results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
2358
|
+
results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles7.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
|
|
2193
2359
|
]
|
|
2194
2360
|
}
|
|
2195
2361
|
)
|
|
@@ -2220,11 +2386,202 @@ function accessibleNavItems(user, table, translate) {
|
|
|
2220
2386
|
return roleRoutesToNavItems(authWeb.resolveAccessibleRoutes(user, table), translate);
|
|
2221
2387
|
}
|
|
2222
2388
|
|
|
2389
|
+
// src/TabFeedback.ts
|
|
2390
|
+
var TabFeedback = /* @__PURE__ */ ((TabFeedback2) => {
|
|
2391
|
+
TabFeedback2["Default"] = "default";
|
|
2392
|
+
TabFeedback2["IndicatorPill"] = "indicator-pill";
|
|
2393
|
+
TabFeedback2["DimSpring"] = "dim-spring";
|
|
2394
|
+
TabFeedback2["SlidingBar"] = "sliding-bar";
|
|
2395
|
+
TabFeedback2["IconLift"] = "icon-lift";
|
|
2396
|
+
return TabFeedback2;
|
|
2397
|
+
})(TabFeedback || {});
|
|
2398
|
+
|
|
2399
|
+
// src/feedback/tabPressFeedback.ts
|
|
2400
|
+
var FULL = 1;
|
|
2401
|
+
var PERCENT = 100;
|
|
2402
|
+
var PRESS_ICON_SCALE = 0.92;
|
|
2403
|
+
var DIM_OPACITY = 0.6;
|
|
2404
|
+
var ICON_LIFT_PX = -3;
|
|
2405
|
+
var SPRING_OVERSHOOT = 1.06;
|
|
2406
|
+
function tabIconTransform(kind, state) {
|
|
2407
|
+
if (kind === "indicator-pill" /* IndicatorPill */ && state.pressed) return [{ scale: PRESS_ICON_SCALE }];
|
|
2408
|
+
const lifted = state.active || state.pressed;
|
|
2409
|
+
return kind === "icon-lift" /* IconLift */ && lifted ? [{ translateY: ICON_LIFT_PX }] : void 0;
|
|
2410
|
+
}
|
|
2411
|
+
function tabDuration(kind) {
|
|
2412
|
+
if (kind === "default" /* Default */) return uiMotion.motionDurations.instant;
|
|
2413
|
+
const slow = kind === "dim-spring" /* DimSpring */ || kind === "sliding-bar" /* SlidingBar */;
|
|
2414
|
+
return slow ? uiMotion.motionDurations.slow : uiMotion.motionDurations.base;
|
|
2415
|
+
}
|
|
2416
|
+
function resolveTabFeedback(kind, state, reducedMotion) {
|
|
2417
|
+
const pillScale = state.active ? FULL : 0;
|
|
2418
|
+
return {
|
|
2419
|
+
opacity: kind === "dim-spring" /* DimSpring */ && state.pressed ? DIM_OPACITY : FULL,
|
|
2420
|
+
iconTransform: reducedMotion ? void 0 : tabIconTransform(kind, state),
|
|
2421
|
+
pillScale: kind === "indicator-pill" /* IndicatorPill */ ? pillScale : null,
|
|
2422
|
+
spring: kind === "dim-spring" /* DimSpring */ && !reducedMotion,
|
|
2423
|
+
durationMs: reducedMotion ? uiMotion.motionDurations.instant : tabDuration(kind)
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
function slidingBarPosition(activeIndex, count) {
|
|
2427
|
+
if (activeIndex < 0 || count <= 0) return null;
|
|
2428
|
+
const share = PERCENT / count;
|
|
2429
|
+
return { left: `${activeIndex * share}%`, width: `${share}%` };
|
|
2430
|
+
}
|
|
2431
|
+
function springSteps() {
|
|
2432
|
+
return [
|
|
2433
|
+
{ toValue: SPRING_OVERSHOOT, durationMs: uiMotion.motionDurations.fast },
|
|
2434
|
+
{ toValue: FULL, durationMs: uiMotion.motionDurations.slow - uiMotion.motionDurations.fast }
|
|
2435
|
+
];
|
|
2436
|
+
}
|
|
2437
|
+
function useSettleSpring(active, enabled) {
|
|
2438
|
+
const scale = React.useRef(new reactNative.Animated.Value(FULL)).current;
|
|
2439
|
+
React.useEffect(() => {
|
|
2440
|
+
if (!enabled || !active) return void 0;
|
|
2441
|
+
const steps = springSteps().map(
|
|
2442
|
+
(step) => reactNative.Animated.timing(scale, {
|
|
2443
|
+
toValue: step.toValue,
|
|
2444
|
+
duration: step.durationMs,
|
|
2445
|
+
easing: uiMotion.motionEasings.standard,
|
|
2446
|
+
useNativeDriver: false
|
|
2447
|
+
})
|
|
2448
|
+
);
|
|
2449
|
+
const animation = reactNative.Animated.sequence(steps);
|
|
2450
|
+
animation.start();
|
|
2451
|
+
return () => animation.stop();
|
|
2452
|
+
}, [active, enabled, scale]);
|
|
2453
|
+
return scale;
|
|
2454
|
+
}
|
|
2455
|
+
var ICON_SIZE = 24;
|
|
2456
|
+
var PILL_WIDTH = 56;
|
|
2457
|
+
var PILL_HEIGHT = 32;
|
|
2458
|
+
var PILL_RADIUS = 16;
|
|
2459
|
+
var TAB_MIN_HEIGHT = 56;
|
|
2460
|
+
var LABEL_SIZE = 12;
|
|
2461
|
+
var LABEL_GAP = 2;
|
|
2462
|
+
var TAB_PADDING = 4;
|
|
2463
|
+
var styles8 = reactNative.StyleSheet.create({
|
|
2464
|
+
tab: {
|
|
2465
|
+
flex: 1,
|
|
2466
|
+
minHeight: TAB_MIN_HEIGHT,
|
|
2467
|
+
alignItems: "center",
|
|
2468
|
+
justifyContent: "center",
|
|
2469
|
+
paddingVertical: TAB_PADDING,
|
|
2470
|
+
rowGap: LABEL_GAP
|
|
2471
|
+
},
|
|
2472
|
+
iconWrap: { width: PILL_WIDTH, height: PILL_HEIGHT, alignItems: "center", justifyContent: "center" },
|
|
2473
|
+
pill: { ...reactNative.StyleSheet.absoluteFillObject, borderRadius: PILL_RADIUS },
|
|
2474
|
+
label: { fontSize: LABEL_SIZE, textAlign: "center" }
|
|
2475
|
+
});
|
|
2476
|
+
function ariaCurrentProps3(isActive) {
|
|
2477
|
+
return isActive ? { "aria-current": "page" } : {};
|
|
2478
|
+
}
|
|
2479
|
+
var BottomTab = ({
|
|
2480
|
+
item,
|
|
2481
|
+
isActive,
|
|
2482
|
+
kind,
|
|
2483
|
+
colors,
|
|
2484
|
+
reducedMotion,
|
|
2485
|
+
navigateHint,
|
|
2486
|
+
onPress
|
|
2487
|
+
}) => {
|
|
2488
|
+
const [pressed, setPressed] = React.useState(false);
|
|
2489
|
+
const feedback = resolveTabFeedback(kind, { active: isActive, pressed }, reducedMotion);
|
|
2490
|
+
const scale = useSettleSpring(isActive, feedback.spring);
|
|
2491
|
+
const color = isActive ? colors.active : colors.rest;
|
|
2492
|
+
const motion = webTransition("transform", feedback.durationMs, reducedMotion);
|
|
2493
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2494
|
+
reactNative.Pressable,
|
|
2495
|
+
{
|
|
2496
|
+
accessibilityHint: navigateHint(item.label),
|
|
2497
|
+
accessibilityLabel: item.label,
|
|
2498
|
+
accessibilityRole: "link",
|
|
2499
|
+
accessibilityState: { selected: isActive },
|
|
2500
|
+
style: [styles8.tab, { opacity: feedback.opacity }],
|
|
2501
|
+
testID: item.testID ?? item.key,
|
|
2502
|
+
onPress: () => onPress(item.route),
|
|
2503
|
+
onPressIn: () => setPressed(true),
|
|
2504
|
+
onPressOut: () => setPressed(false),
|
|
2505
|
+
...ariaCurrentProps3(isActive),
|
|
2506
|
+
children: [
|
|
2507
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles8.iconWrap, { transform: [{ scale }] }], children: [
|
|
2508
|
+
feedback.pillScale === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2509
|
+
reactNative.View,
|
|
2510
|
+
{
|
|
2511
|
+
pointerEvents: "none",
|
|
2512
|
+
style: [styles8.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
|
|
2513
|
+
}
|
|
2514
|
+
),
|
|
2515
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ transform: feedback.iconTransform }, motion], children: item.renderIcon?.(color, ICON_SIZE) })
|
|
2516
|
+
] }),
|
|
2517
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.label, { color }], children: item.label })
|
|
2518
|
+
]
|
|
2519
|
+
}
|
|
2520
|
+
);
|
|
2521
|
+
};
|
|
2522
|
+
var DEFAULT_TEST_ID = "bottom-tab-bar";
|
|
2523
|
+
var SLIDER_HEIGHT = 3;
|
|
2524
|
+
var styles9 = reactNative.StyleSheet.create({
|
|
2525
|
+
bar: { flexDirection: "row", borderTopWidth: reactNative.StyleSheet.hairlineWidth },
|
|
2526
|
+
slider: { position: "absolute", top: 0, height: SLIDER_HEIGHT }
|
|
2527
|
+
});
|
|
2528
|
+
var BottomTabBar = ({
|
|
2529
|
+
items,
|
|
2530
|
+
pathname,
|
|
2531
|
+
onNavigate,
|
|
2532
|
+
tabFeedback = "default" /* Default */,
|
|
2533
|
+
navigateHint = (label) => label,
|
|
2534
|
+
regionLabel = "",
|
|
2535
|
+
containerStyle,
|
|
2536
|
+
testID = DEFAULT_TEST_ID
|
|
2537
|
+
}) => {
|
|
2538
|
+
const { theme } = uiFeedback.useUi();
|
|
2539
|
+
const reducedMotion = useReducedMotion();
|
|
2540
|
+
const primary = theme.palette.primary["500"];
|
|
2541
|
+
const activeRoute = resolveActiveRoute(items, pathname);
|
|
2542
|
+
const activeIndex = items.findIndex((item) => item.route === activeRoute);
|
|
2543
|
+
const bar = tabFeedback === "sliding-bar" /* SlidingBar */ ? slidingBarPosition(activeIndex, items.length) : null;
|
|
2544
|
+
const barMs = resolveTabFeedback(tabFeedback, { active: true, pressed: false }, reducedMotion).durationMs;
|
|
2545
|
+
const colors = { active: primary, rest: theme.colors.textSecondary, pill: theme.colors.surfaceElevated };
|
|
2546
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2547
|
+
reactNative.View,
|
|
2548
|
+
{
|
|
2549
|
+
"aria-label": regionLabel,
|
|
2550
|
+
role: "navigation",
|
|
2551
|
+
style: [styles9.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
|
|
2552
|
+
testID,
|
|
2553
|
+
children: [
|
|
2554
|
+
bar === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2555
|
+
reactNative.View,
|
|
2556
|
+
{
|
|
2557
|
+
pointerEvents: "none",
|
|
2558
|
+
style: [styles9.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
|
|
2559
|
+
}
|
|
2560
|
+
),
|
|
2561
|
+
items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2562
|
+
BottomTab,
|
|
2563
|
+
{
|
|
2564
|
+
colors,
|
|
2565
|
+
isActive: item.route === activeRoute,
|
|
2566
|
+
item,
|
|
2567
|
+
kind: tabFeedback,
|
|
2568
|
+
navigateHint,
|
|
2569
|
+
reducedMotion,
|
|
2570
|
+
onPress: onNavigate
|
|
2571
|
+
},
|
|
2572
|
+
item.key
|
|
2573
|
+
))
|
|
2574
|
+
]
|
|
2575
|
+
}
|
|
2576
|
+
);
|
|
2577
|
+
};
|
|
2578
|
+
|
|
2223
2579
|
exports.ACTIVE_BORDER_RADIUS = ACTIVE_BORDER_RADIUS;
|
|
2224
2580
|
exports.ACTIVE_TINT_OPACITY = ACTIVE_TINT_OPACITY;
|
|
2225
2581
|
exports.APP_SHELL_SUFFIX = APP_SHELL_SUFFIX;
|
|
2226
2582
|
exports.AppShell = AppShell;
|
|
2227
2583
|
exports.BASE_INDENT = BASE_INDENT;
|
|
2584
|
+
exports.BottomTabBar = BottomTabBar;
|
|
2228
2585
|
exports.CHEVRON_ICON_SIZE = CHEVRON_ICON_SIZE;
|
|
2229
2586
|
exports.CollapsedRail = CollapsedRail;
|
|
2230
2587
|
exports.CommandPalette = CommandPalette;
|
|
@@ -2240,6 +2597,7 @@ exports.NAV_TEST_IDS = NAV_TEST_IDS;
|
|
|
2240
2597
|
exports.Nav = Nav;
|
|
2241
2598
|
exports.NavBar = NavBar;
|
|
2242
2599
|
exports.NavExpandableItem = NavExpandableItem;
|
|
2600
|
+
exports.NavHoverFeedback = NavHoverFeedback;
|
|
2243
2601
|
exports.NavOverflowMenu = NavOverflowMenu;
|
|
2244
2602
|
exports.NavShell = NavShell;
|
|
2245
2603
|
exports.PillNav = PillNav;
|
|
@@ -2248,6 +2606,7 @@ exports.RAIL_FULL_BREAKPOINT = RAIL_FULL_BREAKPOINT;
|
|
|
2248
2606
|
exports.Sidebar = Sidebar;
|
|
2249
2607
|
exports.SidebarChevron = SidebarChevron;
|
|
2250
2608
|
exports.SidebarSearch = SidebarSearch;
|
|
2609
|
+
exports.TabFeedback = TabFeedback;
|
|
2251
2610
|
exports.Topbar = Topbar;
|
|
2252
2611
|
exports.accessibleNavItems = accessibleNavItems;
|
|
2253
2612
|
exports.collapsedRailStyles = collapsedRailStyles;
|