@particle-network/ui-native 0.5.1-beta.9 → 0.6.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.
@@ -2,11 +2,12 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useState } from "react";
3
3
  import { StyleSheet, Text, View } from "react-native";
4
4
  import { useColors } from "../../hooks/index.js";
5
+ import { useFlexStyle } from "../layout/index.js";
5
6
  import { Box, useBoxStyle } from "../layout/Box/index.js";
6
7
  import { HStack } from "../layout/HStack.js";
7
8
  import { useTextStyles } from "./text.style.js";
8
9
  const text_Text = /*#__PURE__*/ forwardRef((props, ref)=>{
9
- const { variant = 'body2Bold', fontWeight, color = 'default', lineHeight, align, style, h1, h2, h3, body1, body1Bold, body2, body2Bold, body3, body3Bold, caption1, caption1Bold, underlineStyle, fullWidth, fullHeight, h, minH, maxH, w, minW, maxW, m, mt, mr, mb, ml, ms, me, mh, mv, p, pt, pr, pb, pl, ps, pe, ph, pv, borderStyle, border, borderTop, borderRight, borderBottom, borderLeft, borderStart, borderEnd, borderColor, borderTopColor, borderRightColor, borderBottomColor, borderLeftColor, borderStartColor, borderEndColor, radius, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, topStartRadius, topEndRadius, bottomStartRadius, bottomEndRadius, position, top, right, bottom, left, start, end, bg, bgOpacity, opacity, zIndex, overflow, shadow, ...restProps } = props;
10
+ const { variant = 'body2Bold', fontWeight, color = 'default', lineHeight, align, style, h1, h2, h3, body1, body1Bold, body2, body2Bold, body3, body3Bold, caption1, caption1Bold, underlineStyle, fill, vertical, gap, inline, center, direction, justify, items, self, content, wrap, basis, grow, shrink, fullWidth, fullHeight, h, minH, maxH, w, minW, maxW, m, mt, mr, mb, ml, ms, me, mh, mv, p, pt, pr, pb, pl, ps, pe, ph, pv, borderStyle, border, borderTop, borderRight, borderBottom, borderLeft, borderStart, borderEnd, borderColor, borderTopColor, borderRightColor, borderBottomColor, borderLeftColor, borderStartColor, borderEndColor, radius, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, topStartRadius, topEndRadius, bottomStartRadius, bottomEndRadius, position, top, right, bottom, left, start, end, bg, bgOpacity, opacity, zIndex, overflow, shadow, ...restProps } = props;
10
11
  const [textWidth, setTextWidth] = useState(0);
11
12
  const { getColor } = useColors();
12
13
  const boxStyle = useBoxStyle({
@@ -74,6 +75,22 @@ const text_Text = /*#__PURE__*/ forwardRef((props, ref)=>{
74
75
  overflow,
75
76
  shadow
76
77
  });
78
+ const flexStyle = useFlexStyle({
79
+ fill,
80
+ vertical,
81
+ gap,
82
+ inline,
83
+ center,
84
+ direction,
85
+ justify,
86
+ items,
87
+ self,
88
+ content,
89
+ wrap,
90
+ basis,
91
+ grow,
92
+ shrink
93
+ });
77
94
  const { styles } = useTextStyles({
78
95
  variant,
79
96
  fontWeight,
@@ -159,6 +176,7 @@ const text_Text = /*#__PURE__*/ forwardRef((props, ref)=>{
159
176
  allowFontScaling: false,
160
177
  style: [
161
178
  boxStyle,
179
+ flexStyle,
162
180
  styles.text,
163
181
  underlineStyle && {
164
182
  textDecorationLine: 'underline',
@@ -1,11 +1,12 @@
1
1
  import type { TextProps as RNTextProps, TextStyle } from 'react-native';
2
2
  import type { HexColor, UXForegroundColor } from '@particle-network/ui-shared';
3
+ import type { UseFlexProps } from '../layout';
3
4
  import type { UseBoxProps } from '../layout/Box';
4
5
  type TextVariant = 'h1' | 'h2' | 'h3' | 'body1' | 'body1Bold' | 'body2' | 'body2Bold' | 'body3' | 'body3Bold' | 'caption1' | 'caption1Bold';
5
6
  type TextWeight = 'normal' | 'medium' | 'semibold' | 'bold' | 'extrabold';
6
7
  type TextLineHeight = '1' | '1.4';
7
8
  type TextAlign = 'left' | 'center' | 'right';
8
- export interface TextProps extends RNTextProps, UseBoxProps {
9
+ export interface TextProps extends RNTextProps, UseFlexProps, UseBoxProps {
9
10
  /**
10
11
  * | variant | font-size | font-weight |
11
12
  * | :----------- | :-------- | :---------- |
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import "react";
3
3
  import CircleQuestionIcon from "@particle-network/icons/native/CircleQuestionIcon";
4
4
  import { useMs } from "../../hooks/index.js";
5
- import { Square } from "../layout/Square.js";
5
+ import { Center } from "../layout/index.js";
6
6
  import { UXTooltip } from "../UXTooltip/index.js";
7
7
  const UXHint = (props)=>{
8
8
  const { content, children, iconStyle, iconColor = 'secondary', style, ...restProps } = props;
@@ -10,14 +10,23 @@ const UXHint = (props)=>{
10
10
  return /*#__PURE__*/ jsx(UXTooltip, {
11
11
  content: content || children,
12
12
  ...restProps,
13
- children: /*#__PURE__*/ jsx(Square, {
14
- center: true,
15
- size: 16,
16
- style: style,
13
+ children: /*#__PURE__*/ jsx(Center, {
14
+ style: [
15
+ {
16
+ minWidth: ms(16),
17
+ minHeight: ms(16)
18
+ },
19
+ style
20
+ ],
17
21
  children: /*#__PURE__*/ jsx(CircleQuestionIcon, {
18
22
  color: iconColor,
19
- size: ms(14),
20
- style: iconStyle
23
+ style: [
24
+ {
25
+ width: ms(14),
26
+ height: ms(14)
27
+ },
28
+ iconStyle
29
+ ]
21
30
  })
22
31
  })
23
32
  });
@@ -1,3 +1,4 @@
1
1
  export * from './input';
2
2
  export * from './number-input';
3
+ export * from './range-input';
3
4
  export type * from './types';
@@ -1,2 +1,3 @@
1
1
  export * from "./input.js";
2
2
  export * from "./number-input.js";
3
+ export * from "./range-input.js";
@@ -16,6 +16,8 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
16
16
  const [displayText, setDisplayText] = useState(defaultValue?.toString() ?? '');
17
17
  const [isFocused, setIsFocused] = useState(false);
18
18
  const [isInvalid, setIsInvalid] = useState(isInvalidProp);
19
+ const prevControlledValueRef = useRef(void 0);
20
+ const isInitializedRef = useRef(false);
19
21
  const i18n = useI18n();
20
22
  const { isKeyboardVisible } = useKeyboard();
21
23
  const formatOptions = useMemo(()=>{
@@ -58,6 +60,7 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
58
60
  isFocused
59
61
  });
60
62
  useEffect(()=>{
63
+ if ('web' === Platform.OS) return;
61
64
  if (inputRef.current) inputRef.current.setNativeProps({
62
65
  style: {
63
66
  color: styles.input.color
@@ -68,16 +71,38 @@ const UXNumberInput = /*#__PURE__*/ forwardRef((props, ref)=>{
68
71
  ]);
69
72
  useEffect(()=>{
70
73
  if (void 0 !== controlledValue) {
71
- setInternalValue(controlledValue);
72
- setDisplayText(isNaN(controlledValue) ? '' : controlledValue?.toString());
73
- } else if (void 0 !== defaultValue) {
74
+ const prevValue = prevControlledValueRef.current;
75
+ if (controlledValue !== prevValue) {
76
+ setInternalValue(controlledValue);
77
+ setDisplayText(isNaN(controlledValue) ? '' : controlledValue?.toString());
78
+ prevControlledValueRef.current = controlledValue;
79
+ }
80
+ } else if (void 0 !== defaultValue && !isInitializedRef.current) {
74
81
  setInternalValue(defaultValue);
75
82
  setDisplayText(isNaN(defaultValue) ? '' : defaultValue?.toString());
83
+ isInitializedRef.current = true;
76
84
  }
77
85
  }, [
78
86
  defaultValue,
79
87
  controlledValue
80
88
  ]);
89
+ useEffect(()=>{
90
+ if (!isFocused && void 0 !== controlledValue) {
91
+ const timer = setTimeout(()=>{
92
+ setInternalValue((currentInternalValue)=>{
93
+ if (isNaN(currentInternalValue) && !isNaN(controlledValue)) {
94
+ setDisplayText(controlledValue.toString());
95
+ return controlledValue;
96
+ }
97
+ return currentInternalValue;
98
+ });
99
+ }, 0);
100
+ return ()=>clearTimeout(timer);
101
+ }
102
+ }, [
103
+ isFocused,
104
+ controlledValue
105
+ ]);
81
106
  const numberFormatter = useMemo(()=>new Intl.NumberFormat(void 0, formatOptions), [
82
107
  formatOptions
83
108
  ]);
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import type { View } from 'react-native';
3
+ import type { UXRangeInputProps } from './types';
4
+ export declare const UXRangeInput: React.ForwardRefExoticComponent<UXRangeInputProps & React.RefAttributes<View>>;
@@ -0,0 +1,104 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, forwardRef, useCallback, useMemo, useRef, useState } from "react";
3
+ import { useI18n } from "../../hooks/useI18n.js";
4
+ import { HStack } from "../layout/HStack.js";
5
+ import { UXNumberInput } from "./number-input.js";
6
+ const UXRangeInput = /*#__PURE__*/ forwardRef((props, ref)=>{
7
+ const { asFragment, value, defaultValue, onValueChange, minValue, maxValue, minInputProps = {}, maxInputProps = {}, separator, gap = 8, containerStyle, ...restProps } = props;
8
+ const i18n = useI18n();
9
+ const minInputRef = useRef(null);
10
+ const maxInputRef = useRef(null);
11
+ const [internalValue, setInternalValue] = useState(defaultValue || {
12
+ min: void 0,
13
+ max: void 0
14
+ });
15
+ const currentValue = void 0 !== value ? value : internalValue;
16
+ const handleMinChange = useCallback((newMin)=>{
17
+ const minValue = isNaN(newMin) ? 0 : newMin;
18
+ const newValue = {
19
+ min: minValue,
20
+ max: currentValue.max
21
+ };
22
+ if (void 0 !== currentValue.max && !isNaN(currentValue.max) && minValue > currentValue.max) newValue.max = minValue;
23
+ if (void 0 === value) setInternalValue(newValue);
24
+ onValueChange?.(newValue);
25
+ }, [
26
+ currentValue.max,
27
+ value,
28
+ onValueChange
29
+ ]);
30
+ const handleMaxChange = useCallback((newMax)=>{
31
+ const maxValue = isNaN(newMax) ? 0 : newMax;
32
+ const newValue = {
33
+ min: currentValue.min,
34
+ max: maxValue
35
+ };
36
+ if (void 0 !== currentValue.min && !isNaN(currentValue.min) && maxValue < currentValue.min) newValue.min = maxValue;
37
+ if (void 0 === value) setInternalValue(newValue);
38
+ onValueChange?.(newValue);
39
+ }, [
40
+ currentValue.min,
41
+ value,
42
+ onValueChange
43
+ ]);
44
+ const minInputMaxValue = useMemo(()=>{
45
+ if (void 0 !== currentValue.max && !isNaN(currentValue.max)) return currentValue.max;
46
+ return maxValue;
47
+ }, [
48
+ currentValue.max,
49
+ maxValue
50
+ ]);
51
+ const maxInputMinValue = useMemo(()=>{
52
+ if (void 0 !== currentValue.min && !isNaN(currentValue.min)) return currentValue.min;
53
+ return minValue;
54
+ }, [
55
+ currentValue.min,
56
+ minValue
57
+ ]);
58
+ const Wrapper = asFragment ? Fragment : HStack;
59
+ return /*#__PURE__*/ jsxs(Wrapper, {
60
+ ref: ref,
61
+ gap: gap,
62
+ style: containerStyle,
63
+ items: "center",
64
+ children: [
65
+ /*#__PURE__*/ jsx(UXNumberInput, {
66
+ startContent: i18n.rangeInput.min,
67
+ textAlign: "right",
68
+ ...restProps,
69
+ ...minInputProps,
70
+ ref: minInputRef,
71
+ value: currentValue.min,
72
+ minValue: minValue,
73
+ maxValue: minInputMaxValue,
74
+ containerStyle: [
75
+ {
76
+ flex: 1
77
+ },
78
+ minInputProps.containerStyle
79
+ ],
80
+ onValueChange: handleMinChange
81
+ }),
82
+ separator,
83
+ /*#__PURE__*/ jsx(UXNumberInput, {
84
+ startContent: i18n.rangeInput.max,
85
+ textAlign: "right",
86
+ ...restProps,
87
+ ...maxInputProps,
88
+ ref: maxInputRef,
89
+ value: currentValue.max,
90
+ minValue: maxInputMinValue,
91
+ maxValue: maxValue,
92
+ containerStyle: [
93
+ {
94
+ flex: 1
95
+ },
96
+ maxInputProps.containerStyle
97
+ ],
98
+ onValueChange: handleMaxChange
99
+ })
100
+ ]
101
+ });
102
+ });
103
+ UXRangeInput.displayName = 'UXRangeInput';
104
+ export { UXRangeInput };
@@ -15,6 +15,7 @@ export declare const useStyles: (props: UseStylesProps) => {
15
15
  opacity: number;
16
16
  };
17
17
  input: {
18
+ minWidth: number;
18
19
  height: "auto" | "100%";
19
20
  flex: number;
20
21
  paddingTop: number;
@@ -89,6 +89,7 @@ const useStyles = (props)=>{
89
89
  opacity: isDisabled ? disabledOpacity : 1
90
90
  },
91
91
  input: {
92
+ minWidth: 0,
92
93
  height: 'android' === Platform.OS ? '100%' : 'auto',
93
94
  flex: 1,
94
95
  paddingTop: 0,
@@ -65,3 +65,48 @@ export interface UXNumberInputProps extends UXInputCommonProps {
65
65
  allowNegative?: boolean;
66
66
  onValueChange?: (value: number) => void;
67
67
  }
68
+ export interface UXRangeInputValue {
69
+ min?: number;
70
+ max?: number;
71
+ }
72
+ export interface UXRangeInputProps extends Omit<UXNumberInputProps, 'value' | 'defaultValue' | 'onValueChange' | 'minValue' | 'maxValue'> {
73
+ asFragment?: boolean;
74
+ /**
75
+ * The current value of the range input (controlled)
76
+ */
77
+ value?: UXRangeInputValue;
78
+ /**
79
+ * The default value of the range input (uncontrolled)
80
+ */
81
+ defaultValue?: UXRangeInputValue;
82
+ /**
83
+ * The minimum value allowed for both inputs
84
+ */
85
+ minValue?: number;
86
+ /**
87
+ * The maximum value allowed for both inputs
88
+ */
89
+ maxValue?: number;
90
+ /**
91
+ * Props for the minimum input
92
+ */
93
+ minInputProps?: Partial<UXNumberInputProps>;
94
+ /**
95
+ * Props for the maximum input
96
+ */
97
+ maxInputProps?: Partial<UXNumberInputProps>;
98
+ /**
99
+ * Separator between the two inputs
100
+ * @default '-'
101
+ */
102
+ separator?: ReactNode;
103
+ /**
104
+ * Gap between the inputs (in pixels)
105
+ * @default 8
106
+ */
107
+ gap?: number;
108
+ /**
109
+ * Callback fired when the value changes
110
+ */
111
+ onValueChange?: (value: UXRangeInputValue) => void;
112
+ }
@@ -46,5 +46,9 @@ export declare const en: {
46
46
  min: string;
47
47
  max: string;
48
48
  };
49
+ rangeInput: {
50
+ min: string;
51
+ max: string;
52
+ };
49
53
  };
50
54
  export type LocaleText = typeof en;
@@ -45,6 +45,10 @@ const en = {
45
45
  required: 'This field is required',
46
46
  min: 'Value must be greater than {min}',
47
47
  max: 'Value must be less than {max}'
48
+ },
49
+ rangeInput: {
50
+ min: 'Min',
51
+ max: 'Max'
48
52
  }
49
53
  };
50
54
  export { en };
@@ -50,6 +50,10 @@ export declare const locales: {
50
50
  min: string;
51
51
  max: string;
52
52
  };
53
+ rangeInput: {
54
+ min: string;
55
+ max: string;
56
+ };
53
57
  };
54
58
  zh: {
55
59
  datePicker: {
@@ -99,6 +103,10 @@ export declare const locales: {
99
103
  min: string;
100
104
  max: string;
101
105
  };
106
+ rangeInput: {
107
+ min: string;
108
+ max: string;
109
+ };
102
110
  };
103
111
  };
104
112
  export declare function getLocaleText(locale: Locale): {
@@ -149,4 +157,8 @@ export declare function getLocaleText(locale: Locale): {
149
157
  min: string;
150
158
  max: string;
151
159
  };
160
+ rangeInput: {
161
+ min: string;
162
+ max: string;
163
+ };
152
164
  };
@@ -45,6 +45,10 @@ const zh = {
45
45
  required: '请输入',
46
46
  min: '值不能小于{min}',
47
47
  max: '值不能大于{max}'
48
+ },
49
+ rangeInput: {
50
+ min: '最小',
51
+ max: '最大'
48
52
  }
49
53
  };
50
54
  export { zh };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@particle-network/ui-native",
3
- "version": "0.5.1-beta.9",
3
+ "version": "0.6.0",
4
4
  "main": "./entry.js",
5
5
  "react-native": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -45,8 +45,8 @@
45
45
  "react-native-size-matters": "^0.4.2",
46
46
  "react-native-toast-message": "^2.3.3",
47
47
  "react-native-worklets": "0.5.1",
48
- "@particle-network/ui-shared": "0.4.1-beta.5",
49
- "@particle-network/icons": "0.5.1-beta.6"
48
+ "@particle-network/icons": "0.6.0",
49
+ "@particle-network/ui-shared": "0.5.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@babel/core": "^7.24.0",
@@ -89,9 +89,9 @@
89
89
  "unfetch": "^4.2.0",
90
90
  "vite": "^6.3.5",
91
91
  "zustand": "^5.0.8",
92
- "@particle-network/icons": "0.5.1-beta.6",
93
- "@particle-network/lintstaged-config": "0.1.0",
94
- "@particle-network/eslint-config": "0.3.0"
92
+ "@particle-network/eslint-config": "0.3.0",
93
+ "@particle-network/icons": "0.6.0",
94
+ "@particle-network/lintstaged-config": "0.1.0"
95
95
  },
96
96
  "overrides": {
97
97
  "react-docgen-typescript": "2.2.2",