@gaozh1024/rn-kit 0.3.4 → 0.4.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/README.md +272 -3
- package/dist/index.d.mts +249 -12
- package/dist/index.d.ts +249 -12
- package/dist/index.js +2419 -798
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2351 -739
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -250,6 +250,11 @@ import { useMemo as useMemo2 } from "react";
|
|
|
250
250
|
function getThemeColors(theme, isDark) {
|
|
251
251
|
return {
|
|
252
252
|
primary: theme.colors.primary?.[500] || "#f38b32",
|
|
253
|
+
success: theme.colors.success?.[500] || "#22c55e",
|
|
254
|
+
warning: theme.colors.warning?.[500] || "#f59e0b",
|
|
255
|
+
error: theme.colors.error?.[500] || "#ef4444",
|
|
256
|
+
info: theme.colors.info?.[500] || theme.colors.secondary?.[500] || "#3b82f6",
|
|
257
|
+
muted: isDark ? "#9ca3af" : "#6b7280",
|
|
253
258
|
primarySurface: isDark ? theme.colors.primary?.[900] || "#7c2d12" : theme.colors.primary?.[50] || "#fff7ed",
|
|
254
259
|
background: theme.colors.background?.[500] || (isDark ? "#0a0a0a" : "#ffffff"),
|
|
255
260
|
card: theme.colors.card?.[500] || (isDark ? "#1f2937" : "#ffffff"),
|
|
@@ -346,6 +351,189 @@ function useAsyncState() {
|
|
|
346
351
|
|
|
347
352
|
// src/core/api/create-api.ts
|
|
348
353
|
import { ZodError } from "zod";
|
|
354
|
+
|
|
355
|
+
// src/core/logger/utils.ts
|
|
356
|
+
var LOG_LEVEL_WEIGHT = {
|
|
357
|
+
debug: 10,
|
|
358
|
+
info: 20,
|
|
359
|
+
warn: 30,
|
|
360
|
+
error: 40
|
|
361
|
+
};
|
|
362
|
+
function shouldLog(currentLevel, nextLevel) {
|
|
363
|
+
return LOG_LEVEL_WEIGHT[nextLevel] >= LOG_LEVEL_WEIGHT[currentLevel];
|
|
364
|
+
}
|
|
365
|
+
function createLogEntry(input) {
|
|
366
|
+
return {
|
|
367
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
368
|
+
level: input.level,
|
|
369
|
+
message: input.message,
|
|
370
|
+
data: input.data,
|
|
371
|
+
namespace: input.namespace,
|
|
372
|
+
timestamp: Date.now(),
|
|
373
|
+
source: input.source ?? "logger"
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function formatLogTime(timestamp) {
|
|
377
|
+
const date = new Date(timestamp);
|
|
378
|
+
const hh = String(date.getHours()).padStart(2, "0");
|
|
379
|
+
const mm = String(date.getMinutes()).padStart(2, "0");
|
|
380
|
+
const ss = String(date.getSeconds()).padStart(2, "0");
|
|
381
|
+
return `${hh}:${mm}:${ss}`;
|
|
382
|
+
}
|
|
383
|
+
function stringifyLogData(data) {
|
|
384
|
+
if (data === void 0) return "";
|
|
385
|
+
if (typeof data === "string") return data;
|
|
386
|
+
try {
|
|
387
|
+
return JSON.stringify(data, null, 2);
|
|
388
|
+
} catch {
|
|
389
|
+
return String(data);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function normalizeConsoleArgs(args) {
|
|
393
|
+
if (args.length === 0) {
|
|
394
|
+
return { message: "", data: void 0 };
|
|
395
|
+
}
|
|
396
|
+
const [first, ...rest] = args;
|
|
397
|
+
if (typeof first === "string") {
|
|
398
|
+
return {
|
|
399
|
+
message: first,
|
|
400
|
+
data: rest.length === 0 ? void 0 : rest.length === 1 ? rest[0] : rest
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
message: stringifyLogData(first),
|
|
405
|
+
data: args.length === 1 ? first : args
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function serializeLogEntries(entries) {
|
|
409
|
+
return JSON.stringify(
|
|
410
|
+
entries.map((entry) => ({
|
|
411
|
+
id: entry.id,
|
|
412
|
+
level: entry.level,
|
|
413
|
+
message: entry.message,
|
|
414
|
+
namespace: entry.namespace,
|
|
415
|
+
timestamp: entry.timestamp,
|
|
416
|
+
source: entry.source,
|
|
417
|
+
data: entry.data
|
|
418
|
+
})),
|
|
419
|
+
null,
|
|
420
|
+
2
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/core/logger/transports.ts
|
|
425
|
+
var ANSI_COLORS = {
|
|
426
|
+
debug: "\x1B[90m",
|
|
427
|
+
info: "\x1B[36m",
|
|
428
|
+
warn: "\x1B[33m",
|
|
429
|
+
error: "\x1B[31m",
|
|
430
|
+
reset: "\x1B[0m"
|
|
431
|
+
};
|
|
432
|
+
function supportsAnsiColors() {
|
|
433
|
+
return typeof process !== "undefined" && Boolean(process.stdout?.isTTY);
|
|
434
|
+
}
|
|
435
|
+
function resolveConsoleMethod(consoleRef, entry) {
|
|
436
|
+
const target = consoleRef ?? console;
|
|
437
|
+
if (entry.level === "debug" && typeof target.debug === "function") return target.debug.bind(target);
|
|
438
|
+
if (entry.level === "info" && typeof target.info === "function") return target.info.bind(target);
|
|
439
|
+
if (entry.level === "warn" && typeof target.warn === "function") return target.warn.bind(target);
|
|
440
|
+
if (entry.level === "error" && typeof target.error === "function") return target.error.bind(target);
|
|
441
|
+
return target.log.bind(target);
|
|
442
|
+
}
|
|
443
|
+
function createConsoleTransport(options = {}) {
|
|
444
|
+
const useAnsiColors = options.useAnsiColors ?? supportsAnsiColors();
|
|
445
|
+
return (entry) => {
|
|
446
|
+
if (entry.source === "console") return;
|
|
447
|
+
const method = resolveConsoleMethod(options.consoleRef, entry);
|
|
448
|
+
const time = formatLogTime(entry.timestamp);
|
|
449
|
+
const basePrefix = `[${time}] [${entry.level.toUpperCase()}]${entry.namespace ? ` [${entry.namespace}]` : ""}`;
|
|
450
|
+
const prefix = useAnsiColors ? `${ANSI_COLORS[entry.level]}${basePrefix}${ANSI_COLORS.reset}` : basePrefix;
|
|
451
|
+
if (entry.data === void 0) {
|
|
452
|
+
method(`${prefix} ${entry.message}`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
method(`${prefix} ${entry.message}`, entry.data);
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/core/logger/registry.ts
|
|
460
|
+
var globalLogger = null;
|
|
461
|
+
function setGlobalLogger(logger) {
|
|
462
|
+
globalLogger = logger;
|
|
463
|
+
}
|
|
464
|
+
function getGlobalLogger() {
|
|
465
|
+
return globalLogger;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/core/api/observability.ts
|
|
469
|
+
function defaultSanitize(value, sanitize, stage, field) {
|
|
470
|
+
if (!sanitize) return value;
|
|
471
|
+
return sanitize(value, { stage, field });
|
|
472
|
+
}
|
|
473
|
+
function resolveMessage(event) {
|
|
474
|
+
if (event.stage === "request") {
|
|
475
|
+
return `API Request ${event.method} ${event.path}`;
|
|
476
|
+
}
|
|
477
|
+
if (event.stage === "response") {
|
|
478
|
+
return `API Response ${event.method} ${event.path} ${event.statusCode ?? "-"}`;
|
|
479
|
+
}
|
|
480
|
+
return `API Error ${event.method} ${event.path}`;
|
|
481
|
+
}
|
|
482
|
+
function resolveLevel(error) {
|
|
483
|
+
if (!error) return "info";
|
|
484
|
+
if (error.code === "VALIDATION" || error.code === "BUSINESS") return "warn";
|
|
485
|
+
return "error";
|
|
486
|
+
}
|
|
487
|
+
function createApiLoggerTransport(config = {}) {
|
|
488
|
+
return (event) => {
|
|
489
|
+
const logger = config.logger ?? getGlobalLogger();
|
|
490
|
+
if (!logger) return;
|
|
491
|
+
const data = {
|
|
492
|
+
endpointName: event.endpointName,
|
|
493
|
+
method: event.method,
|
|
494
|
+
path: event.path,
|
|
495
|
+
url: event.url,
|
|
496
|
+
statusCode: event.statusCode,
|
|
497
|
+
durationMs: event.durationMs,
|
|
498
|
+
input: config.includeInput || event.stage !== "request" ? defaultSanitize(event.input, config.sanitize, event.stage, "input") : void 0,
|
|
499
|
+
responseData: config.includeResponseData ? defaultSanitize(event.responseData, config.sanitize, event.stage, "responseData") : void 0,
|
|
500
|
+
error: event.error ? defaultSanitize(
|
|
501
|
+
{
|
|
502
|
+
code: event.error.code,
|
|
503
|
+
message: event.error.message,
|
|
504
|
+
statusCode: event.error.statusCode,
|
|
505
|
+
retryable: event.error.retryable
|
|
506
|
+
},
|
|
507
|
+
config.sanitize,
|
|
508
|
+
event.stage,
|
|
509
|
+
"error"
|
|
510
|
+
) : void 0
|
|
511
|
+
};
|
|
512
|
+
const namespace = config.namespace ?? "api";
|
|
513
|
+
const message = resolveMessage(event);
|
|
514
|
+
if (event.stage === "request") {
|
|
515
|
+
logger.debug(message, data, namespace);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (event.stage === "response") {
|
|
519
|
+
logger.info(message, data, namespace);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
logger[resolveLevel(event.error)](message, data, namespace);
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function resolveApiObservability(config) {
|
|
526
|
+
const enabled = config?.enabled ?? isDevelopment();
|
|
527
|
+
if (!enabled) {
|
|
528
|
+
return { enabled, transports: [] };
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
enabled,
|
|
532
|
+
transports: [createApiLoggerTransport(config), ...config?.transports ?? []]
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/core/api/create-api.ts
|
|
349
537
|
function parseZodError(error) {
|
|
350
538
|
const first = error.errors[0];
|
|
351
539
|
return {
|
|
@@ -398,6 +586,7 @@ async function notifyError(error, context, endpoint, config) {
|
|
|
398
586
|
function createAPI(config) {
|
|
399
587
|
const endpoints = {};
|
|
400
588
|
const fetcher = config.fetcher ?? fetch;
|
|
589
|
+
const observability = resolveApiObservability(config.observability);
|
|
401
590
|
for (const [name, ep] of Object.entries(config.endpoints)) {
|
|
402
591
|
endpoints[name] = async (input) => {
|
|
403
592
|
const context = {
|
|
@@ -413,11 +602,43 @@ function createAPI(config) {
|
|
|
413
602
|
}
|
|
414
603
|
}
|
|
415
604
|
const url = config.baseURL + ep.path;
|
|
605
|
+
const startAt = Date.now();
|
|
606
|
+
if (observability.enabled) {
|
|
607
|
+
await Promise.all(
|
|
608
|
+
observability.transports.map(
|
|
609
|
+
(transport) => transport({
|
|
610
|
+
stage: "request",
|
|
611
|
+
endpointName: name,
|
|
612
|
+
path: ep.path,
|
|
613
|
+
method: ep.method,
|
|
614
|
+
url,
|
|
615
|
+
input
|
|
616
|
+
})
|
|
617
|
+
)
|
|
618
|
+
);
|
|
619
|
+
}
|
|
416
620
|
let response;
|
|
417
621
|
try {
|
|
418
622
|
response = await fetcher(url, { method: ep.method });
|
|
419
623
|
} catch (error) {
|
|
420
|
-
|
|
624
|
+
const enhanced = await notifyError(parseNetworkError(error), context, ep, config);
|
|
625
|
+
if (observability.enabled) {
|
|
626
|
+
await Promise.all(
|
|
627
|
+
observability.transports.map(
|
|
628
|
+
(transport) => transport({
|
|
629
|
+
stage: "error",
|
|
630
|
+
endpointName: name,
|
|
631
|
+
path: ep.path,
|
|
632
|
+
method: ep.method,
|
|
633
|
+
url,
|
|
634
|
+
input,
|
|
635
|
+
durationMs: Date.now() - startAt,
|
|
636
|
+
error: enhanced
|
|
637
|
+
})
|
|
638
|
+
)
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
throw enhanced;
|
|
421
642
|
}
|
|
422
643
|
context.response = response;
|
|
423
644
|
let data = void 0;
|
|
@@ -432,18 +653,115 @@ function createAPI(config) {
|
|
|
432
653
|
}
|
|
433
654
|
context.responseData = data;
|
|
434
655
|
if (!response.ok) {
|
|
435
|
-
|
|
656
|
+
const enhanced = await notifyError(parseHttpError(response, data), context, ep, config);
|
|
657
|
+
if (observability.enabled) {
|
|
658
|
+
await Promise.all(
|
|
659
|
+
observability.transports.map(
|
|
660
|
+
(transport) => transport({
|
|
661
|
+
stage: "error",
|
|
662
|
+
endpointName: name,
|
|
663
|
+
path: ep.path,
|
|
664
|
+
method: ep.method,
|
|
665
|
+
url,
|
|
666
|
+
input,
|
|
667
|
+
response,
|
|
668
|
+
responseData: data,
|
|
669
|
+
statusCode: response.status,
|
|
670
|
+
durationMs: Date.now() - startAt,
|
|
671
|
+
error: enhanced
|
|
672
|
+
})
|
|
673
|
+
)
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
throw enhanced;
|
|
436
677
|
}
|
|
437
678
|
const businessError = ep.parseBusinessError?.(data, response) ?? config.parseBusinessError?.(data, response);
|
|
438
679
|
if (businessError) {
|
|
439
|
-
|
|
680
|
+
const enhanced = await notifyError(businessError, context, ep, config);
|
|
681
|
+
if (observability.enabled) {
|
|
682
|
+
await Promise.all(
|
|
683
|
+
observability.transports.map(
|
|
684
|
+
(transport) => transport({
|
|
685
|
+
stage: "error",
|
|
686
|
+
endpointName: name,
|
|
687
|
+
path: ep.path,
|
|
688
|
+
method: ep.method,
|
|
689
|
+
url,
|
|
690
|
+
input,
|
|
691
|
+
response,
|
|
692
|
+
responseData: data,
|
|
693
|
+
statusCode: response.status,
|
|
694
|
+
durationMs: Date.now() - startAt,
|
|
695
|
+
error: enhanced
|
|
696
|
+
})
|
|
697
|
+
)
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
throw enhanced;
|
|
440
701
|
}
|
|
441
702
|
try {
|
|
442
703
|
if (ep.output) {
|
|
443
|
-
|
|
704
|
+
const parsed = ep.output.parse(data);
|
|
705
|
+
if (observability.enabled) {
|
|
706
|
+
await Promise.all(
|
|
707
|
+
observability.transports.map(
|
|
708
|
+
(transport) => transport({
|
|
709
|
+
stage: "response",
|
|
710
|
+
endpointName: name,
|
|
711
|
+
path: ep.path,
|
|
712
|
+
method: ep.method,
|
|
713
|
+
url,
|
|
714
|
+
input,
|
|
715
|
+
response,
|
|
716
|
+
responseData: parsed,
|
|
717
|
+
statusCode: response.status,
|
|
718
|
+
durationMs: Date.now() - startAt
|
|
719
|
+
})
|
|
720
|
+
)
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
return parsed;
|
|
444
724
|
}
|
|
445
725
|
} catch (error) {
|
|
446
|
-
|
|
726
|
+
const enhanced = await notifyError(parseUnknownError(error), context, ep, config);
|
|
727
|
+
if (observability.enabled) {
|
|
728
|
+
await Promise.all(
|
|
729
|
+
observability.transports.map(
|
|
730
|
+
(transport) => transport({
|
|
731
|
+
stage: "error",
|
|
732
|
+
endpointName: name,
|
|
733
|
+
path: ep.path,
|
|
734
|
+
method: ep.method,
|
|
735
|
+
url,
|
|
736
|
+
input,
|
|
737
|
+
response,
|
|
738
|
+
responseData: data,
|
|
739
|
+
statusCode: response.status,
|
|
740
|
+
durationMs: Date.now() - startAt,
|
|
741
|
+
error: enhanced
|
|
742
|
+
})
|
|
743
|
+
)
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
throw enhanced;
|
|
747
|
+
}
|
|
748
|
+
if (observability.enabled) {
|
|
749
|
+
await Promise.all(
|
|
750
|
+
observability.transports.map(
|
|
751
|
+
(transport) => transport({
|
|
752
|
+
stage: "response",
|
|
753
|
+
endpointName: name,
|
|
754
|
+
path: ep.path,
|
|
755
|
+
method: ep.method,
|
|
756
|
+
url,
|
|
757
|
+
input,
|
|
758
|
+
response,
|
|
759
|
+
responseData: data,
|
|
760
|
+
statusCode: response.status,
|
|
761
|
+
durationMs: Date.now() - startAt
|
|
762
|
+
})
|
|
763
|
+
)
|
|
764
|
+
);
|
|
447
765
|
}
|
|
448
766
|
return data;
|
|
449
767
|
};
|
|
@@ -1037,7 +1355,7 @@ function AppView({
|
|
|
1037
1355
|
}
|
|
1038
1356
|
|
|
1039
1357
|
// src/ui/primitives/AppScrollView.tsx
|
|
1040
|
-
import { ScrollView } from "react-native";
|
|
1358
|
+
import { Keyboard, ScrollView, TouchableWithoutFeedback } from "react-native";
|
|
1041
1359
|
import { jsx as jsx3 } from "nativewind/jsx-runtime";
|
|
1042
1360
|
function AppScrollView({
|
|
1043
1361
|
flex,
|
|
@@ -1049,6 +1367,7 @@ function AppScrollView({
|
|
|
1049
1367
|
surface,
|
|
1050
1368
|
rounded,
|
|
1051
1369
|
className,
|
|
1370
|
+
dismissKeyboardOnPressOutside = false,
|
|
1052
1371
|
children,
|
|
1053
1372
|
style,
|
|
1054
1373
|
...props
|
|
@@ -1056,7 +1375,7 @@ function AppScrollView({
|
|
|
1056
1375
|
const { theme, isDark } = useOptionalTheme();
|
|
1057
1376
|
const resolvedBgColor = resolveSurfaceColor(surface, theme, isDark) ?? resolveNamedColor(bg, theme, isDark);
|
|
1058
1377
|
const shouldUseClassBg = !!bg && !resolvedBgColor;
|
|
1059
|
-
|
|
1378
|
+
const content = /* @__PURE__ */ jsx3(
|
|
1060
1379
|
ScrollView,
|
|
1061
1380
|
{
|
|
1062
1381
|
className: cn(
|
|
@@ -1069,11 +1388,16 @@ function AppScrollView({
|
|
|
1069
1388
|
rounded && `rounded-${rounded}`,
|
|
1070
1389
|
className
|
|
1071
1390
|
),
|
|
1072
|
-
style: [resolvedBgColor ? { backgroundColor: resolvedBgColor } : void 0, style],
|
|
1073
1391
|
...props,
|
|
1392
|
+
style: [resolvedBgColor ? { backgroundColor: resolvedBgColor } : void 0, style],
|
|
1393
|
+
keyboardShouldPersistTaps: dismissKeyboardOnPressOutside ? props.keyboardShouldPersistTaps ?? "handled" : props.keyboardShouldPersistTaps,
|
|
1074
1394
|
children
|
|
1075
1395
|
}
|
|
1076
1396
|
);
|
|
1397
|
+
if (!dismissKeyboardOnPressOutside) {
|
|
1398
|
+
return content;
|
|
1399
|
+
}
|
|
1400
|
+
return /* @__PURE__ */ jsx3(TouchableWithoutFeedback, { onPress: Keyboard.dismiss, accessible: false, children: content });
|
|
1077
1401
|
}
|
|
1078
1402
|
|
|
1079
1403
|
// src/ui/primitives/AppText.tsx
|
|
@@ -1156,8 +1480,19 @@ function AppPressable({
|
|
|
1156
1480
|
);
|
|
1157
1481
|
}
|
|
1158
1482
|
|
|
1159
|
-
// src/ui/
|
|
1483
|
+
// src/ui/primitives/KeyboardDismissView.tsx
|
|
1484
|
+
import { Keyboard as Keyboard2, TouchableWithoutFeedback as TouchableWithoutFeedback2 } from "react-native";
|
|
1160
1485
|
import { jsx as jsx6 } from "nativewind/jsx-runtime";
|
|
1486
|
+
function KeyboardDismissView({ enabled = true, children, ...props }) {
|
|
1487
|
+
const content = /* @__PURE__ */ jsx6(AppView, { ...props, children });
|
|
1488
|
+
if (!enabled) {
|
|
1489
|
+
return content;
|
|
1490
|
+
}
|
|
1491
|
+
return /* @__PURE__ */ jsx6(TouchableWithoutFeedback2, { onPress: Keyboard2.dismiss, accessible: false, children: content });
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// src/ui/layout/Row.tsx
|
|
1495
|
+
import { jsx as jsx7 } from "nativewind/jsx-runtime";
|
|
1161
1496
|
var justifyMap = {
|
|
1162
1497
|
start: "justify-start",
|
|
1163
1498
|
center: "justify-center",
|
|
@@ -1172,11 +1507,11 @@ var itemsMap = {
|
|
|
1172
1507
|
stretch: "items-stretch"
|
|
1173
1508
|
};
|
|
1174
1509
|
function Row({ justify = "start", items = "center", className, ...props }) {
|
|
1175
|
-
return /* @__PURE__ */
|
|
1510
|
+
return /* @__PURE__ */ jsx7(AppView, { row: true, className: cn(justifyMap[justify], itemsMap[items], className), ...props });
|
|
1176
1511
|
}
|
|
1177
1512
|
|
|
1178
1513
|
// src/ui/layout/Col.tsx
|
|
1179
|
-
import { jsx as
|
|
1514
|
+
import { jsx as jsx8 } from "nativewind/jsx-runtime";
|
|
1180
1515
|
var justifyMap2 = {
|
|
1181
1516
|
start: "justify-start",
|
|
1182
1517
|
center: "justify-center",
|
|
@@ -1191,19 +1526,19 @@ var itemsMap2 = {
|
|
|
1191
1526
|
stretch: "items-stretch"
|
|
1192
1527
|
};
|
|
1193
1528
|
function Col({ justify = "start", items = "stretch", className, ...props }) {
|
|
1194
|
-
return /* @__PURE__ */
|
|
1529
|
+
return /* @__PURE__ */ jsx8(AppView, { className: cn(justifyMap2[justify], itemsMap2[items], className), ...props });
|
|
1195
1530
|
}
|
|
1196
1531
|
|
|
1197
1532
|
// src/ui/layout/Center.tsx
|
|
1198
|
-
import { jsx as
|
|
1533
|
+
import { jsx as jsx9 } from "nativewind/jsx-runtime";
|
|
1199
1534
|
function Center({ flex = true, ...props }) {
|
|
1200
|
-
return /* @__PURE__ */
|
|
1535
|
+
return /* @__PURE__ */ jsx9(AppView, { center: true, flex, ...props });
|
|
1201
1536
|
}
|
|
1202
1537
|
|
|
1203
1538
|
// src/ui/layout/SafeScreen.tsx
|
|
1204
|
-
import { View as View2, StyleSheet as StyleSheet2 } from "react-native";
|
|
1539
|
+
import { Keyboard as Keyboard3, TouchableWithoutFeedback as TouchableWithoutFeedback3, View as View2, StyleSheet as StyleSheet2 } from "react-native";
|
|
1205
1540
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
1206
|
-
import { jsx as
|
|
1541
|
+
import { jsx as jsx10 } from "nativewind/jsx-runtime";
|
|
1207
1542
|
function SafeScreen({
|
|
1208
1543
|
top = true,
|
|
1209
1544
|
bottom = true,
|
|
@@ -1212,6 +1547,7 @@ function SafeScreen({
|
|
|
1212
1547
|
bg,
|
|
1213
1548
|
surface,
|
|
1214
1549
|
flex = true,
|
|
1550
|
+
dismissKeyboardOnPressOutside = false,
|
|
1215
1551
|
className,
|
|
1216
1552
|
children,
|
|
1217
1553
|
style,
|
|
@@ -1221,7 +1557,7 @@ function SafeScreen({
|
|
|
1221
1557
|
const { theme, isDark } = useOptionalTheme();
|
|
1222
1558
|
const resolvedBgColor = resolveSurfaceColor(surface, theme, isDark) ?? resolveNamedColor(bg, theme, isDark);
|
|
1223
1559
|
const shouldUseClassBg = !!bg && !resolvedBgColor;
|
|
1224
|
-
|
|
1560
|
+
const content = /* @__PURE__ */ jsx10(
|
|
1225
1561
|
View2,
|
|
1226
1562
|
{
|
|
1227
1563
|
className: cn(flex && "flex-1", shouldUseClassBg && `bg-${bg}`, className),
|
|
@@ -1240,6 +1576,10 @@ function SafeScreen({
|
|
|
1240
1576
|
children
|
|
1241
1577
|
}
|
|
1242
1578
|
);
|
|
1579
|
+
if (!dismissKeyboardOnPressOutside) {
|
|
1580
|
+
return content;
|
|
1581
|
+
}
|
|
1582
|
+
return /* @__PURE__ */ jsx10(TouchableWithoutFeedback3, { onPress: Keyboard3.dismiss, accessible: false, children: content });
|
|
1243
1583
|
}
|
|
1244
1584
|
var styles = StyleSheet2.create({
|
|
1245
1585
|
flex: {
|
|
@@ -1251,19 +1591,20 @@ function AppScreen({
|
|
|
1251
1591
|
className,
|
|
1252
1592
|
...props
|
|
1253
1593
|
}) {
|
|
1254
|
-
return /* @__PURE__ */
|
|
1594
|
+
return /* @__PURE__ */ jsx10(SafeScreen, { flex: true, surface: "background", ...props, className, children });
|
|
1255
1595
|
}
|
|
1256
1596
|
function SafeBottom({
|
|
1257
1597
|
children,
|
|
1258
1598
|
className,
|
|
1259
1599
|
...props
|
|
1260
1600
|
}) {
|
|
1261
|
-
return /* @__PURE__ */
|
|
1601
|
+
return /* @__PURE__ */ jsx10(SafeScreen, { bottom: true, left: true, right: true, flex: false, ...props, className, children });
|
|
1262
1602
|
}
|
|
1263
1603
|
|
|
1264
1604
|
// src/ui/actions/AppButton.tsx
|
|
1265
|
-
import {
|
|
1266
|
-
import {
|
|
1605
|
+
import { useCallback as useCallback9 } from "react";
|
|
1606
|
+
import { ActivityIndicator, Keyboard as Keyboard4 } from "react-native";
|
|
1607
|
+
import { jsx as jsx11 } from "nativewind/jsx-runtime";
|
|
1267
1608
|
function AppButton({
|
|
1268
1609
|
variant = "solid",
|
|
1269
1610
|
size = "md",
|
|
@@ -1271,6 +1612,7 @@ function AppButton({
|
|
|
1271
1612
|
loading,
|
|
1272
1613
|
disabled,
|
|
1273
1614
|
onPress,
|
|
1615
|
+
dismissKeyboardOnPress = true,
|
|
1274
1616
|
children,
|
|
1275
1617
|
className
|
|
1276
1618
|
}) {
|
|
@@ -1291,11 +1633,17 @@ function AppButton({
|
|
|
1291
1633
|
const ghostBackgroundColor = isDark ? "rgba(255,255,255,0.04)" : "transparent";
|
|
1292
1634
|
const loadingColor = variant === "solid" ? "white" : buttonColors[color];
|
|
1293
1635
|
const textColor = variant === "solid" ? "#ffffff" : variant === "ghost" ? ghostTextColor : buttonColors[color];
|
|
1636
|
+
const handlePress = useCallback9(() => {
|
|
1637
|
+
if (dismissKeyboardOnPress) {
|
|
1638
|
+
Keyboard4.dismiss();
|
|
1639
|
+
}
|
|
1640
|
+
onPress?.();
|
|
1641
|
+
}, [dismissKeyboardOnPress, onPress]);
|
|
1294
1642
|
const buttonStyle = variant === "solid" ? { backgroundColor: buttonColors[color] } : variant === "outline" ? { borderWidth: 0.5, borderColor: buttonColors[color], backgroundColor: "transparent" } : { backgroundColor: ghostBackgroundColor };
|
|
1295
|
-
return /* @__PURE__ */
|
|
1643
|
+
return /* @__PURE__ */ jsx11(
|
|
1296
1644
|
AppPressable,
|
|
1297
1645
|
{
|
|
1298
|
-
onPress,
|
|
1646
|
+
onPress: onPress ? handlePress : void 0,
|
|
1299
1647
|
disabled: isDisabled,
|
|
1300
1648
|
className: cn(
|
|
1301
1649
|
"flex-row items-center justify-center rounded-lg",
|
|
@@ -1304,30 +1652,61 @@ function AppButton({
|
|
|
1304
1652
|
className
|
|
1305
1653
|
),
|
|
1306
1654
|
style: buttonStyle,
|
|
1307
|
-
children: loading ? /* @__PURE__ */
|
|
1655
|
+
children: loading ? /* @__PURE__ */ jsx11(ActivityIndicator, { size: "small", color: loadingColor }) : /* @__PURE__ */ jsx11(AppText, { weight: "semibold", style: { color: textColor }, children })
|
|
1308
1656
|
}
|
|
1309
1657
|
);
|
|
1310
1658
|
}
|
|
1311
1659
|
|
|
1312
1660
|
// src/ui/feedback/Toast.tsx
|
|
1313
|
-
import { jsx as
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
error: "bg-red-500",
|
|
1317
|
-
warning: "bg-yellow-500",
|
|
1318
|
-
info: "bg-blue-500"
|
|
1319
|
-
};
|
|
1320
|
-
function Toast({ message, type = "info", visible = true }) {
|
|
1661
|
+
import { jsx as jsx12 } from "nativewind/jsx-runtime";
|
|
1662
|
+
function Toast({ message, type = "info", visible = true, testID }) {
|
|
1663
|
+
const { theme } = useOptionalTheme();
|
|
1321
1664
|
if (!visible) return null;
|
|
1322
|
-
|
|
1665
|
+
const palette = {
|
|
1666
|
+
success: {
|
|
1667
|
+
backgroundColor: theme.colors.success?.[500] || "#22c55e",
|
|
1668
|
+
textColor: "#ffffff"
|
|
1669
|
+
},
|
|
1670
|
+
error: {
|
|
1671
|
+
backgroundColor: theme.colors.error?.[500] || "#ef4444",
|
|
1672
|
+
textColor: "#ffffff"
|
|
1673
|
+
},
|
|
1674
|
+
warning: {
|
|
1675
|
+
backgroundColor: theme.colors.warning?.[500] || "#f59e0b",
|
|
1676
|
+
textColor: "#111827"
|
|
1677
|
+
},
|
|
1678
|
+
info: {
|
|
1679
|
+
backgroundColor: theme.colors.info?.[500] || theme.colors.primary?.[500] || "#3b82f6",
|
|
1680
|
+
textColor: "#ffffff"
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
const currentPalette = palette[type];
|
|
1684
|
+
return /* @__PURE__ */ jsx12(
|
|
1685
|
+
AppView,
|
|
1686
|
+
{
|
|
1687
|
+
testID,
|
|
1688
|
+
className: "px-4 py-3 rounded-lg",
|
|
1689
|
+
style: { backgroundColor: currentPalette.backgroundColor },
|
|
1690
|
+
children: /* @__PURE__ */ jsx12(AppText, { style: { color: currentPalette.textColor }, children: message })
|
|
1691
|
+
}
|
|
1692
|
+
);
|
|
1323
1693
|
}
|
|
1324
1694
|
|
|
1325
1695
|
// src/ui/feedback/Alert.tsx
|
|
1326
|
-
import { useCallback as
|
|
1327
|
-
import { Modal, TouchableOpacity, StyleSheet as StyleSheet3 } from "react-native";
|
|
1328
|
-
import { jsx as
|
|
1696
|
+
import { useCallback as useCallback10, useEffect as useEffect5, useRef as useRef6 } from "react";
|
|
1697
|
+
import { Modal, TouchableOpacity, StyleSheet as StyleSheet3, Animated } from "react-native";
|
|
1698
|
+
import { jsx as jsx13, jsxs } from "nativewind/jsx-runtime";
|
|
1699
|
+
function createAnimatedValue(value) {
|
|
1700
|
+
const AnimatedValue = Animated.Value;
|
|
1701
|
+
try {
|
|
1702
|
+
return new AnimatedValue(value);
|
|
1703
|
+
} catch {
|
|
1704
|
+
return AnimatedValue(value);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1329
1707
|
function Alert({ visible, title, message, buttons, onClose }) {
|
|
1330
1708
|
const { theme, isDark } = useTheme();
|
|
1709
|
+
const progress = useRef6(createAnimatedValue(0)).current;
|
|
1331
1710
|
const modalBgColor = isDark ? "#1f2937" : "#ffffff";
|
|
1332
1711
|
const textColor = isDark ? "#ffffff" : "#1f2937";
|
|
1333
1712
|
const messageColor = isDark ? "#9ca3af" : "#6b7280";
|
|
@@ -1335,7 +1714,19 @@ function Alert({ visible, title, message, buttons, onClose }) {
|
|
|
1335
1714
|
const cancelButtonBg = isDark ? "#374151" : "#f3f4f6";
|
|
1336
1715
|
const cancelButtonText = isDark ? "#ffffff" : "#374151";
|
|
1337
1716
|
const destructiveColor = theme.colors.error?.[500] || "#ef4444";
|
|
1338
|
-
|
|
1717
|
+
useEffect5(() => {
|
|
1718
|
+
if (!visible) {
|
|
1719
|
+
progress.setValue(0);
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
progress.setValue(0);
|
|
1723
|
+
Animated.timing(progress, {
|
|
1724
|
+
toValue: 1,
|
|
1725
|
+
duration: 220,
|
|
1726
|
+
useNativeDriver: true
|
|
1727
|
+
}).start();
|
|
1728
|
+
}, [progress, visible]);
|
|
1729
|
+
const handleButtonPress = useCallback10(
|
|
1339
1730
|
(button) => (e) => {
|
|
1340
1731
|
e.stopPropagation();
|
|
1341
1732
|
button.onPress?.();
|
|
@@ -1372,104 +1763,137 @@ function Alert({ visible, title, message, buttons, onClose }) {
|
|
|
1372
1763
|
}
|
|
1373
1764
|
return "#ffffff";
|
|
1374
1765
|
};
|
|
1375
|
-
return /* @__PURE__ */
|
|
1766
|
+
return /* @__PURE__ */ jsx13(
|
|
1376
1767
|
Modal,
|
|
1377
1768
|
{
|
|
1378
1769
|
visible,
|
|
1379
1770
|
transparent: true,
|
|
1380
|
-
animationType: "
|
|
1771
|
+
animationType: "none",
|
|
1381
1772
|
onRequestClose: onClose,
|
|
1382
1773
|
statusBarTranslucent: true,
|
|
1383
|
-
children: /* @__PURE__ */
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
{
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1774
|
+
children: /* @__PURE__ */ jsxs(AppView, { className: "flex-1", center: true, children: [
|
|
1775
|
+
/* @__PURE__ */ jsx13(
|
|
1776
|
+
Animated.View,
|
|
1777
|
+
{
|
|
1778
|
+
style: [
|
|
1779
|
+
StyleSheet3.absoluteFillObject,
|
|
1780
|
+
{
|
|
1781
|
+
backgroundColor: "rgba(0,0,0,0.5)",
|
|
1782
|
+
opacity: progress
|
|
1783
|
+
}
|
|
1784
|
+
]
|
|
1785
|
+
}
|
|
1786
|
+
),
|
|
1787
|
+
/* @__PURE__ */ jsxs(
|
|
1788
|
+
Animated.View,
|
|
1789
|
+
{
|
|
1790
|
+
className: "rounded-xl mx-8 min-w-[280px]",
|
|
1791
|
+
style: [
|
|
1792
|
+
{
|
|
1793
|
+
backgroundColor: modalBgColor,
|
|
1794
|
+
opacity: progress,
|
|
1795
|
+
transform: [
|
|
1796
|
+
{
|
|
1797
|
+
translateY: progress.interpolate({
|
|
1798
|
+
inputRange: [0, 1],
|
|
1799
|
+
outputRange: [16, 0]
|
|
1800
|
+
})
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
scale: progress.interpolate({
|
|
1804
|
+
inputRange: [0, 1],
|
|
1805
|
+
outputRange: [0.96, 1]
|
|
1806
|
+
})
|
|
1807
|
+
}
|
|
1808
|
+
]
|
|
1809
|
+
}
|
|
1810
|
+
],
|
|
1811
|
+
children: [
|
|
1812
|
+
/* @__PURE__ */ jsxs(AppView, { className: "px-6 py-5", children: [
|
|
1813
|
+
/* @__PURE__ */ jsx13(
|
|
1814
|
+
AppText,
|
|
1815
|
+
{
|
|
1816
|
+
size: "lg",
|
|
1817
|
+
weight: "semibold",
|
|
1818
|
+
className: "text-center mb-2",
|
|
1819
|
+
style: { color: textColor },
|
|
1820
|
+
children: title
|
|
1821
|
+
}
|
|
1822
|
+
),
|
|
1823
|
+
message && /* @__PURE__ */ jsx13(AppText, { size: "sm", style: { color: messageColor }, className: "text-center leading-5", children: message })
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsx13(AppView, { className: "border-t", style: { borderTopColor: borderColor }, children: buttons.length === 1 ? (
|
|
1826
|
+
// 单个按钮
|
|
1827
|
+
/* @__PURE__ */ jsx13(
|
|
1828
|
+
TouchableOpacity,
|
|
1829
|
+
{
|
|
1830
|
+
onPress: handleButtonPress(buttons[0]),
|
|
1831
|
+
className: "py-3 rounded-b-xl",
|
|
1832
|
+
style: [styles2.singleButton, getButtonStyle(buttons[0])],
|
|
1833
|
+
children: /* @__PURE__ */ jsx13(
|
|
1834
|
+
AppText,
|
|
1835
|
+
{
|
|
1836
|
+
weight: "medium",
|
|
1837
|
+
className: "text-center",
|
|
1838
|
+
style: { color: getButtonTextColor(buttons[0]) },
|
|
1839
|
+
children: buttons[0].text
|
|
1840
|
+
}
|
|
1841
|
+
)
|
|
1842
|
+
}
|
|
1843
|
+
)
|
|
1844
|
+
) : buttons.length === 2 ? (
|
|
1845
|
+
// 两个按钮横向排列
|
|
1846
|
+
/* @__PURE__ */ jsx13(AppView, { row: true, style: styles2.twoButtonContainer, children: buttons.map((button, index) => /* @__PURE__ */ jsx13(
|
|
1847
|
+
TouchableOpacity,
|
|
1848
|
+
{
|
|
1849
|
+
onPress: handleButtonPress(button),
|
|
1850
|
+
className: cn(
|
|
1851
|
+
"py-3 flex-1",
|
|
1852
|
+
index === 0 && "rounded-bl-xl",
|
|
1853
|
+
index === 1 && "rounded-br-xl"
|
|
1854
|
+
),
|
|
1855
|
+
style: [
|
|
1856
|
+
styles2.twoButton,
|
|
1857
|
+
index > 0 && { borderLeftColor: borderColor },
|
|
1858
|
+
getButtonStyle(button)
|
|
1859
|
+
],
|
|
1860
|
+
children: /* @__PURE__ */ jsx13(
|
|
1861
|
+
AppText,
|
|
1862
|
+
{
|
|
1863
|
+
weight: "medium",
|
|
1864
|
+
className: "text-center",
|
|
1865
|
+
style: { color: getButtonTextColor(button) },
|
|
1866
|
+
children: button.text
|
|
1867
|
+
}
|
|
1868
|
+
)
|
|
1869
|
+
},
|
|
1870
|
+
index
|
|
1871
|
+
)) })
|
|
1872
|
+
) : (
|
|
1873
|
+
// 多个按钮纵向排列
|
|
1874
|
+
/* @__PURE__ */ jsx13(AppView, { className: "gap-2 pb-4 px-4", children: buttons.map((button, index) => /* @__PURE__ */ jsx13(
|
|
1875
|
+
TouchableOpacity,
|
|
1876
|
+
{
|
|
1877
|
+
onPress: handleButtonPress(button),
|
|
1878
|
+
className: "py-3 rounded-lg",
|
|
1879
|
+
style: getButtonStyle(button),
|
|
1880
|
+
children: /* @__PURE__ */ jsx13(
|
|
1881
|
+
AppText,
|
|
1882
|
+
{
|
|
1883
|
+
weight: "medium",
|
|
1884
|
+
className: "text-center",
|
|
1885
|
+
style: { color: getButtonTextColor(button) },
|
|
1886
|
+
children: button.text
|
|
1887
|
+
}
|
|
1888
|
+
)
|
|
1889
|
+
},
|
|
1890
|
+
index
|
|
1891
|
+
)) })
|
|
1892
|
+
) })
|
|
1893
|
+
]
|
|
1894
|
+
}
|
|
1895
|
+
)
|
|
1896
|
+
] })
|
|
1473
1897
|
}
|
|
1474
1898
|
);
|
|
1475
1899
|
}
|
|
@@ -1488,19 +1912,8 @@ var styles2 = StyleSheet3.create({
|
|
|
1488
1912
|
});
|
|
1489
1913
|
|
|
1490
1914
|
// src/ui/feedback/Loading.tsx
|
|
1491
|
-
import { ActivityIndicator as
|
|
1492
|
-
import {
|
|
1493
|
-
function Loading({ text, color, overlay = false, visible = true, testID }) {
|
|
1494
|
-
if (!visible) return null;
|
|
1495
|
-
const content = /* @__PURE__ */ jsxs2(AppView, { center: true, gap: 3, testID, children: [
|
|
1496
|
-
/* @__PURE__ */ jsx13(ActivityIndicator2, { size: "large", color }),
|
|
1497
|
-
text && /* @__PURE__ */ jsx13(AppText, { style: color ? { color } : void 0, children: text })
|
|
1498
|
-
] });
|
|
1499
|
-
if (overlay) {
|
|
1500
|
-
return /* @__PURE__ */ jsx13(AppView, { center: true, flex: true, className: "absolute inset-0 bg-black/30", testID, children: content });
|
|
1501
|
-
}
|
|
1502
|
-
return content;
|
|
1503
|
-
}
|
|
1915
|
+
import { ActivityIndicator as ActivityIndicator4 } from "react-native";
|
|
1916
|
+
import { useEffect as useEffect6, useState as useState12 } from "react";
|
|
1504
1917
|
|
|
1505
1918
|
// src/ui/display/Progress.tsx
|
|
1506
1919
|
import { jsx as jsx14 } from "nativewind/jsx-runtime";
|
|
@@ -1694,13 +2107,13 @@ var FileIcons = {
|
|
|
1694
2107
|
};
|
|
1695
2108
|
|
|
1696
2109
|
// src/ui/display/AppImage.tsx
|
|
1697
|
-
import { useState as useState10, useCallback as
|
|
2110
|
+
import { useState as useState10, useCallback as useCallback11 } from "react";
|
|
1698
2111
|
import {
|
|
1699
2112
|
Image,
|
|
1700
|
-
ActivityIndicator as
|
|
2113
|
+
ActivityIndicator as ActivityIndicator2,
|
|
1701
2114
|
View as View4
|
|
1702
2115
|
} from "react-native";
|
|
1703
|
-
import { jsx as jsx17, jsxs as
|
|
2116
|
+
import { jsx as jsx17, jsxs as jsxs2 } from "nativewind/jsx-runtime";
|
|
1704
2117
|
var radiusMap = {
|
|
1705
2118
|
none: 0,
|
|
1706
2119
|
sm: 2,
|
|
@@ -1738,11 +2151,11 @@ function AppImage({
|
|
|
1738
2151
|
const [hasError, setHasError] = useState10(false);
|
|
1739
2152
|
const { theme } = useTheme();
|
|
1740
2153
|
const resolvedRadius = resolveRadius(borderRadius);
|
|
1741
|
-
const handleLoad =
|
|
2154
|
+
const handleLoad = useCallback11(() => {
|
|
1742
2155
|
setIsLoading(false);
|
|
1743
2156
|
onLoad?.();
|
|
1744
2157
|
}, [onLoad]);
|
|
1745
|
-
const handleError =
|
|
2158
|
+
const handleError = useCallback11(
|
|
1746
2159
|
(error) => {
|
|
1747
2160
|
setIsLoading(false);
|
|
1748
2161
|
setHasError(true);
|
|
@@ -1783,7 +2196,7 @@ function AppImage({
|
|
|
1783
2196
|
center: true,
|
|
1784
2197
|
className: "absolute inset-0 bg-gray-100",
|
|
1785
2198
|
style: { borderRadius: resolvedRadius },
|
|
1786
|
-
children: /* @__PURE__ */ jsx17(
|
|
2199
|
+
children: /* @__PURE__ */ jsx17(ActivityIndicator2, { color: theme.colors.primary?.[500] })
|
|
1787
2200
|
}
|
|
1788
2201
|
);
|
|
1789
2202
|
}
|
|
@@ -1823,7 +2236,7 @@ function AppImage({
|
|
|
1823
2236
|
};
|
|
1824
2237
|
const isNumberWidth = typeof width === "number";
|
|
1825
2238
|
const isNumberHeight = typeof height === "number";
|
|
1826
|
-
const content = /* @__PURE__ */
|
|
2239
|
+
const content = /* @__PURE__ */ jsxs2(
|
|
1827
2240
|
View4,
|
|
1828
2241
|
{
|
|
1829
2242
|
className: cn("overflow-hidden", className),
|
|
@@ -1856,22 +2269,31 @@ function AppImage({
|
|
|
1856
2269
|
}
|
|
1857
2270
|
|
|
1858
2271
|
// src/ui/display/AppList.tsx
|
|
1859
|
-
import { useState as useState11, useCallback as
|
|
2272
|
+
import React4, { useState as useState11, useCallback as useCallback12, useMemo as useMemo3 } from "react";
|
|
1860
2273
|
import {
|
|
1861
2274
|
FlatList,
|
|
1862
2275
|
RefreshControl,
|
|
1863
|
-
ActivityIndicator as
|
|
2276
|
+
ActivityIndicator as ActivityIndicator3,
|
|
1864
2277
|
StyleSheet as StyleSheet4
|
|
1865
2278
|
} from "react-native";
|
|
1866
|
-
import { Fragment, jsx as jsx18, jsxs as
|
|
2279
|
+
import { Fragment, jsx as jsx18, jsxs as jsxs3 } from "nativewind/jsx-runtime";
|
|
2280
|
+
function renderListSlot(slot) {
|
|
2281
|
+
if (!slot) return null;
|
|
2282
|
+
if (React4.isValidElement(slot)) return slot;
|
|
2283
|
+
if (typeof slot === "function") {
|
|
2284
|
+
const SlotComponent = slot;
|
|
2285
|
+
return /* @__PURE__ */ jsx18(SlotComponent, {});
|
|
2286
|
+
}
|
|
2287
|
+
return null;
|
|
2288
|
+
}
|
|
1867
2289
|
function SkeletonItem2({ render }) {
|
|
1868
2290
|
const colors = useThemeColors();
|
|
1869
2291
|
if (render) {
|
|
1870
2292
|
return render();
|
|
1871
2293
|
}
|
|
1872
|
-
return /* @__PURE__ */ jsx18(AppView, { p: 4, gap: 3, testID: "skeleton", children: /* @__PURE__ */
|
|
2294
|
+
return /* @__PURE__ */ jsx18(AppView, { p: 4, gap: 3, testID: "skeleton", children: /* @__PURE__ */ jsxs3(AppView, { row: true, gap: 3, children: [
|
|
1873
2295
|
/* @__PURE__ */ jsx18(AppView, { className: "w-16 h-16 rounded-lg", style: { backgroundColor: colors.divider } }),
|
|
1874
|
-
/* @__PURE__ */
|
|
2296
|
+
/* @__PURE__ */ jsxs3(AppView, { flex: true, gap: 2, children: [
|
|
1875
2297
|
/* @__PURE__ */ jsx18(AppView, { className: "h-4 w-3/4 rounded", style: { backgroundColor: colors.divider } }),
|
|
1876
2298
|
/* @__PURE__ */ jsx18(AppView, { className: "h-3 w-1/2 rounded", style: { backgroundColor: colors.divider } })
|
|
1877
2299
|
] })
|
|
@@ -1883,7 +2305,7 @@ function EmptyState({
|
|
|
1883
2305
|
icon
|
|
1884
2306
|
}) {
|
|
1885
2307
|
const colors = useThemeColors();
|
|
1886
|
-
return /* @__PURE__ */
|
|
2308
|
+
return /* @__PURE__ */ jsxs3(Center, { py: 20, children: [
|
|
1887
2309
|
/* @__PURE__ */ jsx18(Icon, { name: icon || "inbox", size: 64, color: colors.textMuted }),
|
|
1888
2310
|
/* @__PURE__ */ jsx18(AppText, { size: "lg", weight: "medium", className: "mt-4", style: { color: colors.text }, children: title || "\u6682\u65E0\u6570\u636E" }),
|
|
1889
2311
|
description && /* @__PURE__ */ jsx18(AppText, { size: "sm", className: "mt-2", style: { color: colors.textMuted }, children: description })
|
|
@@ -1897,7 +2319,7 @@ function ErrorState({
|
|
|
1897
2319
|
retryText
|
|
1898
2320
|
}) {
|
|
1899
2321
|
const colors = useThemeColors();
|
|
1900
|
-
return /* @__PURE__ */
|
|
2322
|
+
return /* @__PURE__ */ jsxs3(Center, { py: 20, children: [
|
|
1901
2323
|
/* @__PURE__ */ jsx18(Icon, { name: "error-outline", size: 64, color: "error-300" }),
|
|
1902
2324
|
/* @__PURE__ */ jsx18(AppText, { size: "lg", weight: "medium", color: "error-500", className: "mt-4", children: errorTitle || "\u52A0\u8F7D\u5931\u8D25" }),
|
|
1903
2325
|
/* @__PURE__ */ jsx18(AppText, { size: "sm", style: { color: colors.textMuted }, className: "mt-2 text-center px-8", children: error.message || errorDescription || "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5" }),
|
|
@@ -1917,7 +2339,7 @@ function ErrorState({
|
|
|
1917
2339
|
}
|
|
1918
2340
|
function LoadMoreFooter({ loading }) {
|
|
1919
2341
|
if (!loading) return null;
|
|
1920
|
-
return /* @__PURE__ */ jsx18(Center, { py: 4, children: /* @__PURE__ */ jsx18(
|
|
2342
|
+
return /* @__PURE__ */ jsx18(Center, { py: 4, children: /* @__PURE__ */ jsx18(ActivityIndicator3, { size: "small" }) });
|
|
1921
2343
|
}
|
|
1922
2344
|
function Divider({ style }) {
|
|
1923
2345
|
const colors = useThemeColors();
|
|
@@ -1964,7 +2386,7 @@ function AppList({
|
|
|
1964
2386
|
}) {
|
|
1965
2387
|
const { theme } = useTheme();
|
|
1966
2388
|
const [isLoadingMore, setIsLoadingMore] = useState11(false);
|
|
1967
|
-
const handleEndReached =
|
|
2389
|
+
const handleEndReached = useCallback12(async () => {
|
|
1968
2390
|
if (isLoadingMore || !hasMore || !onEndReached) return;
|
|
1969
2391
|
setIsLoadingMore(true);
|
|
1970
2392
|
try {
|
|
@@ -1973,16 +2395,16 @@ function AppList({
|
|
|
1973
2395
|
setIsLoadingMore(false);
|
|
1974
2396
|
}
|
|
1975
2397
|
}, [isLoadingMore, hasMore, onEndReached]);
|
|
1976
|
-
const defaultKeyExtractor =
|
|
2398
|
+
const defaultKeyExtractor = useCallback12(
|
|
1977
2399
|
(item, index) => {
|
|
1978
2400
|
if (keyExtractor) return keyExtractor(item, index);
|
|
1979
2401
|
return `item-${index}`;
|
|
1980
2402
|
},
|
|
1981
2403
|
[keyExtractor]
|
|
1982
2404
|
);
|
|
1983
|
-
const wrappedRenderItem =
|
|
2405
|
+
const wrappedRenderItem = useCallback12(
|
|
1984
2406
|
(info) => {
|
|
1985
|
-
return /* @__PURE__ */
|
|
2407
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1986
2408
|
divider && info.index > 0 && /* @__PURE__ */ jsx18(Divider, { style: dividerStyle }),
|
|
1987
2409
|
renderItem(info)
|
|
1988
2410
|
] });
|
|
@@ -1993,10 +2415,14 @@ function AppList({
|
|
|
1993
2415
|
() => new Array(skeletonCount).fill(null).map((_, i) => ({ _skeletonId: i })),
|
|
1994
2416
|
[skeletonCount]
|
|
1995
2417
|
);
|
|
1996
|
-
const skeletonRenderItem =
|
|
2418
|
+
const skeletonRenderItem = useCallback12(
|
|
1997
2419
|
() => /* @__PURE__ */ jsx18(SkeletonItem2, { render: skeletonRender }),
|
|
1998
2420
|
[skeletonRender]
|
|
1999
2421
|
);
|
|
2422
|
+
const flatListKey = useMemo3(() => {
|
|
2423
|
+
if (horizontal) return "app-list-horizontal";
|
|
2424
|
+
return `app-list-columns-${numColumns ?? 1}`;
|
|
2425
|
+
}, [horizontal, numColumns]);
|
|
2000
2426
|
if (loading && data.length === 0) {
|
|
2001
2427
|
return /* @__PURE__ */ jsx18(
|
|
2002
2428
|
FlatList,
|
|
@@ -2008,7 +2434,8 @@ function AppList({
|
|
|
2008
2434
|
style,
|
|
2009
2435
|
showsVerticalScrollIndicator,
|
|
2010
2436
|
showsHorizontalScrollIndicator
|
|
2011
|
-
}
|
|
2437
|
+
},
|
|
2438
|
+
`${flatListKey}-skeleton`
|
|
2012
2439
|
);
|
|
2013
2440
|
}
|
|
2014
2441
|
if (error && data.length === 0) {
|
|
@@ -2024,15 +2451,19 @@ function AppList({
|
|
|
2024
2451
|
) });
|
|
2025
2452
|
}
|
|
2026
2453
|
const ListEmptyComponent = useMemo3(() => {
|
|
2027
|
-
if (EmptyComponent) return
|
|
2454
|
+
if (EmptyComponent) return renderListSlot(EmptyComponent);
|
|
2028
2455
|
return /* @__PURE__ */ jsx18(EmptyState, { title: emptyTitle, description: emptyDescription, icon: emptyIcon });
|
|
2029
2456
|
}, [EmptyComponent, emptyTitle, emptyDescription, emptyIcon]);
|
|
2030
2457
|
const FooterComponent = useMemo3(() => {
|
|
2031
|
-
return /* @__PURE__ */
|
|
2458
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2032
2459
|
/* @__PURE__ */ jsx18(LoadMoreFooter, { loading: isLoadingMore }),
|
|
2033
|
-
ListFooterComponent
|
|
2460
|
+
renderListSlot(ListFooterComponent)
|
|
2034
2461
|
] });
|
|
2035
2462
|
}, [isLoadingMore, ListFooterComponent]);
|
|
2463
|
+
const HeaderComponent = useMemo3(
|
|
2464
|
+
() => renderListSlot(ListHeaderComponent),
|
|
2465
|
+
[ListHeaderComponent]
|
|
2466
|
+
);
|
|
2036
2467
|
return /* @__PURE__ */ jsx18(
|
|
2037
2468
|
FlatList,
|
|
2038
2469
|
{
|
|
@@ -2051,7 +2482,7 @@ function AppList({
|
|
|
2051
2482
|
onEndReached: onEndReached ? handleEndReached : void 0,
|
|
2052
2483
|
onEndReachedThreshold,
|
|
2053
2484
|
ListEmptyComponent,
|
|
2054
|
-
ListHeaderComponent,
|
|
2485
|
+
ListHeaderComponent: HeaderComponent,
|
|
2055
2486
|
ListFooterComponent: FooterComponent,
|
|
2056
2487
|
contentContainerStyle,
|
|
2057
2488
|
style,
|
|
@@ -2064,7 +2495,8 @@ function AppList({
|
|
|
2064
2495
|
maxToRenderPerBatch: 10,
|
|
2065
2496
|
windowSize: 10,
|
|
2066
2497
|
initialNumToRender: 10
|
|
2067
|
-
}
|
|
2498
|
+
},
|
|
2499
|
+
flatListKey
|
|
2068
2500
|
);
|
|
2069
2501
|
}
|
|
2070
2502
|
var styles3 = StyleSheet4.create({
|
|
@@ -2076,7 +2508,7 @@ var styles3 = StyleSheet4.create({
|
|
|
2076
2508
|
// src/ui/display/PageDrawer.tsx
|
|
2077
2509
|
import React5 from "react";
|
|
2078
2510
|
import { BackHandler, Modal as Modal2, PanResponder, StyleSheet as StyleSheet5 } from "react-native";
|
|
2079
|
-
import { jsx as jsx19, jsxs as
|
|
2511
|
+
import { jsx as jsx19, jsxs as jsxs4 } from "nativewind/jsx-runtime";
|
|
2080
2512
|
function PageDrawer({
|
|
2081
2513
|
visible,
|
|
2082
2514
|
onClose,
|
|
@@ -2096,7 +2528,6 @@ function PageDrawer({
|
|
|
2096
2528
|
}) {
|
|
2097
2529
|
const colors = useThemeColors();
|
|
2098
2530
|
const [translateX, setTranslateX] = React5.useState(0);
|
|
2099
|
-
if (!visible) return null;
|
|
2100
2531
|
const handleClose = React5.useCallback(() => {
|
|
2101
2532
|
setTranslateX(0);
|
|
2102
2533
|
onClose?.();
|
|
@@ -2140,7 +2571,8 @@ function PageDrawer({
|
|
|
2140
2571
|
}),
|
|
2141
2572
|
[handleClose, placement, swipeEnabled, swipeThreshold, width]
|
|
2142
2573
|
);
|
|
2143
|
-
|
|
2574
|
+
if (!visible) return null;
|
|
2575
|
+
const drawerContent = /* @__PURE__ */ jsxs4(
|
|
2144
2576
|
AppView,
|
|
2145
2577
|
{
|
|
2146
2578
|
testID: contentTestID,
|
|
@@ -2159,7 +2591,7 @@ function PageDrawer({
|
|
|
2159
2591
|
}
|
|
2160
2592
|
],
|
|
2161
2593
|
children: [
|
|
2162
|
-
(header || title || showCloseButton) && /* @__PURE__ */
|
|
2594
|
+
(header || title || showCloseButton) && /* @__PURE__ */ jsxs4(
|
|
2163
2595
|
AppView,
|
|
2164
2596
|
{
|
|
2165
2597
|
row: true,
|
|
@@ -2187,7 +2619,7 @@ function PageDrawer({
|
|
|
2187
2619
|
]
|
|
2188
2620
|
}
|
|
2189
2621
|
);
|
|
2190
|
-
return /* @__PURE__ */ jsx19(Modal2, { visible: true, transparent: true, animationType: "fade", onRequestClose: handleClose, children: /* @__PURE__ */
|
|
2622
|
+
return /* @__PURE__ */ jsx19(Modal2, { visible: true, transparent: true, animationType: "fade", onRequestClose: handleClose, children: /* @__PURE__ */ jsxs4(
|
|
2191
2623
|
AppView,
|
|
2192
2624
|
{
|
|
2193
2625
|
testID,
|
|
@@ -2236,14 +2668,57 @@ function GradientView({
|
|
|
2236
2668
|
return /* @__PURE__ */ jsx20(LinearGradient, { colors: [...colors], start, end, style, ...props, children });
|
|
2237
2669
|
}
|
|
2238
2670
|
|
|
2671
|
+
// src/ui/feedback/Loading.tsx
|
|
2672
|
+
import { jsx as jsx21, jsxs as jsxs5 } from "nativewind/jsx-runtime";
|
|
2673
|
+
var LOADING_CLOSE_DELAY = 3e4;
|
|
2674
|
+
function Loading({
|
|
2675
|
+
text,
|
|
2676
|
+
color,
|
|
2677
|
+
overlay = false,
|
|
2678
|
+
visible = true,
|
|
2679
|
+
testID,
|
|
2680
|
+
onClose
|
|
2681
|
+
}) {
|
|
2682
|
+
const [showCloseButton, setShowCloseButton] = useState12(false);
|
|
2683
|
+
useEffect6(() => {
|
|
2684
|
+
if (!visible) {
|
|
2685
|
+
setShowCloseButton(false);
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
setShowCloseButton(false);
|
|
2689
|
+
const timer = setTimeout(() => {
|
|
2690
|
+
setShowCloseButton(true);
|
|
2691
|
+
}, LOADING_CLOSE_DELAY);
|
|
2692
|
+
return () => clearTimeout(timer);
|
|
2693
|
+
}, [visible]);
|
|
2694
|
+
if (!visible) return null;
|
|
2695
|
+
const content = /* @__PURE__ */ jsxs5(AppView, { center: true, gap: 3, testID, children: [
|
|
2696
|
+
/* @__PURE__ */ jsx21(ActivityIndicator4, { size: "large", color }),
|
|
2697
|
+
text && /* @__PURE__ */ jsx21(AppText, { style: color ? { color } : void 0, children: text }),
|
|
2698
|
+
showCloseButton && onClose && /* @__PURE__ */ jsx21(
|
|
2699
|
+
AppPressable,
|
|
2700
|
+
{
|
|
2701
|
+
testID: testID ? `${testID}-close` : "loading-close",
|
|
2702
|
+
className: "mt-1 p-1",
|
|
2703
|
+
onPress: onClose,
|
|
2704
|
+
children: /* @__PURE__ */ jsx21(Icon, { name: "close", size: "md", color: color || "white" })
|
|
2705
|
+
}
|
|
2706
|
+
)
|
|
2707
|
+
] });
|
|
2708
|
+
if (overlay) {
|
|
2709
|
+
return /* @__PURE__ */ jsx21(AppView, { center: true, flex: true, className: "absolute inset-0 bg-black/30", testID, children: content });
|
|
2710
|
+
}
|
|
2711
|
+
return content;
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2239
2714
|
// src/ui/form/AppInput.tsx
|
|
2240
|
-
import { forwardRef, useState as
|
|
2715
|
+
import { forwardRef, useState as useState13 } from "react";
|
|
2241
2716
|
import { TextInput, View as View5, StyleSheet as StyleSheet6 } from "react-native";
|
|
2242
|
-
import { jsx as
|
|
2717
|
+
import { jsx as jsx22, jsxs as jsxs6 } from "nativewind/jsx-runtime";
|
|
2243
2718
|
var AppInput = forwardRef(
|
|
2244
2719
|
({ label, error, disabled = false, leftIcon, rightIcon, className, style, ...props }, ref) => {
|
|
2245
2720
|
const colors = useThemeColors();
|
|
2246
|
-
const [isFocused, setIsFocused] =
|
|
2721
|
+
const [isFocused, setIsFocused] = useState13(false);
|
|
2247
2722
|
const errorColor = "#ef4444";
|
|
2248
2723
|
const getBorderColor = () => {
|
|
2249
2724
|
if (error) return errorColor;
|
|
@@ -2251,7 +2726,7 @@ var AppInput = forwardRef(
|
|
|
2251
2726
|
return colors.border;
|
|
2252
2727
|
};
|
|
2253
2728
|
return /* @__PURE__ */ jsxs6(AppView, { className: cn("flex-col gap-1", className), children: [
|
|
2254
|
-
label && /* @__PURE__ */
|
|
2729
|
+
label && /* @__PURE__ */ jsx22(AppText, { size: "sm", weight: "medium", style: { color: colors.textSecondary }, children: label }),
|
|
2255
2730
|
/* @__PURE__ */ jsxs6(
|
|
2256
2731
|
AppView,
|
|
2257
2732
|
{
|
|
@@ -2267,8 +2742,8 @@ var AppInput = forwardRef(
|
|
|
2267
2742
|
}
|
|
2268
2743
|
],
|
|
2269
2744
|
children: [
|
|
2270
|
-
leftIcon && /* @__PURE__ */
|
|
2271
|
-
/* @__PURE__ */
|
|
2745
|
+
leftIcon && /* @__PURE__ */ jsx22(View5, { style: styles5.icon, children: leftIcon }),
|
|
2746
|
+
/* @__PURE__ */ jsx22(
|
|
2272
2747
|
TextInput,
|
|
2273
2748
|
{
|
|
2274
2749
|
ref,
|
|
@@ -2287,11 +2762,11 @@ var AppInput = forwardRef(
|
|
|
2287
2762
|
...props
|
|
2288
2763
|
}
|
|
2289
2764
|
),
|
|
2290
|
-
rightIcon && /* @__PURE__ */
|
|
2765
|
+
rightIcon && /* @__PURE__ */ jsx22(View5, { style: styles5.icon, children: rightIcon })
|
|
2291
2766
|
]
|
|
2292
2767
|
}
|
|
2293
2768
|
),
|
|
2294
|
-
error && /* @__PURE__ */
|
|
2769
|
+
error && /* @__PURE__ */ jsx22(AppText, { size: "xs", style: { color: errorColor }, children: error })
|
|
2295
2770
|
] });
|
|
2296
2771
|
}
|
|
2297
2772
|
);
|
|
@@ -2313,9 +2788,9 @@ var styles5 = StyleSheet6.create({
|
|
|
2313
2788
|
});
|
|
2314
2789
|
|
|
2315
2790
|
// src/ui/form/Checkbox.tsx
|
|
2316
|
-
import { useState as
|
|
2791
|
+
import { useState as useState14 } from "react";
|
|
2317
2792
|
import { TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet7 } from "react-native";
|
|
2318
|
-
import { jsx as
|
|
2793
|
+
import { jsx as jsx23, jsxs as jsxs7 } from "nativewind/jsx-runtime";
|
|
2319
2794
|
function Checkbox({
|
|
2320
2795
|
checked,
|
|
2321
2796
|
defaultChecked,
|
|
@@ -2326,7 +2801,7 @@ function Checkbox({
|
|
|
2326
2801
|
testID
|
|
2327
2802
|
}) {
|
|
2328
2803
|
const colors = useThemeColors();
|
|
2329
|
-
const [internalChecked, setInternalChecked] =
|
|
2804
|
+
const [internalChecked, setInternalChecked] = useState14(defaultChecked || false);
|
|
2330
2805
|
const isChecked = checked !== void 0 ? checked : internalChecked;
|
|
2331
2806
|
const toggle = () => {
|
|
2332
2807
|
if (disabled) return;
|
|
@@ -2347,7 +2822,7 @@ function Checkbox({
|
|
|
2347
2822
|
testID,
|
|
2348
2823
|
activeOpacity: 0.7,
|
|
2349
2824
|
children: [
|
|
2350
|
-
/* @__PURE__ */
|
|
2825
|
+
/* @__PURE__ */ jsx23(
|
|
2351
2826
|
AppView,
|
|
2352
2827
|
{
|
|
2353
2828
|
className: cn(
|
|
@@ -2361,10 +2836,10 @@ function Checkbox({
|
|
|
2361
2836
|
borderColor: isChecked ? colors.primary : colors.border
|
|
2362
2837
|
}
|
|
2363
2838
|
],
|
|
2364
|
-
children: isChecked && /* @__PURE__ */
|
|
2839
|
+
children: isChecked && /* @__PURE__ */ jsx23(AppView, { pointerEvents: "none", style: styles6.iconContainer, testID: `${testID}-icon`, children: /* @__PURE__ */ jsx23(Icon, { name: "check", size: 14, color: "white", style: styles6.icon }) })
|
|
2365
2840
|
}
|
|
2366
2841
|
),
|
|
2367
|
-
children && /* @__PURE__ */
|
|
2842
|
+
children && /* @__PURE__ */ jsx23(AppText, { size: "sm", style: { color: colors.text }, children })
|
|
2368
2843
|
]
|
|
2369
2844
|
}
|
|
2370
2845
|
);
|
|
@@ -2372,6 +2847,16 @@ function Checkbox({
|
|
|
2372
2847
|
var styles6 = StyleSheet7.create({
|
|
2373
2848
|
checkbox: {
|
|
2374
2849
|
borderWidth: 0.5
|
|
2850
|
+
},
|
|
2851
|
+
iconContainer: {
|
|
2852
|
+
...StyleSheet7.absoluteFillObject,
|
|
2853
|
+
alignItems: "center",
|
|
2854
|
+
justifyContent: "center"
|
|
2855
|
+
},
|
|
2856
|
+
icon: {
|
|
2857
|
+
lineHeight: 14,
|
|
2858
|
+
includeFontPadding: false,
|
|
2859
|
+
textAlignVertical: "center"
|
|
2375
2860
|
}
|
|
2376
2861
|
});
|
|
2377
2862
|
|
|
@@ -2387,7 +2872,7 @@ var isGroupOptionDisabled = (groupDisabled, optionDisabled) => {
|
|
|
2387
2872
|
};
|
|
2388
2873
|
|
|
2389
2874
|
// src/ui/form/CheckboxGroup.tsx
|
|
2390
|
-
import { jsx as
|
|
2875
|
+
import { jsx as jsx24 } from "nativewind/jsx-runtime";
|
|
2391
2876
|
function CheckboxGroup({
|
|
2392
2877
|
value = [],
|
|
2393
2878
|
onChange,
|
|
@@ -2400,7 +2885,7 @@ function CheckboxGroup({
|
|
|
2400
2885
|
onChange(toggleGroupValue(value, optionValue, checked));
|
|
2401
2886
|
};
|
|
2402
2887
|
const isRow = direction === "row";
|
|
2403
|
-
return /* @__PURE__ */
|
|
2888
|
+
return /* @__PURE__ */ jsx24(AppView, { row: isRow, flex: isRow, gap: 4, children: options.map((option) => /* @__PURE__ */ jsx24(
|
|
2404
2889
|
Checkbox,
|
|
2405
2890
|
{
|
|
2406
2891
|
checked: value.includes(option.value),
|
|
@@ -2413,9 +2898,9 @@ function CheckboxGroup({
|
|
|
2413
2898
|
}
|
|
2414
2899
|
|
|
2415
2900
|
// src/ui/form/Radio.tsx
|
|
2416
|
-
import { useState as
|
|
2901
|
+
import { useState as useState15 } from "react";
|
|
2417
2902
|
import { TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet8 } from "react-native";
|
|
2418
|
-
import { jsx as
|
|
2903
|
+
import { jsx as jsx25, jsxs as jsxs8 } from "nativewind/jsx-runtime";
|
|
2419
2904
|
function Radio({
|
|
2420
2905
|
checked,
|
|
2421
2906
|
defaultChecked,
|
|
@@ -2426,7 +2911,7 @@ function Radio({
|
|
|
2426
2911
|
testID
|
|
2427
2912
|
}) {
|
|
2428
2913
|
const colors = useThemeColors();
|
|
2429
|
-
const [internalChecked, setInternalChecked] =
|
|
2914
|
+
const [internalChecked, setInternalChecked] = useState15(defaultChecked || false);
|
|
2430
2915
|
const isChecked = checked !== void 0 ? checked : internalChecked;
|
|
2431
2916
|
const toggle = () => {
|
|
2432
2917
|
if (disabled) return;
|
|
@@ -2447,7 +2932,7 @@ function Radio({
|
|
|
2447
2932
|
testID,
|
|
2448
2933
|
activeOpacity: 0.7,
|
|
2449
2934
|
children: [
|
|
2450
|
-
/* @__PURE__ */
|
|
2935
|
+
/* @__PURE__ */ jsx25(
|
|
2451
2936
|
AppView,
|
|
2452
2937
|
{
|
|
2453
2938
|
className: cn("w-5 h-5 rounded-full items-center justify-center", isChecked && "border-2"),
|
|
@@ -2459,7 +2944,7 @@ function Radio({
|
|
|
2459
2944
|
borderWidth: isChecked ? 0.5 : 0.5
|
|
2460
2945
|
}
|
|
2461
2946
|
],
|
|
2462
|
-
children: isChecked && /* @__PURE__ */
|
|
2947
|
+
children: isChecked && /* @__PURE__ */ jsx25(
|
|
2463
2948
|
AppView,
|
|
2464
2949
|
{
|
|
2465
2950
|
className: "rounded-full",
|
|
@@ -2468,7 +2953,7 @@ function Radio({
|
|
|
2468
2953
|
)
|
|
2469
2954
|
}
|
|
2470
2955
|
),
|
|
2471
|
-
children && /* @__PURE__ */
|
|
2956
|
+
children && /* @__PURE__ */ jsx25(AppText, { size: "sm", style: { color: colors.text }, children })
|
|
2472
2957
|
]
|
|
2473
2958
|
}
|
|
2474
2959
|
);
|
|
@@ -2484,7 +2969,7 @@ var styles7 = StyleSheet8.create({
|
|
|
2484
2969
|
});
|
|
2485
2970
|
|
|
2486
2971
|
// src/ui/form/RadioGroup.tsx
|
|
2487
|
-
import { jsx as
|
|
2972
|
+
import { jsx as jsx26 } from "nativewind/jsx-runtime";
|
|
2488
2973
|
function RadioGroup({
|
|
2489
2974
|
value,
|
|
2490
2975
|
onChange,
|
|
@@ -2493,7 +2978,7 @@ function RadioGroup({
|
|
|
2493
2978
|
disabled = false
|
|
2494
2979
|
}) {
|
|
2495
2980
|
const isRow = direction === "row";
|
|
2496
|
-
return /* @__PURE__ */
|
|
2981
|
+
return /* @__PURE__ */ jsx26(AppView, { row: isRow, flex: isRow, gap: 4, children: options.map((option) => /* @__PURE__ */ jsx26(
|
|
2497
2982
|
Radio,
|
|
2498
2983
|
{
|
|
2499
2984
|
checked: value === option.value,
|
|
@@ -2506,9 +2991,17 @@ function RadioGroup({
|
|
|
2506
2991
|
}
|
|
2507
2992
|
|
|
2508
2993
|
// src/ui/form/Switch.tsx
|
|
2509
|
-
import { useState as
|
|
2510
|
-
import {
|
|
2511
|
-
import { jsx as
|
|
2994
|
+
import { useEffect as useEffect7, useRef as useRef7, useState as useState16 } from "react";
|
|
2995
|
+
import { Animated as Animated2, StyleSheet as StyleSheet9, TouchableOpacity as TouchableOpacity4 } from "react-native";
|
|
2996
|
+
import { jsx as jsx27 } from "nativewind/jsx-runtime";
|
|
2997
|
+
function createAnimatedValue2(value) {
|
|
2998
|
+
const AnimatedValue = Animated2.Value;
|
|
2999
|
+
try {
|
|
3000
|
+
return new AnimatedValue(value);
|
|
3001
|
+
} catch {
|
|
3002
|
+
return AnimatedValue(value);
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
2512
3005
|
function Switch({
|
|
2513
3006
|
checked,
|
|
2514
3007
|
defaultChecked,
|
|
@@ -2520,35 +3013,79 @@ function Switch({
|
|
|
2520
3013
|
style
|
|
2521
3014
|
}) {
|
|
2522
3015
|
const colors = useThemeColors();
|
|
2523
|
-
const [internalChecked, setInternalChecked] =
|
|
3016
|
+
const [internalChecked, setInternalChecked] = useState16(defaultChecked || false);
|
|
3017
|
+
const [isInteractionLocked, setIsInteractionLocked] = useState16(false);
|
|
3018
|
+
const isFirstRender = useRef7(true);
|
|
3019
|
+
const unlockTimerRef = useRef7(null);
|
|
2524
3020
|
const isChecked = checked !== void 0 ? checked : internalChecked;
|
|
3021
|
+
const sizes = {
|
|
3022
|
+
sm: { width: 36, height: 20, thumb: 16, padding: 2 },
|
|
3023
|
+
md: { width: 48, height: 26, thumb: 22, padding: 2 },
|
|
3024
|
+
lg: { width: 60, height: 32, thumb: 28, padding: 2 }
|
|
3025
|
+
};
|
|
3026
|
+
const config = sizes[size];
|
|
3027
|
+
const maxTranslateX = config.width - config.thumb - config.padding * 2;
|
|
3028
|
+
const thumbTranslateX = useRef7(createAnimatedValue2(isChecked ? maxTranslateX : 0)).current;
|
|
3029
|
+
const clearUnlockTimer = () => {
|
|
3030
|
+
if (!unlockTimerRef.current) return;
|
|
3031
|
+
clearTimeout(unlockTimerRef.current);
|
|
3032
|
+
unlockTimerRef.current = null;
|
|
3033
|
+
};
|
|
3034
|
+
const animateThumb = (nextChecked, shouldUnlock = true) => {
|
|
3035
|
+
Animated2.timing(thumbTranslateX, {
|
|
3036
|
+
toValue: nextChecked ? maxTranslateX : 0,
|
|
3037
|
+
duration: 180,
|
|
3038
|
+
useNativeDriver: true
|
|
3039
|
+
}).start((result) => {
|
|
3040
|
+
if (result?.finished ?? true) {
|
|
3041
|
+
thumbTranslateX.setValue(nextChecked ? maxTranslateX : 0);
|
|
3042
|
+
}
|
|
3043
|
+
if (shouldUnlock) {
|
|
3044
|
+
clearUnlockTimer();
|
|
3045
|
+
setIsInteractionLocked(false);
|
|
3046
|
+
}
|
|
3047
|
+
});
|
|
3048
|
+
};
|
|
3049
|
+
useEffect7(() => {
|
|
3050
|
+
if (isFirstRender.current) {
|
|
3051
|
+
thumbTranslateX.setValue(isChecked ? maxTranslateX : 0);
|
|
3052
|
+
isFirstRender.current = false;
|
|
3053
|
+
return;
|
|
3054
|
+
}
|
|
3055
|
+
animateThumb(isChecked);
|
|
3056
|
+
}, [isChecked, maxTranslateX, thumbTranslateX]);
|
|
3057
|
+
useEffect7(() => {
|
|
3058
|
+
return () => {
|
|
3059
|
+
clearUnlockTimer();
|
|
3060
|
+
};
|
|
3061
|
+
}, []);
|
|
2525
3062
|
const toggle = () => {
|
|
2526
|
-
if (disabled) return;
|
|
3063
|
+
if (disabled || isInteractionLocked) return;
|
|
2527
3064
|
const newChecked = !isChecked;
|
|
3065
|
+
setIsInteractionLocked(true);
|
|
2528
3066
|
if (checked === void 0) {
|
|
2529
3067
|
setInternalChecked(newChecked);
|
|
3068
|
+
} else {
|
|
3069
|
+
animateThumb(newChecked, false);
|
|
3070
|
+
unlockTimerRef.current = setTimeout(() => {
|
|
3071
|
+
unlockTimerRef.current = null;
|
|
3072
|
+
setIsInteractionLocked(false);
|
|
3073
|
+
}, 220);
|
|
2530
3074
|
}
|
|
2531
3075
|
onChange?.(newChecked);
|
|
2532
3076
|
};
|
|
2533
|
-
const
|
|
2534
|
-
const
|
|
2535
|
-
const
|
|
2536
|
-
|
|
2537
|
-
sm: { width: 36, height: 20, thumb: 16, padding: 2 },
|
|
2538
|
-
md: { width: 48, height: 26, thumb: 22, padding: 2 },
|
|
2539
|
-
lg: { width: 60, height: 32, thumb: 28, padding: 2 }
|
|
2540
|
-
};
|
|
2541
|
-
const config = sizes[size];
|
|
2542
|
-
const thumbPosition = isChecked ? config.width - config.thumb - config.padding : config.padding;
|
|
2543
|
-
return /* @__PURE__ */ jsx26(
|
|
3077
|
+
const trackBackgroundColor = disabled ? isChecked ? colors.primarySurface : colors.divider : isChecked ? colors.primary : colors.divider;
|
|
3078
|
+
const trackBorderColor = disabled ? isChecked ? colors.primarySurface : colors.border : isChecked ? colors.primary : colors.border;
|
|
3079
|
+
const thumbBackgroundColor = disabled ? colors.card : colors.textInverse;
|
|
3080
|
+
return /* @__PURE__ */ jsx27(
|
|
2544
3081
|
TouchableOpacity4,
|
|
2545
3082
|
{
|
|
2546
3083
|
onPress: toggle,
|
|
2547
|
-
disabled,
|
|
3084
|
+
disabled: disabled || isInteractionLocked,
|
|
2548
3085
|
className: cn(className),
|
|
2549
3086
|
testID,
|
|
2550
|
-
activeOpacity: disabled ? 1 : 0.8,
|
|
2551
|
-
children: /* @__PURE__ */
|
|
3087
|
+
activeOpacity: disabled || isInteractionLocked ? 1 : 0.8,
|
|
3088
|
+
children: /* @__PURE__ */ jsx27(
|
|
2552
3089
|
AppView,
|
|
2553
3090
|
{
|
|
2554
3091
|
className: "rounded-full",
|
|
@@ -2557,22 +3094,22 @@ function Switch({
|
|
|
2557
3094
|
{
|
|
2558
3095
|
width: config.width,
|
|
2559
3096
|
height: config.height,
|
|
2560
|
-
backgroundColor:
|
|
2561
|
-
|
|
3097
|
+
backgroundColor: trackBackgroundColor,
|
|
3098
|
+
borderColor: trackBorderColor
|
|
2562
3099
|
},
|
|
2563
3100
|
style
|
|
2564
3101
|
],
|
|
2565
|
-
children: /* @__PURE__ */
|
|
2566
|
-
|
|
3102
|
+
children: /* @__PURE__ */ jsx27(
|
|
3103
|
+
Animated2.View,
|
|
2567
3104
|
{
|
|
2568
|
-
className: "rounded-full",
|
|
2569
3105
|
style: [
|
|
2570
3106
|
styles8.thumb,
|
|
2571
3107
|
{
|
|
2572
3108
|
width: config.thumb,
|
|
2573
3109
|
height: config.thumb,
|
|
2574
|
-
|
|
2575
|
-
|
|
3110
|
+
borderRadius: config.thumb / 2,
|
|
3111
|
+
backgroundColor: thumbBackgroundColor,
|
|
3112
|
+
transform: [{ translateX: thumbTranslateX }],
|
|
2576
3113
|
shadowColor: "#000000",
|
|
2577
3114
|
shadowOffset: { width: 0, height: 1 },
|
|
2578
3115
|
shadowOpacity: 0.25,
|
|
@@ -2589,7 +3126,8 @@ function Switch({
|
|
|
2589
3126
|
var styles8 = StyleSheet9.create({
|
|
2590
3127
|
track: {
|
|
2591
3128
|
justifyContent: "center",
|
|
2592
|
-
padding: 2
|
|
3129
|
+
padding: 2,
|
|
3130
|
+
borderWidth: 0.5
|
|
2593
3131
|
},
|
|
2594
3132
|
thumb: {
|
|
2595
3133
|
elevation: 2,
|
|
@@ -2601,7 +3139,7 @@ var styles8 = StyleSheet9.create({
|
|
|
2601
3139
|
});
|
|
2602
3140
|
|
|
2603
3141
|
// src/ui/form/Slider.tsx
|
|
2604
|
-
import {
|
|
3142
|
+
import { useCallback as useCallback13, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef8, useState as useState17 } from "react";
|
|
2605
3143
|
import {
|
|
2606
3144
|
View as View6,
|
|
2607
3145
|
PanResponder as PanResponder2,
|
|
@@ -2634,7 +3172,7 @@ function useFormThemeColors() {
|
|
|
2634
3172
|
}
|
|
2635
3173
|
|
|
2636
3174
|
// src/ui/form/Slider.tsx
|
|
2637
|
-
import { jsx as
|
|
3175
|
+
import { jsx as jsx28, jsxs as jsxs9 } from "nativewind/jsx-runtime";
|
|
2638
3176
|
function Slider({
|
|
2639
3177
|
value,
|
|
2640
3178
|
defaultValue = 0,
|
|
@@ -2648,53 +3186,82 @@ function Slider({
|
|
|
2648
3186
|
className
|
|
2649
3187
|
}) {
|
|
2650
3188
|
const colors = useFormThemeColors();
|
|
2651
|
-
const [internalValue, setInternalValue] =
|
|
2652
|
-
const [
|
|
2653
|
-
const
|
|
3189
|
+
const [internalValue, setInternalValue] = useState17(defaultValue);
|
|
3190
|
+
const [isDragging, setIsDragging] = useState17(false);
|
|
3191
|
+
const trackWidthRef = useRef8(0);
|
|
3192
|
+
const currentValueRef = useRef8(value ?? defaultValue);
|
|
3193
|
+
const dragStartValueRef = useRef8(value ?? defaultValue);
|
|
2654
3194
|
const currentValue = value !== void 0 ? value : internalValue;
|
|
3195
|
+
const range = max - min;
|
|
2655
3196
|
const disabledOpacity = 0.4;
|
|
2656
|
-
const progress = (currentValue - min) /
|
|
2657
|
-
|
|
3197
|
+
const progress = range <= 0 ? 0 : (currentValue - min) / range * 100;
|
|
3198
|
+
useEffect8(() => {
|
|
3199
|
+
currentValueRef.current = currentValue;
|
|
3200
|
+
}, [currentValue]);
|
|
3201
|
+
const clampValue = useCallback13(
|
|
3202
|
+
(nextValue) => {
|
|
3203
|
+
if (!Number.isFinite(nextValue)) return currentValueRef.current;
|
|
3204
|
+
return Math.min(max, Math.max(min, nextValue));
|
|
3205
|
+
},
|
|
3206
|
+
[max, min]
|
|
3207
|
+
);
|
|
3208
|
+
const valueToPosition = useCallback13(
|
|
3209
|
+
(nextValue) => {
|
|
3210
|
+
if (trackWidthRef.current <= 0 || range <= 0) return 0;
|
|
3211
|
+
return (clampValue(nextValue) - min) / range * trackWidthRef.current;
|
|
3212
|
+
},
|
|
3213
|
+
[clampValue, min, range]
|
|
3214
|
+
);
|
|
3215
|
+
const getValueFromPosition = useCallback13(
|
|
2658
3216
|
(position) => {
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
3217
|
+
if (trackWidthRef.current <= 0 || range <= 0 || !Number.isFinite(position)) {
|
|
3218
|
+
return clampValue(currentValueRef.current);
|
|
3219
|
+
}
|
|
3220
|
+
const percentage = Math.max(0, Math.min(1, position / trackWidthRef.current));
|
|
3221
|
+
const rawValue = min + percentage * range;
|
|
3222
|
+
const steppedValue = step > 0 ? Math.round((rawValue - min) / step) * step + min : rawValue;
|
|
3223
|
+
return clampValue(steppedValue);
|
|
2663
3224
|
},
|
|
2664
|
-
[
|
|
3225
|
+
[clampValue, min, range, step]
|
|
2665
3226
|
);
|
|
2666
|
-
const setValue =
|
|
3227
|
+
const setValue = useCallback13(
|
|
2667
3228
|
(newValue) => {
|
|
2668
|
-
const clampedValue =
|
|
3229
|
+
const clampedValue = clampValue(newValue);
|
|
2669
3230
|
if (value === void 0) {
|
|
2670
3231
|
setInternalValue(clampedValue);
|
|
2671
3232
|
}
|
|
3233
|
+
currentValueRef.current = clampedValue;
|
|
2672
3234
|
onChange?.(clampedValue);
|
|
2673
3235
|
},
|
|
2674
|
-
[
|
|
3236
|
+
[clampValue, onChange, value]
|
|
2675
3237
|
);
|
|
2676
|
-
const panResponder =
|
|
2677
|
-
PanResponder2.create({
|
|
3238
|
+
const panResponder = useMemo5(
|
|
3239
|
+
() => PanResponder2.create({
|
|
2678
3240
|
onStartShouldSetPanResponder: () => !disabled,
|
|
2679
3241
|
onMoveShouldSetPanResponder: () => !disabled,
|
|
2680
3242
|
onPanResponderGrant: () => {
|
|
3243
|
+
dragStartValueRef.current = currentValueRef.current;
|
|
2681
3244
|
setIsDragging(true);
|
|
2682
3245
|
},
|
|
2683
3246
|
onPanResponderMove: (_, gestureState) => {
|
|
2684
|
-
const position =
|
|
3247
|
+
const position = valueToPosition(dragStartValueRef.current) + gestureState.dx;
|
|
2685
3248
|
const newValue = getValueFromPosition(position);
|
|
2686
3249
|
setValue(newValue);
|
|
2687
3250
|
},
|
|
2688
3251
|
onPanResponderRelease: (_, gestureState) => {
|
|
2689
|
-
const position =
|
|
3252
|
+
const position = valueToPosition(dragStartValueRef.current) + gestureState.dx;
|
|
2690
3253
|
const newValue = getValueFromPosition(position);
|
|
2691
3254
|
setValue(newValue);
|
|
2692
3255
|
setIsDragging(false);
|
|
2693
3256
|
onChangeEnd?.(newValue);
|
|
3257
|
+
},
|
|
3258
|
+
onPanResponderTerminate: () => {
|
|
3259
|
+
setIsDragging(false);
|
|
2694
3260
|
}
|
|
2695
|
-
})
|
|
2696
|
-
|
|
2697
|
-
|
|
3261
|
+
}),
|
|
3262
|
+
[disabled, getValueFromPosition, onChangeEnd, setValue, valueToPosition]
|
|
3263
|
+
);
|
|
3264
|
+
const handleTrackPress = useCallback13(
|
|
2698
3265
|
(event) => {
|
|
2699
3266
|
if (disabled) return;
|
|
2700
3267
|
const { locationX } = event.nativeEvent;
|
|
@@ -2704,8 +3271,9 @@ function Slider({
|
|
|
2704
3271
|
},
|
|
2705
3272
|
[disabled, getValueFromPosition, setValue, onChangeEnd]
|
|
2706
3273
|
);
|
|
2707
|
-
const onLayout =
|
|
2708
|
-
|
|
3274
|
+
const onLayout = useCallback13((event) => {
|
|
3275
|
+
const width = event.nativeEvent.layout.width;
|
|
3276
|
+
trackWidthRef.current = width;
|
|
2709
3277
|
}, []);
|
|
2710
3278
|
return /* @__PURE__ */ jsxs9(AppView, { className: cn("py-2", className), children: [
|
|
2711
3279
|
showTooltip && isDragging && /* @__PURE__ */ jsxs9(
|
|
@@ -2721,8 +3289,8 @@ function Slider({
|
|
|
2721
3289
|
}
|
|
2722
3290
|
],
|
|
2723
3291
|
children: [
|
|
2724
|
-
/* @__PURE__ */
|
|
2725
|
-
/* @__PURE__ */
|
|
3292
|
+
/* @__PURE__ */ jsx28(AppText, { size: "xs", style: { color: colors.text }, children: Math.round(currentValue) }),
|
|
3293
|
+
/* @__PURE__ */ jsx28(
|
|
2726
3294
|
AppView,
|
|
2727
3295
|
{
|
|
2728
3296
|
style: [
|
|
@@ -2747,7 +3315,7 @@ function Slider({
|
|
|
2747
3315
|
],
|
|
2748
3316
|
onTouchEnd: handleTrackPress,
|
|
2749
3317
|
children: [
|
|
2750
|
-
/* @__PURE__ */
|
|
3318
|
+
/* @__PURE__ */ jsx28(
|
|
2751
3319
|
AppView,
|
|
2752
3320
|
{
|
|
2753
3321
|
className: "rounded-full",
|
|
@@ -2760,7 +3328,7 @@ function Slider({
|
|
|
2760
3328
|
]
|
|
2761
3329
|
}
|
|
2762
3330
|
),
|
|
2763
|
-
/* @__PURE__ */
|
|
3331
|
+
/* @__PURE__ */ jsx28(
|
|
2764
3332
|
AppView,
|
|
2765
3333
|
{
|
|
2766
3334
|
className: "absolute rounded-full items-center justify-center",
|
|
@@ -2777,7 +3345,7 @@ function Slider({
|
|
|
2777
3345
|
}
|
|
2778
3346
|
],
|
|
2779
3347
|
...panResponder.panHandlers,
|
|
2780
|
-
children: /* @__PURE__ */
|
|
3348
|
+
children: /* @__PURE__ */ jsx28(
|
|
2781
3349
|
AppView,
|
|
2782
3350
|
{
|
|
2783
3351
|
className: "rounded-full",
|
|
@@ -2837,16 +3405,215 @@ var styles9 = StyleSheet10.create({
|
|
|
2837
3405
|
});
|
|
2838
3406
|
|
|
2839
3407
|
// src/ui/form/Select.tsx
|
|
2840
|
-
import { useState as
|
|
3408
|
+
import { useState as useState19, useCallback as useCallback14, useMemo as useMemo7 } from "react";
|
|
2841
3409
|
import {
|
|
2842
|
-
Modal as Modal3,
|
|
2843
3410
|
View as View7,
|
|
2844
3411
|
TouchableOpacity as TouchableOpacity5,
|
|
2845
3412
|
FlatList as FlatList2,
|
|
2846
3413
|
TextInput as TextInput2,
|
|
3414
|
+
StyleSheet as StyleSheet12
|
|
3415
|
+
} from "react-native";
|
|
3416
|
+
|
|
3417
|
+
// src/ui/form/BottomSheetModal.tsx
|
|
3418
|
+
import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef9, useState as useState18 } from "react";
|
|
3419
|
+
import {
|
|
3420
|
+
Animated as Animated3,
|
|
3421
|
+
Modal as Modal3,
|
|
3422
|
+
PanResponder as PanResponder3,
|
|
2847
3423
|
StyleSheet as StyleSheet11
|
|
2848
3424
|
} from "react-native";
|
|
2849
|
-
import {
|
|
3425
|
+
import { jsx as jsx29, jsxs as jsxs10 } from "nativewind/jsx-runtime";
|
|
3426
|
+
var SHEET_OPEN_DURATION = 220;
|
|
3427
|
+
var SHEET_CLOSE_DURATION = 180;
|
|
3428
|
+
var OVERLAY_OPEN_DURATION = 180;
|
|
3429
|
+
var OVERLAY_CLOSE_DURATION = 160;
|
|
3430
|
+
var SHEET_INITIAL_OFFSET = 24;
|
|
3431
|
+
var SHEET_CLOSED_OFFSET = 240;
|
|
3432
|
+
var SHEET_DRAG_CLOSE_THRESHOLD = 72;
|
|
3433
|
+
var SHEET_DRAG_VELOCITY_THRESHOLD = 1;
|
|
3434
|
+
function createAnimatedValue3(value) {
|
|
3435
|
+
const AnimatedValue = Animated3.Value;
|
|
3436
|
+
try {
|
|
3437
|
+
return new AnimatedValue(value);
|
|
3438
|
+
} catch {
|
|
3439
|
+
return AnimatedValue(value);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
function startAnimations(animations, onComplete) {
|
|
3443
|
+
if (animations.length === 0) {
|
|
3444
|
+
onComplete?.();
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
let completed = 0;
|
|
3448
|
+
animations.forEach((animation) => {
|
|
3449
|
+
animation.start(() => {
|
|
3450
|
+
completed += 1;
|
|
3451
|
+
if (completed >= animations.length) {
|
|
3452
|
+
onComplete?.();
|
|
3453
|
+
}
|
|
3454
|
+
});
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
function BottomSheetModal({
|
|
3458
|
+
visible,
|
|
3459
|
+
onRequestClose,
|
|
3460
|
+
overlayColor,
|
|
3461
|
+
surfaceColor,
|
|
3462
|
+
children,
|
|
3463
|
+
closeOnBackdropPress = false,
|
|
3464
|
+
maxHeight = "70%",
|
|
3465
|
+
showHandle = true,
|
|
3466
|
+
contentClassName,
|
|
3467
|
+
contentStyle,
|
|
3468
|
+
swipeToClose = true,
|
|
3469
|
+
backdropTestID = "bottom-sheet-backdrop",
|
|
3470
|
+
handleTestID = "bottom-sheet-handle"
|
|
3471
|
+
}) {
|
|
3472
|
+
const [renderVisible, setRenderVisible] = useState18(visible);
|
|
3473
|
+
const overlayOpacity = useRef9(createAnimatedValue3(0)).current;
|
|
3474
|
+
const sheetTranslateY = useRef9(createAnimatedValue3(SHEET_INITIAL_OFFSET)).current;
|
|
3475
|
+
const isDraggingRef = useRef9(false);
|
|
3476
|
+
const handlePanResponder = useMemo6(
|
|
3477
|
+
() => PanResponder3.create({
|
|
3478
|
+
onMoveShouldSetPanResponder: (_event, gestureState) => {
|
|
3479
|
+
if (!visible || !swipeToClose) return false;
|
|
3480
|
+
const isVertical = Math.abs(gestureState.dy) > Math.abs(gestureState.dx);
|
|
3481
|
+
return isVertical && gestureState.dy > 6;
|
|
3482
|
+
},
|
|
3483
|
+
onPanResponderGrant: () => {
|
|
3484
|
+
isDraggingRef.current = true;
|
|
3485
|
+
},
|
|
3486
|
+
onPanResponderMove: (_event, gestureState) => {
|
|
3487
|
+
if (!visible || !swipeToClose) return;
|
|
3488
|
+
sheetTranslateY.setValue(Math.max(0, Math.min(SHEET_CLOSED_OFFSET, gestureState.dy)));
|
|
3489
|
+
},
|
|
3490
|
+
onPanResponderRelease: (_event, gestureState) => {
|
|
3491
|
+
isDraggingRef.current = false;
|
|
3492
|
+
if (!visible || !swipeToClose) {
|
|
3493
|
+
sheetTranslateY.setValue(0);
|
|
3494
|
+
return;
|
|
3495
|
+
}
|
|
3496
|
+
const shouldClose = gestureState.dy >= SHEET_DRAG_CLOSE_THRESHOLD || gestureState.vy >= SHEET_DRAG_VELOCITY_THRESHOLD;
|
|
3497
|
+
if (shouldClose) {
|
|
3498
|
+
onRequestClose();
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3501
|
+
Animated3.timing(sheetTranslateY, {
|
|
3502
|
+
toValue: 0,
|
|
3503
|
+
duration: SHEET_OPEN_DURATION,
|
|
3504
|
+
useNativeDriver: true
|
|
3505
|
+
}).start();
|
|
3506
|
+
},
|
|
3507
|
+
onPanResponderTerminate: () => {
|
|
3508
|
+
isDraggingRef.current = false;
|
|
3509
|
+
Animated3.timing(sheetTranslateY, {
|
|
3510
|
+
toValue: 0,
|
|
3511
|
+
duration: SHEET_OPEN_DURATION,
|
|
3512
|
+
useNativeDriver: true
|
|
3513
|
+
}).start();
|
|
3514
|
+
}
|
|
3515
|
+
}),
|
|
3516
|
+
[onRequestClose, sheetTranslateY, swipeToClose, visible]
|
|
3517
|
+
);
|
|
3518
|
+
useEffect9(() => {
|
|
3519
|
+
if (visible) {
|
|
3520
|
+
setRenderVisible(true);
|
|
3521
|
+
overlayOpacity.setValue(0);
|
|
3522
|
+
sheetTranslateY.setValue(SHEET_INITIAL_OFFSET);
|
|
3523
|
+
startAnimations([
|
|
3524
|
+
Animated3.timing(overlayOpacity, {
|
|
3525
|
+
toValue: 1,
|
|
3526
|
+
duration: OVERLAY_OPEN_DURATION,
|
|
3527
|
+
useNativeDriver: true
|
|
3528
|
+
}),
|
|
3529
|
+
Animated3.timing(sheetTranslateY, {
|
|
3530
|
+
toValue: 0,
|
|
3531
|
+
duration: SHEET_OPEN_DURATION,
|
|
3532
|
+
useNativeDriver: true
|
|
3533
|
+
})
|
|
3534
|
+
]);
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
if (!renderVisible) return;
|
|
3538
|
+
isDraggingRef.current = false;
|
|
3539
|
+
startAnimations(
|
|
3540
|
+
[
|
|
3541
|
+
Animated3.timing(overlayOpacity, {
|
|
3542
|
+
toValue: 0,
|
|
3543
|
+
duration: OVERLAY_CLOSE_DURATION,
|
|
3544
|
+
useNativeDriver: true
|
|
3545
|
+
}),
|
|
3546
|
+
Animated3.timing(sheetTranslateY, {
|
|
3547
|
+
toValue: SHEET_CLOSED_OFFSET,
|
|
3548
|
+
duration: SHEET_CLOSE_DURATION,
|
|
3549
|
+
useNativeDriver: true
|
|
3550
|
+
})
|
|
3551
|
+
],
|
|
3552
|
+
() => {
|
|
3553
|
+
setRenderVisible(false);
|
|
3554
|
+
}
|
|
3555
|
+
);
|
|
3556
|
+
}, [overlayOpacity, renderVisible, sheetTranslateY, visible]);
|
|
3557
|
+
return /* @__PURE__ */ jsx29(Modal3, { visible: renderVisible, transparent: true, animationType: "none", onRequestClose, children: /* @__PURE__ */ jsxs10(AppView, { flex: true, justify: "end", children: [
|
|
3558
|
+
/* @__PURE__ */ jsx29(
|
|
3559
|
+
Animated3.View,
|
|
3560
|
+
{
|
|
3561
|
+
pointerEvents: "none",
|
|
3562
|
+
style: [StyleSheet11.absoluteFillObject, { backgroundColor: overlayColor, opacity: overlayOpacity }]
|
|
3563
|
+
}
|
|
3564
|
+
),
|
|
3565
|
+
closeOnBackdropPress && /* @__PURE__ */ jsx29(AppPressable, { testID: backdropTestID, className: "flex-1", onPress: onRequestClose }),
|
|
3566
|
+
/* @__PURE__ */ jsxs10(
|
|
3567
|
+
Animated3.View,
|
|
3568
|
+
{
|
|
3569
|
+
className: contentClassName,
|
|
3570
|
+
style: [
|
|
3571
|
+
styles10.sheet,
|
|
3572
|
+
{
|
|
3573
|
+
backgroundColor: surfaceColor,
|
|
3574
|
+
maxHeight,
|
|
3575
|
+
transform: [{ translateY: sheetTranslateY }]
|
|
3576
|
+
},
|
|
3577
|
+
contentStyle
|
|
3578
|
+
],
|
|
3579
|
+
children: [
|
|
3580
|
+
showHandle && /* @__PURE__ */ jsx29(
|
|
3581
|
+
AppView,
|
|
3582
|
+
{
|
|
3583
|
+
testID: handleTestID,
|
|
3584
|
+
center: true,
|
|
3585
|
+
className: "pt-2 pb-1",
|
|
3586
|
+
...swipeToClose ? handlePanResponder.panHandlers : void 0,
|
|
3587
|
+
children: /* @__PURE__ */ jsx29(AppView, { style: styles10.handle })
|
|
3588
|
+
}
|
|
3589
|
+
),
|
|
3590
|
+
children
|
|
3591
|
+
]
|
|
3592
|
+
}
|
|
3593
|
+
)
|
|
3594
|
+
] }) });
|
|
3595
|
+
}
|
|
3596
|
+
var styles10 = StyleSheet11.create({
|
|
3597
|
+
handle: {
|
|
3598
|
+
width: 36,
|
|
3599
|
+
height: 4,
|
|
3600
|
+
borderRadius: 999,
|
|
3601
|
+
backgroundColor: "rgba(156,163,175,0.7)"
|
|
3602
|
+
},
|
|
3603
|
+
sheet: {
|
|
3604
|
+
borderTopLeftRadius: 24,
|
|
3605
|
+
borderTopRightRadius: 24,
|
|
3606
|
+
overflow: "hidden",
|
|
3607
|
+
shadowColor: "#000000",
|
|
3608
|
+
shadowOffset: { width: 0, height: -4 },
|
|
3609
|
+
shadowOpacity: 0.12,
|
|
3610
|
+
shadowRadius: 16,
|
|
3611
|
+
elevation: 12
|
|
3612
|
+
}
|
|
3613
|
+
});
|
|
3614
|
+
|
|
3615
|
+
// src/ui/form/Select.tsx
|
|
3616
|
+
import { Fragment as Fragment2, jsx as jsx30, jsxs as jsxs11 } from "nativewind/jsx-runtime";
|
|
2850
3617
|
function formatSelectedCountText(template, count) {
|
|
2851
3618
|
return template.replace("{{count}}", String(count));
|
|
2852
3619
|
}
|
|
@@ -2869,24 +3636,24 @@ function Select({
|
|
|
2869
3636
|
className
|
|
2870
3637
|
}) {
|
|
2871
3638
|
const colors = useFormThemeColors();
|
|
2872
|
-
const [visible, setVisible] =
|
|
2873
|
-
const [searchKeyword, setSearchKeyword] =
|
|
2874
|
-
const selectedValues =
|
|
3639
|
+
const [visible, setVisible] = useState19(false);
|
|
3640
|
+
const [searchKeyword, setSearchKeyword] = useState19("");
|
|
3641
|
+
const selectedValues = useMemo7(() => {
|
|
2875
3642
|
if (multiple) {
|
|
2876
3643
|
return Array.isArray(value) ? value : [];
|
|
2877
3644
|
}
|
|
2878
3645
|
return value ? [value] : [];
|
|
2879
3646
|
}, [value, multiple]);
|
|
2880
|
-
const displayText =
|
|
3647
|
+
const displayText = useMemo7(() => {
|
|
2881
3648
|
if (selectedValues.length === 0) return placeholder;
|
|
2882
3649
|
const selectedLabels = options.filter((opt) => selectedValues.includes(opt.value)).map((opt) => opt.label);
|
|
2883
3650
|
return selectedLabels.join(", ") || placeholder;
|
|
2884
3651
|
}, [selectedValues, options, placeholder]);
|
|
2885
|
-
const filteredOptions =
|
|
3652
|
+
const filteredOptions = useMemo7(() => {
|
|
2886
3653
|
if (!searchable || !searchKeyword) return options;
|
|
2887
3654
|
return options.filter((opt) => opt.label.toLowerCase().includes(searchKeyword.toLowerCase()));
|
|
2888
3655
|
}, [options, searchable, searchKeyword]);
|
|
2889
|
-
const handleSelect =
|
|
3656
|
+
const handleSelect = useCallback14(
|
|
2890
3657
|
(optionValue) => {
|
|
2891
3658
|
if (multiple) {
|
|
2892
3659
|
const currentValues = Array.isArray(value) ? value : [];
|
|
@@ -2897,26 +3664,26 @@ function Select({
|
|
|
2897
3664
|
setVisible(false);
|
|
2898
3665
|
}
|
|
2899
3666
|
},
|
|
2900
|
-
[multiple,
|
|
3667
|
+
[multiple, onChange, value]
|
|
2901
3668
|
);
|
|
2902
|
-
const handleClear =
|
|
3669
|
+
const handleClear = useCallback14(
|
|
2903
3670
|
(e) => {
|
|
2904
3671
|
e.stopPropagation();
|
|
2905
3672
|
onChange?.(multiple ? [] : "");
|
|
2906
3673
|
},
|
|
2907
3674
|
[multiple, onChange]
|
|
2908
3675
|
);
|
|
2909
|
-
const handleSearch =
|
|
3676
|
+
const handleSearch = useCallback14(
|
|
2910
3677
|
(text) => {
|
|
2911
3678
|
setSearchKeyword(text);
|
|
2912
3679
|
onSearch?.(text);
|
|
2913
3680
|
},
|
|
2914
3681
|
[onSearch]
|
|
2915
3682
|
);
|
|
2916
|
-
const renderOption =
|
|
3683
|
+
const renderOption = useCallback14(
|
|
2917
3684
|
({ item }) => {
|
|
2918
3685
|
const isSelected = selectedValues.includes(item.value);
|
|
2919
|
-
return /* @__PURE__ */
|
|
3686
|
+
return /* @__PURE__ */ jsxs11(
|
|
2920
3687
|
AppPressable,
|
|
2921
3688
|
{
|
|
2922
3689
|
className: cn(
|
|
@@ -2924,22 +3691,22 @@ function Select({
|
|
|
2924
3691
|
isSelected && "bg-primary-50"
|
|
2925
3692
|
),
|
|
2926
3693
|
style: [
|
|
2927
|
-
|
|
3694
|
+
styles11.optionItem,
|
|
2928
3695
|
{ borderBottomColor: colors.divider },
|
|
2929
3696
|
isSelected && { backgroundColor: colors.primarySurface }
|
|
2930
3697
|
],
|
|
2931
3698
|
onPress: () => handleSelect(item.value),
|
|
2932
3699
|
children: [
|
|
2933
|
-
/* @__PURE__ */
|
|
2934
|
-
isSelected && /* @__PURE__ */
|
|
3700
|
+
/* @__PURE__ */ jsx30(AppText, { style: { color: isSelected ? colors.primary : colors.text }, children: item.label }),
|
|
3701
|
+
isSelected && /* @__PURE__ */ jsx30(Icon, { name: "check", size: "sm", color: "primary-500" })
|
|
2935
3702
|
]
|
|
2936
3703
|
}
|
|
2937
3704
|
);
|
|
2938
3705
|
},
|
|
2939
3706
|
[selectedValues, handleSelect, colors]
|
|
2940
3707
|
);
|
|
2941
|
-
return /* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
3708
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
3709
|
+
/* @__PURE__ */ jsxs11(
|
|
2943
3710
|
AppPressable,
|
|
2944
3711
|
{
|
|
2945
3712
|
className: cn(
|
|
@@ -2947,11 +3714,11 @@ function Select({
|
|
|
2947
3714
|
disabled ? "opacity-60" : "",
|
|
2948
3715
|
className
|
|
2949
3716
|
),
|
|
2950
|
-
style: [
|
|
3717
|
+
style: [styles11.trigger, { backgroundColor: colors.surface, borderColor: colors.border }],
|
|
2951
3718
|
disabled,
|
|
2952
3719
|
onPress: () => setVisible(true),
|
|
2953
3720
|
children: [
|
|
2954
|
-
/* @__PURE__ */
|
|
3721
|
+
/* @__PURE__ */ jsx30(
|
|
2955
3722
|
AppText,
|
|
2956
3723
|
{
|
|
2957
3724
|
className: "flex-1",
|
|
@@ -2960,111 +3727,106 @@ function Select({
|
|
|
2960
3727
|
children: displayText
|
|
2961
3728
|
}
|
|
2962
3729
|
),
|
|
2963
|
-
/* @__PURE__ */
|
|
2964
|
-
clearable && selectedValues.length > 0 && !disabled && /* @__PURE__ */
|
|
2965
|
-
/* @__PURE__ */
|
|
3730
|
+
/* @__PURE__ */ jsxs11(View7, { className: "flex-row items-center", children: [
|
|
3731
|
+
clearable && selectedValues.length > 0 && !disabled && /* @__PURE__ */ jsx30(TouchableOpacity5, { onPress: handleClear, className: "mr-2 p-1", children: /* @__PURE__ */ jsx30(Icon, { name: "close", size: "sm", color: colors.icon }) }),
|
|
3732
|
+
/* @__PURE__ */ jsx30(Icon, { name: "keyboard-arrow-down", size: "md", color: colors.icon })
|
|
2966
3733
|
] })
|
|
2967
3734
|
]
|
|
2968
3735
|
}
|
|
2969
3736
|
),
|
|
2970
|
-
/* @__PURE__ */
|
|
2971
|
-
|
|
3737
|
+
/* @__PURE__ */ jsx30(
|
|
3738
|
+
BottomSheetModal,
|
|
2972
3739
|
{
|
|
2973
3740
|
visible,
|
|
2974
|
-
transparent: true,
|
|
2975
|
-
animationType: "slide",
|
|
2976
3741
|
onRequestClose: () => setVisible(false),
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
children: /* @__PURE__ */ jsxs10(
|
|
3003
|
-
AppView,
|
|
3004
|
-
{
|
|
3005
|
-
row: true,
|
|
3006
|
-
items: "center",
|
|
3007
|
-
className: "px-3 py-2 rounded-lg",
|
|
3008
|
-
style: { backgroundColor: colors.surfaceMuted },
|
|
3009
|
-
children: [
|
|
3010
|
-
/* @__PURE__ */ jsx28(View7, { style: { marginRight: 8 }, children: /* @__PURE__ */ jsx28(Icon, { name: "search", size: "sm", color: colors.icon }) }),
|
|
3011
|
-
/* @__PURE__ */ jsx28(
|
|
3012
|
-
TextInput2,
|
|
3013
|
-
{
|
|
3014
|
-
className: "flex-1 text-base",
|
|
3015
|
-
style: { color: colors.text },
|
|
3016
|
-
placeholder: searchPlaceholder,
|
|
3017
|
-
placeholderTextColor: colors.textMuted,
|
|
3018
|
-
value: searchKeyword,
|
|
3019
|
-
onChangeText: handleSearch,
|
|
3020
|
-
autoFocus: true
|
|
3021
|
-
}
|
|
3022
|
-
),
|
|
3023
|
-
searchKeyword.length > 0 && /* @__PURE__ */ jsx28(TouchableOpacity5, { onPress: () => setSearchKeyword(""), children: /* @__PURE__ */ jsx28(Icon, { name: "close", size: "sm", color: colors.icon }) })
|
|
3024
|
-
]
|
|
3025
|
-
}
|
|
3026
|
-
)
|
|
3027
|
-
}
|
|
3028
|
-
),
|
|
3029
|
-
/* @__PURE__ */ jsx28(
|
|
3030
|
-
FlatList2,
|
|
3031
|
-
{
|
|
3032
|
-
data: filteredOptions,
|
|
3033
|
-
keyExtractor: (item) => item.value,
|
|
3034
|
-
renderItem: renderOption,
|
|
3035
|
-
ListEmptyComponent: /* @__PURE__ */ jsx28(AppView, { center: true, className: "py-8", children: /* @__PURE__ */ jsx28(AppText, { style: { color: colors.textMuted }, children: emptyText }) })
|
|
3036
|
-
}
|
|
3037
|
-
),
|
|
3038
|
-
multiple && /* @__PURE__ */ jsxs10(
|
|
3742
|
+
overlayColor: colors.overlay,
|
|
3743
|
+
surfaceColor: colors.surface,
|
|
3744
|
+
closeOnBackdropPress: true,
|
|
3745
|
+
contentClassName: "max-h-[70%]",
|
|
3746
|
+
children: /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
3747
|
+
/* @__PURE__ */ jsxs11(
|
|
3748
|
+
AppView,
|
|
3749
|
+
{
|
|
3750
|
+
row: true,
|
|
3751
|
+
between: true,
|
|
3752
|
+
items: "center",
|
|
3753
|
+
className: "px-4 py-3",
|
|
3754
|
+
style: [styles11.header, { borderBottomColor: colors.divider }],
|
|
3755
|
+
children: [
|
|
3756
|
+
/* @__PURE__ */ jsx30(AppText, { className: "text-lg font-semibold", style: { color: colors.text }, children: multiple ? multipleSelectTitle : singleSelectTitle }),
|
|
3757
|
+
/* @__PURE__ */ jsx30(TouchableOpacity5, { onPress: () => setVisible(false), children: /* @__PURE__ */ jsx30(Icon, { name: "close", size: "md", color: colors.icon }) })
|
|
3758
|
+
]
|
|
3759
|
+
}
|
|
3760
|
+
),
|
|
3761
|
+
searchable && /* @__PURE__ */ jsx30(
|
|
3762
|
+
AppView,
|
|
3763
|
+
{
|
|
3764
|
+
className: "px-4 py-3",
|
|
3765
|
+
style: [styles11.searchBox, { borderBottomColor: colors.divider }],
|
|
3766
|
+
children: /* @__PURE__ */ jsxs11(
|
|
3039
3767
|
AppView,
|
|
3040
3768
|
{
|
|
3041
3769
|
row: true,
|
|
3042
|
-
between: true,
|
|
3043
3770
|
items: "center",
|
|
3044
|
-
className: "px-
|
|
3045
|
-
style:
|
|
3771
|
+
className: "px-3 py-2 rounded-lg",
|
|
3772
|
+
style: { backgroundColor: colors.surfaceMuted },
|
|
3046
3773
|
children: [
|
|
3047
|
-
/* @__PURE__ */
|
|
3048
|
-
/* @__PURE__ */
|
|
3049
|
-
|
|
3774
|
+
/* @__PURE__ */ jsx30(View7, { style: { marginRight: 8 }, children: /* @__PURE__ */ jsx30(Icon, { name: "search", size: "sm", color: colors.icon }) }),
|
|
3775
|
+
/* @__PURE__ */ jsx30(
|
|
3776
|
+
TextInput2,
|
|
3050
3777
|
{
|
|
3051
|
-
className: "
|
|
3052
|
-
style: {
|
|
3053
|
-
|
|
3054
|
-
|
|
3778
|
+
className: "flex-1 text-base",
|
|
3779
|
+
style: { color: colors.text },
|
|
3780
|
+
placeholder: searchPlaceholder,
|
|
3781
|
+
placeholderTextColor: colors.textMuted,
|
|
3782
|
+
value: searchKeyword,
|
|
3783
|
+
onChangeText: handleSearch,
|
|
3784
|
+
autoFocus: true
|
|
3055
3785
|
}
|
|
3056
|
-
)
|
|
3786
|
+
),
|
|
3787
|
+
searchKeyword.length > 0 && /* @__PURE__ */ jsx30(TouchableOpacity5, { onPress: () => setSearchKeyword(""), children: /* @__PURE__ */ jsx30(Icon, { name: "close", size: "sm", color: colors.icon }) })
|
|
3057
3788
|
]
|
|
3058
3789
|
}
|
|
3059
3790
|
)
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3791
|
+
}
|
|
3792
|
+
),
|
|
3793
|
+
/* @__PURE__ */ jsx30(
|
|
3794
|
+
FlatList2,
|
|
3795
|
+
{
|
|
3796
|
+
data: filteredOptions,
|
|
3797
|
+
keyExtractor: (item, index) => `${item.value}-${index}`,
|
|
3798
|
+
renderItem: renderOption,
|
|
3799
|
+
ListEmptyComponent: /* @__PURE__ */ jsx30(AppView, { center: true, className: "py-8", children: /* @__PURE__ */ jsx30(AppText, { style: { color: colors.textMuted }, children: emptyText }) })
|
|
3800
|
+
}
|
|
3801
|
+
),
|
|
3802
|
+
multiple && /* @__PURE__ */ jsxs11(
|
|
3803
|
+
AppView,
|
|
3804
|
+
{
|
|
3805
|
+
row: true,
|
|
3806
|
+
between: true,
|
|
3807
|
+
items: "center",
|
|
3808
|
+
className: "px-4 py-3",
|
|
3809
|
+
style: [styles11.footer, { borderTopColor: colors.divider }],
|
|
3810
|
+
children: [
|
|
3811
|
+
/* @__PURE__ */ jsx30(AppText, { style: { color: colors.textMuted }, children: formatSelectedCountText(selectedCountText, selectedValues.length) }),
|
|
3812
|
+
/* @__PURE__ */ jsx30(
|
|
3813
|
+
TouchableOpacity5,
|
|
3814
|
+
{
|
|
3815
|
+
className: "px-4 py-2 rounded-lg",
|
|
3816
|
+
style: { backgroundColor: colors.primary },
|
|
3817
|
+
onPress: () => setVisible(false),
|
|
3818
|
+
children: /* @__PURE__ */ jsx30(AppText, { className: "font-medium", style: { color: colors.textInverse }, children: confirmText })
|
|
3819
|
+
}
|
|
3820
|
+
)
|
|
3821
|
+
]
|
|
3822
|
+
}
|
|
3823
|
+
)
|
|
3824
|
+
] })
|
|
3063
3825
|
}
|
|
3064
3826
|
)
|
|
3065
3827
|
] });
|
|
3066
3828
|
}
|
|
3067
|
-
var
|
|
3829
|
+
var styles11 = StyleSheet12.create({
|
|
3068
3830
|
trigger: {
|
|
3069
3831
|
borderWidth: 0.5
|
|
3070
3832
|
},
|
|
@@ -3082,126 +3844,306 @@ var styles10 = StyleSheet11.create({
|
|
|
3082
3844
|
}
|
|
3083
3845
|
});
|
|
3084
3846
|
|
|
3085
|
-
// src/ui/form/
|
|
3086
|
-
import {
|
|
3087
|
-
import {
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3847
|
+
// src/ui/form/Picker.tsx
|
|
3848
|
+
import { useCallback as useCallback15, useEffect as useEffect10, useMemo as useMemo8, useRef as useRef10, useState as useState20 } from "react";
|
|
3849
|
+
import {
|
|
3850
|
+
ScrollView as ScrollView2,
|
|
3851
|
+
StyleSheet as StyleSheet13,
|
|
3852
|
+
TouchableOpacity as TouchableOpacity6
|
|
3853
|
+
} from "react-native";
|
|
3854
|
+
import { Fragment as Fragment3, jsx as jsx31, jsxs as jsxs12 } from "nativewind/jsx-runtime";
|
|
3855
|
+
function findFirstEnabledValue(column) {
|
|
3856
|
+
return column.options.find((option) => !option.disabled)?.value;
|
|
3857
|
+
}
|
|
3858
|
+
function normalizeValues(columns, values) {
|
|
3859
|
+
return columns.map((column, index) => {
|
|
3860
|
+
const candidate = values?.[index];
|
|
3861
|
+
const matched = column.options.find((option) => option.value === candidate && !option.disabled);
|
|
3862
|
+
return matched?.value ?? findFirstEnabledValue(column) ?? column.options[0]?.value ?? "";
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
function WheelPickerColumn({
|
|
3866
|
+
colors,
|
|
3867
|
+
column,
|
|
3868
|
+
onChange,
|
|
3869
|
+
rowHeight,
|
|
3092
3870
|
selectedValue,
|
|
3093
|
-
onSelect,
|
|
3094
|
-
isDisabled,
|
|
3095
|
-
formatLabel = (value) => String(value),
|
|
3096
3871
|
showDivider = false,
|
|
3097
|
-
|
|
3872
|
+
visibleRows
|
|
3098
3873
|
}) {
|
|
3099
|
-
|
|
3874
|
+
const scrollRef = useRef10(null);
|
|
3875
|
+
const paddingRows = Math.floor(visibleRows / 2);
|
|
3876
|
+
const selectedIndex = Math.max(
|
|
3877
|
+
0,
|
|
3878
|
+
column.options.findIndex((option) => option.value === selectedValue)
|
|
3879
|
+
);
|
|
3880
|
+
const scrollToIndex = useCallback15(
|
|
3881
|
+
(index, animated) => {
|
|
3882
|
+
scrollRef.current?.scrollTo?.({ y: index * rowHeight, animated });
|
|
3883
|
+
},
|
|
3884
|
+
[rowHeight]
|
|
3885
|
+
);
|
|
3886
|
+
const selectNearestEnabled = useCallback15(
|
|
3887
|
+
(targetIndex) => {
|
|
3888
|
+
if (column.options.length === 0) return;
|
|
3889
|
+
const maxIndex = column.options.length - 1;
|
|
3890
|
+
const clampedIndex = Math.max(0, Math.min(maxIndex, targetIndex));
|
|
3891
|
+
const exactOption = column.options[clampedIndex];
|
|
3892
|
+
if (exactOption && !exactOption.disabled) {
|
|
3893
|
+
onChange(exactOption.value);
|
|
3894
|
+
scrollToIndex(clampedIndex, true);
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
for (let distance = 1; distance <= maxIndex; distance += 1) {
|
|
3898
|
+
const prevIndex = clampedIndex - distance;
|
|
3899
|
+
if (prevIndex >= 0) {
|
|
3900
|
+
const prevOption = column.options[prevIndex];
|
|
3901
|
+
if (prevOption && !prevOption.disabled) {
|
|
3902
|
+
onChange(prevOption.value);
|
|
3903
|
+
scrollToIndex(prevIndex, true);
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
const nextIndex = clampedIndex + distance;
|
|
3908
|
+
if (nextIndex <= maxIndex) {
|
|
3909
|
+
const nextOption = column.options[nextIndex];
|
|
3910
|
+
if (nextOption && !nextOption.disabled) {
|
|
3911
|
+
onChange(nextOption.value);
|
|
3912
|
+
scrollToIndex(nextIndex, true);
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
},
|
|
3918
|
+
[column.options, onChange, scrollToIndex]
|
|
3919
|
+
);
|
|
3920
|
+
const handleScrollEnd = useCallback15(
|
|
3921
|
+
(event) => {
|
|
3922
|
+
const offsetY = event.nativeEvent.contentOffset?.y ?? 0;
|
|
3923
|
+
selectNearestEnabled(Math.round(offsetY / rowHeight));
|
|
3924
|
+
},
|
|
3925
|
+
[rowHeight, selectNearestEnabled]
|
|
3926
|
+
);
|
|
3927
|
+
useEffect10(() => {
|
|
3928
|
+
scrollToIndex(selectedIndex, false);
|
|
3929
|
+
}, [scrollToIndex, selectedIndex]);
|
|
3930
|
+
return /* @__PURE__ */ jsxs12(
|
|
3100
3931
|
AppView,
|
|
3101
3932
|
{
|
|
3102
3933
|
flex: true,
|
|
3103
3934
|
style: [
|
|
3104
|
-
showDivider
|
|
3935
|
+
showDivider ? styles12.columnDivider : void 0,
|
|
3105
3936
|
showDivider ? { borderRightColor: colors.divider } : void 0
|
|
3106
3937
|
],
|
|
3107
3938
|
children: [
|
|
3108
|
-
/* @__PURE__ */
|
|
3109
|
-
/* @__PURE__ */
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3939
|
+
column.title && /* @__PURE__ */ jsx31(AppView, { center: true, className: "py-2", style: { backgroundColor: colors.headerSurface }, children: /* @__PURE__ */ jsx31(AppText, { className: "text-sm font-medium", style: { color: colors.textMuted }, children: column.title }) }),
|
|
3940
|
+
/* @__PURE__ */ jsxs12(
|
|
3941
|
+
AppView,
|
|
3942
|
+
{
|
|
3943
|
+
style: [
|
|
3944
|
+
styles12.wheelViewport,
|
|
3945
|
+
{
|
|
3946
|
+
height: rowHeight * visibleRows,
|
|
3947
|
+
backgroundColor: colors.surface
|
|
3948
|
+
}
|
|
3949
|
+
],
|
|
3950
|
+
children: [
|
|
3951
|
+
/* @__PURE__ */ jsx31(
|
|
3952
|
+
ScrollView2,
|
|
3121
3953
|
{
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3954
|
+
ref: scrollRef,
|
|
3955
|
+
showsVerticalScrollIndicator: false,
|
|
3956
|
+
snapToInterval: rowHeight,
|
|
3957
|
+
decelerationRate: "fast",
|
|
3958
|
+
onMomentumScrollEnd: handleScrollEnd,
|
|
3959
|
+
onScrollEndDrag: handleScrollEnd,
|
|
3960
|
+
contentContainerStyle: { paddingVertical: rowHeight * paddingRows },
|
|
3961
|
+
children: column.options.map((option, index) => {
|
|
3962
|
+
const selected = option.value === selectedValue;
|
|
3963
|
+
return /* @__PURE__ */ jsx31(
|
|
3964
|
+
TouchableOpacity6,
|
|
3965
|
+
{
|
|
3966
|
+
disabled: option.disabled,
|
|
3967
|
+
onPress: () => {
|
|
3968
|
+
if (option.disabled) return;
|
|
3969
|
+
onChange(option.value);
|
|
3970
|
+
scrollToIndex(index, true);
|
|
3971
|
+
},
|
|
3972
|
+
style: [
|
|
3973
|
+
styles12.optionButton,
|
|
3974
|
+
{
|
|
3975
|
+
height: rowHeight,
|
|
3976
|
+
opacity: option.disabled ? 0.35 : selected ? 1 : 0.72
|
|
3977
|
+
}
|
|
3978
|
+
],
|
|
3979
|
+
children: /* @__PURE__ */ jsx31(
|
|
3980
|
+
AppText,
|
|
3981
|
+
{
|
|
3982
|
+
className: cn(selected ? "font-semibold" : void 0),
|
|
3983
|
+
style: { color: selected ? colors.primary : colors.text },
|
|
3984
|
+
children: option.label
|
|
3985
|
+
}
|
|
3986
|
+
)
|
|
3987
|
+
},
|
|
3988
|
+
`${column.key}-${String(option.value)}-${index}`
|
|
3989
|
+
);
|
|
3990
|
+
})
|
|
3991
|
+
}
|
|
3992
|
+
),
|
|
3993
|
+
/* @__PURE__ */ jsx31(
|
|
3994
|
+
AppView,
|
|
3995
|
+
{
|
|
3996
|
+
pointerEvents: "none",
|
|
3997
|
+
style: [
|
|
3998
|
+
styles12.fadeMask,
|
|
3999
|
+
{
|
|
4000
|
+
top: 0,
|
|
4001
|
+
height: rowHeight * paddingRows
|
|
4002
|
+
}
|
|
4003
|
+
],
|
|
4004
|
+
children: [0.92, 0.72, 0.48, 0.24].map((opacity) => /* @__PURE__ */ jsx31(
|
|
4005
|
+
AppView,
|
|
4006
|
+
{
|
|
4007
|
+
flex: true,
|
|
4008
|
+
style: { backgroundColor: colors.surface, opacity }
|
|
4009
|
+
},
|
|
4010
|
+
`top-${opacity}`
|
|
4011
|
+
))
|
|
4012
|
+
}
|
|
4013
|
+
),
|
|
4014
|
+
/* @__PURE__ */ jsx31(
|
|
4015
|
+
AppView,
|
|
4016
|
+
{
|
|
4017
|
+
pointerEvents: "none",
|
|
4018
|
+
style: [
|
|
4019
|
+
styles12.fadeMask,
|
|
4020
|
+
{
|
|
4021
|
+
bottom: 0,
|
|
4022
|
+
height: rowHeight * paddingRows
|
|
4023
|
+
}
|
|
4024
|
+
],
|
|
4025
|
+
children: [0.24, 0.48, 0.72, 0.92].map((opacity) => /* @__PURE__ */ jsx31(
|
|
4026
|
+
AppView,
|
|
4027
|
+
{
|
|
4028
|
+
flex: true,
|
|
4029
|
+
style: { backgroundColor: colors.surface, opacity }
|
|
4030
|
+
},
|
|
4031
|
+
`bottom-${opacity}`
|
|
4032
|
+
))
|
|
4033
|
+
}
|
|
4034
|
+
),
|
|
4035
|
+
/* @__PURE__ */ jsx31(
|
|
4036
|
+
AppView,
|
|
4037
|
+
{
|
|
4038
|
+
pointerEvents: "none",
|
|
4039
|
+
style: [
|
|
4040
|
+
styles12.selectionOverlay,
|
|
4041
|
+
{
|
|
4042
|
+
top: rowHeight * paddingRows,
|
|
4043
|
+
height: rowHeight,
|
|
4044
|
+
borderColor: colors.divider,
|
|
4045
|
+
backgroundColor: colors.primarySurface
|
|
4046
|
+
}
|
|
4047
|
+
]
|
|
3127
4048
|
}
|
|
3128
4049
|
)
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
}) })
|
|
4050
|
+
]
|
|
4051
|
+
}
|
|
4052
|
+
)
|
|
3133
4053
|
]
|
|
3134
4054
|
}
|
|
3135
4055
|
);
|
|
3136
4056
|
}
|
|
3137
|
-
function
|
|
4057
|
+
function Picker({
|
|
3138
4058
|
value,
|
|
3139
4059
|
onChange,
|
|
3140
|
-
|
|
4060
|
+
columns,
|
|
4061
|
+
placeholder = "\u8BF7\u9009\u62E9",
|
|
3141
4062
|
disabled = false,
|
|
3142
|
-
format = "yyyy-MM-dd",
|
|
3143
|
-
minDate,
|
|
3144
|
-
maxDate,
|
|
3145
4063
|
className,
|
|
4064
|
+
pickerTitle = "\u8BF7\u9009\u62E9",
|
|
3146
4065
|
cancelText = "\u53D6\u6D88",
|
|
3147
4066
|
confirmText = "\u786E\u5B9A",
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
4067
|
+
triggerIconName = "keyboard-arrow-down",
|
|
4068
|
+
renderDisplayText,
|
|
4069
|
+
renderFooter,
|
|
4070
|
+
tempValue,
|
|
4071
|
+
defaultTempValue,
|
|
4072
|
+
onTempChange,
|
|
4073
|
+
onOpen,
|
|
4074
|
+
rowHeight = 40,
|
|
4075
|
+
visibleRows = 5
|
|
3156
4076
|
}) {
|
|
3157
4077
|
const colors = useFormThemeColors();
|
|
3158
|
-
const [visible, setVisible] =
|
|
3159
|
-
const [
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
for (let i = currentYear - 50; i <= currentYear + 50; i++) {
|
|
3171
|
-
arr.push(i);
|
|
4078
|
+
const [visible, setVisible] = useState20(false);
|
|
4079
|
+
const [internalTempValues, setInternalTempValues] = useState20(
|
|
4080
|
+
normalizeValues(columns, defaultTempValue ?? value)
|
|
4081
|
+
);
|
|
4082
|
+
const isControlledTemp = tempValue !== void 0;
|
|
4083
|
+
const tempValues = useMemo8(
|
|
4084
|
+
() => isControlledTemp ? normalizeValues(columns, tempValue) : internalTempValues,
|
|
4085
|
+
[columns, internalTempValues, isControlledTemp, tempValue]
|
|
4086
|
+
);
|
|
4087
|
+
useEffect10(() => {
|
|
4088
|
+
if (!isControlledTemp) {
|
|
4089
|
+
setInternalTempValues((previous) => normalizeValues(columns, previous));
|
|
3172
4090
|
}
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
4091
|
+
}, [columns, isControlledTemp]);
|
|
4092
|
+
const selectedOptions = useMemo8(
|
|
4093
|
+
() => columns.map(
|
|
4094
|
+
(column, index) => column.options.find((option) => option.value === value?.[index] && !option.disabled)
|
|
4095
|
+
),
|
|
4096
|
+
[columns, value]
|
|
4097
|
+
);
|
|
4098
|
+
const displayText = useMemo8(() => {
|
|
4099
|
+
if (!value || value.length === 0) return placeholder;
|
|
4100
|
+
if (renderDisplayText) return renderDisplayText(selectedOptions);
|
|
4101
|
+
const labels = selectedOptions.map((option) => option?.label).filter(Boolean);
|
|
4102
|
+
return labels.length > 0 ? labels.join(" / ") : placeholder;
|
|
4103
|
+
}, [placeholder, renderDisplayText, selectedOptions, value]);
|
|
4104
|
+
const setTempValues = useCallback15(
|
|
4105
|
+
(nextValues) => {
|
|
4106
|
+
const normalized = normalizeValues(columns, nextValues);
|
|
4107
|
+
if (isControlledTemp) {
|
|
4108
|
+
onTempChange?.(normalized);
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4111
|
+
setInternalTempValues(normalized);
|
|
4112
|
+
onTempChange?.(normalized);
|
|
3190
4113
|
},
|
|
3191
|
-
[
|
|
4114
|
+
[columns, isControlledTemp, onTempChange]
|
|
3192
4115
|
);
|
|
3193
|
-
const
|
|
3194
|
-
(
|
|
3195
|
-
const
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
if (day !== void 0) newDate.setDate(day);
|
|
3199
|
-
setTempDate(newDate);
|
|
4116
|
+
const updateColumnValue = useCallback15(
|
|
4117
|
+
(columnIndex, nextValue) => {
|
|
4118
|
+
const nextValues = [...tempValues];
|
|
4119
|
+
nextValues[columnIndex] = nextValue;
|
|
4120
|
+
setTempValues(nextValues);
|
|
3200
4121
|
},
|
|
3201
|
-
[
|
|
4122
|
+
[setTempValues, tempValues]
|
|
3202
4123
|
);
|
|
3203
|
-
|
|
3204
|
-
|
|
4124
|
+
const openModal = useCallback15(() => {
|
|
4125
|
+
const normalized = normalizeValues(columns, tempValue ?? value ?? defaultTempValue);
|
|
4126
|
+
if (!isControlledTemp) {
|
|
4127
|
+
setInternalTempValues(normalized);
|
|
4128
|
+
}
|
|
4129
|
+
onTempChange?.(normalized);
|
|
4130
|
+
onOpen?.();
|
|
4131
|
+
setVisible(true);
|
|
4132
|
+
}, [
|
|
4133
|
+
columns,
|
|
4134
|
+
defaultTempValue,
|
|
4135
|
+
isControlledTemp,
|
|
4136
|
+
onOpen,
|
|
4137
|
+
onTempChange,
|
|
4138
|
+
tempValue,
|
|
4139
|
+
value
|
|
4140
|
+
]);
|
|
4141
|
+
const handleConfirm = useCallback15(() => {
|
|
4142
|
+
onChange?.(tempValues);
|
|
4143
|
+
setVisible(false);
|
|
4144
|
+
}, [onChange, tempValues]);
|
|
4145
|
+
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4146
|
+
/* @__PURE__ */ jsxs12(
|
|
3205
4147
|
AppPressable,
|
|
3206
4148
|
{
|
|
3207
4149
|
className: cn(
|
|
@@ -3209,147 +4151,252 @@ function DatePicker({
|
|
|
3209
4151
|
disabled ? "opacity-60" : "",
|
|
3210
4152
|
className
|
|
3211
4153
|
),
|
|
3212
|
-
style: [
|
|
4154
|
+
style: [styles12.trigger, { backgroundColor: colors.surface, borderColor: colors.border }],
|
|
3213
4155
|
disabled,
|
|
3214
|
-
onPress:
|
|
3215
|
-
setTempDate(value || /* @__PURE__ */ new Date());
|
|
3216
|
-
setVisible(true);
|
|
3217
|
-
},
|
|
4156
|
+
onPress: openModal,
|
|
3218
4157
|
children: [
|
|
3219
|
-
/* @__PURE__ */
|
|
4158
|
+
/* @__PURE__ */ jsx31(
|
|
3220
4159
|
AppText,
|
|
3221
4160
|
{
|
|
3222
4161
|
className: "flex-1",
|
|
3223
|
-
style: { color: value ? colors.text : colors.textMuted },
|
|
4162
|
+
style: { color: value && value.length > 0 ? colors.text : colors.textMuted },
|
|
3224
4163
|
numberOfLines: 1,
|
|
3225
4164
|
children: displayText
|
|
3226
4165
|
}
|
|
3227
4166
|
),
|
|
3228
|
-
/* @__PURE__ */
|
|
4167
|
+
/* @__PURE__ */ jsx31(Icon, { name: triggerIconName, size: "md", color: colors.icon })
|
|
3229
4168
|
]
|
|
3230
4169
|
}
|
|
3231
4170
|
),
|
|
3232
|
-
/* @__PURE__ */
|
|
3233
|
-
|
|
4171
|
+
/* @__PURE__ */ jsx31(
|
|
4172
|
+
BottomSheetModal,
|
|
3234
4173
|
{
|
|
3235
4174
|
visible,
|
|
3236
|
-
transparent: true,
|
|
3237
|
-
animationType: "slide",
|
|
3238
4175
|
onRequestClose: () => setVisible(false),
|
|
3239
|
-
|
|
3240
|
-
|
|
4176
|
+
overlayColor: colors.overlay,
|
|
4177
|
+
surfaceColor: colors.surface,
|
|
4178
|
+
closeOnBackdropPress: true,
|
|
4179
|
+
children: /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4180
|
+
/* @__PURE__ */ jsxs12(
|
|
3241
4181
|
AppView,
|
|
3242
4182
|
{
|
|
3243
4183
|
row: true,
|
|
3244
4184
|
between: true,
|
|
3245
4185
|
items: "center",
|
|
3246
4186
|
className: "px-4 py-3",
|
|
3247
|
-
style: [
|
|
4187
|
+
style: [styles12.header, { borderBottomColor: colors.divider }],
|
|
3248
4188
|
children: [
|
|
3249
|
-
/* @__PURE__ */
|
|
3250
|
-
/* @__PURE__ */
|
|
3251
|
-
/* @__PURE__ */
|
|
4189
|
+
/* @__PURE__ */ jsx31(TouchableOpacity6, { onPress: () => setVisible(false), children: /* @__PURE__ */ jsx31(AppText, { style: { color: colors.textMuted }, children: cancelText }) }),
|
|
4190
|
+
/* @__PURE__ */ jsx31(AppText, { className: "text-lg font-semibold", style: { color: colors.text }, children: pickerTitle }),
|
|
4191
|
+
/* @__PURE__ */ jsx31(TouchableOpacity6, { onPress: handleConfirm, children: /* @__PURE__ */ jsx31(AppText, { className: "font-medium", style: { color: colors.primary }, children: confirmText }) })
|
|
3252
4192
|
]
|
|
3253
4193
|
}
|
|
3254
4194
|
),
|
|
3255
|
-
/* @__PURE__ */
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
/* @__PURE__ */ jsx29(
|
|
3270
|
-
PickerColumn,
|
|
3271
|
-
{
|
|
3272
|
-
title: monthLabel,
|
|
3273
|
-
values: months,
|
|
3274
|
-
selectedValue: tempDate.getMonth() + 1,
|
|
3275
|
-
onSelect: (month) => updateTempDate(void 0, month),
|
|
3276
|
-
isDisabled: (month) => isDateDisabled(tempDate.getFullYear(), month, tempDate.getDate()),
|
|
3277
|
-
formatLabel: (month) => `${month}\u6708`,
|
|
3278
|
-
colors,
|
|
3279
|
-
showDivider: true
|
|
3280
|
-
}
|
|
3281
|
-
),
|
|
3282
|
-
/* @__PURE__ */ jsx29(
|
|
3283
|
-
PickerColumn,
|
|
3284
|
-
{
|
|
3285
|
-
title: dayLabel,
|
|
3286
|
-
values: days,
|
|
3287
|
-
selectedValue: tempDate.getDate(),
|
|
3288
|
-
onSelect: (day) => updateTempDate(void 0, void 0, day),
|
|
3289
|
-
isDisabled: (day) => isDateDisabled(tempDate.getFullYear(), tempDate.getMonth() + 1, day),
|
|
3290
|
-
colors
|
|
3291
|
-
}
|
|
3292
|
-
)
|
|
3293
|
-
] }),
|
|
3294
|
-
/* @__PURE__ */ jsxs11(
|
|
4195
|
+
/* @__PURE__ */ jsx31(AppView, { row: true, children: columns.map((column, index) => /* @__PURE__ */ jsx31(
|
|
4196
|
+
WheelPickerColumn,
|
|
4197
|
+
{
|
|
4198
|
+
colors,
|
|
4199
|
+
column,
|
|
4200
|
+
onChange: (nextValue) => updateColumnValue(index, nextValue),
|
|
4201
|
+
rowHeight,
|
|
4202
|
+
selectedValue: tempValues[index],
|
|
4203
|
+
showDivider: index < columns.length - 1,
|
|
4204
|
+
visibleRows
|
|
4205
|
+
},
|
|
4206
|
+
column.key
|
|
4207
|
+
)) }),
|
|
4208
|
+
renderFooter && /* @__PURE__ */ jsx31(
|
|
3295
4209
|
AppView,
|
|
3296
4210
|
{
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
children: [
|
|
3301
|
-
/* @__PURE__ */ jsx29(
|
|
3302
|
-
TouchableOpacity6,
|
|
3303
|
-
{
|
|
3304
|
-
className: "flex-1 py-2 items-center rounded-lg",
|
|
3305
|
-
style: { backgroundColor: colors.surfaceMuted },
|
|
3306
|
-
onPress: () => setTempDate(/* @__PURE__ */ new Date()),
|
|
3307
|
-
children: /* @__PURE__ */ jsx29(AppText, { style: { color: colors.text }, children: todayText })
|
|
3308
|
-
}
|
|
3309
|
-
),
|
|
3310
|
-
minDate && /* @__PURE__ */ jsx29(
|
|
3311
|
-
TouchableOpacity6,
|
|
3312
|
-
{
|
|
3313
|
-
className: "flex-1 py-2 items-center rounded-lg",
|
|
3314
|
-
style: { backgroundColor: colors.surfaceMuted },
|
|
3315
|
-
onPress: () => setTempDate(minDate),
|
|
3316
|
-
children: /* @__PURE__ */ jsx29(AppText, { style: { color: colors.text }, children: minDateText })
|
|
3317
|
-
}
|
|
3318
|
-
),
|
|
3319
|
-
maxDate && /* @__PURE__ */ jsx29(
|
|
3320
|
-
TouchableOpacity6,
|
|
3321
|
-
{
|
|
3322
|
-
className: "flex-1 py-2 items-center rounded-lg",
|
|
3323
|
-
style: { backgroundColor: colors.surfaceMuted },
|
|
3324
|
-
onPress: () => setTempDate(maxDate),
|
|
3325
|
-
children: /* @__PURE__ */ jsx29(AppText, { style: { color: colors.text }, children: maxDateText })
|
|
3326
|
-
}
|
|
3327
|
-
)
|
|
3328
|
-
]
|
|
4211
|
+
className: "px-4 py-3",
|
|
4212
|
+
style: [styles12.footer, { borderTopColor: colors.divider }],
|
|
4213
|
+
children: renderFooter({ close: () => setVisible(false), setTempValues, tempValues })
|
|
3329
4214
|
}
|
|
3330
4215
|
)
|
|
3331
|
-
] })
|
|
4216
|
+
] })
|
|
3332
4217
|
}
|
|
3333
4218
|
)
|
|
3334
4219
|
] });
|
|
3335
4220
|
}
|
|
3336
|
-
var
|
|
4221
|
+
var styles12 = StyleSheet13.create({
|
|
3337
4222
|
trigger: {
|
|
3338
4223
|
borderWidth: 0.5
|
|
3339
4224
|
},
|
|
3340
4225
|
header: {
|
|
3341
4226
|
borderBottomWidth: 0.5
|
|
3342
4227
|
},
|
|
3343
|
-
|
|
3344
|
-
|
|
4228
|
+
footer: {
|
|
4229
|
+
borderTopWidth: 0.5
|
|
4230
|
+
},
|
|
4231
|
+
columnDivider: {
|
|
4232
|
+
borderRightWidth: 0.5
|
|
4233
|
+
},
|
|
4234
|
+
wheelViewport: {
|
|
4235
|
+
position: "relative",
|
|
4236
|
+
overflow: "hidden"
|
|
4237
|
+
},
|
|
4238
|
+
fadeMask: {
|
|
4239
|
+
position: "absolute",
|
|
4240
|
+
left: 0,
|
|
4241
|
+
right: 0
|
|
3345
4242
|
},
|
|
3346
|
-
|
|
3347
|
-
|
|
4243
|
+
selectionOverlay: {
|
|
4244
|
+
position: "absolute",
|
|
4245
|
+
left: 8,
|
|
4246
|
+
right: 8,
|
|
4247
|
+
borderRadius: 12,
|
|
4248
|
+
borderWidth: 0.5
|
|
4249
|
+
},
|
|
4250
|
+
optionButton: {
|
|
4251
|
+
alignItems: "center",
|
|
4252
|
+
justifyContent: "center"
|
|
3348
4253
|
}
|
|
3349
4254
|
});
|
|
3350
4255
|
|
|
4256
|
+
// src/ui/form/DatePicker.tsx
|
|
4257
|
+
import { useCallback as useCallback16, useEffect as useEffect11, useMemo as useMemo9, useState as useState21 } from "react";
|
|
4258
|
+
import { TouchableOpacity as TouchableOpacity7 } from "react-native";
|
|
4259
|
+
import { jsx as jsx32 } from "nativewind/jsx-runtime";
|
|
4260
|
+
function createSafeDate(year, month, day) {
|
|
4261
|
+
const daysInMonth = new Date(year, month, 0).getDate();
|
|
4262
|
+
return new Date(year, month - 1, Math.min(day, daysInMonth));
|
|
4263
|
+
}
|
|
4264
|
+
function getDateValues(date) {
|
|
4265
|
+
return [date.getFullYear(), date.getMonth() + 1, date.getDate()];
|
|
4266
|
+
}
|
|
4267
|
+
function DatePicker({
|
|
4268
|
+
value,
|
|
4269
|
+
onChange,
|
|
4270
|
+
placeholder = "\u8BF7\u9009\u62E9\u65E5\u671F",
|
|
4271
|
+
disabled = false,
|
|
4272
|
+
format = "yyyy-MM-dd",
|
|
4273
|
+
minDate,
|
|
4274
|
+
maxDate,
|
|
4275
|
+
className,
|
|
4276
|
+
cancelText = "\u53D6\u6D88",
|
|
4277
|
+
confirmText = "\u786E\u5B9A",
|
|
4278
|
+
pickerTitle = "\u9009\u62E9\u65E5\u671F",
|
|
4279
|
+
pickerDateFormat: _pickerDateFormat = "yyyy\u5E74MM\u6708dd\u65E5",
|
|
4280
|
+
yearLabel = "\u5E74",
|
|
4281
|
+
monthLabel = "\u6708",
|
|
4282
|
+
dayLabel = "\u65E5",
|
|
4283
|
+
todayText = "\u4ECA\u5929",
|
|
4284
|
+
minDateText = "\u6700\u65E9",
|
|
4285
|
+
maxDateText = "\u6700\u665A"
|
|
4286
|
+
}) {
|
|
4287
|
+
const colors = useFormThemeColors();
|
|
4288
|
+
const [tempDate, setTempDate] = useState21(value || /* @__PURE__ */ new Date());
|
|
4289
|
+
useEffect11(() => {
|
|
4290
|
+
if (value) setTempDate(value);
|
|
4291
|
+
}, [value]);
|
|
4292
|
+
const years = useMemo9(() => {
|
|
4293
|
+
const baseYear = value?.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
4294
|
+
const startYear = minDate?.getFullYear() ?? baseYear - 50;
|
|
4295
|
+
const endYear = maxDate?.getFullYear() ?? baseYear + 50;
|
|
4296
|
+
return Array.from({ length: endYear - startYear + 1 }, (_, index) => startYear + index);
|
|
4297
|
+
}, [maxDate, minDate, value]);
|
|
4298
|
+
const months = useMemo9(() => Array.from({ length: 12 }, (_, index) => index + 1), []);
|
|
4299
|
+
const days = useMemo9(() => {
|
|
4300
|
+
const daysInMonth = new Date(tempDate.getFullYear(), tempDate.getMonth() + 1, 0).getDate();
|
|
4301
|
+
return Array.from({ length: daysInMonth }, (_, index) => index + 1);
|
|
4302
|
+
}, [tempDate]);
|
|
4303
|
+
const isDateDisabled = useCallback16(
|
|
4304
|
+
(year, month, day) => {
|
|
4305
|
+
const date = createSafeDate(year, month, day);
|
|
4306
|
+
if (minDate && date < minDate) return true;
|
|
4307
|
+
if (maxDate && date > maxDate) return true;
|
|
4308
|
+
return false;
|
|
4309
|
+
},
|
|
4310
|
+
[maxDate, minDate]
|
|
4311
|
+
);
|
|
4312
|
+
const columns = useMemo9(
|
|
4313
|
+
() => [
|
|
4314
|
+
{
|
|
4315
|
+
key: "year",
|
|
4316
|
+
title: yearLabel,
|
|
4317
|
+
options: years.map((year) => ({
|
|
4318
|
+
label: String(year),
|
|
4319
|
+
value: year,
|
|
4320
|
+
disabled: isDateDisabled(year, tempDate.getMonth() + 1, tempDate.getDate())
|
|
4321
|
+
}))
|
|
4322
|
+
},
|
|
4323
|
+
{
|
|
4324
|
+
key: "month",
|
|
4325
|
+
title: monthLabel,
|
|
4326
|
+
options: months.map((month) => ({
|
|
4327
|
+
label: `${month}\u6708`,
|
|
4328
|
+
value: month,
|
|
4329
|
+
disabled: isDateDisabled(tempDate.getFullYear(), month, tempDate.getDate())
|
|
4330
|
+
}))
|
|
4331
|
+
},
|
|
4332
|
+
{
|
|
4333
|
+
key: "day",
|
|
4334
|
+
title: dayLabel,
|
|
4335
|
+
options: days.map((day) => ({
|
|
4336
|
+
label: `${day}\u65E5`,
|
|
4337
|
+
value: day,
|
|
4338
|
+
disabled: isDateDisabled(tempDate.getFullYear(), tempDate.getMonth() + 1, day)
|
|
4339
|
+
}))
|
|
4340
|
+
}
|
|
4341
|
+
],
|
|
4342
|
+
[dayLabel, days, isDateDisabled, monthLabel, months, tempDate, yearLabel, years]
|
|
4343
|
+
);
|
|
4344
|
+
const handleTempChange = useCallback16((nextValues) => {
|
|
4345
|
+
const [nextYear, nextMonth, nextDay] = nextValues;
|
|
4346
|
+
if (typeof nextYear !== "number" || typeof nextMonth !== "number" || typeof nextDay !== "number") {
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
setTempDate(createSafeDate(nextYear, nextMonth, nextDay));
|
|
4350
|
+
}, []);
|
|
4351
|
+
const handleChange = useCallback16(
|
|
4352
|
+
(nextValues) => {
|
|
4353
|
+
const [nextYear, nextMonth, nextDay] = nextValues;
|
|
4354
|
+
if (typeof nextYear !== "number" || typeof nextMonth !== "number" || typeof nextDay !== "number") {
|
|
4355
|
+
return;
|
|
4356
|
+
}
|
|
4357
|
+
onChange?.(createSafeDate(nextYear, nextMonth, nextDay));
|
|
4358
|
+
},
|
|
4359
|
+
[onChange]
|
|
4360
|
+
);
|
|
4361
|
+
const quickActions = useMemo9(() => {
|
|
4362
|
+
const actions = [{ label: todayText, date: /* @__PURE__ */ new Date() }];
|
|
4363
|
+
if (minDate) actions.push({ label: minDateText, date: minDate });
|
|
4364
|
+
if (maxDate) actions.push({ label: maxDateText, date: maxDate });
|
|
4365
|
+
return actions;
|
|
4366
|
+
}, [maxDate, maxDateText, minDate, minDateText, todayText]);
|
|
4367
|
+
return /* @__PURE__ */ jsx32(
|
|
4368
|
+
Picker,
|
|
4369
|
+
{
|
|
4370
|
+
value: value ? getDateValues(value) : void 0,
|
|
4371
|
+
tempValue: getDateValues(tempDate),
|
|
4372
|
+
onTempChange: handleTempChange,
|
|
4373
|
+
onChange: handleChange,
|
|
4374
|
+
onOpen: () => setTempDate(value || /* @__PURE__ */ new Date()),
|
|
4375
|
+
columns,
|
|
4376
|
+
placeholder,
|
|
4377
|
+
disabled,
|
|
4378
|
+
className,
|
|
4379
|
+
pickerTitle,
|
|
4380
|
+
cancelText,
|
|
4381
|
+
confirmText,
|
|
4382
|
+
triggerIconName: "calendar-today",
|
|
4383
|
+
renderDisplayText: () => value ? formatDate(value, format) : placeholder,
|
|
4384
|
+
renderFooter: () => /* @__PURE__ */ jsx32(AppView, { row: true, className: "gap-2", children: quickActions.map((action) => /* @__PURE__ */ jsx32(
|
|
4385
|
+
TouchableOpacity7,
|
|
4386
|
+
{
|
|
4387
|
+
className: "flex-1 py-2 items-center rounded-lg",
|
|
4388
|
+
style: { backgroundColor: colors.surfaceMuted },
|
|
4389
|
+
onPress: () => setTempDate(action.date),
|
|
4390
|
+
children: /* @__PURE__ */ jsx32(AppText, { style: { color: colors.text }, children: action.label })
|
|
4391
|
+
},
|
|
4392
|
+
action.label
|
|
4393
|
+
)) })
|
|
4394
|
+
}
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
4397
|
+
|
|
3351
4398
|
// src/ui/form/FormItem.tsx
|
|
3352
|
-
import { jsx as
|
|
4399
|
+
import { jsx as jsx33, jsxs as jsxs13 } from "nativewind/jsx-runtime";
|
|
3353
4400
|
function FormItem({
|
|
3354
4401
|
name: _name,
|
|
3355
4402
|
label,
|
|
@@ -3361,19 +4408,19 @@ function FormItem({
|
|
|
3361
4408
|
labelClassName
|
|
3362
4409
|
}) {
|
|
3363
4410
|
const colors = useThemeColors();
|
|
3364
|
-
return /* @__PURE__ */
|
|
3365
|
-
label && /* @__PURE__ */
|
|
3366
|
-
/* @__PURE__ */
|
|
3367
|
-
required && /* @__PURE__ */
|
|
4411
|
+
return /* @__PURE__ */ jsxs13(AppView, { className: cn("mb-4", className), children: [
|
|
4412
|
+
label && /* @__PURE__ */ jsxs13(AppView, { row: true, items: "center", gap: 1, className: cn("mb-2", labelClassName), children: [
|
|
4413
|
+
/* @__PURE__ */ jsx33(AppText, { size: "sm", weight: "medium", style: { color: colors.textSecondary }, children: label }),
|
|
4414
|
+
required && /* @__PURE__ */ jsx33(AppText, { color: "error-500", children: "*" })
|
|
3368
4415
|
] }),
|
|
3369
4416
|
children,
|
|
3370
|
-
error && /* @__PURE__ */
|
|
3371
|
-
help && !error && /* @__PURE__ */
|
|
4417
|
+
error && /* @__PURE__ */ jsx33(AppText, { size: "sm", color: "error-500", className: "mt-1", children: error }),
|
|
4418
|
+
help && !error && /* @__PURE__ */ jsx33(AppText, { size: "sm", className: "mt-1", style: { color: colors.textMuted }, children: help })
|
|
3372
4419
|
] });
|
|
3373
4420
|
}
|
|
3374
4421
|
|
|
3375
4422
|
// src/ui/form/useForm.ts
|
|
3376
|
-
import { useState as
|
|
4423
|
+
import { useState as useState22, useCallback as useCallback17, useMemo as useMemo10 } from "react";
|
|
3377
4424
|
var getIssuePath = (issue) => issue.path.map(String).join(".");
|
|
3378
4425
|
var getFieldError = (issues, name) => {
|
|
3379
4426
|
const exactIssue = issues.find((issue) => getIssuePath(issue) === name);
|
|
@@ -3389,16 +4436,16 @@ var buildFormErrors = (issues) => {
|
|
|
3389
4436
|
}, {});
|
|
3390
4437
|
};
|
|
3391
4438
|
function useForm({ schema, defaultValues }) {
|
|
3392
|
-
const [values, setValues] =
|
|
3393
|
-
const [errors, setErrors] =
|
|
3394
|
-
const [isSubmitting, setIsSubmitting] =
|
|
3395
|
-
const isDirty =
|
|
4439
|
+
const [values, setValues] = useState22(defaultValues);
|
|
4440
|
+
const [errors, setErrors] = useState22({});
|
|
4441
|
+
const [isSubmitting, setIsSubmitting] = useState22(false);
|
|
4442
|
+
const isDirty = useMemo10(() => {
|
|
3396
4443
|
return JSON.stringify(values) !== JSON.stringify(defaultValues);
|
|
3397
4444
|
}, [values, defaultValues]);
|
|
3398
|
-
const isValid =
|
|
4445
|
+
const isValid = useMemo10(() => {
|
|
3399
4446
|
return Object.keys(errors).length === 0;
|
|
3400
4447
|
}, [errors]);
|
|
3401
|
-
const clearFieldError =
|
|
4448
|
+
const clearFieldError = useCallback17((name) => {
|
|
3402
4449
|
setErrors((prev) => {
|
|
3403
4450
|
if (!(name in prev)) return prev;
|
|
3404
4451
|
const next = { ...prev };
|
|
@@ -3406,20 +4453,20 @@ function useForm({ schema, defaultValues }) {
|
|
|
3406
4453
|
return next;
|
|
3407
4454
|
});
|
|
3408
4455
|
}, []);
|
|
3409
|
-
const setValue =
|
|
4456
|
+
const setValue = useCallback17(
|
|
3410
4457
|
(name, value) => {
|
|
3411
4458
|
setValues((prev) => ({ ...prev, [name]: value }));
|
|
3412
4459
|
clearFieldError(name);
|
|
3413
4460
|
},
|
|
3414
4461
|
[clearFieldError]
|
|
3415
4462
|
);
|
|
3416
|
-
const getValue =
|
|
4463
|
+
const getValue = useCallback17(
|
|
3417
4464
|
(name) => {
|
|
3418
4465
|
return values[name];
|
|
3419
4466
|
},
|
|
3420
4467
|
[values]
|
|
3421
4468
|
);
|
|
3422
|
-
const validateField =
|
|
4469
|
+
const validateField = useCallback17(
|
|
3423
4470
|
async (name) => {
|
|
3424
4471
|
const fieldName = name;
|
|
3425
4472
|
const result = await schema.safeParseAsync(values);
|
|
@@ -3440,7 +4487,7 @@ function useForm({ schema, defaultValues }) {
|
|
|
3440
4487
|
},
|
|
3441
4488
|
[schema, values, clearFieldError]
|
|
3442
4489
|
);
|
|
3443
|
-
const validate =
|
|
4490
|
+
const validate = useCallback17(async () => {
|
|
3444
4491
|
const result = await schema.safeParseAsync(values);
|
|
3445
4492
|
if (result.success) {
|
|
3446
4493
|
setErrors({});
|
|
@@ -3449,12 +4496,12 @@ function useForm({ schema, defaultValues }) {
|
|
|
3449
4496
|
setErrors(buildFormErrors(result.error.issues));
|
|
3450
4497
|
return false;
|
|
3451
4498
|
}, [schema, values]);
|
|
3452
|
-
const reset =
|
|
4499
|
+
const reset = useCallback17(() => {
|
|
3453
4500
|
setValues(defaultValues);
|
|
3454
4501
|
setErrors({});
|
|
3455
4502
|
setIsSubmitting(false);
|
|
3456
4503
|
}, [defaultValues]);
|
|
3457
|
-
const handleSubmit =
|
|
4504
|
+
const handleSubmit = useCallback17(
|
|
3458
4505
|
async (onSubmit) => {
|
|
3459
4506
|
const valid = await validate();
|
|
3460
4507
|
if (!valid) return;
|
|
@@ -3483,38 +4530,38 @@ function useForm({ schema, defaultValues }) {
|
|
|
3483
4530
|
}
|
|
3484
4531
|
|
|
3485
4532
|
// src/ui/hooks/useToggle.ts
|
|
3486
|
-
import { useCallback as
|
|
4533
|
+
import { useCallback as useCallback18, useState as useState23 } from "react";
|
|
3487
4534
|
function useToggle(defaultValue = false) {
|
|
3488
|
-
const [value, setValue] =
|
|
3489
|
-
const toggle =
|
|
4535
|
+
const [value, setValue] = useState23(defaultValue);
|
|
4536
|
+
const toggle = useCallback18(() => {
|
|
3490
4537
|
setValue((v) => !v);
|
|
3491
4538
|
}, []);
|
|
3492
|
-
const set =
|
|
4539
|
+
const set = useCallback18((newValue) => {
|
|
3493
4540
|
setValue(newValue);
|
|
3494
4541
|
}, []);
|
|
3495
|
-
const setTrue =
|
|
4542
|
+
const setTrue = useCallback18(() => {
|
|
3496
4543
|
setValue(true);
|
|
3497
4544
|
}, []);
|
|
3498
|
-
const setFalse =
|
|
4545
|
+
const setFalse = useCallback18(() => {
|
|
3499
4546
|
setValue(false);
|
|
3500
4547
|
}, []);
|
|
3501
4548
|
return [value, { toggle, set, setTrue, setFalse }];
|
|
3502
4549
|
}
|
|
3503
4550
|
|
|
3504
4551
|
// src/ui/hooks/usePageDrawer.ts
|
|
3505
|
-
import { useCallback as
|
|
4552
|
+
import { useCallback as useCallback19, useState as useState24 } from "react";
|
|
3506
4553
|
function usePageDrawer(defaultVisible = false) {
|
|
3507
|
-
const [visible, setVisibleState] =
|
|
3508
|
-
const open =
|
|
4554
|
+
const [visible, setVisibleState] = useState24(defaultVisible);
|
|
4555
|
+
const open = useCallback19(() => {
|
|
3509
4556
|
setVisibleState(true);
|
|
3510
4557
|
}, []);
|
|
3511
|
-
const close =
|
|
4558
|
+
const close = useCallback19(() => {
|
|
3512
4559
|
setVisibleState(false);
|
|
3513
4560
|
}, []);
|
|
3514
|
-
const toggle =
|
|
4561
|
+
const toggle = useCallback19(() => {
|
|
3515
4562
|
setVisibleState((current) => !current);
|
|
3516
4563
|
}, []);
|
|
3517
|
-
const setVisible =
|
|
4564
|
+
const setVisible = useCallback19((nextVisible) => {
|
|
3518
4565
|
setVisibleState(nextVisible);
|
|
3519
4566
|
}, []);
|
|
3520
4567
|
return {
|
|
@@ -3527,10 +4574,10 @@ function usePageDrawer(defaultVisible = false) {
|
|
|
3527
4574
|
}
|
|
3528
4575
|
|
|
3529
4576
|
// src/ui/hooks/useDebounce.ts
|
|
3530
|
-
import { useEffect as
|
|
4577
|
+
import { useEffect as useEffect12, useState as useState25 } from "react";
|
|
3531
4578
|
function useDebounce(value, delay = 500) {
|
|
3532
|
-
const [debouncedValue, setDebouncedValue] =
|
|
3533
|
-
|
|
4579
|
+
const [debouncedValue, setDebouncedValue] = useState25(value);
|
|
4580
|
+
useEffect12(() => {
|
|
3534
4581
|
const timer = setTimeout(() => {
|
|
3535
4582
|
setDebouncedValue(value);
|
|
3536
4583
|
}, delay);
|
|
@@ -3542,11 +4589,11 @@ function useDebounce(value, delay = 500) {
|
|
|
3542
4589
|
}
|
|
3543
4590
|
|
|
3544
4591
|
// src/ui/hooks/useThrottle.ts
|
|
3545
|
-
import { useEffect as
|
|
4592
|
+
import { useEffect as useEffect13, useRef as useRef11, useState as useState26 } from "react";
|
|
3546
4593
|
function useThrottle(value, delay = 200) {
|
|
3547
|
-
const [throttledValue, setThrottledValue] =
|
|
3548
|
-
const lastUpdatedRef =
|
|
3549
|
-
|
|
4594
|
+
const [throttledValue, setThrottledValue] = useState26(value);
|
|
4595
|
+
const lastUpdatedRef = useRef11(Date.now());
|
|
4596
|
+
useEffect13(() => {
|
|
3550
4597
|
const now = Date.now();
|
|
3551
4598
|
const timeElapsed = now - lastUpdatedRef.current;
|
|
3552
4599
|
if (timeElapsed >= delay) {
|
|
@@ -3567,12 +4614,12 @@ function useThrottle(value, delay = 200) {
|
|
|
3567
4614
|
}
|
|
3568
4615
|
|
|
3569
4616
|
// src/ui/hooks/useKeyboard.ts
|
|
3570
|
-
import { useEffect as
|
|
3571
|
-
import { Keyboard, Platform } from "react-native";
|
|
4617
|
+
import { useEffect as useEffect14, useState as useState27, useCallback as useCallback20 } from "react";
|
|
4618
|
+
import { Keyboard as Keyboard5, Platform } from "react-native";
|
|
3572
4619
|
function useKeyboard() {
|
|
3573
|
-
const [visible, setVisible] =
|
|
3574
|
-
const [height, setHeight] =
|
|
3575
|
-
|
|
4620
|
+
const [visible, setVisible] = useState27(false);
|
|
4621
|
+
const [height, setHeight] = useState27(0);
|
|
4622
|
+
useEffect14(() => {
|
|
3576
4623
|
const handleKeyboardWillShow = (event) => {
|
|
3577
4624
|
setVisible(true);
|
|
3578
4625
|
setHeight(event.endCoordinates.height);
|
|
@@ -3589,11 +4636,11 @@ function useKeyboard() {
|
|
|
3589
4636
|
setVisible(false);
|
|
3590
4637
|
setHeight(0);
|
|
3591
4638
|
};
|
|
3592
|
-
const willShowSub =
|
|
4639
|
+
const willShowSub = Keyboard5.addListener(
|
|
3593
4640
|
Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
|
|
3594
4641
|
Platform.OS === "ios" ? handleKeyboardWillShow : handleKeyboardDidShow
|
|
3595
4642
|
);
|
|
3596
|
-
const willHideSub =
|
|
4643
|
+
const willHideSub = Keyboard5.addListener(
|
|
3597
4644
|
Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide",
|
|
3598
4645
|
Platform.OS === "ios" ? handleKeyboardWillHide : handleKeyboardDidHide
|
|
3599
4646
|
);
|
|
@@ -3602,17 +4649,17 @@ function useKeyboard() {
|
|
|
3602
4649
|
willHideSub.remove();
|
|
3603
4650
|
};
|
|
3604
4651
|
}, []);
|
|
3605
|
-
const dismiss =
|
|
3606
|
-
|
|
4652
|
+
const dismiss = useCallback20(() => {
|
|
4653
|
+
Keyboard5.dismiss();
|
|
3607
4654
|
}, []);
|
|
3608
4655
|
return { visible, height, dismiss };
|
|
3609
4656
|
}
|
|
3610
4657
|
|
|
3611
4658
|
// src/ui/hooks/useDimensions.ts
|
|
3612
|
-
import { useEffect as
|
|
4659
|
+
import { useEffect as useEffect15, useState as useState28 } from "react";
|
|
3613
4660
|
import { Dimensions } from "react-native";
|
|
3614
4661
|
function useDimensions() {
|
|
3615
|
-
const [dimensions, setDimensions] =
|
|
4662
|
+
const [dimensions, setDimensions] = useState28(() => {
|
|
3616
4663
|
const window = Dimensions.get("window");
|
|
3617
4664
|
return {
|
|
3618
4665
|
width: window.width,
|
|
@@ -3621,7 +4668,7 @@ function useDimensions() {
|
|
|
3621
4668
|
fontScale: window.fontScale
|
|
3622
4669
|
};
|
|
3623
4670
|
});
|
|
3624
|
-
|
|
4671
|
+
useEffect15(() => {
|
|
3625
4672
|
const handleChange = ({ window }) => {
|
|
3626
4673
|
setDimensions({
|
|
3627
4674
|
width: window.width,
|
|
@@ -3639,15 +4686,15 @@ function useDimensions() {
|
|
|
3639
4686
|
}
|
|
3640
4687
|
|
|
3641
4688
|
// src/ui/hooks/useOrientation.ts
|
|
3642
|
-
import { useEffect as
|
|
4689
|
+
import { useEffect as useEffect16, useState as useState29 } from "react";
|
|
3643
4690
|
import { Dimensions as Dimensions2 } from "react-native";
|
|
3644
4691
|
function useOrientation() {
|
|
3645
4692
|
const getOrientation = () => {
|
|
3646
4693
|
const { width, height } = Dimensions2.get("window");
|
|
3647
4694
|
return width > height ? "landscape" : "portrait";
|
|
3648
4695
|
};
|
|
3649
|
-
const [orientation, setOrientation] =
|
|
3650
|
-
|
|
4696
|
+
const [orientation, setOrientation] = useState29(getOrientation);
|
|
4697
|
+
useEffect16(() => {
|
|
3651
4698
|
const handleChange = ({ window }) => {
|
|
3652
4699
|
const newOrientation = window.width > window.height ? "landscape" : "portrait";
|
|
3653
4700
|
setOrientation(newOrientation);
|
|
@@ -3693,7 +4740,7 @@ function createNavigationTheme(pantherTheme, isDark) {
|
|
|
3693
4740
|
}
|
|
3694
4741
|
|
|
3695
4742
|
// src/navigation/provider.tsx
|
|
3696
|
-
import { jsx as
|
|
4743
|
+
import { jsx as jsx34 } from "nativewind/jsx-runtime";
|
|
3697
4744
|
function NavigationProvider({
|
|
3698
4745
|
children,
|
|
3699
4746
|
linking,
|
|
@@ -3708,7 +4755,7 @@ function NavigationProvider({
|
|
|
3708
4755
|
() => customTheme || createNavigationTheme(theme, isDark),
|
|
3709
4756
|
[customTheme, theme, isDark]
|
|
3710
4757
|
);
|
|
3711
|
-
return /* @__PURE__ */
|
|
4758
|
+
return /* @__PURE__ */ jsx34(
|
|
3712
4759
|
NavigationContainer,
|
|
3713
4760
|
{
|
|
3714
4761
|
theme: navigationTheme,
|
|
@@ -3726,14 +4773,14 @@ function NavigationProvider({
|
|
|
3726
4773
|
import { createStackNavigator, TransitionPresets } from "@react-navigation/stack";
|
|
3727
4774
|
|
|
3728
4775
|
// src/navigation/navigators/StackNavigator.tsx
|
|
3729
|
-
import { jsx as
|
|
4776
|
+
import { jsx as jsx35 } from "nativewind/jsx-runtime";
|
|
3730
4777
|
var NativeStack = createStackNavigator();
|
|
3731
4778
|
var defaultScreenOptions = {
|
|
3732
4779
|
headerShown: false,
|
|
3733
4780
|
...TransitionPresets.SlideFromRightIOS
|
|
3734
4781
|
};
|
|
3735
4782
|
function StackNavigator({ initialRouteName, screenOptions, children }) {
|
|
3736
|
-
return /* @__PURE__ */
|
|
4783
|
+
return /* @__PURE__ */ jsx35(
|
|
3737
4784
|
NativeStack.Navigator,
|
|
3738
4785
|
{
|
|
3739
4786
|
initialRouteName,
|
|
@@ -3745,7 +4792,7 @@ function StackNavigator({ initialRouteName, screenOptions, children }) {
|
|
|
3745
4792
|
StackNavigator.Screen = NativeStack.Screen;
|
|
3746
4793
|
StackNavigator.Group = NativeStack.Group;
|
|
3747
4794
|
function createStackScreens(routes) {
|
|
3748
|
-
return routes.map((route) => /* @__PURE__ */
|
|
4795
|
+
return routes.map((route) => /* @__PURE__ */ jsx35(
|
|
3749
4796
|
StackNavigator.Screen,
|
|
3750
4797
|
{
|
|
3751
4798
|
name: route.name,
|
|
@@ -3762,9 +4809,9 @@ import React7 from "react";
|
|
|
3762
4809
|
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
|
3763
4810
|
|
|
3764
4811
|
// src/navigation/components/BottomTabBar.tsx
|
|
3765
|
-
import { View as View8, TouchableOpacity as
|
|
4812
|
+
import { View as View8, TouchableOpacity as TouchableOpacity8, StyleSheet as StyleSheet14 } from "react-native";
|
|
3766
4813
|
import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
|
|
3767
|
-
import { jsx as
|
|
4814
|
+
import { jsx as jsx36, jsxs as jsxs14 } from "nativewind/jsx-runtime";
|
|
3768
4815
|
var DEFAULT_TAB_BAR_HEIGHT = 65;
|
|
3769
4816
|
function BottomTabBar({
|
|
3770
4817
|
state,
|
|
@@ -3786,11 +4833,11 @@ function BottomTabBar({
|
|
|
3786
4833
|
const inactiveColor = inactiveTintColor || colors.textMuted;
|
|
3787
4834
|
const backgroundColor = style?.backgroundColor || colors.card;
|
|
3788
4835
|
const borderTopColor = colors.divider;
|
|
3789
|
-
return /* @__PURE__ */
|
|
4836
|
+
return /* @__PURE__ */ jsx36(
|
|
3790
4837
|
View8,
|
|
3791
4838
|
{
|
|
3792
4839
|
style: [
|
|
3793
|
-
|
|
4840
|
+
styles13.container,
|
|
3794
4841
|
{ borderTopColor },
|
|
3795
4842
|
{ backgroundColor, height: height + insets.bottom, paddingBottom: insets.bottom },
|
|
3796
4843
|
style
|
|
@@ -3821,8 +4868,8 @@ function BottomTabBar({
|
|
|
3821
4868
|
size: 24
|
|
3822
4869
|
}) : null;
|
|
3823
4870
|
const badge = options.tabBarBadge;
|
|
3824
|
-
return /* @__PURE__ */
|
|
3825
|
-
|
|
4871
|
+
return /* @__PURE__ */ jsxs14(
|
|
4872
|
+
TouchableOpacity8,
|
|
3826
4873
|
{
|
|
3827
4874
|
accessibilityRole: "button",
|
|
3828
4875
|
accessibilityState: isFocused ? { selected: true } : {},
|
|
@@ -3831,21 +4878,21 @@ function BottomTabBar({
|
|
|
3831
4878
|
onPress,
|
|
3832
4879
|
onLongPress,
|
|
3833
4880
|
style: [
|
|
3834
|
-
|
|
4881
|
+
styles13.tab,
|
|
3835
4882
|
{
|
|
3836
4883
|
backgroundColor: isFocused ? activeBackgroundColor : inactiveBackgroundColor
|
|
3837
4884
|
}
|
|
3838
4885
|
],
|
|
3839
4886
|
children: [
|
|
3840
|
-
/* @__PURE__ */
|
|
4887
|
+
/* @__PURE__ */ jsxs14(View8, { style: [styles13.iconContainer, iconStyle], children: [
|
|
3841
4888
|
iconName,
|
|
3842
|
-
badge != null && /* @__PURE__ */
|
|
4889
|
+
badge != null && /* @__PURE__ */ jsx36(View8, { style: [styles13.badge, { backgroundColor: activeColor }], children: /* @__PURE__ */ jsx36(AppText, { style: styles13.badgeText, children: typeof badge === "number" && badge > 99 ? "99+" : badge }) })
|
|
3843
4890
|
] }),
|
|
3844
|
-
showLabel && /* @__PURE__ */
|
|
4891
|
+
showLabel && /* @__PURE__ */ jsx36(
|
|
3845
4892
|
AppText,
|
|
3846
4893
|
{
|
|
3847
4894
|
style: [
|
|
3848
|
-
|
|
4895
|
+
styles13.label,
|
|
3849
4896
|
{ color: isFocused ? activeColor : inactiveColor },
|
|
3850
4897
|
labelStyle
|
|
3851
4898
|
],
|
|
@@ -3861,7 +4908,7 @@ function BottomTabBar({
|
|
|
3861
4908
|
}
|
|
3862
4909
|
);
|
|
3863
4910
|
}
|
|
3864
|
-
var
|
|
4911
|
+
var styles13 = StyleSheet14.create({
|
|
3865
4912
|
container: {
|
|
3866
4913
|
flexDirection: "row",
|
|
3867
4914
|
borderTopWidth: 0.5,
|
|
@@ -3904,7 +4951,7 @@ var styles12 = StyleSheet13.create({
|
|
|
3904
4951
|
});
|
|
3905
4952
|
|
|
3906
4953
|
// src/navigation/navigators/TabNavigator.tsx
|
|
3907
|
-
import { jsx as
|
|
4954
|
+
import { jsx as jsx37 } from "nativewind/jsx-runtime";
|
|
3908
4955
|
var NativeTab = createBottomTabNavigator();
|
|
3909
4956
|
var defaultScreenOptions2 = {
|
|
3910
4957
|
headerShown: false,
|
|
@@ -3952,7 +4999,7 @@ function TabNavigator({
|
|
|
3952
4999
|
}, [tabBarOptions, screenOptions]);
|
|
3953
5000
|
const resolvedTabBar = React7.useMemo(() => {
|
|
3954
5001
|
if (tabBar) return tabBar;
|
|
3955
|
-
return (props) => /* @__PURE__ */
|
|
5002
|
+
return (props) => /* @__PURE__ */ jsx37(
|
|
3956
5003
|
BottomTabBar,
|
|
3957
5004
|
{
|
|
3958
5005
|
...props,
|
|
@@ -3979,7 +5026,7 @@ function TabNavigator({
|
|
|
3979
5026
|
tabBarOptions?.style,
|
|
3980
5027
|
tabBarOptions?.height
|
|
3981
5028
|
]);
|
|
3982
|
-
return /* @__PURE__ */
|
|
5029
|
+
return /* @__PURE__ */ jsx37(
|
|
3983
5030
|
NativeTab.Navigator,
|
|
3984
5031
|
{
|
|
3985
5032
|
initialRouteName,
|
|
@@ -3991,7 +5038,7 @@ function TabNavigator({
|
|
|
3991
5038
|
}
|
|
3992
5039
|
TabNavigator.Screen = NativeTab.Screen;
|
|
3993
5040
|
function createTabScreens(routes) {
|
|
3994
|
-
return routes.map((route) => /* @__PURE__ */
|
|
5041
|
+
return routes.map((route) => /* @__PURE__ */ jsx37(
|
|
3995
5042
|
TabNavigator.Screen,
|
|
3996
5043
|
{
|
|
3997
5044
|
name: route.name,
|
|
@@ -4006,7 +5053,7 @@ function createTabScreens(routes) {
|
|
|
4006
5053
|
// src/navigation/navigators/DrawerNavigator.tsx
|
|
4007
5054
|
import React8 from "react";
|
|
4008
5055
|
import { createDrawerNavigator } from "@react-navigation/drawer";
|
|
4009
|
-
import { jsx as
|
|
5056
|
+
import { jsx as jsx38 } from "nativewind/jsx-runtime";
|
|
4010
5057
|
var NativeDrawer = createDrawerNavigator();
|
|
4011
5058
|
function DrawerNavigator({
|
|
4012
5059
|
initialRouteName,
|
|
@@ -4036,7 +5083,7 @@ function DrawerNavigator({
|
|
|
4036
5083
|
...screenOptions
|
|
4037
5084
|
};
|
|
4038
5085
|
}, [screenOptions, drawerOptions, navigationTheme, theme]);
|
|
4039
|
-
return /* @__PURE__ */
|
|
5086
|
+
return /* @__PURE__ */ jsx38(
|
|
4040
5087
|
NativeDrawer.Navigator,
|
|
4041
5088
|
{
|
|
4042
5089
|
initialRouteName,
|
|
@@ -4048,7 +5095,7 @@ function DrawerNavigator({
|
|
|
4048
5095
|
}
|
|
4049
5096
|
DrawerNavigator.Screen = NativeDrawer.Screen;
|
|
4050
5097
|
function createDrawerScreens(routes) {
|
|
4051
|
-
return routes.map((route) => /* @__PURE__ */
|
|
5098
|
+
return routes.map((route) => /* @__PURE__ */ jsx38(
|
|
4052
5099
|
DrawerNavigator.Screen,
|
|
4053
5100
|
{
|
|
4054
5101
|
name: route.name,
|
|
@@ -4061,7 +5108,7 @@ function createDrawerScreens(routes) {
|
|
|
4061
5108
|
}
|
|
4062
5109
|
|
|
4063
5110
|
// src/navigation/components/AppHeader.tsx
|
|
4064
|
-
import { StyleSheet as
|
|
5111
|
+
import { StyleSheet as StyleSheet18 } from "react-native";
|
|
4065
5112
|
import { useSafeAreaInsets as useSafeAreaInsets3 } from "react-native-safe-area-context";
|
|
4066
5113
|
|
|
4067
5114
|
// src/overlay/AppProvider.tsx
|
|
@@ -4070,7 +5117,7 @@ import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
|
4070
5117
|
// src/overlay/AppStatusBar.tsx
|
|
4071
5118
|
import { StatusBar } from "react-native";
|
|
4072
5119
|
import { useIsFocused } from "@react-navigation/native";
|
|
4073
|
-
import { jsx as
|
|
5120
|
+
import { jsx as jsx39 } from "nativewind/jsx-runtime";
|
|
4074
5121
|
function AppStatusBar({
|
|
4075
5122
|
barStyle = "auto",
|
|
4076
5123
|
backgroundColor,
|
|
@@ -4080,7 +5127,7 @@ function AppStatusBar({
|
|
|
4080
5127
|
const { theme, isDark } = useTheme();
|
|
4081
5128
|
const resolvedBarStyle = barStyle === "auto" ? isDark ? "light-content" : "dark-content" : barStyle;
|
|
4082
5129
|
const resolvedBackgroundColor = backgroundColor ?? (translucent ? "transparent" : theme.colors.background?.[500] || "#ffffff");
|
|
4083
|
-
return /* @__PURE__ */
|
|
5130
|
+
return /* @__PURE__ */ jsx39(
|
|
4084
5131
|
StatusBar,
|
|
4085
5132
|
{
|
|
4086
5133
|
barStyle: resolvedBarStyle,
|
|
@@ -4093,11 +5140,11 @@ function AppStatusBar({
|
|
|
4093
5140
|
function AppFocusedStatusBar(props) {
|
|
4094
5141
|
const isFocused = useIsFocused();
|
|
4095
5142
|
if (!isFocused) return null;
|
|
4096
|
-
return /* @__PURE__ */
|
|
5143
|
+
return /* @__PURE__ */ jsx39(AppStatusBar, { ...props });
|
|
4097
5144
|
}
|
|
4098
5145
|
|
|
4099
5146
|
// src/overlay/loading/provider.tsx
|
|
4100
|
-
import { useState as
|
|
5147
|
+
import { useState as useState31, useCallback as useCallback21, useEffect as useEffect18, useRef as useRef12 } from "react";
|
|
4101
5148
|
|
|
4102
5149
|
// src/overlay/loading/context.ts
|
|
4103
5150
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
@@ -4109,15 +5156,42 @@ function useLoadingContext() {
|
|
|
4109
5156
|
}
|
|
4110
5157
|
|
|
4111
5158
|
// src/overlay/loading/component.tsx
|
|
4112
|
-
import {
|
|
4113
|
-
import {
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
5159
|
+
import { useEffect as useEffect17, useState as useState30 } from "react";
|
|
5160
|
+
import { View as View9, Modal as Modal4, ActivityIndicator as ActivityIndicator5, StyleSheet as StyleSheet15 } from "react-native";
|
|
5161
|
+
import { jsx as jsx40, jsxs as jsxs15 } from "nativewind/jsx-runtime";
|
|
5162
|
+
var LOADING_CLOSE_DELAY2 = 3e4;
|
|
5163
|
+
function LoadingModal({
|
|
5164
|
+
visible,
|
|
5165
|
+
text,
|
|
5166
|
+
onRequestClose
|
|
5167
|
+
}) {
|
|
5168
|
+
const [showCloseButton, setShowCloseButton] = useState30(false);
|
|
5169
|
+
useEffect17(() => {
|
|
5170
|
+
if (!visible) {
|
|
5171
|
+
setShowCloseButton(false);
|
|
5172
|
+
return;
|
|
5173
|
+
}
|
|
5174
|
+
setShowCloseButton(false);
|
|
5175
|
+
const timer = setTimeout(() => {
|
|
5176
|
+
setShowCloseButton(true);
|
|
5177
|
+
}, LOADING_CLOSE_DELAY2);
|
|
5178
|
+
return () => clearTimeout(timer);
|
|
5179
|
+
}, [visible]);
|
|
5180
|
+
return /* @__PURE__ */ jsx40(Modal4, { transparent: true, visible, animationType: "fade", children: /* @__PURE__ */ jsx40(View9, { style: styles14.overlay, children: /* @__PURE__ */ jsxs15(View9, { style: [styles14.loadingBox, { backgroundColor: "rgba(0,0,0,0.8)" }], children: [
|
|
5181
|
+
/* @__PURE__ */ jsx40(ActivityIndicator5, { size: "large", color: "#fff" }),
|
|
5182
|
+
text && /* @__PURE__ */ jsx40(AppText, { className: "text-white mt-3 text-sm", children: text }),
|
|
5183
|
+
showCloseButton && onRequestClose && /* @__PURE__ */ jsx40(
|
|
5184
|
+
AppPressable,
|
|
5185
|
+
{
|
|
5186
|
+
testID: "loading-close",
|
|
5187
|
+
className: "mt-3 p-1",
|
|
5188
|
+
onPress: onRequestClose,
|
|
5189
|
+
children: /* @__PURE__ */ jsx40(Icon, { name: "close", size: "md", color: "#ffffff" })
|
|
5190
|
+
}
|
|
5191
|
+
)
|
|
4118
5192
|
] }) }) });
|
|
4119
5193
|
}
|
|
4120
|
-
var
|
|
5194
|
+
var styles14 = StyleSheet15.create({
|
|
4121
5195
|
overlay: {
|
|
4122
5196
|
flex: 1,
|
|
4123
5197
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
@@ -4133,24 +5207,53 @@ var styles13 = StyleSheet14.create({
|
|
|
4133
5207
|
});
|
|
4134
5208
|
|
|
4135
5209
|
// src/overlay/loading/provider.tsx
|
|
4136
|
-
import { jsx as
|
|
5210
|
+
import { jsx as jsx41, jsxs as jsxs16 } from "nativewind/jsx-runtime";
|
|
5211
|
+
var MIN_VISIBLE_DURATION = 500;
|
|
4137
5212
|
function LoadingProvider({ children }) {
|
|
4138
|
-
const [state, setState] =
|
|
4139
|
-
const
|
|
4140
|
-
|
|
5213
|
+
const [state, setState] = useState31({ visible: false });
|
|
5214
|
+
const shownAtRef = useRef12(0);
|
|
5215
|
+
const pendingCountRef = useRef12(0);
|
|
5216
|
+
const hideTimerRef = useRef12(null);
|
|
5217
|
+
const clearHideTimer = useCallback21(() => {
|
|
5218
|
+
if (!hideTimerRef.current) return;
|
|
5219
|
+
clearTimeout(hideTimerRef.current);
|
|
5220
|
+
hideTimerRef.current = null;
|
|
4141
5221
|
}, []);
|
|
4142
|
-
const
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
5222
|
+
const show = useCallback21((text) => {
|
|
5223
|
+
pendingCountRef.current += 1;
|
|
5224
|
+
clearHideTimer();
|
|
5225
|
+
setState((previous) => {
|
|
5226
|
+
if (!previous.visible) {
|
|
5227
|
+
shownAtRef.current = Date.now();
|
|
5228
|
+
}
|
|
5229
|
+
return { visible: true, text };
|
|
5230
|
+
});
|
|
5231
|
+
}, [clearHideTimer]);
|
|
5232
|
+
const hide = useCallback21(() => {
|
|
5233
|
+
pendingCountRef.current = Math.max(0, pendingCountRef.current - 1);
|
|
5234
|
+
if (pendingCountRef.current > 0) return;
|
|
5235
|
+
const elapsed = Date.now() - shownAtRef.current;
|
|
5236
|
+
const remaining = Math.max(0, MIN_VISIBLE_DURATION - elapsed);
|
|
5237
|
+
clearHideTimer();
|
|
5238
|
+
if (remaining === 0) {
|
|
5239
|
+
setState({ visible: false });
|
|
5240
|
+
return;
|
|
5241
|
+
}
|
|
5242
|
+
hideTimerRef.current = setTimeout(() => {
|
|
5243
|
+
hideTimerRef.current = null;
|
|
5244
|
+
setState({ visible: false });
|
|
5245
|
+
}, remaining);
|
|
5246
|
+
}, [clearHideTimer]);
|
|
5247
|
+
useEffect18(() => () => clearHideTimer(), [clearHideTimer]);
|
|
5248
|
+
return /* @__PURE__ */ jsxs16(LoadingContext.Provider, { value: { show, hide }, children: [
|
|
4146
5249
|
children,
|
|
4147
|
-
/* @__PURE__ */
|
|
5250
|
+
/* @__PURE__ */ jsx41(LoadingModal, { ...state, onRequestClose: hide })
|
|
4148
5251
|
] });
|
|
4149
5252
|
}
|
|
4150
5253
|
|
|
4151
5254
|
// src/overlay/toast/provider.tsx
|
|
4152
|
-
import { useState as
|
|
4153
|
-
import { View as View10, StyleSheet as
|
|
5255
|
+
import { useState as useState32, useCallback as useCallback22, useRef as useRef14 } from "react";
|
|
5256
|
+
import { View as View10, StyleSheet as StyleSheet16 } from "react-native";
|
|
4154
5257
|
|
|
4155
5258
|
// src/overlay/toast/context.ts
|
|
4156
5259
|
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
@@ -4162,26 +5265,54 @@ function useToastContext() {
|
|
|
4162
5265
|
}
|
|
4163
5266
|
|
|
4164
5267
|
// src/overlay/toast/component.tsx
|
|
4165
|
-
import { useRef as
|
|
4166
|
-
import { Animated } from "react-native";
|
|
4167
|
-
import { jsx as
|
|
5268
|
+
import { useRef as useRef13, useEffect as useEffect19 } from "react";
|
|
5269
|
+
import { Animated as Animated4 } from "react-native";
|
|
5270
|
+
import { jsx as jsx42 } from "nativewind/jsx-runtime";
|
|
5271
|
+
function createAnimatedValue4(value) {
|
|
5272
|
+
const AnimatedValue = Animated4.Value;
|
|
5273
|
+
try {
|
|
5274
|
+
return new AnimatedValue(value);
|
|
5275
|
+
} catch {
|
|
5276
|
+
return AnimatedValue(value);
|
|
5277
|
+
}
|
|
5278
|
+
}
|
|
4168
5279
|
function ToastItemView({ message, type, onHide }) {
|
|
4169
|
-
const fadeAnim =
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
5280
|
+
const fadeAnim = useRef13(createAnimatedValue4(0)).current;
|
|
5281
|
+
const { theme } = useOptionalTheme();
|
|
5282
|
+
useEffect19(() => {
|
|
5283
|
+
Animated4.sequence([
|
|
5284
|
+
Animated4.timing(fadeAnim, { toValue: 1, duration: 200, useNativeDriver: true }),
|
|
5285
|
+
Animated4.delay(2500),
|
|
5286
|
+
Animated4.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: true })
|
|
4175
5287
|
]).start(onHide);
|
|
4176
5288
|
}, []);
|
|
4177
|
-
const
|
|
4178
|
-
success:
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
5289
|
+
const palette = {
|
|
5290
|
+
success: {
|
|
5291
|
+
backgroundColor: theme.colors.success?.[500] || "#22c55e",
|
|
5292
|
+
textColor: "#ffffff"
|
|
5293
|
+
},
|
|
5294
|
+
error: {
|
|
5295
|
+
backgroundColor: theme.colors.error?.[500] || "#ef4444",
|
|
5296
|
+
textColor: "#ffffff"
|
|
5297
|
+
},
|
|
5298
|
+
warning: {
|
|
5299
|
+
backgroundColor: theme.colors.warning?.[500] || "#f59e0b",
|
|
5300
|
+
textColor: "#111827"
|
|
5301
|
+
},
|
|
5302
|
+
info: {
|
|
5303
|
+
backgroundColor: theme.colors.info?.[500] || theme.colors.primary?.[500] || "#3b82f6",
|
|
5304
|
+
textColor: "#ffffff"
|
|
5305
|
+
}
|
|
4182
5306
|
};
|
|
4183
|
-
|
|
4184
|
-
|
|
5307
|
+
const currentPalette = palette[type];
|
|
5308
|
+
const bgStyles = {
|
|
5309
|
+
success: "bg-green-500",
|
|
5310
|
+
error: "bg-red-500",
|
|
5311
|
+
warning: "bg-yellow-500",
|
|
5312
|
+
info: "bg-blue-500"
|
|
5313
|
+
};
|
|
5314
|
+
return /* @__PURE__ */ jsx42(
|
|
5315
|
+
Animated4.View,
|
|
4185
5316
|
{
|
|
4186
5317
|
style: {
|
|
4187
5318
|
opacity: fadeAnim,
|
|
@@ -4194,17 +5325,25 @@ function ToastItemView({ message, type, onHide }) {
|
|
|
4194
5325
|
}
|
|
4195
5326
|
]
|
|
4196
5327
|
},
|
|
4197
|
-
children: /* @__PURE__ */
|
|
5328
|
+
children: /* @__PURE__ */ jsx42(
|
|
5329
|
+
AppView,
|
|
5330
|
+
{
|
|
5331
|
+
testID: `toast-item-${type}`,
|
|
5332
|
+
className: `${bgStyles[type]} px-4 py-3 rounded-lg mb-2 mx-4 shadow-lg`,
|
|
5333
|
+
style: { backgroundColor: currentPalette.backgroundColor },
|
|
5334
|
+
children: /* @__PURE__ */ jsx42(AppText, { className: "text-center", style: { color: currentPalette.textColor }, children: message })
|
|
5335
|
+
}
|
|
5336
|
+
)
|
|
4198
5337
|
}
|
|
4199
5338
|
);
|
|
4200
5339
|
}
|
|
4201
5340
|
|
|
4202
5341
|
// src/overlay/toast/provider.tsx
|
|
4203
|
-
import { jsx as
|
|
5342
|
+
import { jsx as jsx43, jsxs as jsxs17 } from "nativewind/jsx-runtime";
|
|
4204
5343
|
function ToastProvider({ children }) {
|
|
4205
|
-
const [toasts, setToasts] =
|
|
4206
|
-
const timersRef =
|
|
4207
|
-
const remove =
|
|
5344
|
+
const [toasts, setToasts] = useState32([]);
|
|
5345
|
+
const timersRef = useRef14(/* @__PURE__ */ new Map());
|
|
5346
|
+
const remove = useCallback22((id) => {
|
|
4208
5347
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
4209
5348
|
const timer = timersRef.current.get(id);
|
|
4210
5349
|
if (timer) {
|
|
@@ -4212,7 +5351,7 @@ function ToastProvider({ children }) {
|
|
|
4212
5351
|
timersRef.current.delete(id);
|
|
4213
5352
|
}
|
|
4214
5353
|
}, []);
|
|
4215
|
-
const show =
|
|
5354
|
+
const show = useCallback22(
|
|
4216
5355
|
(message, type = "info", duration = 3e3) => {
|
|
4217
5356
|
const id = Math.random().toString(36).substring(7);
|
|
4218
5357
|
const toast = { id, message, type, duration };
|
|
@@ -4222,28 +5361,28 @@ function ToastProvider({ children }) {
|
|
|
4222
5361
|
},
|
|
4223
5362
|
[remove]
|
|
4224
5363
|
);
|
|
4225
|
-
const success =
|
|
5364
|
+
const success = useCallback22(
|
|
4226
5365
|
(message, duration) => show(message, "success", duration),
|
|
4227
5366
|
[show]
|
|
4228
5367
|
);
|
|
4229
|
-
const error =
|
|
5368
|
+
const error = useCallback22(
|
|
4230
5369
|
(message, duration) => show(message, "error", duration),
|
|
4231
5370
|
[show]
|
|
4232
5371
|
);
|
|
4233
|
-
const info =
|
|
5372
|
+
const info = useCallback22(
|
|
4234
5373
|
(message, duration) => show(message, "info", duration),
|
|
4235
5374
|
[show]
|
|
4236
5375
|
);
|
|
4237
|
-
const warning =
|
|
5376
|
+
const warning = useCallback22(
|
|
4238
5377
|
(message, duration) => show(message, "warning", duration),
|
|
4239
5378
|
[show]
|
|
4240
5379
|
);
|
|
4241
|
-
return /* @__PURE__ */
|
|
5380
|
+
return /* @__PURE__ */ jsxs17(ToastContext.Provider, { value: { show, success, error, info, warning }, children: [
|
|
4242
5381
|
children,
|
|
4243
|
-
/* @__PURE__ */
|
|
5382
|
+
/* @__PURE__ */ jsx43(View10, { style: styles15.toastContainer, pointerEvents: "none", children: toasts.map((toast) => /* @__PURE__ */ jsx43(ToastItemView, { ...toast, onHide: () => remove(toast.id) }, toast.id)) })
|
|
4244
5383
|
] });
|
|
4245
5384
|
}
|
|
4246
|
-
var
|
|
5385
|
+
var styles15 = StyleSheet16.create({
|
|
4247
5386
|
toastContainer: {
|
|
4248
5387
|
position: "absolute",
|
|
4249
5388
|
top: 60,
|
|
@@ -4254,7 +5393,7 @@ var styles14 = StyleSheet15.create({
|
|
|
4254
5393
|
});
|
|
4255
5394
|
|
|
4256
5395
|
// src/overlay/alert/provider.tsx
|
|
4257
|
-
import { useState as
|
|
5396
|
+
import { useState as useState33, useCallback as useCallback23 } from "react";
|
|
4258
5397
|
|
|
4259
5398
|
// src/overlay/alert/context.ts
|
|
4260
5399
|
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
@@ -4266,8 +5405,17 @@ function useAlertContext() {
|
|
|
4266
5405
|
}
|
|
4267
5406
|
|
|
4268
5407
|
// src/overlay/alert/component.tsx
|
|
4269
|
-
import {
|
|
4270
|
-
import {
|
|
5408
|
+
import { useEffect as useEffect20, useRef as useRef15 } from "react";
|
|
5409
|
+
import { View as View11, Modal as Modal5, StyleSheet as StyleSheet17, Animated as Animated5 } from "react-native";
|
|
5410
|
+
import { jsx as jsx44, jsxs as jsxs18 } from "nativewind/jsx-runtime";
|
|
5411
|
+
function createAnimatedValue5(value) {
|
|
5412
|
+
const AnimatedValue = Animated5.Value;
|
|
5413
|
+
try {
|
|
5414
|
+
return new AnimatedValue(value);
|
|
5415
|
+
} catch {
|
|
5416
|
+
return AnimatedValue(value);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
4271
5419
|
function AlertModal({
|
|
4272
5420
|
visible,
|
|
4273
5421
|
title,
|
|
@@ -4278,23 +5426,67 @@ function AlertModal({
|
|
|
4278
5426
|
onConfirm,
|
|
4279
5427
|
onCancel
|
|
4280
5428
|
}) {
|
|
5429
|
+
const progress = useRef15(createAnimatedValue5(0)).current;
|
|
5430
|
+
useEffect20(() => {
|
|
5431
|
+
if (!visible) {
|
|
5432
|
+
progress.setValue(0);
|
|
5433
|
+
return;
|
|
5434
|
+
}
|
|
5435
|
+
progress.setValue(0);
|
|
5436
|
+
Animated5.timing(progress, {
|
|
5437
|
+
toValue: 1,
|
|
5438
|
+
duration: 220,
|
|
5439
|
+
useNativeDriver: true
|
|
5440
|
+
}).start();
|
|
5441
|
+
}, [progress, visible]);
|
|
4281
5442
|
if (!visible) return null;
|
|
4282
|
-
return /* @__PURE__ */
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
5443
|
+
return /* @__PURE__ */ jsx44(Modal5, { transparent: true, visible: true, animationType: "none", children: /* @__PURE__ */ jsxs18(View11, { style: styles16.container, children: [
|
|
5444
|
+
/* @__PURE__ */ jsx44(Animated5.View, { style: [styles16.overlay, { opacity: progress }] }),
|
|
5445
|
+
/* @__PURE__ */ jsxs18(
|
|
5446
|
+
Animated5.View,
|
|
5447
|
+
{
|
|
5448
|
+
style: [
|
|
5449
|
+
styles16.alertBox,
|
|
5450
|
+
{
|
|
5451
|
+
opacity: progress,
|
|
5452
|
+
transform: [
|
|
5453
|
+
{
|
|
5454
|
+
translateY: progress.interpolate({
|
|
5455
|
+
inputRange: [0, 1],
|
|
5456
|
+
outputRange: [16, 0]
|
|
5457
|
+
})
|
|
5458
|
+
},
|
|
5459
|
+
{
|
|
5460
|
+
scale: progress.interpolate({
|
|
5461
|
+
inputRange: [0, 1],
|
|
5462
|
+
outputRange: [0.96, 1]
|
|
5463
|
+
})
|
|
5464
|
+
}
|
|
5465
|
+
]
|
|
5466
|
+
}
|
|
5467
|
+
],
|
|
5468
|
+
children: [
|
|
5469
|
+
title && /* @__PURE__ */ jsx44(AppText, { className: "text-lg font-semibold text-center mb-2", children: title }),
|
|
5470
|
+
message && /* @__PURE__ */ jsx44(AppText, { className: "text-gray-600 text-center mb-4", children: message }),
|
|
5471
|
+
/* @__PURE__ */ jsxs18(AppView, { row: true, gap: 3, className: "mt-2", children: [
|
|
5472
|
+
showCancel && /* @__PURE__ */ jsx44(AppPressable, { onPress: onCancel, className: "flex-1 py-3 bg-gray-100 rounded-lg", children: /* @__PURE__ */ jsx44(AppText, { className: "text-center text-gray-700", children: cancelText || "\u53D6\u6D88" }) }),
|
|
5473
|
+
/* @__PURE__ */ jsx44(AppPressable, { onPress: onConfirm, className: "flex-1 py-3 bg-primary-500 rounded-lg", children: /* @__PURE__ */ jsx44(AppText, { className: "text-center text-white", children: confirmText || "\u786E\u5B9A" }) })
|
|
5474
|
+
] })
|
|
5475
|
+
]
|
|
5476
|
+
}
|
|
5477
|
+
)
|
|
5478
|
+
] }) });
|
|
4290
5479
|
}
|
|
4291
|
-
var
|
|
4292
|
-
|
|
5480
|
+
var styles16 = StyleSheet17.create({
|
|
5481
|
+
container: {
|
|
4293
5482
|
flex: 1,
|
|
4294
|
-
backgroundColor: "rgba(0,0,0,0.5)",
|
|
4295
5483
|
justifyContent: "center",
|
|
4296
5484
|
alignItems: "center"
|
|
4297
5485
|
},
|
|
5486
|
+
overlay: {
|
|
5487
|
+
...StyleSheet17.absoluteFillObject,
|
|
5488
|
+
backgroundColor: "rgba(0,0,0,0.5)"
|
|
5489
|
+
},
|
|
4298
5490
|
alertBox: {
|
|
4299
5491
|
backgroundColor: "white",
|
|
4300
5492
|
borderRadius: 12,
|
|
@@ -4310,32 +5502,32 @@ var styles15 = StyleSheet16.create({
|
|
|
4310
5502
|
});
|
|
4311
5503
|
|
|
4312
5504
|
// src/overlay/alert/provider.tsx
|
|
4313
|
-
import { jsx as
|
|
5505
|
+
import { jsx as jsx45, jsxs as jsxs19 } from "nativewind/jsx-runtime";
|
|
4314
5506
|
function AlertProvider({ children }) {
|
|
4315
|
-
const [alert, setAlert] =
|
|
4316
|
-
const showAlert =
|
|
5507
|
+
const [alert, setAlert] = useState33(null);
|
|
5508
|
+
const showAlert = useCallback23((options) => {
|
|
4317
5509
|
setAlert({ ...options, visible: true });
|
|
4318
5510
|
}, []);
|
|
4319
|
-
const confirm =
|
|
5511
|
+
const confirm = useCallback23(
|
|
4320
5512
|
(options) => {
|
|
4321
5513
|
showAlert({ ...options, showCancel: true });
|
|
4322
5514
|
},
|
|
4323
5515
|
[showAlert]
|
|
4324
5516
|
);
|
|
4325
|
-
const hide =
|
|
5517
|
+
const hide = useCallback23(() => {
|
|
4326
5518
|
setAlert(null);
|
|
4327
5519
|
}, []);
|
|
4328
|
-
const handleConfirm =
|
|
5520
|
+
const handleConfirm = useCallback23(() => {
|
|
4329
5521
|
alert?.onConfirm?.();
|
|
4330
5522
|
hide();
|
|
4331
5523
|
}, [alert, hide]);
|
|
4332
|
-
const handleCancel =
|
|
5524
|
+
const handleCancel = useCallback23(() => {
|
|
4333
5525
|
alert?.onCancel?.();
|
|
4334
5526
|
hide();
|
|
4335
5527
|
}, [alert, hide]);
|
|
4336
|
-
return /* @__PURE__ */
|
|
5528
|
+
return /* @__PURE__ */ jsxs19(AlertContext.Provider, { value: { alert: showAlert, confirm }, children: [
|
|
4337
5529
|
children,
|
|
4338
|
-
/* @__PURE__ */
|
|
5530
|
+
/* @__PURE__ */ jsx45(
|
|
4339
5531
|
AlertModal,
|
|
4340
5532
|
{
|
|
4341
5533
|
visible: alert?.visible ?? false,
|
|
@@ -4351,14 +5543,378 @@ function AlertProvider({ children }) {
|
|
|
4351
5543
|
] });
|
|
4352
5544
|
}
|
|
4353
5545
|
|
|
5546
|
+
// src/overlay/logger/provider.tsx
|
|
5547
|
+
import { useCallback as useCallback24, useEffect as useEffect21, useMemo as useMemo12, useState as useState35 } from "react";
|
|
5548
|
+
|
|
5549
|
+
// src/overlay/logger/context.ts
|
|
5550
|
+
import { createContext as createContext5, useContext as useContext5 } from "react";
|
|
5551
|
+
var LoggerContext = createContext5(null);
|
|
5552
|
+
function useLoggerContext() {
|
|
5553
|
+
const ctx = useContext5(LoggerContext);
|
|
5554
|
+
if (!ctx) throw new Error("useLogger must be used within LoggerProvider");
|
|
5555
|
+
return ctx;
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
// src/overlay/logger/component.tsx
|
|
5559
|
+
import { useMemo as useMemo11, useState as useState34 } from "react";
|
|
5560
|
+
import { jsx as jsx46, jsxs as jsxs20 } from "nativewind/jsx-runtime";
|
|
5561
|
+
var FILTERS = ["all", "error", "warn", "info", "debug"];
|
|
5562
|
+
var ALL_NAMESPACE = "all";
|
|
5563
|
+
var DEFAULT_SEARCH_PLACEHOLDER = "\u641C\u7D22\u65E5\u5FD7";
|
|
5564
|
+
function withAlpha(color, alpha = "20") {
|
|
5565
|
+
return color.startsWith("#") && color.length === 7 ? `${color}${alpha}` : color;
|
|
5566
|
+
}
|
|
5567
|
+
function matchesSearch(entry, keyword) {
|
|
5568
|
+
if (!keyword.trim()) return true;
|
|
5569
|
+
const normalized = keyword.trim().toLowerCase();
|
|
5570
|
+
const detail = stringifyLogData(entry.data).toLowerCase();
|
|
5571
|
+
return [entry.message, entry.namespace ?? "", detail].join(" ").toLowerCase().includes(normalized);
|
|
5572
|
+
}
|
|
5573
|
+
function LogOverlay({
|
|
5574
|
+
entries,
|
|
5575
|
+
onClear,
|
|
5576
|
+
defaultExpanded = false,
|
|
5577
|
+
exportEnabled = true,
|
|
5578
|
+
onExport
|
|
5579
|
+
}) {
|
|
5580
|
+
const colors = useThemeColors();
|
|
5581
|
+
const [expanded, setExpanded] = useState34(defaultExpanded);
|
|
5582
|
+
const [filter, setFilter] = useState34("all");
|
|
5583
|
+
const [namespaceFilter, setNamespaceFilter] = useState34(ALL_NAMESPACE);
|
|
5584
|
+
const [keyword, setKeyword] = useState34("");
|
|
5585
|
+
const namespaces = useMemo11(
|
|
5586
|
+
() => [
|
|
5587
|
+
ALL_NAMESPACE,
|
|
5588
|
+
...Array.from(
|
|
5589
|
+
new Set(
|
|
5590
|
+
entries.map((entry) => entry.namespace).filter((value) => Boolean(value))
|
|
5591
|
+
)
|
|
5592
|
+
)
|
|
5593
|
+
],
|
|
5594
|
+
[entries]
|
|
5595
|
+
);
|
|
5596
|
+
const filteredEntries = useMemo11(() => {
|
|
5597
|
+
return entries.filter((entry) => {
|
|
5598
|
+
const levelMatched = filter === "all" ? true : entry.level === filter;
|
|
5599
|
+
const namespaceMatched = namespaceFilter === ALL_NAMESPACE ? true : entry.namespace === namespaceFilter;
|
|
5600
|
+
const searchMatched = matchesSearch(entry, keyword);
|
|
5601
|
+
return levelMatched && namespaceMatched && searchMatched;
|
|
5602
|
+
});
|
|
5603
|
+
}, [entries, filter, keyword, namespaceFilter]);
|
|
5604
|
+
const levelStyles = {
|
|
5605
|
+
debug: { text: colors.muted, bg: colors.cardElevated },
|
|
5606
|
+
info: { text: colors.info, bg: withAlpha(colors.info) },
|
|
5607
|
+
warn: { text: colors.warning, bg: withAlpha(colors.warning) },
|
|
5608
|
+
error: { text: colors.error, bg: withAlpha(colors.error) }
|
|
5609
|
+
};
|
|
5610
|
+
const handleExport = () => {
|
|
5611
|
+
const payload = {
|
|
5612
|
+
entries: filteredEntries,
|
|
5613
|
+
serialized: serializeLogEntries(filteredEntries)
|
|
5614
|
+
};
|
|
5615
|
+
if (onExport) {
|
|
5616
|
+
onExport(payload);
|
|
5617
|
+
return;
|
|
5618
|
+
}
|
|
5619
|
+
console.info("[LoggerExport]", payload.serialized);
|
|
5620
|
+
};
|
|
5621
|
+
return /* @__PURE__ */ jsxs20(AppView, { pointerEvents: "box-none", style: { position: "absolute", right: 12, bottom: 24, left: 12, zIndex: 9998 }, children: [
|
|
5622
|
+
expanded && /* @__PURE__ */ jsxs20(
|
|
5623
|
+
AppView,
|
|
5624
|
+
{
|
|
5625
|
+
testID: "logger-overlay-panel",
|
|
5626
|
+
style: {
|
|
5627
|
+
maxHeight: 360,
|
|
5628
|
+
borderRadius: 16,
|
|
5629
|
+
backgroundColor: colors.card,
|
|
5630
|
+
borderWidth: 0.5,
|
|
5631
|
+
borderColor: colors.border,
|
|
5632
|
+
shadowColor: "#000000",
|
|
5633
|
+
shadowOpacity: 0.15,
|
|
5634
|
+
shadowRadius: 16,
|
|
5635
|
+
shadowOffset: { width: 0, height: 8 },
|
|
5636
|
+
elevation: 12,
|
|
5637
|
+
marginBottom: 12
|
|
5638
|
+
},
|
|
5639
|
+
children: [
|
|
5640
|
+
/* @__PURE__ */ jsxs20(AppView, { row: true, items: "center", justify: "between", className: "px-4 py-3", style: { borderBottomWidth: 0.5, borderBottomColor: colors.divider }, children: [
|
|
5641
|
+
/* @__PURE__ */ jsx46(AppText, { weight: "semibold", children: "\u5F00\u53D1\u65E5\u5FD7" }),
|
|
5642
|
+
/* @__PURE__ */ jsxs20(AppView, { row: true, gap: 2, children: [
|
|
5643
|
+
exportEnabled ? /* @__PURE__ */ jsx46(AppPressable, { testID: "logger-overlay-export", onPress: handleExport, className: "px-3 py-1 rounded-full", style: { backgroundColor: colors.cardElevated }, children: /* @__PURE__ */ jsx46(AppText, { size: "xs", tone: "muted", children: "\u5BFC\u51FA" }) }) : null,
|
|
5644
|
+
/* @__PURE__ */ jsx46(AppPressable, { onPress: onClear, className: "px-3 py-1 rounded-full", style: { backgroundColor: colors.cardElevated }, children: /* @__PURE__ */ jsx46(AppText, { size: "xs", tone: "muted", children: "\u6E05\u7A7A" }) }),
|
|
5645
|
+
/* @__PURE__ */ jsx46(AppPressable, { onPress: () => setExpanded(false), className: "px-3 py-1 rounded-full", style: { backgroundColor: colors.cardElevated }, children: /* @__PURE__ */ jsx46(AppText, { size: "xs", tone: "muted", children: "\u6536\u8D77" }) })
|
|
5646
|
+
] })
|
|
5647
|
+
] }),
|
|
5648
|
+
/* @__PURE__ */ jsx46(AppScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, className: "px-3 py-2", contentContainerStyle: { gap: 8 }, children: FILTERS.map((item) => {
|
|
5649
|
+
const active = filter === item;
|
|
5650
|
+
return /* @__PURE__ */ jsx46(
|
|
5651
|
+
AppPressable,
|
|
5652
|
+
{
|
|
5653
|
+
onPress: () => setFilter(item),
|
|
5654
|
+
className: "px-3 py-1 rounded-full",
|
|
5655
|
+
style: { backgroundColor: active ? colors.primary : colors.cardElevated },
|
|
5656
|
+
children: /* @__PURE__ */ jsx46(AppText, { size: "xs", style: { color: active ? colors.textInverse : colors.textSecondary }, children: item.toUpperCase() })
|
|
5657
|
+
},
|
|
5658
|
+
item
|
|
5659
|
+
);
|
|
5660
|
+
}) }),
|
|
5661
|
+
/* @__PURE__ */ jsx46(AppScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, className: "px-3 pb-2", contentContainerStyle: { gap: 8 }, children: namespaces.map((item) => {
|
|
5662
|
+
const active = namespaceFilter === item;
|
|
5663
|
+
const label = item === ALL_NAMESPACE ? "\u5168\u90E8\u6A21\u5757" : String(item);
|
|
5664
|
+
return /* @__PURE__ */ jsx46(
|
|
5665
|
+
AppPressable,
|
|
5666
|
+
{
|
|
5667
|
+
testID: `logger-namespace-${item}`,
|
|
5668
|
+
onPress: () => setNamespaceFilter(item),
|
|
5669
|
+
className: "px-3 py-1 rounded-full",
|
|
5670
|
+
style: { backgroundColor: active ? colors.info : colors.cardElevated },
|
|
5671
|
+
children: /* @__PURE__ */ jsx46(AppText, { size: "xs", style: { color: active ? colors.textInverse : colors.textSecondary }, children: label })
|
|
5672
|
+
},
|
|
5673
|
+
item
|
|
5674
|
+
);
|
|
5675
|
+
}) }),
|
|
5676
|
+
/* @__PURE__ */ jsx46(AppView, { className: "px-3 pb-2", children: /* @__PURE__ */ jsx46(
|
|
5677
|
+
AppInput,
|
|
5678
|
+
{
|
|
5679
|
+
placeholder: DEFAULT_SEARCH_PLACEHOLDER,
|
|
5680
|
+
value: keyword,
|
|
5681
|
+
onChangeText: setKeyword
|
|
5682
|
+
}
|
|
5683
|
+
) }),
|
|
5684
|
+
/* @__PURE__ */ jsx46(AppScrollView, { className: "px-3 pb-3", showsVerticalScrollIndicator: false, children: /* @__PURE__ */ jsx46(AppView, { gap: 2, children: filteredEntries.length === 0 ? /* @__PURE__ */ jsx46(AppView, { className: "py-8 items-center", children: /* @__PURE__ */ jsx46(AppText, { tone: "muted", children: "\u6682\u65E0\u65E5\u5FD7" }) }) : filteredEntries.map((entry) => {
|
|
5685
|
+
const palette = levelStyles[entry.level];
|
|
5686
|
+
const detail = stringifyLogData(entry.data);
|
|
5687
|
+
return /* @__PURE__ */ jsxs20(
|
|
5688
|
+
AppView,
|
|
5689
|
+
{
|
|
5690
|
+
className: "px-3 py-3 rounded-xl",
|
|
5691
|
+
style: { backgroundColor: colors.cardElevated, borderWidth: 0.5, borderColor: colors.divider },
|
|
5692
|
+
children: [
|
|
5693
|
+
/* @__PURE__ */ jsxs20(AppView, { row: true, items: "center", justify: "between", children: [
|
|
5694
|
+
/* @__PURE__ */ jsxs20(AppView, { row: true, items: "center", gap: 2, children: [
|
|
5695
|
+
/* @__PURE__ */ jsx46(AppView, { className: "px-2 py-1 rounded-full", style: { backgroundColor: palette.bg }, children: /* @__PURE__ */ jsx46(AppText, { size: "xs", style: { color: palette.text }, children: entry.level.toUpperCase() }) }),
|
|
5696
|
+
entry.namespace ? /* @__PURE__ */ jsxs20(AppText, { size: "xs", tone: "muted", children: [
|
|
5697
|
+
"[",
|
|
5698
|
+
entry.namespace,
|
|
5699
|
+
"]"
|
|
5700
|
+
] }) : null
|
|
5701
|
+
] }),
|
|
5702
|
+
/* @__PURE__ */ jsx46(AppText, { size: "xs", tone: "muted", children: formatLogTime(entry.timestamp) })
|
|
5703
|
+
] }),
|
|
5704
|
+
/* @__PURE__ */ jsx46(AppText, { className: "mt-2", size: "sm", children: entry.message }),
|
|
5705
|
+
detail ? /* @__PURE__ */ jsx46(AppView, { className: "mt-2 px-2 py-2 rounded-lg", style: { backgroundColor: colors.background }, children: /* @__PURE__ */ jsx46(AppText, { size: "xs", tone: "muted", children: detail }) }) : null
|
|
5706
|
+
]
|
|
5707
|
+
},
|
|
5708
|
+
entry.id
|
|
5709
|
+
);
|
|
5710
|
+
}) }) })
|
|
5711
|
+
]
|
|
5712
|
+
}
|
|
5713
|
+
),
|
|
5714
|
+
/* @__PURE__ */ jsx46(AppView, { pointerEvents: "box-none", row: true, justify: "end", children: /* @__PURE__ */ jsx46(
|
|
5715
|
+
AppPressable,
|
|
5716
|
+
{
|
|
5717
|
+
testID: "logger-overlay-toggle",
|
|
5718
|
+
onPress: () => setExpanded((value) => !value),
|
|
5719
|
+
className: "px-4 py-3 rounded-full",
|
|
5720
|
+
style: { backgroundColor: colors.primary, shadowColor: "#000000", shadowOpacity: 0.18, shadowRadius: 12, shadowOffset: { width: 0, height: 4 }, elevation: 8 },
|
|
5721
|
+
children: /* @__PURE__ */ jsxs20(AppText, { weight: "semibold", style: { color: colors.textInverse }, children: [
|
|
5722
|
+
"Logs ",
|
|
5723
|
+
entries.length
|
|
5724
|
+
] })
|
|
5725
|
+
}
|
|
5726
|
+
) })
|
|
5727
|
+
] });
|
|
5728
|
+
}
|
|
5729
|
+
|
|
5730
|
+
// src/overlay/logger/provider.tsx
|
|
5731
|
+
import { jsx as jsx47, jsxs as jsxs21 } from "nativewind/jsx-runtime";
|
|
5732
|
+
function LoggerProvider({
|
|
5733
|
+
children,
|
|
5734
|
+
enabled = false,
|
|
5735
|
+
level = "debug",
|
|
5736
|
+
maxEntries = 200,
|
|
5737
|
+
overlayEnabled = false,
|
|
5738
|
+
defaultExpanded = false,
|
|
5739
|
+
consoleEnabled = true,
|
|
5740
|
+
transports = [],
|
|
5741
|
+
exportEnabled = true,
|
|
5742
|
+
onExport
|
|
5743
|
+
}) {
|
|
5744
|
+
const [entries, setEntries] = useState35([]);
|
|
5745
|
+
const clear = useCallback24(() => {
|
|
5746
|
+
setEntries([]);
|
|
5747
|
+
}, []);
|
|
5748
|
+
const resolvedTransports = useMemo12(() => {
|
|
5749
|
+
const list = [];
|
|
5750
|
+
if (enabled && consoleEnabled) {
|
|
5751
|
+
list.push(createConsoleTransport());
|
|
5752
|
+
}
|
|
5753
|
+
if (transports.length > 0) {
|
|
5754
|
+
list.push(...transports);
|
|
5755
|
+
}
|
|
5756
|
+
return list;
|
|
5757
|
+
}, [consoleEnabled, enabled, transports]);
|
|
5758
|
+
const write = useCallback24(
|
|
5759
|
+
(nextLevel, message, data, namespace) => {
|
|
5760
|
+
if (!enabled || !shouldLog(level, nextLevel)) return;
|
|
5761
|
+
const entry = createLogEntry({ level: nextLevel, message, data, namespace, source: "logger" });
|
|
5762
|
+
setEntries((prev) => [entry, ...prev].slice(0, maxEntries));
|
|
5763
|
+
resolvedTransports.forEach((transport) => transport(entry));
|
|
5764
|
+
},
|
|
5765
|
+
[enabled, level, maxEntries, resolvedTransports]
|
|
5766
|
+
);
|
|
5767
|
+
const contextValue = useMemo12(
|
|
5768
|
+
() => ({
|
|
5769
|
+
entries,
|
|
5770
|
+
enabled,
|
|
5771
|
+
level,
|
|
5772
|
+
clear,
|
|
5773
|
+
log: write,
|
|
5774
|
+
debug: (message, data, namespace) => write("debug", message, data, namespace),
|
|
5775
|
+
info: (message, data, namespace) => write("info", message, data, namespace),
|
|
5776
|
+
warn: (message, data, namespace) => write("warn", message, data, namespace),
|
|
5777
|
+
error: (message, data, namespace) => write("error", message, data, namespace)
|
|
5778
|
+
}),
|
|
5779
|
+
[clear, enabled, entries, level, write]
|
|
5780
|
+
);
|
|
5781
|
+
useEffect21(() => {
|
|
5782
|
+
if (!enabled) {
|
|
5783
|
+
setGlobalLogger(null);
|
|
5784
|
+
return;
|
|
5785
|
+
}
|
|
5786
|
+
setGlobalLogger(contextValue);
|
|
5787
|
+
return () => {
|
|
5788
|
+
setGlobalLogger(null);
|
|
5789
|
+
};
|
|
5790
|
+
}, [contextValue, enabled]);
|
|
5791
|
+
return /* @__PURE__ */ jsxs21(LoggerContext.Provider, { value: contextValue, children: [
|
|
5792
|
+
children,
|
|
5793
|
+
enabled && overlayEnabled ? /* @__PURE__ */ jsx47(
|
|
5794
|
+
LogOverlay,
|
|
5795
|
+
{
|
|
5796
|
+
entries,
|
|
5797
|
+
onClear: clear,
|
|
5798
|
+
defaultExpanded,
|
|
5799
|
+
exportEnabled,
|
|
5800
|
+
onExport
|
|
5801
|
+
}
|
|
5802
|
+
) : null
|
|
5803
|
+
] });
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5806
|
+
// src/overlay/error-boundary/component.tsx
|
|
5807
|
+
import React12 from "react";
|
|
5808
|
+
import { jsx as jsx48, jsxs as jsxs22 } from "nativewind/jsx-runtime";
|
|
5809
|
+
function areResetKeysChanged(prev = [], next = []) {
|
|
5810
|
+
if (prev.length !== next.length) return true;
|
|
5811
|
+
return prev.some((item, index) => !Object.is(item, next[index]));
|
|
5812
|
+
}
|
|
5813
|
+
function DefaultFallback({
|
|
5814
|
+
error,
|
|
5815
|
+
onReset,
|
|
5816
|
+
title = "\u9875\u9762\u53D1\u751F\u5F02\u5E38",
|
|
5817
|
+
description = "\u5DF2\u81EA\u52A8\u6355\u83B7\u6E32\u67D3\u9519\u8BEF\uFF0C\u4F60\u53EF\u4EE5\u91CD\u8BD5\u5E76\u7ED3\u5408\u5F00\u53D1\u65E5\u5FD7\u7EE7\u7EED\u6392\u67E5\u3002",
|
|
5818
|
+
resetText = "\u91CD\u8BD5",
|
|
5819
|
+
showDetails = isDevelopment()
|
|
5820
|
+
}) {
|
|
5821
|
+
return /* @__PURE__ */ jsxs22(AppView, { testID: "app-error-boundary", flex: true, center: true, className: "px-6 py-8", gap: 4, children: [
|
|
5822
|
+
/* @__PURE__ */ jsxs22(AppView, { className: "items-center", gap: 2, children: [
|
|
5823
|
+
/* @__PURE__ */ jsx48(AppText, { size: "xl", weight: "semibold", children: title }),
|
|
5824
|
+
/* @__PURE__ */ jsx48(AppText, { tone: "muted", style: { textAlign: "center" }, children: description })
|
|
5825
|
+
] }),
|
|
5826
|
+
showDetails ? /* @__PURE__ */ jsx48(
|
|
5827
|
+
AppView,
|
|
5828
|
+
{
|
|
5829
|
+
className: "w-full px-4 py-3 rounded-xl",
|
|
5830
|
+
style: { maxWidth: 560, borderWidth: 0.5 },
|
|
5831
|
+
children: /* @__PURE__ */ jsx48(AppText, { testID: "app-error-boundary-detail", size: "sm", children: error.message || String(error) })
|
|
5832
|
+
}
|
|
5833
|
+
) : null,
|
|
5834
|
+
/* @__PURE__ */ jsx48(
|
|
5835
|
+
AppPressable,
|
|
5836
|
+
{
|
|
5837
|
+
testID: "app-error-boundary-reset",
|
|
5838
|
+
onPress: onReset,
|
|
5839
|
+
className: "px-4 py-3 rounded-lg",
|
|
5840
|
+
style: { borderWidth: 0.5 },
|
|
5841
|
+
children: /* @__PURE__ */ jsx48(AppText, { weight: "semibold", children: resetText })
|
|
5842
|
+
}
|
|
5843
|
+
)
|
|
5844
|
+
] });
|
|
5845
|
+
}
|
|
5846
|
+
var AppErrorBoundary = class extends React12.Component {
|
|
5847
|
+
constructor() {
|
|
5848
|
+
super(...arguments);
|
|
5849
|
+
this.state = {
|
|
5850
|
+
error: null
|
|
5851
|
+
};
|
|
5852
|
+
this.handleReset = () => {
|
|
5853
|
+
this.setState({ error: null });
|
|
5854
|
+
this.props.onReset?.();
|
|
5855
|
+
};
|
|
5856
|
+
}
|
|
5857
|
+
static getDerivedStateFromError(error) {
|
|
5858
|
+
return { error };
|
|
5859
|
+
}
|
|
5860
|
+
componentDidCatch(error, errorInfo) {
|
|
5861
|
+
this.context?.error(
|
|
5862
|
+
"React ErrorBoundary \u6355\u83B7\u6E32\u67D3\u5F02\u5E38",
|
|
5863
|
+
{
|
|
5864
|
+
name: error.name,
|
|
5865
|
+
message: error.message,
|
|
5866
|
+
stack: error.stack,
|
|
5867
|
+
componentStack: errorInfo.componentStack
|
|
5868
|
+
},
|
|
5869
|
+
"react"
|
|
5870
|
+
);
|
|
5871
|
+
this.props.onError?.(error, errorInfo);
|
|
5872
|
+
}
|
|
5873
|
+
componentDidUpdate(prevProps) {
|
|
5874
|
+
if (!this.state.error) return;
|
|
5875
|
+
if (areResetKeysChanged(prevProps.resetKeys, this.props.resetKeys)) {
|
|
5876
|
+
this.handleReset();
|
|
5877
|
+
}
|
|
5878
|
+
}
|
|
5879
|
+
render() {
|
|
5880
|
+
const {
|
|
5881
|
+
children,
|
|
5882
|
+
enabled = false,
|
|
5883
|
+
fallback,
|
|
5884
|
+
title,
|
|
5885
|
+
description,
|
|
5886
|
+
showDetails,
|
|
5887
|
+
resetText
|
|
5888
|
+
} = this.props;
|
|
5889
|
+
if (!enabled) return children;
|
|
5890
|
+
if (!this.state.error) return children;
|
|
5891
|
+
if (typeof fallback === "function") {
|
|
5892
|
+
return fallback({ error: this.state.error, reset: this.handleReset });
|
|
5893
|
+
}
|
|
5894
|
+
if (fallback) return fallback;
|
|
5895
|
+
return /* @__PURE__ */ jsx48(
|
|
5896
|
+
DefaultFallback,
|
|
5897
|
+
{
|
|
5898
|
+
error: this.state.error,
|
|
5899
|
+
onReset: this.handleReset,
|
|
5900
|
+
title,
|
|
5901
|
+
description,
|
|
5902
|
+
showDetails,
|
|
5903
|
+
resetText
|
|
5904
|
+
}
|
|
5905
|
+
);
|
|
5906
|
+
}
|
|
5907
|
+
};
|
|
5908
|
+
AppErrorBoundary.contextType = LoggerContext;
|
|
5909
|
+
|
|
4354
5910
|
// src/overlay/provider.tsx
|
|
4355
|
-
import { jsx as
|
|
4356
|
-
function OverlayProvider({ children }) {
|
|
4357
|
-
return /* @__PURE__ */
|
|
5911
|
+
import { jsx as jsx49 } from "nativewind/jsx-runtime";
|
|
5912
|
+
function OverlayProvider({ children, loggerProps, errorBoundaryProps }) {
|
|
5913
|
+
return /* @__PURE__ */ jsx49(LoggerProvider, { ...loggerProps, children: /* @__PURE__ */ jsx49(AppErrorBoundary, { ...errorBoundaryProps, children: /* @__PURE__ */ jsx49(LoadingProvider, { children: /* @__PURE__ */ jsx49(ToastProvider, { children: /* @__PURE__ */ jsx49(AlertProvider, { children }) }) }) }) });
|
|
4358
5914
|
}
|
|
4359
5915
|
|
|
4360
5916
|
// src/overlay/AppProvider.tsx
|
|
4361
|
-
import { Fragment as Fragment4, jsx as
|
|
5917
|
+
import { Fragment as Fragment4, jsx as jsx50, jsxs as jsxs23 } from "nativewind/jsx-runtime";
|
|
4362
5918
|
var defaultLightTheme = {
|
|
4363
5919
|
colors: {
|
|
4364
5920
|
primary: "#f38b32",
|
|
@@ -4384,6 +5940,8 @@ function AppProvider({
|
|
|
4384
5940
|
enableNavigation = true,
|
|
4385
5941
|
enableOverlay = true,
|
|
4386
5942
|
enableTheme = true,
|
|
5943
|
+
enableLogger = isDevelopment(),
|
|
5944
|
+
enableErrorBoundary = isDevelopment(),
|
|
4387
5945
|
enableStatusBar = true,
|
|
4388
5946
|
enableSafeArea = true,
|
|
4389
5947
|
lightTheme = defaultLightTheme,
|
|
@@ -4391,25 +5949,41 @@ function AppProvider({
|
|
|
4391
5949
|
defaultDark = false,
|
|
4392
5950
|
isDark,
|
|
4393
5951
|
statusBarProps,
|
|
5952
|
+
loggerProps,
|
|
5953
|
+
errorBoundaryProps,
|
|
4394
5954
|
...navigationProps
|
|
4395
5955
|
}) {
|
|
4396
5956
|
let content = children;
|
|
4397
5957
|
if (enableOverlay) {
|
|
4398
|
-
content = /* @__PURE__ */
|
|
5958
|
+
content = /* @__PURE__ */ jsx50(
|
|
5959
|
+
OverlayProvider,
|
|
5960
|
+
{
|
|
5961
|
+
loggerProps: enableLogger ? { enabled: true, overlayEnabled: true, ...loggerProps } : { enabled: false, overlayEnabled: false, ...loggerProps },
|
|
5962
|
+
errorBoundaryProps: enableErrorBoundary ? { enabled: true, ...errorBoundaryProps } : { enabled: false, ...errorBoundaryProps },
|
|
5963
|
+
children: content
|
|
5964
|
+
}
|
|
5965
|
+
);
|
|
5966
|
+
} else {
|
|
5967
|
+
if (enableErrorBoundary) {
|
|
5968
|
+
content = /* @__PURE__ */ jsx50(AppErrorBoundary, { enabled: true, ...errorBoundaryProps, children: content });
|
|
5969
|
+
}
|
|
5970
|
+
if (enableLogger) {
|
|
5971
|
+
content = /* @__PURE__ */ jsx50(LoggerProvider, { enabled: true, overlayEnabled: true, ...loggerProps, children: content });
|
|
5972
|
+
}
|
|
4399
5973
|
}
|
|
4400
5974
|
if (enableNavigation) {
|
|
4401
|
-
content = /* @__PURE__ */
|
|
5975
|
+
content = /* @__PURE__ */ jsx50(NavigationProvider, { ...navigationProps, children: content });
|
|
4402
5976
|
}
|
|
4403
5977
|
if (enableTheme) {
|
|
4404
|
-
content = /* @__PURE__ */
|
|
4405
|
-
enableStatusBar && /* @__PURE__ */
|
|
5978
|
+
content = /* @__PURE__ */ jsx50(ThemeProvider, { light: lightTheme, dark: darkTheme, defaultDark, isDark, children: /* @__PURE__ */ jsxs23(Fragment4, { children: [
|
|
5979
|
+
enableStatusBar && /* @__PURE__ */ jsx50(AppStatusBar, { testID: "status-bar", ...statusBarProps }),
|
|
4406
5980
|
content
|
|
4407
5981
|
] }) });
|
|
4408
5982
|
}
|
|
4409
5983
|
if (enableSafeArea) {
|
|
4410
|
-
content = /* @__PURE__ */
|
|
5984
|
+
content = /* @__PURE__ */ jsx50(SafeAreaProvider, { children: content });
|
|
4411
5985
|
}
|
|
4412
|
-
return /* @__PURE__ */
|
|
5986
|
+
return /* @__PURE__ */ jsx50(Fragment4, { children: content });
|
|
4413
5987
|
}
|
|
4414
5988
|
|
|
4415
5989
|
// src/overlay/loading/hooks.ts
|
|
@@ -4427,8 +6001,29 @@ function useAlert() {
|
|
|
4427
6001
|
return useAlertContext();
|
|
4428
6002
|
}
|
|
4429
6003
|
|
|
6004
|
+
// src/overlay/logger/hooks.ts
|
|
6005
|
+
import { useMemo as useMemo13 } from "react";
|
|
6006
|
+
function useLogger(namespace) {
|
|
6007
|
+
const logger = useLoggerContext();
|
|
6008
|
+
return useMemo13(
|
|
6009
|
+
() => ({
|
|
6010
|
+
entries: logger.entries,
|
|
6011
|
+
enabled: logger.enabled,
|
|
6012
|
+
level: logger.level,
|
|
6013
|
+
clear: logger.clear,
|
|
6014
|
+
namespace,
|
|
6015
|
+
log: (level, message, data) => logger.log(level, message, data, namespace),
|
|
6016
|
+
debug: (message, data) => logger.debug(message, data, namespace),
|
|
6017
|
+
info: (message, data) => logger.info(message, data, namespace),
|
|
6018
|
+
warn: (message, data) => logger.warn(message, data, namespace),
|
|
6019
|
+
error: (message, data) => logger.error(message, data, namespace)
|
|
6020
|
+
}),
|
|
6021
|
+
[logger, namespace]
|
|
6022
|
+
);
|
|
6023
|
+
}
|
|
6024
|
+
|
|
4430
6025
|
// src/navigation/components/AppHeader.tsx
|
|
4431
|
-
import { Fragment as Fragment5, jsx as
|
|
6026
|
+
import { Fragment as Fragment5, jsx as jsx51, jsxs as jsxs24 } from "nativewind/jsx-runtime";
|
|
4432
6027
|
function AppHeader({
|
|
4433
6028
|
title,
|
|
4434
6029
|
subtitle,
|
|
@@ -4442,9 +6037,9 @@ function AppHeader({
|
|
|
4442
6037
|
const colors = useThemeColors();
|
|
4443
6038
|
const insets = useSafeAreaInsets3();
|
|
4444
6039
|
const backgroundColor = transparent ? "transparent" : colors.card;
|
|
4445
|
-
return /* @__PURE__ */
|
|
4446
|
-
/* @__PURE__ */
|
|
4447
|
-
/* @__PURE__ */
|
|
6040
|
+
return /* @__PURE__ */ jsxs24(Fragment5, { children: [
|
|
6041
|
+
/* @__PURE__ */ jsx51(AppFocusedStatusBar, { translucent: true, backgroundColor: "transparent" }),
|
|
6042
|
+
/* @__PURE__ */ jsx51(
|
|
4448
6043
|
AppView,
|
|
4449
6044
|
{
|
|
4450
6045
|
style: [
|
|
@@ -4454,39 +6049,39 @@ function AppHeader({
|
|
|
4454
6049
|
},
|
|
4455
6050
|
style
|
|
4456
6051
|
],
|
|
4457
|
-
children: /* @__PURE__ */
|
|
4458
|
-
/* @__PURE__ */
|
|
4459
|
-
/* @__PURE__ */
|
|
4460
|
-
title && /* @__PURE__ */
|
|
6052
|
+
children: /* @__PURE__ */ jsxs24(AppView, { row: true, items: "center", px: 4, style: styles17.container, children: [
|
|
6053
|
+
/* @__PURE__ */ jsx51(AppView, { style: [styles17.sideContainer, styles17.leftContainer], children: leftIcon && /* @__PURE__ */ jsx51(AppPressable, { onPress: onLeftPress, style: styles17.iconButton, children: /* @__PURE__ */ jsx51(Icon, { name: leftIcon, size: 24, color: colors.text }) }) }),
|
|
6054
|
+
/* @__PURE__ */ jsxs24(AppView, { style: styles17.centerContainer, children: [
|
|
6055
|
+
title && /* @__PURE__ */ jsx51(
|
|
4461
6056
|
AppText,
|
|
4462
6057
|
{
|
|
4463
6058
|
size: "lg",
|
|
4464
6059
|
weight: "semibold",
|
|
4465
|
-
style: [
|
|
6060
|
+
style: [styles17.title, { color: colors.text }],
|
|
4466
6061
|
numberOfLines: 1,
|
|
4467
6062
|
children: title
|
|
4468
6063
|
}
|
|
4469
6064
|
),
|
|
4470
|
-
subtitle && /* @__PURE__ */
|
|
6065
|
+
subtitle && /* @__PURE__ */ jsx51(
|
|
4471
6066
|
AppText,
|
|
4472
6067
|
{
|
|
4473
6068
|
size: "xs",
|
|
4474
|
-
style: [
|
|
6069
|
+
style: [styles17.subtitle, { color: colors.textMuted }],
|
|
4475
6070
|
numberOfLines: 1,
|
|
4476
6071
|
children: subtitle
|
|
4477
6072
|
}
|
|
4478
6073
|
)
|
|
4479
6074
|
] }),
|
|
4480
|
-
/* @__PURE__ */
|
|
4481
|
-
/* @__PURE__ */
|
|
4482
|
-
icon.badge ? /* @__PURE__ */
|
|
6075
|
+
/* @__PURE__ */ jsx51(AppView, { row: true, items: "center", style: [styles17.sideContainer, styles17.rightContainer], children: rightIcons.map((icon, index) => /* @__PURE__ */ jsx51(AppPressable, { onPress: icon.onPress, style: styles17.iconButton, children: /* @__PURE__ */ jsxs24(AppView, { children: [
|
|
6076
|
+
/* @__PURE__ */ jsx51(Icon, { name: icon.icon, size: 24, color: colors.text }),
|
|
6077
|
+
icon.badge ? /* @__PURE__ */ jsx51(AppView, { style: styles17.badge, children: /* @__PURE__ */ jsx51(AppText, { size: "xs", color: "white", style: styles17.badgeText, children: icon.badge > 99 ? "99+" : icon.badge }) }) : null
|
|
4483
6078
|
] }) }, index)) })
|
|
4484
6079
|
] })
|
|
4485
6080
|
}
|
|
4486
6081
|
)
|
|
4487
6082
|
] });
|
|
4488
6083
|
}
|
|
4489
|
-
var
|
|
6084
|
+
var styles17 = StyleSheet18.create({
|
|
4490
6085
|
container: {
|
|
4491
6086
|
height: 44
|
|
4492
6087
|
// iOS 标准导航栏高度
|
|
@@ -4537,9 +6132,9 @@ var styles16 = StyleSheet17.create({
|
|
|
4537
6132
|
});
|
|
4538
6133
|
|
|
4539
6134
|
// src/navigation/components/DrawerContent.tsx
|
|
4540
|
-
import { View as View12, TouchableOpacity as
|
|
6135
|
+
import { View as View12, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet19 } from "react-native";
|
|
4541
6136
|
import { DrawerContentScrollView } from "@react-navigation/drawer";
|
|
4542
|
-
import { jsx as
|
|
6137
|
+
import { jsx as jsx52, jsxs as jsxs25 } from "nativewind/jsx-runtime";
|
|
4543
6138
|
function DrawerContent({
|
|
4544
6139
|
state,
|
|
4545
6140
|
descriptors,
|
|
@@ -4568,20 +6163,20 @@ function DrawerContent({
|
|
|
4568
6163
|
badge: options.tabBarBadge
|
|
4569
6164
|
};
|
|
4570
6165
|
});
|
|
4571
|
-
return /* @__PURE__ */
|
|
4572
|
-
header && /* @__PURE__ */
|
|
4573
|
-
/* @__PURE__ */
|
|
6166
|
+
return /* @__PURE__ */ jsxs25(DrawerContentScrollView, { style: [styles18.container, { backgroundColor }], children: [
|
|
6167
|
+
header && /* @__PURE__ */ jsx52(View12, { style: [styles18.header, { borderBottomColor: dividerColor }], children: header }),
|
|
6168
|
+
/* @__PURE__ */ jsx52(AppView, { className: "py-2", children: drawerItems.map((item) => {
|
|
4574
6169
|
const isFocused = state.routes[state.index].name === item.name;
|
|
4575
6170
|
const onPress = () => {
|
|
4576
6171
|
navigation.navigate(item.name);
|
|
4577
6172
|
};
|
|
4578
|
-
return /* @__PURE__ */
|
|
4579
|
-
|
|
6173
|
+
return /* @__PURE__ */ jsxs25(
|
|
6174
|
+
TouchableOpacity9,
|
|
4580
6175
|
{
|
|
4581
6176
|
onPress,
|
|
4582
|
-
style: [
|
|
6177
|
+
style: [styles18.item, isFocused && { backgroundColor: activeBgColor }],
|
|
4583
6178
|
children: [
|
|
4584
|
-
item.icon && /* @__PURE__ */
|
|
6179
|
+
item.icon && /* @__PURE__ */ jsx52(View12, { style: styles18.iconContainer, children: /* @__PURE__ */ jsx52(
|
|
4585
6180
|
Icon,
|
|
4586
6181
|
{
|
|
4587
6182
|
name: item.icon,
|
|
@@ -4589,28 +6184,28 @@ function DrawerContent({
|
|
|
4589
6184
|
color: isFocused ? activeColor : inactiveColor
|
|
4590
6185
|
}
|
|
4591
6186
|
) }),
|
|
4592
|
-
/* @__PURE__ */
|
|
6187
|
+
/* @__PURE__ */ jsx52(
|
|
4593
6188
|
AppText,
|
|
4594
6189
|
{
|
|
4595
6190
|
style: [
|
|
4596
|
-
|
|
6191
|
+
styles18.label,
|
|
4597
6192
|
{ color: isFocused ? activeColor : inactiveColor },
|
|
4598
|
-
isFocused &&
|
|
6193
|
+
isFocused && styles18.activeLabel
|
|
4599
6194
|
],
|
|
4600
6195
|
numberOfLines: 1,
|
|
4601
6196
|
children: item.label
|
|
4602
6197
|
}
|
|
4603
6198
|
),
|
|
4604
|
-
item.badge != null && /* @__PURE__ */
|
|
6199
|
+
item.badge != null && /* @__PURE__ */ jsx52(View12, { style: [styles18.badge, { backgroundColor: activeColor }], children: /* @__PURE__ */ jsx52(AppText, { style: styles18.badgeText, children: typeof item.badge === "number" && item.badge > 99 ? "99+" : item.badge }) })
|
|
4605
6200
|
]
|
|
4606
6201
|
},
|
|
4607
6202
|
item.name
|
|
4608
6203
|
);
|
|
4609
6204
|
}) }),
|
|
4610
|
-
footer && /* @__PURE__ */
|
|
6205
|
+
footer && /* @__PURE__ */ jsx52(View12, { style: [styles18.footer, { borderTopColor: dividerColor }], children: footer })
|
|
4611
6206
|
] });
|
|
4612
6207
|
}
|
|
4613
|
-
var
|
|
6208
|
+
var styles18 = StyleSheet19.create({
|
|
4614
6209
|
container: {
|
|
4615
6210
|
flex: 1
|
|
4616
6211
|
},
|
|
@@ -4660,7 +6255,7 @@ var styles17 = StyleSheet18.create({
|
|
|
4660
6255
|
});
|
|
4661
6256
|
|
|
4662
6257
|
// src/navigation/hooks/useNavigation.ts
|
|
4663
|
-
import { useEffect as
|
|
6258
|
+
import { useEffect as useEffect22 } from "react";
|
|
4664
6259
|
import { useNavigation as useRNNavigation } from "@react-navigation/native";
|
|
4665
6260
|
function useNavigation() {
|
|
4666
6261
|
return useRNNavigation();
|
|
@@ -4676,7 +6271,7 @@ function useDrawerNavigation() {
|
|
|
4676
6271
|
}
|
|
4677
6272
|
function useBackHandler(handler) {
|
|
4678
6273
|
const navigation = useRNNavigation();
|
|
4679
|
-
|
|
6274
|
+
useEffect22(() => {
|
|
4680
6275
|
const unsubscribe = navigation.addListener("beforeRemove", (e) => {
|
|
4681
6276
|
if (!handler()) {
|
|
4682
6277
|
e.preventDefault();
|
|
@@ -4714,6 +6309,7 @@ export {
|
|
|
4714
6309
|
ActionIcons,
|
|
4715
6310
|
Alert,
|
|
4716
6311
|
AppButton,
|
|
6312
|
+
AppErrorBoundary,
|
|
4717
6313
|
AppFocusedStatusBar,
|
|
4718
6314
|
AppHeader,
|
|
4719
6315
|
AppImage,
|
|
@@ -4741,13 +6337,18 @@ export {
|
|
|
4741
6337
|
FormItem,
|
|
4742
6338
|
GradientView,
|
|
4743
6339
|
Icon,
|
|
6340
|
+
KeyboardDismissView,
|
|
6341
|
+
LOG_LEVEL_WEIGHT,
|
|
4744
6342
|
Loading,
|
|
6343
|
+
LogOverlay,
|
|
6344
|
+
LoggerProvider,
|
|
4745
6345
|
MemoryStorage,
|
|
4746
6346
|
NavigationContainer2 as NavigationContainer,
|
|
4747
6347
|
NavigationIcons,
|
|
4748
6348
|
NavigationProvider,
|
|
4749
6349
|
OverlayProvider,
|
|
4750
6350
|
PageDrawer,
|
|
6351
|
+
Picker,
|
|
4751
6352
|
Progress,
|
|
4752
6353
|
Radio,
|
|
4753
6354
|
RadioGroup,
|
|
@@ -4769,7 +6370,10 @@ export {
|
|
|
4769
6370
|
clsx,
|
|
4770
6371
|
cn,
|
|
4771
6372
|
createAPI,
|
|
6373
|
+
createApiLoggerTransport,
|
|
6374
|
+
createConsoleTransport,
|
|
4772
6375
|
createDrawerScreens,
|
|
6376
|
+
createLogEntry,
|
|
4773
6377
|
createNavigationTheme,
|
|
4774
6378
|
createStackScreens,
|
|
4775
6379
|
createTabScreens,
|
|
@@ -4778,10 +6382,12 @@ export {
|
|
|
4778
6382
|
enhanceError,
|
|
4779
6383
|
formatCurrency,
|
|
4780
6384
|
formatDate,
|
|
6385
|
+
formatLogTime,
|
|
4781
6386
|
formatNumber,
|
|
4782
6387
|
formatPercent,
|
|
4783
6388
|
formatRelativeTime,
|
|
4784
6389
|
generateColorPalette,
|
|
6390
|
+
getGlobalLogger,
|
|
4785
6391
|
getThemeColors,
|
|
4786
6392
|
getValidationErrors,
|
|
4787
6393
|
hexToRgb,
|
|
@@ -4789,11 +6395,16 @@ export {
|
|
|
4789
6395
|
isValidEmail,
|
|
4790
6396
|
isValidPhone,
|
|
4791
6397
|
mapHttpStatus,
|
|
6398
|
+
normalizeConsoleArgs,
|
|
4792
6399
|
omit,
|
|
4793
6400
|
pick,
|
|
4794
6401
|
rgbToHex,
|
|
6402
|
+
serializeLogEntries,
|
|
6403
|
+
setGlobalLogger,
|
|
6404
|
+
shouldLog,
|
|
4795
6405
|
slugify,
|
|
4796
6406
|
storage,
|
|
6407
|
+
stringifyLogData,
|
|
4797
6408
|
truncate,
|
|
4798
6409
|
twMerge,
|
|
4799
6410
|
useAlert,
|
|
@@ -4808,6 +6419,7 @@ export {
|
|
|
4808
6419
|
useIsFocused2 as useIsFocused,
|
|
4809
6420
|
useKeyboard,
|
|
4810
6421
|
useLoading,
|
|
6422
|
+
useLogger,
|
|
4811
6423
|
useMemoizedFn,
|
|
4812
6424
|
useMutation,
|
|
4813
6425
|
useNavigation,
|