@muja-ui/native 0.2.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.cjs ADDED
@@ -0,0 +1,2487 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var reactNative = require('react-native');
5
+ var core = require('@muja-ui/core');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var utils = require('@muja-ui/utils');
8
+ var Svg = require('react-native-svg');
9
+ var icons = require('@muja-ui/icons');
10
+ var reactNativeSafeAreaContext = require('react-native-safe-area-context');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var Svg__default = /*#__PURE__*/_interopDefault(Svg);
15
+
16
+ // src/components/Box.tsx
17
+ var ThemeContext = react.createContext(null);
18
+ function ThemeProvider({
19
+ theme = core.lightTheme,
20
+ darkTheme: darkTheme2 = core.darkTheme,
21
+ defaultColorMode = "system",
22
+ colorMode: colorModeProp,
23
+ onColorModeChange,
24
+ children
25
+ }) {
26
+ const [uncontrolledMode, setUncontrolledMode] = react.useState(defaultColorMode);
27
+ const colorMode = colorModeProp ?? uncontrolledMode;
28
+ const systemScheme = reactNative.useColorScheme();
29
+ const resolvedColorMode = colorMode === "system" ? systemScheme === "dark" ? "dark" : "light" : colorMode;
30
+ const setColorMode = react.useCallback(
31
+ (mode) => {
32
+ if (colorModeProp === void 0) setUncontrolledMode(mode);
33
+ onColorModeChange?.(mode);
34
+ },
35
+ [colorModeProp, onColorModeChange]
36
+ );
37
+ const toggleColorMode = react.useCallback(() => {
38
+ setColorMode(resolvedColorMode === "dark" ? "light" : "dark");
39
+ }, [resolvedColorMode, setColorMode]);
40
+ const value = react.useMemo(
41
+ () => ({
42
+ theme: resolvedColorMode === "dark" ? darkTheme2 : theme,
43
+ colorMode,
44
+ resolvedColorMode,
45
+ setColorMode,
46
+ toggleColorMode
47
+ }),
48
+ [theme, darkTheme2, colorMode, resolvedColorMode, setColorMode, toggleColorMode]
49
+ );
50
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value, children });
51
+ }
52
+ function useThemeContext(hookName) {
53
+ const context = react.useContext(ThemeContext);
54
+ if (!context) {
55
+ throw new Error(`[muja-ui] ${hookName} must be used within a <ThemeProvider>.`);
56
+ }
57
+ return context;
58
+ }
59
+ function useTheme() {
60
+ return useThemeContext("useTheme").theme;
61
+ }
62
+ function useColorMode() {
63
+ const { colorMode, resolvedColorMode, setColorMode, toggleColorMode } = useThemeContext("useColorMode");
64
+ return { colorMode, resolvedColorMode, setColorMode, toggleColorMode };
65
+ }
66
+ function useColorModeValue(lightValue, darkValue) {
67
+ return useThemeContext("useColorModeValue").resolvedColorMode === "dark" ? darkValue : lightValue;
68
+ }
69
+ function shadowStyle(value) {
70
+ const layer = value.layers[0];
71
+ if (!layer) return reactNative.Platform.OS === "android" ? { elevation: 0 } : { shadowOpacity: 0 };
72
+ const alpha = /rgba?\([^)]*,\s*([\d.]+)\s*\)/.exec(layer.color)?.[1];
73
+ return {
74
+ shadowColor: layer.color.replace(/rgba?\(([^)]*?),\s*[\d.]+\s*\)/, "rgb($1)"),
75
+ shadowOffset: { width: layer.x, height: layer.y },
76
+ shadowOpacity: alpha ? Number(alpha) : 0.1,
77
+ shadowRadius: layer.blur / 2,
78
+ // Android's single-number model: approximate the blur as elevation.
79
+ elevation: Math.round(layer.blur / 2)
80
+ };
81
+ }
82
+ var space = (key) => (value, theme) => ({ [key]: theme.space[value] });
83
+ var pair = (a, b) => (value, theme) => ({ [a]: theme.space[value], [b]: theme.space[value] });
84
+ var passthrough = (key) => (value) => ({ [key]: value });
85
+ var resolvers = {
86
+ m: space("margin"),
87
+ mt: space("marginTop"),
88
+ mr: space("marginRight"),
89
+ mb: space("marginBottom"),
90
+ ml: space("marginLeft"),
91
+ mx: pair("marginLeft", "marginRight"),
92
+ my: pair("marginTop", "marginBottom"),
93
+ p: space("padding"),
94
+ pt: space("paddingTop"),
95
+ pr: space("paddingRight"),
96
+ pb: space("paddingBottom"),
97
+ pl: space("paddingLeft"),
98
+ px: pair("paddingLeft", "paddingRight"),
99
+ py: pair("paddingTop", "paddingBottom"),
100
+ bg: ((value, theme) => ({
101
+ backgroundColor: theme.colors[value]
102
+ })),
103
+ borderColor: ((value, theme) => ({
104
+ borderColor: theme.colors[value]
105
+ })),
106
+ borderWidth: ((value, theme) => ({
107
+ borderWidth: theme.borderWidth[value]
108
+ })),
109
+ borderTopWidth: ((value, theme) => ({
110
+ borderTopWidth: theme.borderWidth[value]
111
+ })),
112
+ borderBottomWidth: ((value, theme) => ({
113
+ borderBottomWidth: theme.borderWidth[value]
114
+ })),
115
+ radius: ((value, theme) => ({
116
+ borderRadius: theme.radius[value]
117
+ })),
118
+ shadow: ((value, theme) => shadowStyle(theme.shadow[value])),
119
+ zIndex: ((value, theme) => ({ zIndex: theme.zIndex[value] })),
120
+ opacity: passthrough("opacity"),
121
+ w: passthrough("width"),
122
+ h: passthrough("height"),
123
+ minW: passthrough("minWidth"),
124
+ maxW: passthrough("maxWidth"),
125
+ minH: passthrough("minHeight"),
126
+ maxH: passthrough("maxHeight"),
127
+ flex: passthrough("flex"),
128
+ alignSelf: passthrough("alignSelf"),
129
+ position: passthrough("position"),
130
+ top: passthrough("top"),
131
+ right: passthrough("right"),
132
+ bottom: passthrough("bottom"),
133
+ left: passthrough("left"),
134
+ overflow: passthrough("overflow")
135
+ };
136
+ function isStyleProp(key) {
137
+ return key in resolvers;
138
+ }
139
+ function splitStyleProps(props, theme) {
140
+ const style = {};
141
+ const rest = {};
142
+ for (const key of Object.keys(props)) {
143
+ const value = props[key];
144
+ if (value !== void 0 && isStyleProp(key)) {
145
+ Object.assign(style, resolvers[key](value, theme));
146
+ } else {
147
+ rest[key] = value;
148
+ }
149
+ }
150
+ return { style, rest };
151
+ }
152
+ var Box = react.forwardRef(function Box2(props, ref) {
153
+ const theme = useTheme();
154
+ const { style: styleProp, ...restProps } = props;
155
+ const { style, rest } = splitStyleProps(restProps, theme);
156
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ref, ...rest, style: [style, styleProp] });
157
+ });
158
+ var Flex = react.forwardRef(function Flex2({ direction = "row", align, justify, wrap, gap, rowGap, columnGap, style, ...rest }, ref) {
159
+ const theme = useTheme();
160
+ return /* @__PURE__ */ jsxRuntime.jsx(
161
+ Box,
162
+ {
163
+ ref,
164
+ ...rest,
165
+ style: [
166
+ {
167
+ flexDirection: direction,
168
+ alignItems: align,
169
+ justifyContent: justify,
170
+ flexWrap: wrap,
171
+ gap: gap !== void 0 ? theme.space[gap] : void 0,
172
+ rowGap: rowGap !== void 0 ? theme.space[rowGap] : void 0,
173
+ columnGap: columnGap !== void 0 ? theme.space[columnGap] : void 0
174
+ },
175
+ style
176
+ ]
177
+ }
178
+ );
179
+ });
180
+ var Stack = react.forwardRef(function Stack2({ direction = "column", gap = 4, ...rest }, ref) {
181
+ return /* @__PURE__ */ jsxRuntime.jsx(Flex, { ref, direction, gap, ...rest });
182
+ });
183
+ var HStack = react.forwardRef(function HStack2(props, ref) {
184
+ return /* @__PURE__ */ jsxRuntime.jsx(Stack, { ref, direction: "row", align: "center", ...props });
185
+ });
186
+ function Spacer({ size, axis = "vertical" }) {
187
+ const theme = useTheme();
188
+ if (size === void 0) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flex: 1 } });
189
+ const length = theme.space[size];
190
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: axis === "vertical" ? { height: length } : { width: length } });
191
+ }
192
+ function Divider({ orientation = "horizontal", color = "border", style }) {
193
+ const theme = useTheme();
194
+ return /* @__PURE__ */ jsxRuntime.jsx(
195
+ reactNative.View,
196
+ {
197
+ accessibilityElementsHidden: true,
198
+ importantForAccessibility: "no-hide-descendants",
199
+ style: [
200
+ orientation === "horizontal" ? { height: reactNative.StyleSheet.hairlineWidth, alignSelf: "stretch" } : { width: reactNative.StyleSheet.hairlineWidth, alignSelf: "stretch" },
201
+ { backgroundColor: theme.colors[color] },
202
+ style
203
+ ]
204
+ }
205
+ );
206
+ }
207
+ var Text = react.forwardRef(function Text2(props, ref) {
208
+ const theme = useTheme();
209
+ const {
210
+ size = "md",
211
+ weight,
212
+ family = "sans",
213
+ leading,
214
+ tracking,
215
+ align,
216
+ color = "text",
217
+ truncate,
218
+ style: styleProp,
219
+ numberOfLines,
220
+ ...restProps
221
+ } = props;
222
+ const { style: tokenStyle, rest } = splitStyleProps(
223
+ restProps,
224
+ theme
225
+ );
226
+ const fontSize = theme.typography.fontSize[size];
227
+ const textStyle = {
228
+ fontFamily: theme.typography.fontFamily[family],
229
+ fontSize,
230
+ color: theme.colors[color],
231
+ fontWeight: weight ? theme.typography.fontWeight[weight] : void 0,
232
+ lineHeight: leading ? Math.round(fontSize * theme.typography.lineHeight[leading]) : void 0,
233
+ letterSpacing: tracking ? theme.typography.letterSpacing[tracking] : void 0,
234
+ textAlign: align
235
+ };
236
+ return /* @__PURE__ */ jsxRuntime.jsx(
237
+ reactNative.Text,
238
+ {
239
+ ref,
240
+ numberOfLines: truncate ? 1 : numberOfLines,
241
+ ellipsizeMode: truncate ? "tail" : void 0,
242
+ ...rest,
243
+ style: [textStyle, tokenStyle, styleProp]
244
+ }
245
+ );
246
+ });
247
+ var defaultSize = {
248
+ 1: "3xl",
249
+ 2: "2xl",
250
+ 3: "xl",
251
+ 4: "lg",
252
+ 5: "md",
253
+ 6: "sm"
254
+ };
255
+ var Heading = react.forwardRef(function Heading2({ level = 2, size, weight = "semibold", leading = "tight", ...rest }, ref) {
256
+ return /* @__PURE__ */ jsxRuntime.jsx(
257
+ Text,
258
+ {
259
+ ref,
260
+ accessibilityRole: "header",
261
+ "aria-level": level,
262
+ size: size ?? defaultSize[level],
263
+ weight,
264
+ leading,
265
+ ...rest
266
+ }
267
+ );
268
+ });
269
+ function Icon({
270
+ icon,
271
+ size = 20,
272
+ color = "text",
273
+ label,
274
+ strokeWidth = 2,
275
+ style
276
+ }) {
277
+ const theme = useTheme();
278
+ const definition = typeof icon === "string" ? core.getIcon(icon) : icon;
279
+ if (!definition) {
280
+ utils.warnOnce(
281
+ `Icon "${String(icon)}" is not registered. Call registerIcons() from @muja-ui/core or pass an IconDefinition directly.`
282
+ );
283
+ return null;
284
+ }
285
+ return /* @__PURE__ */ jsxRuntime.jsx(
286
+ Svg__default.default,
287
+ {
288
+ viewBox: definition.viewBox ?? "0 0 24 24",
289
+ width: size,
290
+ height: size,
291
+ fill: "none",
292
+ style,
293
+ accessibilityRole: label ? "image" : void 0,
294
+ accessibilityLabel: label,
295
+ accessibilityElementsHidden: label ? void 0 : true,
296
+ importantForAccessibility: label ? void 0 : "no-hide-descendants",
297
+ children: definition.paths.map((d, index) => /* @__PURE__ */ jsxRuntime.jsx(
298
+ Svg.Path,
299
+ {
300
+ d,
301
+ stroke: theme.colors[color],
302
+ strokeWidth,
303
+ strokeLinecap: "round",
304
+ strokeLinejoin: "round"
305
+ },
306
+ index
307
+ ))
308
+ }
309
+ );
310
+ }
311
+
312
+ // src/system/variants.ts
313
+ function variantColors(variant, theme) {
314
+ const { colors, borderWidth } = theme;
315
+ switch (variant) {
316
+ case "primary":
317
+ return {
318
+ background: colors.primary,
319
+ backgroundPressed: colors.primaryActive,
320
+ foreground: "onPrimary"
321
+ };
322
+ case "secondary":
323
+ return {
324
+ background: colors.secondary,
325
+ backgroundPressed: colors.secondaryActive,
326
+ foreground: "onSecondary"
327
+ };
328
+ case "accent":
329
+ return {
330
+ background: colors.accent,
331
+ backgroundPressed: colors.accentActive,
332
+ foreground: "onAccent"
333
+ };
334
+ case "danger":
335
+ return {
336
+ background: colors.danger,
337
+ backgroundPressed: colors.dangerActive,
338
+ foreground: "onDanger"
339
+ };
340
+ case "outline":
341
+ return {
342
+ background: "transparent",
343
+ backgroundPressed: colors.surfaceActive,
344
+ foreground: "text",
345
+ borderColor: colors.borderStrong,
346
+ borderWidth: borderWidth.thin
347
+ };
348
+ case "ghost":
349
+ return {
350
+ background: "transparent",
351
+ backgroundPressed: colors.surfaceActive,
352
+ foreground: "text"
353
+ };
354
+ case "link":
355
+ return {
356
+ background: "transparent",
357
+ backgroundPressed: "transparent",
358
+ foreground: "primaryText",
359
+ underline: true
360
+ };
361
+ }
362
+ }
363
+ function sizeMetrics(size, theme) {
364
+ switch (size) {
365
+ case "sm":
366
+ return {
367
+ height: 36,
368
+ paddingHorizontal: theme.space[3],
369
+ fontSize: "sm",
370
+ gap: theme.space[1.5],
371
+ iconSize: 16
372
+ };
373
+ case "lg":
374
+ return {
375
+ height: 52,
376
+ paddingHorizontal: theme.space[6],
377
+ fontSize: "lg",
378
+ gap: theme.space[2.5],
379
+ iconSize: 22
380
+ };
381
+ case "md":
382
+ default:
383
+ return {
384
+ height: 44,
385
+ paddingHorizontal: theme.space[4],
386
+ fontSize: "md",
387
+ gap: theme.space[2],
388
+ iconSize: 18
389
+ };
390
+ }
391
+ }
392
+ var Button = react.forwardRef(function Button2({
393
+ variant = "primary",
394
+ size = "md",
395
+ loading = false,
396
+ fullWidth = false,
397
+ leftIcon,
398
+ rightIcon,
399
+ disabled = false,
400
+ children,
401
+ style,
402
+ accessibilityLabel,
403
+ ...rest
404
+ }, ref) {
405
+ const theme = useTheme();
406
+ const colors = variantColors(variant, theme);
407
+ const metrics = sizeMetrics(size, theme);
408
+ const isInert = disabled || loading;
409
+ const isLink = variant === "link";
410
+ return /* @__PURE__ */ jsxRuntime.jsxs(
411
+ reactNative.Pressable,
412
+ {
413
+ ref,
414
+ accessibilityRole: "button",
415
+ accessibilityState: { disabled: isInert, busy: loading },
416
+ accessibilityLabel,
417
+ disabled: isInert,
418
+ style: ({ pressed }) => [
419
+ {
420
+ flexDirection: "row",
421
+ alignItems: "center",
422
+ justifyContent: "center",
423
+ gap: metrics.gap,
424
+ height: isLink ? void 0 : metrics.height,
425
+ paddingHorizontal: isLink ? 0 : metrics.paddingHorizontal,
426
+ borderRadius: isLink ? 0 : theme.radius.md,
427
+ backgroundColor: pressed ? colors.backgroundPressed : colors.background,
428
+ borderColor: colors.borderColor,
429
+ borderWidth: colors.borderWidth,
430
+ alignSelf: fullWidth ? "stretch" : "flex-start",
431
+ opacity: isInert ? theme.opacity.disabled : 1
432
+ },
433
+ style
434
+ ],
435
+ ...rest,
436
+ children: [
437
+ loading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: theme.colors[colors.foreground] }) : leftIcon ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { children: leftIcon }) : null,
438
+ children != null && children !== false ? typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
439
+ Text,
440
+ {
441
+ size: metrics.fontSize,
442
+ weight: "semibold",
443
+ color: colors.foreground,
444
+ style: colors.underline ? { textDecorationLine: "underline" } : void 0,
445
+ children
446
+ }
447
+ ) : children : null,
448
+ rightIcon && !loading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { children: rightIcon }) : null
449
+ ]
450
+ }
451
+ );
452
+ });
453
+ var boxSize = { sm: 36, md: 44, lg: 52 };
454
+ var IconButton = react.forwardRef(function IconButton2({
455
+ icon,
456
+ variant = "ghost",
457
+ size = "md",
458
+ loading = false,
459
+ round = false,
460
+ disabled = false,
461
+ style,
462
+ ...rest
463
+ }, ref) {
464
+ const theme = useTheme();
465
+ const colors = variantColors(variant, theme);
466
+ const dimension = boxSize[size];
467
+ const isInert = disabled || loading;
468
+ return /* @__PURE__ */ jsxRuntime.jsx(
469
+ reactNative.Pressable,
470
+ {
471
+ ref,
472
+ accessibilityRole: "button",
473
+ accessibilityState: { disabled: isInert, busy: loading },
474
+ disabled: isInert,
475
+ hitSlop: dimension < 44 ? (44 - dimension) / 2 : void 0,
476
+ style: ({ pressed }) => [
477
+ {
478
+ width: dimension,
479
+ height: dimension,
480
+ alignItems: "center",
481
+ justifyContent: "center",
482
+ borderRadius: round ? theme.radius.full : theme.radius.md,
483
+ backgroundColor: pressed ? colors.backgroundPressed : colors.background,
484
+ borderColor: colors.borderColor,
485
+ borderWidth: colors.borderWidth,
486
+ opacity: isInert ? theme.opacity.disabled : 1
487
+ },
488
+ style
489
+ ],
490
+ ...rest,
491
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: theme.colors[colors.foreground] }) : icon
492
+ }
493
+ );
494
+ });
495
+ var Input = react.forwardRef(function Input2({
496
+ size = "md",
497
+ invalid = false,
498
+ disabled = false,
499
+ leftElement,
500
+ rightElement,
501
+ style,
502
+ onFocus,
503
+ onBlur,
504
+ ...rest
505
+ }, ref) {
506
+ const theme = useTheme();
507
+ const metrics = sizeMetrics(size, theme);
508
+ const [focused, setFocused] = react.useState(false);
509
+ const borderColor = invalid ? theme.colors.danger : focused ? theme.colors.focusRing : theme.colors.border;
510
+ return /* @__PURE__ */ jsxRuntime.jsxs(
511
+ reactNative.View,
512
+ {
513
+ style: [
514
+ {
515
+ flexDirection: "row",
516
+ alignItems: "center",
517
+ gap: metrics.gap,
518
+ height: metrics.height,
519
+ paddingHorizontal: metrics.paddingHorizontal,
520
+ borderRadius: theme.radius.md,
521
+ borderWidth: focused || invalid ? theme.borderWidth.medium : theme.borderWidth.thin,
522
+ borderColor,
523
+ backgroundColor: disabled ? theme.colors.bgMuted : theme.colors.surface
524
+ },
525
+ style
526
+ ],
527
+ children: [
528
+ leftElement,
529
+ /* @__PURE__ */ jsxRuntime.jsx(
530
+ reactNative.TextInput,
531
+ {
532
+ ref,
533
+ editable: !disabled,
534
+ "aria-invalid": invalid || void 0,
535
+ "aria-disabled": disabled || void 0,
536
+ placeholderTextColor: theme.colors.textMuted,
537
+ selectionColor: theme.colors.primary,
538
+ onFocus: (event) => {
539
+ setFocused(true);
540
+ onFocus?.(event);
541
+ },
542
+ onBlur: (event) => {
543
+ setFocused(false);
544
+ onBlur?.(event);
545
+ },
546
+ style: {
547
+ flex: 1,
548
+ // Android adds its own vertical padding; zero it so `height` wins.
549
+ paddingVertical: 0,
550
+ fontFamily: theme.typography.fontFamily.sans,
551
+ fontSize: theme.typography.fontSize[metrics.fontSize],
552
+ color: disabled ? theme.colors.textDisabled : theme.colors.text
553
+ },
554
+ ...rest
555
+ }
556
+ ),
557
+ rightElement
558
+ ]
559
+ }
560
+ );
561
+ });
562
+ var Textarea = react.forwardRef(function Textarea2({ invalid = false, disabled = false, rows = 4, style, onFocus, onBlur, ...rest }, ref) {
563
+ const theme = useTheme();
564
+ const [focused, setFocused] = react.useState(false);
565
+ const lineHeight = Math.round(
566
+ theme.typography.fontSize.md * theme.typography.lineHeight.normal
567
+ );
568
+ return /* @__PURE__ */ jsxRuntime.jsx(
569
+ reactNative.TextInput,
570
+ {
571
+ ref,
572
+ multiline: true,
573
+ textAlignVertical: "top",
574
+ editable: !disabled,
575
+ "aria-invalid": invalid || void 0,
576
+ placeholderTextColor: theme.colors.textMuted,
577
+ selectionColor: theme.colors.primary,
578
+ onFocus: (event) => {
579
+ setFocused(true);
580
+ onFocus?.(event);
581
+ },
582
+ onBlur: (event) => {
583
+ setFocused(false);
584
+ onBlur?.(event);
585
+ },
586
+ style: [
587
+ {
588
+ minHeight: rows * lineHeight + theme.space[4],
589
+ padding: theme.space[3],
590
+ borderRadius: theme.radius.md,
591
+ borderWidth: focused || invalid ? theme.borderWidth.medium : theme.borderWidth.thin,
592
+ borderColor: invalid ? theme.colors.danger : focused ? theme.colors.focusRing : theme.colors.border,
593
+ backgroundColor: disabled ? theme.colors.bgMuted : theme.colors.surface,
594
+ fontFamily: theme.typography.fontFamily.sans,
595
+ fontSize: theme.typography.fontSize.md,
596
+ lineHeight,
597
+ color: disabled ? theme.colors.textDisabled : theme.colors.text
598
+ },
599
+ style
600
+ ],
601
+ ...rest
602
+ }
603
+ );
604
+ });
605
+ var Label = react.forwardRef(function Label2({ required = false, size = "sm", weight = "medium", children, ...rest }, ref) {
606
+ return /* @__PURE__ */ jsxRuntime.jsxs(Text, { ref, size, weight, ...rest, children: [
607
+ children,
608
+ required ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size, weight, color: "danger", accessibilityLabel: "required", children: " *" }) : null
609
+ ] });
610
+ });
611
+ function FormField({
612
+ label,
613
+ required = false,
614
+ help,
615
+ error,
616
+ children,
617
+ style
618
+ }) {
619
+ const theme = useTheme();
620
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [{ gap: theme.space[1.5] }, style], children: [
621
+ label ? /* @__PURE__ */ jsxRuntime.jsx(Label, { required, color: "textSecondary", children: label }) : null,
622
+ children,
623
+ error ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", color: "dangerText", accessibilityLiveRegion: "polite", children: error }) : help ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", color: "textMuted", children: help }) : null
624
+ ] });
625
+ }
626
+ function useControllableState(controlled, defaultValue, onChange) {
627
+ const [uncontrolled, setUncontrolled] = react.useState(defaultValue);
628
+ const value = controlled !== void 0 ? controlled : uncontrolled;
629
+ const setValue = react.useCallback(
630
+ (next) => {
631
+ if (controlled === void 0) setUncontrolled(next);
632
+ onChange?.(next);
633
+ },
634
+ [controlled, onChange]
635
+ );
636
+ return [value, setValue];
637
+ }
638
+ var boxSize2 = { sm: 18, md: 22, lg: 26 };
639
+ function Checkbox({
640
+ checked: controlledChecked,
641
+ defaultChecked = false,
642
+ onChange,
643
+ indeterminate = false,
644
+ size = "md",
645
+ invalid = false,
646
+ disabled = false,
647
+ children,
648
+ style
649
+ }) {
650
+ const theme = useTheme();
651
+ const [checked, setChecked] = useControllableState(
652
+ controlledChecked,
653
+ defaultChecked,
654
+ onChange
655
+ );
656
+ const dimension = boxSize2[size];
657
+ const filled = checked || indeterminate;
658
+ return /* @__PURE__ */ jsxRuntime.jsxs(
659
+ reactNative.Pressable,
660
+ {
661
+ accessibilityRole: "checkbox",
662
+ accessibilityState: {
663
+ checked: indeterminate ? "mixed" : checked,
664
+ disabled
665
+ },
666
+ "aria-invalid": invalid || void 0,
667
+ disabled,
668
+ onPress: () => setChecked(!checked),
669
+ style: [
670
+ {
671
+ flexDirection: "row",
672
+ alignItems: "center",
673
+ gap: theme.space[2.5],
674
+ opacity: disabled ? theme.opacity.disabled : 1
675
+ },
676
+ style
677
+ ],
678
+ children: [
679
+ /* @__PURE__ */ jsxRuntime.jsx(
680
+ reactNative.View,
681
+ {
682
+ style: {
683
+ width: dimension,
684
+ height: dimension,
685
+ alignItems: "center",
686
+ justifyContent: "center",
687
+ borderRadius: theme.radius.sm,
688
+ borderWidth: theme.borderWidth.medium,
689
+ borderColor: invalid ? theme.colors.danger : filled ? theme.colors.primary : theme.colors.borderStrong,
690
+ backgroundColor: filled ? theme.colors.primary : theme.colors.surface
691
+ },
692
+ children: filled ? /* @__PURE__ */ jsxRuntime.jsx(
693
+ Icon,
694
+ {
695
+ icon: indeterminate ? icons.MinusIcon : icons.CheckIcon,
696
+ size: dimension - 6,
697
+ color: "onPrimary",
698
+ strokeWidth: 3
699
+ }
700
+ ) : null
701
+ }
702
+ ),
703
+ children != null ? typeof children === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: size === "lg" ? "md" : "sm", style: { flexShrink: 1 }, children }) : children : null
704
+ ]
705
+ }
706
+ );
707
+ }
708
+ var track = {
709
+ sm: { width: 36, height: 20, thumb: 16 },
710
+ md: { width: 44, height: 26, thumb: 22 },
711
+ lg: { width: 52, height: 32, thumb: 28 }
712
+ };
713
+ function Switch({
714
+ checked: controlledChecked,
715
+ defaultChecked = false,
716
+ onChange,
717
+ size = "md",
718
+ disabled = false,
719
+ children,
720
+ style
721
+ }) {
722
+ const theme = useTheme();
723
+ const [checked, setChecked] = useControllableState(
724
+ controlledChecked,
725
+ defaultChecked,
726
+ onChange
727
+ );
728
+ const metrics = track[size];
729
+ const inset = (metrics.height - metrics.thumb) / 2;
730
+ return /* @__PURE__ */ jsxRuntime.jsxs(
731
+ reactNative.Pressable,
732
+ {
733
+ accessibilityRole: "switch",
734
+ accessibilityState: { checked, disabled },
735
+ disabled,
736
+ onPress: () => setChecked(!checked),
737
+ style: [
738
+ {
739
+ flexDirection: "row",
740
+ alignItems: "center",
741
+ justifyContent: "space-between",
742
+ gap: theme.space[3],
743
+ opacity: disabled ? theme.opacity.disabled : 1
744
+ },
745
+ style
746
+ ],
747
+ children: [
748
+ children != null ? typeof children === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", style: { flexShrink: 1 }, children }) : children : null,
749
+ /* @__PURE__ */ jsxRuntime.jsx(
750
+ reactNative.View,
751
+ {
752
+ style: {
753
+ width: metrics.width,
754
+ height: metrics.height,
755
+ borderRadius: theme.radius.full,
756
+ padding: inset,
757
+ justifyContent: "center",
758
+ backgroundColor: checked ? theme.colors.primary : theme.colors.secondaryActive
759
+ },
760
+ children: /* @__PURE__ */ jsxRuntime.jsx(
761
+ reactNative.View,
762
+ {
763
+ style: {
764
+ width: metrics.thumb,
765
+ height: metrics.thumb,
766
+ borderRadius: theme.radius.full,
767
+ backgroundColor: theme.colors.surface,
768
+ transform: [{ translateX: checked ? metrics.width - metrics.thumb - inset * 2 : 0 }]
769
+ }
770
+ }
771
+ )
772
+ }
773
+ )
774
+ ]
775
+ }
776
+ );
777
+ }
778
+ var RadioGroupContext = react.createContext(null);
779
+ function RadioGroup({
780
+ value: controlledValue,
781
+ defaultValue,
782
+ onChange,
783
+ size = "md",
784
+ disabled = false,
785
+ orientation = "vertical",
786
+ children,
787
+ style,
788
+ accessibilityLabel
789
+ }) {
790
+ const theme = useTheme();
791
+ const [value, setValue] = useControllableState(
792
+ controlledValue,
793
+ defaultValue,
794
+ onChange
795
+ );
796
+ return /* @__PURE__ */ jsxRuntime.jsx(
797
+ RadioGroupContext.Provider,
798
+ {
799
+ value: { value, setValue, size, disabled },
800
+ children: /* @__PURE__ */ jsxRuntime.jsx(
801
+ reactNative.View,
802
+ {
803
+ accessibilityRole: "radiogroup",
804
+ accessibilityLabel,
805
+ style: [
806
+ {
807
+ flexDirection: orientation === "horizontal" ? "row" : "column",
808
+ flexWrap: orientation === "horizontal" ? "wrap" : "nowrap",
809
+ gap: theme.space[3]
810
+ },
811
+ style
812
+ ],
813
+ children
814
+ }
815
+ )
816
+ }
817
+ );
818
+ }
819
+ var boxSize3 = { sm: 18, md: 22, lg: 26 };
820
+ function Radio({ value, disabled = false, children, style }) {
821
+ const theme = useTheme();
822
+ const group = react.useContext(RadioGroupContext);
823
+ if (!group) {
824
+ utils.warnOnce("<Radio> must be rendered inside a <RadioGroup>.");
825
+ return null;
826
+ }
827
+ const selected = group.value === value;
828
+ const isDisabled = disabled || group.disabled;
829
+ const dimension = boxSize3[group.size];
830
+ return /* @__PURE__ */ jsxRuntime.jsxs(
831
+ reactNative.Pressable,
832
+ {
833
+ accessibilityRole: "radio",
834
+ accessibilityState: { checked: selected, disabled: isDisabled },
835
+ disabled: isDisabled,
836
+ onPress: () => group.setValue(value),
837
+ style: [
838
+ {
839
+ flexDirection: "row",
840
+ alignItems: "center",
841
+ gap: theme.space[2.5],
842
+ opacity: isDisabled ? theme.opacity.disabled : 1
843
+ },
844
+ style
845
+ ],
846
+ children: [
847
+ /* @__PURE__ */ jsxRuntime.jsx(
848
+ reactNative.View,
849
+ {
850
+ style: {
851
+ width: dimension,
852
+ height: dimension,
853
+ alignItems: "center",
854
+ justifyContent: "center",
855
+ borderRadius: theme.radius.full,
856
+ borderWidth: theme.borderWidth.medium,
857
+ borderColor: selected ? theme.colors.primary : theme.colors.borderStrong,
858
+ backgroundColor: theme.colors.surface
859
+ },
860
+ children: selected ? /* @__PURE__ */ jsxRuntime.jsx(
861
+ reactNative.View,
862
+ {
863
+ style: {
864
+ width: dimension / 2,
865
+ height: dimension / 2,
866
+ borderRadius: theme.radius.full,
867
+ backgroundColor: theme.colors.primary
868
+ }
869
+ }
870
+ ) : null
871
+ }
872
+ ),
873
+ children != null ? typeof children === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: group.size === "lg" ? "md" : "sm", style: { flexShrink: 1 }, children }) : children : null
874
+ ]
875
+ }
876
+ );
877
+ }
878
+ var DISMISS_THRESHOLD = 96;
879
+ function BottomSheet({
880
+ open,
881
+ onClose,
882
+ closeOnOverlayPress = true,
883
+ title,
884
+ accessibilityLabel,
885
+ maxHeightRatio = 0.9,
886
+ children,
887
+ style
888
+ }) {
889
+ const theme = useTheme();
890
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
891
+ const { height } = reactNative.useWindowDimensions();
892
+ const translateY = react.useRef(new reactNative.Animated.Value(height)).current;
893
+ const dragY = react.useRef(0);
894
+ react.useEffect(() => {
895
+ reactNative.Animated.timing(translateY, {
896
+ toValue: open ? 0 : height,
897
+ duration: open ? theme.motion.duration.normal : theme.motion.duration.fast,
898
+ easing: reactNative.Easing.out(reactNative.Easing.cubic),
899
+ useNativeDriver: true
900
+ }).start();
901
+ }, [open, height, translateY, theme.motion.duration]);
902
+ const panResponder = react.useRef(
903
+ reactNative.PanResponder.create({
904
+ onMoveShouldSetPanResponder: (_event, gesture) => gesture.dy > 4,
905
+ onPanResponderMove: (_event, gesture) => {
906
+ if (gesture.dy > 0) {
907
+ dragY.current = gesture.dy;
908
+ translateY.setValue(gesture.dy);
909
+ }
910
+ },
911
+ onPanResponderRelease: () => {
912
+ if (dragY.current > DISMISS_THRESHOLD) {
913
+ onClose();
914
+ } else {
915
+ reactNative.Animated.spring(translateY, { toValue: 0, useNativeDriver: true }).start();
916
+ }
917
+ dragY.current = 0;
918
+ }
919
+ })
920
+ ).current;
921
+ return /* @__PURE__ */ jsxRuntime.jsx(
922
+ reactNative.Modal,
923
+ {
924
+ visible: open,
925
+ transparent: true,
926
+ animationType: "none",
927
+ statusBarTranslucent: true,
928
+ onRequestClose: onClose,
929
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, justifyContent: "flex-end" }, children: [
930
+ /* @__PURE__ */ jsxRuntime.jsx(
931
+ reactNative.Pressable,
932
+ {
933
+ accessibilityLabel: closeOnOverlayPress ? "Close" : void 0,
934
+ onPress: closeOnOverlayPress ? onClose : void 0,
935
+ style: { flex: 1, backgroundColor: theme.colors.overlay }
936
+ }
937
+ ),
938
+ /* @__PURE__ */ jsxRuntime.jsxs(
939
+ reactNative.Animated.View,
940
+ {
941
+ accessibilityViewIsModal: true,
942
+ accessibilityLabel: accessibilityLabel ?? title,
943
+ style: [
944
+ {
945
+ maxHeight: height * maxHeightRatio,
946
+ paddingBottom: insets.bottom + theme.space[4],
947
+ borderTopLeftRadius: theme.radius["2xl"],
948
+ borderTopRightRadius: theme.radius["2xl"],
949
+ backgroundColor: theme.colors.surface,
950
+ transform: [{ translateY }]
951
+ },
952
+ style
953
+ ],
954
+ children: [
955
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { ...panResponder.panHandlers, style: { paddingTop: theme.space[3] }, children: [
956
+ /* @__PURE__ */ jsxRuntime.jsx(
957
+ reactNative.View,
958
+ {
959
+ style: {
960
+ alignSelf: "center",
961
+ width: 40,
962
+ height: 4,
963
+ borderRadius: theme.radius.full,
964
+ backgroundColor: theme.colors.borderStrong
965
+ }
966
+ }
967
+ ),
968
+ title ? /* @__PURE__ */ jsxRuntime.jsx(
969
+ Heading,
970
+ {
971
+ level: 3,
972
+ size: "lg",
973
+ style: {
974
+ paddingHorizontal: theme.space[5],
975
+ paddingTop: theme.space[4]
976
+ },
977
+ children: title
978
+ }
979
+ ) : null
980
+ ] }),
981
+ children
982
+ ]
983
+ }
984
+ )
985
+ ] })
986
+ }
987
+ );
988
+ }
989
+ function Select({
990
+ options,
991
+ value,
992
+ onChange,
993
+ placeholder,
994
+ title,
995
+ size = "md",
996
+ invalid = false,
997
+ disabled = false,
998
+ style,
999
+ accessibilityLabel
1000
+ }) {
1001
+ const theme = useTheme();
1002
+ const metrics = sizeMetrics(size, theme);
1003
+ const [open, setOpen] = react.useState(false);
1004
+ const selected = options.find((option) => option.value === value);
1005
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1006
+ /* @__PURE__ */ jsxRuntime.jsxs(
1007
+ reactNative.Pressable,
1008
+ {
1009
+ accessibilityRole: "button",
1010
+ accessibilityLabel: accessibilityLabel ?? title ?? placeholder,
1011
+ accessibilityValue: { text: selected?.label },
1012
+ accessibilityState: { disabled, expanded: open },
1013
+ "aria-invalid": invalid || void 0,
1014
+ disabled,
1015
+ onPress: () => setOpen(true),
1016
+ style: [
1017
+ {
1018
+ flexDirection: "row",
1019
+ alignItems: "center",
1020
+ justifyContent: "space-between",
1021
+ gap: metrics.gap,
1022
+ height: metrics.height,
1023
+ paddingHorizontal: metrics.paddingHorizontal,
1024
+ borderRadius: theme.radius.md,
1025
+ borderWidth: invalid ? theme.borderWidth.medium : theme.borderWidth.thin,
1026
+ borderColor: invalid ? theme.colors.danger : theme.colors.border,
1027
+ backgroundColor: disabled ? theme.colors.bgMuted : theme.colors.surface,
1028
+ opacity: disabled ? theme.opacity.disabled : 1
1029
+ },
1030
+ style
1031
+ ],
1032
+ children: [
1033
+ /* @__PURE__ */ jsxRuntime.jsx(
1034
+ Text,
1035
+ {
1036
+ size: metrics.fontSize,
1037
+ color: selected ? "text" : "textMuted",
1038
+ truncate: true,
1039
+ style: { flex: 1 },
1040
+ children: selected?.label ?? placeholder ?? ""
1041
+ }
1042
+ ),
1043
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.ChevronDownIcon, size: metrics.iconSize, color: "textSecondary" })
1044
+ ]
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ jsxRuntime.jsx(
1048
+ BottomSheet,
1049
+ {
1050
+ open,
1051
+ onClose: () => setOpen(false),
1052
+ title: title ?? placeholder,
1053
+ accessibilityLabel,
1054
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { style: { marginTop: theme.space[2] }, children: options.map((option) => {
1055
+ const isSelected = option.value === value;
1056
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1057
+ reactNative.Pressable,
1058
+ {
1059
+ accessibilityRole: "menuitem",
1060
+ accessibilityState: { selected: isSelected, disabled: option.disabled },
1061
+ disabled: option.disabled,
1062
+ onPress: () => {
1063
+ onChange?.(option.value);
1064
+ setOpen(false);
1065
+ },
1066
+ style: ({ pressed }) => ({
1067
+ flexDirection: "row",
1068
+ alignItems: "center",
1069
+ gap: theme.space[3],
1070
+ paddingHorizontal: theme.space[5],
1071
+ paddingVertical: theme.space[3.5],
1072
+ backgroundColor: pressed ? theme.colors.surfaceActive : "transparent",
1073
+ opacity: option.disabled ? theme.opacity.disabled : 1
1074
+ }),
1075
+ children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1 }, children: [
1077
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", weight: isSelected ? "semibold" : "regular", children: option.label }),
1078
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textMuted", children: option.description }) : null
1079
+ ] }),
1080
+ isSelected ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.CheckIcon, color: "primary" }) : null
1081
+ ]
1082
+ },
1083
+ option.value
1084
+ );
1085
+ }) })
1086
+ }
1087
+ )
1088
+ ] });
1089
+ }
1090
+ function Skeleton({
1091
+ width = "100%",
1092
+ height = 16,
1093
+ radius = "sm",
1094
+ animated = true,
1095
+ style
1096
+ }) {
1097
+ const theme = useTheme();
1098
+ const pulse = react.useRef(new reactNative.Animated.Value(0.5)).current;
1099
+ react.useEffect(() => {
1100
+ if (!animated) return;
1101
+ const loop = reactNative.Animated.loop(
1102
+ reactNative.Animated.sequence([
1103
+ reactNative.Animated.timing(pulse, {
1104
+ toValue: 1,
1105
+ duration: theme.motion.duration.slower,
1106
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
1107
+ useNativeDriver: true
1108
+ }),
1109
+ reactNative.Animated.timing(pulse, {
1110
+ toValue: 0.5,
1111
+ duration: theme.motion.duration.slower,
1112
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
1113
+ useNativeDriver: true
1114
+ })
1115
+ ])
1116
+ );
1117
+ loop.start();
1118
+ return () => loop.stop();
1119
+ }, [animated, pulse, theme.motion.duration.slower]);
1120
+ return /* @__PURE__ */ jsxRuntime.jsx(
1121
+ reactNative.Animated.View,
1122
+ {
1123
+ accessibilityElementsHidden: true,
1124
+ importantForAccessibility: "no-hide-descendants",
1125
+ style: [
1126
+ {
1127
+ width,
1128
+ height,
1129
+ borderRadius: theme.radius[radius],
1130
+ backgroundColor: theme.colors.bgMuted,
1131
+ opacity: animated ? pulse : 0.7
1132
+ },
1133
+ style
1134
+ ]
1135
+ }
1136
+ );
1137
+ }
1138
+ function Spinner({ size = "md", color = "primary", label, style }) {
1139
+ const theme = useTheme();
1140
+ return /* @__PURE__ */ jsxRuntime.jsx(
1141
+ reactNative.ActivityIndicator,
1142
+ {
1143
+ size: size === "sm" ? "small" : "large",
1144
+ color: theme.colors[color],
1145
+ accessibilityRole: "progressbar",
1146
+ accessibilityLabel: label,
1147
+ style: [size === "md" ? { transform: [{ scale: 0.8 }] } : null, style]
1148
+ }
1149
+ );
1150
+ }
1151
+ function Progress({
1152
+ value,
1153
+ max = 100,
1154
+ height = 8,
1155
+ color = "primary",
1156
+ trackColor = "bgMuted",
1157
+ label,
1158
+ style
1159
+ }) {
1160
+ const theme = useTheme();
1161
+ const ratio = max > 0 ? utils.clamp(value / max, 0, 1) : 0;
1162
+ return /* @__PURE__ */ jsxRuntime.jsx(
1163
+ reactNative.View,
1164
+ {
1165
+ accessibilityRole: "progressbar",
1166
+ accessibilityLabel: label,
1167
+ accessibilityValue: { min: 0, max, now: Math.round(value) },
1168
+ style: [
1169
+ {
1170
+ height,
1171
+ borderRadius: theme.radius.full,
1172
+ backgroundColor: theme.colors[trackColor],
1173
+ overflow: "hidden"
1174
+ },
1175
+ style
1176
+ ],
1177
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1178
+ reactNative.View,
1179
+ {
1180
+ style: {
1181
+ width: `${ratio * 100}%`,
1182
+ height: "100%",
1183
+ borderRadius: theme.radius.full,
1184
+ backgroundColor: theme.colors[color]
1185
+ }
1186
+ }
1187
+ )
1188
+ }
1189
+ );
1190
+ }
1191
+ var ToastContext = react.createContext(null);
1192
+ var DEFAULT_DURATION = 4e3;
1193
+ var toneIcon = {
1194
+ neutral: icons.InfoIcon,
1195
+ success: icons.CheckIcon,
1196
+ warning: icons.AlertTriangleIcon,
1197
+ danger: icons.AlertCircleIcon,
1198
+ info: icons.InfoIcon
1199
+ };
1200
+ var toneColor = {
1201
+ neutral: "textSecondary",
1202
+ success: "success",
1203
+ warning: "warning",
1204
+ danger: "danger",
1205
+ info: "info"
1206
+ };
1207
+ function ToastProvider({ children, placement = "top", max = 3 }) {
1208
+ const theme = useTheme();
1209
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
1210
+ const [entries, setEntries] = react.useState([]);
1211
+ const nextId = react.useRef(0);
1212
+ const dismiss = react.useCallback((id) => {
1213
+ setEntries((current) => current.filter((entry) => entry.id !== id));
1214
+ }, []);
1215
+ const toast = react.useCallback(
1216
+ (options) => {
1217
+ const id = nextId.current++;
1218
+ setEntries((current) => [...current, { ...options, id }].slice(-max));
1219
+ },
1220
+ [max]
1221
+ );
1222
+ const value = react.useMemo(() => ({ toast, dismiss }), [toast, dismiss]);
1223
+ return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
1224
+ children,
1225
+ /* @__PURE__ */ jsxRuntime.jsx(
1226
+ reactNative.View,
1227
+ {
1228
+ pointerEvents: "box-none",
1229
+ style: {
1230
+ position: "absolute",
1231
+ left: 0,
1232
+ right: 0,
1233
+ [placement]: (placement === "top" ? insets.top : insets.bottom) + theme.space[2],
1234
+ paddingHorizontal: theme.space[4],
1235
+ gap: theme.space[2],
1236
+ zIndex: theme.zIndex.toast
1237
+ },
1238
+ children: entries.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { entry, onDismiss: dismiss, placement }, entry.id))
1239
+ }
1240
+ )
1241
+ ] });
1242
+ }
1243
+ function ToastCard({
1244
+ entry,
1245
+ onDismiss,
1246
+ placement
1247
+ }) {
1248
+ const theme = useTheme();
1249
+ const tone = entry.tone ?? "neutral";
1250
+ const enter = react.useRef(new reactNative.Animated.Value(0)).current;
1251
+ react.useEffect(() => {
1252
+ reactNative.Animated.timing(enter, {
1253
+ toValue: 1,
1254
+ duration: theme.motion.duration.normal,
1255
+ easing: reactNative.Easing.out(reactNative.Easing.cubic),
1256
+ useNativeDriver: true
1257
+ }).start();
1258
+ }, [enter, theme.motion.duration.normal]);
1259
+ react.useEffect(() => {
1260
+ const duration = entry.duration ?? DEFAULT_DURATION;
1261
+ if (duration <= 0) return;
1262
+ const timer = setTimeout(() => onDismiss(entry.id), duration);
1263
+ return () => clearTimeout(timer);
1264
+ }, [entry.duration, entry.id, onDismiss]);
1265
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1266
+ reactNative.Animated.View,
1267
+ {
1268
+ accessibilityLiveRegion: "polite",
1269
+ accessibilityRole: "alert",
1270
+ style: {
1271
+ flexDirection: "row",
1272
+ alignItems: "flex-start",
1273
+ gap: theme.space[3],
1274
+ padding: theme.space[3.5],
1275
+ borderRadius: theme.radius.lg,
1276
+ borderWidth: theme.borderWidth.thin,
1277
+ borderColor: theme.colors.border,
1278
+ backgroundColor: theme.colors.surface,
1279
+ opacity: enter,
1280
+ transform: [
1281
+ {
1282
+ translateY: enter.interpolate({
1283
+ inputRange: [0, 1],
1284
+ outputRange: [placement === "top" ? -16 : 16, 0]
1285
+ })
1286
+ }
1287
+ ],
1288
+ ...shadowStyle(theme.shadow.lg)
1289
+ },
1290
+ children: [
1291
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: toneIcon[tone], size: 18, color: toneColor[tone] }),
1292
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: theme.space[0.5] }, children: [
1293
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", weight: "semibold", children: entry.title }),
1294
+ entry.description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textSecondary", children: entry.description }) : null
1295
+ ] }),
1296
+ entry.action ? /* @__PURE__ */ jsxRuntime.jsx(
1297
+ reactNative.Pressable,
1298
+ {
1299
+ accessibilityRole: "button",
1300
+ onPress: () => {
1301
+ entry.action?.onPress();
1302
+ onDismiss(entry.id);
1303
+ },
1304
+ hitSlop: 8,
1305
+ children: /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", weight: "semibold", color: "primaryText", children: entry.action.label })
1306
+ }
1307
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1308
+ reactNative.Pressable,
1309
+ {
1310
+ accessibilityRole: "button",
1311
+ accessibilityLabel: "Dismiss",
1312
+ onPress: () => onDismiss(entry.id),
1313
+ hitSlop: 8,
1314
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.XIcon, size: 16, color: "textMuted" })
1315
+ }
1316
+ )
1317
+ ]
1318
+ }
1319
+ );
1320
+ }
1321
+ function useToast() {
1322
+ const context = react.useContext(ToastContext);
1323
+ if (!context) {
1324
+ throw new Error("[muja-ui] useToast must be used within a <ToastProvider>.");
1325
+ }
1326
+ return context;
1327
+ }
1328
+ function EmptyState({ title, description, icon, action, style }) {
1329
+ const theme = useTheme();
1330
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1331
+ reactNative.View,
1332
+ {
1333
+ style: [
1334
+ {
1335
+ alignItems: "center",
1336
+ justifyContent: "center",
1337
+ gap: theme.space[2],
1338
+ paddingVertical: theme.space[10],
1339
+ paddingHorizontal: theme.space[6]
1340
+ },
1341
+ style
1342
+ ],
1343
+ children: [
1344
+ icon ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { marginBottom: theme.space[2] }, children: icon }) : null,
1345
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: "md", align: "center", children: title }),
1346
+ description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textMuted", align: "center", leading: "normal", children: description }) : null,
1347
+ action ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { marginTop: theme.space[3] }, children: action }) : null
1348
+ ]
1349
+ }
1350
+ );
1351
+ }
1352
+ var tones = {
1353
+ neutral: {
1354
+ solidBg: "secondaryActive",
1355
+ solidFg: "onSecondary",
1356
+ subtleBg: "bgMuted",
1357
+ subtleFg: "textSecondary"
1358
+ },
1359
+ primary: {
1360
+ solidBg: "primary",
1361
+ solidFg: "onPrimary",
1362
+ subtleBg: "primarySubtle",
1363
+ subtleFg: "primaryText"
1364
+ },
1365
+ accent: { solidBg: "accent", solidFg: "onAccent", subtleBg: "accentSubtle", subtleFg: "accentText" },
1366
+ success: {
1367
+ solidBg: "success",
1368
+ solidFg: "onSuccess",
1369
+ subtleBg: "successSubtle",
1370
+ subtleFg: "successText"
1371
+ },
1372
+ warning: {
1373
+ solidBg: "warning",
1374
+ solidFg: "onWarning",
1375
+ subtleBg: "warningSubtle",
1376
+ subtleFg: "warningText"
1377
+ },
1378
+ danger: { solidBg: "danger", solidFg: "onDanger", subtleBg: "dangerSubtle", subtleFg: "dangerText" },
1379
+ info: { solidBg: "info", solidFg: "onInfo", subtleBg: "infoSubtle", subtleFg: "infoText" }
1380
+ };
1381
+ function Badge({
1382
+ tone = "neutral",
1383
+ variant = "subtle",
1384
+ children,
1385
+ leftIcon,
1386
+ style
1387
+ }) {
1388
+ const theme = useTheme();
1389
+ const palette = tones[tone];
1390
+ const background = variant === "solid" ? theme.colors[palette.solidBg] : variant === "subtle" ? theme.colors[palette.subtleBg] : "transparent";
1391
+ const foreground = variant === "solid" ? palette.solidFg : palette.subtleFg;
1392
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1393
+ reactNative.View,
1394
+ {
1395
+ style: [
1396
+ {
1397
+ flexDirection: "row",
1398
+ alignItems: "center",
1399
+ alignSelf: "flex-start",
1400
+ gap: theme.space[1],
1401
+ paddingHorizontal: theme.space[2],
1402
+ paddingVertical: theme.space[0.5],
1403
+ borderRadius: theme.radius.full,
1404
+ backgroundColor: background,
1405
+ borderWidth: variant === "outline" ? theme.borderWidth.thin : 0,
1406
+ borderColor: variant === "outline" ? theme.colors[palette.solidBg] : void 0
1407
+ },
1408
+ style
1409
+ ],
1410
+ children: [
1411
+ leftIcon,
1412
+ typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", weight: "medium", color: foreground, children }) : children
1413
+ ]
1414
+ }
1415
+ );
1416
+ }
1417
+ function Chip({
1418
+ children,
1419
+ selected = false,
1420
+ disabled = false,
1421
+ onPress,
1422
+ onRemove,
1423
+ leftIcon,
1424
+ style
1425
+ }) {
1426
+ const theme = useTheme();
1427
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1428
+ leftIcon,
1429
+ typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", weight: "medium", color: selected ? "onPrimary" : "textSecondary", children }) : children,
1430
+ onRemove ? /* @__PURE__ */ jsxRuntime.jsx(
1431
+ reactNative.Pressable,
1432
+ {
1433
+ accessibilityRole: "button",
1434
+ accessibilityLabel: "Remove",
1435
+ hitSlop: 8,
1436
+ onPress: onRemove,
1437
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.XIcon, size: 14, color: selected ? "onPrimary" : "textSecondary" })
1438
+ }
1439
+ ) : null
1440
+ ] });
1441
+ const shape = (pressed) => ({
1442
+ flexDirection: "row",
1443
+ alignItems: "center",
1444
+ alignSelf: "flex-start",
1445
+ gap: theme.space[1.5],
1446
+ height: 32,
1447
+ paddingHorizontal: theme.space[3],
1448
+ borderRadius: theme.radius.full,
1449
+ borderWidth: theme.borderWidth.thin,
1450
+ borderColor: selected ? theme.colors.primary : theme.colors.border,
1451
+ backgroundColor: selected ? theme.colors.primary : pressed ? theme.colors.surfaceActive : theme.colors.surface,
1452
+ opacity: disabled ? theme.opacity.disabled : 1
1453
+ });
1454
+ if (!onPress) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [shape(false), style], children: body });
1455
+ return /* @__PURE__ */ jsxRuntime.jsx(
1456
+ reactNative.Pressable,
1457
+ {
1458
+ accessibilityRole: "button",
1459
+ accessibilityState: { selected, disabled },
1460
+ disabled,
1461
+ onPress,
1462
+ style: ({ pressed }) => [shape(pressed), style],
1463
+ children: body
1464
+ }
1465
+ );
1466
+ }
1467
+ var Card = react.forwardRef(function Card2({ variant = "outline", onPress, accessibilityLabel, children, style }, ref) {
1468
+ const theme = useTheme();
1469
+ const base = {
1470
+ borderRadius: theme.radius.lg,
1471
+ backgroundColor: variant === "filled" ? theme.colors.bgSubtle : theme.colors.surface,
1472
+ borderWidth: variant === "outline" ? theme.borderWidth.thin : 0,
1473
+ borderColor: theme.colors.border,
1474
+ ...variant === "elevated" ? shadowStyle(theme.shadow.sm) : null
1475
+ };
1476
+ if (!onPress) {
1477
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ref, style: [base, style], children });
1478
+ }
1479
+ return /* @__PURE__ */ jsxRuntime.jsx(
1480
+ reactNative.Pressable,
1481
+ {
1482
+ ref,
1483
+ accessibilityRole: "button",
1484
+ accessibilityLabel,
1485
+ onPress,
1486
+ style: ({ pressed }) => [
1487
+ base,
1488
+ pressed ? { backgroundColor: theme.colors.surfaceActive } : null,
1489
+ style
1490
+ ],
1491
+ children
1492
+ }
1493
+ );
1494
+ });
1495
+ function CardHeader({ children, style }) {
1496
+ const theme = useTheme();
1497
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ padding: theme.space[4], paddingBottom: theme.space[2], gap: theme.space[1] }, style], children });
1498
+ }
1499
+ function CardTitle({ children, style }) {
1500
+ return /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: "md", style, children });
1501
+ }
1502
+ function CardDescription({ children, style }) {
1503
+ return /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textMuted", style, children });
1504
+ }
1505
+ function CardContent({ children, style }) {
1506
+ const theme = useTheme();
1507
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ padding: theme.space[4], paddingTop: theme.space[2] }, style], children });
1508
+ }
1509
+ function CardFooter({ children, style }) {
1510
+ const theme = useTheme();
1511
+ return /* @__PURE__ */ jsxRuntime.jsx(
1512
+ reactNative.View,
1513
+ {
1514
+ style: [
1515
+ {
1516
+ flexDirection: "row",
1517
+ alignItems: "center",
1518
+ gap: theme.space[2],
1519
+ padding: theme.space[4],
1520
+ paddingTop: 0
1521
+ },
1522
+ style
1523
+ ],
1524
+ children
1525
+ }
1526
+ );
1527
+ }
1528
+ function Container({ gutter = 4, maxWidth: maxWidth2, children, style }) {
1529
+ const theme = useTheme();
1530
+ return /* @__PURE__ */ jsxRuntime.jsx(
1531
+ reactNative.View,
1532
+ {
1533
+ style: [
1534
+ {
1535
+ paddingHorizontal: theme.space[gutter],
1536
+ width: "100%",
1537
+ maxWidth: maxWidth2,
1538
+ alignSelf: maxWidth2 ? "center" : void 0
1539
+ },
1540
+ style
1541
+ ],
1542
+ children
1543
+ }
1544
+ );
1545
+ }
1546
+ function Section({ title, description, action, gap = 3, children, style }) {
1547
+ const theme = useTheme();
1548
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [{ gap: theme.space[gap] }, style], children: [
1549
+ title || action ? /* @__PURE__ */ jsxRuntime.jsxs(
1550
+ reactNative.View,
1551
+ {
1552
+ style: {
1553
+ flexDirection: "row",
1554
+ alignItems: "center",
1555
+ justifyContent: "space-between",
1556
+ gap: theme.space[3]
1557
+ },
1558
+ children: [
1559
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: theme.space[0.5] }, children: [
1560
+ title ? /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: "lg", children: title }) : null,
1561
+ description ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textMuted", children: description }) : null
1562
+ ] }),
1563
+ action
1564
+ ]
1565
+ }
1566
+ ) : null,
1567
+ children
1568
+ ] });
1569
+ }
1570
+ function Screen({
1571
+ children,
1572
+ bg = "bg",
1573
+ scrollable = false,
1574
+ refreshing,
1575
+ onRefresh,
1576
+ edges = ["top", "bottom"],
1577
+ bottomInset = 0,
1578
+ contentContainerStyle,
1579
+ style
1580
+ }) {
1581
+ const theme = useTheme();
1582
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
1583
+ const padding = {
1584
+ paddingTop: edges.includes("top") ? insets.top : 0,
1585
+ paddingBottom: (edges.includes("bottom") ? insets.bottom : 0) + bottomInset
1586
+ };
1587
+ if (!scrollable) {
1588
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ flex: 1, backgroundColor: theme.colors[bg] }, padding, style], children });
1589
+ }
1590
+ return /* @__PURE__ */ jsxRuntime.jsx(
1591
+ reactNative.ScrollView,
1592
+ {
1593
+ style: [{ flex: 1, backgroundColor: theme.colors[bg] }, style],
1594
+ contentContainerStyle: [padding, contentContainerStyle],
1595
+ keyboardShouldPersistTaps: "handled",
1596
+ refreshControl: onRefresh ? /* @__PURE__ */ jsxRuntime.jsx(
1597
+ reactNative.RefreshControl,
1598
+ {
1599
+ refreshing: refreshing ?? false,
1600
+ onRefresh,
1601
+ tintColor: theme.colors.primary,
1602
+ colors: [theme.colors.primary]
1603
+ }
1604
+ ) : void 0,
1605
+ children
1606
+ }
1607
+ );
1608
+ }
1609
+ function ListRow({
1610
+ title,
1611
+ subtitle,
1612
+ left,
1613
+ right,
1614
+ onPress,
1615
+ disabled = false,
1616
+ hideChevron = false,
1617
+ style
1618
+ }) {
1619
+ const theme = useTheme();
1620
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1621
+ left,
1622
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: theme.space[0.5] }, children: [
1623
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", numberOfLines: 1, children: title }),
1624
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", color: "textMuted", numberOfLines: 2, children: subtitle }) : null
1625
+ ] }),
1626
+ right ?? (onPress && !hideChevron ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.ChevronRightIcon, size: 18, color: "textMuted" }) : null)
1627
+ ] });
1628
+ const layout = {
1629
+ flexDirection: "row",
1630
+ alignItems: "center",
1631
+ gap: theme.space[3],
1632
+ paddingHorizontal: theme.space[4],
1633
+ paddingVertical: theme.space[3.5],
1634
+ minHeight: 56,
1635
+ opacity: disabled ? theme.opacity.disabled : 1
1636
+ };
1637
+ if (!onPress) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [layout, style], children: content });
1638
+ return /* @__PURE__ */ jsxRuntime.jsx(
1639
+ reactNative.Pressable,
1640
+ {
1641
+ accessibilityRole: "button",
1642
+ accessibilityState: { disabled },
1643
+ disabled,
1644
+ onPress,
1645
+ style: ({ pressed }) => [
1646
+ layout,
1647
+ pressed ? { backgroundColor: theme.colors.surfaceActive } : null,
1648
+ style
1649
+ ],
1650
+ children: content
1651
+ }
1652
+ );
1653
+ }
1654
+ var sizeMap = { sm: 32, md: 40, lg: 56 };
1655
+ function initials(name) {
1656
+ return name.trim().split(/\s+/).slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
1657
+ }
1658
+ function Avatar({ source, name, size = "md", style }) {
1659
+ const theme = useTheme();
1660
+ const [failed, setFailed] = react.useState(false);
1661
+ const dimension = typeof size === "number" ? size : sizeMap[size];
1662
+ const showImage = !!source && !failed;
1663
+ return /* @__PURE__ */ jsxRuntime.jsx(
1664
+ reactNative.View,
1665
+ {
1666
+ style: [
1667
+ {
1668
+ width: dimension,
1669
+ height: dimension,
1670
+ borderRadius: theme.radius.full,
1671
+ backgroundColor: theme.colors.primarySubtle,
1672
+ alignItems: "center",
1673
+ justifyContent: "center",
1674
+ overflow: "hidden"
1675
+ },
1676
+ style
1677
+ ],
1678
+ children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
1679
+ reactNative.Image,
1680
+ {
1681
+ source: { uri: source },
1682
+ accessibilityLabel: name,
1683
+ onError: () => setFailed(true),
1684
+ style: { width: "100%", height: "100%" }
1685
+ }
1686
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1687
+ Text,
1688
+ {
1689
+ size: dimension >= 56 ? "lg" : dimension >= 40 ? "md" : "sm",
1690
+ weight: "semibold",
1691
+ color: "primaryText",
1692
+ accessibilityLabel: name,
1693
+ children: name ? initials(name) : "?"
1694
+ }
1695
+ )
1696
+ }
1697
+ );
1698
+ }
1699
+ function Tabs({
1700
+ items,
1701
+ value: controlledValue,
1702
+ defaultValue,
1703
+ onChange,
1704
+ variant = "underline",
1705
+ scrollable = false,
1706
+ style,
1707
+ accessibilityLabel
1708
+ }) {
1709
+ const theme = useTheme();
1710
+ const [value, setValue] = useControllableState(
1711
+ controlledValue,
1712
+ defaultValue ?? items[0]?.value,
1713
+ onChange
1714
+ );
1715
+ const isSegmented = variant === "segmented";
1716
+ const tabs = items.map((item) => {
1717
+ const selected = item.value === value;
1718
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1719
+ reactNative.Pressable,
1720
+ {
1721
+ accessibilityRole: "tab",
1722
+ accessibilityState: { selected },
1723
+ onPress: () => setValue(item.value),
1724
+ style: {
1725
+ flex: scrollable ? void 0 : 1,
1726
+ flexDirection: "row",
1727
+ alignItems: "center",
1728
+ justifyContent: "center",
1729
+ gap: theme.space[1.5],
1730
+ paddingHorizontal: theme.space[3],
1731
+ paddingVertical: isSegmented ? theme.space[2] : theme.space[3],
1732
+ borderRadius: isSegmented ? theme.radius.md : 0,
1733
+ backgroundColor: isSegmented && selected ? theme.colors.surface : "transparent",
1734
+ borderBottomWidth: isSegmented ? 0 : theme.borderWidth.medium,
1735
+ borderBottomColor: selected ? theme.colors.primary : "transparent"
1736
+ },
1737
+ children: [
1738
+ /* @__PURE__ */ jsxRuntime.jsx(
1739
+ Text,
1740
+ {
1741
+ size: "sm",
1742
+ weight: selected ? "semibold" : "medium",
1743
+ color: selected ? isSegmented ? "text" : "primaryText" : "textMuted",
1744
+ numberOfLines: 1,
1745
+ children: item.label
1746
+ }
1747
+ ),
1748
+ item.badge !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", color: "textMuted", children: item.badge }) : null
1749
+ ]
1750
+ },
1751
+ item.value
1752
+ );
1753
+ });
1754
+ const container = isSegmented ? {
1755
+ flexDirection: "row",
1756
+ gap: theme.space[1],
1757
+ padding: theme.space[1],
1758
+ borderRadius: theme.radius.lg,
1759
+ backgroundColor: theme.colors.bgMuted
1760
+ } : {
1761
+ flexDirection: "row",
1762
+ borderBottomWidth: theme.borderWidth.thin,
1763
+ borderBottomColor: theme.colors.border
1764
+ };
1765
+ if (scrollable) {
1766
+ return /* @__PURE__ */ jsxRuntime.jsx(
1767
+ reactNative.ScrollView,
1768
+ {
1769
+ horizontal: true,
1770
+ showsHorizontalScrollIndicator: false,
1771
+ accessibilityRole: "tablist",
1772
+ accessibilityLabel,
1773
+ contentContainerStyle: [container, style],
1774
+ children: tabs
1775
+ }
1776
+ );
1777
+ }
1778
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { accessibilityRole: "tablist", accessibilityLabel, style: [container, style], children: tabs });
1779
+ }
1780
+ function Accordion({
1781
+ items,
1782
+ value: controlledValue,
1783
+ defaultValue = [],
1784
+ onChange,
1785
+ single = false,
1786
+ style
1787
+ }) {
1788
+ const theme = useTheme();
1789
+ const [open, setOpen] = useControllableState(controlledValue, defaultValue, onChange);
1790
+ const toggle = (itemValue) => {
1791
+ const isOpen = open.includes(itemValue);
1792
+ if (single) {
1793
+ setOpen(isOpen ? [] : [itemValue]);
1794
+ return;
1795
+ }
1796
+ setOpen(isOpen ? open.filter((entry) => entry !== itemValue) : [...open, itemValue]);
1797
+ };
1798
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style, children: items.map((item, index) => {
1799
+ const isOpen = open.includes(item.value);
1800
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1801
+ reactNative.View,
1802
+ {
1803
+ style: {
1804
+ borderTopWidth: index === 0 ? 0 : theme.borderWidth.thin,
1805
+ borderTopColor: theme.colors.border
1806
+ },
1807
+ children: [
1808
+ /* @__PURE__ */ jsxRuntime.jsxs(
1809
+ reactNative.Pressable,
1810
+ {
1811
+ accessibilityRole: "button",
1812
+ accessibilityState: { expanded: isOpen },
1813
+ onPress: () => toggle(item.value),
1814
+ style: ({ pressed }) => ({
1815
+ flexDirection: "row",
1816
+ alignItems: "center",
1817
+ justifyContent: "space-between",
1818
+ gap: theme.space[3],
1819
+ paddingVertical: theme.space[4],
1820
+ backgroundColor: pressed ? theme.colors.surfaceActive : "transparent"
1821
+ }),
1822
+ children: [
1823
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", weight: "medium", style: { flex: 1 }, children: item.title }),
1824
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { transform: [{ rotate: isOpen ? "180deg" : "0deg" }] }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.ChevronDownIcon, size: 18, color: "textSecondary" }) })
1825
+ ]
1826
+ }
1827
+ ),
1828
+ isOpen ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { paddingBottom: theme.space[4] }, children: item.content }) : null
1829
+ ]
1830
+ },
1831
+ item.value
1832
+ );
1833
+ }) });
1834
+ }
1835
+ function Collapse({ title, defaultOpen = false, children, style }) {
1836
+ const [open, setOpen] = react.useState(defaultOpen);
1837
+ return /* @__PURE__ */ jsxRuntime.jsx(
1838
+ Accordion,
1839
+ {
1840
+ items: [{ value: "only", title, content: children }],
1841
+ value: open ? ["only"] : [],
1842
+ onChange: (next) => setOpen(next.length > 0),
1843
+ style
1844
+ }
1845
+ );
1846
+ }
1847
+
1848
+ // src/internal/date.ts
1849
+ function startOfDay(date) {
1850
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
1851
+ }
1852
+ function startOfMonth(date) {
1853
+ return new Date(date.getFullYear(), date.getMonth(), 1);
1854
+ }
1855
+ function addDays(date, amount) {
1856
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
1857
+ }
1858
+ function addMonths(date, amount) {
1859
+ return new Date(date.getFullYear(), date.getMonth() + amount, 1);
1860
+ }
1861
+ function isSameDay(a, b) {
1862
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
1863
+ }
1864
+ function dayKey(date) {
1865
+ const month = String(date.getMonth() + 1).padStart(2, "0");
1866
+ const day = String(date.getDate()).padStart(2, "0");
1867
+ return `${date.getFullYear()}-${month}-${day}`;
1868
+ }
1869
+ function monthGrid(month, weekStartsOn) {
1870
+ const first = startOfMonth(month);
1871
+ const offset = (first.getDay() - weekStartsOn + 7) % 7;
1872
+ const start = addDays(first, -offset);
1873
+ return Array.from({ length: 42 }, (_, index) => addDays(start, index));
1874
+ }
1875
+ function Calendar({
1876
+ value: controlledValue,
1877
+ defaultValue,
1878
+ onChange,
1879
+ defaultMonth,
1880
+ minDate,
1881
+ maxDate,
1882
+ weekStartsOn = 1,
1883
+ locale,
1884
+ markedDates,
1885
+ onMonthChange,
1886
+ style
1887
+ }) {
1888
+ const theme = useTheme();
1889
+ const [selected, setSelected] = useControllableState(
1890
+ controlledValue,
1891
+ defaultValue,
1892
+ onChange
1893
+ );
1894
+ const [month, setMonth] = react.useState(() => defaultMonth ?? selected ?? /* @__PURE__ */ new Date());
1895
+ const today = startOfDay(/* @__PURE__ */ new Date());
1896
+ const days = react.useMemo(() => monthGrid(month, weekStartsOn), [month, weekStartsOn]);
1897
+ const monthLabel = react.useMemo(
1898
+ () => new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(month),
1899
+ [month, locale]
1900
+ );
1901
+ const weekdayLabels = react.useMemo(() => {
1902
+ const formatter = new Intl.DateTimeFormat(locale, { weekday: "short" });
1903
+ const anchor = new Date(2024, 0, 7);
1904
+ return Array.from(
1905
+ { length: 7 },
1906
+ (_, index) => formatter.format(addDays(anchor, index + weekStartsOn))
1907
+ );
1908
+ }, [locale, weekStartsOn]);
1909
+ const goToMonth = (amount) => {
1910
+ const next = addMonths(month, amount);
1911
+ setMonth(next);
1912
+ onMonthChange?.(next);
1913
+ };
1914
+ const isDisabled = (date) => minDate !== void 0 && date < startOfDay(minDate) || maxDate !== void 0 && date > startOfDay(maxDate);
1915
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style, children: [
1916
+ /* @__PURE__ */ jsxRuntime.jsxs(
1917
+ reactNative.View,
1918
+ {
1919
+ style: {
1920
+ flexDirection: "row",
1921
+ alignItems: "center",
1922
+ justifyContent: "space-between",
1923
+ paddingBottom: theme.space[2]
1924
+ },
1925
+ children: [
1926
+ /* @__PURE__ */ jsxRuntime.jsx(
1927
+ IconButton,
1928
+ {
1929
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.ChevronLeftIcon, color: "textSecondary" }),
1930
+ accessibilityLabel: "Previous month",
1931
+ size: "sm",
1932
+ onPress: () => goToMonth(-1)
1933
+ }
1934
+ ),
1935
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", weight: "semibold", accessibilityLiveRegion: "polite", children: monthLabel }),
1936
+ /* @__PURE__ */ jsxRuntime.jsx(
1937
+ IconButton,
1938
+ {
1939
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.ChevronRightIcon, color: "textSecondary" }),
1940
+ accessibilityLabel: "Next month",
1941
+ size: "sm",
1942
+ onPress: () => goToMonth(1)
1943
+ }
1944
+ )
1945
+ ]
1946
+ }
1947
+ ),
1948
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row" }, children: weekdayLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx(
1949
+ Text,
1950
+ {
1951
+ size: "xs",
1952
+ weight: "medium",
1953
+ color: "textMuted",
1954
+ align: "center",
1955
+ style: { flex: 1, paddingVertical: theme.space[1.5] },
1956
+ children: label
1957
+ },
1958
+ label
1959
+ )) }),
1960
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", flexWrap: "wrap" }, children: days.map((day) => {
1961
+ const outside = day.getMonth() !== month.getMonth();
1962
+ const isSelected = selected !== void 0 && isSameDay(day, selected);
1963
+ const isToday = isSameDay(day, today);
1964
+ const disabled = isDisabled(day);
1965
+ const mark = markedDates?.[dayKey(day)];
1966
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1967
+ reactNative.Pressable,
1968
+ {
1969
+ accessibilityRole: "button",
1970
+ accessibilityLabel: new Intl.DateTimeFormat(locale, { dateStyle: "full" }).format(day),
1971
+ accessibilityState: { selected: isSelected, disabled },
1972
+ disabled,
1973
+ onPress: () => setSelected(day),
1974
+ style: {
1975
+ width: `${100 / 7}%`,
1976
+ aspectRatio: 1,
1977
+ alignItems: "center",
1978
+ justifyContent: "center",
1979
+ gap: 2
1980
+ },
1981
+ children: [
1982
+ /* @__PURE__ */ jsxRuntime.jsx(
1983
+ reactNative.View,
1984
+ {
1985
+ style: {
1986
+ width: 34,
1987
+ height: 34,
1988
+ alignItems: "center",
1989
+ justifyContent: "center",
1990
+ borderRadius: theme.radius.full,
1991
+ backgroundColor: isSelected ? theme.colors.primary : "transparent",
1992
+ borderWidth: !isSelected && isToday ? theme.borderWidth.thin : 0,
1993
+ borderColor: theme.colors.primary,
1994
+ opacity: disabled ? theme.opacity.disabled : outside ? 0.4 : 1
1995
+ },
1996
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1997
+ Text,
1998
+ {
1999
+ size: "sm",
2000
+ weight: isSelected || isToday ? "semibold" : "regular",
2001
+ color: isSelected ? "onPrimary" : outside ? "textMuted" : "text",
2002
+ children: day.getDate()
2003
+ }
2004
+ )
2005
+ }
2006
+ ),
2007
+ mark && !isSelected ? /* @__PURE__ */ jsxRuntime.jsx(
2008
+ reactNative.View,
2009
+ {
2010
+ style: {
2011
+ width: 4,
2012
+ height: 4,
2013
+ borderRadius: theme.radius.full,
2014
+ backgroundColor: theme.colors[mark]
2015
+ }
2016
+ }
2017
+ ) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { height: 4 } })
2018
+ ]
2019
+ },
2020
+ dayKey(day)
2021
+ );
2022
+ }) })
2023
+ ] });
2024
+ }
2025
+ function Carousel({
2026
+ children,
2027
+ slideWidth,
2028
+ gutter = 16,
2029
+ dots = true,
2030
+ onSlideChange,
2031
+ style,
2032
+ accessibilityLabel
2033
+ }) {
2034
+ const theme = useTheme();
2035
+ const { width } = reactNative.useWindowDimensions();
2036
+ const itemWidth = slideWidth ?? width - gutter * 2;
2037
+ const [index, setIndex] = react.useState(0);
2038
+ const lastIndex = react.useRef(0);
2039
+ const handleScroll = (event) => {
2040
+ const next = Math.round(event.nativeEvent.contentOffset.x / (itemWidth + theme.space[3]));
2041
+ if (next === lastIndex.current) return;
2042
+ lastIndex.current = next;
2043
+ setIndex(next);
2044
+ onSlideChange?.(next);
2045
+ };
2046
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style, children: [
2047
+ /* @__PURE__ */ jsxRuntime.jsx(
2048
+ reactNative.ScrollView,
2049
+ {
2050
+ horizontal: true,
2051
+ showsHorizontalScrollIndicator: false,
2052
+ decelerationRate: "fast",
2053
+ snapToInterval: itemWidth + theme.space[3],
2054
+ snapToAlignment: "start",
2055
+ onMomentumScrollEnd: handleScroll,
2056
+ accessibilityLabel,
2057
+ contentContainerStyle: { gap: theme.space[3], paddingHorizontal: gutter },
2058
+ children: children.map((child, slideIndex) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: itemWidth }, children: child }, slideIndex))
2059
+ }
2060
+ ),
2061
+ dots && children.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
2062
+ reactNative.View,
2063
+ {
2064
+ style: {
2065
+ flexDirection: "row",
2066
+ alignSelf: "center",
2067
+ gap: theme.space[1.5],
2068
+ paddingTop: theme.space[3]
2069
+ },
2070
+ children: children.map((_, dotIndex) => /* @__PURE__ */ jsxRuntime.jsx(
2071
+ reactNative.View,
2072
+ {
2073
+ style: {
2074
+ width: dotIndex === index ? 18 : 6,
2075
+ height: 6,
2076
+ borderRadius: theme.radius.full,
2077
+ backgroundColor: dotIndex === index ? theme.colors.primary : theme.colors.borderStrong
2078
+ }
2079
+ },
2080
+ dotIndex
2081
+ ))
2082
+ }
2083
+ ) : null
2084
+ ] });
2085
+ }
2086
+ function Tooltip({ label, placement = "top", children, style }) {
2087
+ const theme = useTheme();
2088
+ const [visible, setVisible] = react.useState(false);
2089
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [{ position: "relative" }, style], children: [
2090
+ /* @__PURE__ */ jsxRuntime.jsx(
2091
+ reactNative.Pressable,
2092
+ {
2093
+ accessibilityHint: label,
2094
+ delayLongPress: 200,
2095
+ onLongPress: () => setVisible(true),
2096
+ onPressOut: () => setVisible(false),
2097
+ children
2098
+ }
2099
+ ),
2100
+ visible ? /* @__PURE__ */ jsxRuntime.jsx(
2101
+ reactNative.View,
2102
+ {
2103
+ accessibilityRole: "alert",
2104
+ pointerEvents: "none",
2105
+ style: {
2106
+ position: "absolute",
2107
+ [placement === "top" ? "bottom" : "top"]: "100%",
2108
+ left: 0,
2109
+ marginVertical: theme.space[1.5],
2110
+ maxWidth: 240,
2111
+ paddingHorizontal: theme.space[2.5],
2112
+ paddingVertical: theme.space[1.5],
2113
+ borderRadius: theme.radius.md,
2114
+ backgroundColor: theme.colors.text,
2115
+ ...shadowStyle(theme.shadow.md)
2116
+ },
2117
+ children: /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", style: { color: theme.colors.textInverse }, children: label })
2118
+ }
2119
+ ) : null
2120
+ ] });
2121
+ }
2122
+ var maxWidth = { sm: 340, md: 420, lg: 560 };
2123
+ function Modal({
2124
+ open,
2125
+ onClose,
2126
+ size = "md",
2127
+ closeOnOverlayPress = true,
2128
+ accessibilityLabel,
2129
+ children,
2130
+ style
2131
+ }) {
2132
+ const theme = useTheme();
2133
+ const { width } = reactNative.useWindowDimensions();
2134
+ return /* @__PURE__ */ jsxRuntime.jsx(
2135
+ reactNative.Modal,
2136
+ {
2137
+ visible: open,
2138
+ transparent: true,
2139
+ animationType: "fade",
2140
+ statusBarTranslucent: true,
2141
+ onRequestClose: onClose,
2142
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2143
+ reactNative.Pressable,
2144
+ {
2145
+ accessibilityLabel: closeOnOverlayPress ? "Close" : void 0,
2146
+ onPress: closeOnOverlayPress ? onClose : void 0,
2147
+ style: {
2148
+ flex: 1,
2149
+ alignItems: "center",
2150
+ justifyContent: "center",
2151
+ padding: theme.space[5],
2152
+ backgroundColor: theme.colors.overlay
2153
+ },
2154
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2155
+ reactNative.Pressable,
2156
+ {
2157
+ accessibilityRole: "alert",
2158
+ accessibilityViewIsModal: true,
2159
+ accessibilityLabel,
2160
+ onPress: () => {
2161
+ },
2162
+ style: [
2163
+ {
2164
+ width: Math.min(maxWidth[size], width - theme.space[10]),
2165
+ maxHeight: "85%",
2166
+ borderRadius: theme.radius.xl,
2167
+ backgroundColor: theme.colors.surface,
2168
+ overflow: "hidden"
2169
+ },
2170
+ style
2171
+ ],
2172
+ children
2173
+ }
2174
+ )
2175
+ }
2176
+ )
2177
+ }
2178
+ );
2179
+ }
2180
+ function ModalHeader({ children, onClose, style }) {
2181
+ const theme = useTheme();
2182
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2183
+ reactNative.View,
2184
+ {
2185
+ style: [
2186
+ {
2187
+ flexDirection: "row",
2188
+ alignItems: "center",
2189
+ justifyContent: "space-between",
2190
+ gap: theme.space[3],
2191
+ paddingHorizontal: theme.space[5],
2192
+ paddingTop: theme.space[5],
2193
+ paddingBottom: theme.space[3]
2194
+ },
2195
+ style
2196
+ ],
2197
+ children: [
2198
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flex: 1 }, children }),
2199
+ onClose ? /* @__PURE__ */ jsxRuntime.jsx(
2200
+ IconButton,
2201
+ {
2202
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.XIcon, color: "textSecondary" }),
2203
+ accessibilityLabel: "Close",
2204
+ size: "sm",
2205
+ variant: "ghost",
2206
+ onPress: onClose
2207
+ }
2208
+ ) : null
2209
+ ]
2210
+ }
2211
+ );
2212
+ }
2213
+ function ModalTitle({ children }) {
2214
+ return /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: "lg", children });
2215
+ }
2216
+ function ModalBody({ children, scrollable = true, style }) {
2217
+ const theme = useTheme();
2218
+ const padding = { paddingHorizontal: theme.space[5], paddingBottom: theme.space[4] };
2219
+ if (!scrollable) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [padding, style], children });
2220
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { contentContainerStyle: [padding, style], keyboardShouldPersistTaps: "handled", children });
2221
+ }
2222
+ function ModalFooter({
2223
+ children,
2224
+ style
2225
+ }) {
2226
+ const theme = useTheme();
2227
+ return /* @__PURE__ */ jsxRuntime.jsx(
2228
+ reactNative.View,
2229
+ {
2230
+ style: [
2231
+ {
2232
+ flexDirection: "row",
2233
+ justifyContent: "flex-end",
2234
+ gap: theme.space[2],
2235
+ paddingHorizontal: theme.space[5],
2236
+ paddingTop: theme.space[3],
2237
+ paddingBottom: theme.space[5]
2238
+ },
2239
+ style
2240
+ ],
2241
+ children
2242
+ }
2243
+ );
2244
+ }
2245
+ function Drawer({
2246
+ open,
2247
+ onClose,
2248
+ side = "right",
2249
+ widthRatio = 0.85,
2250
+ title,
2251
+ accessibilityLabel,
2252
+ children,
2253
+ style
2254
+ }) {
2255
+ const theme = useTheme();
2256
+ const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
2257
+ const { width } = reactNative.useWindowDimensions();
2258
+ const panelWidth = width * widthRatio;
2259
+ const offset = side === "right" ? panelWidth : -panelWidth;
2260
+ const translateX = react.useRef(new reactNative.Animated.Value(offset)).current;
2261
+ react.useEffect(() => {
2262
+ reactNative.Animated.timing(translateX, {
2263
+ toValue: open ? 0 : offset,
2264
+ duration: open ? theme.motion.duration.normal : theme.motion.duration.fast,
2265
+ easing: reactNative.Easing.out(reactNative.Easing.cubic),
2266
+ useNativeDriver: true
2267
+ }).start();
2268
+ }, [open, offset, translateX, theme.motion.duration]);
2269
+ return /* @__PURE__ */ jsxRuntime.jsx(
2270
+ reactNative.Modal,
2271
+ {
2272
+ visible: open,
2273
+ transparent: true,
2274
+ animationType: "none",
2275
+ statusBarTranslucent: true,
2276
+ onRequestClose: onClose,
2277
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, flexDirection: side === "right" ? "row" : "row-reverse" }, children: [
2278
+ /* @__PURE__ */ jsxRuntime.jsx(
2279
+ reactNative.Pressable,
2280
+ {
2281
+ accessibilityLabel: "Close",
2282
+ onPress: onClose,
2283
+ style: { flex: 1, backgroundColor: theme.colors.overlay }
2284
+ }
2285
+ ),
2286
+ /* @__PURE__ */ jsxRuntime.jsxs(
2287
+ reactNative.Animated.View,
2288
+ {
2289
+ accessibilityViewIsModal: true,
2290
+ accessibilityLabel: accessibilityLabel ?? title,
2291
+ style: [
2292
+ {
2293
+ width: panelWidth,
2294
+ paddingTop: insets.top,
2295
+ paddingBottom: insets.bottom,
2296
+ backgroundColor: theme.colors.surface,
2297
+ transform: [{ translateX }]
2298
+ },
2299
+ style
2300
+ ],
2301
+ children: [
2302
+ title ? /* @__PURE__ */ jsxRuntime.jsxs(
2303
+ reactNative.View,
2304
+ {
2305
+ style: {
2306
+ flexDirection: "row",
2307
+ alignItems: "center",
2308
+ justifyContent: "space-between",
2309
+ gap: theme.space[3],
2310
+ padding: theme.space[4],
2311
+ borderBottomWidth: theme.borderWidth.thin,
2312
+ borderBottomColor: theme.colors.border
2313
+ },
2314
+ children: [
2315
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: "lg", style: { flex: 1 }, children: title }),
2316
+ /* @__PURE__ */ jsxRuntime.jsx(
2317
+ IconButton,
2318
+ {
2319
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: icons.XIcon, color: "textSecondary" }),
2320
+ accessibilityLabel: "Close",
2321
+ size: "sm",
2322
+ onPress: onClose
2323
+ }
2324
+ )
2325
+ ]
2326
+ }
2327
+ ) : null,
2328
+ children
2329
+ ]
2330
+ }
2331
+ )
2332
+ ] })
2333
+ }
2334
+ );
2335
+ }
2336
+ function ActionSheet({
2337
+ open,
2338
+ onClose,
2339
+ title,
2340
+ description,
2341
+ actions,
2342
+ cancelLabel = "Cancel",
2343
+ style
2344
+ }) {
2345
+ const theme = useTheme();
2346
+ return /* @__PURE__ */ jsxRuntime.jsxs(BottomSheet, { open, onClose, title, style, children: [
2347
+ description ? /* @__PURE__ */ jsxRuntime.jsx(
2348
+ Text,
2349
+ {
2350
+ size: "sm",
2351
+ color: "textMuted",
2352
+ style: { paddingHorizontal: theme.space[5], paddingTop: theme.space[2] },
2353
+ children: description
2354
+ }
2355
+ ) : null,
2356
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { paddingTop: theme.space[3] }, accessibilityRole: "menu", children: [
2357
+ actions.map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
2358
+ reactNative.Pressable,
2359
+ {
2360
+ accessibilityRole: "menuitem",
2361
+ accessibilityState: { disabled: action.disabled },
2362
+ disabled: action.disabled,
2363
+ onPress: () => {
2364
+ onClose();
2365
+ action.onPress();
2366
+ },
2367
+ style: ({ pressed }) => ({
2368
+ flexDirection: "row",
2369
+ alignItems: "center",
2370
+ gap: theme.space[3],
2371
+ paddingHorizontal: theme.space[5],
2372
+ paddingVertical: theme.space[4],
2373
+ backgroundColor: pressed ? theme.colors.surfaceActive : "transparent",
2374
+ opacity: action.disabled ? theme.opacity.disabled : 1
2375
+ }),
2376
+ children: [
2377
+ action.icon,
2378
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", weight: "medium", color: action.destructive ? "dangerText" : "text", children: action.label })
2379
+ ]
2380
+ },
2381
+ action.label
2382
+ )),
2383
+ cancelLabel ? /* @__PURE__ */ jsxRuntime.jsx(
2384
+ reactNative.Pressable,
2385
+ {
2386
+ accessibilityRole: "button",
2387
+ onPress: onClose,
2388
+ style: ({ pressed }) => ({
2389
+ paddingHorizontal: theme.space[5],
2390
+ paddingVertical: theme.space[4],
2391
+ borderTopWidth: theme.borderWidth.thin,
2392
+ borderTopColor: theme.colors.border,
2393
+ backgroundColor: pressed ? theme.colors.surfaceActive : "transparent"
2394
+ }),
2395
+ children: /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "md", weight: "semibold", color: "textSecondary", children: cancelLabel })
2396
+ }
2397
+ ) : null
2398
+ ] })
2399
+ ] });
2400
+ }
2401
+
2402
+ Object.defineProperty(exports, "createTheme", {
2403
+ enumerable: true,
2404
+ get: function () { return core.createTheme; }
2405
+ });
2406
+ Object.defineProperty(exports, "darkTheme", {
2407
+ enumerable: true,
2408
+ get: function () { return core.darkTheme; }
2409
+ });
2410
+ Object.defineProperty(exports, "lightTheme", {
2411
+ enumerable: true,
2412
+ get: function () { return core.lightTheme; }
2413
+ });
2414
+ Object.defineProperty(exports, "registerIcons", {
2415
+ enumerable: true,
2416
+ get: function () { return core.registerIcons; }
2417
+ });
2418
+ exports.Accordion = Accordion;
2419
+ exports.ActionSheet = ActionSheet;
2420
+ exports.Avatar = Avatar;
2421
+ exports.Badge = Badge;
2422
+ exports.BottomSheet = BottomSheet;
2423
+ exports.Box = Box;
2424
+ exports.Button = Button;
2425
+ exports.Calendar = Calendar;
2426
+ exports.Card = Card;
2427
+ exports.CardContent = CardContent;
2428
+ exports.CardDescription = CardDescription;
2429
+ exports.CardFooter = CardFooter;
2430
+ exports.CardHeader = CardHeader;
2431
+ exports.CardTitle = CardTitle;
2432
+ exports.Carousel = Carousel;
2433
+ exports.Checkbox = Checkbox;
2434
+ exports.Chip = Chip;
2435
+ exports.Collapse = Collapse;
2436
+ exports.Container = Container;
2437
+ exports.Divider = Divider;
2438
+ exports.Drawer = Drawer;
2439
+ exports.EmptyState = EmptyState;
2440
+ exports.Flex = Flex;
2441
+ exports.FormField = FormField;
2442
+ exports.HStack = HStack;
2443
+ exports.Heading = Heading;
2444
+ exports.Icon = Icon;
2445
+ exports.IconButton = IconButton;
2446
+ exports.Input = Input;
2447
+ exports.Label = Label;
2448
+ exports.ListRow = ListRow;
2449
+ exports.Modal = Modal;
2450
+ exports.ModalBody = ModalBody;
2451
+ exports.ModalFooter = ModalFooter;
2452
+ exports.ModalHeader = ModalHeader;
2453
+ exports.ModalTitle = ModalTitle;
2454
+ exports.Progress = Progress;
2455
+ exports.Radio = Radio;
2456
+ exports.RadioGroup = RadioGroup;
2457
+ exports.Screen = Screen;
2458
+ exports.Section = Section;
2459
+ exports.Select = Select;
2460
+ exports.Skeleton = Skeleton;
2461
+ exports.Spacer = Spacer;
2462
+ exports.Spinner = Spinner;
2463
+ exports.Stack = Stack;
2464
+ exports.Switch = Switch;
2465
+ exports.Tabs = Tabs;
2466
+ exports.Text = Text;
2467
+ exports.Textarea = Textarea;
2468
+ exports.ThemeProvider = ThemeProvider;
2469
+ exports.ToastProvider = ToastProvider;
2470
+ exports.Tooltip = Tooltip;
2471
+ exports.addDays = addDays;
2472
+ exports.addMonths = addMonths;
2473
+ exports.dayKey = dayKey;
2474
+ exports.isSameDay = isSameDay;
2475
+ exports.monthGrid = monthGrid;
2476
+ exports.shadowStyle = shadowStyle;
2477
+ exports.sizeMetrics = sizeMetrics;
2478
+ exports.splitStyleProps = splitStyleProps;
2479
+ exports.startOfDay = startOfDay;
2480
+ exports.startOfMonth = startOfMonth;
2481
+ exports.useColorMode = useColorMode;
2482
+ exports.useColorModeValue = useColorModeValue;
2483
+ exports.useTheme = useTheme;
2484
+ exports.useToast = useToast;
2485
+ exports.variantColors = variantColors;
2486
+ //# sourceMappingURL=index.cjs.map
2487
+ //# sourceMappingURL=index.cjs.map