react-native-x-components 0.1.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/dist/XActionSheet/index.d.ts +47 -0
  4. package/dist/XActionSheet/index.js +100 -0
  5. package/dist/XAnimatedSearchPanel/index.d.ts +33 -0
  6. package/dist/XAnimatedSearchPanel/index.js +295 -0
  7. package/dist/XAnimatedView/index.d.ts +77 -0
  8. package/dist/XAnimatedView/index.js +227 -0
  9. package/dist/XButton/index.d.ts +44 -0
  10. package/dist/XButton/index.js +126 -0
  11. package/dist/XCalendar/XCalendarPopup.d.ts +38 -0
  12. package/dist/XCalendar/XCalendarPopup.js +76 -0
  13. package/dist/XCalendar/index.d.ts +50 -0
  14. package/dist/XCalendar/index.js +241 -0
  15. package/dist/XCarousel/index.d.ts +72 -0
  16. package/dist/XCarousel/index.js +183 -0
  17. package/dist/XCascadeSelect/index.d.ts +39 -0
  18. package/dist/XCascadeSelect/index.js +147 -0
  19. package/dist/XChart/index.d.ts +78 -0
  20. package/dist/XChart/index.js +162 -0
  21. package/dist/XCheckbox/index.d.ts +61 -0
  22. package/dist/XCheckbox/index.js +128 -0
  23. package/dist/XConfirmForm/index.d.ts +69 -0
  24. package/dist/XConfirmForm/index.js +113 -0
  25. package/dist/XDivider/index.d.ts +33 -0
  26. package/dist/XDivider/index.js +61 -0
  27. package/dist/XDropdownMenu/index.d.ts +66 -0
  28. package/dist/XDropdownMenu/index.js +258 -0
  29. package/dist/XElevator/index.d.ts +52 -0
  30. package/dist/XElevator/index.js +138 -0
  31. package/dist/XForm/FormStore.d.ts +82 -0
  32. package/dist/XForm/FormStore.js +279 -0
  33. package/dist/XForm/index.d.ts +116 -0
  34. package/dist/XForm/index.js +212 -0
  35. package/dist/XForm/types.d.ts +82 -0
  36. package/dist/XForm/types.js +1 -0
  37. package/dist/XForm/useForm.d.ts +2 -0
  38. package/dist/XForm/useForm.js +19 -0
  39. package/dist/XForm/utils.d.ts +42 -0
  40. package/dist/XForm/utils.js +131 -0
  41. package/dist/XForm/validate.d.ts +20 -0
  42. package/dist/XForm/validate.js +118 -0
  43. package/dist/XFormPro/components/FieldTrigger.d.ts +14 -0
  44. package/dist/XFormPro/components/FieldTrigger.js +38 -0
  45. package/dist/XFormPro/components/XDateField.d.ts +22 -0
  46. package/dist/XFormPro/components/XDateField.js +22 -0
  47. package/dist/XFormPro/components/XSelectField.d.ts +16 -0
  48. package/dist/XFormPro/components/XSelectField.js +19 -0
  49. package/dist/XFormPro/index.d.ts +44 -0
  50. package/dist/XFormPro/index.js +111 -0
  51. package/dist/XFormPro/renderers.d.ts +24 -0
  52. package/dist/XFormPro/renderers.js +53 -0
  53. package/dist/XFormPro/types.d.ts +142 -0
  54. package/dist/XFormPro/types.js +1 -0
  55. package/dist/XImage/index.d.ts +13 -0
  56. package/dist/XImage/index.js +32 -0
  57. package/dist/XInput/index.d.ts +43 -0
  58. package/dist/XInput/index.js +136 -0
  59. package/dist/XLicensePlate/index.d.ts +40 -0
  60. package/dist/XLicensePlate/index.js +172 -0
  61. package/dist/XLocale/index.d.ts +23 -0
  62. package/dist/XLocale/index.js +174 -0
  63. package/dist/XModalForm/index.d.ts +32 -0
  64. package/dist/XModalForm/index.js +85 -0
  65. package/dist/XMultiSelect/index.d.ts +31 -0
  66. package/dist/XMultiSelect/index.js +131 -0
  67. package/dist/XNumberKeyboard/index.d.ts +57 -0
  68. package/dist/XNumberKeyboard/index.js +142 -0
  69. package/dist/XPicker/index.d.ts +35 -0
  70. package/dist/XPicker/index.js +96 -0
  71. package/dist/XPickerDate/index.d.ts +58 -0
  72. package/dist/XPickerDate/index.js +234 -0
  73. package/dist/XPopupProvider/index.d.ts +23 -0
  74. package/dist/XPopupProvider/index.js +6 -0
  75. package/dist/XProgress/index.d.ts +49 -0
  76. package/dist/XProgress/index.js +196 -0
  77. package/dist/XProviders/XImagePreview/index.d.ts +44 -0
  78. package/dist/XProviders/XImagePreview/index.js +164 -0
  79. package/dist/XProviders/XLoadingModal/index.d.ts +46 -0
  80. package/dist/XProviders/XLoadingModal/index.js +119 -0
  81. package/dist/XProviders/XToast/index.d.ts +50 -0
  82. package/dist/XProviders/XToast/index.js +183 -0
  83. package/dist/XProviders/index.d.ts +31 -0
  84. package/dist/XProviders/index.js +28 -0
  85. package/dist/XProviders/usageExample.d.ts +19 -0
  86. package/dist/XProviders/usageExample.js +83 -0
  87. package/dist/XPullView/index.d.ts +76 -0
  88. package/dist/XPullView/index.js +220 -0
  89. package/dist/XRadio/index.d.ts +83 -0
  90. package/dist/XRadio/index.js +186 -0
  91. package/dist/XRecord/index.d.ts +37 -0
  92. package/dist/XRecord/index.js +160 -0
  93. package/dist/XSignature/XSignatureSkia.d.ts +39 -0
  94. package/dist/XSignature/XSignatureSkia.js +184 -0
  95. package/dist/XSignature/core.d.ts +69 -0
  96. package/dist/XSignature/core.js +108 -0
  97. package/dist/XSignature/index.d.ts +33 -0
  98. package/dist/XSignature/index.js +88 -0
  99. package/dist/XTabs/index.d.ts +65 -0
  100. package/dist/XTabs/index.js +210 -0
  101. package/dist/XTag/index.d.ts +61 -0
  102. package/dist/XTag/index.js +153 -0
  103. package/dist/XTopView/index.d.ts +25 -0
  104. package/dist/XTopView/index.js +8 -0
  105. package/dist/XTopViewStore/index.d.ts +45 -0
  106. package/dist/XTopViewStore/index.js +49 -0
  107. package/dist/XUpload/XUploadImage.d.ts +37 -0
  108. package/dist/XUpload/XUploadImage.js +142 -0
  109. package/dist/XUpload/XUploadVideo.d.ts +24 -0
  110. package/dist/XUpload/XUploadVideo.js +83 -0
  111. package/dist/XUpload/adapters.d.ts +50 -0
  112. package/dist/XUpload/adapters.js +110 -0
  113. package/dist/XUpload/helpers.d.ts +35 -0
  114. package/dist/XUpload/helpers.js +85 -0
  115. package/dist/XUpload/index.d.ts +12 -0
  116. package/dist/XUpload/index.js +5 -0
  117. package/dist/XUpload/provider.d.ts +26 -0
  118. package/dist/XUpload/provider.js +43 -0
  119. package/dist/XUpload/types.d.ts +31 -0
  120. package/dist/XUpload/types.js +4 -0
  121. package/dist/XWheel/index.d.ts +49 -0
  122. package/dist/XWheel/index.js +263 -0
  123. package/dist/index.d.ts +113 -0
  124. package/dist/index.js +127 -0
  125. package/dist/theme.d.ts +164 -0
  126. package/dist/theme.js +161 -0
  127. package/package.json +78 -0
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ============================================================================
4
+ * XUpload Provider —— 上传适配器的注入/获取
5
+ * ============================================================================
6
+ *
7
+ * 【两级注入】
8
+ * 1. 全局默认:setXUploadAdapter(adapter)(App 启动时注入 MinIO/业务适配器);
9
+ * 2. 局部覆盖:<XUploadProvider adapter={...}>(子树内所有上传组件生效),
10
+ * 没有时回退全局默认;连全局也没注入时兜底 Mock 并 warn 一次。
11
+ * ============================================================================
12
+ */
13
+ import { createContext, useContext, useMemo } from 'react';
14
+ import { createMinioPresignedAdapter, createMockUploadAdapter } from './adapters';
15
+ /** 全局默认适配器(初始为 Mock,避免演示页崩溃) */
16
+ let globalAdapter = createMockUploadAdapter();
17
+ let warnedNoAdapter = false;
18
+ /** 注入全局默认适配器(App 启动时调用一次) */
19
+ export function setXUploadAdapter(adapter) {
20
+ globalAdapter = adapter;
21
+ }
22
+ /** 获取全局默认适配器 */
23
+ export function getXUploadAdapter() {
24
+ return globalAdapter;
25
+ }
26
+ const XUploadContext = createContext(null);
27
+ export function XUploadProvider({ adapter, children }) {
28
+ const value = useMemo(() => adapter, [adapter]);
29
+ return _jsx(XUploadContext.Provider, { value: value, children: children });
30
+ }
31
+ /** 上传组件内部取适配器:局部 > 全局 > Mock(仅首次 warn) */
32
+ export function useXUploadAdapter(localAdapter) {
33
+ const contextAdapter = useContext(XUploadContext);
34
+ const resolved = localAdapter ?? contextAdapter ?? globalAdapter;
35
+ if (!localAdapter && !contextAdapter && !warnedNoAdapter) {
36
+ warnedNoAdapter = true;
37
+ console.warn('[XUpload] 未注入上传适配器,当前使用 Mock 上传(结果不会真正上传)。' +
38
+ '请在 App 入口调用 setXUploadAdapter(createMinioPresignedAdapter({...})) 或使用 <XUploadProvider>。');
39
+ }
40
+ return resolved;
41
+ }
42
+ /** 便捷构造:MinIO 预签名适配器(re-export) */
43
+ export const createMinioAdapter = createMinioPresignedAdapter;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * XUpload 类型定义 —— 上传文件 / 上传结果 / 适配器协议
3
+ */
4
+ /** 待上传文件(本机) */
5
+ export interface XUploadFile {
6
+ /** 本地地址 file://... */
7
+ uri: string;
8
+ /** 文件名(含扩展名) */
9
+ name: string;
10
+ /** MIME 类型 */
11
+ mimeType: string;
12
+ /** 字节数(可选) */
13
+ size?: number;
14
+ }
15
+ /** 上传结果:MinIO/OSS 模式带 objectKey;直传模式带 url */
16
+ export interface XUploadResult {
17
+ /** 可预览的地址(直链/预签名回显/本机地址) */
18
+ url?: string;
19
+ /** 对象 key(预签名模式核心字段,回显时再换预签名 URL) */
20
+ objectKey?: string;
21
+ /** 透传后端返回的其余字段 */
22
+ [key: string]: any;
23
+ }
24
+ /**
25
+ * 上传适配器协议 —— UI 与上传逻辑的唯一边界。
26
+ * 业务实现一个 upload 即可接入任意后端(MinIO 预签名 / 直传 / 七牛...)。
27
+ */
28
+ export interface XUploadAdapter {
29
+ /** 上传一个文件;onProgress 回传 0~100 */
30
+ upload: (file: XUploadFile, onProgress?: (percent: number) => void) => Promise<XUploadResult>;
31
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * XUpload 类型定义 —— 上传文件 / 上传结果 / 适配器协议
3
+ */
4
+ export {};
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ============================================================================
3
+ * XWheel -- 通用滚轮列(antd-mobile picker-view/wheel.tsx 架构的 RN 移植)
4
+ * ============================================================================
5
+ *
6
+ * 【被 XPickerDate(多列日期)与 XPicker(单列选项)共用】
7
+ *
8
+ * 架构(antd 的核心,丝滑的全部秘密):
9
+ * - 没有 ScrollView。"一个手势 + 一个平移值":Gesture.Pan 驱动单个 y 共享值,
10
+ * 整个行容器只是一个 View 整体 translateY。滚动中每帧只有 1 个 transform
11
+ * 更新,零逐行计算、零 JS 参与。
12
+ * - 所有行视觉完全一致(无逐行高亮)。选中表现完全来自遮罩层:
13
+ * 上下渐变把远处行淡出到背景色 + 中间两条 1px 框线。
14
+ * - 拖拽中越界走橡皮筋(rubberbandIfOutOfBounds,antd 原公式);
15
+ * 松手按"位置 + 速度投影"夹边界取整到行,withSpring 落位,
16
+ * 同时立即上报选中(不等动画结束,antd 行为)。
17
+ * ============================================================================
18
+ */
19
+ import React from 'react';
20
+ /** 滚轮选项:label 显示文本,value 回传值 */
21
+ export interface XWheelOption<T = any> {
22
+ label: string;
23
+ value: T;
24
+ }
25
+ /** 父组件在"确定"时通过 ref 强制结算的句柄 */
26
+ export interface XWheelHandle {
27
+ /**
28
+ * 按当前视觉位置返回选项(拖拽/spring 进行中也能拿到正确值)。
29
+ * antd 确认的是"看到的"值,不是"上次落位的"。
30
+ */
31
+ flush: () => XWheelOption | undefined;
32
+ }
33
+ interface XWheelProps {
34
+ /** 选项列表(顺序即列内顺序) */
35
+ items: XWheelOption[];
36
+ /** 当前选中值(受控,父组件传;不在列表中时夹到最近边界) */
37
+ value?: any;
38
+ /** 选中上报(松手/点选时触发;antd 的 onSelect) */
39
+ onSelect: (value: any) => void;
40
+ }
41
+ /**
42
+ * XWheel -- 单列滚轮。
43
+ *
44
+ * 唯一的动态值是 y(0 = 第一项停在选中位,负值向上滚)。
45
+ * 每帧只发生一件事:y 变化 -> 一个 useAnimatedStyle 重新求值 ->
46
+ * 容器整体 translateY。所有行是纯静态 View,滚动中零重渲染、零重绘。
47
+ */
48
+ export declare const XWheel: React.NamedExoticComponent<XWheelProps & React.RefAttributes<XWheelHandle>>;
49
+ export {};
@@ -0,0 +1,263 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ============================================================================
4
+ * XWheel -- 通用滚轮列(antd-mobile picker-view/wheel.tsx 架构的 RN 移植)
5
+ * ============================================================================
6
+ *
7
+ * 【被 XPickerDate(多列日期)与 XPicker(单列选项)共用】
8
+ *
9
+ * 架构(antd 的核心,丝滑的全部秘密):
10
+ * - 没有 ScrollView。"一个手势 + 一个平移值":Gesture.Pan 驱动单个 y 共享值,
11
+ * 整个行容器只是一个 View 整体 translateY。滚动中每帧只有 1 个 transform
12
+ * 更新,零逐行计算、零 JS 参与。
13
+ * - 所有行视觉完全一致(无逐行高亮)。选中表现完全来自遮罩层:
14
+ * 上下渐变把远处行淡出到背景色 + 中间两条 1px 框线。
15
+ * - 拖拽中越界走橡皮筋(rubberbandIfOutOfBounds,antd 原公式);
16
+ * 松手按"位置 + 速度投影"夹边界取整到行,withSpring 落位,
17
+ * 同时立即上报选中(不等动画结束,antd 行为)。
18
+ * ============================================================================
19
+ */
20
+ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
21
+ import { Pressable, StyleSheet, View, Text } from 'react-native';
22
+ import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
23
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
24
+ import LinearGradient from 'react-native-linear-gradient';
25
+ import { useXTheme } from '../theme';
26
+ /** 每行高度(antd: --item-height 34px) */
27
+ const ITEM_HEIGHT = 34;
28
+ /** 列可视高度(antd: --height 240px,约 7 行) */
29
+ const VISIBLE_HEIGHT = 240;
30
+ /** 把 #rrggbb 转成带透明度的 rgba(遮罩渐变用,避免写死白色背景) */
31
+ const hexToRgba = (hex, alpha) => {
32
+ const h = hex.replace('#', '');
33
+ const r = parseInt(h.slice(0, 2), 16);
34
+ const g = parseInt(h.slice(2, 4), 16);
35
+ const b = parseInt(h.slice(4, 6), 16);
36
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
37
+ };
38
+ /**
39
+ * 弹簧参数(antd: react-spring tension 400 / mass 0.8,换算到 Reanimated)。
40
+ * damping 50 ≈ 轻微过阻尼,落位干脆利落,与 antd 观感一致。
41
+ */
42
+ const SPRING_CONFIG = { mass: 0.8, stiffness: 400, damping: 50 };
43
+ /**
44
+ * 松手动量投影系数。antd: 位置 + velocity(px/ms) * 50;RNGH velocity 是
45
+ * px/s,等价换算为 velocity * 0.05(50ms 的动量投影,保守、不飘)。
46
+ */
47
+ const VELOCITY_PROJECTION = 0.05;
48
+ /** 橡皮筋参数(antd 原值:dimension = itemHeight*50, constant 0.2) */
49
+ const RUBBERBAND_EXTENT_FACTOR = 50;
50
+ const RUBBERBAND_CONSTANT = 0.2;
51
+ /** 手势激活阈值:垂直位移超过它才算拖拽,小于它视为点击(行内 Pressable 接管) */
52
+ const PAN_ACTIVE_OFFSET = 6;
53
+ /* ------------------------------------------------------------------ *
54
+ * antd utils/bound.ts 与 utils/rubberband.ts 的逐行移植 *
55
+ * ------------------------------------------------------------------ */
56
+ const bound = (position, min, max) => {
57
+ 'worklet';
58
+ return Math.min(Math.max(position, min), max);
59
+ };
60
+ const rubberband = (distance, dimension, constant) => {
61
+ 'worklet';
62
+ return (distance * dimension * constant) / (dimension + constant * distance);
63
+ };
64
+ /**
65
+ * 越界橡皮筋(antd 原公式):越界后阻力随距离增大,拖不出"一屏"。
66
+ * dimension = itemHeight * 50, constant = 0.2 与 antd 完全一致。
67
+ * 注意:被手势 worklet 调用,自身必须是 worklet('worklet' 指令不可省)。
68
+ */
69
+ const rubberbandIfOutOfBounds = (position, min, max, dimension) => {
70
+ 'worklet';
71
+ const c = RUBBERBAND_CONSTANT;
72
+ if (position < min) {
73
+ return -rubberband(min - position, dimension, c) + min;
74
+ }
75
+ if (position > max) {
76
+ return +rubberband(position - max, dimension, c) + max;
77
+ }
78
+ return position;
79
+ };
80
+ /**
81
+ * 把 value 换算成索引。
82
+ * 若 value 不在 items 里(如切到 2 月后,天列只剩 1~28,而 value 还是 31),
83
+ * 则夹到最近的边界:大于最大值 -> 最后一项,否则 -> 第一项。
84
+ */
85
+ const clampIndex = (items, value) => {
86
+ let index = items.findIndex(item => item.value === value);
87
+ if (index === -1) {
88
+ index = items.length ? (value > items[items.length - 1].value ? items.length - 1 : 0) : 0;
89
+ }
90
+ return Math.max(0, index);
91
+ };
92
+ /**
93
+ * XWheel -- 单列滚轮。
94
+ *
95
+ * 唯一的动态值是 y(0 = 第一项停在选中位,负值向上滚)。
96
+ * 每帧只发生一件事:y 变化 -> 一个 useAnimatedStyle 重新求值 ->
97
+ * 容器整体 translateY。所有行是纯静态 View,滚动中零重渲染、零重绘。
98
+ */
99
+ export const XWheel = React.memo(forwardRef(function XWheel({ items, value, onSelect }, ref) {
100
+ const t = useXTheme();
101
+ /** 受控 value 对应的目标索引 */
102
+ const targetIndex = clampIndex(items, value);
103
+ const listRef = useRef(items);
104
+ listRef.current = items;
105
+ const onSelectRef = useRef(onSelect);
106
+ onSelectRef.current = onSelect;
107
+ /** 滚轮位置(antd 的 y):0 ~ -(n-1)*ITEM_HEIGHT,负值向上 */
108
+ const y = useSharedValue(-targetIndex * ITEM_HEIGHT);
109
+ /** 拖拽进行中(同步受控更新用;antd 的 draggingRef) */
110
+ const isDragging = useSharedValue(false);
111
+ /** 手势起始时的 y(onUpdate 里从它累加 translationY) */
112
+ const startY = useSharedValue(0);
113
+ /** 最后已上报的值:去重,切断"上报 -> 父更新 -> 再同步"循环 */
114
+ const lastSynced = useSharedValue(value);
115
+ /** JS 侧选中上报(stable,供 worklet runOnJS 调用;antd 的 onSelect) */
116
+ const select = useCallback((v) => {
117
+ if (v !== undefined && v !== lastSynced.value) {
118
+ lastSynced.value = v;
119
+ onSelectRef.current?.(v);
120
+ }
121
+ }, [lastSynced]);
122
+ /** 滚到某行(antd 的 scrollSelect:spring 动画 + 立即选中) */
123
+ const scrollSelect = useCallback((index) => {
124
+ const list = listRef.current;
125
+ const i = Math.min(Math.max(index, 0), Math.max(list.length - 1, 0));
126
+ y.value = withSpring(-i * ITEM_HEIGHT, SPRING_CONFIG);
127
+ select(list[i]?.value);
128
+ }, [y, select]);
129
+ /**
130
+ * 手势(antd 的 useDrag + rubberband)。
131
+ * deps=[items]:数据变化(如天列 31->28)时重建闭包,min 实时正确 --
132
+ * 拖着列时外部数据变化,松手自动被新边界夹取。
133
+ */
134
+ const pan = useMemo(() => Gesture.Pan()
135
+ // 垂直位移超过阈值才算拖滚轮;更小的位移留给行内 Pressable(点选)
136
+ .activeOffsetY([-PAN_ACTIVE_OFFSET, PAN_ACTIVE_OFFSET])
137
+ .onStart(() => {
138
+ 'worklet';
139
+ isDragging.value = true;
140
+ startY.value = y.value;
141
+ })
142
+ .onUpdate(e => {
143
+ 'worklet';
144
+ if (!items.length) {
145
+ return;
146
+ }
147
+ const min = -((items.length - 1) * ITEM_HEIGHT);
148
+ y.value = rubberbandIfOutOfBounds(startY.value + e.translationY, min, 0, ITEM_HEIGHT * RUBBERBAND_EXTENT_FACTOR);
149
+ })
150
+ .onEnd(e => {
151
+ 'worklet';
152
+ isDragging.value = false;
153
+ if (!items.length) {
154
+ return;
155
+ }
156
+ const min = -((items.length - 1) * ITEM_HEIGHT);
157
+ // antd:位置 + 动量投影 -> 夹边界 -> 取整到行
158
+ const projected = y.value + e.velocityY * VELOCITY_PROJECTION;
159
+ const bounded = bound(projected, min, 0);
160
+ const target = -Math.round(bounded / ITEM_HEIGHT);
161
+ y.value = withSpring(-target * ITEM_HEIGHT, SPRING_CONFIG);
162
+ // 去重统一在 select 内部(不在此预写 lastSynced,
163
+ // 否则双重去重互相打架导致选中永远上报不出去)
164
+ const v = items[target]?.value;
165
+ if (v !== undefined) {
166
+ runOnJS(select)(v);
167
+ }
168
+ })
169
+ // 手势被打断(如弹层关闭):只复位拖拽标记,位置交给受控同步
170
+ .onFinalize(() => {
171
+ 'worklet';
172
+ isDragging.value = false;
173
+ }), [items, isDragging, startY, y, lastSynced, select]);
174
+ /**
175
+ * 受控同步(antd 的 useIsomorphicLayoutEffect):
176
+ * 外部 value / 数据源变化时对齐。拖拽中坚决不动 y(antd 同款防护)。
177
+ */
178
+ useEffect(() => {
179
+ lastSynced.value = value;
180
+ if (isDragging.value) {
181
+ return;
182
+ }
183
+ y.value = withSpring(-targetIndex * ITEM_HEIGHT, SPRING_CONFIG);
184
+ // eslint-disable-next-line react-hooks/exhaustive-deps
185
+ }, [targetIndex, items]);
186
+ /**
187
+ * flush:确定按钮按下时按当前视觉位置取选项。
188
+ * 拖拽/spring 进行中拿到的也是"看到的"值(antd 行为)。
189
+ */
190
+ useImperativeHandle(ref, () => ({
191
+ flush: () => {
192
+ const list = listRef.current;
193
+ const index = Math.min(Math.max(-Math.round(y.value / ITEM_HEIGHT), 0), Math.max(list.length - 1, 0));
194
+ return list[index];
195
+ },
196
+ }), []);
197
+ /** 整个行容器唯一的动画样式:每帧只重求值这一个 */
198
+ const wheelStyle = useAnimatedStyle(() => ({
199
+ transform: [{ translateY: y.value }],
200
+ }));
201
+ /** 点某一行:滚到它并直接选中(antd 的 handleClick -> scrollSelect) */
202
+ const handlePressItem = useCallback((index) => {
203
+ if (isDragging.value) {
204
+ return;
205
+ }
206
+ scrollSelect(index);
207
+ }, [isDragging, scrollSelect]);
208
+ return (_jsxs(View, { style: styles.column, children: [_jsx(GestureDetector, { gesture: pan, children: _jsx(View, { style: StyleSheet.absoluteFill, children: _jsx(Animated.View, { style: [styles.wheel, wheelStyle], children: items.map((item, index) => (_jsx(Pressable, { onPress: () => handlePressItem(index), style: styles.item, children: _jsx(Text, { allowFontScaling: false, style: [styles.itemText, { color: t.colorText }], children: item.label }) }, String(item.value)))) }) }) }), _jsx(LinearGradient, { colors: [t.colorBgContainer, hexToRgba(t.colorBgContainer, 0.6)], start: { x: 0, y: 0 }, end: { x: 0, y: 1 }, pointerEvents: 'none', style: styles.maskTop }), _jsx(LinearGradient, { colors: [hexToRgba(t.colorBgContainer, 0.6), t.colorBgContainer], start: { x: 0, y: 0 }, end: { x: 0, y: 1 }, pointerEvents: 'none', style: styles.maskBottom }), _jsx(View, { pointerEvents: 'none', style: [styles.maskMiddle, { borderColor: t.colorSplit }] })] }));
209
+ }));
210
+ const styles = StyleSheet.create({
211
+ /** 单列:240 高,相对定位,遮罩绝对定位盖在上面 */
212
+ column: {
213
+ height: VISIBLE_HEIGHT,
214
+ flex: 1,
215
+ overflow: 'hidden',
216
+ position: 'relative',
217
+ },
218
+ /**
219
+ * 行容器(antd 的 column-wheel):绝对定位在垂直中点,
220
+ * 唯一的动画是整体 translateY(y 为负值时向上滚)。
221
+ */
222
+ wheel: {
223
+ position: 'absolute',
224
+ top: (VISIBLE_HEIGHT - ITEM_HEIGHT) / 2,
225
+ left: 0,
226
+ right: 0,
227
+ },
228
+ /** 单行:纯静态样式(antd 的 column-item,高度 34、字色统一、无任何动画) */
229
+ item: {
230
+ height: ITEM_HEIGHT,
231
+ justifyContent: 'center',
232
+ alignItems: 'center',
233
+ },
234
+ itemText: {
235
+ fontSize: 16,
236
+ },
237
+ /** 上遮罩:远端全背景色 -> 靠中间半透明(antd mask-top 的渐变) */
238
+ maskTop: {
239
+ position: 'absolute',
240
+ top: 0,
241
+ left: 0,
242
+ right: 0,
243
+ height: (VISIBLE_HEIGHT - ITEM_HEIGHT) / 2,
244
+ },
245
+ /** 下遮罩:靠中间半透明 -> 远端全背景色(antd mask-bottom 的渐变) */
246
+ maskBottom: {
247
+ position: 'absolute',
248
+ bottom: 0,
249
+ left: 0,
250
+ right: 0,
251
+ height: (VISIBLE_HEIGHT - ITEM_HEIGHT) / 2,
252
+ },
253
+ /** 中间选中框:两条 1px 边框(antd mask-middle),背景透明不遮行 */
254
+ maskMiddle: {
255
+ position: 'absolute',
256
+ top: (VISIBLE_HEIGHT - ITEM_HEIGHT) / 2,
257
+ left: 0,
258
+ right: 0,
259
+ height: ITEM_HEIGHT,
260
+ borderTopWidth: StyleSheet.hairlineWidth,
261
+ borderBottomWidth: StyleSheet.hairlineWidth,
262
+ },
263
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * ============================================================================
3
+ * X-Components 统一出口(barrel)
4
+ * ============================================================================
5
+ *
6
+ * 所有 X 打头组件从这里导入:
7
+ * import {XButton, XForm, XRadio, XCheckbox, XProgress, XDivider} from '@/components/X-Components';
8
+ * import {XActionSheet, XPicker, XPickerDate, XConfirmForm, confirm} from '@/components/X-Components';
9
+ *
10
+ * 也可以从子目录直接导入具体组件。
11
+ *
12
+ * 【弹窗族组件关系图】(原 DuxPopup 整体迁入,组件名加 X 前缀)
13
+ * XPopupProvider(App 根部)
14
+ * ├── XConfirmForm(命令式确认框实例)
15
+ * └── XTopView(全局宿主:画所有弹层)
16
+ * │
17
+ * useXTopViewStore(zustand:add/update/remove)
18
+ * │
19
+ * ┌───────────┴───────────┐
20
+ * │ │
21
+ * XPullView XAnimatedView(通用动画引擎)
22
+ * (通用弹出层,核心) anim() 链式构建器
23
+ * │
24
+ * ┌───────────────┼────────────────┐
25
+ * │ │ │
26
+ * XActionSheet XPicker XPickerDate
27
+ * (底部菜单) (单列滚轮) (日期时间滚轮,format 定列)
28
+ * │ │
29
+ * └───────┬────────┘
30
+ * │
31
+ * XWheel(通用滚轮列)
32
+ * │
33
+ * XModalForm(居中弹层:确认/表单)
34
+ * │
35
+ * XConfirmForm(命令式封装:show() → Promise<boolean>)
36
+ *
37
+ * 【学习路径建议】
38
+ * 1. 先读 XPullView —— 整套架构的核心(TopView 挂载 + 入场/离场动画);
39
+ * 2. 再读 XTopViewStore / XTopView —— 全局宿主的原理;
40
+ * 3. 然后读 XActionSheet / XPicker / XModalForm —— 学会"组合复用"XPullView;
41
+ * 4. 最后读 XConfirmForm —— 命令式 API(Promise)的实现套路。
42
+ * ============================================================================
43
+ */
44
+ export { default as XButton } from './XButton';
45
+ export type { XButtonProps, XButtonType, XButtonSize, XButtonShape } from './XButton';
46
+ export { default as XDivider } from './XDivider';
47
+ export type { XDividerProps } from './XDivider';
48
+ export { default as XInput, XTextArea } from './XInput';
49
+ export type { XInputProps } from './XInput';
50
+ export { XImage } from './XImage';
51
+ export type { ImageWithLoaderProps as XImageProps } from './XImage';
52
+ export { default as XRadio } from './XRadio';
53
+ export type { XRadioProps, XRadioGroupProps, XRadioButtonProps, XRadioChangeEvent } from './XRadio';
54
+ export { default as XCheckbox } from './XCheckbox';
55
+ export type { XCheckboxProps, XCheckboxGroupProps, XCheckboxChangeEvent } from './XCheckbox';
56
+ export { default as XProgress } from './XProgress';
57
+ export type { XProgressProps } from './XProgress';
58
+ export { default as XTag } from './XTag';
59
+ export type { XTagProps, XTagType, XTagSize, XTagVariant } from './XTag';
60
+ export { default as XForm, useForm as XFormUseForm } from './XForm';
61
+ export type { XFormProps, XFormItemProps, XRule, XRuleObject, XNamePath, XFormInstance, XErrorEntity, XFieldData, XValidateErrorEntity, XValidateTrigger } from './XForm';
62
+ export { default as XCascadeSelect } from './XCascadeSelect';
63
+ export type { XCascadeSelectProps, XCascadeOption } from './XCascadeSelect';
64
+ export { default as XMultiSelect } from './XMultiSelect';
65
+ export type { XMultiSelectProps } from './XMultiSelect';
66
+ export { default as XFormPro } from './XFormPro';
67
+ export { registerXFormProRenderer } from './XFormPro';
68
+ export type { XFormModelValue, XOptionItem, XFormProRule, XBaseFormItemProps, XFormProItemProps, XFormItemOptions, XFormProProps, XFormProInst, XFm, XFormProCustomRenderer, } from './XFormPro';
69
+ export { default as XAnimatedSearchPanel } from './XAnimatedSearchPanel';
70
+ export type { XAnimatedSearchPanelProps } from './XAnimatedSearchPanel';
71
+ export * from './XAnimatedView';
72
+ export * from './XPullView';
73
+ export * from './XTopView';
74
+ export * from './XTopViewStore';
75
+ export * from './XActionSheet';
76
+ export * from './XWheel';
77
+ export * from './XPicker';
78
+ export * from './XPickerDate';
79
+ export * from './XModalForm';
80
+ export * from './XConfirmForm';
81
+ export * from './XPopupProvider';
82
+ export { XLoadingModal, XLoadingModalProvider, XLoadingModalService, XImagePreview, XImagePreviewProvider, XImagePreviewService, XToastProvider, XToastService, useXToast, } from './XProviders';
83
+ export type { XLoadingModalProps, XLoadingModalConfig, XImagePreviewParams, XToastOptions, XToastType, XToastPosition } from './XProviders';
84
+ export { xTheme } from './theme';
85
+ export { lightTokens, darkTokens, setXThemeMode, getXThemeMode, useXThemeMode, useXTheme, useXThemeScheme } from './theme';
86
+ export type { XTheme, XThemeMode, XThemeScheme } from './theme';
87
+ export { useXLocale, setXLocale, getXLocale, xT, xMonthTitle, X_WEEKDAYS } from './XLocale';
88
+ export type { XLocale, XLocaleKey } from './XLocale';
89
+ export { XCarousel } from './XCarousel';
90
+ export type { XCarouselProps, XCarouselItem } from './XCarousel';
91
+ export { XTabs, XTabPane } from './XTabs';
92
+ export type { XTabsProps, XTabPaneProps } from './XTabs';
93
+ export { XElevator } from './XElevator';
94
+ export type { XElevatorProps, XElevatorSection } from './XElevator';
95
+ export { default as XDropdownMenu, XDropdownMenuItem } from './XDropdownMenu';
96
+ export type { XDropdownMenuProps, XDropdownMenuItemProps, XMenuOption } from './XDropdownMenu';
97
+ export { XCalendar } from './XCalendar';
98
+ export type { XCalendarProps, XCalendarMode, XCalendarValue } from './XCalendar';
99
+ export { XCalendarPopup } from './XCalendar/XCalendarPopup';
100
+ export type { XCalendarPopupProps } from './XCalendar/XCalendarPopup';
101
+ export { XRecord } from './XRecord';
102
+ export type { XRecordProps, XRecordValue } from './XRecord';
103
+ export { XNumberKeyboard, XNumberKeyboardPopup } from './XNumberKeyboard';
104
+ export type { XNumberKeyboardProps, XNumberKeyboardPopupProps } from './XNumberKeyboard';
105
+ export { default as XLicensePlate, XLicensePlateKeyboard } from './XLicensePlate';
106
+ export type { XLicensePlateProps, XLicensePlateKeyboardProps, XLicensePlatePhase } from './XLicensePlate';
107
+ export { XLineChart, XBarChart, XPieChart } from './XChart';
108
+ export type { XLineChartProps, XBarChartProps, XBarChartItem, XPieChartProps, XPieChartItem } from './XChart';
109
+ export { XSignature } from './XSignature';
110
+ export type { XSignatureProps } from './XSignature';
111
+ export { XSignatureSkia } from './XSignature/XSignatureSkia';
112
+ export type { XSignatureSkiaProps, XSignatureSkiaValue, XSignatureCharacter, XSignatureStroke, } from './XSignature/XSignatureSkia';
113
+ export * from './XUpload';
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * ============================================================================
3
+ * X-Components 统一出口(barrel)
4
+ * ============================================================================
5
+ *
6
+ * 所有 X 打头组件从这里导入:
7
+ * import {XButton, XForm, XRadio, XCheckbox, XProgress, XDivider} from '@/components/X-Components';
8
+ * import {XActionSheet, XPicker, XPickerDate, XConfirmForm, confirm} from '@/components/X-Components';
9
+ *
10
+ * 也可以从子目录直接导入具体组件。
11
+ *
12
+ * 【弹窗族组件关系图】(原 DuxPopup 整体迁入,组件名加 X 前缀)
13
+ * XPopupProvider(App 根部)
14
+ * ├── XConfirmForm(命令式确认框实例)
15
+ * └── XTopView(全局宿主:画所有弹层)
16
+ * │
17
+ * useXTopViewStore(zustand:add/update/remove)
18
+ * │
19
+ * ┌───────────┴───────────┐
20
+ * │ │
21
+ * XPullView XAnimatedView(通用动画引擎)
22
+ * (通用弹出层,核心) anim() 链式构建器
23
+ * │
24
+ * ┌───────────────┼────────────────┐
25
+ * │ │ │
26
+ * XActionSheet XPicker XPickerDate
27
+ * (底部菜单) (单列滚轮) (日期时间滚轮,format 定列)
28
+ * │ │
29
+ * └───────┬────────┘
30
+ * │
31
+ * XWheel(通用滚轮列)
32
+ * │
33
+ * XModalForm(居中弹层:确认/表单)
34
+ * │
35
+ * XConfirmForm(命令式封装:show() → Promise<boolean>)
36
+ *
37
+ * 【学习路径建议】
38
+ * 1. 先读 XPullView —— 整套架构的核心(TopView 挂载 + 入场/离场动画);
39
+ * 2. 再读 XTopViewStore / XTopView —— 全局宿主的原理;
40
+ * 3. 然后读 XActionSheet / XPicker / XModalForm —— 学会"组合复用"XPullView;
41
+ * 4. 最后读 XConfirmForm —— 命令式 API(Promise)的实现套路。
42
+ * ============================================================================
43
+ */
44
+ // ---------------------------------------------------------------------------
45
+ // 基础组件(对标 antd)
46
+ // ---------------------------------------------------------------------------
47
+ export { default as XButton } from './XButton';
48
+ export { default as XDivider } from './XDivider';
49
+ export { default as XInput, XTextArea } from './XInput';
50
+ export { XImage } from './XImage';
51
+ export { default as XRadio } from './XRadio';
52
+ export { default as XCheckbox } from './XCheckbox';
53
+ export { default as XProgress } from './XProgress';
54
+ export { default as XTag } from './XTag';
55
+ export { default as XForm, useForm as XFormUseForm } from './XForm';
56
+ export { default as XCascadeSelect } from './XCascadeSelect';
57
+ export { default as XMultiSelect } from './XMultiSelect';
58
+ export { default as XFormPro } from './XFormPro';
59
+ export { registerXFormProRenderer } from './XFormPro';
60
+ // ---------------------------------------------------------------------------
61
+ // 复合组件(列表头折叠搜索面板,原 components/AnimatedSearchPanel 迁入)
62
+ // ---------------------------------------------------------------------------
63
+ export { default as XAnimatedSearchPanel } from './XAnimatedSearchPanel';
64
+ // ---------------------------------------------------------------------------
65
+ // 弹窗族(原 DuxPopup 迁入)
66
+ // ---------------------------------------------------------------------------
67
+ export * from './XAnimatedView';
68
+ export * from './XPullView';
69
+ export * from './XTopView';
70
+ export * from './XTopViewStore';
71
+ export * from './XActionSheet';
72
+ export * from './XWheel';
73
+ export * from './XPicker';
74
+ export * from './XPickerDate';
75
+ export * from './XModalForm';
76
+ export * from './XConfirmForm';
77
+ export * from './XPopupProvider';
78
+ // ---------------------------------------------------------------------------
79
+ // 命令式全局组件(Provider 类:Loading / 图片预览 / Toast,见 XProviders/usageExample.tsx)
80
+ // ---------------------------------------------------------------------------
81
+ export { XLoadingModal, XLoadingModalProvider, XLoadingModalService, XImagePreview, XImagePreviewProvider, XImagePreviewService, XToastProvider, XToastService, useXToast, } from './XProviders';
82
+ export { xTheme } from './theme';
83
+ export { lightTokens, darkTokens, setXThemeMode, getXThemeMode, useXThemeMode, useXTheme, useXThemeScheme } from './theme';
84
+ // ---------------------------------------------------------------------------
85
+ // 国际化(中英)
86
+ // ---------------------------------------------------------------------------
87
+ export { useXLocale, setXLocale, getXLocale, xT, xMonthTitle, X_WEEKDAYS } from './XLocale';
88
+ // ---------------------------------------------------------------------------
89
+ // 轮播图 / 选项卡 / 电梯楼层
90
+ // ---------------------------------------------------------------------------
91
+ export { XCarousel } from './XCarousel';
92
+ export { XTabs, XTabPane } from './XTabs';
93
+ export { XElevator } from './XElevator';
94
+ // ---------------------------------------------------------------------------
95
+ // 下拉菜单
96
+ // ---------------------------------------------------------------------------
97
+ export { default as XDropdownMenu, XDropdownMenuItem } from './XDropdownMenu';
98
+ // ---------------------------------------------------------------------------
99
+ // 日历 / 弹窗日历
100
+ // ---------------------------------------------------------------------------
101
+ export { XCalendar } from './XCalendar';
102
+ export { XCalendarPopup } from './XCalendar/XCalendarPopup';
103
+ // ---------------------------------------------------------------------------
104
+ // 录音
105
+ // ---------------------------------------------------------------------------
106
+ export { XRecord } from './XRecord';
107
+ // ---------------------------------------------------------------------------
108
+ // 数字键盘(含弹出模式)
109
+ // ---------------------------------------------------------------------------
110
+ export { XNumberKeyboard, XNumberKeyboardPopup } from './XNumberKeyboard';
111
+ // ---------------------------------------------------------------------------
112
+ // 车牌号输入
113
+ // ---------------------------------------------------------------------------
114
+ export { default as XLicensePlate, XLicensePlateKeyboard } from './XLicensePlate';
115
+ // ---------------------------------------------------------------------------
116
+ // 图表(SVG 轻量三件套)
117
+ // ---------------------------------------------------------------------------
118
+ export { XLineChart, XBarChart, XPieChart } from './XChart';
119
+ // ---------------------------------------------------------------------------
120
+ // 签名(普通 + Skia 逐字)
121
+ // ---------------------------------------------------------------------------
122
+ export { XSignature } from './XSignature';
123
+ export { XSignatureSkia } from './XSignature/XSignatureSkia';
124
+ // ---------------------------------------------------------------------------
125
+ // 上传(适配器模式:普通 multipart + MinIO 预签名直传)
126
+ // ---------------------------------------------------------------------------
127
+ export * from './XUpload';