@particle-network/ui-native 0.4.2-beta.8 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/UXButton/button.styles.js +8 -3
- package/dist/components/UXButton/button.types.d.ts +5 -3
- package/dist/components/UXSlider/constants.d.ts +7 -0
- package/dist/components/UXSlider/constants.js +8 -0
- package/dist/components/UXSlider/index.d.ts +1 -0
- package/dist/components/UXSlider/index.js +1 -0
- package/dist/components/UXSlider/slider.d.ts +3 -0
- package/dist/components/UXSlider/slider.js +284 -0
- package/dist/components/UXSlider/slider.styles.d.ts +92 -0
- package/dist/components/UXSlider/slider.styles.js +100 -0
- package/dist/components/UXSlider/slider.types.d.ts +30 -0
- package/dist/components/UXSlider/slider.types.js +0 -0
- package/dist/components/UXTabSwitch/index.d.ts +1 -6
- package/dist/components/UXTabSwitch/index.js +2 -6
- package/dist/components/UXTabs/styles.d.ts +2 -2
- package/dist/components/UXTabs/styles.js +8 -9
- package/dist/components/date-picker/date-picker.js +1 -1
- package/dist/components/date-picker/date-range-picker.js +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/input/input.js +16 -4
- package/dist/components/input/number-input.js +44 -34
- package/dist/config/config.default.js +4 -2
- package/dist/config/config.street.js +4 -2
- package/dist/locales/en.d.ts +5 -0
- package/dist/locales/en.js +5 -0
- package/dist/locales/index.d.ts +15 -0
- package/dist/locales/zh.js +5 -0
- package/dist/types/theme.d.ts +2 -0
- package/package.json +15 -14
|
@@ -14,6 +14,7 @@ const useStyles = (props)=>{
|
|
|
14
14
|
sm: ms(buttonConfig.size.sm),
|
|
15
15
|
md: ms(buttonConfig.size.md),
|
|
16
16
|
lg: ms(buttonConfig.size.lg),
|
|
17
|
+
xl: ms(buttonConfig.size.xl),
|
|
17
18
|
auto: void 0
|
|
18
19
|
}), [
|
|
19
20
|
buttonConfig?.size,
|
|
@@ -24,6 +25,7 @@ const useStyles = (props)=>{
|
|
|
24
25
|
sm: ms(buttonConfig.fontSize.sm),
|
|
25
26
|
md: ms(buttonConfig.fontSize.md),
|
|
26
27
|
lg: ms(buttonConfig.fontSize.lg),
|
|
28
|
+
xl: ms(buttonConfig.fontSize.xl),
|
|
27
29
|
auto: ms(buttonConfig.fontSize.md)
|
|
28
30
|
}), [
|
|
29
31
|
ms,
|
|
@@ -33,7 +35,8 @@ const useStyles = (props)=>{
|
|
|
33
35
|
xs: ms(14),
|
|
34
36
|
sm: ms(16),
|
|
35
37
|
md: ms(18),
|
|
36
|
-
lg: ms(
|
|
38
|
+
lg: ms(20),
|
|
39
|
+
xl: ms(24),
|
|
37
40
|
auto: ms(16)
|
|
38
41
|
}), [
|
|
39
42
|
ms
|
|
@@ -42,14 +45,16 @@ const useStyles = (props)=>{
|
|
|
42
45
|
xs: 'xs',
|
|
43
46
|
sm: 'sm',
|
|
44
47
|
md: 'sm',
|
|
45
|
-
lg:
|
|
48
|
+
lg: 8,
|
|
49
|
+
xl: 'md',
|
|
46
50
|
auto: 'sm'
|
|
47
51
|
};
|
|
48
52
|
const paddingMap = useMemo(()=>({
|
|
49
53
|
xs: ms(10),
|
|
50
54
|
sm: ms(14),
|
|
51
55
|
md: ms(14),
|
|
52
|
-
lg: ms(14)
|
|
56
|
+
lg: ms(14),
|
|
57
|
+
xl: ms(14)
|
|
53
58
|
}), [
|
|
54
59
|
ms
|
|
55
60
|
]);
|
|
@@ -13,7 +13,8 @@ export interface UXButtonProps extends Omit<UXPressableProps, 'style' | 'disable
|
|
|
13
13
|
* | xs | 20 | 36 |
|
|
14
14
|
* | sm | 24 | 40 |
|
|
15
15
|
* | md | 30 | 44 |
|
|
16
|
-
* | lg |
|
|
16
|
+
* | lg | 36 | 46 |
|
|
17
|
+
* | xl | 44 | 48 |
|
|
17
18
|
*
|
|
18
19
|
* fontSize
|
|
19
20
|
* | size | ux | street |
|
|
@@ -21,9 +22,10 @@ export interface UXButtonProps extends Omit<UXPressableProps, 'style' | 'disable
|
|
|
21
22
|
* | xs | 11 | 12 |
|
|
22
23
|
* | sm | 10 | 14 |
|
|
23
24
|
* | md | 12 | 16 |
|
|
24
|
-
* | lg |
|
|
25
|
+
* | lg | 14 | 16 |
|
|
26
|
+
* | xl | 16 | 18 |
|
|
25
27
|
*/
|
|
26
|
-
size?: 'xs' | 'sm' | 'md' | 'lg' | 'auto';
|
|
28
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'auto';
|
|
27
29
|
variant?: 'solid' | 'bordered' | 'flat' | 'text' | 'light';
|
|
28
30
|
radius?: UXRadius;
|
|
29
31
|
isDisabled?: boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const THUMB_SIZE = 12;
|
|
2
|
+
export declare const THUMB_INNER_SIZE = 8;
|
|
3
|
+
export declare const TRACK_HEIGHT = 2;
|
|
4
|
+
export declare const MARK_SIZE = 4;
|
|
5
|
+
export declare const TOOLTIP_ARROW_SIZE = 6;
|
|
6
|
+
export declare const TOOLTIP_MARGIN_BOTTOM = 4;
|
|
7
|
+
export declare const LABEL_WIDTH = 40;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const THUMB_SIZE = 12;
|
|
2
|
+
const THUMB_INNER_SIZE = 8;
|
|
3
|
+
const TRACK_HEIGHT = 2;
|
|
4
|
+
const MARK_SIZE = 4;
|
|
5
|
+
const TOOLTIP_ARROW_SIZE = 6;
|
|
6
|
+
const TOOLTIP_MARGIN_BOTTOM = 4;
|
|
7
|
+
const LABEL_WIDTH = 40;
|
|
8
|
+
export { LABEL_WIDTH, MARK_SIZE, THUMB_INNER_SIZE, THUMB_SIZE, TOOLTIP_ARROW_SIZE, TOOLTIP_MARGIN_BOTTOM, TRACK_HEIGHT };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './slider';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./slider.js";
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
5
|
+
import react_native_reanimated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
|
6
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
7
|
+
import { useTheme } from "../../hooks/index.js";
|
|
8
|
+
import { Box } from "../layout/index.js";
|
|
9
|
+
import { Text } from "../Text/index.js";
|
|
10
|
+
import { UXPressable } from "../UXPressable/index.js";
|
|
11
|
+
import { LABEL_WIDTH, MARK_SIZE, THUMB_SIZE } from "./constants.js";
|
|
12
|
+
import { useStyles } from "./slider.styles.js";
|
|
13
|
+
const UXSlider = ({ color = 'primary', minValue = 0, maxValue = 100, defaultValue, value: controlledValue, onChange, onChangeEnd, fillOffset, marks, showMarksSteps = true, step, showTooltip, getValue, ...restProps })=>{
|
|
14
|
+
const { colors } = useTheme();
|
|
15
|
+
const [trackWidth, setTrackWidth] = useState(0);
|
|
16
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? minValue);
|
|
17
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
18
|
+
const [tooltipWidth, setTooltipWidth] = useState(0);
|
|
19
|
+
const styles = useStyles({
|
|
20
|
+
color
|
|
21
|
+
});
|
|
22
|
+
let currentValue = void 0 !== controlledValue ? Math.max(minValue, Math.min(maxValue, controlledValue)) : internalValue;
|
|
23
|
+
if (!Number.isFinite(currentValue)) currentValue = fillOffset ?? minValue;
|
|
24
|
+
const thumbX = useSharedValue(0);
|
|
25
|
+
const trackWidthShared = useSharedValue(0);
|
|
26
|
+
const valueToPosition = useCallback((val)=>{
|
|
27
|
+
if (0 === trackWidth) return THUMB_SIZE / 2;
|
|
28
|
+
const usableWidth = trackWidth - THUMB_SIZE;
|
|
29
|
+
return THUMB_SIZE / 2 + (val - minValue) / (maxValue - minValue) * usableWidth;
|
|
30
|
+
}, [
|
|
31
|
+
trackWidth,
|
|
32
|
+
minValue,
|
|
33
|
+
maxValue
|
|
34
|
+
]);
|
|
35
|
+
const valueToMarkThumbPosition = useCallback((val)=>{
|
|
36
|
+
if (0 === trackWidth) return MARK_SIZE / 2;
|
|
37
|
+
const usableWidth = trackWidth - MARK_SIZE;
|
|
38
|
+
return MARK_SIZE / 2 + (val - minValue) / (maxValue - minValue) * usableWidth;
|
|
39
|
+
}, [
|
|
40
|
+
trackWidth,
|
|
41
|
+
minValue,
|
|
42
|
+
maxValue
|
|
43
|
+
]);
|
|
44
|
+
const positionToValueWorklet = (pos, width)=>{
|
|
45
|
+
'worklet';
|
|
46
|
+
if (0 === width) return minValue;
|
|
47
|
+
const usableWidth = width - THUMB_SIZE;
|
|
48
|
+
if (usableWidth <= 0) return minValue;
|
|
49
|
+
const adjustedPos = pos - THUMB_SIZE / 2;
|
|
50
|
+
const percentage = Math.max(0, Math.min(1, adjustedPos / usableWidth));
|
|
51
|
+
let val = minValue + percentage * (maxValue - minValue);
|
|
52
|
+
if (step && step > 0) {
|
|
53
|
+
val = Math.round(val / step) * step;
|
|
54
|
+
const decimals = (step.toString().split('.')[1] || '').length;
|
|
55
|
+
val = Number(val.toFixed(decimals));
|
|
56
|
+
}
|
|
57
|
+
return Math.max(minValue, Math.min(maxValue, val));
|
|
58
|
+
};
|
|
59
|
+
useEffect(()=>{
|
|
60
|
+
thumbX.value = valueToPosition(currentValue);
|
|
61
|
+
}, [
|
|
62
|
+
currentValue,
|
|
63
|
+
valueToPosition,
|
|
64
|
+
thumbX
|
|
65
|
+
]);
|
|
66
|
+
useEffect(()=>{
|
|
67
|
+
trackWidthShared.value = trackWidth;
|
|
68
|
+
}, [
|
|
69
|
+
trackWidth,
|
|
70
|
+
trackWidthShared
|
|
71
|
+
]);
|
|
72
|
+
const setDraggingTrue = useCallback(()=>setIsDragging(true), []);
|
|
73
|
+
const setDraggingFalse = useCallback(()=>setIsDragging(false), []);
|
|
74
|
+
const handleValueChange = useCallback((newValue)=>{
|
|
75
|
+
if (void 0 === controlledValue) setInternalValue(newValue);
|
|
76
|
+
onChange?.(newValue);
|
|
77
|
+
}, [
|
|
78
|
+
controlledValue,
|
|
79
|
+
onChange
|
|
80
|
+
]);
|
|
81
|
+
const handleChangeEnd = useCallback((finalValue)=>{
|
|
82
|
+
onChangeEnd?.(finalValue);
|
|
83
|
+
}, [
|
|
84
|
+
onChangeEnd
|
|
85
|
+
]);
|
|
86
|
+
const panGesture = Gesture.Pan().activeOffsetX([
|
|
87
|
+
-8,
|
|
88
|
+
8
|
|
89
|
+
]).failOffsetY([
|
|
90
|
+
-16,
|
|
91
|
+
16
|
|
92
|
+
]).onStart((event)=>{
|
|
93
|
+
'worklet';
|
|
94
|
+
scheduleOnRN(setDraggingTrue);
|
|
95
|
+
const minPos = THUMB_SIZE / 2;
|
|
96
|
+
const maxPos = trackWidthShared.value - THUMB_SIZE / 2;
|
|
97
|
+
const touchX = Math.max(minPos, Math.min(event.x, maxPos));
|
|
98
|
+
thumbX.value = touchX;
|
|
99
|
+
}).onUpdate((event)=>{
|
|
100
|
+
'worklet';
|
|
101
|
+
const minPos = THUMB_SIZE / 2;
|
|
102
|
+
const maxPos = trackWidthShared.value - THUMB_SIZE / 2;
|
|
103
|
+
const touchX = Math.max(minPos, Math.min(event.x, maxPos));
|
|
104
|
+
thumbX.value = touchX;
|
|
105
|
+
const newValue = positionToValueWorklet(touchX, trackWidthShared.value);
|
|
106
|
+
scheduleOnRN(handleValueChange, newValue);
|
|
107
|
+
}).onEnd(()=>{
|
|
108
|
+
'worklet';
|
|
109
|
+
scheduleOnRN(setDraggingFalse);
|
|
110
|
+
const finalValue = positionToValueWorklet(thumbX.value, trackWidthShared.value);
|
|
111
|
+
scheduleOnRN(handleChangeEnd, finalValue);
|
|
112
|
+
});
|
|
113
|
+
const tapGesture = Gesture.Tap().onEnd((event)=>{
|
|
114
|
+
'worklet';
|
|
115
|
+
if (event.y > THUMB_SIZE) return;
|
|
116
|
+
const minPos = THUMB_SIZE / 2;
|
|
117
|
+
const maxPos = trackWidthShared.value - THUMB_SIZE / 2;
|
|
118
|
+
const touchX = Math.max(minPos, Math.min(event.x, maxPos));
|
|
119
|
+
thumbX.value = touchX;
|
|
120
|
+
const newValue = positionToValueWorklet(touchX, trackWidthShared.value);
|
|
121
|
+
scheduleOnRN(handleValueChange, newValue);
|
|
122
|
+
scheduleOnRN(handleChangeEnd, newValue);
|
|
123
|
+
});
|
|
124
|
+
const gesture = Gesture.Simultaneous(panGesture, tapGesture);
|
|
125
|
+
const labelBlockerGesture = Gesture.Native();
|
|
126
|
+
const thumbAnimatedStyle = useAnimatedStyle(()=>({
|
|
127
|
+
transform: [
|
|
128
|
+
{
|
|
129
|
+
translateX: thumbX.value - THUMB_SIZE / 2
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}));
|
|
133
|
+
const fillAnimatedStyle = useAnimatedStyle(()=>{
|
|
134
|
+
if (void 0 !== fillOffset) {
|
|
135
|
+
const offsetPos = (fillOffset - minValue) / (maxValue - minValue) * trackWidthShared.value;
|
|
136
|
+
const currentPos = thumbX.value;
|
|
137
|
+
return currentPos >= offsetPos ? {
|
|
138
|
+
left: offsetPos,
|
|
139
|
+
width: currentPos - offsetPos
|
|
140
|
+
} : {
|
|
141
|
+
left: currentPos,
|
|
142
|
+
width: offsetPos - currentPos
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
left: 0,
|
|
147
|
+
width: thumbX.value
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
const tooltipAnimatedStyle = useAnimatedStyle(()=>{
|
|
151
|
+
const left = thumbX.value - tooltipWidth / 2;
|
|
152
|
+
const clampedLeft = Math.max(0, Math.min(trackWidthShared.value - tooltipWidth, left));
|
|
153
|
+
return {
|
|
154
|
+
transform: [
|
|
155
|
+
{
|
|
156
|
+
translateX: clampedLeft
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
const getTooltipText = useCallback(()=>{
|
|
162
|
+
if (getValue) return getValue(currentValue);
|
|
163
|
+
return currentValue.toString();
|
|
164
|
+
}, [
|
|
165
|
+
currentValue,
|
|
166
|
+
getValue
|
|
167
|
+
]);
|
|
168
|
+
const handleTrackLayout = (event)=>setTrackWidth(event.nativeEvent.layout.width);
|
|
169
|
+
const handleTooltipLayout = (event)=>setTooltipWidth(event.nativeEvent.layout.width);
|
|
170
|
+
const handleMarkLabelPress = useCallback((markValue)=>{
|
|
171
|
+
setIsDragging(false);
|
|
172
|
+
thumbX.value = valueToPosition(markValue);
|
|
173
|
+
handleValueChange(markValue);
|
|
174
|
+
handleChangeEnd(markValue);
|
|
175
|
+
}, [
|
|
176
|
+
thumbX,
|
|
177
|
+
valueToPosition,
|
|
178
|
+
handleValueChange,
|
|
179
|
+
handleChangeEnd
|
|
180
|
+
]);
|
|
181
|
+
return /*#__PURE__*/ jsx(Box, {
|
|
182
|
+
fullWidth: true,
|
|
183
|
+
ph: marks && marks.length > 0 && marks.some((mark)=>mark.value === maxValue) ? LABEL_WIDTH / 2 : 0,
|
|
184
|
+
...restProps,
|
|
185
|
+
children: /*#__PURE__*/ jsx(GestureDetector, {
|
|
186
|
+
gesture: gesture,
|
|
187
|
+
children: /*#__PURE__*/ jsxs(react_native_reanimated.View, {
|
|
188
|
+
style: styles.gestureWrapper,
|
|
189
|
+
children: [
|
|
190
|
+
/*#__PURE__*/ jsxs(View, {
|
|
191
|
+
style: styles.trackContainer,
|
|
192
|
+
onLayout: handleTrackLayout,
|
|
193
|
+
children: [
|
|
194
|
+
showTooltip && isDragging && /*#__PURE__*/ jsx(react_native_reanimated.View, {
|
|
195
|
+
style: [
|
|
196
|
+
styles.tooltipWrapper,
|
|
197
|
+
tooltipAnimatedStyle
|
|
198
|
+
],
|
|
199
|
+
pointerEvents: "none",
|
|
200
|
+
onLayout: handleTooltipLayout,
|
|
201
|
+
children: /*#__PURE__*/ jsxs(View, {
|
|
202
|
+
style: styles.tooltip,
|
|
203
|
+
children: [
|
|
204
|
+
/*#__PURE__*/ jsx(Text, {
|
|
205
|
+
color: "white",
|
|
206
|
+
children: getTooltipText()
|
|
207
|
+
}),
|
|
208
|
+
/*#__PURE__*/ jsx(View, {
|
|
209
|
+
style: styles.tooltipArrow
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
})
|
|
213
|
+
}),
|
|
214
|
+
/*#__PURE__*/ jsx(Box, {
|
|
215
|
+
style: styles.track
|
|
216
|
+
}),
|
|
217
|
+
/*#__PURE__*/ jsx(react_native_reanimated.View, {
|
|
218
|
+
style: [
|
|
219
|
+
styles.fill,
|
|
220
|
+
fillAnimatedStyle
|
|
221
|
+
]
|
|
222
|
+
}),
|
|
223
|
+
showMarksSteps && marks?.map((mark, index)=>{
|
|
224
|
+
const markPos = valueToMarkThumbPosition(mark.value);
|
|
225
|
+
const isActive = void 0 !== fillOffset ? currentValue >= fillOffset && mark.value <= currentValue && mark.value >= fillOffset || currentValue < fillOffset && mark.value >= currentValue && mark.value <= fillOffset : mark.value <= currentValue;
|
|
226
|
+
return /*#__PURE__*/ jsx(View, {
|
|
227
|
+
style: [
|
|
228
|
+
styles.mark,
|
|
229
|
+
{
|
|
230
|
+
left: markPos - MARK_SIZE / 2,
|
|
231
|
+
backgroundColor: isActive ? colors[color] : colors.tertiary
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
}, index);
|
|
235
|
+
}),
|
|
236
|
+
/*#__PURE__*/ jsx(react_native_reanimated.View, {
|
|
237
|
+
style: [
|
|
238
|
+
styles.thumb,
|
|
239
|
+
thumbAnimatedStyle
|
|
240
|
+
]
|
|
241
|
+
})
|
|
242
|
+
]
|
|
243
|
+
}),
|
|
244
|
+
marks && marks.length > 0 && /*#__PURE__*/ jsx(GestureDetector, {
|
|
245
|
+
gesture: labelBlockerGesture,
|
|
246
|
+
children: /*#__PURE__*/ jsx(View, {
|
|
247
|
+
style: styles.labelsContainer,
|
|
248
|
+
children: marks.map((mark, index)=>{
|
|
249
|
+
const isActive = void 0 !== fillOffset ? currentValue >= fillOffset && mark.value <= currentValue && mark.value >= fillOffset || currentValue < fillOffset && mark.value >= currentValue && mark.value <= fillOffset : mark.value <= currentValue;
|
|
250
|
+
return /*#__PURE__*/ jsx(UXPressable, {
|
|
251
|
+
center: true,
|
|
252
|
+
style: [
|
|
253
|
+
styles.markLabelWrapper,
|
|
254
|
+
{
|
|
255
|
+
left: valueToMarkThumbPosition(mark.value) - LABEL_WIDTH / 2
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
hitSlop: {
|
|
259
|
+
top: 8,
|
|
260
|
+
bottom: 8,
|
|
261
|
+
left: 8,
|
|
262
|
+
right: 8
|
|
263
|
+
},
|
|
264
|
+
onPress: ()=>handleMarkLabelPress(mark.value),
|
|
265
|
+
children: /*#__PURE__*/ jsx(Text, {
|
|
266
|
+
caption1: true,
|
|
267
|
+
style: [
|
|
268
|
+
{
|
|
269
|
+
color: isActive ? colors.foreground : colors.secondary
|
|
270
|
+
}
|
|
271
|
+
],
|
|
272
|
+
numberOfLines: 1,
|
|
273
|
+
children: mark.label
|
|
274
|
+
})
|
|
275
|
+
}, index);
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
]
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
export { UXSlider };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { UXSliderProps } from './slider.types';
|
|
2
|
+
export declare const useStyles: ({ color }: UXSliderProps) => {
|
|
3
|
+
gestureWrapper: {
|
|
4
|
+
width: "100%";
|
|
5
|
+
};
|
|
6
|
+
trackContainer: {
|
|
7
|
+
width: "100%";
|
|
8
|
+
justifyContent: "center";
|
|
9
|
+
position: "relative";
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
track: {
|
|
13
|
+
width: "100%";
|
|
14
|
+
borderRadius: number;
|
|
15
|
+
position: "absolute";
|
|
16
|
+
backgroundColor: string;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
fill: {
|
|
20
|
+
position: "absolute";
|
|
21
|
+
borderRadius: number;
|
|
22
|
+
backgroundColor: string;
|
|
23
|
+
height: number;
|
|
24
|
+
};
|
|
25
|
+
mark: {
|
|
26
|
+
position: "absolute";
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
top: "50%";
|
|
30
|
+
marginTop: number;
|
|
31
|
+
borderRadius: number;
|
|
32
|
+
};
|
|
33
|
+
thumb: {
|
|
34
|
+
alignItems: "center";
|
|
35
|
+
justifyContent: "center";
|
|
36
|
+
position: "absolute";
|
|
37
|
+
top: "50%";
|
|
38
|
+
marginTop: number;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
borderRadius: number;
|
|
42
|
+
shadowColor: string;
|
|
43
|
+
shadowOffset: {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
};
|
|
47
|
+
shadowOpacity: number;
|
|
48
|
+
shadowRadius: number;
|
|
49
|
+
elevation: number;
|
|
50
|
+
backgroundColor: string;
|
|
51
|
+
};
|
|
52
|
+
labelsContainer: {
|
|
53
|
+
width: "100%";
|
|
54
|
+
height: number;
|
|
55
|
+
marginTop: number;
|
|
56
|
+
position: "relative";
|
|
57
|
+
};
|
|
58
|
+
markLabelWrapper: {
|
|
59
|
+
position: "absolute";
|
|
60
|
+
width: number;
|
|
61
|
+
overflow: "visible";
|
|
62
|
+
};
|
|
63
|
+
tooltipWrapper: {
|
|
64
|
+
position: "absolute";
|
|
65
|
+
left: number;
|
|
66
|
+
top: number;
|
|
67
|
+
justifyContent: "center";
|
|
68
|
+
alignItems: "center";
|
|
69
|
+
zIndex: number;
|
|
70
|
+
};
|
|
71
|
+
tooltip: {
|
|
72
|
+
position: "absolute";
|
|
73
|
+
paddingHorizontal: number;
|
|
74
|
+
paddingVertical: number;
|
|
75
|
+
borderRadius: number;
|
|
76
|
+
alignItems: "center";
|
|
77
|
+
backgroundColor: string;
|
|
78
|
+
bottom: number;
|
|
79
|
+
};
|
|
80
|
+
tooltipArrow: {
|
|
81
|
+
position: "absolute";
|
|
82
|
+
bottom: number;
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
borderLeftWidth: number;
|
|
86
|
+
borderRightWidth: number;
|
|
87
|
+
borderTopWidth: number;
|
|
88
|
+
borderLeftColor: string;
|
|
89
|
+
borderRightColor: string;
|
|
90
|
+
borderTopColor: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { useTheme } from "../../hooks/index.js";
|
|
3
|
+
import { LABEL_WIDTH, MARK_SIZE, THUMB_SIZE, TOOLTIP_ARROW_SIZE, TOOLTIP_MARGIN_BOTTOM, TRACK_HEIGHT } from "./constants.js";
|
|
4
|
+
const useStyles = ({ color })=>{
|
|
5
|
+
const { colors } = useTheme();
|
|
6
|
+
const colorValue = colors[color];
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
gestureWrapper: {
|
|
9
|
+
width: '100%'
|
|
10
|
+
},
|
|
11
|
+
trackContainer: {
|
|
12
|
+
width: '100%',
|
|
13
|
+
justifyContent: 'center',
|
|
14
|
+
position: 'relative',
|
|
15
|
+
height: THUMB_SIZE
|
|
16
|
+
},
|
|
17
|
+
track: {
|
|
18
|
+
width: '100%',
|
|
19
|
+
borderRadius: TRACK_HEIGHT / 2,
|
|
20
|
+
position: 'absolute',
|
|
21
|
+
backgroundColor: colors['bg-200'],
|
|
22
|
+
height: TRACK_HEIGHT
|
|
23
|
+
},
|
|
24
|
+
fill: {
|
|
25
|
+
position: 'absolute',
|
|
26
|
+
borderRadius: TRACK_HEIGHT / 2,
|
|
27
|
+
backgroundColor: colorValue,
|
|
28
|
+
height: TRACK_HEIGHT
|
|
29
|
+
},
|
|
30
|
+
mark: {
|
|
31
|
+
position: 'absolute',
|
|
32
|
+
width: MARK_SIZE,
|
|
33
|
+
height: MARK_SIZE,
|
|
34
|
+
top: '50%',
|
|
35
|
+
marginTop: -MARK_SIZE / 2,
|
|
36
|
+
borderRadius: 999
|
|
37
|
+
},
|
|
38
|
+
thumb: {
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
justifyContent: 'center',
|
|
41
|
+
position: 'absolute',
|
|
42
|
+
top: '50%',
|
|
43
|
+
marginTop: -THUMB_SIZE / 2,
|
|
44
|
+
width: THUMB_SIZE,
|
|
45
|
+
height: THUMB_SIZE,
|
|
46
|
+
borderRadius: 999,
|
|
47
|
+
shadowColor: '#000',
|
|
48
|
+
shadowOffset: {
|
|
49
|
+
width: 0,
|
|
50
|
+
height: 2
|
|
51
|
+
},
|
|
52
|
+
shadowOpacity: 0.1,
|
|
53
|
+
shadowRadius: 3,
|
|
54
|
+
elevation: 3,
|
|
55
|
+
backgroundColor: colorValue
|
|
56
|
+
},
|
|
57
|
+
labelsContainer: {
|
|
58
|
+
width: '100%',
|
|
59
|
+
height: 14,
|
|
60
|
+
marginTop: 6,
|
|
61
|
+
position: 'relative'
|
|
62
|
+
},
|
|
63
|
+
markLabelWrapper: {
|
|
64
|
+
position: 'absolute',
|
|
65
|
+
width: LABEL_WIDTH,
|
|
66
|
+
overflow: 'visible'
|
|
67
|
+
},
|
|
68
|
+
tooltipWrapper: {
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
left: 0,
|
|
71
|
+
top: 0,
|
|
72
|
+
justifyContent: 'center',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
zIndex: 100
|
|
75
|
+
},
|
|
76
|
+
tooltip: {
|
|
77
|
+
position: 'absolute',
|
|
78
|
+
paddingHorizontal: 12,
|
|
79
|
+
paddingVertical: 6,
|
|
80
|
+
borderRadius: 6,
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
backgroundColor: colorValue,
|
|
83
|
+
bottom: THUMB_SIZE / 2 + TOOLTIP_MARGIN_BOTTOM
|
|
84
|
+
},
|
|
85
|
+
tooltipArrow: {
|
|
86
|
+
position: 'absolute',
|
|
87
|
+
bottom: -TOOLTIP_ARROW_SIZE + 1,
|
|
88
|
+
width: 0,
|
|
89
|
+
height: 0,
|
|
90
|
+
borderLeftWidth: TOOLTIP_ARROW_SIZE,
|
|
91
|
+
borderRightWidth: TOOLTIP_ARROW_SIZE,
|
|
92
|
+
borderTopWidth: TOOLTIP_ARROW_SIZE,
|
|
93
|
+
borderLeftColor: 'transparent',
|
|
94
|
+
borderRightColor: 'transparent',
|
|
95
|
+
borderTopColor: colorValue
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return styles;
|
|
99
|
+
};
|
|
100
|
+
export { useStyles };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { UXForegroundColor } from '@particle-network/ui-shared';
|
|
2
|
+
import type { BoxProps } from '../layout';
|
|
3
|
+
export interface UXSliderProps extends BoxProps {
|
|
4
|
+
color?: Exclude<UXForegroundColor, 'default' | 'white'>;
|
|
5
|
+
minValue?: number;
|
|
6
|
+
maxValue?: number;
|
|
7
|
+
defaultValue?: number;
|
|
8
|
+
value?: number;
|
|
9
|
+
onChange?: (value: number) => void;
|
|
10
|
+
onChangeEnd?: (value: number) => void;
|
|
11
|
+
/**
|
|
12
|
+
* 起始填充的偏移量
|
|
13
|
+
*/
|
|
14
|
+
fillOffset?: number;
|
|
15
|
+
/**
|
|
16
|
+
* 滑杆底部标记点
|
|
17
|
+
*/
|
|
18
|
+
marks?: {
|
|
19
|
+
value: number;
|
|
20
|
+
label: string;
|
|
21
|
+
}[];
|
|
22
|
+
/** 是否显示滑杆上标记记点 */
|
|
23
|
+
showMarksSteps?: boolean;
|
|
24
|
+
/** 拖动步长 */
|
|
25
|
+
step?: number;
|
|
26
|
+
/** 拖动时是否显示气泡提示框 */
|
|
27
|
+
showTooltip?: boolean;
|
|
28
|
+
/** 格式化 value,用于显示 tooltip 的文本 */
|
|
29
|
+
getValue?: (value: number) => string;
|
|
30
|
+
}
|
|
File without changes
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type UXTabsProps } from '../UXTabs';
|
|
3
|
-
interface UXTabSwitchProps {
|
|
4
|
-
tabStyle?: UXTabsProps['tabStyle'];
|
|
5
|
-
containerStyle?: UXTabsProps['containerStyle'];
|
|
6
|
-
size?: UXTabsProps['size'];
|
|
7
|
-
color?: UXTabsProps['color'];
|
|
3
|
+
interface UXTabSwitchProps extends Omit<UXTabsProps, 'variant' | 'selectedKey' | 'onSelectionChange'> {
|
|
8
4
|
onTitle?: string;
|
|
9
5
|
onIcon?: React.ReactNode;
|
|
10
6
|
offTitle?: string;
|
|
11
7
|
offIcon?: React.ReactNode;
|
|
12
8
|
isSelected?: boolean;
|
|
13
|
-
isDisabled?: boolean;
|
|
14
9
|
onValueChange?: (isSelected: boolean) => void;
|
|
15
10
|
}
|
|
16
11
|
export declare const UXTabSwitch: React.FC<UXTabSwitchProps>;
|
|
@@ -3,19 +3,15 @@ import "react";
|
|
|
3
3
|
import { useI18n } from "../../hooks/useI18n.js";
|
|
4
4
|
import { UXTab, UXTabs } from "../UXTabs/index.js";
|
|
5
5
|
const UXTabSwitch = (props)=>{
|
|
6
|
-
const {
|
|
6
|
+
const { onTitle, onIcon, offTitle, offIcon, isSelected = false, onValueChange, ...restProps } = props;
|
|
7
7
|
const i18n = useI18n();
|
|
8
8
|
return /*#__PURE__*/ jsxs(UXTabs, {
|
|
9
|
-
tabStyle: tabStyle,
|
|
10
|
-
containerStyle: containerStyle,
|
|
11
|
-
size: size,
|
|
12
9
|
variant: "switch",
|
|
13
|
-
color: color,
|
|
14
|
-
isDisabled: isDisabled,
|
|
15
10
|
selectedKey: isSelected.toString(),
|
|
16
11
|
onSelectionChange: (key)=>{
|
|
17
12
|
onValueChange?.('true' === key);
|
|
18
13
|
},
|
|
14
|
+
...restProps,
|
|
19
15
|
children: [
|
|
20
16
|
/*#__PURE__*/ jsx(UXTab, {
|
|
21
17
|
title: offTitle || i18n.switch.off,
|
|
@@ -5,7 +5,7 @@ export declare const useStyles: (props: Partial<UXTabsProps> & {
|
|
|
5
5
|
tabsWrapper: {
|
|
6
6
|
backgroundColor: string | undefined;
|
|
7
7
|
gap: import("@particle-network/ui-shared").SpacingType;
|
|
8
|
-
flex: number;
|
|
8
|
+
flex: number | undefined;
|
|
9
9
|
justifyContent: "flex-start" | "space-between";
|
|
10
10
|
borderRadius: number | undefined;
|
|
11
11
|
height: number;
|
|
@@ -14,7 +14,7 @@ export declare const useStyles: (props: Partial<UXTabsProps> & {
|
|
|
14
14
|
};
|
|
15
15
|
tab: {
|
|
16
16
|
flexDirection: "column";
|
|
17
|
-
|
|
17
|
+
flex: number | undefined;
|
|
18
18
|
height: "100%";
|
|
19
19
|
alignItems: "center";
|
|
20
20
|
justifyContent: "center";
|
|
@@ -81,7 +81,7 @@ const useStyles = (props)=>{
|
|
|
81
81
|
]);
|
|
82
82
|
const wrapperBackgroundColor = useMemo(()=>{
|
|
83
83
|
if ('switch' === variant) return getColor('bg-200');
|
|
84
|
-
if ('solid' === variant) return getColor('bg-
|
|
84
|
+
if ('solid' === variant) return getColor('bg-200');
|
|
85
85
|
return 'transparent';
|
|
86
86
|
}, [
|
|
87
87
|
variant,
|
|
@@ -125,8 +125,8 @@ const useStyles = (props)=>{
|
|
|
125
125
|
if (!isSelected) return 'transparent';
|
|
126
126
|
if ('text' === variant || 'underlined' === variant) return 'transparent';
|
|
127
127
|
if ('default' === color) {
|
|
128
|
-
if ('
|
|
129
|
-
return getColor('
|
|
128
|
+
if ('light' === variant) return getColor('bg-200');
|
|
129
|
+
return getColor('tertiary');
|
|
130
130
|
}
|
|
131
131
|
return getColor(color);
|
|
132
132
|
}, [
|
|
@@ -153,7 +153,7 @@ const useStyles = (props)=>{
|
|
|
153
153
|
tabsWrapper: {
|
|
154
154
|
backgroundColor: wrapperBackgroundColor,
|
|
155
155
|
gap: gapValue,
|
|
156
|
-
flex: fullWidth || w ? 1 : 0,
|
|
156
|
+
flex: fullWidth || w ? 1 : void 0,
|
|
157
157
|
justifyContent: fullWidth || w ? 'space-between' : 'flex-start',
|
|
158
158
|
borderRadius: wrapperBorderRadius,
|
|
159
159
|
height,
|
|
@@ -162,11 +162,10 @@ const useStyles = (props)=>{
|
|
|
162
162
|
},
|
|
163
163
|
tab: {
|
|
164
164
|
flexDirection: 'column',
|
|
165
|
-
|
|
166
|
-
'
|
|
167
|
-
'
|
|
168
|
-
|
|
169
|
-
].includes(variant) ? 0 : 1,
|
|
165
|
+
flex: [
|
|
166
|
+
'solid',
|
|
167
|
+
'switch'
|
|
168
|
+
].includes(variant) ? 1 : void 0,
|
|
170
169
|
height: '100%',
|
|
171
170
|
alignItems: 'center',
|
|
172
171
|
justifyContent: 'center',
|
|
@@ -70,7 +70,7 @@ const UXDatePicker = ({ title, value, defaultValue, defaultInternalValue, onChan
|
|
|
70
70
|
footer: /*#__PURE__*/ jsx(UXButton, {
|
|
71
71
|
fullWidth: true,
|
|
72
72
|
color: "primary",
|
|
73
|
-
size: "
|
|
73
|
+
size: "xl",
|
|
74
74
|
onPress: handleConfirm,
|
|
75
75
|
children: i18n.datePicker.confirm
|
|
76
76
|
}),
|
|
@@ -122,7 +122,7 @@ const UXDateRangePicker = ({ title, value, defaultValue, defaultInternalValue, o
|
|
|
122
122
|
/*#__PURE__*/ jsx(UXButton, {
|
|
123
123
|
fullWidth: true,
|
|
124
124
|
color: "primary",
|
|
125
|
-
size: "
|
|
125
|
+
size: "xl",
|
|
126
126
|
onPress: handleConfirm,
|
|
127
127
|
children: i18n.datePicker.confirm
|
|
128
128
|
})
|
package/dist/components/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./UXListBox/index.js";
|
|
|
13
13
|
export * from "./UXModal/index.js";
|
|
14
14
|
export * from "./UXPressable/index.js";
|
|
15
15
|
export * from "./UXRadio/index.js";
|
|
16
|
+
export * from "./UXSlider/index.js";
|
|
16
17
|
export * from "./UXSpinner/index.js";
|
|
17
18
|
export * from "./UXSwitch/index.js";
|
|
18
19
|
export * from "./UXTabs/index.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { TextInput } from "react-native";
|
|
4
|
-
import { useColors, useKeyboard } from "../../hooks/index.js";
|
|
3
|
+
import { Platform, TextInput } from "react-native";
|
|
4
|
+
import { useColors, useI18n, useKeyboard } from "../../hooks/index.js";
|
|
5
5
|
import { Icon } from "../../icons/index.js";
|
|
6
6
|
import { HStack } from "../layout/HStack.js";
|
|
7
7
|
import { VStack } from "../layout/VStack.js";
|
|
@@ -16,6 +16,7 @@ const UXInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
16
16
|
const [isFocused, setIsFocused] = useState(false);
|
|
17
17
|
const [isInvalid, setIsInvalid] = useState(isInvalidProp);
|
|
18
18
|
const { isKeyboardVisible } = useKeyboard();
|
|
19
|
+
const i18n = useI18n();
|
|
19
20
|
useEffect(()=>{
|
|
20
21
|
if (!isKeyboardVisible && blurOnKeyboardHide) inputRef.current?.blur();
|
|
21
22
|
}, [
|
|
@@ -43,6 +44,16 @@ const UXInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
43
44
|
isInvalid,
|
|
44
45
|
isFocused
|
|
45
46
|
});
|
|
47
|
+
useEffect(()=>{
|
|
48
|
+
if ('web' === Platform.OS) return;
|
|
49
|
+
if (inputRef.current) inputRef.current.setNativeProps({
|
|
50
|
+
style: {
|
|
51
|
+
color: styles.input.color
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}, [
|
|
55
|
+
styles.input.color
|
|
56
|
+
]);
|
|
46
57
|
useEffect(()=>{
|
|
47
58
|
if (void 0 !== controlledValue) setInternalValue(controlledValue);
|
|
48
59
|
else if (void 0 !== defaultValue) setInternalValue(defaultValue);
|
|
@@ -78,7 +89,7 @@ const UXInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
78
89
|
setIsInvalid(true);
|
|
79
90
|
return {
|
|
80
91
|
isValid: false,
|
|
81
|
-
errorMessage:
|
|
92
|
+
errorMessage: i18n.validation.required
|
|
82
93
|
};
|
|
83
94
|
}
|
|
84
95
|
setIsInvalid(false);
|
|
@@ -87,7 +98,8 @@ const UXInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
87
98
|
};
|
|
88
99
|
}, [
|
|
89
100
|
internalValue,
|
|
90
|
-
isRequired
|
|
101
|
+
isRequired,
|
|
102
|
+
i18n
|
|
91
103
|
]);
|
|
92
104
|
const renderError = useMemo(()=>{
|
|
93
105
|
if (isInvalidProp && errorMessage) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Platform, TextInput } from "react-native";
|
|
4
|
-
import { useColors, useKeyboard } from "../../hooks/index.js";
|
|
4
|
+
import { useColors, useI18n, useKeyboard } from "../../hooks/index.js";
|
|
5
5
|
import { Icon } from "../../icons/index.js";
|
|
6
6
|
import { HStack } from "../layout/HStack.js";
|
|
7
7
|
import { VStack } from "../layout/VStack.js";
|
|
@@ -9,14 +9,25 @@ import { Text } from "../Text/index.js";
|
|
|
9
9
|
import { UXPressable } from "../UXPressable/index.js";
|
|
10
10
|
import { useStyles } from "./styles.js";
|
|
11
11
|
const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
12
|
-
const { containerStyle, wrapperStyle, inputStyle, value: controlledValue, defaultValue, errorMessage, minValue, maxValue,
|
|
12
|
+
const { containerStyle, wrapperStyle, inputStyle, value: controlledValue, defaultValue, errorMessage, minValue, maxValue, startContent, endContent, isRequired, isReadOnly, isDisabled, isClearable, isInvalid: isInvalidProp, autoErrorMessage, allowNegative, label, onChangeText, onValueChange, onFocus, onBlur, blurOnKeyboardHide = true, ...restProps } = props;
|
|
13
13
|
const { getColor } = useColors();
|
|
14
14
|
const inputRef = useRef(null);
|
|
15
15
|
const [internalValue, setInternalValue] = useState(defaultValue ?? NaN);
|
|
16
16
|
const [displayText, setDisplayText] = useState(defaultValue?.toString() ?? '');
|
|
17
17
|
const [isFocused, setIsFocused] = useState(false);
|
|
18
18
|
const [isInvalid, setIsInvalid] = useState(isInvalidProp);
|
|
19
|
+
const i18n = useI18n();
|
|
19
20
|
const { isKeyboardVisible } = useKeyboard();
|
|
21
|
+
const formatOptions = useMemo(()=>{
|
|
22
|
+
const restFormatOptions = {
|
|
23
|
+
maximumFractionDigits: 3,
|
|
24
|
+
...restProps.formatOptions ?? {}
|
|
25
|
+
};
|
|
26
|
+
delete restFormatOptions.signDisplay;
|
|
27
|
+
return restFormatOptions;
|
|
28
|
+
}, [
|
|
29
|
+
restProps.formatOptions
|
|
30
|
+
]);
|
|
20
31
|
useEffect(()=>{
|
|
21
32
|
if (!isKeyboardVisible && blurOnKeyboardHide) inputRef.current?.blur();
|
|
22
33
|
}, [
|
|
@@ -46,6 +57,15 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
46
57
|
isInvalid,
|
|
47
58
|
isFocused
|
|
48
59
|
});
|
|
60
|
+
useEffect(()=>{
|
|
61
|
+
if (inputRef.current) inputRef.current.setNativeProps({
|
|
62
|
+
style: {
|
|
63
|
+
color: styles.input.color
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}, [
|
|
67
|
+
styles.input.color
|
|
68
|
+
]);
|
|
49
69
|
useEffect(()=>{
|
|
50
70
|
if (void 0 !== controlledValue) {
|
|
51
71
|
setInternalValue(controlledValue);
|
|
@@ -58,24 +78,21 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
58
78
|
defaultValue,
|
|
59
79
|
controlledValue
|
|
60
80
|
]);
|
|
61
|
-
const numberFormatter = useMemo(()=>
|
|
62
|
-
if (formatOptions) return new Intl.NumberFormat(void 0, formatOptions);
|
|
63
|
-
return null;
|
|
64
|
-
}, [
|
|
81
|
+
const numberFormatter = useMemo(()=>new Intl.NumberFormat(void 0, formatOptions), [
|
|
65
82
|
formatOptions
|
|
66
83
|
]);
|
|
67
84
|
const validationResult = useMemo(()=>{
|
|
68
85
|
if (isNaN(internalValue) && isRequired) return {
|
|
69
86
|
isValid: false,
|
|
70
|
-
errorMessage:
|
|
87
|
+
errorMessage: i18n.validation.required
|
|
71
88
|
};
|
|
72
89
|
if (void 0 !== minValue && internalValue < minValue) return {
|
|
73
90
|
isValid: false,
|
|
74
|
-
errorMessage:
|
|
91
|
+
errorMessage: i18n.validation.min.replace('{min}', minValue.toString())
|
|
75
92
|
};
|
|
76
93
|
if (void 0 !== maxValue && internalValue > maxValue) return {
|
|
77
94
|
isValid: false,
|
|
78
|
-
errorMessage:
|
|
95
|
+
errorMessage: i18n.validation.max.replace('{max}', maxValue.toString())
|
|
79
96
|
};
|
|
80
97
|
setIsInvalid(false);
|
|
81
98
|
return {
|
|
@@ -85,22 +102,19 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
85
102
|
internalValue,
|
|
86
103
|
isRequired,
|
|
87
104
|
minValue,
|
|
88
|
-
maxValue
|
|
105
|
+
maxValue,
|
|
106
|
+
i18n
|
|
89
107
|
]);
|
|
90
108
|
const adjustValueToRange = useCallback((num)=>{
|
|
91
|
-
|
|
92
|
-
if (void 0 !== minValue &&
|
|
93
|
-
if (void 0 !== maxValue &&
|
|
94
|
-
return
|
|
109
|
+
if (isNaN(num)) return NaN;
|
|
110
|
+
if (void 0 !== minValue && num < minValue) return minValue;
|
|
111
|
+
if (void 0 !== maxValue && num > maxValue) return maxValue;
|
|
112
|
+
return num;
|
|
95
113
|
}, [
|
|
96
114
|
minValue,
|
|
97
115
|
maxValue
|
|
98
116
|
]);
|
|
99
|
-
const adjustValueToFractionDigits = useCallback((numValue)=>
|
|
100
|
-
if (formatOptions?.minimumFractionDigits !== void 0 || formatOptions?.maximumFractionDigits !== void 0) return Number(numberFormatter?.format(numValue).replaceAll(',', ''));
|
|
101
|
-
return numValue;
|
|
102
|
-
}, [
|
|
103
|
-
formatOptions,
|
|
117
|
+
const adjustValueToFractionDigits = useCallback((numValue)=>Number(numberFormatter?.format(numValue).replaceAll(',', '')), [
|
|
104
118
|
numberFormatter
|
|
105
119
|
]);
|
|
106
120
|
const handleChangeText = useCallback((text)=>{
|
|
@@ -111,17 +125,11 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
111
125
|
if (cleanedText.includes('-') && !cleanedText.startsWith('-')) return;
|
|
112
126
|
setDisplayText(cleanedText);
|
|
113
127
|
onChangeText?.(cleanedText);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
onValueChange?.(NaN);
|
|
118
|
-
} else {
|
|
119
|
-
setInternalValue(numValue);
|
|
120
|
-
onValueChange?.(numValue);
|
|
121
|
-
}
|
|
128
|
+
if ('' === cleanedText || '-' === cleanedText) return void setInternalValue(NaN);
|
|
129
|
+
const numValue = Number(cleanedText);
|
|
130
|
+
isNaN(numValue) ? setInternalValue(NaN) : setInternalValue(numValue);
|
|
122
131
|
}, [
|
|
123
|
-
onChangeText
|
|
124
|
-
onValueChange
|
|
132
|
+
onChangeText
|
|
125
133
|
]);
|
|
126
134
|
const handleFocus = useCallback((e)=>{
|
|
127
135
|
setIsFocused(true);
|
|
@@ -131,8 +139,9 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
131
139
|
]);
|
|
132
140
|
const handleBlur = useCallback((e)=>{
|
|
133
141
|
setIsFocused(false);
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
if (isNaN(internalValue)) onValueChange?.(NaN);
|
|
143
|
+
else {
|
|
144
|
+
const adjustedValue = adjustValueToFractionDigits(adjustValueToRange(internalValue));
|
|
136
145
|
const adjustedText = adjustedValue?.toString();
|
|
137
146
|
setDisplayText(adjustedText);
|
|
138
147
|
setInternalValue(adjustedValue);
|
|
@@ -165,10 +174,11 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
165
174
|
if (isNaN(internalValue)) return '';
|
|
166
175
|
if (numberFormatter) {
|
|
167
176
|
const formattedValue = numberFormatter.format(internalValue);
|
|
168
|
-
|
|
177
|
+
const signDisplay = restProps.formatOptions?.signDisplay;
|
|
178
|
+
if ('exceptZero' === signDisplay || 'always' === signDisplay) {
|
|
169
179
|
if (internalValue > 0) return `+${formattedValue}`;
|
|
170
180
|
else if (internalValue < 0) ;
|
|
171
|
-
else if ('always' ===
|
|
181
|
+
else if ('always' === signDisplay) return `+${formattedValue}`;
|
|
172
182
|
}
|
|
173
183
|
return formattedValue;
|
|
174
184
|
}
|
|
@@ -178,7 +188,7 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
178
188
|
displayText,
|
|
179
189
|
numberFormatter,
|
|
180
190
|
isFocused,
|
|
181
|
-
formatOptions
|
|
191
|
+
restProps.formatOptions
|
|
182
192
|
]);
|
|
183
193
|
const renderError = useMemo(()=>{
|
|
184
194
|
if (isInvalidProp && errorMessage) {
|
package/dist/locales/en.d.ts
CHANGED
package/dist/locales/en.js
CHANGED
package/dist/locales/index.d.ts
CHANGED
|
@@ -45,6 +45,11 @@ export declare const locales: {
|
|
|
45
45
|
on: string;
|
|
46
46
|
off: string;
|
|
47
47
|
};
|
|
48
|
+
validation: {
|
|
49
|
+
required: string;
|
|
50
|
+
min: string;
|
|
51
|
+
max: string;
|
|
52
|
+
};
|
|
48
53
|
};
|
|
49
54
|
zh: {
|
|
50
55
|
datePicker: {
|
|
@@ -89,6 +94,11 @@ export declare const locales: {
|
|
|
89
94
|
on: string;
|
|
90
95
|
off: string;
|
|
91
96
|
};
|
|
97
|
+
validation: {
|
|
98
|
+
required: string;
|
|
99
|
+
min: string;
|
|
100
|
+
max: string;
|
|
101
|
+
};
|
|
92
102
|
};
|
|
93
103
|
};
|
|
94
104
|
export declare function getLocaleText(locale: Locale): {
|
|
@@ -134,4 +144,9 @@ export declare function getLocaleText(locale: Locale): {
|
|
|
134
144
|
on: string;
|
|
135
145
|
off: string;
|
|
136
146
|
};
|
|
147
|
+
validation: {
|
|
148
|
+
required: string;
|
|
149
|
+
min: string;
|
|
150
|
+
max: string;
|
|
151
|
+
};
|
|
137
152
|
};
|
package/dist/locales/zh.js
CHANGED
package/dist/types/theme.d.ts
CHANGED
|
@@ -10,12 +10,14 @@ export interface CommonComponentConfig {
|
|
|
10
10
|
sm: number;
|
|
11
11
|
md: number;
|
|
12
12
|
lg: number;
|
|
13
|
+
xl?: number;
|
|
13
14
|
};
|
|
14
15
|
fontSize: {
|
|
15
16
|
xs?: number;
|
|
16
17
|
sm: number;
|
|
17
18
|
md: number;
|
|
18
19
|
lg: number;
|
|
20
|
+
xl?: number;
|
|
19
21
|
};
|
|
20
22
|
color: Record<string, {
|
|
21
23
|
background: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-network/ui-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"main": "./entry.js",
|
|
5
5
|
"react-native": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -44,8 +44,9 @@
|
|
|
44
44
|
"react-native-paper": "^5.14.5",
|
|
45
45
|
"react-native-size-matters": "^0.4.2",
|
|
46
46
|
"react-native-toast-message": "^2.3.3",
|
|
47
|
-
"
|
|
48
|
-
"@particle-network/ui-shared": "0.
|
|
47
|
+
"react-native-worklets": "0.5.1",
|
|
48
|
+
"@particle-network/ui-shared": "0.4.0",
|
|
49
|
+
"@particle-network/icons": "0.5.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@babel/core": "^7.24.0",
|
|
@@ -59,13 +60,13 @@
|
|
|
59
60
|
"@rsbuild/core": "^1.5.0",
|
|
60
61
|
"@rsbuild/plugin-react": "^1.3.5",
|
|
61
62
|
"@rslib/core": "^0.12.2",
|
|
62
|
-
"@storybook/addon-docs": "^
|
|
63
|
-
"@storybook/addon-ondevice-actions": "^
|
|
64
|
-
"@storybook/addon-ondevice-backgrounds": "^
|
|
65
|
-
"@storybook/addon-ondevice-controls": "^
|
|
66
|
-
"@storybook/addon-ondevice-notes": "^
|
|
67
|
-
"@storybook/react-native": "^
|
|
68
|
-
"@storybook/react-native-web-vite": "^
|
|
63
|
+
"@storybook/addon-docs": "^10.0.0",
|
|
64
|
+
"@storybook/addon-ondevice-actions": "^10.0.0",
|
|
65
|
+
"@storybook/addon-ondevice-backgrounds": "^10.0.0",
|
|
66
|
+
"@storybook/addon-ondevice-controls": "^10.0.0",
|
|
67
|
+
"@storybook/addon-ondevice-notes": "^10.0.0",
|
|
68
|
+
"@storybook/react-native": "^10.0.0",
|
|
69
|
+
"@storybook/react-native-web-vite": "^10.0.0",
|
|
69
70
|
"@types/react": "^19.1.10",
|
|
70
71
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
71
72
|
"babel-plugin-react-docgen-typescript": "^1.5.1",
|
|
@@ -79,18 +80,18 @@
|
|
|
79
80
|
"react-dom": "19.1.0",
|
|
80
81
|
"react-native": "0.81.5",
|
|
81
82
|
"react-native-gesture-handler": "~2.28.0",
|
|
82
|
-
"react-native-reanimated": "
|
|
83
|
+
"react-native-reanimated": "4.1.6",
|
|
83
84
|
"react-native-safe-area-context": "5.6.1",
|
|
84
85
|
"react-native-svg": "15.12.1",
|
|
85
86
|
"react-native-web": "^0.21.1",
|
|
86
87
|
"serve": "^14.2.5",
|
|
87
|
-
"storybook": "^
|
|
88
|
+
"storybook": "^10.0.0",
|
|
88
89
|
"unfetch": "^4.2.0",
|
|
89
90
|
"vite": "^6.3.5",
|
|
90
91
|
"zustand": "^5.0.8",
|
|
91
92
|
"@particle-network/eslint-config": "0.3.0",
|
|
92
|
-
"@particle-network/
|
|
93
|
-
"@particle-network/
|
|
93
|
+
"@particle-network/lintstaged-config": "0.1.0",
|
|
94
|
+
"@particle-network/icons": "0.5.0"
|
|
94
95
|
},
|
|
95
96
|
"overrides": {
|
|
96
97
|
"react-docgen-typescript": "2.2.2",
|