@gaozh1024/rn-kit 0.2.0-beta.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.js ADDED
@@ -0,0 +1,4627 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ActionIcons: () => ActionIcons,
34
+ Alert: () => Alert,
35
+ AppButton: () => AppButton,
36
+ AppHeader: () => AppHeader,
37
+ AppImage: () => AppImage,
38
+ AppInput: () => AppInput,
39
+ AppList: () => AppList,
40
+ AppPressable: () => AppPressable,
41
+ AppProvider: () => AppProvider,
42
+ AppScrollView: () => AppScrollView,
43
+ AppStatusBar: () => AppStatusBar,
44
+ AppText: () => AppText,
45
+ AppView: () => AppView,
46
+ BottomTabBar: () => BottomTabBar,
47
+ Card: () => Card,
48
+ Center: () => Center,
49
+ Checkbox: () => Checkbox,
50
+ CheckboxGroup: () => CheckboxGroup,
51
+ Col: () => Col,
52
+ DatePicker: () => DatePicker,
53
+ DrawerContent: () => DrawerContent,
54
+ DrawerNavigator: () => DrawerNavigator,
55
+ ErrorCode: () => ErrorCode,
56
+ FileIcons: () => FileIcons,
57
+ FormItem: () => FormItem,
58
+ Icon: () => Icon,
59
+ Loading: () => Loading,
60
+ MemoryStorage: () => MemoryStorage,
61
+ NavigationContainer: () => import_native5.NavigationContainer,
62
+ NavigationIcons: () => NavigationIcons,
63
+ NavigationProvider: () => NavigationProvider,
64
+ OverlayProvider: () => OverlayProvider,
65
+ Page: () => Page,
66
+ Progress: () => Progress,
67
+ Radio: () => Radio,
68
+ RadioGroup: () => RadioGroup,
69
+ Row: () => Row,
70
+ SafeAreaProvider: () => import_react_native_safe_area_context5.SafeAreaProvider,
71
+ SafeBottom: () => SafeBottom,
72
+ SafeScreen: () => SafeScreen,
73
+ Select: () => Select,
74
+ Slider: () => Slider,
75
+ StackNavigator: () => StackNavigator,
76
+ StatusIcons: () => StatusIcons,
77
+ Switch: () => Switch,
78
+ TabNavigator: () => TabNavigator,
79
+ ThemeProvider: () => ThemeProvider,
80
+ Toast: () => Toast,
81
+ adjustBrightness: () => adjustBrightness,
82
+ capitalize: () => capitalize,
83
+ clamp: () => clamp,
84
+ clsx: () => import_clsx.clsx,
85
+ cn: () => cn,
86
+ createAPI: () => createAPI,
87
+ createDrawerScreens: () => createDrawerScreens,
88
+ createNavigationTheme: () => createNavigationTheme,
89
+ createStackScreens: () => createStackScreens,
90
+ createTabScreens: () => createTabScreens,
91
+ createTheme: () => createTheme,
92
+ deepMerge: () => deepMerge,
93
+ enhanceError: () => enhanceError,
94
+ formatCurrency: () => formatCurrency,
95
+ formatDate: () => formatDate,
96
+ formatNumber: () => formatNumber,
97
+ formatPercent: () => formatPercent,
98
+ formatRelativeTime: () => formatRelativeTime,
99
+ generateColorPalette: () => generateColorPalette,
100
+ getThemeColors: () => getThemeColors,
101
+ getValidationErrors: () => getValidationErrors,
102
+ hexToRgb: () => hexToRgb,
103
+ isDevelopment: () => isDevelopment,
104
+ isValidEmail: () => isValidEmail,
105
+ isValidPhone: () => isValidPhone,
106
+ mapHttpStatus: () => mapHttpStatus,
107
+ omit: () => omit,
108
+ pick: () => pick,
109
+ rgbToHex: () => rgbToHex,
110
+ slugify: () => slugify,
111
+ storage: () => storage,
112
+ truncate: () => truncate,
113
+ twMerge: () => import_tailwind_merge.twMerge,
114
+ useAlert: () => useAlert,
115
+ useAsyncState: () => useAsyncState,
116
+ useBackHandler: () => useBackHandler,
117
+ useDebounce: () => useDebounce,
118
+ useDimensions: () => useDimensions,
119
+ useDrawerNavigation: () => useDrawerNavigation,
120
+ useFocusEffect: () => import_native4.useFocusEffect,
121
+ useForm: () => useForm,
122
+ useInfinite: () => useInfinite,
123
+ useIsFocused: () => import_native4.useIsFocused,
124
+ useKeyboard: () => useKeyboard,
125
+ useLoading: () => useLoading,
126
+ useMemoizedFn: () => useMemoizedFn,
127
+ useMutation: () => import_react_query.useMutation,
128
+ useNavigation: () => useNavigation,
129
+ useNavigationState: () => useNavigationState,
130
+ useOrientation: () => useOrientation,
131
+ usePagination: () => usePagination,
132
+ usePrevious: () => usePrevious,
133
+ useQuery: () => import_react_query.useQuery,
134
+ useRefresh: () => useRefresh,
135
+ useRequest: () => useRequest,
136
+ useRoute: () => useRoute,
137
+ useScrollToTop: () => import_native4.useScrollToTop,
138
+ useSetState: () => useSetState,
139
+ useStackNavigation: () => useStackNavigation,
140
+ useStorage: () => useStorage,
141
+ useTabNavigation: () => useTabNavigation,
142
+ useTheme: () => useTheme,
143
+ useThemeColors: () => useThemeColors,
144
+ useThrottle: () => useThrottle,
145
+ useToast: () => useToast,
146
+ useToggle: () => useToggle,
147
+ useUpdateEffect: () => useUpdateEffect,
148
+ z: () => import_zod2.z
149
+ });
150
+ module.exports = __toCommonJS(index_exports);
151
+
152
+ // src/utils/cn.ts
153
+ var import_clsx = require("clsx");
154
+ var import_tailwind_merge = require("tailwind-merge");
155
+ function cn(...inputs) {
156
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
157
+ }
158
+
159
+ // src/utils/color.ts
160
+ function hexToRgb(hex) {
161
+ const clean = hex.replace("#", "");
162
+ const bigint = parseInt(clean, 16);
163
+ return {
164
+ r: bigint >> 16 & 255,
165
+ g: bigint >> 8 & 255,
166
+ b: bigint & 255
167
+ };
168
+ }
169
+ function rgbToHex(rgb) {
170
+ return "#" + [rgb.r, rgb.g, rgb.b].map((x) => x.toString(16).padStart(2, "0")).join("");
171
+ }
172
+ function adjustBrightness(rgb, factor) {
173
+ const adjust = (c) => Math.max(0, Math.min(255, c + (factor > 0 ? (255 - c) * factor : c * factor)));
174
+ return { r: adjust(rgb.r), g: adjust(rgb.g), b: adjust(rgb.b) };
175
+ }
176
+ function generateColorPalette(baseHex) {
177
+ const rgb = hexToRgb(baseHex);
178
+ const factors = {
179
+ 0: 0.95,
180
+ 50: 0.9,
181
+ 100: 0.75,
182
+ 200: 0.5,
183
+ 300: 0.3,
184
+ 400: 0.1,
185
+ 500: 0,
186
+ 600: -0.1,
187
+ 700: -0.25,
188
+ 800: -0.4,
189
+ 900: -0.55,
190
+ 950: -0.7
191
+ };
192
+ const result = {};
193
+ for (const [level, factor] of Object.entries(factors)) {
194
+ result[parseInt(level)] = rgbToHex(adjustBrightness(rgb, factor));
195
+ }
196
+ return result;
197
+ }
198
+
199
+ // src/utils/platform.ts
200
+ function isDevelopment() {
201
+ return process.env.NODE_ENV === "development";
202
+ }
203
+
204
+ // src/utils/date/index.ts
205
+ function formatDate(date, format = "yyyy-MM-dd") {
206
+ const d = new Date(date);
207
+ const year = d.getFullYear();
208
+ const month = String(d.getMonth() + 1).padStart(2, "0");
209
+ const day = String(d.getDate()).padStart(2, "0");
210
+ return format.replace("yyyy", String(year)).replace("MM", month).replace("dd", day);
211
+ }
212
+ function formatRelativeTime(date) {
213
+ const now = /* @__PURE__ */ new Date();
214
+ const diff = now.getTime() - new Date(date).getTime();
215
+ const minutes = Math.floor(diff / 6e4);
216
+ const hours = Math.floor(diff / 36e5);
217
+ const days = Math.floor(diff / 864e5);
218
+ if (minutes < 1) return "\u521A\u521A";
219
+ if (minutes < 60) return `${minutes}\u5206\u949F\u524D`;
220
+ if (hours < 24) return `${hours}\u5C0F\u65F6\u524D`;
221
+ if (days < 30) return `${days}\u5929\u524D`;
222
+ return formatDate(date);
223
+ }
224
+
225
+ // src/utils/string/index.ts
226
+ function truncate(str, length, suffix = "...") {
227
+ if (str.length <= length) return str;
228
+ return str.slice(0, length - suffix.length) + suffix;
229
+ }
230
+ function slugify(str) {
231
+ return str.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
232
+ }
233
+ function capitalize(str) {
234
+ return str.charAt(0).toUpperCase() + str.slice(1);
235
+ }
236
+
237
+ // src/utils/number/index.ts
238
+ function formatNumber(num) {
239
+ return num.toLocaleString("zh-CN");
240
+ }
241
+ function formatCurrency(num, currency = "\xA5") {
242
+ return `${currency}${formatNumber(num)}`;
243
+ }
244
+ function formatPercent(num, decimals = 2) {
245
+ return `${(num * 100).toFixed(decimals)}%`;
246
+ }
247
+ function clamp(num, min, max) {
248
+ return Math.min(Math.max(num, min), max);
249
+ }
250
+
251
+ // src/utils/object/index.ts
252
+ function deepMerge(target, source) {
253
+ const result = { ...target };
254
+ for (const key in source) {
255
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
256
+ result[key] = deepMerge(result[key] || {}, source[key]);
257
+ } else {
258
+ result[key] = source[key];
259
+ }
260
+ }
261
+ return result;
262
+ }
263
+ function pick(obj, keys) {
264
+ const result = {};
265
+ for (const key of keys) {
266
+ if (key in obj) result[key] = obj[key];
267
+ }
268
+ return result;
269
+ }
270
+ function omit(obj, keys) {
271
+ const result = { ...obj };
272
+ for (const key of keys) delete result[key];
273
+ return result;
274
+ }
275
+
276
+ // src/utils/validation/index.ts
277
+ function getValidationErrors(error) {
278
+ const errors = {};
279
+ for (const issue of error.issues) {
280
+ const path = issue.path.join(".");
281
+ errors[path] = issue.message;
282
+ }
283
+ return errors;
284
+ }
285
+ function isValidEmail(email) {
286
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
287
+ }
288
+ function isValidPhone(phone) {
289
+ return /^1[3-9]\d{9}$/.test(phone);
290
+ }
291
+
292
+ // src/theme/create-theme.ts
293
+ var defaultSpacing = {
294
+ 0: 0,
295
+ 1: 4,
296
+ 2: 8,
297
+ 3: 12,
298
+ 4: 16,
299
+ 5: 20,
300
+ 6: 24,
301
+ 8: 32,
302
+ 10: 40,
303
+ 12: 48
304
+ };
305
+ var defaultBorderRadius = {
306
+ none: 0,
307
+ sm: 2,
308
+ md: 6,
309
+ lg: 8,
310
+ xl: 12,
311
+ "2xl": 16,
312
+ "3xl": 24,
313
+ full: 9999
314
+ };
315
+ var defaultColors = {
316
+ background: "#ffffff",
317
+ card: "#ffffff",
318
+ text: "#1f2937",
319
+ border: "#e5e7eb",
320
+ error: "#ef4444",
321
+ success: "#22c55e",
322
+ warning: "#f59e0b",
323
+ info: "#3b82f6"
324
+ };
325
+ function resolveColor(token) {
326
+ return typeof token === "string" ? generateColorPalette(token) : token;
327
+ }
328
+ function createTheme(config) {
329
+ const colors = {};
330
+ for (const [name, value] of Object.entries(defaultColors)) {
331
+ colors[name] = resolveColor(value);
332
+ }
333
+ for (const [name, token] of Object.entries(config.colors)) {
334
+ colors[name] = resolveColor(token);
335
+ }
336
+ return {
337
+ colors,
338
+ spacing: config.spacing ?? defaultSpacing,
339
+ borderRadius: config.borderRadius ?? defaultBorderRadius
340
+ };
341
+ }
342
+
343
+ // src/theme/provider.tsx
344
+ var import_react = require("react");
345
+ var import_jsx_runtime = require("nativewind/jsx-runtime");
346
+ var ThemeContext = (0, import_react.createContext)(null);
347
+ var fallbackTheme = createTheme({
348
+ colors: {
349
+ primary: "#f38b32",
350
+ secondary: "#3b82f6",
351
+ background: "#ffffff",
352
+ card: "#ffffff",
353
+ text: "#1f2937",
354
+ border: "#e5e7eb",
355
+ error: "#ef4444",
356
+ success: "#22c55e",
357
+ warning: "#f59e0b",
358
+ info: "#3b82f6"
359
+ }
360
+ });
361
+ function ThemeProvider({
362
+ children,
363
+ light,
364
+ dark,
365
+ defaultDark = false,
366
+ isDark: controlledIsDark,
367
+ onThemeChange
368
+ }) {
369
+ const [internalIsDark, setInternalIsDark] = (0, import_react.useState)(defaultDark);
370
+ const isDark = controlledIsDark ?? internalIsDark;
371
+ const theme = (0, import_react.useMemo)(() => {
372
+ const config = isDark && dark ? dark : light;
373
+ return createTheme(config);
374
+ }, [isDark, light, dark]);
375
+ const toggleTheme = () => {
376
+ const nextIsDark = !isDark;
377
+ if (controlledIsDark === void 0) {
378
+ setInternalIsDark(nextIsDark);
379
+ }
380
+ onThemeChange?.(nextIsDark);
381
+ };
382
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: { theme, isDark, toggleTheme }, children });
383
+ }
384
+ function useTheme() {
385
+ const context = (0, import_react.useContext)(ThemeContext);
386
+ if (!context) throw new Error("useTheme must be used within ThemeProvider");
387
+ return context;
388
+ }
389
+ function useOptionalTheme() {
390
+ const context = (0, import_react.useContext)(ThemeContext);
391
+ return context || {
392
+ theme: fallbackTheme,
393
+ isDark: false,
394
+ toggleTheme: () => {
395
+ }
396
+ };
397
+ }
398
+
399
+ // src/theme/tokens.ts
400
+ var import_react2 = require("react");
401
+ function getThemeColors(theme, isDark) {
402
+ return {
403
+ primary: theme.colors.primary?.[500] || "#f38b32",
404
+ primarySurface: isDark ? theme.colors.primary?.[900] || "#7c2d12" : theme.colors.primary?.[50] || "#fff7ed",
405
+ background: theme.colors.background?.[500] || (isDark ? "#0a0a0a" : "#ffffff"),
406
+ card: theme.colors.card?.[500] || (isDark ? "#1f2937" : "#ffffff"),
407
+ cardElevated: (isDark ? theme.colors.card?.[800] || theme.colors.card?.[700] : theme.colors.card?.[500]) || (isDark ? "#1f2937" : "#ffffff"),
408
+ text: theme.colors.text?.[500] || (isDark ? "#ffffff" : "#1f2937"),
409
+ textSecondary: isDark ? "#d1d5db" : "#374151",
410
+ textMuted: isDark ? "#9ca3af" : "#6b7280",
411
+ textInverse: "#ffffff",
412
+ border: isDark ? theme.colors.border?.[600] || theme.colors.border?.[500] || "#4b5563" : theme.colors.border?.[500] || "#d1d5db",
413
+ divider: isDark ? theme.colors.border?.[700] || theme.colors.border?.[500] || "#374151" : theme.colors.border?.[500] || "#e5e7eb",
414
+ iconMuted: isDark ? "#9ca3af" : "#6b7280"
415
+ };
416
+ }
417
+ function useThemeColors() {
418
+ const { theme, isDark } = useOptionalTheme();
419
+ return (0, import_react2.useMemo)(() => getThemeColors(theme, isDark), [theme, isDark]);
420
+ }
421
+
422
+ // src/core/index.ts
423
+ var import_zod2 = require("zod");
424
+ var import_react_query = require("@tanstack/react-query");
425
+
426
+ // src/core/error/types.ts
427
+ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
428
+ ErrorCode2["VALIDATION"] = "VALIDATION";
429
+ ErrorCode2["NETWORK"] = "NETWORK";
430
+ ErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED";
431
+ ErrorCode2["FORBIDDEN"] = "FORBIDDEN";
432
+ ErrorCode2["SERVER"] = "SERVER";
433
+ ErrorCode2["BUSINESS"] = "BUSINESS";
434
+ ErrorCode2["UNKNOWN"] = "UNKNOWN";
435
+ return ErrorCode2;
436
+ })(ErrorCode || {});
437
+
438
+ // src/core/error/helpers.ts
439
+ function mapHttpStatus(status) {
440
+ if (status === 401) return "UNAUTHORIZED" /* UNAUTHORIZED */;
441
+ if (status === 403) return "FORBIDDEN" /* FORBIDDEN */;
442
+ if (status >= 400 && status < 500) return "VALIDATION" /* VALIDATION */;
443
+ if (status >= 500) return "SERVER" /* SERVER */;
444
+ return "UNKNOWN" /* UNKNOWN */;
445
+ }
446
+ function enhanceError(error) {
447
+ return {
448
+ ...error,
449
+ get isValidation() {
450
+ return error.code === "VALIDATION" /* VALIDATION */;
451
+ },
452
+ get isNetwork() {
453
+ return error.code === "NETWORK" /* NETWORK */;
454
+ },
455
+ get isAuth() {
456
+ return error.code === "UNAUTHORIZED" /* UNAUTHORIZED */ || error.code === "FORBIDDEN" /* FORBIDDEN */;
457
+ },
458
+ get isRetryable() {
459
+ return error.retryable ?? error.code === "NETWORK" /* NETWORK */;
460
+ }
461
+ };
462
+ }
463
+
464
+ // src/core/hooks/useAsyncState.ts
465
+ var import_react3 = require("react");
466
+ function useAsyncState() {
467
+ const [data, setData] = (0, import_react3.useState)(null);
468
+ const [error, setError] = (0, import_react3.useState)(null);
469
+ const [loading, setLoading] = (0, import_react3.useState)(false);
470
+ const execute = (0, import_react3.useCallback)(async (promise) => {
471
+ setLoading(true);
472
+ setError(null);
473
+ try {
474
+ const result = await promise;
475
+ setData(result);
476
+ return result;
477
+ } catch (err) {
478
+ const appError = {
479
+ code: err.code || "UNKNOWN",
480
+ message: err.message || "Unknown error",
481
+ ...err
482
+ };
483
+ const enhanced = enhanceError(appError);
484
+ setError(enhanced);
485
+ throw enhanced;
486
+ } finally {
487
+ setLoading(false);
488
+ }
489
+ }, []);
490
+ const reset = (0, import_react3.useCallback)(() => {
491
+ setData(null);
492
+ setError(null);
493
+ setLoading(false);
494
+ }, []);
495
+ return { data, error, loading, execute, reset };
496
+ }
497
+
498
+ // src/core/api/create-api.ts
499
+ var import_zod = require("zod");
500
+ function parseZodError(error) {
501
+ const first = error.errors[0];
502
+ return {
503
+ code: "VALIDATION" /* VALIDATION */,
504
+ message: first?.message || "Validation failed",
505
+ field: first?.path.join(".")
506
+ };
507
+ }
508
+ function isAppError(error) {
509
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string";
510
+ }
511
+ function parseNetworkError(error) {
512
+ return {
513
+ code: "NETWORK" /* NETWORK */,
514
+ message: error instanceof Error ? error.message : "Network request failed",
515
+ retryable: true,
516
+ original: error
517
+ };
518
+ }
519
+ function parseHttpError(response, data) {
520
+ const fallbackMessage = response.statusText || `Request failed with status ${response.status}`;
521
+ const message = typeof data === "object" && data !== null && "message" in data && typeof data.message === "string" ? data.message : fallbackMessage;
522
+ return {
523
+ code: mapHttpStatus(response.status),
524
+ message,
525
+ statusCode: response.status,
526
+ retryable: response.status >= 500,
527
+ original: data
528
+ };
529
+ }
530
+ function parseUnknownError(error) {
531
+ if (error instanceof import_zod.ZodError) {
532
+ return parseZodError(error);
533
+ }
534
+ if (isAppError(error)) {
535
+ return error;
536
+ }
537
+ return {
538
+ code: "UNKNOWN" /* UNKNOWN */,
539
+ message: error instanceof Error ? error.message : "Unknown error",
540
+ original: error
541
+ };
542
+ }
543
+ async function notifyError(error, context, endpoint, config) {
544
+ const enhanced = enhanceError(error);
545
+ await endpoint.onError?.(enhanced, context);
546
+ await config.onError?.(enhanced, context);
547
+ return enhanced;
548
+ }
549
+ function createAPI(config) {
550
+ const endpoints = {};
551
+ const fetcher = config.fetcher ?? fetch;
552
+ for (const [name, ep] of Object.entries(config.endpoints)) {
553
+ endpoints[name] = async (input) => {
554
+ const context = {
555
+ endpointName: name,
556
+ path: ep.path,
557
+ method: ep.method,
558
+ input
559
+ };
560
+ if (ep.input) {
561
+ const result = ep.input.safeParse(input);
562
+ if (!result.success) {
563
+ throw await notifyError(parseZodError(result.error), context, ep, config);
564
+ }
565
+ }
566
+ const url = config.baseURL + ep.path;
567
+ let response;
568
+ try {
569
+ response = await fetcher(url, { method: ep.method });
570
+ } catch (error) {
571
+ throw await notifyError(parseNetworkError(error), context, ep, config);
572
+ }
573
+ context.response = response;
574
+ let data = void 0;
575
+ const contentType = response.headers.get("content-type") || "";
576
+ const canParseJson = contentType.includes("application/json");
577
+ if (canParseJson) {
578
+ try {
579
+ data = await response.json();
580
+ } catch {
581
+ data = void 0;
582
+ }
583
+ }
584
+ context.responseData = data;
585
+ if (!response.ok) {
586
+ throw await notifyError(parseHttpError(response, data), context, ep, config);
587
+ }
588
+ const businessError = ep.parseBusinessError?.(data, response) ?? config.parseBusinessError?.(data, response);
589
+ if (businessError) {
590
+ throw await notifyError(businessError, context, ep, config);
591
+ }
592
+ try {
593
+ if (ep.output) {
594
+ return ep.output.parse(data);
595
+ }
596
+ } catch (error) {
597
+ throw await notifyError(parseUnknownError(error), context, ep, config);
598
+ }
599
+ return data;
600
+ };
601
+ }
602
+ return endpoints;
603
+ }
604
+
605
+ // src/core/storage/memory-storage.ts
606
+ var MemoryStorage = class {
607
+ constructor() {
608
+ /** 内部存储的 Map 实例 */
609
+ this.memory = /* @__PURE__ */ new Map();
610
+ }
611
+ /**
612
+ * 存储键值对
613
+ * @param key - 存储键名
614
+ * @param value - 要存储的字符串值
615
+ * @returns 解析为 void 的 Promise
616
+ */
617
+ async setItem(key, value) {
618
+ this.memory.set(key, value);
619
+ }
620
+ /**
621
+ * 根据键名获取值
622
+ * @param key - 存储键名
623
+ * @returns 存储的字符串值,如果不存在则返回 null
624
+ */
625
+ async getItem(key) {
626
+ return this.memory.get(key) ?? null;
627
+ }
628
+ /**
629
+ * 删除指定键名的存储项
630
+ * @param key - 要删除的键名
631
+ * @returns 解析为 void 的 Promise
632
+ */
633
+ async removeItem(key) {
634
+ this.memory.delete(key);
635
+ }
636
+ };
637
+ var storage = new MemoryStorage();
638
+
639
+ // src/core/hooks/useRequest.ts
640
+ var import_react4 = require("react");
641
+ function useRequest(service, options = {}) {
642
+ const {
643
+ manual = false,
644
+ deps = [],
645
+ defaultParams,
646
+ onSuccess,
647
+ onError,
648
+ onFinally,
649
+ retryCount = 0,
650
+ retryDelay = 1e3
651
+ } = options;
652
+ const [data, setData] = (0, import_react4.useState)(void 0);
653
+ const [loading, setLoading] = (0, import_react4.useState)(!manual);
654
+ const [error, setError] = (0, import_react4.useState)(void 0);
655
+ const serviceRef = (0, import_react4.useRef)(service);
656
+ const paramsRef = (0, import_react4.useRef)(defaultParams);
657
+ const retryCountRef = (0, import_react4.useRef)(0);
658
+ const canceledRef = (0, import_react4.useRef)(false);
659
+ serviceRef.current = service;
660
+ const run = (0, import_react4.useCallback)(
661
+ async (...params) => {
662
+ paramsRef.current = params;
663
+ setLoading(true);
664
+ setError(void 0);
665
+ try {
666
+ const result = await serviceRef.current(...params);
667
+ if (!canceledRef.current) {
668
+ setData(result);
669
+ retryCountRef.current = 0;
670
+ onSuccess?.(result, params);
671
+ }
672
+ return result;
673
+ } catch (err) {
674
+ if (!canceledRef.current) {
675
+ const error2 = err instanceof Error ? err : new Error(String(err));
676
+ setError(error2);
677
+ onError?.(error2, params);
678
+ if (retryCountRef.current < retryCount) {
679
+ retryCountRef.current++;
680
+ setTimeout(() => {
681
+ if (!canceledRef.current) {
682
+ run(...params);
683
+ }
684
+ }, retryDelay * retryCountRef.current);
685
+ }
686
+ }
687
+ throw err;
688
+ } finally {
689
+ if (!canceledRef.current) {
690
+ setLoading(false);
691
+ onFinally?.(params);
692
+ }
693
+ }
694
+ },
695
+ [onSuccess, onError, onFinally, retryCount, retryDelay]
696
+ );
697
+ const refresh = (0, import_react4.useCallback)(() => {
698
+ if (paramsRef.current) {
699
+ return run(...paramsRef.current);
700
+ }
701
+ throw new Error("No params to refresh");
702
+ }, [run]);
703
+ const cancel = (0, import_react4.useCallback)(() => {
704
+ canceledRef.current = true;
705
+ }, []);
706
+ const mutate = (0, import_react4.useCallback)((newData) => {
707
+ setData((prev) => {
708
+ if (typeof newData === "function") {
709
+ return newData(prev);
710
+ }
711
+ return newData;
712
+ });
713
+ }, []);
714
+ (0, import_react4.useEffect)(() => {
715
+ if (!manual) {
716
+ run(...defaultParams || []);
717
+ }
718
+ return () => {
719
+ canceledRef.current = true;
720
+ };
721
+ }, deps);
722
+ return {
723
+ data,
724
+ loading,
725
+ error,
726
+ run,
727
+ refresh,
728
+ cancel,
729
+ mutate
730
+ };
731
+ }
732
+
733
+ // src/core/hooks/usePagination.ts
734
+ var import_react5 = require("react");
735
+ function usePagination(service, options = {}) {
736
+ const { defaultCurrent = 1, defaultPageSize = 10 } = options;
737
+ const [data, setData] = (0, import_react5.useState)([]);
738
+ const [current, setCurrent] = (0, import_react5.useState)(defaultCurrent);
739
+ const [pageSize] = (0, import_react5.useState)(defaultPageSize);
740
+ const [total, setTotal] = (0, import_react5.useState)(0);
741
+ const [loading, setLoading] = (0, import_react5.useState)(false);
742
+ const [refreshing, setRefreshing] = (0, import_react5.useState)(false);
743
+ const [loadingMore, setLoadingMore] = (0, import_react5.useState)(false);
744
+ const [error, setError] = (0, import_react5.useState)(null);
745
+ const serviceRef = (0, import_react5.useRef)(service);
746
+ serviceRef.current = service;
747
+ const hasMore = data.length < total;
748
+ const fetch2 = (0, import_react5.useCallback)(async (params, isRefresh = false) => {
749
+ try {
750
+ const result = await serviceRef.current(params);
751
+ const items = result.data ?? result.list ?? [];
752
+ if (isRefresh) {
753
+ setData(items);
754
+ } else {
755
+ setData((prev) => [...prev, ...items]);
756
+ }
757
+ setTotal(result.total);
758
+ setError(null);
759
+ return result;
760
+ } catch (err) {
761
+ const error2 = err instanceof Error ? err : new Error(String(err));
762
+ setError(error2);
763
+ throw err;
764
+ }
765
+ }, []);
766
+ const refresh = (0, import_react5.useCallback)(async () => {
767
+ setRefreshing(true);
768
+ try {
769
+ await fetch2({ current: 1, pageSize }, true);
770
+ setCurrent(1);
771
+ } finally {
772
+ setRefreshing(false);
773
+ }
774
+ }, [fetch2, pageSize]);
775
+ const loadMore = (0, import_react5.useCallback)(async () => {
776
+ if (loadingMore || !hasMore) return;
777
+ setLoadingMore(true);
778
+ try {
779
+ const nextPage = current + 1;
780
+ await fetch2({ current: nextPage, pageSize }, false);
781
+ setCurrent(nextPage);
782
+ } finally {
783
+ setLoadingMore(false);
784
+ }
785
+ }, [fetch2, current, pageSize, loadingMore, hasMore]);
786
+ const changePage = (0, import_react5.useCallback)(
787
+ async (page) => {
788
+ setLoading(true);
789
+ try {
790
+ await fetch2({ current: page, pageSize }, true);
791
+ setCurrent(page);
792
+ } finally {
793
+ setLoading(false);
794
+ }
795
+ },
796
+ [fetch2, pageSize]
797
+ );
798
+ return {
799
+ data,
800
+ current,
801
+ pageSize,
802
+ total,
803
+ hasMore,
804
+ loading,
805
+ refreshing,
806
+ loadingMore,
807
+ error,
808
+ refresh,
809
+ loadMore,
810
+ changePage
811
+ };
812
+ }
813
+
814
+ // src/core/hooks/usePrevious.ts
815
+ var import_react6 = require("react");
816
+ function usePrevious(value) {
817
+ const ref = (0, import_react6.useRef)(void 0);
818
+ (0, import_react6.useEffect)(() => {
819
+ ref.current = value;
820
+ }, [value]);
821
+ return ref.current;
822
+ }
823
+
824
+ // src/core/hooks/useSetState.ts
825
+ var import_react7 = require("react");
826
+ function useSetState(initialState) {
827
+ const [state, setState] = (0, import_react7.useState)(initialState);
828
+ const setMergeState = (0, import_react7.useCallback)((patch) => {
829
+ setState((prev) => ({
830
+ ...prev,
831
+ ...typeof patch === "function" ? patch(prev) : patch
832
+ }));
833
+ }, []);
834
+ const resetState = (0, import_react7.useCallback)(() => {
835
+ setState(initialState);
836
+ }, [initialState]);
837
+ return [state, setMergeState, resetState];
838
+ }
839
+
840
+ // src/core/hooks/useMemoizedFn.ts
841
+ var import_react8 = require("react");
842
+ function useMemoizedFn(fn) {
843
+ const fnRef = (0, import_react8.useRef)(fn);
844
+ fnRef.current = fn;
845
+ const memoizedFn = (0, import_react8.useCallback)(
846
+ ((...args) => {
847
+ return fnRef.current(...args);
848
+ }),
849
+ []
850
+ );
851
+ return memoizedFn;
852
+ }
853
+
854
+ // src/core/hooks/useUpdateEffect.ts
855
+ var import_react9 = require("react");
856
+ function useUpdateEffect(effect, deps) {
857
+ const isFirstRender = (0, import_react9.useRef)(true);
858
+ (0, import_react9.useEffect)(() => {
859
+ if (isFirstRender.current) {
860
+ isFirstRender.current = false;
861
+ return;
862
+ }
863
+ return effect();
864
+ }, deps);
865
+ }
866
+
867
+ // src/core/hooks/useStorage.ts
868
+ var import_react10 = require("react");
869
+ function useStorage(key, defaultValue) {
870
+ const [value, setValue] = (0, import_react10.useState)(defaultValue);
871
+ const [, setIsLoaded] = (0, import_react10.useState)(false);
872
+ (0, import_react10.useEffect)(() => {
873
+ const loadValue = async () => {
874
+ try {
875
+ const stored = await storage.getItem(key);
876
+ if (stored !== null) {
877
+ setValue(JSON.parse(stored));
878
+ }
879
+ } catch (error) {
880
+ console.warn(`Failed to load storage key "${key}":`, error);
881
+ } finally {
882
+ setIsLoaded(true);
883
+ }
884
+ };
885
+ loadValue();
886
+ }, [key]);
887
+ const setStoredValue = (0, import_react10.useCallback)(
888
+ async (newValue) => {
889
+ try {
890
+ const valueToStore = newValue instanceof Function ? newValue(value) : newValue;
891
+ setValue(valueToStore);
892
+ await storage.setItem(key, JSON.stringify(valueToStore));
893
+ } catch (error) {
894
+ console.warn(`Failed to save storage key "${key}":`, error);
895
+ }
896
+ },
897
+ [key, value]
898
+ );
899
+ const removeValue = (0, import_react10.useCallback)(async () => {
900
+ try {
901
+ setValue(defaultValue);
902
+ await storage.removeItem(key);
903
+ } catch (error) {
904
+ console.warn(`Failed to remove storage key "${key}":`, error);
905
+ }
906
+ }, [key, defaultValue]);
907
+ return [value, setStoredValue, removeValue];
908
+ }
909
+
910
+ // src/core/hooks/useRefresh.ts
911
+ var import_react11 = require("react");
912
+ function useRefresh(fetcher) {
913
+ const [data, setData] = (0, import_react11.useState)(void 0);
914
+ const [refreshing, setRefreshing] = (0, import_react11.useState)(false);
915
+ const [error, setError] = (0, import_react11.useState)(void 0);
916
+ const refresh = (0, import_react11.useCallback)(async () => {
917
+ setRefreshing(true);
918
+ setError(void 0);
919
+ try {
920
+ const result = await fetcher();
921
+ setData(result);
922
+ } catch (err) {
923
+ const errorObj = err instanceof Error ? err : new Error(String(err));
924
+ setError(errorObj);
925
+ } finally {
926
+ setRefreshing(false);
927
+ }
928
+ }, [fetcher]);
929
+ return {
930
+ data,
931
+ refreshing,
932
+ error,
933
+ refresh
934
+ };
935
+ }
936
+
937
+ // src/core/hooks/useInfinite.ts
938
+ var import_react12 = require("react");
939
+ function useInfinite(fetcher, options = {}) {
940
+ const { defaultPage = 1, pageSize = 10 } = options;
941
+ const [data, setData] = (0, import_react12.useState)([]);
942
+ const [page, setPage] = (0, import_react12.useState)(defaultPage);
943
+ const [loading, setLoading] = (0, import_react12.useState)(false);
944
+ const [loadingMore, setLoadingMore] = (0, import_react12.useState)(false);
945
+ const [hasMore, setHasMore] = (0, import_react12.useState)(true);
946
+ const [error, setError] = (0, import_react12.useState)(void 0);
947
+ const fetchData = (0, import_react12.useCallback)(
948
+ async (targetPage, isLoadMore) => {
949
+ if (isLoadMore) {
950
+ setLoadingMore(true);
951
+ } else {
952
+ setLoading(true);
953
+ }
954
+ setError(void 0);
955
+ try {
956
+ const result = await fetcher({ page: targetPage, pageSize });
957
+ const items = result.data ?? result.list ?? [];
958
+ if (isLoadMore) {
959
+ setData((prev) => [...prev, ...items]);
960
+ } else {
961
+ setData(items);
962
+ }
963
+ setHasMore(result.hasMore);
964
+ setPage(targetPage);
965
+ } catch (err) {
966
+ const errorObj = err instanceof Error ? err : new Error(String(err));
967
+ setError(errorObj);
968
+ } finally {
969
+ setLoading(false);
970
+ setLoadingMore(false);
971
+ }
972
+ },
973
+ [fetcher, pageSize]
974
+ );
975
+ const load = (0, import_react12.useCallback)(async () => {
976
+ await fetchData(defaultPage, false);
977
+ }, [fetchData, defaultPage]);
978
+ const loadMore = (0, import_react12.useCallback)(async () => {
979
+ if (loadingMore || !hasMore) return;
980
+ await fetchData(page + 1, true);
981
+ }, [fetchData, page, loadingMore, hasMore]);
982
+ const refresh = (0, import_react12.useCallback)(async () => {
983
+ await fetchData(defaultPage, false);
984
+ }, [fetchData, defaultPage]);
985
+ const reset = (0, import_react12.useCallback)(() => {
986
+ setData([]);
987
+ setPage(defaultPage);
988
+ setLoading(false);
989
+ setLoadingMore(false);
990
+ setHasMore(true);
991
+ setError(void 0);
992
+ }, [defaultPage]);
993
+ return {
994
+ data,
995
+ page,
996
+ loading,
997
+ loadingMore,
998
+ hasMore,
999
+ error,
1000
+ load,
1001
+ loadMore,
1002
+ refresh,
1003
+ reset
1004
+ };
1005
+ }
1006
+
1007
+ // src/ui/primitives/AppView.tsx
1008
+ var import_react_native = require("react-native");
1009
+
1010
+ // src/ui/utils/theme-color.ts
1011
+ var grayPalette = {
1012
+ 50: "#f9fafb",
1013
+ 100: "#f3f4f6",
1014
+ 200: "#e5e7eb",
1015
+ 300: "#d1d5db",
1016
+ 400: "#9ca3af",
1017
+ 500: "#6b7280",
1018
+ 600: "#4b5563",
1019
+ 700: "#374151",
1020
+ 800: "#1f2937",
1021
+ 900: "#111827",
1022
+ 950: "#030712"
1023
+ };
1024
+ var staticPalettes = {
1025
+ gray: grayPalette,
1026
+ white: { 500: "#ffffff" },
1027
+ black: { 500: "#000000" }
1028
+ };
1029
+ function resolvePaletteColor(name, shade, theme) {
1030
+ const palette = theme.colors[name] ?? staticPalettes[name];
1031
+ if (!palette) return void 0;
1032
+ return palette[shade] ?? palette[500] ?? palette["500"];
1033
+ }
1034
+ function resolveNamedColor(color, theme, isDark) {
1035
+ if (!color) return void 0;
1036
+ switch (color) {
1037
+ case "text":
1038
+ case "foreground":
1039
+ case "default":
1040
+ return theme.colors.text?.[500] ?? (isDark ? "#ffffff" : "#1f2937");
1041
+ case "muted":
1042
+ case "text-muted":
1043
+ case "secondary-text":
1044
+ return isDark ? "#9ca3af" : "#6b7280";
1045
+ case "inverse":
1046
+ case "on-primary":
1047
+ return "#ffffff";
1048
+ case "background":
1049
+ case "screen":
1050
+ case "page":
1051
+ return theme.colors.background?.[500] ?? (isDark ? "#0a0a0a" : "#ffffff");
1052
+ case "card":
1053
+ case "surface":
1054
+ return theme.colors.card?.[500] ?? (isDark ? "#1f2937" : "#ffffff");
1055
+ case "border":
1056
+ return theme.colors.border?.[500] ?? (isDark ? "#404040" : "#e5e5e5");
1057
+ case "danger":
1058
+ return theme.colors.error?.[500] ?? "#ef4444";
1059
+ case "success":
1060
+ case "warning":
1061
+ case "error":
1062
+ case "info":
1063
+ case "primary":
1064
+ case "secondary":
1065
+ return theme.colors[color]?.[500];
1066
+ case "transparent":
1067
+ return "transparent";
1068
+ }
1069
+ if (color.startsWith("#") || color.startsWith("rgb(") || color.startsWith("rgba(")) {
1070
+ return color;
1071
+ }
1072
+ if (theme.colors[color]?.[500]) {
1073
+ return theme.colors[color][500];
1074
+ }
1075
+ if (color.includes("-")) {
1076
+ const [name, shade] = color.split("-");
1077
+ if (name === "danger") {
1078
+ return theme.colors.error?.[shade] ?? theme.colors.error?.[500] ?? "#ef4444";
1079
+ }
1080
+ return resolvePaletteColor(name, shade, theme);
1081
+ }
1082
+ return void 0;
1083
+ }
1084
+ function resolveSurfaceColor(surface, theme, isDark) {
1085
+ switch (surface) {
1086
+ case "background":
1087
+ return theme.colors.background?.[500] ?? (isDark ? "#0a0a0a" : "#ffffff");
1088
+ case "card":
1089
+ return theme.colors.card?.[500] ?? (isDark ? "#1f2937" : "#ffffff");
1090
+ case "muted":
1091
+ return isDark ? "#111827" : "#f3f4f6";
1092
+ default:
1093
+ return void 0;
1094
+ }
1095
+ }
1096
+ function resolveTextTone(tone, theme, isDark) {
1097
+ switch (tone) {
1098
+ case "default":
1099
+ return theme.colors.text?.[500] ?? (isDark ? "#ffffff" : "#1f2937");
1100
+ case "muted":
1101
+ return isDark ? "#9ca3af" : "#6b7280";
1102
+ case "inverse":
1103
+ return "#ffffff";
1104
+ case "primary":
1105
+ case "secondary":
1106
+ case "success":
1107
+ case "warning":
1108
+ case "error":
1109
+ return resolveNamedColor(tone, theme, isDark);
1110
+ default:
1111
+ return void 0;
1112
+ }
1113
+ }
1114
+
1115
+ // src/ui/primitives/AppView.tsx
1116
+ var import_jsx_runtime2 = require("nativewind/jsx-runtime");
1117
+ function AppView({
1118
+ flex,
1119
+ row,
1120
+ center,
1121
+ between,
1122
+ items,
1123
+ justify,
1124
+ p,
1125
+ px,
1126
+ py,
1127
+ gap,
1128
+ bg,
1129
+ surface,
1130
+ rounded,
1131
+ className,
1132
+ children,
1133
+ style,
1134
+ ...props
1135
+ }) {
1136
+ const { theme, isDark } = useOptionalTheme();
1137
+ const resolvedBgColor = resolveSurfaceColor(surface, theme, isDark) ?? resolveNamedColor(bg, theme, isDark);
1138
+ const shouldUseClassBg = !!bg && !resolvedBgColor;
1139
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1140
+ import_react_native.View,
1141
+ {
1142
+ className: cn(
1143
+ flex === true && "flex-1",
1144
+ typeof flex === "number" && `flex-${flex}`,
1145
+ row ? "flex-row" : "flex-col",
1146
+ center && "items-center justify-center",
1147
+ between && "justify-between",
1148
+ items && `items-${items}`,
1149
+ justify && `justify-${justify}`,
1150
+ p !== void 0 && `p-${p}`,
1151
+ px !== void 0 && `px-${px}`,
1152
+ py !== void 0 && `py-${py}`,
1153
+ gap !== void 0 && `gap-${gap}`,
1154
+ shouldUseClassBg && `bg-${bg}`,
1155
+ rounded && `rounded-${rounded}`,
1156
+ className
1157
+ ),
1158
+ style: [resolvedBgColor ? { backgroundColor: resolvedBgColor } : void 0, style],
1159
+ ...props,
1160
+ children
1161
+ }
1162
+ );
1163
+ }
1164
+
1165
+ // src/ui/primitives/AppScrollView.tsx
1166
+ var import_react_native2 = require("react-native");
1167
+ var import_jsx_runtime3 = require("nativewind/jsx-runtime");
1168
+ function AppScrollView({
1169
+ flex,
1170
+ bg,
1171
+ p,
1172
+ px,
1173
+ py,
1174
+ gap,
1175
+ surface,
1176
+ rounded,
1177
+ className,
1178
+ children,
1179
+ style,
1180
+ ...props
1181
+ }) {
1182
+ const { theme, isDark } = useOptionalTheme();
1183
+ const resolvedBgColor = resolveSurfaceColor(surface, theme, isDark) ?? resolveNamedColor(bg, theme, isDark);
1184
+ const shouldUseClassBg = !!bg && !resolvedBgColor;
1185
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1186
+ import_react_native2.ScrollView,
1187
+ {
1188
+ className: cn(
1189
+ flex && "flex-1",
1190
+ shouldUseClassBg && `bg-${bg}`,
1191
+ p !== void 0 && `p-${p}`,
1192
+ px !== void 0 && `px-${px}`,
1193
+ py !== void 0 && `py-${py}`,
1194
+ gap !== void 0 && `gap-${gap}`,
1195
+ rounded && `rounded-${rounded}`,
1196
+ className
1197
+ ),
1198
+ style: [resolvedBgColor ? { backgroundColor: resolvedBgColor } : void 0, style],
1199
+ ...props,
1200
+ children
1201
+ }
1202
+ );
1203
+ }
1204
+
1205
+ // src/ui/primitives/AppText.tsx
1206
+ var import_react_native3 = require("react-native");
1207
+ var import_jsx_runtime4 = require("nativewind/jsx-runtime");
1208
+ function AppText({
1209
+ size = "md",
1210
+ weight = "normal",
1211
+ color,
1212
+ tone,
1213
+ className,
1214
+ children,
1215
+ style,
1216
+ ...props
1217
+ }) {
1218
+ const { theme, isDark } = useOptionalTheme();
1219
+ const sizeMap3 = {
1220
+ xs: "text-xs",
1221
+ sm: "text-sm",
1222
+ md: "text-base",
1223
+ lg: "text-lg",
1224
+ xl: "text-xl",
1225
+ "2xl": "text-2xl",
1226
+ "3xl": "text-3xl"
1227
+ };
1228
+ const weightMap = {
1229
+ normal: "font-normal",
1230
+ medium: "font-medium",
1231
+ semibold: "font-semibold",
1232
+ bold: "font-bold"
1233
+ };
1234
+ const resolvedColor = resolveTextTone(tone, theme, isDark) ?? resolveNamedColor(color, theme, isDark);
1235
+ const shouldUseClassColor = !!color && !resolvedColor;
1236
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1237
+ import_react_native3.Text,
1238
+ {
1239
+ className: cn(sizeMap3[size], weightMap[weight], shouldUseClassColor && `text-${color}`, className),
1240
+ style: [resolvedColor ? { color: resolvedColor } : void 0, style],
1241
+ ...props,
1242
+ children
1243
+ }
1244
+ );
1245
+ }
1246
+
1247
+ // src/ui/primitives/AppPressable.tsx
1248
+ var React2 = __toESM(require("react"));
1249
+ var import_react_native4 = require("react-native");
1250
+ var import_jsx_runtime5 = require("nativewind/jsx-runtime");
1251
+ function AppPressable({
1252
+ className,
1253
+ pressedClassName,
1254
+ children,
1255
+ ...props
1256
+ }) {
1257
+ const [isPressed, setIsPressed] = React2.useState(false);
1258
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1259
+ import_react_native4.Pressable,
1260
+ {
1261
+ className: cn(className, isPressed && pressedClassName),
1262
+ onPressIn: (e) => {
1263
+ setIsPressed(true);
1264
+ props.onPressIn?.(e);
1265
+ },
1266
+ onPressOut: (e) => {
1267
+ setIsPressed(false);
1268
+ props.onPressOut?.(e);
1269
+ },
1270
+ ...props,
1271
+ children
1272
+ }
1273
+ );
1274
+ }
1275
+
1276
+ // src/ui/layout/Row.tsx
1277
+ var import_jsx_runtime6 = require("nativewind/jsx-runtime");
1278
+ var justifyMap = {
1279
+ start: "justify-start",
1280
+ center: "justify-center",
1281
+ end: "justify-end",
1282
+ between: "justify-between",
1283
+ around: "justify-around"
1284
+ };
1285
+ var itemsMap = {
1286
+ start: "items-start",
1287
+ center: "items-center",
1288
+ end: "items-end",
1289
+ stretch: "items-stretch"
1290
+ };
1291
+ function Row({ justify = "start", items = "center", className, ...props }) {
1292
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AppView, { row: true, className: cn(justifyMap[justify], itemsMap[items], className), ...props });
1293
+ }
1294
+
1295
+ // src/ui/layout/Col.tsx
1296
+ var import_jsx_runtime7 = require("nativewind/jsx-runtime");
1297
+ var justifyMap2 = {
1298
+ start: "justify-start",
1299
+ center: "justify-center",
1300
+ end: "justify-end",
1301
+ between: "justify-between",
1302
+ around: "justify-around"
1303
+ };
1304
+ var itemsMap2 = {
1305
+ start: "items-start",
1306
+ center: "items-center",
1307
+ end: "items-end",
1308
+ stretch: "items-stretch"
1309
+ };
1310
+ function Col({ justify = "start", items = "stretch", className, ...props }) {
1311
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AppView, { className: cn(justifyMap2[justify], itemsMap2[items], className), ...props });
1312
+ }
1313
+
1314
+ // src/ui/layout/Center.tsx
1315
+ var import_jsx_runtime8 = require("nativewind/jsx-runtime");
1316
+ function Center({ flex = true, ...props }) {
1317
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AppView, { center: true, flex, ...props });
1318
+ }
1319
+
1320
+ // src/ui/layout/SafeScreen.tsx
1321
+ var import_react_native5 = require("react-native");
1322
+ var import_react_native_safe_area_context = require("react-native-safe-area-context");
1323
+ var import_jsx_runtime9 = require("nativewind/jsx-runtime");
1324
+ function SafeScreen({
1325
+ top = true,
1326
+ bottom = true,
1327
+ left = false,
1328
+ right = false,
1329
+ bg,
1330
+ flex = true,
1331
+ className,
1332
+ children,
1333
+ style,
1334
+ ...props
1335
+ }) {
1336
+ const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
1337
+ const { theme, isDark } = useOptionalTheme();
1338
+ const resolvedBgColor = resolveNamedColor(bg, theme, isDark);
1339
+ const shouldUseClassBg = !!bg && !resolvedBgColor;
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1341
+ import_react_native5.View,
1342
+ {
1343
+ className: cn(flex && "flex-1", shouldUseClassBg && `bg-${bg}`, className),
1344
+ style: [
1345
+ flex && styles.flex,
1346
+ resolvedBgColor ? { backgroundColor: resolvedBgColor } : void 0,
1347
+ {
1348
+ paddingTop: top ? insets.top : 0,
1349
+ paddingBottom: bottom ? insets.bottom : 0,
1350
+ paddingLeft: left ? insets.left : 0,
1351
+ paddingRight: right ? insets.right : 0
1352
+ },
1353
+ style
1354
+ ],
1355
+ ...props,
1356
+ children
1357
+ }
1358
+ );
1359
+ }
1360
+ var styles = import_react_native5.StyleSheet.create({
1361
+ flex: {
1362
+ flex: 1
1363
+ }
1364
+ });
1365
+ function Page({
1366
+ children,
1367
+ className,
1368
+ ...props
1369
+ }) {
1370
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SafeScreen, { flex: true, bg: "background", ...props, className, children });
1371
+ }
1372
+ function SafeBottom({
1373
+ children,
1374
+ className,
1375
+ ...props
1376
+ }) {
1377
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SafeScreen, { bottom: true, left: true, right: true, flex: false, ...props, className, children });
1378
+ }
1379
+
1380
+ // src/ui/actions/AppButton.tsx
1381
+ var import_react_native6 = require("react-native");
1382
+ var import_jsx_runtime10 = require("nativewind/jsx-runtime");
1383
+ function AppButton({
1384
+ variant = "solid",
1385
+ size = "md",
1386
+ color = "primary",
1387
+ loading,
1388
+ disabled,
1389
+ onPress,
1390
+ children,
1391
+ className
1392
+ }) {
1393
+ const { theme, isDark } = useOptionalTheme();
1394
+ const isDisabled = disabled || loading;
1395
+ const sizeClasses = { sm: "px-3 py-2", md: "px-4 py-3", lg: "px-6 py-4" };
1396
+ const buttonColors = {
1397
+ primary: theme.colors.primary?.[500] || "#f38b32",
1398
+ secondary: theme.colors.secondary?.[500] || "#3b82f6",
1399
+ danger: theme.colors.error?.[500] || "#ef4444"
1400
+ };
1401
+ const ghostTextColor = isDark ? "#ffffff" : theme.colors.text?.[500] || "#1f2937";
1402
+ const ghostBackgroundColor = isDark ? "rgba(255,255,255,0.04)" : "transparent";
1403
+ const loadingColor = variant === "solid" ? "white" : buttonColors[color];
1404
+ const textColor = variant === "solid" ? "#ffffff" : variant === "ghost" ? ghostTextColor : buttonColors[color];
1405
+ const buttonStyle = variant === "solid" ? { backgroundColor: buttonColors[color] } : variant === "outline" ? { borderWidth: 0.5, borderColor: buttonColors[color], backgroundColor: "transparent" } : { backgroundColor: ghostBackgroundColor };
1406
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1407
+ AppPressable,
1408
+ {
1409
+ onPress,
1410
+ disabled: isDisabled,
1411
+ className: cn(
1412
+ "flex-row items-center justify-center rounded-lg",
1413
+ sizeClasses[size],
1414
+ isDisabled && "opacity-50",
1415
+ className
1416
+ ),
1417
+ style: buttonStyle,
1418
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native6.ActivityIndicator, { size: "small", color: loadingColor }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AppText, { weight: "semibold", style: { color: textColor }, children })
1419
+ }
1420
+ );
1421
+ }
1422
+
1423
+ // src/ui/feedback/Toast.tsx
1424
+ var import_jsx_runtime11 = require("nativewind/jsx-runtime");
1425
+ var typeStyles = {
1426
+ success: "bg-green-500",
1427
+ error: "bg-red-500",
1428
+ warning: "bg-yellow-500",
1429
+ info: "bg-blue-500"
1430
+ };
1431
+ function Toast({ message, type = "info", visible = true }) {
1432
+ if (!visible) return null;
1433
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AppView, { className: cn("px-4 py-3 rounded-lg", typeStyles[type]), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AppText, { color: "white", children: message }) });
1434
+ }
1435
+
1436
+ // src/ui/feedback/Alert.tsx
1437
+ var import_react13 = require("react");
1438
+ var import_react_native7 = require("react-native");
1439
+ var import_jsx_runtime12 = require("nativewind/jsx-runtime");
1440
+ function Alert({ visible, title, message, buttons, onClose }) {
1441
+ const { theme, isDark } = useTheme();
1442
+ const modalBgColor = isDark ? "#1f2937" : "#ffffff";
1443
+ const textColor = isDark ? "#ffffff" : "#1f2937";
1444
+ const messageColor = isDark ? "#9ca3af" : "#6b7280";
1445
+ const borderColor = isDark ? "#374151" : "#e5e7eb";
1446
+ const cancelButtonBg = isDark ? "#374151" : "#f3f4f6";
1447
+ const cancelButtonText = isDark ? "#ffffff" : "#374151";
1448
+ const destructiveColor = theme.colors.error?.[500] || "#ef4444";
1449
+ const handleButtonPress = (0, import_react13.useCallback)(
1450
+ (button) => (e) => {
1451
+ e.stopPropagation();
1452
+ button.onPress?.();
1453
+ onClose?.();
1454
+ },
1455
+ [onClose]
1456
+ );
1457
+ const getButtonStyle = (button) => {
1458
+ if (button.style === "destructive") {
1459
+ return {
1460
+ backgroundColor: "transparent",
1461
+ borderWidth: 0.5,
1462
+ borderColor: destructiveColor
1463
+ };
1464
+ }
1465
+ if (button.style === "cancel") {
1466
+ return {
1467
+ backgroundColor: cancelButtonBg,
1468
+ borderWidth: 0.5,
1469
+ borderColor: isDark ? "#4b5563" : "#d1d5db"
1470
+ };
1471
+ }
1472
+ return {
1473
+ backgroundColor: theme.colors.primary?.[500] || "#f38b32",
1474
+ borderWidth: 0
1475
+ };
1476
+ };
1477
+ const getButtonTextColor = (button) => {
1478
+ if (button.style === "destructive") {
1479
+ return destructiveColor;
1480
+ }
1481
+ if (button.style === "cancel") {
1482
+ return cancelButtonText;
1483
+ }
1484
+ return "#ffffff";
1485
+ };
1486
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1487
+ import_react_native7.Modal,
1488
+ {
1489
+ visible,
1490
+ transparent: true,
1491
+ animationType: "fade",
1492
+ onRequestClose: onClose,
1493
+ statusBarTranslucent: true,
1494
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AppView, { className: "flex-1", style: { backgroundColor: "rgba(0,0,0,0.5)" }, center: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1495
+ AppView,
1496
+ {
1497
+ className: "rounded-xl mx-8 min-w-[280px]",
1498
+ style: { backgroundColor: modalBgColor },
1499
+ children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(AppView, { className: "px-6 py-5", children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1502
+ AppText,
1503
+ {
1504
+ size: "lg",
1505
+ weight: "semibold",
1506
+ className: "text-center mb-2",
1507
+ style: { color: textColor },
1508
+ children: title
1509
+ }
1510
+ ),
1511
+ message && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AppText, { size: "sm", style: { color: messageColor }, className: "text-center leading-5", children: message })
1512
+ ] }),
1513
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AppView, { className: "border-t", style: { borderTopColor: borderColor }, children: buttons.length === 1 ? (
1514
+ // 单个按钮
1515
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1516
+ import_react_native7.TouchableOpacity,
1517
+ {
1518
+ onPress: handleButtonPress(buttons[0]),
1519
+ className: "py-3 rounded-b-xl",
1520
+ style: [styles2.singleButton, getButtonStyle(buttons[0])],
1521
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1522
+ AppText,
1523
+ {
1524
+ weight: "medium",
1525
+ className: "text-center",
1526
+ style: { color: getButtonTextColor(buttons[0]) },
1527
+ children: buttons[0].text
1528
+ }
1529
+ )
1530
+ }
1531
+ )
1532
+ ) : buttons.length === 2 ? (
1533
+ // 两个按钮横向排列
1534
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AppView, { row: true, style: styles2.twoButtonContainer, children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1535
+ import_react_native7.TouchableOpacity,
1536
+ {
1537
+ onPress: handleButtonPress(button),
1538
+ className: cn(
1539
+ "py-3 flex-1",
1540
+ index === 0 && "rounded-bl-xl",
1541
+ index === 1 && "rounded-br-xl"
1542
+ ),
1543
+ style: [
1544
+ styles2.twoButton,
1545
+ index > 0 && { borderLeftColor: borderColor },
1546
+ getButtonStyle(button)
1547
+ ],
1548
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1549
+ AppText,
1550
+ {
1551
+ weight: "medium",
1552
+ className: "text-center",
1553
+ style: { color: getButtonTextColor(button) },
1554
+ children: button.text
1555
+ }
1556
+ )
1557
+ },
1558
+ index
1559
+ )) })
1560
+ ) : (
1561
+ // 多个按钮纵向排列
1562
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AppView, { className: "gap-2 pb-4 px-4", children: buttons.map((button, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1563
+ import_react_native7.TouchableOpacity,
1564
+ {
1565
+ onPress: handleButtonPress(button),
1566
+ className: "py-3 rounded-lg",
1567
+ style: getButtonStyle(button),
1568
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1569
+ AppText,
1570
+ {
1571
+ weight: "medium",
1572
+ className: "text-center",
1573
+ style: { color: getButtonTextColor(button) },
1574
+ children: button.text
1575
+ }
1576
+ )
1577
+ },
1578
+ index
1579
+ )) })
1580
+ ) })
1581
+ ]
1582
+ }
1583
+ ) })
1584
+ }
1585
+ );
1586
+ }
1587
+ var styles2 = import_react_native7.StyleSheet.create({
1588
+ singleButton: {
1589
+ borderBottomLeftRadius: 12,
1590
+ borderBottomRightRadius: 12
1591
+ },
1592
+ twoButtonContainer: {
1593
+ borderBottomLeftRadius: 12,
1594
+ borderBottomRightRadius: 12
1595
+ },
1596
+ twoButton: {
1597
+ borderLeftWidth: 0.5
1598
+ }
1599
+ });
1600
+
1601
+ // src/ui/feedback/Loading.tsx
1602
+ var import_react_native8 = require("react-native");
1603
+ var import_jsx_runtime13 = require("nativewind/jsx-runtime");
1604
+ function Loading({
1605
+ text,
1606
+ color,
1607
+ overlay = false,
1608
+ visible = true,
1609
+ testID
1610
+ }) {
1611
+ if (!visible) return null;
1612
+ const content = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(AppView, { center: true, gap: 3, testID, children: [
1613
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native8.ActivityIndicator, { size: "large", color }),
1614
+ text && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AppText, { style: color ? { color } : void 0, children: text })
1615
+ ] });
1616
+ if (overlay) {
1617
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AppView, { center: true, flex: true, className: "absolute inset-0 bg-black/30", testID, children: content });
1618
+ }
1619
+ return content;
1620
+ }
1621
+
1622
+ // src/ui/display/Progress.tsx
1623
+ var import_jsx_runtime14 = require("nativewind/jsx-runtime");
1624
+ var sizeMap = { xs: "h-1", sm: "h-1.5", md: "h-2", lg: "h-3", xl: "h-4" };
1625
+ var colorMap = {
1626
+ primary: "bg-primary-500",
1627
+ secondary: "bg-secondary-500",
1628
+ success: "bg-success-500",
1629
+ warning: "bg-warning-500",
1630
+ error: "bg-error-500"
1631
+ };
1632
+ function Progress({
1633
+ value,
1634
+ max = 100,
1635
+ size = "md",
1636
+ color = "primary",
1637
+ testID,
1638
+ className,
1639
+ barClassName
1640
+ }) {
1641
+ const { theme, isDark } = useTheme();
1642
+ const percentage = Math.min(Math.max(value / max * 100, 0), 100);
1643
+ const trackBgColor = isDark ? theme.colors.border?.[700] || "#374151" : "#e5e7eb";
1644
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1645
+ AppView,
1646
+ {
1647
+ className: cn("w-full rounded-full", sizeMap[size], className),
1648
+ style: { backgroundColor: trackBgColor },
1649
+ testID,
1650
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1651
+ AppView,
1652
+ {
1653
+ className: cn("rounded-full", sizeMap[size], colorMap[color], barClassName),
1654
+ style: { width: `${percentage}%` }
1655
+ }
1656
+ )
1657
+ }
1658
+ );
1659
+ }
1660
+
1661
+ // src/ui/display/Card.tsx
1662
+ var import_react_native9 = require("react-native");
1663
+ var import_jsx_runtime15 = require("nativewind/jsx-runtime");
1664
+ function Card({
1665
+ children,
1666
+ className,
1667
+ style,
1668
+ noShadow = false,
1669
+ noBorder = false,
1670
+ noRadius = false,
1671
+ ...props
1672
+ }) {
1673
+ const colors = useThemeColors();
1674
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1675
+ import_react_native9.View,
1676
+ {
1677
+ className: cn(
1678
+ !noRadius && "rounded-lg",
1679
+ !noShadow && "shadow-sm",
1680
+ "overflow-hidden",
1681
+ className
1682
+ ),
1683
+ style: [
1684
+ {
1685
+ backgroundColor: colors.card,
1686
+ ...noBorder ? {} : { borderWidth: 0.5, borderColor: colors.divider }
1687
+ },
1688
+ style
1689
+ ],
1690
+ ...props,
1691
+ children
1692
+ }
1693
+ );
1694
+ }
1695
+
1696
+ // src/ui/display/Icon.tsx
1697
+ var import_MaterialIcons = __toESM(require("react-native-vector-icons/MaterialIcons"));
1698
+ var import_jsx_runtime16 = require("nativewind/jsx-runtime");
1699
+ var sizeMap2 = {
1700
+ xs: 16,
1701
+ sm: 20,
1702
+ md: 24,
1703
+ lg: 32,
1704
+ xl: 48
1705
+ };
1706
+ function resolveSize(size = "md") {
1707
+ if (typeof size === "number") return size;
1708
+ return sizeMap2[size] || 24;
1709
+ }
1710
+ function Icon({ name, size = "md", color = "gray-600", style, onPress, testID }) {
1711
+ const { theme, isDark } = useOptionalTheme();
1712
+ const resolvedSize = resolveSize(size);
1713
+ const resolvedColor = resolveNamedColor(color, theme, isDark) ?? color;
1714
+ if (onPress) {
1715
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AppPressable, { onPress, testID, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_MaterialIcons.default, { name, size: resolvedSize, color: resolvedColor, style }) });
1716
+ }
1717
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1718
+ import_MaterialIcons.default,
1719
+ {
1720
+ name,
1721
+ size: resolvedSize,
1722
+ color: resolvedColor,
1723
+ style,
1724
+ testID
1725
+ }
1726
+ );
1727
+ }
1728
+ var NavigationIcons = {
1729
+ home: "home",
1730
+ explore: "explore",
1731
+ profile: "person",
1732
+ settings: "settings",
1733
+ back: "arrow-back",
1734
+ forward: "arrow-forward",
1735
+ close: "close",
1736
+ menu: "menu",
1737
+ more: "more-vert"
1738
+ };
1739
+ var ActionIcons = {
1740
+ add: "add",
1741
+ edit: "edit",
1742
+ delete: "delete",
1743
+ search: "search",
1744
+ share: "share",
1745
+ favorite: "favorite",
1746
+ favoriteBorder: "favorite-border",
1747
+ check: "check",
1748
+ checkCircle: "check-circle",
1749
+ close: "close",
1750
+ closeCircle: "cancel",
1751
+ copy: "content-copy",
1752
+ download: "download",
1753
+ upload: "upload"
1754
+ };
1755
+ var StatusIcons = {
1756
+ info: "info",
1757
+ success: "check-circle",
1758
+ warning: "warning",
1759
+ error: "error",
1760
+ help: "help",
1761
+ loading: "refresh"
1762
+ };
1763
+ var FileIcons = {
1764
+ file: "insert-drive-file",
1765
+ image: "image",
1766
+ video: "videocam",
1767
+ audio: "audiotrack",
1768
+ folder: "folder",
1769
+ folderOpen: "folder-open"
1770
+ };
1771
+
1772
+ // src/ui/display/AppImage.tsx
1773
+ var import_react14 = require("react");
1774
+ var import_react_native10 = require("react-native");
1775
+ var import_jsx_runtime17 = require("nativewind/jsx-runtime");
1776
+ var radiusMap = {
1777
+ none: 0,
1778
+ sm: 2,
1779
+ md: 6,
1780
+ lg: 8,
1781
+ xl: 12,
1782
+ "2xl": 16,
1783
+ full: 9999
1784
+ };
1785
+ function resolveRadius(radius) {
1786
+ if (typeof radius === "number") return radius;
1787
+ return radiusMap[radius || "none"];
1788
+ }
1789
+ function SkeletonItem() {
1790
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AppView, { flex: true, className: "bg-gray-200 animate-pulse" });
1791
+ }
1792
+ function AppImage({
1793
+ source,
1794
+ width = "100%",
1795
+ height = "auto",
1796
+ borderRadius = "none",
1797
+ placeholder,
1798
+ errorPlaceholder,
1799
+ loadingIndicator = false,
1800
+ showError = false,
1801
+ resizeMode = "cover",
1802
+ onLoad,
1803
+ onError,
1804
+ onPress,
1805
+ onLongPress,
1806
+ className,
1807
+ style
1808
+ }) {
1809
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(true);
1810
+ const [hasError, setHasError] = (0, import_react14.useState)(false);
1811
+ const { theme } = useTheme();
1812
+ const resolvedRadius = resolveRadius(borderRadius);
1813
+ const handleLoad = (0, import_react14.useCallback)(() => {
1814
+ setIsLoading(false);
1815
+ onLoad?.();
1816
+ }, [onLoad]);
1817
+ const handleError = (0, import_react14.useCallback)(
1818
+ (error) => {
1819
+ setIsLoading(false);
1820
+ setHasError(true);
1821
+ onError?.(error);
1822
+ },
1823
+ [onError]
1824
+ );
1825
+ const imageStyle = [
1826
+ {
1827
+ width: "100%",
1828
+ height: "100%",
1829
+ borderRadius: resolvedRadius
1830
+ },
1831
+ style
1832
+ ];
1833
+ const renderLoading = () => {
1834
+ if (!isLoading) return null;
1835
+ if (placeholder) {
1836
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1837
+ import_react_native10.Image,
1838
+ {
1839
+ source: placeholder,
1840
+ style: {
1841
+ position: "absolute",
1842
+ width: "100%",
1843
+ height: "100%",
1844
+ borderRadius: resolvedRadius
1845
+ },
1846
+ resizeMode
1847
+ }
1848
+ );
1849
+ }
1850
+ if (loadingIndicator) {
1851
+ if (typeof loadingIndicator === "boolean") {
1852
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1853
+ AppView,
1854
+ {
1855
+ center: true,
1856
+ className: "absolute inset-0 bg-gray-100",
1857
+ style: { borderRadius: resolvedRadius },
1858
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native10.ActivityIndicator, { color: theme.colors.primary?.[500] })
1859
+ }
1860
+ );
1861
+ }
1862
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AppView, { center: true, className: "absolute inset-0", style: { borderRadius: resolvedRadius }, children: loadingIndicator });
1863
+ }
1864
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SkeletonItem, {});
1865
+ };
1866
+ const renderError = () => {
1867
+ if (!hasError) return null;
1868
+ if (errorPlaceholder) {
1869
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1870
+ import_react_native10.Image,
1871
+ {
1872
+ source: errorPlaceholder,
1873
+ style: {
1874
+ position: "absolute",
1875
+ width: "100%",
1876
+ height: "100%",
1877
+ borderRadius: resolvedRadius
1878
+ },
1879
+ resizeMode
1880
+ }
1881
+ );
1882
+ }
1883
+ if (showError) {
1884
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1885
+ AppView,
1886
+ {
1887
+ center: true,
1888
+ className: "absolute inset-0 bg-gray-100",
1889
+ style: { borderRadius: resolvedRadius },
1890
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { name: "error", size: "lg", color: "error-500" })
1891
+ }
1892
+ );
1893
+ }
1894
+ return null;
1895
+ };
1896
+ const isNumberWidth = typeof width === "number";
1897
+ const isNumberHeight = typeof height === "number";
1898
+ const content = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1899
+ import_react_native10.View,
1900
+ {
1901
+ className: cn("overflow-hidden", className),
1902
+ style: {
1903
+ width: isNumberWidth ? width : "100%",
1904
+ height: isNumberHeight ? height : void 0,
1905
+ aspectRatio: typeof height === "string" && height.startsWith("aspect-") ? Number(height.replace("aspect-", "").split("/")[0]) / Number(height.replace("aspect-", "").split("/")[1] || 1) : void 0,
1906
+ borderRadius: resolvedRadius
1907
+ },
1908
+ children: [
1909
+ renderLoading(),
1910
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1911
+ import_react_native10.Image,
1912
+ {
1913
+ source,
1914
+ style: imageStyle,
1915
+ resizeMode,
1916
+ onLoad: handleLoad,
1917
+ onError: handleError
1918
+ }
1919
+ ),
1920
+ renderError()
1921
+ ]
1922
+ }
1923
+ );
1924
+ if (onPress || onLongPress) {
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AppPressable, { onPress, onLongPress, children: content });
1926
+ }
1927
+ return content;
1928
+ }
1929
+
1930
+ // src/ui/display/AppList.tsx
1931
+ var import_react15 = require("react");
1932
+ var import_react_native11 = require("react-native");
1933
+ var import_jsx_runtime18 = require("nativewind/jsx-runtime");
1934
+ function SkeletonItem2({ render }) {
1935
+ const colors = useThemeColors();
1936
+ if (render) {
1937
+ return render();
1938
+ }
1939
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppView, { p: 4, gap: 3, testID: "skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(AppView, { row: true, gap: 3, children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppView, { className: "w-16 h-16 rounded-lg", style: { backgroundColor: colors.divider } }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(AppView, { flex: true, gap: 2, children: [
1942
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppView, { className: "h-4 w-3/4 rounded", style: { backgroundColor: colors.divider } }),
1943
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppView, { className: "h-3 w-1/2 rounded", style: { backgroundColor: colors.divider } })
1944
+ ] })
1945
+ ] }) });
1946
+ }
1947
+ function EmptyState({
1948
+ title,
1949
+ description,
1950
+ icon
1951
+ }) {
1952
+ const colors = useThemeColors();
1953
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Center, { py: 20, children: [
1954
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { name: icon || "inbox", size: 64, color: colors.textMuted }),
1955
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppText, { size: "lg", weight: "medium", className: "mt-4", style: { color: colors.text }, children: title || "\u6682\u65E0\u6570\u636E" }),
1956
+ description && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppText, { size: "sm", className: "mt-2", style: { color: colors.textMuted }, children: description })
1957
+ ] });
1958
+ }
1959
+ function ErrorState({ error, onRetry }) {
1960
+ const colors = useThemeColors();
1961
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Center, { py: 20, children: [
1962
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { name: "error-outline", size: 64, color: "error-300" }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppText, { size: "lg", weight: "medium", color: "error-500", className: "mt-4", children: "\u52A0\u8F7D\u5931\u8D25" }),
1964
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppText, { size: "sm", style: { color: colors.textMuted }, className: "mt-2 text-center px-8", children: error.message || "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5" }),
1965
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1966
+ AppPressable,
1967
+ {
1968
+ onPress: onRetry,
1969
+ className: "mt-6 px-4 py-2 rounded-lg",
1970
+ style: [
1971
+ styles3.retryButton,
1972
+ { backgroundColor: colors.cardElevated, borderColor: colors.border }
1973
+ ],
1974
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppText, { style: { color: colors.textSecondary }, className: "text-center", children: "\u91CD\u65B0\u52A0\u8F7D" })
1975
+ }
1976
+ )
1977
+ ] });
1978
+ }
1979
+ function LoadMoreFooter({ loading }) {
1980
+ if (!loading) return null;
1981
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Center, { py: 4, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native11.ActivityIndicator, { size: "small" }) });
1982
+ }
1983
+ function Divider({ style }) {
1984
+ const colors = useThemeColors();
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1986
+ AppView,
1987
+ {
1988
+ className: "h-px",
1989
+ style: [{ marginVertical: 0 }, { backgroundColor: colors.divider }, style]
1990
+ }
1991
+ );
1992
+ }
1993
+ function AppList({
1994
+ data,
1995
+ renderItem,
1996
+ keyExtractor,
1997
+ loading = false,
1998
+ refreshing = false,
1999
+ onRefresh,
2000
+ hasMore = false,
2001
+ onEndReached,
2002
+ onEndReachedThreshold = 0.5,
2003
+ error,
2004
+ onRetry,
2005
+ emptyTitle,
2006
+ emptyDescription,
2007
+ emptyIcon,
2008
+ EmptyComponent,
2009
+ divider = false,
2010
+ dividerStyle,
2011
+ skeletonCount = 6,
2012
+ skeletonRender,
2013
+ ListHeaderComponent,
2014
+ ListFooterComponent,
2015
+ contentContainerStyle,
2016
+ style,
2017
+ numColumns,
2018
+ columnWrapperStyle,
2019
+ horizontal,
2020
+ showsVerticalScrollIndicator,
2021
+ showsHorizontalScrollIndicator
2022
+ }) {
2023
+ const { theme } = useTheme();
2024
+ const [isLoadingMore, setIsLoadingMore] = (0, import_react15.useState)(false);
2025
+ const handleEndReached = (0, import_react15.useCallback)(async () => {
2026
+ if (isLoadingMore || !hasMore || !onEndReached) return;
2027
+ setIsLoadingMore(true);
2028
+ try {
2029
+ await onEndReached();
2030
+ } finally {
2031
+ setIsLoadingMore(false);
2032
+ }
2033
+ }, [isLoadingMore, hasMore, onEndReached]);
2034
+ const defaultKeyExtractor = (0, import_react15.useCallback)(
2035
+ (item, index) => {
2036
+ if (keyExtractor) return keyExtractor(item, index);
2037
+ return `item-${index}`;
2038
+ },
2039
+ [keyExtractor]
2040
+ );
2041
+ const wrappedRenderItem = (0, import_react15.useCallback)(
2042
+ (info) => {
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
2044
+ divider && info.index > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Divider, { style: dividerStyle }),
2045
+ renderItem(info)
2046
+ ] });
2047
+ },
2048
+ [renderItem, divider, dividerStyle]
2049
+ );
2050
+ const skeletonData = (0, import_react15.useMemo)(
2051
+ () => new Array(skeletonCount).fill(null).map((_, i) => ({ _skeletonId: i })),
2052
+ [skeletonCount]
2053
+ );
2054
+ const skeletonRenderItem = (0, import_react15.useCallback)(
2055
+ () => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SkeletonItem2, { render: skeletonRender }),
2056
+ [skeletonRender]
2057
+ );
2058
+ if (loading && data.length === 0) {
2059
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2060
+ import_react_native11.FlatList,
2061
+ {
2062
+ data: skeletonData,
2063
+ renderItem: skeletonRenderItem,
2064
+ keyExtractor: (_, index) => `skeleton-${index}`,
2065
+ contentContainerStyle,
2066
+ style,
2067
+ showsVerticalScrollIndicator,
2068
+ showsHorizontalScrollIndicator
2069
+ }
2070
+ );
2071
+ }
2072
+ if (error && data.length === 0) {
2073
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Center, { style, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ErrorState, { error, onRetry }) });
2074
+ }
2075
+ const ListEmptyComponent = (0, import_react15.useMemo)(() => {
2076
+ if (EmptyComponent) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EmptyComponent, {});
2077
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EmptyState, { title: emptyTitle, description: emptyDescription, icon: emptyIcon });
2078
+ }, [EmptyComponent, emptyTitle, emptyDescription, emptyIcon]);
2079
+ const FooterComponent = (0, import_react15.useMemo)(() => {
2080
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LoadMoreFooter, { loading: isLoadingMore }),
2082
+ ListFooterComponent
2083
+ ] });
2084
+ }, [isLoadingMore, ListFooterComponent]);
2085
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2086
+ import_react_native11.FlatList,
2087
+ {
2088
+ data,
2089
+ renderItem: wrappedRenderItem,
2090
+ keyExtractor: defaultKeyExtractor,
2091
+ refreshControl: onRefresh ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2092
+ import_react_native11.RefreshControl,
2093
+ {
2094
+ refreshing,
2095
+ onRefresh,
2096
+ tintColor: theme.colors.primary?.[500],
2097
+ colors: [theme.colors.primary?.[500]]
2098
+ }
2099
+ ) : void 0,
2100
+ onEndReached: onEndReached ? handleEndReached : void 0,
2101
+ onEndReachedThreshold,
2102
+ ListEmptyComponent,
2103
+ ListHeaderComponent,
2104
+ ListFooterComponent: FooterComponent,
2105
+ contentContainerStyle,
2106
+ style,
2107
+ numColumns,
2108
+ columnWrapperStyle,
2109
+ horizontal,
2110
+ showsVerticalScrollIndicator,
2111
+ showsHorizontalScrollIndicator,
2112
+ removeClippedSubviews: true,
2113
+ maxToRenderPerBatch: 10,
2114
+ windowSize: 10,
2115
+ initialNumToRender: 10
2116
+ }
2117
+ );
2118
+ }
2119
+ var styles3 = import_react_native11.StyleSheet.create({
2120
+ retryButton: {
2121
+ borderWidth: 0.5
2122
+ }
2123
+ });
2124
+
2125
+ // src/ui/form/AppInput.tsx
2126
+ var import_react16 = require("react");
2127
+ var import_react_native12 = require("react-native");
2128
+ var import_jsx_runtime19 = require("nativewind/jsx-runtime");
2129
+ var AppInput = (0, import_react16.forwardRef)(
2130
+ ({ label, error, disabled = false, leftIcon, rightIcon, className, style, ...props }, ref) => {
2131
+ const colors = useThemeColors();
2132
+ const [isFocused, setIsFocused] = (0, import_react16.useState)(false);
2133
+ const errorColor = "#ef4444";
2134
+ const getBorderColor = () => {
2135
+ if (error) return errorColor;
2136
+ if (isFocused) return colors.primary;
2137
+ return colors.border;
2138
+ };
2139
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(AppView, { className: cn("flex-col gap-1", className), children: [
2140
+ label && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AppText, { size: "sm", weight: "medium", style: { color: colors.textSecondary }, children: label }),
2141
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2142
+ AppView,
2143
+ {
2144
+ row: true,
2145
+ items: "center",
2146
+ className: "rounded-lg px-3",
2147
+ style: [
2148
+ styles4.inputContainer,
2149
+ {
2150
+ backgroundColor: colors.card,
2151
+ borderColor: getBorderColor(),
2152
+ opacity: disabled ? 0.6 : 1
2153
+ }
2154
+ ],
2155
+ children: [
2156
+ leftIcon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native12.View, { style: styles4.icon, children: leftIcon }),
2157
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2158
+ import_react_native12.TextInput,
2159
+ {
2160
+ ref,
2161
+ className: "flex-1 py-3 text-base",
2162
+ style: [styles4.input, { color: colors.text }, style],
2163
+ placeholderTextColor: colors.textMuted,
2164
+ editable: !disabled,
2165
+ onFocus: (e) => {
2166
+ setIsFocused(true);
2167
+ props.onFocus?.(e);
2168
+ },
2169
+ onBlur: (e) => {
2170
+ setIsFocused(false);
2171
+ props.onBlur?.(e);
2172
+ },
2173
+ ...props
2174
+ }
2175
+ ),
2176
+ rightIcon && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native12.View, { style: styles4.icon, children: rightIcon })
2177
+ ]
2178
+ }
2179
+ ),
2180
+ error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AppText, { size: "xs", style: { color: errorColor }, children: error })
2181
+ ] });
2182
+ }
2183
+ );
2184
+ AppInput.displayName = "AppInput";
2185
+ var styles4 = import_react_native12.StyleSheet.create({
2186
+ inputContainer: {
2187
+ borderWidth: 0.5,
2188
+ minHeight: 48
2189
+ },
2190
+ input: {
2191
+ padding: 0,
2192
+ margin: 0
2193
+ },
2194
+ icon: {
2195
+ marginHorizontal: 4
2196
+ }
2197
+ });
2198
+
2199
+ // src/ui/form/Checkbox.tsx
2200
+ var import_react17 = require("react");
2201
+ var import_react_native13 = require("react-native");
2202
+ var import_jsx_runtime20 = require("nativewind/jsx-runtime");
2203
+ function Checkbox({
2204
+ checked,
2205
+ defaultChecked,
2206
+ onChange,
2207
+ disabled = false,
2208
+ children,
2209
+ className,
2210
+ testID
2211
+ }) {
2212
+ const colors = useThemeColors();
2213
+ const [internalChecked, setInternalChecked] = (0, import_react17.useState)(defaultChecked || false);
2214
+ const isChecked = checked !== void 0 ? checked : internalChecked;
2215
+ const toggle = () => {
2216
+ if (disabled) return;
2217
+ const newChecked = !isChecked;
2218
+ if (checked === void 0) {
2219
+ setInternalChecked(newChecked);
2220
+ }
2221
+ onChange?.(newChecked);
2222
+ };
2223
+ const disabledOpacity = 0.4;
2224
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2225
+ import_react_native13.TouchableOpacity,
2226
+ {
2227
+ onPress: toggle,
2228
+ disabled,
2229
+ className: cn("flex-row items-center gap-2", className),
2230
+ style: disabled ? { opacity: disabledOpacity } : void 0,
2231
+ testID,
2232
+ activeOpacity: 0.7,
2233
+ children: [
2234
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2235
+ AppView,
2236
+ {
2237
+ className: cn(
2238
+ "w-5 h-5 rounded items-center justify-center",
2239
+ isChecked ? "bg-primary-500" : "bg-white border"
2240
+ ),
2241
+ style: [
2242
+ styles5.checkbox,
2243
+ {
2244
+ backgroundColor: isChecked ? colors.primary : colors.cardElevated,
2245
+ borderColor: isChecked ? colors.primary : colors.border
2246
+ }
2247
+ ],
2248
+ children: isChecked && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AppView, { testID: `${testID}-icon`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { name: "check", size: "sm", color: "white" }) })
2249
+ }
2250
+ ),
2251
+ children && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AppText, { size: "sm", style: { color: colors.text }, children })
2252
+ ]
2253
+ }
2254
+ );
2255
+ }
2256
+ var styles5 = import_react_native13.StyleSheet.create({
2257
+ checkbox: {
2258
+ borderWidth: 0.5
2259
+ }
2260
+ });
2261
+
2262
+ // src/ui/form/CheckboxGroup.tsx
2263
+ var import_jsx_runtime21 = require("nativewind/jsx-runtime");
2264
+ function CheckboxGroup({
2265
+ value = [],
2266
+ onChange,
2267
+ options = [],
2268
+ direction = "column",
2269
+ disabled = false
2270
+ }) {
2271
+ const handleChange = (optionValue, checked) => {
2272
+ if (!onChange) return;
2273
+ if (checked) {
2274
+ onChange([...value, optionValue]);
2275
+ } else {
2276
+ onChange(value.filter((v) => v !== optionValue));
2277
+ }
2278
+ };
2279
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AppView, { row: direction === "row", flex: direction === "row", gap: 4, children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2280
+ Checkbox,
2281
+ {
2282
+ checked: value.includes(option.value),
2283
+ onChange: (checked) => handleChange(option.value, checked),
2284
+ disabled: disabled || option.disabled,
2285
+ children: option.label
2286
+ },
2287
+ option.value
2288
+ )) });
2289
+ }
2290
+
2291
+ // src/ui/form/Radio.tsx
2292
+ var import_react18 = require("react");
2293
+ var import_react_native14 = require("react-native");
2294
+ var import_jsx_runtime22 = require("nativewind/jsx-runtime");
2295
+ function Radio({
2296
+ checked,
2297
+ defaultChecked,
2298
+ onChange,
2299
+ disabled = false,
2300
+ children,
2301
+ className,
2302
+ testID
2303
+ }) {
2304
+ const colors = useThemeColors();
2305
+ const [internalChecked, setInternalChecked] = (0, import_react18.useState)(defaultChecked || false);
2306
+ const isChecked = checked !== void 0 ? checked : internalChecked;
2307
+ const toggle = () => {
2308
+ if (disabled) return;
2309
+ const newChecked = !isChecked;
2310
+ if (checked === void 0) {
2311
+ setInternalChecked(newChecked);
2312
+ }
2313
+ onChange?.(newChecked);
2314
+ };
2315
+ const disabledOpacity = 0.4;
2316
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2317
+ import_react_native14.TouchableOpacity,
2318
+ {
2319
+ onPress: toggle,
2320
+ disabled,
2321
+ className: cn("flex-row items-center gap-2", className),
2322
+ style: disabled ? { opacity: disabledOpacity } : void 0,
2323
+ testID,
2324
+ activeOpacity: 0.7,
2325
+ children: [
2326
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2327
+ AppView,
2328
+ {
2329
+ className: cn("w-5 h-5 rounded-full items-center justify-center", isChecked && "border-2"),
2330
+ style: [
2331
+ styles6.radio,
2332
+ {
2333
+ backgroundColor: colors.card,
2334
+ borderColor: isChecked ? colors.primary : colors.border,
2335
+ borderWidth: isChecked ? 0.5 : 0.5
2336
+ }
2337
+ ],
2338
+ children: isChecked && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2339
+ AppView,
2340
+ {
2341
+ className: "rounded-full",
2342
+ style: [styles6.inner, { backgroundColor: colors.primary }]
2343
+ }
2344
+ )
2345
+ }
2346
+ ),
2347
+ children && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AppText, { size: "sm", style: { color: colors.text }, children })
2348
+ ]
2349
+ }
2350
+ );
2351
+ }
2352
+ var styles6 = import_react_native14.StyleSheet.create({
2353
+ radio: {
2354
+ borderWidth: 0.5
2355
+ },
2356
+ inner: {
2357
+ width: 10,
2358
+ height: 10
2359
+ }
2360
+ });
2361
+
2362
+ // src/ui/form/RadioGroup.tsx
2363
+ var import_jsx_runtime23 = require("nativewind/jsx-runtime");
2364
+ function RadioGroup({
2365
+ value,
2366
+ onChange,
2367
+ options = [],
2368
+ direction = "column",
2369
+ disabled = false
2370
+ }) {
2371
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(AppView, { row: direction === "row", flex: direction === "row", gap: 4, children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2372
+ Radio,
2373
+ {
2374
+ checked: value === option.value,
2375
+ onChange: () => onChange?.(option.value),
2376
+ disabled: disabled || option.disabled,
2377
+ children: option.label
2378
+ },
2379
+ option.value
2380
+ )) });
2381
+ }
2382
+
2383
+ // src/ui/form/Switch.tsx
2384
+ var import_react19 = require("react");
2385
+ var import_react_native15 = require("react-native");
2386
+ var import_jsx_runtime24 = require("nativewind/jsx-runtime");
2387
+ function Switch({
2388
+ checked,
2389
+ defaultChecked,
2390
+ onChange,
2391
+ disabled = false,
2392
+ size = "md",
2393
+ className,
2394
+ testID,
2395
+ style
2396
+ }) {
2397
+ const colors = useThemeColors();
2398
+ const [internalChecked, setInternalChecked] = (0, import_react19.useState)(defaultChecked || false);
2399
+ const isChecked = checked !== void 0 ? checked : internalChecked;
2400
+ const toggle = () => {
2401
+ if (disabled) return;
2402
+ const newChecked = !isChecked;
2403
+ if (checked === void 0) {
2404
+ setInternalChecked(newChecked);
2405
+ }
2406
+ onChange?.(newChecked);
2407
+ };
2408
+ const uncheckedTrackColor = colors.divider;
2409
+ const checkedTrackColor = colors.primary;
2410
+ const disabledOpacity = 0.4;
2411
+ const sizes = {
2412
+ sm: { width: 36, height: 20, thumb: 16, padding: 2 },
2413
+ md: { width: 48, height: 26, thumb: 22, padding: 2 },
2414
+ lg: { width: 60, height: 32, thumb: 28, padding: 2 }
2415
+ };
2416
+ const config = sizes[size];
2417
+ const thumbPosition = isChecked ? config.width - config.thumb - config.padding : config.padding;
2418
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2419
+ import_react_native15.TouchableOpacity,
2420
+ {
2421
+ onPress: toggle,
2422
+ disabled,
2423
+ className: cn(className),
2424
+ testID,
2425
+ activeOpacity: disabled ? 1 : 0.8,
2426
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2427
+ AppView,
2428
+ {
2429
+ className: "rounded-full",
2430
+ style: [
2431
+ styles7.track,
2432
+ {
2433
+ width: config.width,
2434
+ height: config.height,
2435
+ backgroundColor: isChecked ? checkedTrackColor : uncheckedTrackColor,
2436
+ opacity: disabled ? disabledOpacity : 1
2437
+ },
2438
+ style
2439
+ ],
2440
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2441
+ AppView,
2442
+ {
2443
+ className: "rounded-full",
2444
+ style: [
2445
+ styles7.thumb,
2446
+ {
2447
+ width: config.thumb,
2448
+ height: config.thumb,
2449
+ backgroundColor: colors.textInverse,
2450
+ transform: [{ translateX: thumbPosition }],
2451
+ shadowColor: "#000000",
2452
+ shadowOffset: { width: 0, height: 1 },
2453
+ shadowOpacity: 0.25,
2454
+ shadowRadius: 1
2455
+ }
2456
+ ]
2457
+ }
2458
+ )
2459
+ }
2460
+ )
2461
+ }
2462
+ );
2463
+ }
2464
+ var styles7 = import_react_native15.StyleSheet.create({
2465
+ track: {
2466
+ justifyContent: "center",
2467
+ padding: 2
2468
+ },
2469
+ thumb: {
2470
+ elevation: 2,
2471
+ shadowColor: "#000000",
2472
+ shadowOffset: { width: 0, height: 1 },
2473
+ shadowOpacity: 0.2,
2474
+ shadowRadius: 1
2475
+ }
2476
+ });
2477
+
2478
+ // src/ui/form/Slider.tsx
2479
+ var import_react21 = require("react");
2480
+ var import_react_native16 = require("react-native");
2481
+
2482
+ // src/ui/form/useFormTheme.ts
2483
+ var import_react20 = require("react");
2484
+ function useFormThemeColors() {
2485
+ const { isDark } = useOptionalTheme();
2486
+ const colors = useThemeColors();
2487
+ return (0, import_react20.useMemo)(
2488
+ () => ({
2489
+ primary: colors.primary,
2490
+ primarySurface: colors.primarySurface,
2491
+ surface: colors.cardElevated,
2492
+ surfaceMuted: isDark ? colors.divider : "#f3f4f6",
2493
+ headerSurface: isDark ? "#111827" : "#f3f4f6",
2494
+ text: colors.text,
2495
+ textSecondary: colors.textSecondary,
2496
+ textMuted: colors.textMuted,
2497
+ textInverse: colors.textInverse,
2498
+ border: colors.border,
2499
+ divider: colors.divider,
2500
+ icon: colors.textMuted,
2501
+ overlay: "rgba(0,0,0,0.5)"
2502
+ }),
2503
+ [colors, isDark]
2504
+ );
2505
+ }
2506
+
2507
+ // src/ui/form/Slider.tsx
2508
+ var import_jsx_runtime25 = require("nativewind/jsx-runtime");
2509
+ function Slider({
2510
+ value,
2511
+ defaultValue = 0,
2512
+ min = 0,
2513
+ max = 100,
2514
+ step = 1,
2515
+ disabled = false,
2516
+ showTooltip = false,
2517
+ onChange,
2518
+ onChangeEnd,
2519
+ className
2520
+ }) {
2521
+ const colors = useFormThemeColors();
2522
+ const [internalValue, setInternalValue] = (0, import_react21.useState)(defaultValue);
2523
+ const [trackWidth, setTrackWidth] = (0, import_react21.useState)(0);
2524
+ const [isDragging, setIsDragging] = (0, import_react21.useState)(false);
2525
+ const currentValue = value !== void 0 ? value : internalValue;
2526
+ const disabledOpacity = 0.4;
2527
+ const progress = (currentValue - min) / (max - min) * 100;
2528
+ const getValueFromPosition = (0, import_react21.useCallback)(
2529
+ (position) => {
2530
+ const percentage = Math.max(0, Math.min(1, position / trackWidth));
2531
+ const rawValue = min + percentage * (max - min);
2532
+ const steppedValue = Math.round(rawValue / step) * step;
2533
+ return Math.min(max, Math.max(min, steppedValue));
2534
+ },
2535
+ [trackWidth, min, max, step]
2536
+ );
2537
+ const setValue = (0, import_react21.useCallback)(
2538
+ (newValue) => {
2539
+ const clampedValue = Math.min(max, Math.max(min, newValue));
2540
+ if (value === void 0) {
2541
+ setInternalValue(clampedValue);
2542
+ }
2543
+ onChange?.(clampedValue);
2544
+ },
2545
+ [value, min, max, onChange]
2546
+ );
2547
+ const panResponder = (0, import_react21.useRef)(
2548
+ import_react_native16.PanResponder.create({
2549
+ onStartShouldSetPanResponder: () => !disabled,
2550
+ onMoveShouldSetPanResponder: () => !disabled,
2551
+ onPanResponderGrant: () => {
2552
+ setIsDragging(true);
2553
+ },
2554
+ onPanResponderMove: (_, gestureState) => {
2555
+ const position = progress / 100 * trackWidth + gestureState.dx;
2556
+ const newValue = getValueFromPosition(position);
2557
+ setValue(newValue);
2558
+ },
2559
+ onPanResponderRelease: (_, gestureState) => {
2560
+ const position = progress / 100 * trackWidth + gestureState.dx;
2561
+ const newValue = getValueFromPosition(position);
2562
+ setValue(newValue);
2563
+ setIsDragging(false);
2564
+ onChangeEnd?.(newValue);
2565
+ }
2566
+ })
2567
+ ).current;
2568
+ const handleTrackPress = (0, import_react21.useCallback)(
2569
+ (event) => {
2570
+ if (disabled) return;
2571
+ const { locationX } = event.nativeEvent;
2572
+ const newValue = getValueFromPosition(locationX);
2573
+ setValue(newValue);
2574
+ onChangeEnd?.(newValue);
2575
+ },
2576
+ [disabled, getValueFromPosition, setValue, onChangeEnd]
2577
+ );
2578
+ const onLayout = (0, import_react21.useCallback)((event) => {
2579
+ setTrackWidth(event.nativeEvent.layout.width);
2580
+ }, []);
2581
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(AppView, { className: cn("py-2", className), children: [
2582
+ showTooltip && isDragging && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
2583
+ AppView,
2584
+ {
2585
+ className: "absolute rounded px-2 py-1 -top-8",
2586
+ style: [
2587
+ styles8.tooltip,
2588
+ {
2589
+ backgroundColor: colors.surfaceMuted,
2590
+ left: `${progress}%`,
2591
+ transform: [{ translateX: -16 }]
2592
+ }
2593
+ ],
2594
+ children: [
2595
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AppText, { size: "xs", style: { color: colors.text }, children: Math.round(currentValue) }),
2596
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2597
+ AppView,
2598
+ {
2599
+ style: [
2600
+ styles8.tooltipArrow,
2601
+ {
2602
+ borderTopColor: colors.surfaceMuted
2603
+ }
2604
+ ]
2605
+ }
2606
+ )
2607
+ ]
2608
+ }
2609
+ ),
2610
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
2611
+ import_react_native16.View,
2612
+ {
2613
+ onLayout,
2614
+ className: "rounded-full",
2615
+ style: [
2616
+ styles8.track,
2617
+ { backgroundColor: colors.divider, opacity: disabled ? disabledOpacity : 1 }
2618
+ ],
2619
+ onTouchEnd: handleTrackPress,
2620
+ children: [
2621
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2622
+ AppView,
2623
+ {
2624
+ className: "rounded-full",
2625
+ style: [
2626
+ styles8.filledTrack,
2627
+ {
2628
+ backgroundColor: colors.primary,
2629
+ width: `${progress}%`
2630
+ }
2631
+ ]
2632
+ }
2633
+ ),
2634
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2635
+ AppView,
2636
+ {
2637
+ className: "absolute rounded-full items-center justify-center",
2638
+ style: [
2639
+ styles8.thumb,
2640
+ {
2641
+ backgroundColor: colors.textInverse,
2642
+ left: `${progress}%`,
2643
+ transform: [{ translateX: -12 }],
2644
+ shadowColor: "#000000",
2645
+ shadowOffset: { width: 0, height: 2 },
2646
+ shadowOpacity: 0.25,
2647
+ shadowRadius: 2
2648
+ }
2649
+ ],
2650
+ ...panResponder.panHandlers,
2651
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
2652
+ AppView,
2653
+ {
2654
+ className: "rounded-full",
2655
+ style: [
2656
+ styles8.thumbDot,
2657
+ {
2658
+ backgroundColor: colors.primary
2659
+ }
2660
+ ]
2661
+ }
2662
+ )
2663
+ }
2664
+ )
2665
+ ]
2666
+ }
2667
+ )
2668
+ ] });
2669
+ }
2670
+ var styles8 = import_react_native16.StyleSheet.create({
2671
+ track: {
2672
+ height: 6,
2673
+ width: "100%"
2674
+ },
2675
+ filledTrack: {
2676
+ height: 6
2677
+ },
2678
+ thumb: {
2679
+ width: 24,
2680
+ height: 24,
2681
+ top: -9,
2682
+ elevation: 3,
2683
+ shadowColor: "#000000",
2684
+ shadowOffset: { width: 0, height: 2 },
2685
+ shadowOpacity: 0.2,
2686
+ shadowRadius: 2
2687
+ },
2688
+ thumbDot: {
2689
+ width: 8,
2690
+ height: 8
2691
+ },
2692
+ tooltip: {
2693
+ minWidth: 32,
2694
+ alignItems: "center",
2695
+ elevation: 4
2696
+ },
2697
+ tooltipArrow: {
2698
+ position: "absolute",
2699
+ bottom: -4,
2700
+ width: 0,
2701
+ height: 0,
2702
+ borderLeftWidth: 4,
2703
+ borderRightWidth: 4,
2704
+ borderTopWidth: 4,
2705
+ borderLeftColor: "transparent",
2706
+ borderRightColor: "transparent"
2707
+ }
2708
+ });
2709
+
2710
+ // src/ui/form/Select.tsx
2711
+ var import_react22 = require("react");
2712
+ var import_react_native17 = require("react-native");
2713
+ var import_jsx_runtime26 = require("nativewind/jsx-runtime");
2714
+ function Select({
2715
+ value,
2716
+ onChange,
2717
+ options,
2718
+ placeholder = "\u8BF7\u9009\u62E9",
2719
+ multiple = false,
2720
+ searchable = false,
2721
+ onSearch,
2722
+ disabled = false,
2723
+ clearable = true,
2724
+ className
2725
+ }) {
2726
+ const colors = useFormThemeColors();
2727
+ const [visible, setVisible] = (0, import_react22.useState)(false);
2728
+ const [searchKeyword, setSearchKeyword] = (0, import_react22.useState)("");
2729
+ const selectedValues = (0, import_react22.useMemo)(() => {
2730
+ if (multiple) {
2731
+ return Array.isArray(value) ? value : [];
2732
+ }
2733
+ return value ? [value] : [];
2734
+ }, [value, multiple]);
2735
+ const displayText = (0, import_react22.useMemo)(() => {
2736
+ if (selectedValues.length === 0) return placeholder;
2737
+ const selectedLabels = options.filter((opt) => selectedValues.includes(opt.value)).map((opt) => opt.label);
2738
+ return selectedLabels.join(", ") || placeholder;
2739
+ }, [selectedValues, options, placeholder]);
2740
+ const filteredOptions = (0, import_react22.useMemo)(() => {
2741
+ if (!searchable || !searchKeyword) return options;
2742
+ return options.filter((opt) => opt.label.toLowerCase().includes(searchKeyword.toLowerCase()));
2743
+ }, [options, searchable, searchKeyword]);
2744
+ const handleSelect = (0, import_react22.useCallback)(
2745
+ (optionValue) => {
2746
+ if (multiple) {
2747
+ const currentValues = Array.isArray(value) ? value : [];
2748
+ const newValues = currentValues.includes(optionValue) ? currentValues.filter((v) => v !== optionValue) : [...currentValues, optionValue];
2749
+ onChange?.(newValues);
2750
+ } else {
2751
+ onChange?.(optionValue);
2752
+ setVisible(false);
2753
+ }
2754
+ },
2755
+ [multiple, value, onChange]
2756
+ );
2757
+ const handleClear = (0, import_react22.useCallback)(
2758
+ (e) => {
2759
+ e.stopPropagation();
2760
+ onChange?.(multiple ? [] : "");
2761
+ },
2762
+ [multiple, onChange]
2763
+ );
2764
+ const handleSearch = (0, import_react22.useCallback)(
2765
+ (text) => {
2766
+ setSearchKeyword(text);
2767
+ onSearch?.(text);
2768
+ },
2769
+ [onSearch]
2770
+ );
2771
+ const renderOption = (0, import_react22.useCallback)(
2772
+ ({ item }) => {
2773
+ const isSelected = selectedValues.includes(item.value);
2774
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2775
+ AppPressable,
2776
+ {
2777
+ className: cn(
2778
+ "flex-row items-center justify-between px-4 py-3",
2779
+ isSelected && "bg-primary-50"
2780
+ ),
2781
+ style: [
2782
+ styles9.optionItem,
2783
+ { borderBottomColor: colors.divider },
2784
+ isSelected && { backgroundColor: colors.primarySurface }
2785
+ ],
2786
+ onPress: () => handleSelect(item.value),
2787
+ children: [
2788
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppText, { style: { color: isSelected ? colors.primary : colors.text }, children: item.label }),
2789
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "check", size: "sm", color: "primary-500" })
2790
+ ]
2791
+ }
2792
+ );
2793
+ },
2794
+ [selectedValues, handleSelect, colors]
2795
+ );
2796
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
2797
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2798
+ AppPressable,
2799
+ {
2800
+ className: cn(
2801
+ "flex-row items-center justify-between px-4 py-3 rounded-lg",
2802
+ disabled ? "opacity-60" : "",
2803
+ className
2804
+ ),
2805
+ style: [styles9.trigger, { backgroundColor: colors.surface, borderColor: colors.border }],
2806
+ disabled,
2807
+ onPress: () => setVisible(true),
2808
+ children: [
2809
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2810
+ AppText,
2811
+ {
2812
+ className: "flex-1",
2813
+ style: { color: selectedValues.length === 0 ? colors.textMuted : colors.text },
2814
+ numberOfLines: 1,
2815
+ children: displayText
2816
+ }
2817
+ ),
2818
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_native17.View, { className: "flex-row items-center", children: [
2819
+ clearable && selectedValues.length > 0 && !disabled && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native17.TouchableOpacity, { onPress: handleClear, className: "mr-2 p-1", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "close", size: "sm", color: colors.icon }) }),
2820
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "keyboard-arrow-down", size: "md", color: colors.icon })
2821
+ ] })
2822
+ ]
2823
+ }
2824
+ ),
2825
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2826
+ import_react_native17.Modal,
2827
+ {
2828
+ visible,
2829
+ transparent: true,
2830
+ animationType: "slide",
2831
+ onRequestClose: () => setVisible(false),
2832
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppView, { className: "flex-1", style: { backgroundColor: colors.overlay }, justify: "end", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2833
+ AppView,
2834
+ {
2835
+ className: "rounded-t-2xl max-h-[70%]",
2836
+ style: { backgroundColor: colors.surface },
2837
+ children: [
2838
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2839
+ AppView,
2840
+ {
2841
+ row: true,
2842
+ between: true,
2843
+ items: "center",
2844
+ className: "px-4 py-3",
2845
+ style: [styles9.header, { borderBottomColor: colors.divider }],
2846
+ children: [
2847
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppText, { className: "text-lg font-semibold", style: { color: colors.text }, children: multiple ? "\u9009\u62E9\u9009\u9879" : "\u8BF7\u9009\u62E9" }),
2848
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native17.TouchableOpacity, { onPress: () => setVisible(false), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "close", size: "md", color: colors.icon }) })
2849
+ ]
2850
+ }
2851
+ ),
2852
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2853
+ AppView,
2854
+ {
2855
+ className: "px-4 py-3",
2856
+ style: [styles9.searchBox, { borderBottomColor: colors.divider }],
2857
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2858
+ AppView,
2859
+ {
2860
+ row: true,
2861
+ items: "center",
2862
+ className: "px-3 py-2 rounded-lg",
2863
+ style: { backgroundColor: colors.surfaceMuted },
2864
+ children: [
2865
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native17.View, { style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "search", size: "sm", color: colors.icon }) }),
2866
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2867
+ import_react_native17.TextInput,
2868
+ {
2869
+ className: "flex-1 text-base",
2870
+ style: { color: colors.text },
2871
+ placeholder: "\u641C\u7D22...",
2872
+ placeholderTextColor: colors.textMuted,
2873
+ value: searchKeyword,
2874
+ onChangeText: handleSearch,
2875
+ autoFocus: true
2876
+ }
2877
+ ),
2878
+ searchKeyword.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native17.TouchableOpacity, { onPress: () => setSearchKeyword(""), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "close", size: "sm", color: colors.icon }) })
2879
+ ]
2880
+ }
2881
+ )
2882
+ }
2883
+ ),
2884
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2885
+ import_react_native17.FlatList,
2886
+ {
2887
+ data: filteredOptions,
2888
+ keyExtractor: (item) => item.value,
2889
+ renderItem: renderOption,
2890
+ ListEmptyComponent: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppView, { center: true, className: "py-8", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppText, { style: { color: colors.textMuted }, children: "\u6682\u65E0\u9009\u9879" }) })
2891
+ }
2892
+ ),
2893
+ multiple && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2894
+ AppView,
2895
+ {
2896
+ row: true,
2897
+ between: true,
2898
+ items: "center",
2899
+ className: "px-4 py-3",
2900
+ style: [styles9.footer, { borderTopColor: colors.divider }],
2901
+ children: [
2902
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(AppText, { style: { color: colors.textMuted }, children: [
2903
+ "\u5DF2\u9009\u62E9 ",
2904
+ selectedValues.length,
2905
+ " \u9879"
2906
+ ] }),
2907
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2908
+ import_react_native17.TouchableOpacity,
2909
+ {
2910
+ className: "px-4 py-2 rounded-lg",
2911
+ style: { backgroundColor: colors.primary },
2912
+ onPress: () => setVisible(false),
2913
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(AppText, { className: "font-medium", style: { color: colors.textInverse }, children: "\u786E\u5B9A" })
2914
+ }
2915
+ )
2916
+ ]
2917
+ }
2918
+ )
2919
+ ]
2920
+ }
2921
+ ) })
2922
+ }
2923
+ )
2924
+ ] });
2925
+ }
2926
+ var styles9 = import_react_native17.StyleSheet.create({
2927
+ trigger: {
2928
+ borderWidth: 0.5
2929
+ },
2930
+ header: {
2931
+ borderBottomWidth: 0.5
2932
+ },
2933
+ searchBox: {
2934
+ borderBottomWidth: 0.5
2935
+ },
2936
+ optionItem: {
2937
+ borderBottomWidth: 0.5
2938
+ },
2939
+ footer: {
2940
+ borderTopWidth: 0.5
2941
+ }
2942
+ });
2943
+
2944
+ // src/ui/form/DatePicker.tsx
2945
+ var import_react23 = require("react");
2946
+ var import_react_native18 = require("react-native");
2947
+ var import_jsx_runtime27 = require("nativewind/jsx-runtime");
2948
+ function PickerColumn({
2949
+ title,
2950
+ values,
2951
+ selectedValue,
2952
+ onSelect,
2953
+ isDisabled,
2954
+ formatLabel = (value) => String(value),
2955
+ showDivider = false,
2956
+ colors
2957
+ }) {
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
2959
+ AppView,
2960
+ {
2961
+ flex: true,
2962
+ style: [
2963
+ showDivider && styles10.column,
2964
+ showDivider ? { borderRightColor: colors.divider } : void 0
2965
+ ],
2966
+ children: [
2967
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppView, { center: true, className: "py-2", style: { backgroundColor: colors.headerSurface }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { className: "text-sm font-medium", style: { color: colors.textMuted }, children: title }) }),
2968
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppView, { className: "flex-1", children: values.map((value) => {
2969
+ const selected = selectedValue === value;
2970
+ const disabled = isDisabled(value);
2971
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2972
+ import_react_native18.TouchableOpacity,
2973
+ {
2974
+ className: cn("py-2 items-center", selected && "bg-primary-50"),
2975
+ style: selected ? { backgroundColor: colors.primarySurface } : void 0,
2976
+ disabled,
2977
+ onPress: () => onSelect(value),
2978
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2979
+ AppText,
2980
+ {
2981
+ className: cn(selected ? "font-semibold" : void 0, disabled && "opacity-30"),
2982
+ style: {
2983
+ color: selected ? colors.primary : colors.textSecondary
2984
+ },
2985
+ children: formatLabel(value)
2986
+ }
2987
+ )
2988
+ },
2989
+ value
2990
+ );
2991
+ }) })
2992
+ ]
2993
+ }
2994
+ );
2995
+ }
2996
+ function DatePicker({
2997
+ value,
2998
+ onChange,
2999
+ placeholder = "\u8BF7\u9009\u62E9\u65E5\u671F",
3000
+ disabled = false,
3001
+ format = "yyyy-MM-dd",
3002
+ minDate,
3003
+ maxDate,
3004
+ className
3005
+ }) {
3006
+ const colors = useFormThemeColors();
3007
+ const [visible, setVisible] = (0, import_react23.useState)(false);
3008
+ const [tempDate, setTempDate] = (0, import_react23.useState)(value || /* @__PURE__ */ new Date());
3009
+ const displayText = (0, import_react23.useMemo)(() => {
3010
+ return value ? formatDate(value, format) : placeholder;
3011
+ }, [value, format, placeholder]);
3012
+ const handleConfirm = (0, import_react23.useCallback)(() => {
3013
+ onChange?.(tempDate);
3014
+ setVisible(false);
3015
+ }, [tempDate, onChange]);
3016
+ const years = (0, import_react23.useMemo)(() => {
3017
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
3018
+ const arr = [];
3019
+ for (let i = currentYear - 50; i <= currentYear + 50; i++) {
3020
+ arr.push(i);
3021
+ }
3022
+ return arr;
3023
+ }, []);
3024
+ const months = (0, import_react23.useMemo)(() => {
3025
+ return Array.from({ length: 12 }, (_, i) => i + 1);
3026
+ }, []);
3027
+ const days = (0, import_react23.useMemo)(() => {
3028
+ const year = tempDate.getFullYear();
3029
+ const month = tempDate.getMonth();
3030
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
3031
+ return Array.from({ length: daysInMonth }, (_, i) => i + 1);
3032
+ }, [tempDate]);
3033
+ const isDateDisabled = (0, import_react23.useCallback)(
3034
+ (year, month, day) => {
3035
+ const date = new Date(year, month - 1, day);
3036
+ if (minDate && date < minDate) return true;
3037
+ if (maxDate && date > maxDate) return true;
3038
+ return false;
3039
+ },
3040
+ [minDate, maxDate]
3041
+ );
3042
+ const updateTempDate = (0, import_react23.useCallback)(
3043
+ (year, month, day) => {
3044
+ const newDate = new Date(tempDate);
3045
+ if (year !== void 0) newDate.setFullYear(year);
3046
+ if (month !== void 0) newDate.setMonth(month - 1);
3047
+ if (day !== void 0) newDate.setDate(day);
3048
+ setTempDate(newDate);
3049
+ },
3050
+ [tempDate]
3051
+ );
3052
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3053
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3054
+ AppPressable,
3055
+ {
3056
+ className: cn(
3057
+ "flex-row items-center justify-between px-4 py-3 rounded-lg",
3058
+ disabled ? "opacity-60" : "",
3059
+ className
3060
+ ),
3061
+ style: [styles10.trigger, { backgroundColor: colors.surface, borderColor: colors.border }],
3062
+ disabled,
3063
+ onPress: () => {
3064
+ setTempDate(value || /* @__PURE__ */ new Date());
3065
+ setVisible(true);
3066
+ },
3067
+ children: [
3068
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3069
+ AppText,
3070
+ {
3071
+ className: "flex-1",
3072
+ style: { color: value ? colors.text : colors.textMuted },
3073
+ numberOfLines: 1,
3074
+ children: displayText
3075
+ }
3076
+ ),
3077
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { name: "calendar-today", size: "md", color: colors.icon })
3078
+ ]
3079
+ }
3080
+ ),
3081
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3082
+ import_react_native18.Modal,
3083
+ {
3084
+ visible,
3085
+ transparent: true,
3086
+ animationType: "slide",
3087
+ onRequestClose: () => setVisible(false),
3088
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppView, { className: "flex-1", style: { backgroundColor: colors.overlay }, justify: "end", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(AppView, { className: "rounded-t-2xl", style: { backgroundColor: colors.surface }, children: [
3089
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3090
+ AppView,
3091
+ {
3092
+ row: true,
3093
+ between: true,
3094
+ items: "center",
3095
+ className: "px-4 py-3",
3096
+ style: [styles10.header, { borderBottomColor: colors.divider }],
3097
+ children: [
3098
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native18.TouchableOpacity, { onPress: () => setVisible(false), children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { style: { color: colors.textMuted }, children: "\u53D6\u6D88" }) }),
3099
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { className: "text-lg font-semibold", style: { color: colors.text }, children: "\u9009\u62E9\u65E5\u671F" }),
3100
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native18.TouchableOpacity, { onPress: handleConfirm, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { style: { color: colors.primary }, className: "font-medium", children: "\u786E\u5B9A" }) })
3101
+ ]
3102
+ }
3103
+ ),
3104
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppView, { center: true, className: "py-4", style: { backgroundColor: colors.headerSurface }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { className: "text-2xl font-semibold", style: { color: colors.text }, children: formatDate(tempDate, "yyyy\u5E74MM\u6708dd\u65E5") }) }),
3105
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(AppView, { row: true, className: "h-48", children: [
3106
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3107
+ PickerColumn,
3108
+ {
3109
+ title: "\u5E74",
3110
+ values: years,
3111
+ selectedValue: tempDate.getFullYear(),
3112
+ onSelect: (year) => updateTempDate(year),
3113
+ isDisabled: (year) => isDateDisabled(year, tempDate.getMonth() + 1, tempDate.getDate()),
3114
+ colors,
3115
+ showDivider: true
3116
+ }
3117
+ ),
3118
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3119
+ PickerColumn,
3120
+ {
3121
+ title: "\u6708",
3122
+ values: months,
3123
+ selectedValue: tempDate.getMonth() + 1,
3124
+ onSelect: (month) => updateTempDate(void 0, month),
3125
+ isDisabled: (month) => isDateDisabled(tempDate.getFullYear(), month, tempDate.getDate()),
3126
+ formatLabel: (month) => `${month}\u6708`,
3127
+ colors,
3128
+ showDivider: true
3129
+ }
3130
+ ),
3131
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3132
+ PickerColumn,
3133
+ {
3134
+ title: "\u65E5",
3135
+ values: days,
3136
+ selectedValue: tempDate.getDate(),
3137
+ onSelect: (day) => updateTempDate(void 0, void 0, day),
3138
+ isDisabled: (day) => isDateDisabled(tempDate.getFullYear(), tempDate.getMonth() + 1, day),
3139
+ colors
3140
+ }
3141
+ )
3142
+ ] }),
3143
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3144
+ AppView,
3145
+ {
3146
+ row: true,
3147
+ className: "px-4 py-3 gap-2",
3148
+ style: [styles10.footer, { borderTopColor: colors.divider }],
3149
+ children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3151
+ import_react_native18.TouchableOpacity,
3152
+ {
3153
+ className: "flex-1 py-2 items-center rounded-lg",
3154
+ style: { backgroundColor: colors.surfaceMuted },
3155
+ onPress: () => setTempDate(/* @__PURE__ */ new Date()),
3156
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { style: { color: colors.text }, children: "\u4ECA\u5929" })
3157
+ }
3158
+ ),
3159
+ minDate && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3160
+ import_react_native18.TouchableOpacity,
3161
+ {
3162
+ className: "flex-1 py-2 items-center rounded-lg",
3163
+ style: { backgroundColor: colors.surfaceMuted },
3164
+ onPress: () => setTempDate(minDate),
3165
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { style: { color: colors.text }, children: "\u6700\u65E9" })
3166
+ }
3167
+ ),
3168
+ maxDate && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3169
+ import_react_native18.TouchableOpacity,
3170
+ {
3171
+ className: "flex-1 py-2 items-center rounded-lg",
3172
+ style: { backgroundColor: colors.surfaceMuted },
3173
+ onPress: () => setTempDate(maxDate),
3174
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(AppText, { style: { color: colors.text }, children: "\u6700\u665A" })
3175
+ }
3176
+ )
3177
+ ]
3178
+ }
3179
+ )
3180
+ ] }) })
3181
+ }
3182
+ )
3183
+ ] });
3184
+ }
3185
+ var styles10 = import_react_native18.StyleSheet.create({
3186
+ trigger: {
3187
+ borderWidth: 0.5
3188
+ },
3189
+ header: {
3190
+ borderBottomWidth: 0.5
3191
+ },
3192
+ column: {
3193
+ borderRightWidth: 0.5
3194
+ },
3195
+ footer: {
3196
+ borderTopWidth: 0.5
3197
+ }
3198
+ });
3199
+
3200
+ // src/ui/form/FormItem.tsx
3201
+ var import_jsx_runtime28 = require("nativewind/jsx-runtime");
3202
+ function FormItem({
3203
+ name: _name,
3204
+ label,
3205
+ error,
3206
+ help,
3207
+ required,
3208
+ children,
3209
+ className,
3210
+ labelClassName
3211
+ }) {
3212
+ const colors = useThemeColors();
3213
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(AppView, { className: cn("mb-4", className), children: [
3214
+ label && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(AppView, { row: true, items: "center", gap: 1, className: cn("mb-2", labelClassName), children: [
3215
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AppText, { size: "sm", weight: "medium", style: { color: colors.textSecondary }, children: label }),
3216
+ required && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AppText, { color: "error-500", children: "*" })
3217
+ ] }),
3218
+ children,
3219
+ error && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AppText, { size: "sm", color: "error-500", className: "mt-1", children: error }),
3220
+ help && !error && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AppText, { size: "sm", className: "mt-1", style: { color: colors.textMuted }, children: help })
3221
+ ] });
3222
+ }
3223
+
3224
+ // src/ui/form/useForm.ts
3225
+ var import_react24 = require("react");
3226
+ function useForm({
3227
+ schema,
3228
+ defaultValues
3229
+ }) {
3230
+ const [values, setValues] = (0, import_react24.useState)(defaultValues);
3231
+ const [errors, setErrors] = (0, import_react24.useState)({});
3232
+ const [isSubmitting, setIsSubmitting] = (0, import_react24.useState)(false);
3233
+ const isDirty = (0, import_react24.useMemo)(() => {
3234
+ return JSON.stringify(values) !== JSON.stringify(defaultValues);
3235
+ }, [values, defaultValues]);
3236
+ const isValid = (0, import_react24.useMemo)(() => {
3237
+ return Object.keys(errors).length === 0;
3238
+ }, [errors]);
3239
+ const setValue = (0, import_react24.useCallback)((name, value) => {
3240
+ setValues((prev) => ({ ...prev, [name]: value }));
3241
+ setErrors((prev) => {
3242
+ const next = { ...prev };
3243
+ delete next[name];
3244
+ return next;
3245
+ });
3246
+ }, []);
3247
+ const getValue = (0, import_react24.useCallback)(
3248
+ (name) => {
3249
+ return values[name];
3250
+ },
3251
+ [values]
3252
+ );
3253
+ const validateField = (0, import_react24.useCallback)(
3254
+ async (name) => {
3255
+ try {
3256
+ const shape = schema.shape;
3257
+ if (shape && shape[name]) {
3258
+ await shape[name].parseAsync(values[name]);
3259
+ setErrors((prev) => {
3260
+ const next = { ...prev };
3261
+ delete next[name];
3262
+ return next;
3263
+ });
3264
+ return true;
3265
+ }
3266
+ return true;
3267
+ } catch (error) {
3268
+ setErrors((prev) => ({
3269
+ ...prev,
3270
+ [name]: error.errors?.[0]?.message || "\u9A8C\u8BC1\u5931\u8D25"
3271
+ }));
3272
+ return false;
3273
+ }
3274
+ },
3275
+ [schema, values]
3276
+ );
3277
+ const validate = (0, import_react24.useCallback)(async () => {
3278
+ try {
3279
+ await schema.parseAsync(values);
3280
+ setErrors({});
3281
+ return true;
3282
+ } catch (error) {
3283
+ const formErrors = {};
3284
+ error.errors?.forEach((err) => {
3285
+ const path = err.path.join(".");
3286
+ formErrors[path] = err.message;
3287
+ });
3288
+ setErrors(formErrors);
3289
+ return false;
3290
+ }
3291
+ }, [schema, values]);
3292
+ const reset = (0, import_react24.useCallback)(() => {
3293
+ setValues(defaultValues);
3294
+ setErrors({});
3295
+ setIsSubmitting(false);
3296
+ }, [defaultValues]);
3297
+ const handleSubmit = (0, import_react24.useCallback)(
3298
+ async (onSubmit) => {
3299
+ const valid = await validate();
3300
+ if (!valid) return;
3301
+ setIsSubmitting(true);
3302
+ try {
3303
+ await onSubmit?.(values);
3304
+ } finally {
3305
+ setIsSubmitting(false);
3306
+ }
3307
+ },
3308
+ [validate, values]
3309
+ );
3310
+ return {
3311
+ values,
3312
+ errors,
3313
+ isValid,
3314
+ isDirty,
3315
+ isSubmitting,
3316
+ setValue,
3317
+ getValue,
3318
+ validate,
3319
+ validateField,
3320
+ reset,
3321
+ handleSubmit
3322
+ };
3323
+ }
3324
+
3325
+ // src/ui/hooks/useToggle.ts
3326
+ var import_react25 = require("react");
3327
+ function useToggle(defaultValue = false) {
3328
+ const [value, setValue] = (0, import_react25.useState)(defaultValue);
3329
+ const toggle = (0, import_react25.useCallback)(() => {
3330
+ setValue((v) => !v);
3331
+ }, []);
3332
+ const set = (0, import_react25.useCallback)((newValue) => {
3333
+ setValue(newValue);
3334
+ }, []);
3335
+ const setTrue = (0, import_react25.useCallback)(() => {
3336
+ setValue(true);
3337
+ }, []);
3338
+ const setFalse = (0, import_react25.useCallback)(() => {
3339
+ setValue(false);
3340
+ }, []);
3341
+ return [value, { toggle, set, setTrue, setFalse }];
3342
+ }
3343
+
3344
+ // src/ui/hooks/useDebounce.ts
3345
+ var import_react26 = require("react");
3346
+ function useDebounce(value, delay = 500) {
3347
+ const [debouncedValue, setDebouncedValue] = (0, import_react26.useState)(value);
3348
+ (0, import_react26.useEffect)(() => {
3349
+ const timer = setTimeout(() => {
3350
+ setDebouncedValue(value);
3351
+ }, delay);
3352
+ return () => {
3353
+ clearTimeout(timer);
3354
+ };
3355
+ }, [value, delay]);
3356
+ return debouncedValue;
3357
+ }
3358
+
3359
+ // src/ui/hooks/useThrottle.ts
3360
+ var import_react27 = require("react");
3361
+ function useThrottle(value, delay = 200) {
3362
+ const [throttledValue, setThrottledValue] = (0, import_react27.useState)(value);
3363
+ const lastUpdatedRef = (0, import_react27.useRef)(Date.now());
3364
+ (0, import_react27.useEffect)(() => {
3365
+ const now = Date.now();
3366
+ const timeElapsed = now - lastUpdatedRef.current;
3367
+ if (timeElapsed >= delay) {
3368
+ lastUpdatedRef.current = now;
3369
+ setThrottledValue(value);
3370
+ return void 0;
3371
+ } else {
3372
+ const timer = setTimeout(() => {
3373
+ lastUpdatedRef.current = Date.now();
3374
+ setThrottledValue(value);
3375
+ }, delay - timeElapsed);
3376
+ return () => {
3377
+ clearTimeout(timer);
3378
+ };
3379
+ }
3380
+ }, [value, delay]);
3381
+ return throttledValue;
3382
+ }
3383
+
3384
+ // src/ui/hooks/useKeyboard.ts
3385
+ var import_react28 = require("react");
3386
+ var import_react_native19 = require("react-native");
3387
+ function useKeyboard() {
3388
+ const [visible, setVisible] = (0, import_react28.useState)(false);
3389
+ const [height, setHeight] = (0, import_react28.useState)(0);
3390
+ (0, import_react28.useEffect)(() => {
3391
+ const handleKeyboardWillShow = (event) => {
3392
+ setVisible(true);
3393
+ setHeight(event.endCoordinates.height);
3394
+ };
3395
+ const handleKeyboardDidShow = (event) => {
3396
+ setVisible(true);
3397
+ setHeight(event.endCoordinates.height);
3398
+ };
3399
+ const handleKeyboardWillHide = () => {
3400
+ setVisible(false);
3401
+ setHeight(0);
3402
+ };
3403
+ const handleKeyboardDidHide = () => {
3404
+ setVisible(false);
3405
+ setHeight(0);
3406
+ };
3407
+ const willShowSub = import_react_native19.Keyboard.addListener(
3408
+ import_react_native19.Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
3409
+ import_react_native19.Platform.OS === "ios" ? handleKeyboardWillShow : handleKeyboardDidShow
3410
+ );
3411
+ const willHideSub = import_react_native19.Keyboard.addListener(
3412
+ import_react_native19.Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide",
3413
+ import_react_native19.Platform.OS === "ios" ? handleKeyboardWillHide : handleKeyboardDidHide
3414
+ );
3415
+ return () => {
3416
+ willShowSub.remove();
3417
+ willHideSub.remove();
3418
+ };
3419
+ }, []);
3420
+ const dismiss = (0, import_react28.useCallback)(() => {
3421
+ import_react_native19.Keyboard.dismiss();
3422
+ }, []);
3423
+ return { visible, height, dismiss };
3424
+ }
3425
+
3426
+ // src/ui/hooks/useDimensions.ts
3427
+ var import_react29 = require("react");
3428
+ var import_react_native20 = require("react-native");
3429
+ function useDimensions() {
3430
+ const [dimensions, setDimensions] = (0, import_react29.useState)(() => {
3431
+ const window = import_react_native20.Dimensions.get("window");
3432
+ return {
3433
+ width: window.width,
3434
+ height: window.height,
3435
+ scale: window.scale,
3436
+ fontScale: window.fontScale
3437
+ };
3438
+ });
3439
+ (0, import_react29.useEffect)(() => {
3440
+ const handleChange = ({ window }) => {
3441
+ setDimensions({
3442
+ width: window.width,
3443
+ height: window.height,
3444
+ scale: window.scale,
3445
+ fontScale: window.fontScale
3446
+ });
3447
+ };
3448
+ const subscription = import_react_native20.Dimensions.addEventListener("change", handleChange);
3449
+ return () => {
3450
+ subscription.remove();
3451
+ };
3452
+ }, []);
3453
+ return dimensions;
3454
+ }
3455
+
3456
+ // src/ui/hooks/useOrientation.ts
3457
+ var import_react30 = require("react");
3458
+ var import_react_native21 = require("react-native");
3459
+ function useOrientation() {
3460
+ const getOrientation = () => {
3461
+ const { width, height } = import_react_native21.Dimensions.get("window");
3462
+ return width > height ? "landscape" : "portrait";
3463
+ };
3464
+ const [orientation, setOrientation] = (0, import_react30.useState)(getOrientation);
3465
+ (0, import_react30.useEffect)(() => {
3466
+ const handleChange = ({ window }) => {
3467
+ const newOrientation = window.width > window.height ? "landscape" : "portrait";
3468
+ setOrientation(newOrientation);
3469
+ };
3470
+ const subscription = import_react_native21.Dimensions.addEventListener("change", handleChange);
3471
+ return () => {
3472
+ subscription.remove();
3473
+ };
3474
+ }, []);
3475
+ return {
3476
+ orientation,
3477
+ isPortrait: orientation === "portrait",
3478
+ isLandscape: orientation === "landscape"
3479
+ };
3480
+ }
3481
+
3482
+ // src/navigation/provider.tsx
3483
+ var import_react31 = __toESM(require("react"));
3484
+ var import_native = require("@react-navigation/native");
3485
+
3486
+ // src/navigation/utils/navigation-theme.ts
3487
+ function createNavigationTheme(pantherTheme, isDark) {
3488
+ const { primary, background, card, text, divider } = getThemeColors(pantherTheme, isDark);
3489
+ return {
3490
+ dark: isDark,
3491
+ colors: {
3492
+ primary,
3493
+ background,
3494
+ card,
3495
+ text,
3496
+ border: divider,
3497
+ notification: pantherTheme.colors.error?.[500] || "#ef4444"
3498
+ },
3499
+ fonts: {
3500
+ regular: { fontFamily: "System", fontWeight: "400" },
3501
+ medium: { fontFamily: "System", fontWeight: "500" },
3502
+ bold: { fontFamily: "System", fontWeight: "700" },
3503
+ heavy: { fontFamily: "System", fontWeight: "900" }
3504
+ }
3505
+ };
3506
+ }
3507
+
3508
+ // src/navigation/provider.tsx
3509
+ var import_jsx_runtime29 = require("nativewind/jsx-runtime");
3510
+ function NavigationProvider({
3511
+ children,
3512
+ linking,
3513
+ fallback,
3514
+ onReady,
3515
+ onStateChange,
3516
+ onUnhandledAction,
3517
+ theme: customTheme
3518
+ }) {
3519
+ const { theme, isDark } = useTheme();
3520
+ const navigationTheme = import_react31.default.useMemo(
3521
+ () => customTheme || createNavigationTheme(theme, isDark),
3522
+ [customTheme, theme, isDark]
3523
+ );
3524
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3525
+ import_native.NavigationContainer,
3526
+ {
3527
+ theme: navigationTheme,
3528
+ linking,
3529
+ fallback,
3530
+ onReady,
3531
+ onStateChange,
3532
+ onUnhandledAction,
3533
+ children
3534
+ }
3535
+ );
3536
+ }
3537
+
3538
+ // src/navigation/vendor/stack.ts
3539
+ var import_stack = require("@react-navigation/stack");
3540
+
3541
+ // src/navigation/navigators/StackNavigator.tsx
3542
+ var import_jsx_runtime30 = require("nativewind/jsx-runtime");
3543
+ var NativeStack = (0, import_stack.createStackNavigator)();
3544
+ var defaultScreenOptions = {
3545
+ headerShown: false,
3546
+ ...import_stack.TransitionPresets.SlideFromRightIOS
3547
+ };
3548
+ function StackNavigator({ initialRouteName, screenOptions, children }) {
3549
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3550
+ NativeStack.Navigator,
3551
+ {
3552
+ initialRouteName,
3553
+ screenOptions: { ...defaultScreenOptions, ...screenOptions },
3554
+ children
3555
+ }
3556
+ );
3557
+ }
3558
+ StackNavigator.Screen = NativeStack.Screen;
3559
+ StackNavigator.Group = NativeStack.Group;
3560
+ function createStackScreens(routes) {
3561
+ return routes.map((route) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3562
+ StackNavigator.Screen,
3563
+ {
3564
+ name: route.name,
3565
+ component: route.component,
3566
+ options: route.options,
3567
+ initialParams: route.initialParams
3568
+ },
3569
+ route.name
3570
+ ));
3571
+ }
3572
+
3573
+ // src/navigation/navigators/TabNavigator.tsx
3574
+ var import_react32 = __toESM(require("react"));
3575
+ var import_bottom_tabs = require("@react-navigation/bottom-tabs");
3576
+
3577
+ // src/navigation/components/BottomTabBar.tsx
3578
+ var import_react_native22 = require("react-native");
3579
+ var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
3580
+ var import_jsx_runtime31 = require("nativewind/jsx-runtime");
3581
+ var DEFAULT_TAB_BAR_HEIGHT = 65;
3582
+ function BottomTabBar({
3583
+ state,
3584
+ descriptors,
3585
+ navigation,
3586
+ showLabel = true,
3587
+ activeTintColor,
3588
+ inactiveTintColor,
3589
+ height = DEFAULT_TAB_BAR_HEIGHT,
3590
+ activeBackgroundColor,
3591
+ inactiveBackgroundColor,
3592
+ iconStyle,
3593
+ labelStyle,
3594
+ style
3595
+ }) {
3596
+ const colors = useThemeColors();
3597
+ const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
3598
+ const activeColor = activeTintColor || colors.primary;
3599
+ const inactiveColor = inactiveTintColor || colors.textMuted;
3600
+ const backgroundColor = style?.backgroundColor || colors.card;
3601
+ const borderTopColor = colors.divider;
3602
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3603
+ import_react_native22.View,
3604
+ {
3605
+ style: [
3606
+ styles11.container,
3607
+ { borderTopColor },
3608
+ { backgroundColor, height: height + insets.bottom, paddingBottom: insets.bottom },
3609
+ style
3610
+ ],
3611
+ children: state.routes.map((route, index) => {
3612
+ const { options } = descriptors[route.key];
3613
+ const isFocused = state.index === index;
3614
+ const onPress = () => {
3615
+ const event = navigation.emit({
3616
+ type: "tabPress",
3617
+ target: route.key,
3618
+ canPreventDefault: true
3619
+ });
3620
+ if (!isFocused && !event.defaultPrevented) {
3621
+ navigation.navigate(route.name);
3622
+ }
3623
+ };
3624
+ const onLongPress = () => {
3625
+ navigation.emit({
3626
+ type: "tabLongPress",
3627
+ target: route.key
3628
+ });
3629
+ };
3630
+ const label = options.tabBarLabel !== void 0 ? options.tabBarLabel : options.title !== void 0 ? options.title : route.name;
3631
+ const iconName = options.tabBarIcon ? options.tabBarIcon({
3632
+ focused: isFocused,
3633
+ color: isFocused ? activeColor : inactiveColor,
3634
+ size: 24
3635
+ }) : null;
3636
+ const badge = options.tabBarBadge;
3637
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
3638
+ import_react_native22.TouchableOpacity,
3639
+ {
3640
+ accessibilityRole: "button",
3641
+ accessibilityState: isFocused ? { selected: true } : {},
3642
+ accessibilityLabel: options.tabBarAccessibilityLabel,
3643
+ testID: options.tabBarTestID,
3644
+ onPress,
3645
+ onLongPress,
3646
+ style: [
3647
+ styles11.tab,
3648
+ {
3649
+ backgroundColor: isFocused ? activeBackgroundColor : inactiveBackgroundColor
3650
+ }
3651
+ ],
3652
+ children: [
3653
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_native22.View, { style: [styles11.iconContainer, iconStyle], children: [
3654
+ iconName,
3655
+ badge != null && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_native22.View, { style: [styles11.badge, { backgroundColor: activeColor }], children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(AppText, { style: styles11.badgeText, children: typeof badge === "number" && badge > 99 ? "99+" : badge }) })
3656
+ ] }),
3657
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3658
+ AppText,
3659
+ {
3660
+ style: [
3661
+ styles11.label,
3662
+ { color: isFocused ? activeColor : inactiveColor },
3663
+ labelStyle
3664
+ ],
3665
+ numberOfLines: 1,
3666
+ children: label
3667
+ }
3668
+ )
3669
+ ]
3670
+ },
3671
+ route.key
3672
+ );
3673
+ })
3674
+ }
3675
+ );
3676
+ }
3677
+ var styles11 = import_react_native22.StyleSheet.create({
3678
+ container: {
3679
+ flexDirection: "row",
3680
+ borderTopWidth: 0.5,
3681
+ elevation: 8,
3682
+ shadowColor: "#000",
3683
+ shadowOffset: { width: 0, height: -2 },
3684
+ shadowOpacity: 0.1,
3685
+ shadowRadius: 3
3686
+ },
3687
+ tab: {
3688
+ flex: 1,
3689
+ alignItems: "center",
3690
+ justifyContent: "center",
3691
+ paddingVertical: 6
3692
+ },
3693
+ iconContainer: {
3694
+ position: "relative",
3695
+ marginBottom: 2
3696
+ },
3697
+ badge: {
3698
+ position: "absolute",
3699
+ top: -6,
3700
+ right: -10,
3701
+ minWidth: 18,
3702
+ height: 18,
3703
+ borderRadius: 9,
3704
+ justifyContent: "center",
3705
+ alignItems: "center",
3706
+ paddingHorizontal: 4
3707
+ },
3708
+ badgeText: {
3709
+ color: "#fff",
3710
+ fontSize: 10,
3711
+ fontWeight: "bold"
3712
+ },
3713
+ label: {
3714
+ fontSize: 12,
3715
+ marginTop: 2
3716
+ }
3717
+ });
3718
+
3719
+ // src/navigation/navigators/TabNavigator.tsx
3720
+ var import_jsx_runtime32 = require("nativewind/jsx-runtime");
3721
+ var NativeTab = (0, import_bottom_tabs.createBottomTabNavigator)();
3722
+ var defaultScreenOptions2 = {
3723
+ headerShown: false,
3724
+ tabBarShowLabel: true
3725
+ };
3726
+ function TabNavigator({
3727
+ initialRouteName,
3728
+ tabBarOptions,
3729
+ tabBar,
3730
+ screenOptions,
3731
+ children
3732
+ }) {
3733
+ const mergedScreenOptions = import_react32.default.useMemo(() => {
3734
+ const options = { ...defaultScreenOptions2, ...screenOptions };
3735
+ if (tabBarOptions) {
3736
+ if (tabBarOptions.showLabel !== void 0) {
3737
+ options.tabBarShowLabel = tabBarOptions.showLabel;
3738
+ }
3739
+ if (tabBarOptions.activeTintColor) {
3740
+ options.tabBarActiveTintColor = tabBarOptions.activeTintColor;
3741
+ }
3742
+ if (tabBarOptions.inactiveTintColor) {
3743
+ options.tabBarInactiveTintColor = tabBarOptions.inactiveTintColor;
3744
+ }
3745
+ if (tabBarOptions.activeBackgroundColor) {
3746
+ options.tabBarActiveBackgroundColor = tabBarOptions.activeBackgroundColor;
3747
+ }
3748
+ if (tabBarOptions.inactiveBackgroundColor) {
3749
+ options.tabBarInactiveBackgroundColor = tabBarOptions.inactiveBackgroundColor;
3750
+ }
3751
+ if (tabBarOptions.hideOnKeyboard !== void 0) {
3752
+ options.tabBarHideOnKeyboard = tabBarOptions.hideOnKeyboard;
3753
+ }
3754
+ if (tabBarOptions.labelPosition) {
3755
+ options.tabBarLabelPosition = tabBarOptions.labelPosition;
3756
+ }
3757
+ if (tabBarOptions.labelStyle) {
3758
+ options.tabBarLabelStyle = tabBarOptions.labelStyle;
3759
+ }
3760
+ if (tabBarOptions.style) {
3761
+ options.tabBarStyle = tabBarOptions.style;
3762
+ }
3763
+ }
3764
+ return options;
3765
+ }, [tabBarOptions, screenOptions]);
3766
+ const resolvedTabBar = import_react32.default.useMemo(() => {
3767
+ if (tabBar) return tabBar;
3768
+ return (props) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3769
+ BottomTabBar,
3770
+ {
3771
+ ...props,
3772
+ showLabel: tabBarOptions?.showLabel,
3773
+ activeTintColor: tabBarOptions?.activeTintColor,
3774
+ inactiveTintColor: tabBarOptions?.inactiveTintColor,
3775
+ activeBackgroundColor: tabBarOptions?.activeBackgroundColor,
3776
+ inactiveBackgroundColor: tabBarOptions?.inactiveBackgroundColor,
3777
+ iconStyle: tabBarOptions?.iconStyle,
3778
+ labelStyle: tabBarOptions?.labelStyle,
3779
+ style: tabBarOptions?.style,
3780
+ height: tabBarOptions?.height
3781
+ }
3782
+ );
3783
+ }, [
3784
+ tabBar,
3785
+ tabBarOptions?.showLabel,
3786
+ tabBarOptions?.activeTintColor,
3787
+ tabBarOptions?.inactiveTintColor,
3788
+ tabBarOptions?.activeBackgroundColor,
3789
+ tabBarOptions?.inactiveBackgroundColor,
3790
+ tabBarOptions?.iconStyle,
3791
+ tabBarOptions?.labelStyle,
3792
+ tabBarOptions?.style,
3793
+ tabBarOptions?.height
3794
+ ]);
3795
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3796
+ NativeTab.Navigator,
3797
+ {
3798
+ initialRouteName,
3799
+ screenOptions: mergedScreenOptions,
3800
+ tabBar: resolvedTabBar,
3801
+ children
3802
+ }
3803
+ );
3804
+ }
3805
+ TabNavigator.Screen = NativeTab.Screen;
3806
+ function createTabScreens(routes) {
3807
+ return routes.map((route) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
3808
+ TabNavigator.Screen,
3809
+ {
3810
+ name: route.name,
3811
+ component: route.component,
3812
+ options: route.options,
3813
+ initialParams: route.initialParams
3814
+ },
3815
+ route.name
3816
+ ));
3817
+ }
3818
+
3819
+ // src/navigation/navigators/DrawerNavigator.tsx
3820
+ var import_react33 = __toESM(require("react"));
3821
+ var import_drawer = require("@react-navigation/drawer");
3822
+ var import_jsx_runtime33 = require("nativewind/jsx-runtime");
3823
+ var NativeDrawer = (0, import_drawer.createDrawerNavigator)();
3824
+ function DrawerNavigator({
3825
+ initialRouteName,
3826
+ screenOptions,
3827
+ drawerContent,
3828
+ drawerOptions,
3829
+ children
3830
+ }) {
3831
+ const { theme, isDark } = useTheme();
3832
+ const navigationTheme = createNavigationTheme(theme, isDark);
3833
+ const mergedScreenOptions = import_react33.default.useMemo(() => {
3834
+ return {
3835
+ headerShown: false,
3836
+ drawerStyle: {
3837
+ backgroundColor: navigationTheme.colors.card,
3838
+ width: drawerOptions?.drawerWidth || 280
3839
+ },
3840
+ drawerActiveTintColor: theme.colors.primary?.[500] || "#f38b32",
3841
+ drawerInactiveTintColor: navigationTheme.colors.text,
3842
+ drawerLabelStyle: {
3843
+ fontSize: 16
3844
+ },
3845
+ drawerType: drawerOptions?.drawerType,
3846
+ overlayColor: drawerOptions?.overlayColor,
3847
+ edgeWidth: drawerOptions?.edgeWidth,
3848
+ minSwipeDistance: drawerOptions?.minSwipeDistance,
3849
+ ...screenOptions
3850
+ };
3851
+ }, [screenOptions, drawerOptions, navigationTheme, theme]);
3852
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3853
+ NativeDrawer.Navigator,
3854
+ {
3855
+ initialRouteName,
3856
+ screenOptions: mergedScreenOptions,
3857
+ drawerContent,
3858
+ children
3859
+ }
3860
+ );
3861
+ }
3862
+ DrawerNavigator.Screen = NativeDrawer.Screen;
3863
+ function createDrawerScreens(routes) {
3864
+ return routes.map((route) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3865
+ DrawerNavigator.Screen,
3866
+ {
3867
+ name: route.name,
3868
+ component: route.component,
3869
+ options: route.options,
3870
+ initialParams: route.initialParams
3871
+ },
3872
+ route.name
3873
+ ));
3874
+ }
3875
+
3876
+ // src/navigation/components/AppHeader.tsx
3877
+ var import_react_native23 = require("react-native");
3878
+ var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
3879
+ var import_jsx_runtime34 = require("nativewind/jsx-runtime");
3880
+ function AppHeader({
3881
+ title,
3882
+ subtitle,
3883
+ leftIcon = "chevron-left",
3884
+ onLeftPress,
3885
+ rightIcons = [],
3886
+ transparent = false,
3887
+ safeArea = true,
3888
+ style
3889
+ }) {
3890
+ const colors = useThemeColors();
3891
+ const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
3892
+ const backgroundColor = transparent ? "transparent" : colors.card;
3893
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3894
+ AppView,
3895
+ {
3896
+ style: [
3897
+ {
3898
+ backgroundColor,
3899
+ paddingTop: safeArea ? insets.top : 0
3900
+ },
3901
+ style
3902
+ ],
3903
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(AppView, { row: true, items: "center", px: 4, style: styles12.container, children: [
3904
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppView, { style: [styles12.sideContainer, styles12.leftContainer], children: leftIcon && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppPressable, { onPress: onLeftPress, style: styles12.iconButton, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { name: leftIcon, size: 24, color: colors.text }) }) }),
3905
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(AppView, { style: styles12.centerContainer, children: [
3906
+ title && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3907
+ AppText,
3908
+ {
3909
+ size: "lg",
3910
+ weight: "semibold",
3911
+ style: [styles12.title, { color: colors.text }],
3912
+ numberOfLines: 1,
3913
+ children: title
3914
+ }
3915
+ ),
3916
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
3917
+ AppText,
3918
+ {
3919
+ size: "xs",
3920
+ style: [styles12.subtitle, { color: colors.textMuted }],
3921
+ numberOfLines: 1,
3922
+ children: subtitle
3923
+ }
3924
+ )
3925
+ ] }),
3926
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppView, { row: true, items: "center", style: [styles12.sideContainer, styles12.rightContainer], children: rightIcons.map((icon, index) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppPressable, { onPress: icon.onPress, style: styles12.iconButton, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(AppView, { children: [
3927
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { name: icon.icon, size: 24, color: colors.text }),
3928
+ icon.badge ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppView, { style: styles12.badge, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(AppText, { size: "xs", color: "white", style: styles12.badgeText, children: icon.badge > 99 ? "99+" : icon.badge }) }) : null
3929
+ ] }) }, index)) })
3930
+ ] })
3931
+ }
3932
+ );
3933
+ }
3934
+ var styles12 = import_react_native23.StyleSheet.create({
3935
+ container: {
3936
+ height: 44
3937
+ // iOS 标准导航栏高度
3938
+ },
3939
+ sideContainer: {
3940
+ width: 70,
3941
+ // 固定宽度,确保标题居中
3942
+ flexDirection: "row",
3943
+ alignItems: "center"
3944
+ },
3945
+ leftContainer: {
3946
+ justifyContent: "flex-start"
3947
+ },
3948
+ rightContainer: {
3949
+ justifyContent: "flex-end"
3950
+ },
3951
+ centerContainer: {
3952
+ flex: 1,
3953
+ alignItems: "center",
3954
+ justifyContent: "center"
3955
+ },
3956
+ title: {
3957
+ textAlign: "center"
3958
+ },
3959
+ subtitle: {
3960
+ textAlign: "center",
3961
+ marginTop: 2
3962
+ },
3963
+ iconButton: {
3964
+ padding: 8
3965
+ },
3966
+ badge: {
3967
+ position: "absolute",
3968
+ top: -4,
3969
+ right: -4,
3970
+ minWidth: 16,
3971
+ height: 16,
3972
+ borderRadius: 8,
3973
+ backgroundColor: "#ef4444",
3974
+ alignItems: "center",
3975
+ justifyContent: "center",
3976
+ paddingHorizontal: 4
3977
+ },
3978
+ badgeText: {
3979
+ fontSize: 10,
3980
+ fontWeight: "bold"
3981
+ }
3982
+ });
3983
+
3984
+ // src/navigation/components/DrawerContent.tsx
3985
+ var import_react_native24 = require("react-native");
3986
+ var import_drawer2 = require("@react-navigation/drawer");
3987
+ var import_jsx_runtime35 = require("nativewind/jsx-runtime");
3988
+ function DrawerContent({
3989
+ state,
3990
+ descriptors,
3991
+ navigation,
3992
+ header,
3993
+ footer,
3994
+ items,
3995
+ activeBackgroundColor,
3996
+ activeTintColor,
3997
+ inactiveTintColor
3998
+ }) {
3999
+ const { theme, isDark } = useTheme();
4000
+ const colors = useThemeColors();
4001
+ const activeBgColor = activeBackgroundColor || (isDark ? colors.primarySurface : theme.colors.primary?.[50] || "#fff7ed");
4002
+ const activeColor = activeTintColor || colors.primary;
4003
+ const inactiveColor = inactiveTintColor || (isDark ? "#d1d5db" : "#4b5563");
4004
+ const backgroundColor = colors.card;
4005
+ const dividerColor = colors.divider;
4006
+ const drawerItems = items || state.routes.map((route) => {
4007
+ const descriptor = descriptors[route.key];
4008
+ const options = descriptor.options;
4009
+ return {
4010
+ name: route.name,
4011
+ label: options.drawerLabel || options.title || route.name,
4012
+ icon: options.drawerIcon?.({ size: 24, color: inactiveColor })?.props?.name,
4013
+ badge: options.tabBarBadge
4014
+ };
4015
+ });
4016
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_drawer2.DrawerContentScrollView, { style: [styles13.container, { backgroundColor }], children: [
4017
+ header && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native24.View, { style: [styles13.header, { borderBottomColor: dividerColor }], children: header }),
4018
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AppView, { className: "py-2", children: drawerItems.map((item) => {
4019
+ const isFocused = state.routes[state.index].name === item.name;
4020
+ const onPress = () => {
4021
+ navigation.navigate(item.name);
4022
+ };
4023
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
4024
+ import_react_native24.TouchableOpacity,
4025
+ {
4026
+ onPress,
4027
+ style: [styles13.item, isFocused && { backgroundColor: activeBgColor }],
4028
+ children: [
4029
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native24.View, { style: styles13.iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4030
+ Icon,
4031
+ {
4032
+ name: item.icon,
4033
+ size: "md",
4034
+ color: isFocused ? activeColor : inactiveColor
4035
+ }
4036
+ ) }),
4037
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4038
+ AppText,
4039
+ {
4040
+ style: [
4041
+ styles13.label,
4042
+ { color: isFocused ? activeColor : inactiveColor },
4043
+ isFocused && styles13.activeLabel
4044
+ ],
4045
+ numberOfLines: 1,
4046
+ children: item.label
4047
+ }
4048
+ ),
4049
+ item.badge != null && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native24.View, { style: [styles13.badge, { backgroundColor: activeColor }], children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(AppText, { style: styles13.badgeText, children: typeof item.badge === "number" && item.badge > 99 ? "99+" : item.badge }) })
4050
+ ]
4051
+ },
4052
+ item.name
4053
+ );
4054
+ }) }),
4055
+ footer && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native24.View, { style: [styles13.footer, { borderTopColor: dividerColor }], children: footer })
4056
+ ] });
4057
+ }
4058
+ var styles13 = import_react_native24.StyleSheet.create({
4059
+ container: {
4060
+ flex: 1
4061
+ },
4062
+ header: {
4063
+ padding: 16,
4064
+ borderBottomWidth: 0.5
4065
+ },
4066
+ item: {
4067
+ flexDirection: "row",
4068
+ alignItems: "center",
4069
+ paddingHorizontal: 16,
4070
+ paddingVertical: 12,
4071
+ marginHorizontal: 8,
4072
+ marginVertical: 2,
4073
+ borderRadius: 8
4074
+ },
4075
+ iconContainer: {
4076
+ width: 32,
4077
+ alignItems: "center",
4078
+ marginRight: 8
4079
+ },
4080
+ label: {
4081
+ flex: 1,
4082
+ fontSize: 16
4083
+ },
4084
+ activeLabel: {
4085
+ fontWeight: "600"
4086
+ },
4087
+ badge: {
4088
+ minWidth: 20,
4089
+ height: 20,
4090
+ borderRadius: 10,
4091
+ justifyContent: "center",
4092
+ alignItems: "center",
4093
+ paddingHorizontal: 6
4094
+ },
4095
+ badgeText: {
4096
+ color: "#fff",
4097
+ fontSize: 12,
4098
+ fontWeight: "bold"
4099
+ },
4100
+ footer: {
4101
+ marginTop: "auto",
4102
+ padding: 16,
4103
+ borderTopWidth: 0.5
4104
+ }
4105
+ });
4106
+
4107
+ // src/navigation/hooks/useNavigation.ts
4108
+ var import_react34 = require("react");
4109
+ var import_native2 = require("@react-navigation/native");
4110
+ function useNavigation() {
4111
+ return (0, import_native2.useNavigation)();
4112
+ }
4113
+ function useStackNavigation() {
4114
+ return (0, import_native2.useNavigation)();
4115
+ }
4116
+ function useTabNavigation() {
4117
+ return (0, import_native2.useNavigation)();
4118
+ }
4119
+ function useDrawerNavigation() {
4120
+ return (0, import_native2.useNavigation)();
4121
+ }
4122
+ function useBackHandler(handler) {
4123
+ const navigation = (0, import_native2.useNavigation)();
4124
+ (0, import_react34.useEffect)(() => {
4125
+ const unsubscribe = navigation.addListener("beforeRemove", (e) => {
4126
+ if (!handler()) {
4127
+ e.preventDefault();
4128
+ }
4129
+ });
4130
+ return unsubscribe;
4131
+ }, [navigation, handler]);
4132
+ }
4133
+
4134
+ // src/navigation/hooks/useRoute.ts
4135
+ var import_native3 = require("@react-navigation/native");
4136
+ function useRoute() {
4137
+ return (0, import_native3.useRoute)();
4138
+ }
4139
+
4140
+ // src/navigation/hooks/useNavigationState.ts
4141
+ var import_native4 = require("@react-navigation/native");
4142
+ function useNavigationState(selector) {
4143
+ return (0, import_native4.useNavigationState)(selector);
4144
+ }
4145
+
4146
+ // src/navigation/index.ts
4147
+ var import_native5 = require("@react-navigation/native");
4148
+
4149
+ // src/overlay/AppProvider.tsx
4150
+ var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
4151
+
4152
+ // src/overlay/AppStatusBar.tsx
4153
+ var import_react_native25 = require("react-native");
4154
+ var import_jsx_runtime36 = require("nativewind/jsx-runtime");
4155
+ function AppStatusBar({
4156
+ barStyle = "auto",
4157
+ backgroundColor,
4158
+ translucent = false,
4159
+ ...props
4160
+ }) {
4161
+ const { theme, isDark } = useTheme();
4162
+ const resolvedBarStyle = barStyle === "auto" ? isDark ? "light-content" : "dark-content" : barStyle;
4163
+ const resolvedBackgroundColor = backgroundColor ?? (translucent ? "transparent" : theme.colors.background?.[500] || "#ffffff");
4164
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4165
+ import_react_native25.StatusBar,
4166
+ {
4167
+ barStyle: resolvedBarStyle,
4168
+ backgroundColor: resolvedBackgroundColor,
4169
+ translucent,
4170
+ ...props
4171
+ }
4172
+ );
4173
+ }
4174
+
4175
+ // src/overlay/loading/provider.tsx
4176
+ var import_react36 = require("react");
4177
+
4178
+ // src/overlay/loading/context.ts
4179
+ var import_react35 = require("react");
4180
+ var LoadingContext = (0, import_react35.createContext)(null);
4181
+ function useLoadingContext() {
4182
+ const ctx = (0, import_react35.useContext)(LoadingContext);
4183
+ if (!ctx) throw new Error("useLoading must be used within OverlayProvider");
4184
+ return ctx;
4185
+ }
4186
+
4187
+ // src/overlay/loading/component.tsx
4188
+ var import_react_native26 = require("react-native");
4189
+ var import_jsx_runtime37 = require("nativewind/jsx-runtime");
4190
+ function LoadingModal({ visible, text }) {
4191
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native26.Modal, { transparent: true, visible, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native26.View, { style: styles14.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react_native26.View, { style: [styles14.loadingBox, { backgroundColor: "rgba(0,0,0,0.8)" }], children: [
4192
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native26.ActivityIndicator, { size: "large", color: "#fff" }),
4193
+ text && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(AppText, { className: "text-white mt-3 text-sm", children: text })
4194
+ ] }) }) });
4195
+ }
4196
+ var styles14 = import_react_native26.StyleSheet.create({
4197
+ overlay: {
4198
+ flex: 1,
4199
+ backgroundColor: "rgba(0,0,0,0.5)",
4200
+ justifyContent: "center",
4201
+ alignItems: "center"
4202
+ },
4203
+ loadingBox: {
4204
+ padding: 24,
4205
+ borderRadius: 12,
4206
+ alignItems: "center",
4207
+ minWidth: 120
4208
+ }
4209
+ });
4210
+
4211
+ // src/overlay/loading/provider.tsx
4212
+ var import_jsx_runtime38 = require("nativewind/jsx-runtime");
4213
+ function LoadingProvider({ children }) {
4214
+ const [state, setState] = (0, import_react36.useState)({ visible: false });
4215
+ const show = (0, import_react36.useCallback)((text) => {
4216
+ setState({ visible: true, text });
4217
+ }, []);
4218
+ const hide = (0, import_react36.useCallback)(() => {
4219
+ setState({ visible: false });
4220
+ }, []);
4221
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(LoadingContext.Provider, { value: { show, hide }, children: [
4222
+ children,
4223
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(LoadingModal, { ...state })
4224
+ ] });
4225
+ }
4226
+
4227
+ // src/overlay/toast/provider.tsx
4228
+ var import_react39 = require("react");
4229
+ var import_react_native28 = require("react-native");
4230
+
4231
+ // src/overlay/toast/context.ts
4232
+ var import_react37 = require("react");
4233
+ var ToastContext = (0, import_react37.createContext)(null);
4234
+ function useToastContext() {
4235
+ const ctx = (0, import_react37.useContext)(ToastContext);
4236
+ if (!ctx) throw new Error("useToast must be used within OverlayProvider");
4237
+ return ctx;
4238
+ }
4239
+
4240
+ // src/overlay/toast/component.tsx
4241
+ var import_react38 = require("react");
4242
+ var import_react_native27 = require("react-native");
4243
+ var import_jsx_runtime39 = require("nativewind/jsx-runtime");
4244
+ function ToastItemView({ message, type, onHide }) {
4245
+ const fadeAnim = (0, import_react38.useRef)(new import_react_native27.Animated.Value(0)).current;
4246
+ (0, import_react38.useEffect)(() => {
4247
+ import_react_native27.Animated.sequence([
4248
+ import_react_native27.Animated.timing(fadeAnim, { toValue: 1, duration: 200, useNativeDriver: true }),
4249
+ import_react_native27.Animated.delay(2500),
4250
+ import_react_native27.Animated.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: true })
4251
+ ]).start(onHide);
4252
+ }, []);
4253
+ const bgColors = {
4254
+ success: "bg-success-500",
4255
+ error: "bg-error-500",
4256
+ warning: "bg-warning-500",
4257
+ info: "bg-primary-500"
4258
+ };
4259
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4260
+ import_react_native27.Animated.View,
4261
+ {
4262
+ style: {
4263
+ opacity: fadeAnim,
4264
+ transform: [
4265
+ {
4266
+ translateY: fadeAnim.interpolate({
4267
+ inputRange: [0, 1],
4268
+ outputRange: [-20, 0]
4269
+ })
4270
+ }
4271
+ ]
4272
+ },
4273
+ children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(AppView, { className: `${bgColors[type]} px-4 py-3 rounded-lg mb-2 mx-4 shadow-lg`, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(AppText, { className: "text-white text-center", children: message }) })
4274
+ }
4275
+ );
4276
+ }
4277
+
4278
+ // src/overlay/toast/provider.tsx
4279
+ var import_jsx_runtime40 = require("nativewind/jsx-runtime");
4280
+ function ToastProvider({ children }) {
4281
+ const [toasts, setToasts] = (0, import_react39.useState)([]);
4282
+ const timersRef = (0, import_react39.useRef)(/* @__PURE__ */ new Map());
4283
+ const remove = (0, import_react39.useCallback)((id) => {
4284
+ setToasts((prev) => prev.filter((t) => t.id !== id));
4285
+ const timer = timersRef.current.get(id);
4286
+ if (timer) {
4287
+ clearTimeout(timer);
4288
+ timersRef.current.delete(id);
4289
+ }
4290
+ }, []);
4291
+ const show = (0, import_react39.useCallback)(
4292
+ (message, type = "info", duration = 3e3) => {
4293
+ const id = Math.random().toString(36).substring(7);
4294
+ const toast = { id, message, type, duration };
4295
+ setToasts((prev) => [...prev, toast]);
4296
+ const timer = setTimeout(() => remove(id), duration);
4297
+ timersRef.current.set(id, timer);
4298
+ },
4299
+ [remove]
4300
+ );
4301
+ const success = (0, import_react39.useCallback)(
4302
+ (message, duration) => show(message, "success", duration),
4303
+ [show]
4304
+ );
4305
+ const error = (0, import_react39.useCallback)(
4306
+ (message, duration) => show(message, "error", duration),
4307
+ [show]
4308
+ );
4309
+ const info = (0, import_react39.useCallback)(
4310
+ (message, duration) => show(message, "info", duration),
4311
+ [show]
4312
+ );
4313
+ const warning = (0, import_react39.useCallback)(
4314
+ (message, duration) => show(message, "warning", duration),
4315
+ [show]
4316
+ );
4317
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(ToastContext.Provider, { value: { show, success, error, info, warning }, children: [
4318
+ children,
4319
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native28.View, { style: styles15.toastContainer, pointerEvents: "none", children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ToastItemView, { ...toast, onHide: () => remove(toast.id) }, toast.id)) })
4320
+ ] });
4321
+ }
4322
+ var styles15 = import_react_native28.StyleSheet.create({
4323
+ toastContainer: {
4324
+ position: "absolute",
4325
+ top: 60,
4326
+ left: 0,
4327
+ right: 0,
4328
+ zIndex: 9999
4329
+ }
4330
+ });
4331
+
4332
+ // src/overlay/alert/provider.tsx
4333
+ var import_react41 = require("react");
4334
+
4335
+ // src/overlay/alert/context.ts
4336
+ var import_react40 = require("react");
4337
+ var AlertContext = (0, import_react40.createContext)(null);
4338
+ function useAlertContext() {
4339
+ const ctx = (0, import_react40.useContext)(AlertContext);
4340
+ if (!ctx) throw new Error("useAlert must be used within OverlayProvider");
4341
+ return ctx;
4342
+ }
4343
+
4344
+ // src/overlay/alert/component.tsx
4345
+ var import_react_native29 = require("react-native");
4346
+ var import_jsx_runtime41 = require("nativewind/jsx-runtime");
4347
+ function AlertModal({
4348
+ visible,
4349
+ title,
4350
+ message,
4351
+ confirmText,
4352
+ cancelText,
4353
+ showCancel,
4354
+ onConfirm,
4355
+ onCancel
4356
+ }) {
4357
+ if (!visible) return null;
4358
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native29.Modal, { transparent: true, visible: true, animationType: "fade", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native29.View, { style: styles16.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_native29.View, { style: styles16.alertBox, children: [
4359
+ title && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppText, { className: "text-lg font-semibold text-center mb-2", children: title }),
4360
+ message && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppText, { className: "text-gray-600 text-center mb-4", children: message }),
4361
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(AppView, { row: true, gap: 3, className: "mt-2", children: [
4362
+ showCancel && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppPressable, { onPress: onCancel, className: "flex-1 py-3 bg-gray-100 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppText, { className: "text-center text-gray-700", children: cancelText || "\u53D6\u6D88" }) }),
4363
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppPressable, { onPress: onConfirm, className: "flex-1 py-3 bg-primary-500 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(AppText, { className: "text-center text-white", children: confirmText || "\u786E\u5B9A" }) })
4364
+ ] })
4365
+ ] }) }) });
4366
+ }
4367
+ var styles16 = import_react_native29.StyleSheet.create({
4368
+ overlay: {
4369
+ flex: 1,
4370
+ backgroundColor: "rgba(0,0,0,0.5)",
4371
+ justifyContent: "center",
4372
+ alignItems: "center"
4373
+ },
4374
+ alertBox: {
4375
+ backgroundColor: "white",
4376
+ borderRadius: 12,
4377
+ padding: 24,
4378
+ margin: 32,
4379
+ minWidth: 280,
4380
+ shadowColor: "#000",
4381
+ shadowOffset: { width: 0, height: 2 },
4382
+ shadowOpacity: 0.25,
4383
+ shadowRadius: 4,
4384
+ elevation: 5
4385
+ }
4386
+ });
4387
+
4388
+ // src/overlay/alert/provider.tsx
4389
+ var import_jsx_runtime42 = require("nativewind/jsx-runtime");
4390
+ function AlertProvider({ children }) {
4391
+ const [alert, setAlert] = (0, import_react41.useState)(null);
4392
+ const showAlert = (0, import_react41.useCallback)((options) => {
4393
+ setAlert({ ...options, visible: true });
4394
+ }, []);
4395
+ const confirm = (0, import_react41.useCallback)(
4396
+ (options) => {
4397
+ showAlert({ ...options, showCancel: true });
4398
+ },
4399
+ [showAlert]
4400
+ );
4401
+ const hide = (0, import_react41.useCallback)(() => {
4402
+ setAlert(null);
4403
+ }, []);
4404
+ const handleConfirm = (0, import_react41.useCallback)(() => {
4405
+ alert?.onConfirm?.();
4406
+ hide();
4407
+ }, [alert, hide]);
4408
+ const handleCancel = (0, import_react41.useCallback)(() => {
4409
+ alert?.onCancel?.();
4410
+ hide();
4411
+ }, [alert, hide]);
4412
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(AlertContext.Provider, { value: { alert: showAlert, confirm }, children: [
4413
+ children,
4414
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4415
+ AlertModal,
4416
+ {
4417
+ visible: alert?.visible ?? false,
4418
+ title: alert?.title,
4419
+ message: alert?.message,
4420
+ confirmText: alert?.confirmText,
4421
+ cancelText: alert?.cancelText,
4422
+ showCancel: alert?.showCancel,
4423
+ onConfirm: handleConfirm,
4424
+ onCancel: handleCancel
4425
+ }
4426
+ )
4427
+ ] });
4428
+ }
4429
+
4430
+ // src/overlay/provider.tsx
4431
+ var import_jsx_runtime43 = require("nativewind/jsx-runtime");
4432
+ function OverlayProvider({ children }) {
4433
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(LoadingProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ToastProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(AlertProvider, { children }) }) });
4434
+ }
4435
+
4436
+ // src/overlay/AppProvider.tsx
4437
+ var import_jsx_runtime44 = require("nativewind/jsx-runtime");
4438
+ var defaultLightTheme = {
4439
+ colors: {
4440
+ primary: "#f38b32",
4441
+ secondary: "#3b82f6",
4442
+ success: "#22c55e",
4443
+ warning: "#f59e0b",
4444
+ error: "#ef4444",
4445
+ info: "#3b82f6"
4446
+ }
4447
+ };
4448
+ var defaultDarkTheme = {
4449
+ colors: {
4450
+ primary: "#f38b32",
4451
+ secondary: "#60a5fa",
4452
+ success: "#4ade80",
4453
+ warning: "#fbbf24",
4454
+ error: "#f87171",
4455
+ info: "#60a5fa"
4456
+ }
4457
+ };
4458
+ function AppProvider({
4459
+ children,
4460
+ enableNavigation = true,
4461
+ enableOverlay = true,
4462
+ enableTheme = true,
4463
+ enableStatusBar = true,
4464
+ enableSafeArea = true,
4465
+ lightTheme = defaultLightTheme,
4466
+ darkTheme = defaultDarkTheme,
4467
+ defaultDark = false,
4468
+ isDark,
4469
+ statusBarProps,
4470
+ ...navigationProps
4471
+ }) {
4472
+ let content = children;
4473
+ if (enableOverlay) {
4474
+ content = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(OverlayProvider, { children: content });
4475
+ }
4476
+ if (enableNavigation) {
4477
+ content = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(NavigationProvider, { ...navigationProps, children: content });
4478
+ }
4479
+ if (enableTheme) {
4480
+ content = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ThemeProvider, { light: lightTheme, dark: darkTheme, defaultDark, isDark, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
4481
+ enableStatusBar && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(AppStatusBar, { testID: "status-bar", ...statusBarProps }),
4482
+ content
4483
+ ] }) });
4484
+ }
4485
+ if (enableSafeArea) {
4486
+ content = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native_safe_area_context4.SafeAreaProvider, { children: content });
4487
+ }
4488
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_jsx_runtime44.Fragment, { children: content });
4489
+ }
4490
+
4491
+ // src/overlay/loading/hooks.ts
4492
+ function useLoading() {
4493
+ return useLoadingContext();
4494
+ }
4495
+
4496
+ // src/overlay/toast/hooks.ts
4497
+ function useToast() {
4498
+ return useToastContext();
4499
+ }
4500
+
4501
+ // src/overlay/alert/hooks.ts
4502
+ function useAlert() {
4503
+ return useAlertContext();
4504
+ }
4505
+
4506
+ // src/index.ts
4507
+ var import_react_native_safe_area_context5 = require("react-native-safe-area-context");
4508
+ // Annotate the CommonJS export names for ESM import in node:
4509
+ 0 && (module.exports = {
4510
+ ActionIcons,
4511
+ Alert,
4512
+ AppButton,
4513
+ AppHeader,
4514
+ AppImage,
4515
+ AppInput,
4516
+ AppList,
4517
+ AppPressable,
4518
+ AppProvider,
4519
+ AppScrollView,
4520
+ AppStatusBar,
4521
+ AppText,
4522
+ AppView,
4523
+ BottomTabBar,
4524
+ Card,
4525
+ Center,
4526
+ Checkbox,
4527
+ CheckboxGroup,
4528
+ Col,
4529
+ DatePicker,
4530
+ DrawerContent,
4531
+ DrawerNavigator,
4532
+ ErrorCode,
4533
+ FileIcons,
4534
+ FormItem,
4535
+ Icon,
4536
+ Loading,
4537
+ MemoryStorage,
4538
+ NavigationContainer,
4539
+ NavigationIcons,
4540
+ NavigationProvider,
4541
+ OverlayProvider,
4542
+ Page,
4543
+ Progress,
4544
+ Radio,
4545
+ RadioGroup,
4546
+ Row,
4547
+ SafeAreaProvider,
4548
+ SafeBottom,
4549
+ SafeScreen,
4550
+ Select,
4551
+ Slider,
4552
+ StackNavigator,
4553
+ StatusIcons,
4554
+ Switch,
4555
+ TabNavigator,
4556
+ ThemeProvider,
4557
+ Toast,
4558
+ adjustBrightness,
4559
+ capitalize,
4560
+ clamp,
4561
+ clsx,
4562
+ cn,
4563
+ createAPI,
4564
+ createDrawerScreens,
4565
+ createNavigationTheme,
4566
+ createStackScreens,
4567
+ createTabScreens,
4568
+ createTheme,
4569
+ deepMerge,
4570
+ enhanceError,
4571
+ formatCurrency,
4572
+ formatDate,
4573
+ formatNumber,
4574
+ formatPercent,
4575
+ formatRelativeTime,
4576
+ generateColorPalette,
4577
+ getThemeColors,
4578
+ getValidationErrors,
4579
+ hexToRgb,
4580
+ isDevelopment,
4581
+ isValidEmail,
4582
+ isValidPhone,
4583
+ mapHttpStatus,
4584
+ omit,
4585
+ pick,
4586
+ rgbToHex,
4587
+ slugify,
4588
+ storage,
4589
+ truncate,
4590
+ twMerge,
4591
+ useAlert,
4592
+ useAsyncState,
4593
+ useBackHandler,
4594
+ useDebounce,
4595
+ useDimensions,
4596
+ useDrawerNavigation,
4597
+ useFocusEffect,
4598
+ useForm,
4599
+ useInfinite,
4600
+ useIsFocused,
4601
+ useKeyboard,
4602
+ useLoading,
4603
+ useMemoizedFn,
4604
+ useMutation,
4605
+ useNavigation,
4606
+ useNavigationState,
4607
+ useOrientation,
4608
+ usePagination,
4609
+ usePrevious,
4610
+ useQuery,
4611
+ useRefresh,
4612
+ useRequest,
4613
+ useRoute,
4614
+ useScrollToTop,
4615
+ useSetState,
4616
+ useStackNavigation,
4617
+ useStorage,
4618
+ useTabNavigation,
4619
+ useTheme,
4620
+ useThemeColors,
4621
+ useThrottle,
4622
+ useToast,
4623
+ useToggle,
4624
+ useUpdateEffect,
4625
+ z
4626
+ });
4627
+ //# sourceMappingURL=index.js.map