@dloizides/ui-nav 1.20.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,152 +1,13 @@
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';
6
- import { ModalDropdown } from '@dloizides/ui-layout';
6
+ 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;
@@ -640,15 +651,12 @@ var NavExpandableItem = ({
640
651
  )) }) })
641
652
  ] });
642
653
  };
643
-
644
- // src/searchAffordance.ts
645
- var DEFAULT_SEARCH_BREAKPOINT = 768;
654
+ var DEFAULT_SEARCH_BREAKPOINT = LAYOUT_COLLAPSE_BREAKPOINT;
646
655
  function resolveSearchAffordance(viewport, breakpoint = DEFAULT_SEARCH_BREAKPOINT) {
647
656
  return viewport >= breakpoint ? "inline" : "palette";
648
657
  }
649
658
  var FIELD_MIN_HEIGHT = 38;
650
659
  var CLEAR_GLYPH = "\u2715";
651
- var CLEAR_MIN_TARGET = 28;
652
660
  var styles2 = StyleSheet.create({
653
661
  wrap: {
654
662
  flexDirection: "row",
@@ -666,8 +674,8 @@ var styles2 = StyleSheet.create({
666
674
  minHeight: FIELD_MIN_HEIGHT
667
675
  },
668
676
  clear: {
669
- minWidth: CLEAR_MIN_TARGET,
670
- minHeight: CLEAR_MIN_TARGET,
677
+ minWidth: MIN_TOUCH_TARGET,
678
+ minHeight: MIN_TOUCH_TARGET,
671
679
  alignItems: "center",
672
680
  justifyContent: "center",
673
681
  borderRadius: 6
@@ -1397,7 +1405,6 @@ function useContentBody(state, children, testID) {
1397
1405
  }
1398
1406
  var SCRIM_COLOR = "rgba(0, 0, 0, 0.5)";
1399
1407
  var MENU_GLYPH2 = "\u2630";
1400
- var MENU_MIN_TARGET = 44;
1401
1408
  var MENU_GLYPH_FONT_SIZE = 20;
1402
1409
  var MENU_TOGGLE_PADDING = 12;
1403
1410
  var DRAWER_Z_INDEX = 50;
@@ -1435,8 +1442,8 @@ var styles4 = StyleSheet.create({
1435
1442
  // and receive presses instead of the scrim swallowing them.
1436
1443
  panel: { position: "absolute", top: 0, bottom: 0, left: 0, width: DRAWER_WIDTH, zIndex: PANEL_Z_INDEX },
1437
1444
  menuToggle: {
1438
- minWidth: MENU_MIN_TARGET,
1439
- minHeight: MENU_MIN_TARGET,
1445
+ minWidth: MIN_TOUCH_TARGET,
1446
+ minHeight: MIN_TOUCH_TARGET,
1440
1447
  alignItems: "center",
1441
1448
  justifyContent: "center",
1442
1449
  paddingHorizontal: MENU_TOGGLE_PADDING
@@ -1505,9 +1512,7 @@ var MobileDrawer = ({
1505
1512
  )
1506
1513
  ] });
1507
1514
  };
1508
-
1509
- // src/railMode.ts
1510
- var RAIL_FULL_BREAKPOINT = 768;
1515
+ var RAIL_FULL_BREAKPOINT = LAYOUT_COLLAPSE_BREAKPOINT;
1511
1516
  var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
1512
1517
  function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
1513
1518
  if (!hasSidebar) return "none";