@dloizides/ui-nav 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react';
2
- import { Platform, StyleSheet, Pressable, Text, View, TextInput, useWindowDimensions, ScrollView, ActivityIndicator } from 'react-native';
2
+ import { StyleSheet, Platform, Pressable, Text, View, TextInput, useWindowDimensions, ScrollView, ActivityIndicator } from 'react-native';
3
3
  import { useUi, UiProvider } from '@dloizides/ui-feedback';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
  import { Collapse } from '@dloizides/ui-motion';
@@ -7,146 +7,7 @@ import { LAYOUT_COLLAPSE_BREAKPOINT, ModalDropdown } from '@dloizides/ui-layout'
7
7
  import { resolveAccessibleRoutes } from '@dloizides/auth-web';
8
8
 
9
9
  // src/Sidebar.tsx
10
- var IS_WEB = Platform.OS === "web";
11
- var FOCUS_RING_WIDTH = 2;
12
- var FOCUS_RING_OFFSET = 2;
13
- var HOVER_TRANSITION_MS = 150;
14
- var TINT_TRANSITION_MS = 160;
15
- function focusRingStyle(focused, ringColor) {
16
- if (!IS_WEB || !focused) return void 0;
17
- const ring = {
18
- outlineWidth: FOCUS_RING_WIDTH,
19
- outlineStyle: "solid",
20
- outlineColor: ringColor,
21
- outlineOffset: FOCUS_RING_OFFSET
22
- };
23
- return ring;
24
- }
25
- function webTransition(properties, durationMs, reducedMotion) {
26
- if (!IS_WEB) return void 0;
27
- const transition = {
28
- transitionProperty: properties,
29
- transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
30
- };
31
- return transition;
32
- }
33
- function hoverTransitionStyle(reducedMotion) {
34
- return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
35
- }
36
- function tintTransitionStyle(reducedMotion) {
37
- return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
38
- }
39
- function rotateTransitionStyle(reducedMotion) {
40
- return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
41
- }
42
- var MIN_TARGET = 36;
43
- var styles = StyleSheet.create({
44
- trigger: {
45
- flexDirection: "row",
46
- alignItems: "center",
47
- justifyContent: "space-between",
48
- columnGap: 8,
49
- paddingHorizontal: 12,
50
- minHeight: MIN_TARGET,
51
- borderWidth: 1,
52
- borderRadius: 8
53
- },
54
- label: { fontSize: 14 },
55
- badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
56
- badgeText: { fontSize: 11, fontWeight: "700" }
57
- });
58
- var CommandPaletteTrigger = ({
59
- label,
60
- hint,
61
- shortcut,
62
- onPress,
63
- testID
64
- }) => {
65
- const { theme } = useUi();
66
- const colors = theme.colors;
67
- const primary = theme.palette.primary["500"];
68
- const [focused, setFocused] = React.useState(false);
69
- return /* @__PURE__ */ jsxs(
70
- Pressable,
71
- {
72
- accessibilityHint: hint,
73
- accessibilityLabel: label,
74
- accessibilityRole: "button",
75
- style: [
76
- styles.trigger,
77
- { backgroundColor: colors.surface, borderColor: colors.border },
78
- focusRingStyle(focused, primary)
79
- ],
80
- testID,
81
- onBlur: () => setFocused(false),
82
- onFocus: () => setFocused(true),
83
- onPress,
84
- children: [
85
- /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: colors.textSecondary }], children: label }),
86
- /* @__PURE__ */ jsx(View, { style: [styles.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsx(Text, { style: [styles.badgeText, { color: colors.textSecondary }], children: shortcut }) })
87
- ]
88
- }
89
- );
90
- };
91
-
92
- // src/sidebarFilter.ts
93
- function searchTokens(query) {
94
- return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
95
- }
96
- function labelMatches(item, tokens) {
97
- const label = item.label.toLowerCase();
98
- return tokens.every((token) => label.includes(token));
99
- }
100
- function filterNavItems(items, query) {
101
- const tokens = searchTokens(query);
102
- if (tokens.length === 0) return [...items];
103
- return filterByTokens(items, tokens);
104
- }
105
- function filterByTokens(items, tokens) {
106
- const kept = [];
107
- for (const item of items) {
108
- const selfMatch = labelMatches(item, tokens);
109
- const children = item.children ?? [];
110
- if (selfMatch) {
111
- kept.push(item);
112
- continue;
113
- }
114
- if (children.length === 0) continue;
115
- const prunedChildren = filterByTokens(children, tokens);
116
- if (prunedChildren.length > 0) kept.push({ ...item, children: prunedChildren });
117
- }
118
- return kept;
119
- }
120
- function isFilterActive(query) {
121
- return searchTokens(query).length > 0;
122
- }
123
-
124
- // src/isRouteActive.ts
125
- function isRouteActive(pathname, route, exact = false) {
126
- if (exact) return pathname === route;
127
- if (route === "/") return pathname === "/";
128
- return pathname === route || pathname.startsWith(`${route}/`);
129
- }
130
- function leafRoutes(items) {
131
- return items.flatMap((item) => {
132
- const children = item.children ?? [];
133
- const isGroup = children.length > 0;
134
- return isGroup ? leafRoutes(children) : [{ route: item.route, exact: item.exact === true }];
135
- });
136
- }
137
- function resolveActiveRoute(items, pathname) {
138
- let winner;
139
- let winnerLength = -1;
140
- for (const leaf of leafRoutes(items)) {
141
- const matches = isRouteActive(pathname, leaf.route, leaf.exact);
142
- const isLonger = leaf.route.length > winnerLength;
143
- if (matches && isLonger) {
144
- winner = leaf.route;
145
- winnerLength = leaf.route.length;
146
- }
147
- }
148
- return winner;
149
- }
10
+ var MIN_TOUCH_TARGET = 44;
150
11
  var ACTIVE_BORDER_RADIUS = 4;
151
12
  var ACTIVE_ACCENT_WIDTH = 3;
152
13
  var NAV_ROW_RADIUS = 8;
@@ -209,7 +70,15 @@ var navStyles = StyleSheet.create({
209
70
  userBlock: { marginHorizontal: 12, alignItems: "flex-end" },
210
71
  userName: { fontWeight: "600" },
211
72
  userEmail: { fontSize: 12 },
212
- accountBtn: { marginLeft: 8, paddingHorizontal: 10, paddingVertical: 6, borderRadius: 4 },
73
+ accountBtn: {
74
+ marginLeft: 8,
75
+ paddingHorizontal: 10,
76
+ paddingVertical: 6,
77
+ borderRadius: 4,
78
+ minHeight: MIN_TOUCH_TARGET,
79
+ minWidth: MIN_TOUCH_TARGET,
80
+ justifyContent: "center"
81
+ },
213
82
  accountText: { fontWeight: "600" },
214
83
  // --- Rich account-state header (Move 1c) ---
215
84
  accountTenant: { fontSize: 12 },
@@ -245,8 +114,8 @@ var navBarStyles = StyleSheet.create({
245
114
  paddingVertical: 6,
246
115
  borderRadius: 4,
247
116
  // a11y: keep the hamburger a comfortable ≥44×44 touch target.
248
- minWidth: 44,
249
- minHeight: 44,
117
+ minWidth: MIN_TOUCH_TARGET,
118
+ minHeight: MIN_TOUCH_TARGET,
250
119
  alignItems: "center",
251
120
  justifyContent: "center"
252
121
  },
@@ -288,7 +157,7 @@ var navBarStyles = StyleSheet.create({
288
157
  flexDirection: "row",
289
158
  alignItems: "center",
290
159
  // a11y: comfortable ≥44 tall touch target (rounded-pill hover/active affordance).
291
- minHeight: 44,
160
+ minHeight: MIN_TOUCH_TARGET,
292
161
  justifyContent: "center"
293
162
  },
294
163
  linkStacked: { paddingVertical: 10 },
@@ -324,7 +193,7 @@ var pillNavStyles = StyleSheet.create({
324
193
  paddingHorizontal: 16,
325
194
  paddingVertical: 8,
326
195
  borderRadius: 999,
327
- minHeight: 36,
196
+ minHeight: MIN_TOUCH_TARGET,
328
197
  flexDirection: "row",
329
198
  alignItems: "center",
330
199
  columnGap: 6,
@@ -345,7 +214,8 @@ var darkModeStyles = StyleSheet.create({
345
214
  segment: {
346
215
  paddingHorizontal: 12,
347
216
  paddingVertical: 8,
348
- minHeight: 36,
217
+ minHeight: MIN_TOUCH_TARGET,
218
+ minWidth: MIN_TOUCH_TARGET,
349
219
  flexDirection: "row",
350
220
  alignItems: "center",
351
221
  columnGap: 6,
@@ -356,8 +226,8 @@ var darkModeStyles = StyleSheet.create({
356
226
  cycle: {
357
227
  paddingHorizontal: 12,
358
228
  paddingVertical: 8,
359
- minHeight: 44,
360
- minWidth: 44,
229
+ minHeight: MIN_TOUCH_TARGET,
230
+ minWidth: MIN_TOUCH_TARGET,
361
231
  borderRadius: 8,
362
232
  flexDirection: "row",
363
233
  alignItems: "center",
@@ -402,6 +272,7 @@ var expandableStyles = StyleSheet.create({
402
272
  flexDirection: "row",
403
273
  alignItems: "center",
404
274
  paddingVertical: 9,
275
+ minHeight: MIN_TOUCH_TARGET,
405
276
  borderLeftWidth: ACTIVE_ACCENT_WIDTH,
406
277
  borderLeftColor: "transparent"
407
278
  },
@@ -429,7 +300,8 @@ var expandableStyles = StyleSheet.create({
429
300
  borderRadius: NAV_ROW_RADIUS,
430
301
  flexDirection: "row",
431
302
  alignItems: "center",
432
- paddingVertical: 8
303
+ paddingVertical: 8,
304
+ minHeight: MIN_TOUCH_TARGET
433
305
  },
434
306
  // Depth-0 module groups render as docs-style SECTION HEADERS: extra top rhythm
435
307
  // to separate one module from the next.
@@ -449,6 +321,145 @@ var expandableStyles = StyleSheet.create({
449
321
  var BASE_INDENT = 12;
450
322
  var NAV_ICON_SIZE = 14;
451
323
  var CHEVRON_ICON_SIZE = 12;
324
+ var IS_WEB = Platform.OS === "web";
325
+ var FOCUS_RING_WIDTH = 2;
326
+ var FOCUS_RING_OFFSET = 2;
327
+ var HOVER_TRANSITION_MS = 150;
328
+ var TINT_TRANSITION_MS = 160;
329
+ function focusRingStyle(focused, ringColor) {
330
+ if (!IS_WEB || !focused) return void 0;
331
+ const ring = {
332
+ outlineWidth: FOCUS_RING_WIDTH,
333
+ outlineStyle: "solid",
334
+ outlineColor: ringColor,
335
+ outlineOffset: FOCUS_RING_OFFSET
336
+ };
337
+ return ring;
338
+ }
339
+ function webTransition(properties, durationMs, reducedMotion) {
340
+ if (!IS_WEB) return void 0;
341
+ const transition = {
342
+ transitionProperty: properties,
343
+ transitionDuration: reducedMotion ? "0ms" : `${durationMs}ms`
344
+ };
345
+ return transition;
346
+ }
347
+ function hoverTransitionStyle(reducedMotion) {
348
+ return webTransition("color, background-color", HOVER_TRANSITION_MS, reducedMotion);
349
+ }
350
+ function tintTransitionStyle(reducedMotion) {
351
+ return webTransition("opacity", TINT_TRANSITION_MS, reducedMotion);
352
+ }
353
+ function rotateTransitionStyle(reducedMotion) {
354
+ return webTransition("transform", TINT_TRANSITION_MS, reducedMotion);
355
+ }
356
+ var styles = StyleSheet.create({
357
+ trigger: {
358
+ flexDirection: "row",
359
+ alignItems: "center",
360
+ justifyContent: "space-between",
361
+ columnGap: 8,
362
+ paddingHorizontal: 12,
363
+ minHeight: MIN_TOUCH_TARGET,
364
+ borderWidth: 1,
365
+ borderRadius: 8
366
+ },
367
+ label: { fontSize: 14 },
368
+ badge: { paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, borderWidth: 1 },
369
+ badgeText: { fontSize: 11, fontWeight: "700" }
370
+ });
371
+ var CommandPaletteTrigger = ({
372
+ label,
373
+ hint,
374
+ shortcut,
375
+ onPress,
376
+ testID
377
+ }) => {
378
+ const { theme } = useUi();
379
+ const colors = theme.colors;
380
+ const primary = theme.palette.primary["500"];
381
+ const [focused, setFocused] = React.useState(false);
382
+ return /* @__PURE__ */ jsxs(
383
+ Pressable,
384
+ {
385
+ accessibilityHint: hint,
386
+ accessibilityLabel: label,
387
+ accessibilityRole: "button",
388
+ style: [
389
+ styles.trigger,
390
+ { backgroundColor: colors.surface, borderColor: colors.border },
391
+ focusRingStyle(focused, primary)
392
+ ],
393
+ testID,
394
+ onBlur: () => setFocused(false),
395
+ onFocus: () => setFocused(true),
396
+ onPress,
397
+ children: [
398
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, { color: colors.textSecondary }], children: label }),
399
+ /* @__PURE__ */ jsx(View, { style: [styles.badge, { borderColor: colors.border }], children: /* @__PURE__ */ jsx(Text, { style: [styles.badgeText, { color: colors.textSecondary }], children: shortcut }) })
400
+ ]
401
+ }
402
+ );
403
+ };
404
+
405
+ // src/sidebarFilter.ts
406
+ function searchTokens(query) {
407
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
408
+ }
409
+ function labelMatches(item, tokens) {
410
+ const label = item.label.toLowerCase();
411
+ return tokens.every((token) => label.includes(token));
412
+ }
413
+ function filterNavItems(items, query) {
414
+ const tokens = searchTokens(query);
415
+ if (tokens.length === 0) return [...items];
416
+ return filterByTokens(items, tokens);
417
+ }
418
+ function filterByTokens(items, tokens) {
419
+ const kept = [];
420
+ for (const item of items) {
421
+ const selfMatch = labelMatches(item, tokens);
422
+ const children = item.children ?? [];
423
+ if (selfMatch) {
424
+ kept.push(item);
425
+ continue;
426
+ }
427
+ if (children.length === 0) continue;
428
+ const prunedChildren = filterByTokens(children, tokens);
429
+ if (prunedChildren.length > 0) kept.push({ ...item, children: prunedChildren });
430
+ }
431
+ return kept;
432
+ }
433
+ function isFilterActive(query) {
434
+ return searchTokens(query).length > 0;
435
+ }
436
+
437
+ // src/isRouteActive.ts
438
+ function isRouteActive(pathname, route, exact = false) {
439
+ if (exact) return pathname === route;
440
+ if (route === "/") return pathname === "/";
441
+ return pathname === route || pathname.startsWith(`${route}/`);
442
+ }
443
+ function leafRoutes(items) {
444
+ return items.flatMap((item) => {
445
+ const children = item.children ?? [];
446
+ const isGroup = children.length > 0;
447
+ return isGroup ? leafRoutes(children) : [{ route: item.route, exact: item.exact === true }];
448
+ });
449
+ }
450
+ function resolveActiveRoute(items, pathname) {
451
+ let winner;
452
+ let winnerLength = -1;
453
+ for (const leaf of leafRoutes(items)) {
454
+ const matches = isRouteActive(pathname, leaf.route, leaf.exact);
455
+ const isLonger = leaf.route.length > winnerLength;
456
+ if (matches && isLonger) {
457
+ winner = leaf.route;
458
+ winnerLength = leaf.route.length;
459
+ }
460
+ }
461
+ return winner;
462
+ }
452
463
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
453
464
  function getReducedMotionQuery() {
454
465
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
@@ -646,7 +657,6 @@ function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOIN
646
657
  }
647
658
  var FIELD_MIN_HEIGHT = 38;
648
659
  var CLEAR_GLYPH = "\u2715";
649
- var CLEAR_MIN_TARGET = 28;
650
660
  var styles2 = StyleSheet.create({
651
661
  wrap: {
652
662
  flexDirection: "row",
@@ -664,8 +674,8 @@ var styles2 = StyleSheet.create({
664
674
  minHeight: FIELD_MIN_HEIGHT
665
675
  },
666
676
  clear: {
667
- minWidth: CLEAR_MIN_TARGET,
668
- minHeight: CLEAR_MIN_TARGET,
677
+ minWidth: MIN_TOUCH_TARGET,
678
+ minHeight: MIN_TOUCH_TARGET,
669
679
  alignItems: "center",
670
680
  justifyContent: "center",
671
681
  borderRadius: 6
@@ -1395,7 +1405,6 @@ function useContentBody(state, children, testID) {
1395
1405
  }
1396
1406
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
1397
1407
  var MENU_GLYPH2 = "\u2630";
1398
- var MENU_MIN_TARGET = 44;
1399
1408
  var MENU_GLYPH_FONT_SIZE = 20;
1400
1409
  var MENU_TOGGLE_PADDING = 12;
1401
1410
  var DRAWER_Z_INDEX = 50;
@@ -1433,8 +1442,8 @@ var styles4 = StyleSheet.create({
1433
1442
  // and receive presses instead of the scrim swallowing them.
1434
1443
  panel: { position: "absolute", top: 0, bottom: 0, left: 0, width: DRAWER_WIDTH, zIndex: PANEL_Z_INDEX },
1435
1444
  menuToggle: {
1436
- minWidth: MENU_MIN_TARGET,
1437
- minHeight: MENU_MIN_TARGET,
1445
+ minWidth: MIN_TOUCH_TARGET,
1446
+ minHeight: MIN_TOUCH_TARGET,
1438
1447
  alignItems: "center",
1439
1448
  justifyContent: "center",
1440
1449
  paddingHorizontal: MENU_TOGGLE_PADDING