@nori-ui/core 1.8.0 → 1.9.1

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.
Files changed (39) hide show
  1. package/dist/{chunk-PLQPBMG2.js → chunk-BXZGCOKT.js} +2 -2
  2. package/dist/{chunk-PLQPBMG2.js.map → chunk-BXZGCOKT.js.map} +1 -1
  3. package/dist/{chunk-RI4Y2C5U.js → chunk-KLK7OMFT.js} +3 -3
  4. package/dist/{chunk-RI4Y2C5U.js.map → chunk-KLK7OMFT.js.map} +1 -1
  5. package/dist/chunk-OHWRTHGL.js +495 -0
  6. package/dist/chunk-OHWRTHGL.js.map +1 -0
  7. package/dist/{chunk-V5QSMDZL.js → chunk-QB6RH6UU.js} +3 -3
  8. package/dist/{chunk-V5QSMDZL.js.map → chunk-QB6RH6UU.js.map} +1 -1
  9. package/dist/chunk-S763GTIZ.js +350 -0
  10. package/dist/chunk-S763GTIZ.js.map +1 -0
  11. package/dist/chunk-UJRVWGK7.js +3 -0
  12. package/dist/chunk-UJRVWGK7.js.map +1 -0
  13. package/dist/client.cjs +2248 -1424
  14. package/dist/client.cjs.map +1 -1
  15. package/dist/client.d.cts +2 -0
  16. package/dist/client.d.ts +2 -0
  17. package/dist/client.js +13 -10
  18. package/dist/client.js.map +1 -1
  19. package/dist/components/Accordion/index.js +2 -2
  20. package/dist/components/Command/index.cjs +1371 -0
  21. package/dist/components/Command/index.cjs.map +1 -0
  22. package/dist/components/Command/index.d.cts +89 -0
  23. package/dist/components/Command/index.d.ts +89 -0
  24. package/dist/components/Command/index.js +11 -0
  25. package/dist/components/Command/index.js.map +1 -0
  26. package/dist/components/Dialog/index.js +2 -1
  27. package/dist/components/Sidebar/index.cjs +675 -0
  28. package/dist/components/Sidebar/index.cjs.map +1 -0
  29. package/dist/components/Sidebar/index.d.cts +109 -0
  30. package/dist/components/Sidebar/index.d.ts +109 -0
  31. package/dist/components/Sidebar/index.js +7 -0
  32. package/dist/components/Sidebar/index.js.map +1 -0
  33. package/dist/components/Switch/index.js +2 -2
  34. package/dist/index.cjs +2248 -1424
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +2 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +13 -10
  39. package/package.json +1 -1
@@ -0,0 +1,1371 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var reactNative = require('react-native');
5
+ var jsxRuntime = require('nativewind/jsx-runtime');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+
10
+ // src/theme/px.ts
11
+ function px(value) {
12
+ if (typeof value === "number") {
13
+ return value;
14
+ }
15
+ const n = Number.parseFloat(value);
16
+ return Number.isFinite(n) ? n : 0;
17
+ }
18
+ __name(px, "px");
19
+
20
+ // ../tokens/build/theme.ts
21
+ var theme = {
22
+ color: {
23
+ danger: "#ef4444",
24
+ info: "#3b82f6",
25
+ neutral: {
26
+ "100": "#f4f4f5",
27
+ "200": "#e4e4e7",
28
+ "300": "#d4d4d8",
29
+ "400": "#a1a1aa",
30
+ "50": "#fafafa",
31
+ "500": "#71717a",
32
+ "600": "#52525b",
33
+ "700": "#3f3f46",
34
+ "800": "#27272a",
35
+ "900": "#18181b"
36
+ },
37
+ primary: {
38
+ "100": "#ccfbf1",
39
+ "200": "#99f6e4",
40
+ "300": "#5eead4",
41
+ "400": "#2dd4bf",
42
+ "50": "#f0fdfa",
43
+ "500": "#14b8a6",
44
+ "600": "#0d9488",
45
+ "700": "#0f766e",
46
+ "800": "#115e59",
47
+ "900": "#134e4a"
48
+ },
49
+ success: "#22c55e",
50
+ warning: "#f59e0b"
51
+ },
52
+ fontFamily: {
53
+ body: "system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
54
+ display: "ui-serif, Georgia, 'Times New Roman', serif",
55
+ mono: "ui-monospace, 'SF Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace"
56
+ },
57
+ fontSize: {
58
+ "2xl": "24px",
59
+ "3xl": "30px",
60
+ "4xl": "36px",
61
+ lg: "18px",
62
+ md: "16px",
63
+ sm: "14px",
64
+ xl: "20px",
65
+ xs: "12px"
66
+ },
67
+ fontWeight: {
68
+ bold: "700",
69
+ medium: "500",
70
+ regular: "400",
71
+ semibold: "600"
72
+ },
73
+ lineHeight: {
74
+ normal: "1.4",
75
+ relaxed: "1.6",
76
+ tight: "1.2"
77
+ },
78
+ radius: {
79
+ "2xl": "16px",
80
+ full: "9999px",
81
+ lg: "8px",
82
+ md: "6px",
83
+ none: "0px",
84
+ sm: "4px",
85
+ xl: "12px"
86
+ },
87
+ semantic: {
88
+ background: {
89
+ default: "#fafafa",
90
+ elevated: "#ffffff",
91
+ subtle: "#f4f4f5"
92
+ },
93
+ border: {
94
+ default: "#e4e4e7",
95
+ strong: "#d4d4d8"
96
+ },
97
+ interactive: {
98
+ destructive: "#ef4444",
99
+ primary: "#0d9488",
100
+ primaryHover: "#0f766e",
101
+ primaryPressed: "#115e59"
102
+ },
103
+ text: {
104
+ default: "#18181b",
105
+ inverted: "#fafafa",
106
+ muted: "#52525b"
107
+ }
108
+ },
109
+ shadow: {
110
+ lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
111
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
112
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
113
+ },
114
+ spacing: {
115
+ "0": "0px",
116
+ "1": "4px",
117
+ "10": "40px",
118
+ "12": "48px",
119
+ "16": "64px",
120
+ "2": "8px",
121
+ "20": "80px",
122
+ "24": "96px",
123
+ "3": "12px",
124
+ "4": "16px",
125
+ "5": "20px",
126
+ "6": "24px",
127
+ "8": "32px"
128
+ }
129
+ };
130
+ var themeDark = {
131
+ color: {
132
+ danger: "#ef4444",
133
+ info: "#3b82f6",
134
+ neutral: {
135
+ "100": "#f4f4f5",
136
+ "200": "#e4e4e7",
137
+ "300": "#d4d4d8",
138
+ "400": "#a1a1aa",
139
+ "50": "#fafafa",
140
+ "500": "#71717a",
141
+ "600": "#52525b",
142
+ "700": "#3f3f46",
143
+ "800": "#27272a",
144
+ "900": "#18181b"
145
+ },
146
+ primary: {
147
+ "100": "#ccfbf1",
148
+ "200": "#99f6e4",
149
+ "300": "#5eead4",
150
+ "400": "#2dd4bf",
151
+ "50": "#f0fdfa",
152
+ "500": "#14b8a6",
153
+ "600": "#0d9488",
154
+ "700": "#0f766e",
155
+ "800": "#115e59",
156
+ "900": "#134e4a"
157
+ },
158
+ success: "#22c55e",
159
+ warning: "#f59e0b"
160
+ },
161
+ fontFamily: {
162
+ body: "system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
163
+ display: "ui-serif, Georgia, 'Times New Roman', serif",
164
+ mono: "ui-monospace, 'SF Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace"
165
+ },
166
+ fontSize: {
167
+ "2xl": "24px",
168
+ "3xl": "30px",
169
+ "4xl": "36px",
170
+ lg: "18px",
171
+ md: "16px",
172
+ sm: "14px",
173
+ xl: "20px",
174
+ xs: "12px"
175
+ },
176
+ fontWeight: {
177
+ bold: "700",
178
+ medium: "500",
179
+ regular: "400",
180
+ semibold: "600"
181
+ },
182
+ lineHeight: {
183
+ normal: "1.4",
184
+ relaxed: "1.6",
185
+ tight: "1.2"
186
+ },
187
+ radius: {
188
+ "2xl": "16px",
189
+ full: "9999px",
190
+ lg: "8px",
191
+ md: "6px",
192
+ none: "0px",
193
+ sm: "4px",
194
+ xl: "12px"
195
+ },
196
+ semantic: {
197
+ background: {
198
+ default: "#18181b",
199
+ elevated: "#3f3f46",
200
+ subtle: "#27272a"
201
+ },
202
+ border: {
203
+ default: "#3f3f46",
204
+ strong: "#52525b"
205
+ },
206
+ interactive: {
207
+ destructive: "#ef4444",
208
+ primary: "#2dd4bf",
209
+ primaryHover: "#5eead4",
210
+ primaryPressed: "#99f6e4"
211
+ },
212
+ text: {
213
+ default: "#fafafa",
214
+ inverted: "#18181b",
215
+ muted: "#a1a1aa"
216
+ }
217
+ },
218
+ shadow: {
219
+ lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
220
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
221
+ sm: "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
222
+ },
223
+ spacing: {
224
+ "0": "0px",
225
+ "1": "4px",
226
+ "10": "40px",
227
+ "12": "48px",
228
+ "16": "64px",
229
+ "2": "8px",
230
+ "20": "80px",
231
+ "24": "96px",
232
+ "3": "12px",
233
+ "4": "16px",
234
+ "5": "20px",
235
+ "6": "24px",
236
+ "8": "32px"
237
+ }
238
+ };
239
+ var defaultTheme = {
240
+ light: theme,
241
+ dark: themeDark
242
+ };
243
+ var ThemeContext = react.createContext(defaultTheme);
244
+ ThemeContext.displayName = "ThemeContext";
245
+ var ColorSchemeOverrideContext = react.createContext(null);
246
+ ColorSchemeOverrideContext.displayName = "ColorSchemeOverrideContext";
247
+ var isWeb = reactNative.Platform.OS === "web";
248
+ function readWebScheme() {
249
+ if (typeof document === "undefined") {
250
+ return "light";
251
+ }
252
+ const root = document.documentElement;
253
+ if (root.classList.contains("dark")) {
254
+ return "dark";
255
+ }
256
+ if (root.getAttribute("data-theme") === "dark") {
257
+ return "dark";
258
+ }
259
+ return "light";
260
+ }
261
+ __name(readWebScheme, "readWebScheme");
262
+ function useColorScheme() {
263
+ const override = react.useContext(ColorSchemeOverrideContext);
264
+ const [scheme, setScheme] = react.useState(() => {
265
+ if (isWeb) {
266
+ return readWebScheme();
267
+ }
268
+ return reactNative.Appearance.getColorScheme() ?? "light";
269
+ });
270
+ react.useEffect(() => {
271
+ if (isWeb) {
272
+ const root = document.documentElement;
273
+ const update = /* @__PURE__ */ __name(() => setScheme(readWebScheme()), "update");
274
+ const observer = new MutationObserver(update);
275
+ observer.observe(root, { attributes: true, attributeFilter: ["class", "data-theme"] });
276
+ update();
277
+ return () => observer.disconnect();
278
+ }
279
+ const sub = reactNative.Appearance.addChangeListener(({ colorScheme }) => {
280
+ setScheme(colorScheme ?? "light");
281
+ });
282
+ return () => sub.remove();
283
+ }, []);
284
+ return override ?? scheme;
285
+ }
286
+ __name(useColorScheme, "useColorScheme");
287
+
288
+ // src/theme/use-theme-colors.ts
289
+ function useThemeColors() {
290
+ const scheme = useColorScheme();
291
+ const themePair = react.useContext(ThemeContext);
292
+ return scheme === "dark" ? themePair.dark : themePair.light;
293
+ }
294
+ __name(useThemeColors, "useThemeColors");
295
+
296
+ // src/utils/cn.ts
297
+ function cn(...inputs) {
298
+ const out = [];
299
+ for (const input of inputs) {
300
+ append(out, input);
301
+ }
302
+ return out.join(" ");
303
+ }
304
+ __name(cn, "cn");
305
+ function append(out, input) {
306
+ if (!input) {
307
+ return;
308
+ }
309
+ if (typeof input === "string") {
310
+ if (input.length > 0) {
311
+ out.push(input);
312
+ }
313
+ return;
314
+ }
315
+ if (typeof input === "number") {
316
+ return;
317
+ }
318
+ if (Array.isArray(input)) {
319
+ for (const inner of input) {
320
+ append(out, inner);
321
+ }
322
+ return;
323
+ }
324
+ if (typeof input === "object") {
325
+ for (const key of Object.keys(input)) {
326
+ if (input[key]) {
327
+ out.push(key);
328
+ }
329
+ }
330
+ }
331
+ }
332
+ __name(append, "append");
333
+ var isWeb2 = reactNative.Platform.OS === "web";
334
+ var make = /* @__PURE__ */ __name(({ path, glyph }) => /* @__PURE__ */ __name(function PlaceholderIcon({ size = 20, color = "currentColor" }) {
335
+ const colors = useThemeColors();
336
+ if (isWeb2) {
337
+ return /* @__PURE__ */ jsxRuntime.jsx(
338
+ "svg",
339
+ {
340
+ width: size,
341
+ height: size,
342
+ viewBox: "0 0 24 24",
343
+ fill: "none",
344
+ stroke: color,
345
+ strokeWidth: "2",
346
+ strokeLinecap: "round",
347
+ strokeLinejoin: "round",
348
+ "aria-hidden": "true",
349
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: path })
350
+ }
351
+ );
352
+ }
353
+ const resolvedColor = color === "currentColor" ? colors.semantic.text.default : color;
354
+ return /* @__PURE__ */ jsxRuntime.jsx(
355
+ reactNative.Text,
356
+ {
357
+ accessibilityElementsHidden: true,
358
+ importantForAccessibility: "no-hide-descendants",
359
+ style: { fontSize: size, lineHeight: size, color: resolvedColor },
360
+ children: glyph
361
+ }
362
+ );
363
+ }, "PlaceholderIcon"), "make");
364
+ var defaultSemanticIcons = {
365
+ checkmark: make({ path: "M20 6 9 17l-5-5", glyph: "\u2713" }),
366
+ close: make({ path: "M18 6 6 18 M6 6l12 12", glyph: "\u2715" }),
367
+ eye: make({
368
+ path: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z",
369
+ glyph: "\u{1F441}"
370
+ }),
371
+ eyeOff: make({
372
+ path: "M17.94 17.94A10 10 0 0 1 2 12s3.5-7 10-7c2 0 3.8.6 5.4 1.5 M1 1l22 22",
373
+ glyph: "\u{1F648}"
374
+ }),
375
+ chevronDown: make({ path: "m6 9 6 6 6-6", glyph: "\u2304" }),
376
+ chevronUp: make({ path: "m18 15-6-6-6 6", glyph: "\u2303" }),
377
+ alertTriangle: make({
378
+ path: "M12 9v4 M12 17h.01 M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z",
379
+ glyph: "\u26A0"
380
+ }),
381
+ info: make({
382
+ path: "M12 8h.01 M11 12h1v4h1 M12 22C6.48 22 2 17.52 2 12 2 6.48 6.48 2 12 2c5.52 0 10 4.48 10 10 0 5.52-4.48 10-10 10z",
383
+ glyph: "\u24D8"
384
+ }),
385
+ check: make({ path: "M20 6 9 17l-5-5", glyph: "\u2713" }),
386
+ x: make({ path: "M18 6 6 18 M6 6l12 12", glyph: "\u2715" })
387
+ };
388
+
389
+ // src/slot/compose-refs.ts
390
+ function composeRefs(...refs) {
391
+ return (node) => {
392
+ for (const ref of refs) {
393
+ if (ref == null) {
394
+ continue;
395
+ }
396
+ if (typeof ref === "function") {
397
+ ref(node);
398
+ } else {
399
+ ref.current = node;
400
+ }
401
+ }
402
+ };
403
+ }
404
+ __name(composeRefs, "composeRefs");
405
+ var Slot = react.forwardRef(/* @__PURE__ */ __name(function Slot2(props, forwardedRef) {
406
+ const { children, ...slotProps } = props;
407
+ if (!react.isValidElement(children)) {
408
+ return null;
409
+ }
410
+ const child = react.Children.only(children);
411
+ const merged = mergeProps(slotProps, child.props);
412
+ const childRef = child.ref;
413
+ if (forwardedRef || childRef) {
414
+ merged.ref = composeRefs(forwardedRef, childRef);
415
+ }
416
+ return react.cloneElement(child, merged);
417
+ }, "Slot"));
418
+ Slot.displayName = "Slot";
419
+ function mergeProps(outer, inner) {
420
+ const merged = { ...outer };
421
+ for (const key of Object.keys(inner)) {
422
+ const outerValue = outer[key];
423
+ const innerValue = inner[key];
424
+ if (key === "className" || key === "class") {
425
+ merged[key] = joinClass(outerValue, innerValue);
426
+ continue;
427
+ }
428
+ if (key === "style") {
429
+ merged[key] = {
430
+ ...outerValue,
431
+ ...innerValue
432
+ };
433
+ continue;
434
+ }
435
+ if (isEventHandler(key, outerValue, innerValue)) {
436
+ merged[key] = composeHandlers(outerValue, innerValue);
437
+ continue;
438
+ }
439
+ merged[key] = innerValue;
440
+ }
441
+ return merged;
442
+ }
443
+ __name(mergeProps, "mergeProps");
444
+ function joinClass(outer, inner) {
445
+ const a = typeof outer === "string" ? outer : "";
446
+ const b = typeof inner === "string" ? inner : "";
447
+ const joined = [a, b].filter(Boolean).join(" ");
448
+ return joined.length > 0 ? joined : void 0;
449
+ }
450
+ __name(joinClass, "joinClass");
451
+ function isEventHandler(key, outer, inner) {
452
+ if (!key.startsWith("on") || key.length < 3) {
453
+ return false;
454
+ }
455
+ if (key[2] !== key[2]?.toUpperCase()) {
456
+ return false;
457
+ }
458
+ return typeof outer === "function" && typeof inner === "function";
459
+ }
460
+ __name(isEventHandler, "isEventHandler");
461
+ function composeHandlers(outer, inner) {
462
+ return (...args) => {
463
+ outer(...args);
464
+ inner(...args);
465
+ };
466
+ }
467
+ __name(composeHandlers, "composeHandlers");
468
+
469
+ // src/components/Dialog/blur-backdrop.tsx
470
+ var BlurBackdrop = /* @__PURE__ */ __name((_props) => {
471
+ return null;
472
+ }, "BlurBackdrop");
473
+ var DialogContext = react.createContext(null);
474
+ var useDialogContext = /* @__PURE__ */ __name((label) => {
475
+ const ctx = react.useContext(DialogContext);
476
+ if (!ctx) {
477
+ throw new Error(`<${label}> must be rendered inside a <Dialog>.`);
478
+ }
479
+ return ctx;
480
+ }, "useDialogContext");
481
+ var DialogRoot = /* @__PURE__ */ __name(({ open, defaultOpen = false, onOpenChange, children }) => {
482
+ const [inner, setInner] = react.useState(defaultOpen);
483
+ const isControlled = open !== void 0;
484
+ const current = isControlled ? open : inner;
485
+ const setOpen = react.useCallback(
486
+ (next) => {
487
+ if (!isControlled) {
488
+ setInner(next);
489
+ }
490
+ onOpenChange?.(next);
491
+ },
492
+ [isControlled, onOpenChange]
493
+ );
494
+ const baseId = react.useId();
495
+ const triggerRef = react.useRef(null);
496
+ const ctxValue = {
497
+ open: current,
498
+ setOpen,
499
+ titleId: `${baseId}-title`,
500
+ descriptionId: `${baseId}-description`,
501
+ triggerRef
502
+ };
503
+ return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value: ctxValue, children });
504
+ }, "DialogRoot");
505
+ var DialogTrigger = /* @__PURE__ */ __name(({ asChild = true, children, className, testID }) => {
506
+ const ctx = useDialogContext("DialogTrigger");
507
+ const onPress = react.useCallback(() => ctx.setOpen(true), [ctx]);
508
+ if (asChild && react.isValidElement(children)) {
509
+ const child = children;
510
+ const fire = /* @__PURE__ */ __name((existing) => (event) => {
511
+ existing?.(event);
512
+ ctx.setOpen(true);
513
+ }, "fire");
514
+ return /* @__PURE__ */ jsxRuntime.jsx(
515
+ Slot,
516
+ {
517
+ ref: (node) => {
518
+ ctx.triggerRef.current = node;
519
+ },
520
+ onClick: fire(child.props.onClick),
521
+ onPress: fire(child.props.onPress),
522
+ ...testID !== void 0 ? { "data-testid": testID } : {},
523
+ ...className !== void 0 ? { className } : {},
524
+ children: child
525
+ }
526
+ );
527
+ }
528
+ return /* @__PURE__ */ jsxRuntime.jsx(
529
+ reactNative.Pressable,
530
+ {
531
+ ref: (node) => {
532
+ ctx.triggerRef.current = node;
533
+ },
534
+ onPress,
535
+ ...testID !== void 0 ? { testID } : {},
536
+ ...className !== void 0 ? { className } : {},
537
+ children: wrapStringChildren(children)
538
+ }
539
+ );
540
+ }, "DialogTrigger");
541
+ function wrapStringChildren(children) {
542
+ if (typeof children === "string" || typeof children === "number") {
543
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { children });
544
+ }
545
+ return children;
546
+ }
547
+ __name(wrapStringChildren, "wrapStringChildren");
548
+ var SCRIM_COLOR = "rgba(0, 0, 0, 0.24)";
549
+ var BLUR_AMOUNT = 4;
550
+ var OVERLAY_LAYOUT_BASE = {
551
+ position: reactNative.Platform.OS === "web" ? "fixed" : "absolute",
552
+ top: 0,
553
+ left: 0,
554
+ right: 0,
555
+ bottom: 0,
556
+ alignItems: "center",
557
+ justifyContent: "center",
558
+ // On native the BlurBackdrop sibling renders BEHIND this overlay and
559
+ // already provides dim + frosted-glass via expo-blur's `tint`/`intensity`.
560
+ // Painting SCRIM_COLOR on top would mask the blur entirely (the user
561
+ // sees only a flat tint), so the overlay stays transparent and the
562
+ // BlurView is the dominant visual on native.
563
+ ...reactNative.Platform.OS === "web" ? { zIndex: 50 } : { backgroundColor: "transparent" }
564
+ };
565
+ var CONTENT_LAYOUT_BASE = {
566
+ width: "100%",
567
+ maxWidth: 480,
568
+ // component-density literal — not from theme
569
+ ...reactNative.Platform.OS === "web" ? {
570
+ boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
571
+ } : { elevation: 24 }
572
+ };
573
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
574
+ var DialogContent = /* @__PURE__ */ __name(({ children, className, testID }) => {
575
+ const ctx = useDialogContext("DialogContent");
576
+ const colors = useThemeColors();
577
+ const scheme = useColorScheme();
578
+ const contentRef = react.useRef(null);
579
+ const overlayStyle = {
580
+ ...OVERLAY_LAYOUT_BASE,
581
+ padding: px(colors.spacing["4"])
582
+ };
583
+ const contentStyle = {
584
+ ...CONTENT_LAYOUT_BASE,
585
+ borderRadius: px(colors.radius.xl),
586
+ padding: px(colors.spacing["6"]),
587
+ gap: px(colors.spacing["3"])
588
+ };
589
+ const [entered, setEntered] = react.useState(false);
590
+ react.useEffect(() => {
591
+ if (reactNative.Platform.OS !== "web") {
592
+ setEntered(true);
593
+ return;
594
+ }
595
+ if (!ctx.open) {
596
+ setEntered(false);
597
+ return;
598
+ }
599
+ const id = requestAnimationFrame(() => setEntered(true));
600
+ return () => cancelAnimationFrame(id);
601
+ }, [ctx.open]);
602
+ const enterStyle = reactNative.Platform.OS === "web" ? {
603
+ opacity: entered ? 1 : 0,
604
+ transform: [{ scale: entered ? 1 : 0.96 }],
605
+ transitionProperty: "opacity, transform",
606
+ transitionDuration: "150ms",
607
+ transitionTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)"
608
+ } : {};
609
+ const overlayDomRef = react.useRef(null);
610
+ react.useEffect(() => {
611
+ if (reactNative.Platform.OS !== "web") {
612
+ return;
613
+ }
614
+ const node = overlayDomRef.current;
615
+ if (!node) {
616
+ return;
617
+ }
618
+ node.style.transitionProperty = "background-color, backdrop-filter, -webkit-backdrop-filter";
619
+ node.style.transitionDuration = "150ms, 200ms, 200ms";
620
+ node.style.transitionTimingFunction = "ease-out";
621
+ if (entered) {
622
+ node.style.backgroundColor = SCRIM_COLOR;
623
+ node.style.backdropFilter = `blur(${BLUR_AMOUNT}px)`;
624
+ node.style.setProperty("-webkit-backdrop-filter", `blur(${BLUR_AMOUNT}px)`);
625
+ } else {
626
+ node.style.backgroundColor = "rgba(0, 0, 0, 0)";
627
+ node.style.backdropFilter = "blur(0px)";
628
+ node.style.setProperty("-webkit-backdrop-filter", "blur(0px)");
629
+ }
630
+ }, [entered]);
631
+ react.useEffect(() => {
632
+ if (!ctx.open) {
633
+ return;
634
+ }
635
+ if (reactNative.Platform.OS !== "web") {
636
+ return;
637
+ }
638
+ if (typeof document === "undefined") {
639
+ return;
640
+ }
641
+ const previouslyFocused = document.activeElement;
642
+ const prevBodyOverflow = document.body.style.overflow;
643
+ document.body.style.overflow = "hidden";
644
+ const focusFirst = /* @__PURE__ */ __name(() => {
645
+ const node = contentRef.current;
646
+ if (!node) {
647
+ return;
648
+ }
649
+ const focusable = node.querySelectorAll(FOCUSABLE_SELECTOR);
650
+ const first = focusable[0];
651
+ if (first) {
652
+ first.focus();
653
+ } else {
654
+ node.setAttribute("tabindex", "-1");
655
+ node.focus();
656
+ }
657
+ }, "focusFirst");
658
+ focusFirst();
659
+ const onKeyDown = /* @__PURE__ */ __name((event) => {
660
+ if (event.key === "Escape") {
661
+ event.preventDefault();
662
+ ctx.setOpen(false);
663
+ return;
664
+ }
665
+ if (event.key !== "Tab") {
666
+ return;
667
+ }
668
+ const node = contentRef.current;
669
+ if (!node) {
670
+ return;
671
+ }
672
+ const focusable = Array.from(node.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
673
+ (el) => el.offsetParent !== null || el === document.activeElement
674
+ );
675
+ if (focusable.length === 0) {
676
+ event.preventDefault();
677
+ return;
678
+ }
679
+ const first = focusable[0];
680
+ const last = focusable[focusable.length - 1];
681
+ if (!first || !last) {
682
+ return;
683
+ }
684
+ if (event.shiftKey) {
685
+ if (document.activeElement === first || !node.contains(document.activeElement)) {
686
+ event.preventDefault();
687
+ last.focus();
688
+ }
689
+ } else if (document.activeElement === last) {
690
+ event.preventDefault();
691
+ first.focus();
692
+ }
693
+ }, "onKeyDown");
694
+ document.addEventListener("keydown", onKeyDown);
695
+ return () => {
696
+ document.removeEventListener("keydown", onKeyDown);
697
+ document.body.style.overflow = prevBodyOverflow;
698
+ const restoreTo = ctx.triggerRef.current ?? previouslyFocused;
699
+ restoreTo?.focus?.();
700
+ };
701
+ }, [ctx.open, ctx.setOpen, ctx.triggerRef]);
702
+ const onOverlayPress = react.useCallback(() => ctx.setOpen(false), [ctx]);
703
+ return /* @__PURE__ */ jsxRuntime.jsxs(
704
+ reactNative.Modal,
705
+ {
706
+ visible: ctx.open,
707
+ transparent: true,
708
+ animationType: reactNative.Platform.OS === "web" ? "none" : "fade",
709
+ onRequestClose: () => ctx.setOpen(false),
710
+ children: [
711
+ /* @__PURE__ */ jsxRuntime.jsx(BlurBackdrop, { intensity: 60, tint: scheme === "dark" ? "dark" : "light", style: reactNative.StyleSheet.absoluteFill }),
712
+ /* @__PURE__ */ jsxRuntime.jsx(
713
+ reactNative.Pressable,
714
+ {
715
+ accessibilityRole: "none",
716
+ "aria-hidden": true,
717
+ ref: (node) => {
718
+ overlayDomRef.current = node;
719
+ },
720
+ style: overlayStyle,
721
+ onPress: onOverlayPress,
722
+ children: /* @__PURE__ */ jsxRuntime.jsx(
723
+ reactNative.Pressable,
724
+ {
725
+ onPress: (event) => event.stopPropagation?.(),
726
+ ref: (node) => {
727
+ contentRef.current = node;
728
+ },
729
+ role: "dialog",
730
+ accessibilityRole: "none",
731
+ "aria-modal": true,
732
+ "aria-labelledby": ctx.titleId,
733
+ "aria-describedby": ctx.descriptionId,
734
+ ...testID !== void 0 ? { testID } : {},
735
+ className: cn("w-full max-w-md rounded-xl bg-semantic-background-elevated p-6 gap-3", className),
736
+ style: [contentStyle, { backgroundColor: colors.semantic.background.elevated }, enterStyle],
737
+ children: /* @__PURE__ */ jsxRuntime.jsx(
738
+ reactNative.View,
739
+ {
740
+ className: "flex-col gap-1.5",
741
+ style: { flexDirection: "column", gap: px(colors.spacing["2"]) - 2 },
742
+ children
743
+ }
744
+ )
745
+ }
746
+ )
747
+ }
748
+ )
749
+ ]
750
+ }
751
+ );
752
+ }, "DialogContent");
753
+ var DialogTitle = /* @__PURE__ */ __name(({ children, className }) => {
754
+ const ctx = useDialogContext("DialogTitle");
755
+ const colors = useThemeColors();
756
+ return /* @__PURE__ */ jsxRuntime.jsx(
757
+ reactNative.Text,
758
+ {
759
+ nativeID: ctx.titleId,
760
+ id: ctx.titleId,
761
+ role: "heading",
762
+ "aria-level": 2,
763
+ className: cn("text-lg font-semibold text-semantic-text-default", className),
764
+ style: {
765
+ color: colors.semantic.text.default,
766
+ fontFamily: colors.fontFamily.display,
767
+ fontSize: px(colors.fontSize.lg),
768
+ fontWeight: colors.fontWeight.semibold
769
+ },
770
+ children
771
+ }
772
+ );
773
+ }, "DialogTitle");
774
+ var DialogDescription = /* @__PURE__ */ __name(({ children, className }) => {
775
+ const ctx = useDialogContext("DialogDescription");
776
+ const colors = useThemeColors();
777
+ return /* @__PURE__ */ jsxRuntime.jsx(
778
+ reactNative.Text,
779
+ {
780
+ nativeID: ctx.descriptionId,
781
+ id: ctx.descriptionId,
782
+ className: cn("text-sm text-semantic-text-muted", className),
783
+ style: {
784
+ color: colors.semantic.text.muted,
785
+ fontFamily: colors.fontFamily.body,
786
+ fontSize: px(colors.fontSize.sm),
787
+ lineHeight: px(colors.fontSize.sm) * Number(colors.lineHeight.normal)
788
+ },
789
+ children
790
+ }
791
+ );
792
+ }, "DialogDescription");
793
+ var DialogClose = /* @__PURE__ */ __name(({
794
+ asChild = true,
795
+ children,
796
+ className,
797
+ testID,
798
+ accessibilityLabel = "Close"
799
+ }) => {
800
+ const ctx = useDialogContext("DialogClose");
801
+ const colors = useThemeColors();
802
+ const onPress = react.useCallback(() => ctx.setOpen(false), [ctx]);
803
+ if (asChild && react.isValidElement(children)) {
804
+ const child = children;
805
+ const fire = /* @__PURE__ */ __name((existing) => (event) => {
806
+ existing?.(event);
807
+ ctx.setOpen(false);
808
+ }, "fire");
809
+ return /* @__PURE__ */ jsxRuntime.jsx(
810
+ Slot,
811
+ {
812
+ onClick: fire(child.props.onClick),
813
+ onPress: fire(child.props.onPress),
814
+ ...testID !== void 0 ? { "data-testid": testID } : {},
815
+ ...className !== void 0 ? { className } : {},
816
+ children: child
817
+ }
818
+ );
819
+ }
820
+ if (children !== void 0) {
821
+ return /* @__PURE__ */ jsxRuntime.jsx(
822
+ reactNative.Pressable,
823
+ {
824
+ onPress,
825
+ role: "button",
826
+ accessibilityRole: "button",
827
+ accessibilityLabel,
828
+ "aria-label": accessibilityLabel,
829
+ ...testID !== void 0 ? { testID } : {},
830
+ ...className !== void 0 ? { className } : {},
831
+ children: wrapStringChildren(children)
832
+ }
833
+ );
834
+ }
835
+ return /* @__PURE__ */ jsxRuntime.jsx(
836
+ reactNative.Pressable,
837
+ {
838
+ onPress,
839
+ role: "button",
840
+ accessibilityRole: "button",
841
+ accessibilityLabel,
842
+ "aria-label": accessibilityLabel,
843
+ ...testID !== void 0 ? { testID } : {},
844
+ className: cn("absolute right-3 top-3 w-8 h-8 items-center justify-center rounded-md", className),
845
+ style: {
846
+ position: "absolute",
847
+ right: px(colors.spacing["3"]),
848
+ top: px(colors.spacing["3"]),
849
+ // 32×32 close hit target — component-density literal — not from theme
850
+ width: 32,
851
+ height: 32,
852
+ alignItems: "center",
853
+ justifyContent: "center",
854
+ borderRadius: px(colors.radius.md)
855
+ },
856
+ children: /* @__PURE__ */ jsxRuntime.jsx(defaultSemanticIcons.close, { size: 18, color: colors.semantic.text.muted })
857
+ }
858
+ );
859
+ }, "DialogClose");
860
+ var DialogFooter = /* @__PURE__ */ __name(({ children, className }) => {
861
+ const colors = useThemeColors();
862
+ return /* @__PURE__ */ jsxRuntime.jsx(
863
+ reactNative.View,
864
+ {
865
+ className: cn("mt-4 flex-row items-center justify-end gap-2", className),
866
+ style: {
867
+ marginTop: px(colors.spacing["4"]),
868
+ flexDirection: "row",
869
+ alignItems: "center",
870
+ justifyContent: "flex-end",
871
+ gap: px(colors.spacing["2"])
872
+ },
873
+ children
874
+ }
875
+ );
876
+ }, "DialogFooter");
877
+ var Dialog = Object.assign(DialogRoot, {
878
+ Trigger: DialogTrigger,
879
+ Content: DialogContent,
880
+ Title: DialogTitle,
881
+ Description: DialogDescription,
882
+ Footer: DialogFooter,
883
+ Close: DialogClose
884
+ });
885
+ var CommandContext = react.createContext(null);
886
+ function useCommandContext(caller) {
887
+ const ctx = react.useContext(CommandContext);
888
+ if (!ctx) {
889
+ throw new Error(`<${caller}> must be rendered inside <Command>.`);
890
+ }
891
+ return ctx;
892
+ }
893
+ __name(useCommandContext, "useCommandContext");
894
+ var CommandRoot = /* @__PURE__ */ __name(({ open, defaultOpen = false, onOpenChange, children }) => {
895
+ const [inner, setInner] = react.useState(defaultOpen);
896
+ const isControlled = open !== void 0;
897
+ const current = isControlled ? open : inner;
898
+ const [query, setQuery] = react.useState("");
899
+ const setOpen = react.useCallback(
900
+ (next) => {
901
+ if (!isControlled) {
902
+ setInner(next);
903
+ }
904
+ if (!next) {
905
+ setQuery("");
906
+ }
907
+ onOpenChange?.(next);
908
+ },
909
+ [isControlled, onOpenChange]
910
+ );
911
+ react.useEffect(() => {
912
+ if (reactNative.Platform.OS !== "web") {
913
+ return;
914
+ }
915
+ const handler = /* @__PURE__ */ __name((e) => {
916
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
917
+ e.preventDefault();
918
+ setOpen(!current);
919
+ }
920
+ }, "handler");
921
+ window.addEventListener("keydown", handler);
922
+ return () => window.removeEventListener("keydown", handler);
923
+ }, [current, setOpen]);
924
+ const ctxValue = {
925
+ open: current,
926
+ setOpen,
927
+ query,
928
+ setQuery
929
+ };
930
+ return /* @__PURE__ */ jsxRuntime.jsx(CommandContext.Provider, { value: ctxValue, children });
931
+ }, "CommandRoot");
932
+ var CommandTrigger = /* @__PURE__ */ __name(({ children, className, testID }) => {
933
+ const ctx = useCommandContext("Command.Trigger");
934
+ const open = /* @__PURE__ */ __name(() => ctx.setOpen(true), "open");
935
+ if (reactNative.Platform.OS === "web") {
936
+ if (react.isValidElement(children)) {
937
+ const child = children;
938
+ const existingOnClick = child.props.onClick;
939
+ const existingOnPress = child.props.onPress;
940
+ const fire = /* @__PURE__ */ __name((e) => {
941
+ existingOnClick?.(e);
942
+ existingOnPress?.(e);
943
+ open();
944
+ }, "fire");
945
+ const extraProps = {
946
+ onClick: fire,
947
+ "aria-haspopup": "dialog",
948
+ "aria-expanded": ctx.open ? "true" : "false"
949
+ };
950
+ if (existingOnPress !== void 0) {
951
+ extraProps.onPress = fire;
952
+ }
953
+ return react.cloneElement(child, extraProps);
954
+ }
955
+ return /* @__PURE__ */ jsxRuntime.jsx(
956
+ "button",
957
+ {
958
+ type: "button",
959
+ "data-testid": testID,
960
+ className: cn("nori-command-trigger", className),
961
+ "aria-haspopup": "dialog",
962
+ "aria-expanded": ctx.open,
963
+ onClick: open,
964
+ children
965
+ }
966
+ );
967
+ }
968
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { testID, onPress: open, accessibilityRole: "button", children });
969
+ }, "CommandTrigger");
970
+ var CommandDialogInner = /* @__PURE__ */ __name(({
971
+ placeholder = "Type a command or search\u2026",
972
+ children,
973
+ className,
974
+ testID
975
+ }) => {
976
+ const ctx = useCommandContext("Command.Dialog");
977
+ const colors = useThemeColors();
978
+ const inputRef = react.useRef(null);
979
+ react.useEffect(() => {
980
+ if (!ctx.open) {
981
+ return;
982
+ }
983
+ if (reactNative.Platform.OS !== "web") {
984
+ return;
985
+ }
986
+ const id = setTimeout(() => inputRef.current?.focus(), 50);
987
+ return () => clearTimeout(id);
988
+ }, [ctx.open]);
989
+ const contentProps = testID !== void 0 ? { testID, className: cn("nori-command-dialog", className) } : { className: cn("nori-command-dialog", className) };
990
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: ctx.open, onOpenChange: ctx.setOpen, children: /* @__PURE__ */ jsxRuntime.jsx(Dialog.Content, { ...contentProps, children: reactNative.Platform.OS === "web" ? /* @__PURE__ */ jsxRuntime.jsxs(
991
+ "div",
992
+ {
993
+ style: {
994
+ maxHeight: "80vh",
995
+ display: "flex",
996
+ flexDirection: "column",
997
+ overflow: "hidden",
998
+ minWidth: 360
999
+ },
1000
+ children: [
1001
+ /* @__PURE__ */ jsxRuntime.jsxs(
1002
+ "div",
1003
+ {
1004
+ style: {
1005
+ padding: `${colors.spacing["3"]}px ${colors.spacing["4"]}px`,
1006
+ borderBottom: `1px solid ${colors.semantic.border.default}`,
1007
+ display: "flex",
1008
+ alignItems: "center",
1009
+ gap: colors.spacing["2"]
1010
+ },
1011
+ children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsx(
1013
+ "svg",
1014
+ {
1015
+ width: "16",
1016
+ height: "16",
1017
+ viewBox: "0 0 16 16",
1018
+ fill: "none",
1019
+ "aria-hidden": "true",
1020
+ style: { color: colors.semantic.text.muted, flexShrink: 0 },
1021
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1022
+ "path",
1023
+ {
1024
+ d: "M6.5 11a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM14 14l-3-3",
1025
+ stroke: "currentColor",
1026
+ strokeWidth: "1.5",
1027
+ strokeLinecap: "round",
1028
+ strokeLinejoin: "round"
1029
+ }
1030
+ )
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ jsxRuntime.jsx(
1034
+ "input",
1035
+ {
1036
+ ref: inputRef,
1037
+ type: "text",
1038
+ role: "combobox",
1039
+ "aria-expanded": true,
1040
+ "aria-autocomplete": "list",
1041
+ autoComplete: "off",
1042
+ spellCheck: false,
1043
+ placeholder,
1044
+ value: ctx.query,
1045
+ onChange: (e) => ctx.setQuery(e.target.value),
1046
+ style: {
1047
+ flex: 1,
1048
+ fontSize: 15,
1049
+ background: "transparent",
1050
+ border: "none",
1051
+ outline: "none",
1052
+ color: colors.semantic.text.default,
1053
+ width: "100%"
1054
+ }
1055
+ }
1056
+ )
1057
+ ]
1058
+ }
1059
+ ),
1060
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", style: { overflowY: "auto", flex: 1, maxHeight: 400 }, children })
1061
+ ]
1062
+ }
1063
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
1064
+ /* @__PURE__ */ jsxRuntime.jsx(
1065
+ reactNative.TextInput,
1066
+ {
1067
+ placeholder,
1068
+ placeholderTextColor: colors.semantic.text.muted,
1069
+ value: ctx.query,
1070
+ onChangeText: ctx.setQuery,
1071
+ autoFocus: ctx.open,
1072
+ style: {
1073
+ fontSize: 15,
1074
+ color: colors.semantic.text.default,
1075
+ paddingHorizontal: px(colors.spacing["4"]),
1076
+ paddingVertical: px(colors.spacing["3"]),
1077
+ borderBottomWidth: 1,
1078
+ borderBottomColor: colors.semantic.border.default
1079
+ }
1080
+ }
1081
+ ),
1082
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { style: { maxHeight: 400 }, children })
1083
+ ] }) }) });
1084
+ }, "CommandDialogInner");
1085
+ var CommandEmpty = /* @__PURE__ */ __name(({ children, className, testID }) => {
1086
+ const colors = useThemeColors();
1087
+ if (reactNative.Platform.OS === "web") {
1088
+ return /* @__PURE__ */ jsxRuntime.jsx(
1089
+ "div",
1090
+ {
1091
+ "data-testid": testID,
1092
+ role: "status",
1093
+ "aria-live": "polite",
1094
+ className: cn("nori-command-empty", className),
1095
+ style: {
1096
+ paddingTop: colors.spacing["6"],
1097
+ paddingBottom: colors.spacing["6"],
1098
+ paddingLeft: colors.spacing["4"],
1099
+ paddingRight: colors.spacing["4"],
1100
+ textAlign: "center",
1101
+ color: colors.semantic.text.muted,
1102
+ fontSize: 14
1103
+ },
1104
+ children
1105
+ }
1106
+ );
1107
+ }
1108
+ return /* @__PURE__ */ jsxRuntime.jsx(
1109
+ reactNative.View,
1110
+ {
1111
+ testID,
1112
+ style: {
1113
+ paddingVertical: px(colors.spacing["6"]),
1114
+ paddingHorizontal: px(colors.spacing["4"]),
1115
+ alignItems: "center"
1116
+ },
1117
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors.semantic.text.muted, fontSize: 14 }, children: typeof children === "string" ? children : "No results found." })
1118
+ }
1119
+ );
1120
+ }, "CommandEmpty");
1121
+ var CommandGroup = /* @__PURE__ */ __name(({ heading, children, className, testID }) => {
1122
+ const ctx = useCommandContext("Command.Group");
1123
+ const groupId = react.useId();
1124
+ const colors = useThemeColors();
1125
+ const visibleCount = countVisibleItems(children, ctx.query);
1126
+ if (visibleCount === 0) {
1127
+ return null;
1128
+ }
1129
+ if (reactNative.Platform.OS === "web") {
1130
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1131
+ "section",
1132
+ {
1133
+ "data-testid": testID,
1134
+ "aria-labelledby": heading ? `${groupId}-heading` : void 0,
1135
+ className: cn("nori-command-group", className),
1136
+ children: [
1137
+ heading && /* @__PURE__ */ jsxRuntime.jsx(
1138
+ "div",
1139
+ {
1140
+ id: `${groupId}-heading`,
1141
+ style: {
1142
+ padding: `${colors.spacing["2"]}px ${colors.spacing["4"]}px ${colors.spacing["1"]}px`,
1143
+ fontSize: 11,
1144
+ fontWeight: 600,
1145
+ letterSpacing: "0.05em",
1146
+ textTransform: "uppercase",
1147
+ color: colors.semantic.text.muted
1148
+ },
1149
+ children: heading
1150
+ }
1151
+ ),
1152
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingBottom: colors.spacing["2"] }, children })
1153
+ ]
1154
+ }
1155
+ );
1156
+ }
1157
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { testID, children: [
1158
+ heading && /* @__PURE__ */ jsxRuntime.jsx(
1159
+ reactNative.View,
1160
+ {
1161
+ style: {
1162
+ paddingHorizontal: px(colors.spacing["4"]),
1163
+ paddingTop: px(colors.spacing["2"]),
1164
+ paddingBottom: px(colors.spacing["1"])
1165
+ },
1166
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1167
+ reactNative.Text,
1168
+ {
1169
+ style: {
1170
+ fontSize: 11,
1171
+ fontWeight: "600",
1172
+ textTransform: "uppercase",
1173
+ color: colors.semantic.text.muted,
1174
+ letterSpacing: 0.5
1175
+ },
1176
+ children: heading
1177
+ }
1178
+ )
1179
+ }
1180
+ ),
1181
+ children
1182
+ ] });
1183
+ }, "CommandGroup");
1184
+ var CommandItem = /* @__PURE__ */ __name(({ onSelect, disabled = false, children, className, testID }) => {
1185
+ const ctx = useCommandContext("Command.Item");
1186
+ const colors = useThemeColors();
1187
+ const text = extractText(children);
1188
+ const visible = matchesQuery(text, ctx.query);
1189
+ if (!visible) {
1190
+ return null;
1191
+ }
1192
+ const handleSelect = /* @__PURE__ */ __name(() => {
1193
+ if (disabled) {
1194
+ return;
1195
+ }
1196
+ onSelect?.();
1197
+ ctx.setOpen(false);
1198
+ }, "handleSelect");
1199
+ if (reactNative.Platform.OS === "web") {
1200
+ return /* @__PURE__ */ jsxRuntime.jsx(
1201
+ "div",
1202
+ {
1203
+ "data-testid": testID,
1204
+ role: "option",
1205
+ "aria-selected": "false",
1206
+ "aria-disabled": disabled,
1207
+ tabIndex: disabled ? -1 : 0,
1208
+ className: cn("nori-command-item", className),
1209
+ onClick: handleSelect,
1210
+ onKeyDown: (e) => {
1211
+ if (e.key === "Enter") {
1212
+ e.preventDefault();
1213
+ handleSelect();
1214
+ }
1215
+ },
1216
+ onMouseEnter: (e) => {
1217
+ if (!disabled) {
1218
+ e.currentTarget.style.background = `${colors.semantic.interactive.primary}14`;
1219
+ }
1220
+ },
1221
+ onMouseLeave: (e) => {
1222
+ e.currentTarget.style.background = "transparent";
1223
+ },
1224
+ onFocus: (e) => {
1225
+ if (!disabled) {
1226
+ e.currentTarget.style.background = `${colors.semantic.interactive.primary}14`;
1227
+ }
1228
+ },
1229
+ onBlur: (e) => {
1230
+ e.currentTarget.style.background = "transparent";
1231
+ },
1232
+ style: {
1233
+ display: "flex",
1234
+ alignItems: "center",
1235
+ justifyContent: "space-between",
1236
+ padding: `${colors.spacing["2"]}px ${colors.spacing["4"]}px`,
1237
+ cursor: disabled ? "not-allowed" : "pointer",
1238
+ opacity: disabled ? 0.5 : 1,
1239
+ fontSize: 14,
1240
+ color: colors.semantic.text.default,
1241
+ borderRadius: colors.radius.sm,
1242
+ margin: `0 ${colors.spacing["1"]}px`,
1243
+ outline: "none"
1244
+ },
1245
+ children
1246
+ }
1247
+ );
1248
+ }
1249
+ return /* @__PURE__ */ jsxRuntime.jsx(
1250
+ reactNative.Pressable,
1251
+ {
1252
+ testID,
1253
+ onPress: handleSelect,
1254
+ disabled,
1255
+ accessibilityRole: "button",
1256
+ style: ({ pressed }) => ({
1257
+ flexDirection: "row",
1258
+ alignItems: "center",
1259
+ justifyContent: "space-between",
1260
+ paddingHorizontal: px(colors.spacing["4"]),
1261
+ paddingVertical: px(colors.spacing["3"]),
1262
+ opacity: disabled ? 0.5 : pressed ? 0.7 : 1
1263
+ }),
1264
+ children: typeof children === "string" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { fontSize: 14, color: colors.semantic.text.default }, children }) : children
1265
+ }
1266
+ );
1267
+ }, "CommandItem");
1268
+ var CommandShortcut = /* @__PURE__ */ __name(({ children, className }) => {
1269
+ const colors = useThemeColors();
1270
+ if (reactNative.Platform.OS === "web") {
1271
+ return /* @__PURE__ */ jsxRuntime.jsx(
1272
+ "span",
1273
+ {
1274
+ "aria-hidden": "true",
1275
+ className: cn("nori-command-shortcut", className),
1276
+ style: {
1277
+ marginLeft: "auto",
1278
+ fontSize: 12,
1279
+ color: colors.semantic.text.muted,
1280
+ letterSpacing: "0.05em",
1281
+ opacity: 0.7
1282
+ },
1283
+ children
1284
+ }
1285
+ );
1286
+ }
1287
+ return /* @__PURE__ */ jsxRuntime.jsx(
1288
+ reactNative.Text,
1289
+ {
1290
+ style: {
1291
+ fontSize: 12,
1292
+ color: colors.semantic.text.muted,
1293
+ opacity: 0.7
1294
+ },
1295
+ children: typeof children === "string" ? children : null
1296
+ }
1297
+ );
1298
+ }, "CommandShortcut");
1299
+ function extractText(node) {
1300
+ if (typeof node === "string" || typeof node === "number") {
1301
+ return String(node);
1302
+ }
1303
+ if (Array.isArray(node)) {
1304
+ return node.map(extractText).join(" ");
1305
+ }
1306
+ if (react.isValidElement(node)) {
1307
+ const el = node;
1308
+ return extractText(el.props.children);
1309
+ }
1310
+ return "";
1311
+ }
1312
+ __name(extractText, "extractText");
1313
+ function matchesQuery(text, query) {
1314
+ if (!query.trim()) {
1315
+ return true;
1316
+ }
1317
+ return text.toLowerCase().includes(query.toLowerCase().trim());
1318
+ }
1319
+ __name(matchesQuery, "matchesQuery");
1320
+ function countVisibleItems(children, query) {
1321
+ let count = 0;
1322
+ react.Children.forEach(children, (child) => {
1323
+ if (!react.isValidElement(child)) {
1324
+ return;
1325
+ }
1326
+ const props = child.props;
1327
+ if ("onSelect" in props || !("heading" in props)) {
1328
+ const text = extractText(props.children);
1329
+ if (matchesQuery(text, query)) {
1330
+ count++;
1331
+ }
1332
+ }
1333
+ });
1334
+ return count;
1335
+ }
1336
+ __name(countVisibleItems, "countVisibleItems");
1337
+ var CommandDialogWrapper = /* @__PURE__ */ __name((props) => {
1338
+ const ctx = useCommandContext("Command.Dialog");
1339
+ const { children, ...rest } = props;
1340
+ let anyVisible = false;
1341
+ let emptyNode = null;
1342
+ react.Children.forEach(children, (child) => {
1343
+ if (!react.isValidElement(child)) {
1344
+ return;
1345
+ }
1346
+ const childProps = child.props;
1347
+ if (!("heading" in childProps) && !("onSelect" in childProps) && !("icon" in childProps)) {
1348
+ emptyNode = child;
1349
+ return;
1350
+ }
1351
+ const groupChildren = childProps.children;
1352
+ const count = countVisibleItems(groupChildren, ctx.query);
1353
+ if (count > 0) {
1354
+ anyVisible = true;
1355
+ }
1356
+ });
1357
+ return /* @__PURE__ */ jsxRuntime.jsx(CommandDialogInner, { ...rest, children: anyVisible ? children : emptyNode ?? /* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { children: "No results found." }) });
1358
+ }, "CommandDialogWrapper");
1359
+ var Command = Object.assign(CommandRoot, {
1360
+ Trigger: CommandTrigger,
1361
+ Dialog: CommandDialogWrapper,
1362
+ Empty: CommandEmpty,
1363
+ Group: CommandGroup,
1364
+ Item: CommandItem,
1365
+ Shortcut: CommandShortcut
1366
+ });
1367
+
1368
+ exports.Command = Command;
1369
+ exports.useCommandContext = useCommandContext;
1370
+ //# sourceMappingURL=index.cjs.map
1371
+ //# sourceMappingURL=index.cjs.map