From edef131dba3d2da4ec4d6822168c4f7474f5d415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 11:02:03 +0800 Subject: [PATCH 1/9] [5.2.0-rc.1]feat: refactor Stepper --- components/input/style/index.tsx | 4 +- components/stepper/InputNumber.tsx | 510 ----------------------------- components/stepper/PropsType.tsx | 61 +++- components/stepper/demo/basic.tsx | 132 +++++--- components/stepper/index.en-US.md | 60 +++- components/stepper/index.tsx | 41 +-- components/stepper/index.zh-CN.md | 60 +++- components/stepper/stepper.tsx | 318 ++++++++++++++++++ components/stepper/style/index.tsx | 57 ++-- 9 files changed, 587 insertions(+), 656 deletions(-) delete mode 100644 components/stepper/InputNumber.tsx create mode 100644 components/stepper/stepper.tsx diff --git a/components/input/style/index.tsx b/components/input/style/index.tsx index 6b3d782bb..cec7e1d97 100644 --- a/components/input/style/index.tsx +++ b/components/input/style/index.tsx @@ -34,7 +34,7 @@ export default (theme: Theme) => }, prefix: { fontSize: theme.font_size_heading, - paddingRight: theme.prefix_padding, + marginRight: theme.prefix_padding, }, showCount: { fontSize: theme.font_size_heading, @@ -43,7 +43,7 @@ export default (theme: Theme) => }, suffix: { fontSize: theme.font_size_heading, - paddingLeft: theme.prefix_padding, + marginLeft: theme.prefix_padding, }, warning: { color: theme.color_warning, diff --git a/components/stepper/InputNumber.tsx b/components/stepper/InputNumber.tsx deleted file mode 100644 index c6affad86..000000000 --- a/components/stepper/InputNumber.tsx +++ /dev/null @@ -1,510 +0,0 @@ -import React from 'react' -import { - StyleProp, - Text, - TextInput, - TextStyle, - TouchableWithoutFeedback, - View, - ViewStyle, -} from 'react-native' -import StepperStyles from './style' - -function noop() {} - -function defaultParser(input: string) { - return input.replace(/[^\w\.-]+/g, '') -} - -/** - * When click and hold on a button - the speed of auto changin the value. - */ -const SPEED = 200 - -/** - * When click and hold on a button - the delay before auto changin the value. - */ -const DELAY = 600 - -/** - * Max Safe Integer -- on IE this is not available, so manually set the number in that case. - * The reason this is used, instead of Infinity is because numbers above the MSI are unstable - */ -const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1 - -export interface InputNumberProps { - style?: StyleProp - onChange?: (e: any) => void - readOnly?: boolean - disabled?: boolean - onFocus?: (e?: any) => void - onBlur?: (e: any, ...rest: any[]) => void - max: number - min: number - step: string | number - parser?: (v: any) => void - precision?: number - value?: number - defaultValue?: number - autoFocus?: boolean - styles: ReturnType - upStyle?: StyleProp - downStyle?: StyleProp - inputStyle?: StyleProp - keyboardType?: any -} -export interface InputNumberState { - value: number - inputValue?: number - focused?: boolean -} - -export default class InputNumber< - P extends InputNumberProps = InputNumberProps, - S extends InputNumberState = InputNumberState, -> extends React.Component { - static defaultProps = { - max: MAX_SAFE_INTEGER, - min: -MAX_SAFE_INTEGER, - step: 1, - style: {}, - onChange: noop, - onFocus: noop, - onBlur: noop, - parser: defaultParser, - } - - autoStepTimer: any - _stepDown: any - _stepDownText: any - _stepUp: any - _stepUpText: any - - constructor(props: P) { - super(props as P) - - let value - if ('value' in props) { - value = props.value - } else { - value = props.defaultValue - } - value = this.toNumber(value) - this.state = { - inputValue: this.toPrecisionAsStep(value), - value, - focused: props.autoFocus, - } as S - } - - UNSAFE_componentWillReceiveProps(nextProps: P) { - if ('value' in nextProps) { - const value = this.state.focused - ? nextProps.value - : this.getValidValue(nextProps.value) - this.setState({ - value, - inputValue: value, - }) - } - } - - componentWillUnmount() { - this.stop() - } - - onChange = (e: any) => { - const { parser, onChange } = this.props - const input = parser && parser(this.getValueFromEvent(e).trim()) - this.setState({ inputValue: input } as S) - // tslint:disable-next-line:no-unused-expression - onChange && onChange(this.toNumberWhenUserInput(input)) // valid number or invalid string - } - - onFocus = (...args: any[]) => { - this.setState({ - focused: true, - }) - const { onFocus } = this.props - // tslint:disable-next-line:no-unused-expression - onFocus && onFocus(...args) - } - - onBlur = (e: any, ...args: any[]) => { - this.setState({ - focused: false, - }) - const value = this.getCurrentValidValue(this.state.inputValue) - e.persist() // fix https://github.com/react-component/input-number/issues/51 - this.setValue(value, () => { - const { onBlur } = this.props - // tslint:disable-next-line:no-unused-expression - onBlur && onBlur(e, ...args) - }) - } - - getCurrentValidValue = (value: any) => { - let val = value - if (val === '') { - val = '' - } else if (!this.isNotCompleteNumber(val)) { - val = this.getValidValue(val) - } else { - val = this.state.value - } - return this.toNumber(val) - } - - getValidValue = (value: any) => { - let val = parseFloat(value) - // https://github.com/ant-design/ant-design/issues/7358 - if (isNaN(val)) { - return value - } - if (val < this.props.min) { - val = this.props.min as number - } - if (val > this.props.max) { - val = this.props.max as number - } - return val - } - - setValue = (v: any, callback?: any) => { - // trigger onChange - const newValue = this.isNotCompleteNumber(parseFloat(v)) - ? undefined - : parseFloat(v) - const changed = - newValue !== this.state.value || - `${newValue}` !== `${this.state.inputValue}` // https://github.com/ant-design/ant-design/issues/7363 - if (!('value' in this.props)) { - this.setState( - { - value: newValue, - inputValue: this.toPrecisionAsStep(v), - } as S, - callback, - ) - } else { - // always set input value same as value - this.setState( - { - inputValue: this.toPrecisionAsStep(this.state.value), - }, - callback, - ) - } - if (changed) { - const { onChange } = this.props - // tslint:disable-next-line:no-unused-expression - onChange && onChange(newValue) - } - } - - getPrecision = (value: any) => { - if ('precision' in this.props) { - return this.props.precision as number - } - const valueString = value.toString() - if (valueString.indexOf('e-') >= 0) { - return parseInt(valueString.slice(valueString.indexOf('e-') + 2), 10) - } - let precision = 0 - if (valueString.indexOf('.') >= 0) { - precision = valueString.length - valueString.indexOf('.') - 1 - } - return precision - } - - // step={1.0} value={1.51} - // press + - // then value should be 2.51, rather than 2.5 - // if this.props.precision is undefined - // https://github.com/react-component/input-number/issues/39 - getMaxPrecision = (currentValue: any, ratio = 1) => { - if ('precision' in this.props) { - return this.props.precision as number - } - const { step } = this.props - const ratioPrecision = this.getPrecision(ratio) - const stepPrecision = this.getPrecision(step) - const currentValuePrecision = this.getPrecision(currentValue) - if (!currentValue) { - return ratioPrecision + stepPrecision - } - return Math.max(currentValuePrecision, ratioPrecision + stepPrecision) - } - - getPrecisionFactor = (currentValue: any, ratio = 1) => { - const precision = this.getMaxPrecision(currentValue, ratio) - return Math.pow(10, precision as number) - } - - toPrecisionAsStep = (num: any) => { - if (this.isNotCompleteNumber(num) || num === '') { - return num - } - const precision = Math.abs(this.getMaxPrecision(num)) - if (!isNaN(precision)) { - return Number(num).toFixed(precision) - } - return num.toString() - } - - // '1.' '1x' 'xx' '' => are not complete numbers - isNotCompleteNumber = (num: any) => { - return ( - isNaN(num) || - num === '' || - num === null || - (num && num.toString().indexOf('.') === num.toString().length - 1) - ) - } - - toNumber = (num: any) => { - if (this.isNotCompleteNumber(num)) { - return num - } - if ('precision' in this.props) { - return Number(Number(num).toFixed(this.props.precision)) - } - return Number(num) - } - - // '1.0' '1.00' => may be a inputing number - toNumberWhenUserInput = (num: any) => { - // num.length > 16 => prevent input large number will became Infinity - if ((/\.\d*0$/.test(num) || num.length > 16) && this.state.focused) { - return num - } - return this.toNumber(num) - } - - stepCompute = (type: 'up' | 'down', val: any, rat: any) => { - const { step, min } = this.props - const precisionFactor = this.getPrecisionFactor(val, rat) - const precision = Math.abs(this.getMaxPrecision(val, rat)) - let result - const direct = type === 'up' ? 1 : -1 - if (typeof val === 'number') { - result = ( - (precisionFactor * val + direct * precisionFactor * +step * rat) / - precisionFactor - ).toFixed(precision) - } else { - result = min === -Infinity ? direct * +step : min - } - return this.toNumber(result) - } - - step = (type: 'up' | 'down', e: any, ratio = 1) => { - if (e) { - e.preventDefault() - } - const props = this.props - if (props.disabled) { - return false - } - const value = this.getCurrentValidValue(this.state.inputValue) || 0 - if (this.isNotCompleteNumber(value)) { - return false - } - let val = this.stepCompute(type, value, ratio) - const outOfRange = val > props.max || val < props.min - if (val > props.max) { - val = props.max - } else if (val < props.min) { - val = props.min - } - this.setValue(val) - this.setState({ - focused: true, - }) - return !outOfRange - } - - stop = () => { - if (this.autoStepTimer) { - clearTimeout(this.autoStepTimer) - } - } - - action = (type: 'up' | 'down', e: any, ratio?: any, recursive?: any) => { - if (e.persist) { - e.persist() - } - this.stop() - if (this.step(type, e, ratio)) { - this.autoStepTimer = setTimeout( - () => { - this.action(type, e, ratio, true) - }, - recursive ? SPEED : DELAY, - ) - } - } - - down = (e: any, ratio?: any, recursive?: any) => { - this.action('down', e, ratio, recursive) - } - - up = (e: any, ratio?: any, recursive?: any) => { - this.action('up', e, ratio, recursive) - } - - onPressIn(type: string) { - if (this.props.disabled) { - return - } - const { styles } = this.props - ;(this as any)[type].setNativeProps({ - style: [styles.stepWrap, styles.highlightStepBorderColor], - }) - ;(this as any)[`${type}Text`].setNativeProps({ - style: [styles.stepText, styles.highlightStepTextColor], - }) - } - - onPressOut(type: any) { - if (this.props.disabled) { - return - } - const { styles } = this.props - ;(this as any)[type].setNativeProps({ - style: [styles.stepWrap], - }) - ;(this as any)[`${type}Text`].setNativeProps({ - style: [styles.stepText], - }) - } - - onPressInDown = (e: any) => { - this.onPressIn('_stepDown') - this.down(e, true) - } - onPressOutDown = () => { - this.onPressOut('_stepDown') - this.stop() - } - - onPressInUp = (e: any) => { - this.onPressIn('_stepUp') - this.up(e, true) - } - - onPressOutUp = () => { - this.onPressOut('_stepUp') - this.stop() - } - - getValueFromEvent(e: any) { - return e.nativeEvent.text - } - - render() { - const { props, state } = this - const { style, upStyle, downStyle, inputStyle, styles } = this.props - const editable = !this.props.readOnly && !this.props.disabled - - let upDisabledStyle = null - let downDisabledStyle = null - let upDisabledTextStyle = null - let downDisabledTextStyle = null - const value = +state.value - if (!isNaN(value)) { - const val = Number(value) - if (val >= (props.max as number)) { - upDisabledStyle = styles.stepDisabled - upDisabledTextStyle = styles.disabledStepTextColor - } - if (val <= (props.min as number)) { - downDisabledStyle = styles.stepDisabled - downDisabledTextStyle = styles.disabledStepTextColor - } - } else { - upDisabledStyle = styles.stepDisabled - downDisabledStyle = styles.stepDisabled - upDisabledTextStyle = styles.disabledStepTextColor - downDisabledTextStyle = styles.disabledStepTextColor - } - - let inputDisabledStyle = null - if (props.disabled) { - upDisabledStyle = styles.stepDisabled - downDisabledStyle = styles.stepDisabled - upDisabledTextStyle = styles.disabledStepTextColor - downDisabledTextStyle = styles.disabledStepTextColor - inputDisabledStyle = styles.disabledStepTextColor - } - - let inputDisplayValue - if (state.focused) { - inputDisplayValue = `${state.inputValue}` - } else { - inputDisplayValue = `${state.value}` - } - - if (inputDisplayValue === undefined) { - inputDisplayValue = '' - } - - return ( - - - (this._stepDown = component)} - style={[styles.stepWrap, downDisabledStyle, downStyle]}> - (this._stepDownText = component)} - style={[styles.stepText, downDisabledTextStyle]}> - - - - - - - - (this._stepUp = component)} - style={[styles.stepWrap, upDisabledStyle, upStyle]}> - (this._stepUpText = component)} - style={[styles.stepText, upDisabledTextStyle]}> - + - - - - - ) - } -} diff --git a/components/stepper/PropsType.tsx b/components/stepper/PropsType.tsx index e6a66b104..f01a0e9fd 100644 --- a/components/stepper/PropsType.tsx +++ b/components/stepper/PropsType.tsx @@ -1,17 +1,48 @@ -import { StyleProp, TextStyle, ViewStyle } from 'react-native' +import { TouchableHighlightProps } from 'react-native' +import { InputProps } from '../input/PropsType' +import { Theme } from '../style' +import { StepperStyle } from './style' -export interface StepPropsType { - min?: number - max?: number - step?: number | string - readOnly?: boolean - disabled?: boolean - autoFocus?: boolean - value?: number - defaultValue?: number - onChange?: (value: any) => void - upStyle?: StyleProp - downStyle?: StyleProp - inputStyle?: StyleProp - name?: string +type ValueProps = { + allowEmpty: true + value?: ValueType | null + defaultValue?: ValueType | null + onChange?: (value: ValueType | null) => void } + +type ValuePropsWithNull = { + allowEmpty?: false + value?: ValueType + defaultValue?: ValueType + onChange?: (value: ValueType) => void +} + +export type BaseStepperProps = Omit< + InputProps, + 'value' | 'defaultValue' | 'onChange' | 'styles' +> & + (ValuePropsWithNull | ValueProps) & { + min?: ValueType + max?: ValueType + step?: ValueType + digits?: number + disabled?: boolean + minusButtonProps?: TouchableHighlightProps + plusButtonProps?: TouchableHighlightProps + // Format & Parse + parser?: (text: string) => ValueType + formatter?: (value?: ValueType) => string + styles?: Partial + themeStyles?: (theme: Theme) => Partial + } + +export type NumberStepperProps = BaseStepperProps & { + // stringMode + stringMode?: false +} +export type StringStepperProps = BaseStepperProps & { + // stringMode + stringMode: true +} + +export type StepperProps = NumberStepperProps | StringStepperProps diff --git a/components/stepper/demo/basic.tsx b/components/stepper/demo/basic.tsx index 0a99984cd..47a64eeef 100644 --- a/components/stepper/demo/basic.tsx +++ b/components/stepper/demo/basic.tsx @@ -1,55 +1,109 @@ -/* tslint:disable:no-console */ import React from 'react' -import { View } from 'react-native' -import { List, Stepper } from '../../' +import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native' +import { List, Stepper, Toast } from '../../' -function onChange(value: any) { - console.log('changed', value) -} - -export default class StepperExample extends React.Component { - render() { - const readOnly = ( - - ) - return ( - - +export default function StepperExample() { + return ( + + + + { + console.log(value) + }} + /> + }> + 基础用法 + + }> + 步长设置 + + }> + 限制输入范围 + + }>格式化到一位小数 `$ ${value}`} + parser={(text) => parseFloat(text.replace('$', ''))} + onChange={(value) => { + console.log(value, typeof value) + }} /> }> - readOnly: true + 自定义格式 + + + + }>禁用状态 + }>输入框只读状态 + { + Toast.info('获得焦点') + }} + onBlur={() => { + Toast.info('失去焦点') + }} + /> + }> + 获得/失去焦点 - readOnly: false { + console.log(value) + }} /> }> - Disabled + 允许清空 + + }> + 自定义样式 - - ) - } + + + + ) +} + +const StringModeExample = () => { + const [value, setValue] = React.useState('9999999999999999') + return ( + + + + + + + + + + + ) } diff --git a/components/stepper/index.en-US.md b/components/stepper/index.en-US.md index c3d9f4c0f..311702ffa 100644 --- a/components/stepper/index.en-US.md +++ b/components/stepper/index.en-US.md @@ -11,15 +11,51 @@ title: Stepper ## API -Properties | Descrition | Type | Default ------------|------------|------|-------- -| min | Specifies the minimum value | Number | -Infinity | -| max | Specifies the maximum value | Number | Infinity | -| value | Specifies the value of the `Stepper` | Number | | -| step | Specifies the legal number intervals | Number or String | 1 | -| defaultValue | Specifies the defaultValue of the `Stepper` | Number | | -| onChange | Called when value of the `Stepper` changed | (): void | | -| disabled | Specifies the `Stepper` should be disabled | Boolean | false | -| readOnly | Specifies the `Stepper` is read only | Boolean | false | -| styles | the styles of React-Native component | ReactNative StyleSheet | - | -| inputStyle | react native numeral style | ReactNative StyleSheet | - | +| Name | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| allowEmpty | Whether to allow empty content. | `boolean` | `false` | +| defaultValue | Default value | `number \| null` | `0` | +| digits | Format to a fixed number of digits after the decimal point, set to `0` means format to integer. Will use `formatter` as display value when configured | `number` | - | 5.2.0 | +| disabled | Whether to disabled Stepper | `boolean` | `false` | +| formatter | Format value in input | `(value?: number) => string` | - | 5.2.0 | +| inputStyle | TextInput style | `StyleProp` | - | +| max | Max value | `number` | - | +| min | Min value | `number` | - | +| minusButtonProps | The minus button props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd', children: -, delayLongPress:500 }` | 5.2.0 | +| plusButtonProps | The plus button props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd', children: +, delayLongPress:500 }` | 5.2.0 | +| onChange | Callback when value is changed | `(value: number \| null) => void` | - | +| parser | Parse input text into number which should work with `formatter` | `(text: string) => number` | - | 5.2.0 | +| step | The value to increase or decrease each time, can be a decimal | `number` | `1` | +| stringMode | Set value as string to support high precision decimals. Will set `defaultValue`,`value`, `min`, `max`, `onChange` to `string` type | `boolean` | `false` | 5.2.0 | +| styles | Semantic DOM style | [StepperStyle](#stepperstyle-interface) | - | 5.2.0 | +| value | Current number, controlled value | `number \| null` | - | + + - New in `5.2.0`. In addition, all properties of react-native [TextInput](http://facebook.github.io/react-native/docs/textinput.html) are supported, eg: **`readOnly`** **`onFocus`** **`onBlur`** + + - New in `5.2.0`. Support **Long Press To Trigger** increment or decrement; customizable execution timing: `plusButtonProps={{ delayLongPress: 500 }}`. + + - When `allowEmpty` is `true`, the `value` parameter of `onChange` may be **`null`**, please pay attention when using it. + +### StepperStyle interface + +`5.2.0`refactored the styles + +```typescript +export interface StepperStyle extends Partial { + // extends InputStyle + container: ViewStyle + input: ViewStyle + prefix: ViewStyle // for minus button + suffix: ViewStyle // for plus button + + // StepperStyle + inputDisabled: TextStyle + stepWrap: ViewStyle + stepText: TextStyle + stepDisabled: ViewStyle + disabledStepTextColor: TextStyle +} +``` + +## Ref +Same as [component/Input](/components/input#ref) \ No newline at end of file diff --git a/components/stepper/index.tsx b/components/stepper/index.tsx index 20e4745c8..bb5f90694 100644 --- a/components/stepper/index.tsx +++ b/components/stepper/index.tsx @@ -1,40 +1,5 @@ -import React from 'react' -import { Platform, StyleProp, ViewStyle } from 'react-native' -import { WithTheme, WithThemeStyles } from '../style' -import InputNumber from './InputNumber' -import { StepPropsType } from './PropsType' -import StepperStyles, { StepperStyle } from './style' +import { Stepper } from './stepper' -export interface StepProps - extends StepPropsType, - WithThemeStyles { - style?: StyleProp -} +export type { StepperProps } from './PropsType' -export default class Stepper extends React.Component { - static defaultProps: StepProps = { - step: 1, - readOnly: false, - disabled: false, - inputStyle: {}, - } - - render() { - const { inputStyle, ...restProps } = this.props - const keyboardType = - Platform.OS === 'android' ? 'numeric' : 'numbers-and-punctuation' - - return ( - - {(styles) => ( - - )} - - ) - } -} +export default Stepper diff --git a/components/stepper/index.zh-CN.md b/components/stepper/index.zh-CN.md index e5eec3035..c3728e400 100644 --- a/components/stepper/index.zh-CN.md +++ b/components/stepper/index.zh-CN.md @@ -12,15 +12,51 @@ subtitle: 步进器 ## API -属性 | 说明 | 类型 | 默认值 -----|-----|------|------ -| min | 最小值 | Number | -Infinity | -| max | 最大值 | Number | Infinity | -| value | 当前值 | Number | | -| step | 每次改变步数,可以为小数 | Number or String | 1 | -| defaultValue | 初始值 | Number | | -| onChange | 变化时回调函数 | (): void | | -| disabled | 禁用 | Boolean | false | -| readOnly | input 只读 | Boolean | false | -| styles | react native 组件样式 | ReactNative StyleSheet | - | -| inputStyle | react native 显示数字样式 | ReactNative StyleSheet | - | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| allowEmpty | 是否允许内容为空 | `boolean` | `false` | +| defaultValue | 默认值 | `number \| null` | `0` | +| digits | 格式化到小数点后固定位数,设置为 `0` 表示格式化到整数。配置 `formatter` 时展示会以 `formatter` 为准 | `number` | - | 5.2.0 | +| disabled | 是否禁用步进器 | `boolean` | `false` | +| formatter | 格式化展示数值 | `(value?: number) => string` | - | 5.2.0 | +| inputStyle | TextInput style | `StyleProp` | - | +| max | 最大值 | `number` | - | +| min | 最小值 | `number` | - | +| minusButtonProps | minus 按钮 props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd', children: -, delayLongPress:500 }` | 5.2.0 | +| plusButtonProps | plus 按钮 props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd', children: +, delayLongPress:500 }` | 5.2.0 | +| onChange | 变化时的回调 | `(value: number \| null) => void` | - | +| parser | 将输入解析为对应数字,一般配合 `formatter` 使用 | `(text: string) => number` | - | 5.2.0 | +| step | 每次增加或减少的值,可以为小数 | `number` | `1` | +| stringMode | 字符值模式,开启后支持高精度小数。开启后 `defaultValue`、`value`、`min`、`max`、`onChange` 等都将转换为 `string` 类型 | `boolean` | `false` | 5.2.0 | +| styles | 语义化结构 style | [StepperStyle](#stepperstyle-语义化样式) | - | 5.2.0 | +| value | 当前数,受控值 | `number \| null` | - | + + - `5.2.0`新增。此外还支持 react-native 内置组件 [TextInput](http://facebook.github.io/react-native/docs/textinput.html) 的所有属性,例如:**`readOnly`** **`onFocus`** **`onBlur`** 等。 + + - `5.2.0`新增。支持 **长按持续触发** 递加或递减;可自定义执行时机:`plusButtonProps={{ delayLongPress: 500 }}`。 + + - 当 `allowEmpty` 为 `true` 时,`onChange` 的 `value` 参数可能会为 **`null`**,在使用时请留意。 + +### StepperStyle 语义化样式 + +`5.2.0`重构了样式。 + +```typescript +export interface StepperStyle extends Partial { + // 继承自InputStyle + container: ViewStyle + input: ViewStyle + prefix: ViewStyle // 用于 minus 按钮样式 + suffix: ViewStyle // 用于 plus 按钮样式 + + // StepperStyle + inputDisabled: TextStyle + stepWrap: ViewStyle + stepText: TextStyle + stepDisabled: ViewStyle + disabledStepTextColor: TextStyle +} +``` + +### Ref +同 [components/Input](/components/input-cn#ref) ref \ No newline at end of file diff --git a/components/stepper/stepper.tsx b/components/stepper/stepper.tsx new file mode 100644 index 000000000..29832b718 --- /dev/null +++ b/components/stepper/stepper.tsx @@ -0,0 +1,318 @@ +import getMiniDecimal, { + DecimalClass, + toFixed, +} from '@rc-component/mini-decimal' +import useMergedState from 'rc-util/lib/hooks/useMergedState' +import React, { + forwardRef, + useCallback, + useContext, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react' +import { Text, TextInput, TouchableHighlight } from 'react-native' +import DisabledContext from '../config-provider/DisabledContext' +import Input from '../input' +import { useTheme } from '../style' +import { BaseStepperProps, StepperProps } from './PropsType' +import StepperStyles from './style' + +export function InnerStepper( + props: StepperProps, + ref: React.ForwardedRef, +) { + const contextDisabled = useContext(DisabledContext) + + const { + value: inputValue, + defaultValue = 0 as ValueType, + onChange, + disabled = contextDisabled, + step = 1, + max, + min, + digits, + stringMode, + formatter, + parser, + allowEmpty, + inputStyle, + styles, + minusButtonProps, + plusButtonProps, + ...restInputProps + } = props as BaseStepperProps & { stringMode: boolean } + + // ========================== Parse / Format ========================== + const fixedValue = useCallback( + (value: ValueType): string => { + return ( + digits !== undefined ? toFixed(value.toString(), '.', digits) : value + ).toString() + }, + [digits], + ) + + const getValueAsType = (value: DecimalClass) => + (stringMode ? value.toString() : value.toNumber()) as ValueType + + const parseValue = (text: string): string | null => { + if (text === '') { + return null + } + + if (parser) { + return String(parser(text)) + } + + const decimal = getMiniDecimal(text) + return decimal.isInvalidate() ? null : decimal.toString() + } + + const formatValue = useCallback( + (value: ValueType | null): string => { + if (value === null) { + return '' + } + + return formatter ? formatter(value) : fixedValue(value) + }, + [fixedValue, formatter], + ) + + // ======================== Value & Reducer ======================== + const [mergedValue, setMergedValue] = useMergedState( + defaultValue, + { + value: inputValue, + onChange, + }, + ) + + // >>>>> Value + function setValueWithCheck(nextValue: DecimalClass) { + if (nextValue.isNaN()) { + return + } + + let target = nextValue + + // Put into range + if (min !== undefined) { + const minDecimal = getMiniDecimal(min) + if (target.lessEquals(minDecimal)) { + target = minDecimal + } + } + + if (max !== undefined) { + const maxDecimal = getMiniDecimal(max) + if (maxDecimal.lessEquals(target)) { + target = maxDecimal + } + } + + // Fix digits + if (digits !== undefined) { + target = getMiniDecimal(fixedValue(getValueAsType(target))) + } + + setMergedValue(getValueAsType(target)) + } + + const reducer = ( + state: { value: string }, + action: { type: 'setInputValue' | 'minus' | 'plus'; payload?: string }, + ) => { + if (action.type === 'setInputValue') { + return { value: action.payload } + } + + let stepValue = getMiniDecimal(step) + if (action.type === 'minus') { + stepValue = stepValue.negate() + } + setValueWithCheck( + getMiniDecimal(mergedValue ?? 0).add(stepValue.toString()), + ) + return { state } + } + const [state, dispatch] = useReducer(reducer, { + value: formatValue(mergedValue), + }) + + // >>>>> Input + const handleInputChange = (v: string) => { + dispatch({ type: 'setInputValue', payload: v }) + const valueStr = parseValue(v) + + if (valueStr === null) { + if (allowEmpty) { + setMergedValue(null) + } else { + setMergedValue(defaultValue) + } + } else { + setValueWithCheck(getMiniDecimal(valueStr)) + } + } + + // ============================== Focus =============================== + const [focused, setFocused] = useState(false) + + function triggerFocus(nextFocus: boolean) { + setFocused(nextFocus) + + // We will convert value to original text when focus + if (nextFocus) { + dispatch({ + type: 'setInputValue', + payload: + mergedValue !== null && mergedValue !== undefined + ? String(mergedValue) + : '', + }) + } + } + + // Focus change to format value + useEffect(() => { + if (!focused) { + dispatch({ + type: 'setInputValue', + payload: formatValue(mergedValue), + }) + } + }, [focused, mergedValue, digits, formatValue]) + + // ============================ Operations ============================ + const handleMinus = () => { + dispatch({ type: 'minus' }) + } + + const handlePlus = () => { + dispatch({ type: 'plus' }) + } + + // long press automatic minus/plus + const timer = useRef() + const onLongPressMinus = (time = 1000) => { + dispatch({ type: 'minus' }) + timer.current = setTimeout( + () => onLongPressMinus(time - 400), + Math.max(time, 100), + ) + } + const onLongPressPlus = (time = 1000) => { + dispatch({ type: 'plus' }) + timer.current = setTimeout( + () => onLongPressPlus(time - 400), + Math.max(time, 100), + ) + } + const onPressOut = () => { + timer.current && clearTimeout(timer.current) + } + + useEffect(() => { + return () => { + onPressOut() + } + }, []) + + const minusDisabled = useMemo(() => { + if (disabled) { + return true + } + if (mergedValue === null) { + return false + } + if (min !== undefined) { + return mergedValue <= min + } + return false + }, [disabled, min, mergedValue]) + + const plusDisabled = useMemo(() => { + if (disabled) { + return true + } + if (mergedValue === null) { + return false + } + if (max !== undefined) { + return mergedValue >= max + } + return false + }, [disabled, max, mergedValue]) + + const ss = useTheme({ + styles, + themeStyles: StepperStyles, + }) + + // ============================== Render ============================== + return ( + { + disabled || handleInputChange(val) + }} + onFocus={(e) => { + triggerFocus(true) + props.onFocus?.(e) + }} + onBlur={(e) => { + triggerFocus(false) + props.onBlur?.(e) + }} + prefix={ + onLongPressMinus()} + onPressOut={onPressOut} + disabled={minusDisabled} + activeOpacity={1} + underlayColor="#ddd" + children={ + + - + + } + {...minusButtonProps} + /> + } + suffix={ + onLongPressPlus()} + onPressOut={onPressOut} + disabled={plusDisabled} + activeOpacity={1} + underlayColor="#ddd" + children={ + + + + + } + {...plusButtonProps} + /> + } + /> + ) +} + +export const Stepper = forwardRef(InnerStepper) diff --git a/components/stepper/style/index.tsx b/components/stepper/style/index.tsx index 28417077b..e898b45be 100644 --- a/components/stepper/style/index.tsx +++ b/components/stepper/style/index.tsx @@ -1,55 +1,56 @@ import { StyleSheet, TextStyle, ViewStyle } from 'react-native' +import { InputStyle } from '../../input/style' import { Theme } from '../../style' -export interface StepperStyle { - container: ViewStyle - input: TextStyle +export interface StepperStyle extends Partial { + inputDisabled: TextStyle stepWrap: ViewStyle stepText: TextStyle stepDisabled: ViewStyle disabledStepTextColor: TextStyle - highlightStepTextColor: TextStyle - highlightStepBorderColor: ViewStyle } export default (theme: Theme) => StyleSheet.create({ + // override Input style container: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', + alignItems: 'stretch', + width: 104, }, input: { - flex: 1, - textAlign: 'center', - paddingHorizontal: 8, - fontSize: theme.input_font_size, + fontSize: theme.font_size_base, color: theme.color_text_base, + textAlign: 'center', + backgroundColor: theme.fill_body, + }, + prefix: { + flexShrink: 0, + marginRight: 2, + }, + suffix: { + flexShrink: 0, + marginLeft: 2, + }, + + // Stepper style + inputDisabled: { + opacity: 0.4, }, stepWrap: { width: 28, - height: 28, - borderWidth: theme.border_width_md, - borderColor: theme.border_color_base, - borderRadius: theme.radius_md, - backgroundColor: theme.fill_base, + flex: 1, + justifyContent: 'center', + borderRadius: theme.radius_xs, + backgroundColor: theme.fill_body, }, stepText: { textAlign: 'center', fontSize: 20, - color: theme.color_text_placeholder, + color: theme.color_primary, backgroundColor: 'transparent', }, stepDisabled: { - borderColor: theme.color_text_disabled, - backgroundColor: theme.fill_disabled, + opacity: 0.4, }, disabledStepTextColor: { - color: theme.color_text_disabled, - }, - highlightStepTextColor: { - color: theme.brand_primary, - }, - highlightStepBorderColor: { - borderColor: theme.brand_primary, + color: '#999999', }, }) From 00f620c2748e92aad60bf639e4ec8f6a678f159f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 13:09:11 +0800 Subject: [PATCH 2/9] [5.2.0-rc.1]feat: refactor Toast(support 'position' prop) --- components/toast/PropsType.tsx | 18 ++ components/toast/ToastContainer.tsx | 242 ++++++++++++---------- components/toast/demo/basic.tsx | 300 ++++++++++++++-------------- components/toast/index.en-US.md | 51 ++++- components/toast/index.tsx | 179 ++--------------- components/toast/index.zh-CN.md | 51 ++++- components/toast/methods.tsx | 110 ++++++++++ components/toast/style/index.tsx | 1 - 8 files changed, 513 insertions(+), 439 deletions(-) create mode 100644 components/toast/PropsType.tsx create mode 100644 components/toast/methods.tsx diff --git a/components/toast/PropsType.tsx b/components/toast/PropsType.tsx new file mode 100644 index 000000000..47bc182ef --- /dev/null +++ b/components/toast/PropsType.tsx @@ -0,0 +1,18 @@ +import React from 'react' +import { ToastStyle } from './style' + +export interface ToastProps { + content: string | React.ReactNode + icon?: 'success' | 'fail' | 'offline' | 'loading' | React.ReactNode + duration?: number + mask?: boolean + onAnimationEnd?: () => void + onClose?: () => void + position?: 'top' | 'bottom' | 'center' + styles?: Partial + type?: string +} + +export interface ToastShowProps extends ToastProps { + stackable?: boolean +} diff --git a/components/toast/ToastContainer.tsx b/components/toast/ToastContainer.tsx index 13f831aa0..4719900ac 100644 --- a/components/toast/ToastContainer.tsx +++ b/components/toast/ToastContainer.tsx @@ -1,43 +1,47 @@ -import React from 'react' -import { ActivityIndicator, Animated, Text, View } from 'react-native' +import type { FC } from 'react' +import React, { useEffect, useMemo } from 'react' +import { + ActivityIndicator, + Animated, + Dimensions, + Text, + View, + ViewStyle, +} from 'react-native' +import { mergeProps } from '../_util/with-default-props' import Icon, { IconNames } from '../icon' -import { WithTheme, WithThemeStyles } from '../style' -import ToastStyles, { ToastStyle } from './style/index' +import { useTheme } from '../style' +import { ToastProps } from './PropsType' +import ToastStyles from './style' -export interface ToastProps extends WithThemeStyles { - content: string | React.ReactNode - duration?: number - onClose?: () => void - mask?: boolean - type?: string - onAnimationEnd?: () => void +const defaultProps = { + duration: 3, + mask: true, + onClose() {}, } -export default class ToastContainer extends React.Component { - static defaultProps = { - duration: 3, - mask: true, - onClose() {}, - } +const ToastContainer: FC = (p) => { + const props = mergeProps(defaultProps, p) + const { + icon, + type = '', + mask, + duration, + content, + onAnimationEnd, + onClose, + position, + } = props - anim: Animated.CompositeAnimation | null + const anim = React.useRef() + const fadeAnim = React.useRef(new Animated.Value(0)) - constructor(props: ToastProps) { - super(props) - this.state = { - fadeAnim: new Animated.Value(0), - } - } - - componentDidMount() { - const { onAnimationEnd } = this.props - const duration = this.props.duration as number - const timing = Animated.timing - if (this.anim) { - this.anim = null + useEffect(() => { + if (anim.current) { + anim.current = null } const animArr = [ - timing(this.state.fadeAnim, { + Animated.timing(fadeAnim.current, { toValue: 1, duration: 200, useNativeDriver: true, @@ -46,96 +50,124 @@ export default class ToastContainer extends React.Component { ] if (duration > 0) { animArr.push( - timing(this.state.fadeAnim, { + Animated.timing(fadeAnim.current, { toValue: 0, duration: 200, useNativeDriver: true, }), ) } - this.anim = Animated.sequence(animArr) - this.anim.start(() => { + anim.current = Animated.sequence(animArr) + anim.current.start(() => { if (duration > 0) { - this.anim = null + anim.current = null if (onAnimationEnd) { onAnimationEnd() } } }) - } - componentWillUnmount() { - if (this.anim) { - this.anim.stop() - this.anim = null + return () => { + if (anim.current) { + anim.current.stop() + anim.current = null + } + + if (onClose) { + onClose() + } } - const { onClose } = this.props - if (onClose) { - onClose() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const styles = useTheme({ + styles: props.styles, + themeStyles: ToastStyles, + }) + + const iconDom = useMemo(() => { + let typeTemp = type + if (type === 'info' && typeof icon === 'string') { + if (['success', 'fail', 'offline', 'loading'].includes(icon)) { + typeTemp = icon + } } - } + switch (typeTemp) { + case 'loading': + return ( + + ) + case 'info': + return icon + default: + const iconType: { + [key: string]: IconNames + } = { + success: 'check-circle', + fail: 'close-circle', + offline: 'frown', + } + return ( + + ) + } + }, [icon, styles.centering, styles.image, type]) - render() { - const { type = '', content, mask } = this.props - return ( - - {(styles) => { - const iconType: { - [key: string]: IconNames - } = { - success: 'check-circle', - fail: 'close-circle', - offline: 'frown', - } + const positionStyle: ViewStyle = useMemo(() => { + const { height } = Dimensions.get('window') + const offset = height * 0.2 - let iconDom: React.ReactElement | null = null - if (type === 'loading') { - iconDom = ( - - ) - } else if (type === 'info') { - iconDom = null - } else { - iconDom = ( - - ) - } + switch (position) { + case 'top': + return { + justifyContent: 'flex-start', + marginTop: offset, + } + case 'bottom': + return { + justifyContent: 'flex-end', + marginBottom: offset, + } + default: + return { + justifyContent: 'center', + } + } + }, [position]) - return ( - - - - - {iconDom} - {React.isValidElement(content) ? ( - content - ) : ( - {content} - )} - - - - - ) - }} - - ) - } + return ( + + + + + {iconDom} + {React.isValidElement(content) ? ( + content + ) : ( + {content} + )} + + + + + ) } + +export default ToastContainer diff --git a/components/toast/demo/basic.tsx b/components/toast/demo/basic.tsx index c358ecc47..514883103 100644 --- a/components/toast/demo/basic.tsx +++ b/components/toast/demo/basic.tsx @@ -1,161 +1,155 @@ -/* tslint:disable:no-console */ -import React from 'react' -import { DeviceEventEmitter, Text } from 'react-native' -import { Button, List, Switch, Toast, WhiteSpace, WingBlank } from '../../' +import React, { useEffect, useRef, useState } from 'react' +import { ActivityIndicator, Text } from 'react-native' +import { ScrollView } from 'react-native-gesture-handler' +import { Button, List, Switch, Toast } from '../../' -function showToastStack() { - // multiple toast - Toast.fail({ - content: 'This is a toast tips 1 !!!', - duration: 3, - stackable: true, - }) - Toast.success({ - content: 'This is a toast tips 2 !!!', - duration: 2, - stackable: true, - }) - Toast.info({ - content: 'This is a toast tips 3 !!!', - duration: 1, - stackable: true, - }) -} - -function infoToast() { - Toast.info({ - content: 'Text toast', - }) -} - -function successToast() { - Toast.success('Load success !!!', 1) -} - -function showToastNoMask() { - Toast.info({ - content: 'Toast without mask', - mask: false, - }) -} - -function failToast() { - Toast.fail('Load failed !!!') -} - -function offline() { - Toast.offline('Network connection failed !!!') -} - -function loadingToast() { - Toast.loading({ - content: 'Loading...', - duration: 1, - onClose: () => console.log('Load complete !!!'), - }) -} - -function alwaysShowToastInfo() { - const key = Toast.info('Toast with duration = 0, removed by timer', 0, () => { - Toast.info('Toast.info onClose callback called!') - }) - setTimeout(() => { - Toast.remove(key) - }, 3000) -} - -function alwaysShowToastLoading() { - Toast.loading('Loading...', 0, () => { - Toast.info('Toast.loading onClose callback called!') - }) - setTimeout(() => { - Toast.removeAll() - }, 3000) -} +const ToastExample = () => { + const handler = useRef() + const [enableMask, setEnableMask] = useState(Toast.getConfig().mask) + const [enableStack, setEnableStack] = useState(Toast.getConfig().stackable) -function showCustomViewToast() { - Toast.info( - { content: Toast Custom View }, - 2, + return ( + + + { + Toast.config({ mask }) + setEnableMask(Toast.getConfig().mask) + }} + /> + }> + Enable mask + 是否显示透明蒙层,防止触摸穿透 + + { + Toast.config({ stackable }) + setEnableStack(Toast.getConfig().stackable) + }} + /> + }> + Enable stackable + 是否允许叠加显示 + + + + + + + + + + + + + + + + + + + + ) - setTimeout(() => { - Toast.success( - { content: Toast Custom View }, - 2, - ) - }, 2500) } -export default class ToastExample extends React.Component { - timer: any +export default ToastExample - state = { - enableMask: Toast.getConfig().mask, - enableStack: Toast.getConfig().stackable, - } - - componentWillUnmount() { - DeviceEventEmitter.removeAllListeners('navigatorBack') - if (this.timer) { - clearTimeout(this.timer) - this.timer = null +const CountDownText = () => { + const [count, setCount] = useState(5) + const interval = useRef() + useEffect(() => { + interval.current = setInterval(() => { + setCount((x) => { + if (x > 1) { + return x - 1 + } else { + return x + } + }) + }, 1000) + return () => { + interval.current && clearInterval(interval.current) } - } - - render() { - return ( - - - { - Toast.config({ mask }) - this.setState({ enableMask: Toast.getConfig().mask }) - }} - /> - }> - Enable Mask - - { - Toast.config({ stackable }) - this.setState({ enableStack: Toast.getConfig().stackable }) - }} - /> - }> - Enable Stack - - - - - - - - - - - - - - - - - - - - - - - - ) - } + }, []) + return 还剩 {count} 秒 } diff --git a/components/toast/index.en-US.md b/components/toast/index.en-US.md index b3e4eac57..f6bc39812 100644 --- a/components/toast/index.en-US.md +++ b/components/toast/index.en-US.md @@ -19,13 +19,16 @@ A lightweight feedback or tips, used to display content that does not interrupt Props has these fields: -| Properties | Descrition | Type | Required | Default | -| ---------- | ------------------------------------------------------------------------------------ | ----------------------- | ------- | ------- | -| content | Toast content | `String | React.ReactNode` | Yes | - | -| duration | Delay time to close, which units is second | number | No | 3 | -| onClose | A callback function Triggered when the Toast is closed | Function | No | - | -| mask | Whether to show a transparent mask, which will prevent touch event of the whole page | Boolean | No | true | -| stackable | Whether to allow toast overlay | Boolean | No | true | +| Properties | Descrition | Type | Required | Default | Version | +| ---------- | ---------- | -----| -----------| --------|---------| +| content | Toast content | `String | React.ReactNode` | Yes | - | | +| duration | Delay time to close, which units is second | number | No | 3 | | +| icon | Toast icon | `'success' | 'fail' | 'offline' | 'loading' | React.ReactNode` | No | - | `5.2.0` | +| mask | Whether to show a transparent mask, which will prevent touch event of the whole page | Boolean | No | true | | +| onClose | A callback function Triggered when the Toast is closed | Function | No | - | | +| position | Vertical display position | `'top' | 'bottom' | 'center'` | No | `'center'` | `5.2.0` | +| stackable | Whether to allow toast overlay | Boolean | No | true | | +| styles | Semantic DOM style | [ToastStyle](#toaststyle-interface) | No | - | `5.2.0` | > **Notice:** OnClose is invalid and Toast does not hide, If set duration = 0, toast will not auto hide, you have to manually do it. @@ -34,12 +37,38 @@ import { Toast } from '@ant-design/react-native'; const key = Toast.loading('message'); Toast.remove(key); +``` + +### Toast.removeAll -// Or force close all toasts +Turn off `Toast` in all displays. + +```ts Toast.removeAll() ``` -### Configuration +### Toast.config + +Methods for global configuration. Support `duration`、`mask`、`onClose`、`position`、`stackable` and `style`. The configuration method is as follows: + +```ts +Toast.config({ duration: 1, position: 'top' }) + +// get current config +Toast.getConfig() +``` + +### InputStyle interface -- `Toast.getConfig()` - get current config -- `Toast.config(props)` - customize default value for NOT required parameters +```typescript +interface ToastStyle { + container: ViewStyle + innerContainer: ViewStyle + innerWrap: ViewStyle + iconToast: ViewStyle + textToast: ViewStyle + content: TextStyle + image: TextStyle + centering: ViewStyle +} +``` \ No newline at end of file diff --git a/components/toast/index.tsx b/components/toast/index.tsx index 3d86eb7c4..92ff6ad7a 100644 --- a/components/toast/index.tsx +++ b/components/toast/index.tsx @@ -1,166 +1,29 @@ -import React from 'react' -import Portal from '../portal' -import ToastContainer from './ToastContainer' - -interface IToastConfigurable { - duration?: number - onClose?: () => void - mask?: boolean - stackable?: boolean -} - -interface IToastProps extends IToastConfigurable { - content: string | React.ReactNode -} - -const SHORT = 3 - -const defaultConfig: IToastConfigurable = { - duration: SHORT, - onClose: () => {}, - mask: true, - stackable: true, -} - -let defaultProps = { - ...defaultConfig, -} - -const toastKeyMap: { [key: number]: 1 } = {} - -function remove(key: number) { - Portal.remove(key) - delete toastKeyMap[key] -} - -function removeAll() { - Object.keys(toastKeyMap).forEach((_key) => - Portal.remove(Number.parseInt(_key, 10)), - ) -} - -function notice( - content: string | IToastProps, - type: string, - duration = defaultProps.duration, - onClose = defaultProps.onClose, - mask = defaultProps.mask, -) { - let props = { - ...defaultProps, - content: content as string | React.ReactNode, - type, - duration, - onClose, - mask, - } - - if (typeof content !== 'string') { - props = { - ...props, - ...content, - } - } - - if (!props.stackable) { - removeAll() - } +import { + SHORT, + config, + defaultConfig, + getConfig, + methods, + remove, + removeAll, +} from './methods' - const key = Portal.add( - { - remove(key) - }} - />, - ) - toastKeyMap[key] = 1 - return key -} +export type { ToastProps, ToastShowProps } from './PropsType' -export default { +const Toast = { SHORT, LONG: 8, defaultConfig, - getConfig: () => { - return { ...defaultProps } - }, - config(props: IToastConfigurable) { - defaultProps = { - ...defaultProps, - ...props, - } - }, - /** - * @deprecated use Toast.info instead - */ - show(props: string | IToastProps, duration?: number, mask?: boolean) { - return notice(props, 'info', duration, () => {}, mask) - }, - /** - * - * @param props: toast props - */ - info( - props: string | IToastProps, - duration?: number, - onClose?: () => void, - mask?: boolean, - ) { - return notice(props, 'info', duration, onClose, mask) - }, - /** - * - * @param props: toast props - */ - success( - props: string | IToastProps, - duration?: number, - onClose?: () => void, - mask?: boolean, - ) { - return notice(props, 'success', duration, onClose, mask) - }, - /** - * - * @param props: toast props - */ - fail( - props: string | IToastProps, - duration?: number, - onClose?: () => void, - mask?: boolean, - ) { - return notice(props, 'fail', duration, onClose, mask) - }, - /** - * - * @param props: toast props - */ - offline( - props: string | IToastProps, - duration?: number, - onClose?: () => void, - mask?: boolean, - ) { - return notice(props, 'offline', duration, onClose, mask) - }, - /** - * - * @param props: toast props - */ - loading( - props: string | IToastProps, - duration?: number, - onClose?: () => void, - mask?: boolean, - ) { - return notice(props, 'loading', duration, onClose, mask) - }, + getConfig, + config, + show: methods.show, + info: methods.info, + success: methods.success, + fail: methods.fail, + offline: methods.offline, + loading: methods.loading, remove, removeAll, } + +export default Toast diff --git a/components/toast/index.zh-CN.md b/components/toast/index.zh-CN.md index 74573a6cb..c32677815 100644 --- a/components/toast/index.zh-CN.md +++ b/components/toast/index.zh-CN.md @@ -20,13 +20,16 @@ subtitle: 轻提示 Props 参数如下: -| 属性 | 说明 | 类型 | 必填 | 默认值 | -| -------- | ------------------------------ | -------- | --- | ------ | -| content | 提示内容 | `String | React.ReactNode` | 是 | - | -| duration | 自动关闭的延时,单位秒 | number | 否 | 3 | -| onClose | 关闭后回调 | Function | 否 | - | -| mask | 是否显示透明蒙层,防止触摸穿透 | Boolean | 否 | true | -| stackable | 是否允许叠加显示 | Boolean | 否 | true | +| 属性 | 说明 | 类型 | 必填 | 默认值 | 版本 | +| -------- | ------------------------------ | -------- | --- | ------ | ------| +| content | 提示内容 | `String | React.ReactNode` | 是 | - || +| duration | 自动关闭的延时,单位秒 | number | 否 | 3 || +| icon | 图标 | `'success' | 'fail' | 'offline' | 'loading' | React.ReactNode` | 否 | - | `5.2.0` | +| mask | 是否显示透明蒙层,防止触摸穿透 | Boolean | 否 | true || +| position | 垂直方向显示位置 | `'top' | 'bottom' | 'center'` | 否 | `'center'` | `5.2.0` | +| onClose | 关闭后回调 | Function | 否 | - || +| stackable | 是否允许叠加显示 | Boolean | 否 | true || +| styles | 语义化结构 style | [ToastStyle](#toaststyle-语义化样式) | 否 | - | `5.2.0` | > **注:** duration = 0 时,onClose 无效,toast 不会消失,隐藏 toast 需要手动调用 remove @@ -35,12 +38,38 @@ import { Toast } from '@ant-design/react-native'; const key = Toast.loading('message'); Toast.remove(key); +``` + +### Toast.removeAll -// 或者强制关闭所有Toast +关闭所有显示中的 `Toast`。 + +```ts Toast.removeAll() ``` -### 自定义配置 +### Toast.config + +全局配置,支持配置 `duration`、`mask`、`onClose`、`position`、`stackable` 和 `styles`。配置方法如下: + +```ts +Toast.config({ duration: 1, position: 'top' }) + +// 可获取当前配置 +Toast.getConfig() +``` + +### ToastStyle 语义化样式 -- `Toast.getConfig()` - 获取当前配置 -- `Toast.config(props)` - 配置非必填项的默认值 +```typescript +interface ToastStyle { + container: ViewStyle + innerContainer: ViewStyle + innerWrap: ViewStyle + iconToast: ViewStyle + textToast: ViewStyle + content: TextStyle + image: TextStyle + centering: ViewStyle +} +``` \ No newline at end of file diff --git a/components/toast/methods.tsx b/components/toast/methods.tsx new file mode 100644 index 000000000..d33405659 --- /dev/null +++ b/components/toast/methods.tsx @@ -0,0 +1,110 @@ +import React from 'react' +import Portal from '../portal' +import { ToastShowProps } from './PropsType' +import ToastContainer from './ToastContainer' + +export const SHORT = 3 + +// editable default props +let defaultProps = { + duration: SHORT, + onClose: () => {}, + mask: true, + stackable: true, +} + +// editable portal key map +const toastKeyMap: { [key: number]: 1 } = {} + +export function remove(key: number) { + Portal.remove(key) + delete toastKeyMap[key] +} + +export function removeAll() { + Object.keys(toastKeyMap).forEach((_key) => + Portal.remove(Number.parseInt(_key, 10)), + ) +} + +// Read-only +export const defaultConfig = { ...defaultProps } + +// Read-only +export function getConfig() { + return { ...defaultProps } +} + +// edit defaultProps +export function config(props: Partial) { + defaultProps = { + ...defaultProps, + ...props, + } +} + +export function notice( + content: string | ToastShowProps, + type: string, + duration = defaultProps.duration, + onClose = defaultProps.onClose, + mask = defaultProps.mask, +) { + let props = { + ...defaultProps, + content: content as string | React.ReactNode, + type, + duration, + onClose, + mask, + } + + if (typeof content !== 'string') { + props = { + ...props, + ...content, + } + } + + if (!props.stackable) { + removeAll() + } + + const key = Portal.add( + { + remove(key) + }} + />, + ) + toastKeyMap[key] = 1 + return key +} + +// Compatible with old versions +function base(type: string) { + return ( + props: string | ToastShowProps, + duration?: number, + onClose?: () => void, + mask?: boolean, + ) => notice(props, type, duration, onClose, mask) +} + +const show = ( + props: string | ToastShowProps, + duration?: number, + mask?: boolean, +) => { + return notice(props, 'info', duration ?? 1.5, () => {}, mask) +} + +export const methods = { + show, + info: base('info'), + success: base('success'), + fail: base('fail'), + offline: base('offline'), + loading: base('loading'), +} diff --git a/components/toast/style/index.tsx b/components/toast/style/index.tsx index 4e399913f..911ad0e76 100644 --- a/components/toast/style/index.tsx +++ b/components/toast/style/index.tsx @@ -21,7 +21,6 @@ export default (theme: Theme) => bottom: 0, right: 0, backgroundColor: 'transparent', - justifyContent: 'center', alignItems: 'center', zIndex: theme.toast_zindex, }, From f489134562bce3cb36bc5d4d4f26e8a1627224b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 16:11:08 +0800 Subject: [PATCH 3/9] [5.2.0-rc.1]feat: refactor SwipeAction(support 'closeOnTouchOutside' prop) --- components/_util/hooks/useClickAway.ts | 30 ++++ components/portal/portal-host.tsx | 25 ++- components/swipe-action/PropsType.tsx | 29 ++++ components/swipe-action/RenderActions.tsx | 149 +++++++++++++++++ components/swipe-action/demo/basic.tsx | 28 +++- components/swipe-action/index.en-US.md | 64 +++++-- components/swipe-action/index.tsx | 193 +--------------------- components/swipe-action/index.zh-CN.md | 63 +++++-- components/swipe-action/style/index.tsx | 22 +++ components/swipe-action/swipe-action.tsx | 108 ++++++++++++ 10 files changed, 477 insertions(+), 234 deletions(-) create mode 100644 components/_util/hooks/useClickAway.ts create mode 100644 components/swipe-action/PropsType.tsx create mode 100644 components/swipe-action/RenderActions.tsx create mode 100644 components/swipe-action/style/index.tsx create mode 100644 components/swipe-action/swipe-action.tsx diff --git a/components/_util/hooks/useClickAway.ts b/components/_util/hooks/useClickAway.ts new file mode 100644 index 000000000..cd59cc3cc --- /dev/null +++ b/components/_util/hooks/useClickAway.ts @@ -0,0 +1,30 @@ +import { useEffect, useMemo, useRef } from 'react' +import { + DeviceEventEmitter, + EmitterSubscription, + GestureResponderEvent, + NativeEventEmitter, +} from 'react-native' + +export const USE_CLICK_AWAY_EVENT_NAME = 'ANT_DESIGN_MOBILE_RN_USE_CLICK_AWAY' + +export default function useClickAway( + onClickAway: (event: GestureResponderEvent) => void, +) { + const TopViewEventEmitter = useMemo( + () => DeviceEventEmitter || new NativeEventEmitter(), + [], + ) + + const onClickAwayRef = useRef() + + useEffect(() => { + onClickAwayRef.current = TopViewEventEmitter.addListener( + USE_CLICK_AWAY_EVENT_NAME, + onClickAway, + ) + return () => { + onClickAwayRef.current?.remove?.() + } + }, [TopViewEventEmitter, onClickAway]) +} diff --git a/components/portal/portal-host.tsx b/components/portal/portal-host.tsx index 5678b506e..440b37855 100644 --- a/components/portal/portal-host.tsx +++ b/components/portal/portal-host.tsx @@ -2,15 +2,16 @@ import React from 'react' import { DeviceEventEmitter, EventSubscription, + GestureResponderEvent, NativeEventEmitter, + ScrollView, + ScrollViewProps, StyleSheet, - View, } from 'react-native' +import { USE_CLICK_AWAY_EVENT_NAME } from '../_util/hooks/useClickAway' import PortalManager from './portal-manager' -export type PortalHostProps = { - children: React.ReactNode -} +export type PortalHostProps = ScrollViewProps export type Operation = | { type: 'mount'; key: number; children: React.ReactNode } @@ -149,6 +150,11 @@ export default class PortalHost extends React.Component { } } + // coordinate for hook `useClickAway` + _onTouchEnd = (event: GestureResponderEvent) => { + TopViewEventEmitter.emit(USE_CLICK_AWAY_EVENT_NAME, event) + } + render() { return ( { unmount: this._unmount, }}> {/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */} - - {this.props.children} - + ) diff --git a/components/swipe-action/PropsType.tsx b/components/swipe-action/PropsType.tsx new file mode 100644 index 000000000..e1fd8d680 --- /dev/null +++ b/components/swipe-action/PropsType.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { StyleProp, TextStyle } from 'react-native' +import { RectButtonProps } from 'react-native-gesture-handler' +import { SwipeableProps } from 'react-native-gesture-handler/Swipeable' +import { SwipeActionStyle } from './style' + +export interface SwipeoutButtonProps { + actionButtonProps?: RectButtonProps + backgroundColor?: string + color?: string + disabled?: boolean + onPress?(): void | Promise + style?: StyleProp + text?: React.ReactNode +} + +export interface SwipeActionProps extends SwipeableProps { + closeOnAction?: boolean + closeOnTouchOutside?: boolean + left?: SwipeoutButtonProps[] + right?: SwipeoutButtonProps[] + /** + * @deprecated No longer needed + * @version `5.2.0` + */ + buttonWidth?: number + children?: React.ReactNode + styles?: Partial +} diff --git a/components/swipe-action/RenderActions.tsx b/components/swipe-action/RenderActions.tsx new file mode 100644 index 000000000..35a35b646 --- /dev/null +++ b/components/swipe-action/RenderActions.tsx @@ -0,0 +1,149 @@ +import React, { memo, useCallback, useState } from 'react' +import { + Animated, + I18nManager, + LayoutChangeEvent, + Text, + View, +} from 'react-native' +import { RectButton } from 'react-native-gesture-handler' +import { useTheme } from '../style' +import { SwipeoutButtonProps } from './PropsType' +import SwipeActionStyles, { SwipeActionStyle } from './style' + +interface RenderActionsProps { + buttons?: SwipeoutButtonProps[] + closeOnAction: boolean + isLeft: boolean + progressAnimatedValue: Animated.AnimatedInterpolation + setOpenRef: (open: boolean) => void + setClose: () => void + styles?: Partial +} + +interface SwipeActionButtonProps extends Omit { + button: SwipeoutButtonProps + buttonWidth: number +} + +export const RenderActions: React.FC = memo((props) => { + const { buttons, ...restProps } = props + + const [buttonWidth, setButtonWidth] = useState(0) + + const onLayout = (e: LayoutChangeEvent) => { + setButtonWidth(e.nativeEvent.layout.width) + } + + if (!Array.isArray(buttons) || buttons.length === 0) { + return null + } + return ( + + {buttons.map((button, i) => { + return ( + + ) + })} + + ) +}) + +const SwipeActionButton: React.FC = memo((props) => { + const [loading, setLoading] = useState(false) + + const { + button, + buttonWidth, + closeOnAction, + isLeft, + progressAnimatedValue, + setOpenRef, + setClose, + styles, + } = props + + const pressHandler = useCallback(async () => { + if (button.disabled || loading) { + return + } + // Forced not to be closed + if (!closeOnAction) { + setOpenRef(false) + } + try { + setLoading(true) + if (button.onPress) { + await button.onPress() + } + } finally { + setTimeout(() => { + if (closeOnAction) { + setClose() + } + setLoading(false) + }) + } + }, [button, closeOnAction, loading, setClose, setOpenRef]) + + const [layout, setLayout] = useState<{ x: number; width: number }>({ + x: 0, + width: 0, + }) + const onLayout = (e: LayoutChangeEvent) => { + const { x, width } = e.nativeEvent.layout + setLayout({ x, width }) + } + + const ss = useTheme({ + styles, + themeStyles: SwipeActionStyles, + }) + + const trans = progressAnimatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [isLeft ? -1 * layout.width : buttonWidth - layout.x, 0], + extrapolate: 'clamp', + }) + + return ( + + + {React.isValidElement(button.text) ? ( + button.text + ) : ( + + {button.text} + + )} + + + ) +}) diff --git a/components/swipe-action/demo/basic.tsx b/components/swipe-action/demo/basic.tsx index 771a2fbb3..6ba544500 100644 --- a/components/swipe-action/demo/basic.tsx +++ b/components/swipe-action/demo/basic.tsx @@ -4,11 +4,22 @@ import { View } from 'react-native' import { List, SwipeAction } from '../../' export default class BasicSwipeActionExample extends React.Component { + asyncFunction = () => { + return new Promise((resolve) => { + setTimeout(() => { + console.log('asd') + resolve(123) + }, 1500) + }) + } + render() { const right = [ { text: 'More', - onPress: () => console.log('more'), + onPress: async () => { + await this.asyncFunction() + }, backgroundColor: 'orange', color: 'white', }, @@ -40,9 +51,22 @@ export default class BasicSwipeActionExample extends React.Component { + + Simple example: left and right buttons + + + + + console.log('open')} onSwipeableClose={() => console.log('close')}> - + Simple example: left and right buttons diff --git a/components/swipe-action/index.en-US.md b/components/swipe-action/index.en-US.md index 7389af0d5..58e3ff436 100644 --- a/components/swipe-action/index.en-US.md +++ b/components/swipe-action/index.en-US.md @@ -18,20 +18,50 @@ Call out operations from one side of screen with gesture. ### SwipeAction -Properties | Descrition | Type | Default ------------|------------|------|-------- -| style | style for `swipeout` | Object | | -| left | left buttons for `swipeout` | Array | `null` | -| right | right buttons for `swipeout` | Array | `null` | -| autoClose | auto hide after button is pressed | Boolean | `function() {}` | -| onOpen | callback function that is triggered when the buttons will be opened | (): void | `function() {}` | -| disabled | whether is disabled | Boolean | `false` | -| onClose | callback function that is triggered when the buttons will be closed | (): void | `function() {}` | - -### Button - -| Properties | Descrition | Type | Default | -|------|------------------|-------------------------|--------| -| text | text of button | String | `Click` | -| style | style of button | Object | `` | -| onPress | callback function that is triggered when button will be pressed | (): void | `function() {}` | +| Properties | Descrition | Type | Default | Version | +|-----|-----|------|-------|------| +| closeOnAction | Whether to return to the position automatically when the operation button is clicked | `boolean` | `true` | `5.2.0` | +| closeOnTouchOutside | Whether to return to the position automatically when other areas is clicked | `boolean` | `false` | `5.2.0` | +| left | List of operation buttons on the left | [SwipeoutButtonProps](/components/swipe-action#swipeoutbuttonprops)[] | `[]` | | +| right | List of operation buttons on the right | [SwipeoutButtonProps](/components/swipe-action#swipeoutbuttonprops)[] | `[]` | | +| styles | Semantic DOM style | [SwipeActionStyle](/components/swipe-action#swipeactionstyle-interface) | - | `5.2.0` | + +The rest of the props of `SwipeAction` are exactly the same as [react-native-gesture-handler/Swipeable](https://docs.swmansion.com/react-native-gesture-handler/docs/components/swipeable/), + +**eg: `onSwipeableOpen` , `onSwipeableClose` , `renderLeftActions` , `renderRightActions`** + + +When you set `renderLeftActions` prop, it will override `left` prop;
+when you set `renderRightActions` prop, it will override `right` prop. + +### SwipeoutButtonProps + +| Properties | Descrition | Type | Default | Version | +|-----|------|------|------|------| +| backgroundColor | background color | `string` | - | | +| color | font color | `string` | - | | +| disabled | Whether disabled | `boolean` | - | | +| onPress | Trigger when clicked | `() => void | Promise` | - | `5.2.0` support async | +| style | Aaction button style, effective when `text` is `string` | `StyleProp` | - | | +| text | Text | `string | ReactNode` | - | | +| actionButtonProps | Rest props | [RectButtonProps](https://docs.swmansion.com/react-native-gesture-handler/docs/components/buttons/#rectbutton) | - | `5.2.0` | + +### SwipeActionStyle interface + +```typescript +export interface SwipeActionStyle { + actionButton: ViewStyle + actionText: TextStyle +} +``` + +### Ref + +New in `5.2.0`. Ref to Swipeable[#Ref](https://docs.swmansion.com/react-native-gesture-handler/docs/components/swipeable/#methods) + +| Properties | Descrition | Type| +|-----|------|------| +| close | method that closes component | `() => void` | +| openLeft | method that opens component on left side. | `() => void` | +| openRight | method that opens component on right side. | `() => void` | +| reset | method that resets the swiping states of this `Swipeable` component.
Unlike method `close`, this method does not trigger any animation. | `() => void` | \ No newline at end of file diff --git a/components/swipe-action/index.tsx b/components/swipe-action/index.tsx index d881a6038..143da788c 100644 --- a/components/swipe-action/index.tsx +++ b/components/swipe-action/index.tsx @@ -1,194 +1,5 @@ -import React from 'react' -import { - Animated, - I18nManager, - StyleProp, - StyleSheet, - Text, - TextStyle, - View, - ViewStyle, -} from 'react-native' -import { - PanGestureHandlerProps, - RectButton, -} from 'react-native-gesture-handler' -import Swipeable from 'react-native-gesture-handler/Swipeable' +import { SwipeAction } from './swipe-action' -declare type SwipeableExcludes = Exclude< - keyof PanGestureHandlerProps, - 'onGestureEvent' | 'onHandlerStateChange' -> -interface SwipeableProps - extends Pick { - enableTrackpadTwoFingerGesture?: boolean - friction?: number - leftThreshold?: number - rightThreshold?: number - overshootLeft?: boolean - overshootRight?: boolean - overshootFriction?: number - onSwipeableLeftOpen?: () => void - onSwipeableRightOpen?: () => void - onSwipeableOpen?: () => void - onSwipeableClose?: () => void - onSwipeableLeftWillOpen?: () => void - onSwipeableRightWillOpen?: () => void - onSwipeableWillOpen?: () => void - onSwipeableWillClose?: () => void - /** - * - * This map describes the values to use as inputRange for extra interpolation: - * AnimatedValue: [startValue, endValue] - * - * progressAnimatedValue: [0, 1] - * dragAnimatedValue: [0, +] - * - * To support `rtl` flexbox layouts use `flexDirection` styling. - * */ - renderLeftActions?: ( - progressAnimatedValue: Animated.AnimatedInterpolation, - dragAnimatedValue: Animated.AnimatedInterpolation, - ) => React.ReactNode - /** - * - * This map describes the values to use as inputRange for extra interpolation: - * AnimatedValue: [startValue, endValue] - * - * progressAnimatedValue: [0, 1] - * dragAnimatedValue: [0, -] - * - * To support `rtl` flexbox layouts use `flexDirection` styling. - * */ - renderRightActions?: ( - progressAnimatedValue: Animated.AnimatedInterpolation, - dragAnimatedValue: Animated.AnimatedInterpolation, - ) => React.ReactNode - useNativeAnimations?: boolean - animationOptions?: Record - containerStyle?: StyleProp - childrenContainerStyle?: StyleProp -} - -export interface SwipeActionProps extends SwipeableProps { - left?: SwipeoutButtonProps[] - right?: SwipeoutButtonProps[] - buttonWidth?: number - children?: React.ReactNode -} -export interface SwipeoutButtonProps { - style?: StyleProp - backgroundColor?: string - color?: string - text?: React.ReactNode - disabled?: boolean - onPress?(): void -} -class SwipeAction extends React.Component { - swipeableRow?: Swipeable - - render() { - const { left, right, children, ...restProps } = this.props - - return ( - this.renderActions(v, d, true)} - renderRightActions={(v, d) => this.renderActions(v, d, false)} - {...restProps}> - {children} - - ) - } - - updateRef = (ref: Swipeable) => { - this.swipeableRow = ref - } - close = () => { - this.swipeableRow?.close() - } - - renderActions = ( - progress: Animated.AnimatedInterpolation, - _dragAnimatedValue: Animated.AnimatedInterpolation, - isLeft = false, - ) => { - const { right, left, buttonWidth = 60 } = this.props - const buttons = isLeft ? left : right - if (!buttons) { - return null - } - const len = buttons.length - const width = buttonWidth * len - return ( - - {buttons.map((button, i) => { - const x = isLeft ? -i * buttonWidth : (len - i) * buttonWidth - const trans = progress.interpolate({ - inputRange: [0, 1], - outputRange: [x, 0], - extrapolate: 'clamp', - }) - const pressHandler = () => { - if (button.disabled) { - return - } - this.close() - if (button.onPress) { - button.onPress() - } - } - return ( - - - {React.isValidElement(button.text) ? ( - button.text - ) : ( - - {button.text} - - )} - - - ) - })} - - ) - } -} +export type { SwipeActionProps } from './PropsType' export default SwipeAction - -const styles = StyleSheet.create({ - actionText: { - color: 'white', - fontSize: 16, - backgroundColor: 'transparent', - padding: 10, - }, - rightAction: { - alignItems: 'center', - flex: 1, - justifyContent: 'center', - }, -}) diff --git a/components/swipe-action/index.zh-CN.md b/components/swipe-action/index.zh-CN.md index 76acf107d..c883fe757 100644 --- a/components/swipe-action/index.zh-CN.md +++ b/components/swipe-action/index.zh-CN.md @@ -18,20 +18,49 @@ subtitle: 滑动操作 ### SwipeAction -属性 | 说明 | 类型 | 默认值 -----|-----|------|------ -| style | `swipeout` 样式 | Object | | -| left | 左侧按钮组 | Array | `null` | -| right | 右侧按钮组 | Array | `null` | -| autoClose | 点击按钮后自动隐藏按钮 | Boolean | `function() {}` | -| onOpen | 打开时回调函数 | (): void | `function() {}` | -| disabled | 禁用 `swipeout` | Boolean | `false` | -| onClose | 关闭时回调函数 | (): void | `function() {}` | - -### Button - -| 参数 | 说明 | 类型 | 默认值 | -|------|------------------|-------------------------|--------| -| text | 按钮文案 | String | `Click` | -| style | 按钮样式 | Object | `` | -| onPress | 按钮点击事件 | (): void | `function() {}` | +| 属性 | 说明 | 类型 | 默认值 | 版本 | +|-----|-----|------|-------|------| +| closeOnAction | 点击按钮后自动隐藏按钮 | `boolean` | `true` | `5.2.0` | +| closeOnTouchOutside | 是否在点击其他区域时自动归位 | `boolean` | `false` | `5.2.0` | +| left | 左侧按钮组 | [SwipeoutButtonProps](/components/swipe-action-cn#swipeoutbuttonprops)[] | `[]` | | +| right | 右侧按钮组 | [SwipeoutButtonProps](/components/swipe-action-cn#swipeoutbuttonprops)[] | `[]` | | +| styles | 语义化结构 style | [SwipeActionStyle](/components/swipe-action-cn#swipeactionstyle-语义化样式) | - | `5.2.0` | + +SwipeAction 剩余其他属性和 [react-native-gesture-handler/Swipeable](https://docs.swmansion.com/react-native-gesture-handler/docs/components/swipeable/) 一致, + +**例如: `onSwipeableOpen` 、`onSwipeableClose`**, + + +**其中,设置 `renderLeftActions` 属性时会覆盖 `left` ,设置 `renderRightActions` 属性时会覆盖 `right`**。 + +### SwipeoutButtonProps + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +|-----|------|------|------|------| +| backgroundColor | 背景色 | `string` | - | | +| color | 字体颜色 | `string` | - | | +| disabled | 是否禁用 | `boolean` | - | | +| onPress | 按钮点击事件 | `() => void | Promise` | - | `5.2.0`支持异步 | +| style | 按钮样式,当`text`为`string`时生效 | `StyleProp` | - | | +| text | 按钮文案 | `string | ReactNode` | - | | +| actionButtonProps | 其他额外props | [RectButtonProps](https://docs.swmansion.com/react-native-gesture-handler/docs/components/buttons/#rectbutton) | - | `5.2.0` | + +### SwipeActionStyle 语义化样式 + +```typescript +export interface SwipeActionStyle { + actionButton: ViewStyle + actionText: TextStyle +} +``` + +### Ref + +`5.2.0`新增。 指向 Swipeable[#Ref](https://docs.swmansion.com/react-native-gesture-handler/docs/components/swipeable/#methods) + +| 参数 | 说明 | 类型 | +|-----|------|------| +| close | 让滑动条归位 | `() => void` | +| openLeft | 滑动出左侧操作按钮 | `() => void` | +| openRight | 滑动出右侧操作按钮 | `() => void` | +| reset | 重置此 Swipeable 组件的滑动状态的方法。
与方法 `close` 不同,此方法不会触发任何动画。 | `() => void` | diff --git a/components/swipe-action/style/index.tsx b/components/swipe-action/style/index.tsx new file mode 100644 index 000000000..06cac3ef1 --- /dev/null +++ b/components/swipe-action/style/index.tsx @@ -0,0 +1,22 @@ +import { StyleSheet, TextStyle, ViewStyle } from 'react-native' +import { Theme } from '../../style' + +export interface SwipeActionStyle { + actionButton: ViewStyle + actionText: TextStyle +} + +export default (theme: Theme) => + StyleSheet.create({ + actionButton: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + actionText: { + color: theme.color_text_base_inverse, + fontSize: theme.font_size_base, + backgroundColor: 'transparent', + padding: 10, + }, + }) diff --git a/components/swipe-action/swipe-action.tsx b/components/swipe-action/swipe-action.tsx new file mode 100644 index 000000000..92c703de6 --- /dev/null +++ b/components/swipe-action/swipe-action.tsx @@ -0,0 +1,108 @@ +import React, { useCallback, useImperativeHandle, useRef } from 'react' +import { Animated } from 'react-native' +import Swipeable from 'react-native-gesture-handler/Swipeable' +import useClickAway from '../_util/hooks/useClickAway' +import { SwipeActionProps } from './PropsType' +import { RenderActions } from './RenderActions' + +export const SwipeAction = React.forwardRef( + (props, ref) => { + const { + children, + closeOnAction = true, + closeOnTouchOutside, + left, + right, + styles, + onSwipeableOpen, + onSwipeableWillClose, + ...restProps + } = props + + const swipeActionRef = React.useRef(null) + useImperativeHandle(ref, () => swipeActionRef.current as Swipeable) + + const openRef = useRef(false) + const setOpenRef = useCallback((open) => { + openRef.current = open + }, []) + + const close = useCallback(() => { + if (openRef.current === true) { + openRef.current = false + swipeActionRef.current?.close?.() + } + }, []) + + // ======================== Swipeable renderLeftActions/renderRightActions ======================== + const renderActions = useCallback( + ( + progressAnimatedValue: Animated.AnimatedInterpolation, + isLeft = false, + ) => { + return ( + + ) + }, + [close, closeOnAction, left, right, setOpenRef, styles], + ) + const renderLeftActions = useCallback( + (progressAnimatedValue) => renderActions(progressAnimatedValue, true), + [renderActions], + ) + const renderRightActions = useCallback( + (progressAnimatedValue) => renderActions(progressAnimatedValue, false), + [renderActions], + ) + + // ======================== Swipeable onSwipeableOpen/onSwipeableWillClose ======================== + const handleSwipeableOpen = useCallback( + (...args) => { + openRef.current = true + onSwipeableOpen && onSwipeableOpen.apply(undefined, args) + }, + [onSwipeableOpen], + ) + + const handleSwipeableWillClose = useCallback( + (...args) => { + openRef.current = false + onSwipeableWillClose && onSwipeableWillClose.apply(undefined, args) + }, + [onSwipeableWillClose], + ) + + // ======================== Closing when click outside ======================== + useClickAway(() => { + if (closeOnTouchOutside) { + setTimeout(() => { + close() + }) + } + }) + + return ( + + {children} + + ) + }, +) From 9bfc8c592b2841413c4c43b76e6a218b0262bf3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 16:15:11 +0800 Subject: [PATCH 4/9] [5.2.0-rc.1]deprecated SegmentedControl --- components/index.tsx | 5 +- components/segmented-control/PropsType.tsx | 9 - .../__tests__/__snapshots__/demo.test.js.snap | 162 ------------------ .../segmented-control/__tests__/demo.test.js | 3 - .../segmented-control/__tests__/index.test.js | 11 -- components/segmented-control/demo/basic.md | 53 ------ components/segmented-control/demo/basic.tsx | 43 ----- components/segmented-control/index.en-US.md | 21 +-- components/segmented-control/index.tsx | 5 - components/segmented-control/index.zh-CN.md | 23 +-- .../segmented-control/segmented.android.tsx | 162 ------------------ .../segmented-control/segmented.ios.tsx | 41 ----- components/segmented-control/style/index.tsx | 41 ----- 13 files changed, 10 insertions(+), 569 deletions(-) delete mode 100644 components/segmented-control/PropsType.tsx delete mode 100644 components/segmented-control/__tests__/__snapshots__/demo.test.js.snap delete mode 100644 components/segmented-control/__tests__/demo.test.js delete mode 100644 components/segmented-control/__tests__/index.test.js delete mode 100644 components/segmented-control/demo/basic.md delete mode 100644 components/segmented-control/demo/basic.tsx delete mode 100644 components/segmented-control/index.tsx delete mode 100644 components/segmented-control/segmented.android.tsx delete mode 100644 components/segmented-control/segmented.ios.tsx delete mode 100644 components/segmented-control/style/index.tsx diff --git a/components/index.tsx b/components/index.tsx index dc8ce5170..5a068924a 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -36,7 +36,6 @@ export { default as Provider } from './provider/index' export { default as Radio } from './radio/index' export { default as Result } from './result/index' export { default as SearchBar } from './search-bar/index' -export { default as SegmentedControl } from './segmented-control/index' export { default as Slider } from './slider/index' export { default as Stepper } from './stepper/index' export { default as Steps } from './steps/index' @@ -55,3 +54,7 @@ export { default as WingBlank } from './wing-blank/index' * @deprecated */ export class ImagePicker {} +/** + * @deprecated + */ +export class SegmentedControl {} diff --git a/components/segmented-control/PropsType.tsx b/components/segmented-control/PropsType.tsx deleted file mode 100644 index 232163662..000000000 --- a/components/segmented-control/PropsType.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export interface SegmentedControlPropsType { - tintColor?: string - disabled?: boolean - selectedIndex?: number - values?: string[] - onChange?: (e: any) => void - onValueChange?: (value: string) => void - selectedTextColor?: string //for android platform -} diff --git a/components/segmented-control/__tests__/__snapshots__/demo.test.js.snap b/components/segmented-control/__tests__/__snapshots__/demo.test.js.snap deleted file mode 100644 index 856866d6c..000000000 --- a/components/segmented-control/__tests__/__snapshots__/demo.test.js.snap +++ /dev/null @@ -1,162 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders ./components/segmented-control/demo/basic.tsx correctly 1`] = ` - - - Disabled - - - - - TintColor and Style - - - - - SelectedIndex - - - - - onChange/onValueChange - - - -`; diff --git a/components/segmented-control/__tests__/demo.test.js b/components/segmented-control/__tests__/demo.test.js deleted file mode 100644 index ed1f5b219..000000000 --- a/components/segmented-control/__tests__/demo.test.js +++ /dev/null @@ -1,3 +0,0 @@ -import rnDemoTest from '../../../tests/shared/demoTest' - -rnDemoTest('segmented-control') diff --git a/components/segmented-control/__tests__/index.test.js b/components/segmented-control/__tests__/index.test.js deleted file mode 100644 index 8f9ed72b9..000000000 --- a/components/segmented-control/__tests__/index.test.js +++ /dev/null @@ -1,11 +0,0 @@ -// import React from 'react'; -// import SegmentedControl from '../index'; - -// // No need to render Snapshot again, because of `./demo.test.js` - -describe('SegmentedControl', () => { - it('trigger event correctly', () => { - // todos: write test! - expect(true).toBe(true) - }) -}) diff --git a/components/segmented-control/demo/basic.md b/components/segmented-control/demo/basic.md deleted file mode 100644 index 60fcc279c..000000000 --- a/components/segmented-control/demo/basic.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -order: 0 -title: - zh-CN: 基本 - en-US: Basic ---- - -[Demo Source Code](https://github.com/ant-design/ant-design-mobile-rn/blob/master/components/segmented-control/demo/basic.tsx) - -```jsx -import React from 'react'; -import { Text, View } from 'react-native'; -import { SegmentedControl, WhiteSpace } from '@ant-design/react-native'; -export default class BasicTagExample extends React.Component { - constructor() { - super(...arguments); - this.onChange = e => { - console.log(`selectedIndex:${e.nativeEvent.selectedSegmentIndex}`); - }; - this.onValueChange = value => { - console.log(value); - }; - } - render() { - return ( - - Disabled - - - TintColor and Style - - - SelectedIndex - - - onChange/onValueChange - - - ); - } -} -``` diff --git a/components/segmented-control/demo/basic.tsx b/components/segmented-control/demo/basic.tsx deleted file mode 100644 index a2da2c044..000000000 --- a/components/segmented-control/demo/basic.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* tslint:disable:no-console */ -import React from 'react' -import { Text, View } from 'react-native' -import { SegmentedControl, WhiteSpace } from '../../' - -export default class BasicTagExample extends React.Component { - onChange = (e: any) => { - console.log(`selectedIndex:${e.nativeEvent.selectedSegmentIndex}`) - } - - onValueChange = (value: any) => { - console.log(value) - } - - render() { - return ( - - Disabled - - - TintColor and Style - - - SelectedIndex - - - onChange/onValueChange - - - ) - } -} diff --git a/components/segmented-control/index.en-US.md b/components/segmented-control/index.en-US.md index 5eecd0568..1e057c86a 100644 --- a/components/segmented-control/index.en-US.md +++ b/components/segmented-control/index.en-US.md @@ -2,24 +2,9 @@ category: Components type: Navigation title: SegmentedControl +subtitle: (deprecated) --- -`SegmentedControl` includes at least two segments, it is used to display diffrent views and recommended by `iOS`. +Deprecated since `5.2.0`. -### Rule -- It is similar to the functionality used for `Tabs`, so avoid to use them at same page as much as possible. -- You can use `SegmentedControl` with `NavBar` to display mutiple views. -- Generally there should be no more than 5 segments in one line, each segment has 2-4 words and needs simplified texts. -- Keep the length of the text consistent as much as possible. - -## API - -Properties | Descrition | Type | Default ------------|------------|------|-------- -| style | style of component | Object | `{}` | -| tintColor | accent color of the control | String | `#2DB7F5` | -| disabled | whether the user is able to interact with the control | Boolean | false | -| selectedIndex | the index in `props.values` of the segment to be (pre)selected | Number | 0 | -| values | The labels for the control's segment buttons, in order | array | [] | -| onChange | callback that is called when the user taps a segment; passes the event object as an argument. `e.nativeEvent.selectedSegmentIndex` is selected index. `e.nativeEvent.value` is selected value. | (e): void | function(){} | -| onValueChange | callback that is called when the user taps a segment; passes the segment's value as an argument | (val): void | function(){} | +Please use [@react-native-community/segmented-control](https://github.com/react-native-community/segmented-control#usage) instead. \ No newline at end of file diff --git a/components/segmented-control/index.tsx b/components/segmented-control/index.tsx deleted file mode 100644 index eb7ac43f9..000000000 --- a/components/segmented-control/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Platform } from 'react-native' -import SegmentedAndroid from './segmented.android' -import SegmentedIOS from './segmented.ios' - -export default Platform.OS === 'ios' ? SegmentedIOS : SegmentedAndroid diff --git a/components/segmented-control/index.zh-CN.md b/components/segmented-control/index.zh-CN.md index bd651e9ed..504c68c4a 100644 --- a/components/segmented-control/index.zh-CN.md +++ b/components/segmented-control/index.zh-CN.md @@ -2,26 +2,9 @@ category: Components type: Navigation title: SegmentedControl -subtitle: 分段器 +subtitle: 分段器(已弃用) --- +`5.2.0`开始已弃用。 -由至少 2 个分段控件组成,用作不同视图的显示;是 iOS 的推荐组件。 - -### 规则 -- 和 Tabs 功能相似,尽可能避免一个页面中同时出现这两个组件。 -- 可以搭配 NavBar 一起使用,用于显示多个视图,分段数一般为 2 个。 -- 单独放置一行时,分段数最多为 5 个;文案需要精简,一般 2-4 个字。 -- 尽可能保持文案长度一致。 - -## API - -属性 | 说明 | 类型 | 默认值 -----|-----|------|------ -| style | 自定义样式 | Object | `{}` | -| tintColor | 组件主色调 | String | `#2DB7F5` | -| disabled | 是否启用 | Boolean | false | -| selectedIndex | 选中项在数组中的索引 | Number | 0 | -| values | 选项数组,值是字符串 | array | [] | -| onChange | 回调函数, 其中`e.nativeEvent.selectedSegmentIndex`是选中项索引, `e.nativeEvent.value`是选中的值. | (e): void | function(){} | -| onValueChange | 回调函数 | (val): void | function(){} | +请使用[@react-native-community/segmented-control](https://github.com/react-native-community/segmented-control#usage)替代。 diff --git a/components/segmented-control/segmented.android.tsx b/components/segmented-control/segmented.android.tsx deleted file mode 100644 index 5d1ca06a1..000000000 --- a/components/segmented-control/segmented.android.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import normalizeColor from 'normalize-css-color' -import React from 'react' -import { - StyleProp, - Text, - TouchableHighlight, - View, - ViewStyle, -} from 'react-native' -import { WithTheme, WithThemeStyles } from '../style' -import { SegmentedControlPropsType } from './PropsType' -import AndroidStyles, { SegmentControlStyle } from './style/index' - -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -/** - * number should be a color processed by `normalizeColor` - * alpha should be number between 0 and 1 - */ -function setNormalizedColorAlpha(input: number, alpha: number) { - if (alpha < 0) { - alpha = 0 - } else if (alpha > 1) { - alpha = 1 - } - - alpha = Math.round(alpha * 255) - // magic bitshift guarantees we return an unsigned int - // tslint:disable-next-line:no-bitwise - return ((input & 0xffffff00) | alpha) >>> 0 -} - -export interface SegmentControlProps - extends SegmentedControlPropsType, - WithThemeStyles { - style?: StyleProp -} - -export default class SegmentedControl extends React.Component< - SegmentControlProps, - any -> { - static defaultProps = { - selectedIndex: 0, - disabled: false, - values: [], - onChange() {}, - onValueChange() {}, - style: {}, - selectedTextColor: '#fff', - } - - constructor(props: SegmentControlProps) { - super(props) - this.state = { - selectedIndex: props.selectedIndex, - } - } - - UNSAFE_componentWillReceiveProps(nextProps: SegmentControlProps) { - if (nextProps.selectedIndex !== this.props.selectedIndex) { - this.setState({ - selectedIndex: nextProps.selectedIndex, - }) - } - } - - onPress(e: any, index: number, value: string) { - const { disabled, onChange, onValueChange } = this.props - if (!disabled) { - e.nativeEvent.selectedSegmentIndex = index - e.nativeEvent.value = value - if (onChange) { - onChange(e) - } - if (onValueChange) { - onValueChange(value) - } - this.setState({ - selectedIndex: index, - }) - } - } - - render() { - const { style, disabled, values = [], selectedTextColor } = this.props - let { tintColor } = this.props - return ( - - {(styles, theme) => { - const selectedIndex = this.state.selectedIndex - tintColor = tintColor || theme.segmented_control_color - const items = values.map((value, idx) => { - let itemRadius: any = null - if (idx === 0) { - itemRadius = styles.itemLeftRadius - } else if (idx === values.length - 1) { - itemRadius = styles.itemRightRadius - } - - const itemStyle = [ - styles.item, - itemRadius, - { - backgroundColor: - idx === selectedIndex ? tintColor : 'transparent', - borderColor: tintColor, - }, - ] - - const underlayColor = - idx === selectedIndex - ? tintColor - : '#' + - setNormalizedColorAlpha( - normalizeColor(tintColor), - 0.3, - ).toString(16) - - return ( - this.onPress(e, idx, value)} - underlayColor={underlayColor} - style={itemStyle} - activeOpacity={1}> - - {value} - - - ) - }) - - const enabledOpacity = !disabled ? 1 : 0.5 - const segmentedStyle = { - opacity: enabledOpacity, - borderColor: tintColor, - } - - return ( - {items} - ) - }} - - ) - } -} diff --git a/components/segmented-control/segmented.ios.tsx b/components/segmented-control/segmented.ios.tsx deleted file mode 100644 index 37dd2617a..000000000 --- a/components/segmented-control/segmented.ios.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import SegmentedControlIOS from '@react-native-community/segmented-control' -import React from 'react' -import { StyleProp, ViewStyle } from 'react-native' -import { WithTheme } from '../style' -import { SegmentedControlPropsType } from './PropsType' - -export interface SegmentedControlProps extends SegmentedControlPropsType { - style?: StyleProp -} - -export default class SegmentedControl extends React.Component< - SegmentedControlProps, - any -> { - static defaultProps = { - selectedIndex: 0, - } - - render() { - const { - tintColor, - disabled, - selectedIndex, - selectedTextColor, - ...restProps - } = this.props - return ( - - {(_, theme) => ( - - )} - - ) - } -} diff --git a/components/segmented-control/style/index.tsx b/components/segmented-control/style/index.tsx deleted file mode 100644 index 6c8e63ea9..000000000 --- a/components/segmented-control/style/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { StyleSheet, TextStyle, ViewStyle } from 'react-native' -import { Theme } from '../../style' - -export interface SegmentControlStyle { - segment: ViewStyle - item: ViewStyle - itemLeftRadius: ViewStyle - itemRightRadius: ViewStyle - itemText: TextStyle -} -export default (theme: Theme) => - StyleSheet.create({ - segment: { - flexDirection: 'row', - overflow: 'hidden', - borderWidth: StyleSheet.hairlineWidth, - borderColor: theme.brand_primary, - borderRadius: theme.radius_md, - }, - item: { - flex: 1, - paddingVertical: theme.h_spacing_sm, - borderLeftWidth: StyleSheet.hairlineWidth, - borderRightWidth: StyleSheet.hairlineWidth, - borderStyle: 'solid', - alignItems: 'center', - justifyContent: 'center', - }, - itemLeftRadius: { - borderTopLeftRadius: theme.radius_md, - borderBottomLeftRadius: theme.radius_md, - }, - itemRightRadius: { - borderTopRightRadius: theme.radius_md, - borderBottomRightRadius: theme.radius_md, - }, - itemText: { - textAlign: 'center', - fontSize: theme.font_size_caption_sm, - }, - }) From f0283ccc6cdfab188cce650afdebceac15ade799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 16:39:07 +0800 Subject: [PATCH 5/9] [5.2.0-rc.1]feat: refactor Slider --- components/slider/PropsType.tsx | 30 ++++ components/slider/demo/basic.tsx | 138 ++++++--------- components/slider/index.en-US.md | 26 +-- components/slider/index.tsx | 66 +------ components/slider/index.zh-CN.md | 26 +-- components/slider/marks.tsx | 59 +++++++ components/slider/slider.tsx | 263 ++++++++++++++++++++++++++++ components/slider/style/index.tsx | 103 +++++++++++ components/slider/thumb-icon.tsx | 27 +++ components/slider/thumb.tsx | 79 +++++++++ components/slider/ticks.tsx | 41 +++++ components/style/themes/default.tsx | 3 + 12 files changed, 689 insertions(+), 172 deletions(-) create mode 100644 components/slider/PropsType.tsx create mode 100644 components/slider/marks.tsx create mode 100644 components/slider/slider.tsx create mode 100644 components/slider/style/index.tsx create mode 100644 components/slider/thumb-icon.tsx create mode 100644 components/slider/thumb.tsx create mode 100644 components/slider/ticks.tsx diff --git a/components/slider/PropsType.tsx b/components/slider/PropsType.tsx new file mode 100644 index 000000000..e2caf4d7e --- /dev/null +++ b/components/slider/PropsType.tsx @@ -0,0 +1,30 @@ +import type { ReactNode } from 'react' +import React from 'react' +import { StyleProp, ViewStyle } from 'react-native' +import { SliderStyle } from './style' + +export type SliderMarks = { + [key: number]: React.ReactNode +} + +export type SliderValue = number | [number, number] + +export type SliderProps = { + min?: number + max?: number + value?: SliderValue + defaultValue?: SliderValue + step?: number + marks?: SliderMarks + ticks?: boolean + disabled?: boolean + range?: boolean + icon?: ReactNode + // TODO-luokun + // popover?: boolean | ((value: number) => ReactNode) + // residentPopover?: boolean + onChange?: (value: SliderValue) => void + onAfterChange?: (value: SliderValue) => void + style?: StyleProp + styles?: Partial +} diff --git a/components/slider/demo/basic.tsx b/components/slider/demo/basic.tsx index 42497a62b..55fa2f9fb 100644 --- a/components/slider/demo/basic.tsx +++ b/components/slider/demo/basic.tsx @@ -1,93 +1,61 @@ import React from 'react' -import { Text, View } from 'react-native' -import { Slider } from '../../' - -export default class BasicSliderExample extends React.Component { - constructor(props: any) { - super(props) - this.state = { - changingValue: 0.25, - changedValue: 0.15, - minMaxValue: 0, - slideCompletionCount: 0, - } - } - - handleChange = (value: any) => { - this.setState({ - changingValue: value, - }) - } - - onAfterChange = (value: any) => { - this.setState({ - changedValue: value, - }) +import { ScrollView } from 'react-native' + +import { List, Slider, Toast } from '../../' + +export default function StepperExample() { + const marks = { + 0: 0, + 20: 20, + 40: 40, + 60: 60, + 80: 80, + 100: 100, } - minMaxChange = (value: any) => { - this.setState({ - minMaxValue: value, - }) + const toastValue = (value: number | [number, number]) => { + let text = '' + if (typeof value === 'number') { + text = `${value}` + } else { + text = `[${value.join(',')}]` + } + Toast.show({ content: `当前选中值为:${text}`, position: 'top' }) } - render() { - return ( - - - Default settings - - - - - Initial value: 0.5 - - - - - min: 0, max: 1, current Value: {this.state.minMaxValue} + return ( + + + + + + + + + + + + + + + + + + this.minMaxChange(value)} + step={100} + min={100} + max={1000} + ticks + onAfterChange={toastValue} /> - - - - step: 0.25 - - - - - disabled - - - - - onChange value: {this.state.changingValue} - this.handleChange(value)} - /> - - - - onAfterChange value: {this.state.changedValue} - this.onAfterChange(value)} - /> - - - - custom color: - - - - ) - } + +
+ + + + + + + ) } diff --git a/components/slider/index.en-US.md b/components/slider/index.en-US.md index 5ba6b77c5..e5725fe4f 100644 --- a/components/slider/index.en-US.md +++ b/components/slider/index.en-US.md @@ -12,15 +12,17 @@ A Slider component for selecting particular value in range, eg: controls the dis ## API -Properties | Descrition | Type | Default ------------|------------|------|-------- -| min | Number | 0 | The minimum value the slider can slide to. | -| max | Number | 100 | The maximum value the slider can slide to. | -| step | Number or null | 1 | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | -| value | Number | | The value of slider. | -| defaultValue | Number | 0 | The default value of slider. | -| disabled | Boolean | false | If true, the slider will not be interactable. | -| onChange | Function | Noop | Callback function that is called when the user changes the slider's value. | -| onAfterChange | Function | Noop | Fired when `ontouchend` is fired. | -| maximumTrackTintColor (`iOS`) | String | `#108ee9` | The color used for the track to the right of the button. Overrides the default blue gradient image on iOS. | -| minimumTrackTintColor (iOS) | String | `#ddd` | The color used for the track to the left of the button. Overrides the default blue gradient image on iOS. | +| Properties | Description | Type | Default | +| --- | --- | --- | --- | +| defaultValue | Default value | `number \| [number, number]` | `range ? [0, 0] : 0` | +| disabled | Whether disabled | `boolean` | `false` | +| icon | The icon of slider | `ReactNode` | - | +| marks | Tick marks | `{ [key: number]: React.ReactNode }` | - | +| max | Max value | `number` | `100` | +| min | Min value | `number` | `0` | +| onAfterChange | Consistent with the trigger timing of `touchend`, pass the current value as a parameter | `(value: number \| [number, number]) => void` | - | +| onChange | Triggered when the slider is dragged, and the current dragged value is passed in as a parameter | `(value: number \| [number, number]) => void` | - | +| range | Whether it is a double sliders | `boolean` | `false` | +| step | Step distance, the value must be greater than `0`, and `(max-min)` can be divisible by `step`. When `marks` is not null, the configuration of `step` is invalid | `number` | `1` | +| ticks | Whether to display the scale | `boolean` | `false` | +| value | Current value | `number \| [number, number]` | - | \ No newline at end of file diff --git a/components/slider/index.tsx b/components/slider/index.tsx index ee82cc55b..720c65069 100644 --- a/components/slider/index.tsx +++ b/components/slider/index.tsx @@ -1,65 +1,5 @@ -import React from 'react' -import { View } from 'react-native' -import Slider from '@react-native-community/slider' -import { WithTheme } from '../style' +import { Slider } from './slider' -export interface SliderProps { - maximumTrackTintColor?: string - minimumTrackTintColor?: string - onChange?: (value?: number) => void - onAfterChange?: (value?: number) => void - defaultValue?: number - tipFormatter?: (value?: string) => React.ReactNode - value?: number - min?: number - max?: number - step?: number - disabled?: boolean -} +export type { SliderProps, SliderValue } from './PropsType' -export default class SliderAntm extends React.Component { - static defaultProps = { - onChange() {}, - onAfterChange() {}, - defaultValue: 0, - disabled: false, - } - - render() { - const { - defaultValue, - value, - min, - max, - step, - disabled, - onChange, - onAfterChange, - maximumTrackTintColor, - minimumTrackTintColor, - } = this.props - return ( - - {(_, theme) => ( - - - - )} - - ) - } -} +export default Slider diff --git a/components/slider/index.zh-CN.md b/components/slider/index.zh-CN.md index cf74012e8..4429d7e26 100644 --- a/components/slider/index.zh-CN.md +++ b/components/slider/index.zh-CN.md @@ -13,15 +13,17 @@ subtitle: 滑动输入条 ## API -属性 | 说明 | 类型 | 默认值 -----|-----|------|------ -| min | Number | 0 | 最小值 | -| max | Number | 100 | 最大值 | -| step | Number or null | 1 | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 `null`,此时 Slider 的可选值仅有 marks 标出来的部分 | -| value | Number | | 设置当前取值。 | -| defaultValue | Number | 0 | 设置初始取值。| -| disabled | Boolean | false | 值为 `true` 时,滑块为禁用状态 | -| onChange | Function | Noop | 当 Slider 的值发生改变时,会触发 onChange 事件,并把改变后的值作为参数传入 | -| maximumTrackTintColor(`iOS`) | String | `#108ee9`(RN) | 底部背景色 | -| minimumTrackTintColor(`iOS`) | String | `#ddd` (RN) | 当前选中部分的颜色 | -| onAfterChange | Function | Noop | 与 `ontouchend` 触发时机一致,把当前值作为参数传入 | +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| defaultValue | 默认值 | `number \| [number, number]` | `range ? [0, 0] : 0` | +| disabled | 是否禁用 | `boolean` | `false` | +| icon | 滑块的图标 | `ReactNode` | - | +| marks | 刻度标记 | `{ [key: number]: React.ReactNode }` | - | +| max | 最大值 | `number` | `100` | +| min | 最小值 | `number` | `0` | +| onAfterChange | 与 `touchend` 触发时机一致,把当前值作为参数传入 | `(value: number \| [number, number]) => void` | - | +| onChange | 拖拽滑块时触发,并把当前拖拽的值作为参数传入 | `(value: number \| [number, number]) => void` | - | +| range | 是否为双滑块 | `boolean` | `false` | +| step | 步距,取值必须大于 `0`,并且 `(max - min)` 可被 `step` 整除。当 `marks` 不为空对象时,`step` 的配置失效 | `number` | `1` | +| ticks | 是否显示刻度 | `boolean` | `false` | +| value | 当前值 | `number \| [number, number]` | - | diff --git a/components/slider/marks.tsx b/components/slider/marks.tsx new file mode 100644 index 000000000..8b9398695 --- /dev/null +++ b/components/slider/marks.tsx @@ -0,0 +1,59 @@ +import type { FC, ReactNode } from 'react' +import React from 'react' +import { View } from 'react-native' +import AntmView from '../view' +import { SliderStyle } from './style' + +export type SliderMarks = { + [key: number]: ReactNode +} + +type MarksProps = { + marks: SliderMarks + max: number + min: number + upperBound: number + lowerBound: number + styles: Partial +} + +const Marks: FC = ({ + marks, + upperBound, + lowerBound, + max, + min, + styles, +}) => { + const marksKeys = Object.keys(marks) + + const range = max - min + const elements = marksKeys + .map(parseFloat) + .sort((a, b) => a - b) + .filter((point) => point >= min && point <= max) + .map((point) => { + const markPoint = marks[point] + if (!markPoint && markPoint !== 0) { + return null + } + + const isActive = point <= upperBound && point >= lowerBound + + const style = { + left: `${((point - min) / range) * 100}%`, + } + return ( + + + {markPoint} + + + ) + }) + + return {elements} +} + +export default Marks diff --git a/components/slider/slider.tsx b/components/slider/slider.tsx new file mode 100644 index 000000000..c5a99a714 --- /dev/null +++ b/components/slider/slider.tsx @@ -0,0 +1,263 @@ +import getMiniDecimal, { toFixed } from '@rc-component/mini-decimal' +import useMergedState from 'rc-util/lib/hooks/useMergedState' +import React, { useMemo, useRef, useState } from 'react' +import { + GestureResponderEvent, + LayoutChangeEvent, + LayoutRectangle, + View, +} from 'react-native' +import devWarning from '../_util/devWarning' +import { useTheme } from '../style' +import { SliderProps, SliderValue } from './PropsType' +import Marks from './marks' +import SliderStyles from './style' +import Thumb from './thumb' +import Ticks from './ticks' + +function nearest(arr: number[], target: number) { + return arr.reduce((pre, cur) => { + return Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur + }) +} + +export const Slider: React.FC = (props) => { + const { + min = 0, + max = 100, + disabled = false, + marks, + ticks, + step = 1, + icon, + style, + styles, + } = props + + const ss = useTheme({ + styles, + themeStyles: SliderStyles, + }) + + function sortValue(val: [number, number]): [number, number] { + return val.sort((a, b) => a - b) + } + function convertValue(value: SliderValue): [number, number] { + return (props.range ? value : [min, value]) as any + } + function alignValue(value: number, decimalLen: number) { + const decimal = getMiniDecimal(value) + const fixedStr = toFixed(decimal.toString(), '.', decimalLen) + + return getMiniDecimal(fixedStr).toNumber() + } + + function reverseValue(value: [number, number]): SliderValue { + const mergedDecimalLen = Math.max( + getDecimalLen(step), + getDecimalLen(value[0]), + getDecimalLen(value[1]), + ) + return props.range + ? (value.map((v) => alignValue(v, mergedDecimalLen)) as [number, number]) + : alignValue(value[1], mergedDecimalLen) + } + + function getDecimalLen(n: number) { + return (`${n}`.split('.')[1] || '').length + } + + function onAfterChange(value: [number, number]) { + props.onAfterChange?.(reverseValue(value)) + } + + let propsValue: SliderValue | undefined = props.value + if (props.range && typeof props.value === 'number') { + devWarning( + false, + 'Slider', + 'When `range` prop is enabled, the `value` prop should be an array, like: [0, 0]', + ) + propsValue = [0, props.value] + } + const [rawValue, setRawValue] = useMergedState( + props.defaultValue ?? (props.range ? [min, min] : min), + { value: propsValue, onChange: props.onChange }, + ) + + const sliderValue = sortValue(convertValue(rawValue)) + function setSliderValue(value: [number, number]) { + const next = sortValue(value) + + const current = sliderValue + if (next[0] === current[0] && next[1] === current[1]) { + return + } + setRawValue(reverseValue(next)) + } + + const fillSize = `${(100 * (sliderValue[1] - sliderValue[0])) / (max - min)}%` + const fillStart = `${(100 * (sliderValue[0] - min)) / (max - min)}%` + + // 计算要显示的点 + const pointList = useMemo(() => { + if (marks) { + return Object.keys(marks) + .map(parseFloat) + .sort((a, b) => a - b) + } else if (ticks) { + const points: number[] = [] + for ( + let i = getMiniDecimal(min); + i.lessEquals(getMiniDecimal(max)); + i = i.add(step) + ) { + points.push(i.toNumber()) + } + return points + } + + return [] + }, [marks, ticks, step, min, max]) + + function getValueByPosition(position: number) { + const newPosition = position < min ? min : position > max ? max : position + + let value = min + + // 显示了刻度点,就只能移动到点上 + if (pointList.length) { + value = nearest(pointList, newPosition) + } else { + // 使用 MiniDecimal 避免精度问题 + const cell = Math.round((newPosition - min) / step) + const nextVal = getMiniDecimal(cell).multi(step) + value = getMiniDecimal(min).add(nextVal.toString()).toNumber() + } + return value + } + + const [trackLayout, setTrackLayout] = useState() + const onTrackLayout = (e: LayoutChangeEvent) => { + setTrackLayout(e.nativeEvent.layout) + } + + const onTrackClick = (event: GestureResponderEvent) => { + event.stopPropagation() + if (disabled) { + return + } + if (!trackLayout) { + return + } + const sliderOffsetLeft = trackLayout.x + const position = + ((event.nativeEvent.locationX - sliderOffsetLeft) / + Math.ceil(trackLayout.width)) * + (max - min) + + min + const targetValue = getValueByPosition(position) + let nextSliderValue: [number, number] + if (props.range) { + // 移动的滑块采用就近原则 + if ( + Math.abs(targetValue - sliderValue[0]) > + Math.abs(targetValue - sliderValue[1]) + ) { + nextSliderValue = [sliderValue[0], targetValue] + } else { + nextSliderValue = [targetValue, sliderValue[1]] + } + } else { + nextSliderValue = [min, targetValue] + } + setSliderValue(nextSliderValue) + onAfterChange(nextSliderValue) + } + + const valueBeforeDragRef = useRef<[number, number]>() + + const renderThumb = (index: number) => { + return ( + { + if (!trackLayout) { + return + } + const sliderOffsetLeft = trackLayout.x + const position = + ((locationX - sliderOffsetLeft) / Math.ceil(trackLayout.width)) * + (max - min) + + min + const val = getValueByPosition(position) + if (!valueBeforeDragRef.current) { + valueBeforeDragRef.current = [...sliderValue] + } + valueBeforeDragRef.current[index] = val + const next = sortValue([...valueBeforeDragRef.current]) + setSliderValue(next) + if (last) { + valueBeforeDragRef.current = undefined + onAfterChange(next) + } + }} + style={index === 0 ? { position: 'absolute' } : {}} + styles={ss} + /> + ) + } + + return ( + + true}> + + + {/* 刻度 */} + {props.ticks && ( + + )} + {props.range && renderThumb(0)} + {renderThumb(1)} + + {/* 刻度下的标记 */} + {marks && ( + + )} + + ) +} diff --git a/components/slider/style/index.tsx b/components/slider/style/index.tsx new file mode 100644 index 000000000..6b83d4eb9 --- /dev/null +++ b/components/slider/style/index.tsx @@ -0,0 +1,103 @@ +import { StyleSheet, TextStyle, ViewStyle } from 'react-native' +import { Theme } from '../../style' + +export interface SliderStyle { + slider: ViewStyle + disabled: ViewStyle + trackContianer: ViewStyle + track: ViewStyle + fill: ViewStyle + + // 滑轨按钮 + thumb: ViewStyle + + // 刻度 + ticks: ViewStyle + tick: ViewStyle + tickActive: ViewStyle + + // 刻度下的标记 + mark: ViewStyle + markText: TextStyle + markTextActive: TextStyle +} + +export default (theme: Theme) => + StyleSheet.create({ + slider: { + paddingVertical: 5, + paddingHorizontal: 14, + }, + disabled: {}, + trackContianer: { + paddingVertical: 8, + position: 'relative', + width: '100%', + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + track: { + position: 'absolute', + width: '100%', + zIndex: 1, + height: 3, + borderRadius: 3, + backgroundColor: '#f5f5f5', + }, + fill: { + position: 'absolute', + zIndex: 1, + height: 3, + borderRadius: 3, + backgroundColor: theme.color_primary, + }, + + thumb: { + zIndex: 2, + width: 32, + height: 32, + borderRadius: 32, + marginLeft: -16, + backgroundColor: '#ffffff', + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 1, + }, + shadowOpacity: 0.2, + shadowRadius: 3, + elevation: 3, + }, + + ticks: { + position: 'absolute', + width: '100%', + height: 3, + backgroundColor: 'transparent', + }, + tick: { + position: 'absolute', + top: -2, + width: 7, + height: 7, + marginLeft: -3, + backgroundColor: '#f5f5f5', + borderRadius: 7, + }, + tickActive: { + backgroundColor: theme.color_primary, + }, + + mark: { + position: 'relative', + width: '100%', + height: 11, + }, + markText: { + marginLeft: '-50%', + fontSize: 11, + color: '#333333', + }, + markTextActive: {}, + }) diff --git a/components/slider/thumb-icon.tsx b/components/slider/thumb-icon.tsx new file mode 100644 index 000000000..a66bbfeaf --- /dev/null +++ b/components/slider/thumb-icon.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { StyleSheet, View } from 'react-native' + +export const ThumbIcon = () => { + return ( + + + + + + ) +} + +const style = StyleSheet.create({ + thumbIcon: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + flex: 1, + }, + line: { + width: 2, + height: '30%', + borderRadius: 2, + backgroundColor: '#3086ff', + }, +}) diff --git a/components/slider/thumb.tsx b/components/slider/thumb.tsx new file mode 100644 index 000000000..81e5c1b4d --- /dev/null +++ b/components/slider/thumb.tsx @@ -0,0 +1,79 @@ +import type { FC, ReactNode } from 'react' +import React, { useState } from 'react' +import { + LayoutChangeEvent, + LayoutRectangle, + StyleProp, + ViewStyle, +} from 'react-native' +import { Gesture, GestureDetector } from 'react-native-gesture-handler' +import Animated, { runOnJS, useAnimatedStyle } from 'react-native-reanimated' +import { SliderStyle } from './style' +import { ThumbIcon } from './thumb-icon' + +type ThumbProps = { + value: number + min: number + max: number + disabled: boolean + trackLayout?: LayoutRectangle + onDrag: (value: number, last?: boolean) => void + icon?: ReactNode + // popover: boolean | ((value: number) => ReactNode) + // residentPopover: boolean + style?: StyleProp + styles: Partial +} + +const Thumb: FC = (props) => { + const { + value, + min, + max, + trackLayout, + disabled, + icon, + onDrag, + style, + styles, + } = props + + const animatedStyles = useAnimatedStyle(() => { + return { + transform: [ + { + translateX: ((value - min) / (max - min)) * (trackLayout?.width || 0), + }, + ], + } + }, [max, min, trackLayout?.width, value]) + + const [thumbLayout, setThumbLayout] = useState() + const handleLayout = (e: LayoutChangeEvent) => { + setThumbLayout(e.nativeEvent.layout) + } + + const gesture = Gesture.Pan() + .enabled(!disabled) + .onBegin(() => {}) + .onUpdate((e) => { + runOnJS(onDrag)(e.absoluteX - (thumbLayout?.width || 0)) + }) + .onEnd((e) => { + runOnJS(onDrag)(e.absoluteX - (thumbLayout?.width || 0), true) + }) + .onFinalize(() => {}) + + return ( + + true} + style={[styles.thumb, animatedStyles, style]} + onLayout={handleLayout}> + {icon ? icon : } + + + ) +} + +export default Thumb diff --git a/components/slider/ticks.tsx b/components/slider/ticks.tsx new file mode 100644 index 000000000..764bf04ee --- /dev/null +++ b/components/slider/ticks.tsx @@ -0,0 +1,41 @@ +import type { FC } from 'react' +import React from 'react' +import { View } from 'react-native' +import { SliderStyle } from './style' + +type TicksProps = { + points: number[] + max: number + min: number + upperBound: number + lowerBound: number + styles: Partial +} + +const Ticks: FC = ({ + points, + max, + min, + upperBound, + lowerBound, + styles, +}) => { + const range = max - min + const elements = points.map((point) => { + const offset = `${(Math.abs(point - min) / range) * 100}%` + + const isActived = point <= upperBound && point >= lowerBound + const style = { left: offset } + + return ( + + ) + }) + + return {elements} +} + +export default Ticks diff --git a/components/style/themes/default.tsx b/components/style/themes/default.tsx index 89ea9ac8b..d9d7e933e 100644 --- a/components/style/themes/default.tsx +++ b/components/style/themes/default.tsx @@ -197,4 +197,7 @@ export default { action_sheet_zindex: 1000, popup_zindex: 999, modal_zindex: 999, + tooltip_zindex: 999, + tooltip_dark: 'rgba(0, 0, 0, 0.75)', + arrow_size: 8, } From c41d8779840e5d6ed5689259e2e47a4aa3533469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 16:53:44 +0800 Subject: [PATCH 6/9] [5.2.0-rc.1]update README.md --- README.md | 91 +++++++++---------- README.zh-CN.md | 100 ++++++++++----------- components/date-picker-view/index.en-US.md | 2 +- components/date-picker/index.en-US.md | 6 +- components/date-picker/index.zh-CN.md | 4 +- components/image-picker/index.en-US.md | 2 + components/image-picker/index.zh-CN.md | 2 + components/input-item/index.en-US.md | 2 +- components/input-item/index.zh-CN.md | 2 +- components/input/Input.tsx | 8 +- components/input/PropsType.tsx | 12 ++- components/input/index.en-US.md | 10 ++- components/input/index.zh-CN.md | 9 +- components/list/index.en-US.md | 2 +- components/popover/index.en-US.md | 3 + components/popover/index.zh-CN.md | 4 +- components/textarea-item/index.en-US.md | 2 +- package.json | 4 - rn-kitchen-sink/demoList.js | 38 +++----- 19 files changed, 152 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 6e9ad19af..b4a45f73f 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,16 @@ A configurable Mobile UI specification and React-based implementation. ## Expo - - **Web support** - - Preview Web Platform in [[here] 🔗](https://1uokun.github.io/ant-design-mobile-rn/index.html) +> HTML5 Preview: [ant-design-mobile-rn/index.html](https://1uokun.github.io/ant-design-mobile-rn/index.html) - - **Expo demo app** |SDK 49+| |--| | [expo/ant-design-mobile-rn](https://expo.dev/preview/update?message=5.2.0%20Form%20%26%20Input&updateRuntimeVersion=5.2.0&createdAt=2024-05-24T06%3A10%3A13.909Z&slug=exp&projectId=7729a68b-f881-4294-89f5-5ae751bfb2b2&group=5278146b-c8d6-4c93-8a6b-6f942a2b5fb5) | +Open the camera app on your device and scan the code above,
+need install expo app: https://expo.io/tools +
Expo SDK history version |Expo SDK 44|SDK 47 iOS|SDK 47 Android| @@ -46,43 +46,6 @@ A configurable Mobile UI specification and React-based implementation. | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) |
-> Open the camera app on your device and scan the code above,
-need install expo app: https://expo.io/tools - -## Development - - - **Running On Device** - -```bash -# clone -git clone git@github.com:ant-design/ant-design-mobile-rn.git - -# go to ant rn folder -cd ant-design-mobile-rn - -# install dependencies -yarn - -# start ios -cd rn-kitchen-sink/ios && pod install -yarn ios - -# start android -yarn android -``` - - - **Running On Expo** - -```bash -# go to expo example folder -cd example - -# install dependencies -yarn - -# start expo -yarn expo -``` ## Install & Usage @@ -99,13 +62,13 @@ yarn add @ant-design/react-native ### Installing peer dependencies ```bash -npm install @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler +npm install @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated ``` or ```bash -yarn add @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler +yarn add @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated ``` > You need go to ios folder and run `pod install` (auto linking),Android will handle it by itself. @@ -123,14 +86,48 @@ then npx react-native-asset ``` -[introduce](docs/react/introduce.en-US.md#2-installation) - ## Links - [Home Page](http://rn.mobile.ant.design) +- [More Introduce >](docs/react/introduce.en-US.md) - [Developer Instruction](development.en-US.md) -- [React components](http://github.com/react-component/) -- [Demos](https://github.com/ant-design/antd-mobile-samples) + +## Development + + - **Running On Expo** + +> node >= 18 + +```bash +# go to expo example folder +cd example + +# install dependencies +yarn + +# start expo +yarn expo +``` + + - **Running On Device** + +```bash +# clone +git clone git@github.com:ant-design/ant-design-mobile-rn.git + +# go to ant rn folder +cd ant-design-mobile-rn + +# install dependencies +yarn + +# start ios +cd rn-kitchen-sink/ios && pod install +yarn ios + +# start android +yarn android +``` ## Contributing diff --git a/README.zh-CN.md b/README.zh-CN.md index 324d87040..2d2d19e6c 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -27,15 +27,14 @@ Ant Design 移动端设计规范。`@ant-design/react-native` 是 Ant Design 的 ## Expo - - **Web support** - - 点击[[这里] 🔗 ](https://1uokun.github.io/ant-design-mobile-rn/index.html)预览H5网页版本 +> HTML5 预览: [ant-design-mobile-rn/index.html](https://1uokun.github.io/ant-design-mobile-rn/index.html) - - **Expo demo app** -|SDK 49+ iOS|SDK 49+ Android| -|--|--| -| [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | +|SDK 49+| +|--| +| [expo/ant-design-mobile-rn](https://expo.dev/preview/update?message=5.2.0%20Form%20%26%20Input&updateRuntimeVersion=5.2.0&createdAt=2024-05-24T06%3A10%3A13.909Z&slug=exp&projectId=7729a68b-f881-4294-89f5-5ae751bfb2b2&group=5278146b-c8d6-4c93-8a6b-6f942a2b5fb5) | + +提示:使用本地原相机扫瞄上面的二维码, 需要下载 Expo App: https://expo.io/tools
Expo SDK历史版本 @@ -44,46 +43,6 @@ Ant Design 移动端设计规范。`@ant-design/react-native` 是 Ant Design 的 | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) | [expo/ant-design-mobile-rn](https://expo.dev/@1uokun/ant-design-mobile-rn) |
-> 提示:使用本地原相机扫瞄上面的二维码, 需要下载 Expo App: https://expo.io/tools - -## 本地演示 - - - **使用 react-native-cli 运行** - -```bash -# clone -git clone git@github.com:ant-design/ant-design-mobile-rn.git - -# go to ant rn folder -cd ant-design-mobile-rn - -# install dependencies -yarn - -# start ios -cd rn-kitchen-sink/ios && pod install -yarn ios - -# start android -yarn android - -# start expo -yarn expo -``` - - - **使用 expo-cli 运行** - -```bash -# go to expo example folder -cd example - -# install dependencies -yarn - -# start expo -yarn expo -``` - ## 安装 & 使用 ```bash @@ -99,20 +58,19 @@ yarn add @ant-design/react-native ### 安装peer依赖 ```bash -npm install @react-native-community/segmented-control @react-native-community/slider +npm install @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated ``` or ```bash -yarn add @react-native-community/segmented-control @react-native-community/slider +yarn add @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated ``` > 安装完依赖后需要到 iOS 目录 `pod install`(auto linking),Android 不需要手动处理 ### 链接字体图标 - ```js // 配置根目录下的 `react-native.config.js` 文件 module.exports = { @@ -125,14 +83,46 @@ module.exports = { npx react-native-asset ``` -[介绍](docs/react/introduce.zh-CN.md#2-安装) - ## 链接 - [首页](http://rn.mobile.ant.design) -- [开发文档](development.zh-CN.md) -- [底层 React 模块](http://github.com/react-component) -- [官方 Demo 集合](https://github.com/ant-design/antd-mobile-samples) +- [更详细的安装说明 >](docs/react/introduce.zh-CN.md) +- [开发者文档](development.zh-CN.md) + +## 本地演示 + + - **使用 expo-cli 运行** + +```bash +# go to expo example folder +cd example + +# install dependencies +yarn + +# start expo +yarn expo +``` + + - **使用 react-native-cli 运行** + +```bash +# clone +git clone git@github.com:ant-design/ant-design-mobile-rn.git + +# go to ant rn folder +cd ant-design-mobile-rn + +# install dependencies +yarn + +# start ios +cd rn-kitchen-sink/ios && pod install +yarn ios + +# start android +yarn android +``` ## 欢迎贡献 diff --git a/components/date-picker-view/index.en-US.md b/components/date-picker-view/index.en-US.md index 16cfc1eb5..4133f3309 100644 --- a/components/date-picker-view/index.en-US.md +++ b/components/date-picker-view/index.en-US.md @@ -45,4 +45,4 @@ Properties | Descrition | Type | Default | Version | filter | Filter available time | `DatePickerFilter` | - | `5.1.0` | -In addition, the following attributes of [PickerView](/components/picker-view) are supported: `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `renderMaskTop` `renderMaskBottom` +In addition, the following properties of [PickerView](/components/picker-view) are supported: `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `renderMaskTop` `renderMaskBottom` diff --git a/components/date-picker/index.en-US.md b/components/date-picker/index.en-US.md index 83473547b..5f540f2c4 100644 --- a/components/date-picker/index.en-US.md +++ b/components/date-picker/index.en-US.md @@ -50,14 +50,14 @@ Properties | Descrition | Type | Default | Version | filter | Filter available time | `DatePickerFilter` | - | `5.1.0` | -In addition, the following attributes of [Picker](/components/picker) are supported: `onPickerChange` `onVisibleChange` `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `title` `okText` `dismissText` `okButtonProps` `dismissButtonProps` `visible` `children` `renderMaskTop` `renderMaskBottom` +In addition, the following properties of [Picker](/components/picker) are supported: `onPickerChange` `onVisibleChange` `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `title` `okText` `dismissText` `okButtonProps` `dismissButtonProps` `visible` `children` `renderMaskTop` `renderMaskBottom` ### Children -Same as [Picker](/components/picker/#Children), except type `format` is different: +Same as [Picker](/components/picker#children), except type `format` is different: Properties | Descrition | Type | Default ----|-----|------|------ | format | format the selected value |`(value: Date) => date string` | import [Day.js Format](https://day.js.org/docs/en/parse/string-format), precision:`YYYY-MM-DD`,`YYYY-MM-DD HH:mm:ss`| ### Ref -Same as `Picker` \ No newline at end of file +Same as [Picker](/components/picker#ref) \ No newline at end of file diff --git a/components/date-picker/index.zh-CN.md b/components/date-picker/index.zh-CN.md index 10f1ce244..f81bfe410 100644 --- a/components/date-picker/index.zh-CN.md +++ b/components/date-picker/index.zh-CN.md @@ -54,11 +54,11 @@ type DatePickerFilter = Partial< 此外还支持 [Picker](/components/picker-cn) 的以下属性:`onPickerChange` `onVisibleChange` `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `title` `okText` `dismissText` `okButtonProps` `dismissButtonProps` `visible` `children` `renderMaskTop` `renderMaskBottom` ### Children -同 [Picker](/components/picker-cn/#Children),其中`format`类型不同: +同 [Picker](/components/picker-cn#children),其中`format`类型不同: 属性 | 说明 | 类型 | 默认值 ----|-----|------|------ | format | 格式化选中的值 |`(value: Date) => date string` | 引用 [Day.js Format](https://day.js.org/docs/en/parse/string-format),参数对应精度:`YYYY-MM-DD`,`YYYY-MM-DD HH:mm:ss`| ### Ref -同 `Picker`。 +同 [Picker](/components/picker-cn#ref)。 diff --git a/components/image-picker/index.en-US.md b/components/image-picker/index.en-US.md index ec92b258b..aef2ebc4e 100644 --- a/components/image-picker/index.en-US.md +++ b/components/image-picker/index.en-US.md @@ -4,3 +4,5 @@ type: Data Entry title: DatePickerView subtitle: (deprecated) --- + +Deprecated since `5.1.0`. \ No newline at end of file diff --git a/components/image-picker/index.zh-CN.md b/components/image-picker/index.zh-CN.md index a2ba39099..b1f70f4d1 100644 --- a/components/image-picker/index.zh-CN.md +++ b/components/image-picker/index.zh-CN.md @@ -4,3 +4,5 @@ type: Data Entry title: ImagePicker subtitle: 图片选择器(已弃用) --- + +`5.1.0`开始已弃用。 \ No newline at end of file diff --git a/components/input-item/index.en-US.md b/components/input-item/index.en-US.md index 362aaaf8d..a93d065e2 100644 --- a/components/input-item/index.en-US.md +++ b/components/input-item/index.en-US.md @@ -5,7 +5,7 @@ title: InputItem subtitle: (archived) --- -> This package has been deprecated, recommend [components/Input](/components/input) +> This package has been deprecated in `5.2.0`, recommend [components/Input](/components/input) A foundational component for inputting text into the app via a keyboard. diff --git a/components/input-item/index.zh-CN.md b/components/input-item/index.zh-CN.md index 9eb4f7fc8..455f712cd 100644 --- a/components/input-item/index.zh-CN.md +++ b/components/input-item/index.zh-CN.md @@ -5,7 +5,7 @@ title: InputItem subtitle: 文本输入(归档) --- -> 已停止更新,推荐使用 [components/Input](/components/input-cn) +> `5.2.0` 已停止更新,推荐使用 [components/Input](/components/input-cn) 用于接受单行文本。 diff --git a/components/input/Input.tsx b/components/input/Input.tsx index dab8646cb..848b9a582 100644 --- a/components/input/Input.tsx +++ b/components/input/Input.tsx @@ -33,12 +33,14 @@ const InternalInput: React.ForwardRefRenderFunction = ( const { allowClear, disabled = contextDisabled, - editable = true, + readOnly, + editable = !readOnly, maxLength, prefix, showCount, status: customStatus, style, + inputStyle, suffix, themeStyles = InputStyles, type, @@ -322,7 +324,7 @@ const InternalInput: React.ForwardRefRenderFunction = ( } }, [feedbackIcon, hasFeedback, statusClassName, styles.suffix, suffix]) return ( - + {prefixDom} = ( onSubmitEditing={Keyboard.dismiss} {...restProps} keyboardType={keyboardType} - style={[styles.input, statusClassName, style]} + style={[styles.input, statusClassName, inputStyle]} ref={inputRef} /> {clearIconDom} diff --git a/components/input/PropsType.tsx b/components/input/PropsType.tsx index 6666fb54a..c4c0817b7 100644 --- a/components/input/PropsType.tsx +++ b/components/input/PropsType.tsx @@ -1,5 +1,10 @@ import React from 'react' -import { KeyboardTypeOptions, TextInputProps } from 'react-native' +import { + KeyboardTypeOptions, + StyleProp, + TextInputProps, + ViewStyle, +} from 'react-native' import { Theme } from '../style' import { InputStyle } from './style/index' import { TextAreaStyle } from './style/textarea' @@ -12,11 +17,12 @@ export interface AutoSizeType { maxRows?: number } export type InputStatus = 'error' | 'warning' -export interface InputProps extends TextInputProps { +export interface InputProps extends Omit { allowClear?: boolean | ClearIconType disabled?: boolean maxLength?: number prefix?: React.ReactNode + readOnly?: boolean showCount?: | boolean | { @@ -28,6 +34,8 @@ export interface InputProps extends TextInputProps { }) => React.ReactNode } status?: InputStatus + inputStyle?: TextInputProps['style'] + style?: StyleProp styles?: Partial suffix?: React.ReactNode themeStyles?: (theme: Theme) => Partial diff --git a/components/input/index.en-US.md b/components/input/index.en-US.md index 5c7cc32b9..34b03cde3 100644 --- a/components/input/index.en-US.md +++ b/components/input/index.en-US.md @@ -24,6 +24,8 @@ Entering content through the keyboard is the most basic form field wrapper. | prefix | The prefix icon for the Input | ReactNode | - | | showCount | Whether to show character count | `boolean` \| `{ formatter: (info: { value: string, count: number, maxLength?: number }) => ReactNode }` | false | | status | Set validation status | 'error' \| 'warning' | - | +| inputStyle | TextInput style | `StyleProp` | - | +| style | Container style | `StyleProp` | - | | styles | Semantic DOM style | [InputStyle](#inputstyle-interface) | - | | suffix | The suffix icon for the Input | ReactNode | - | | type | Declare the Input type, the same as the native [keyboardType](http://facebook.github.io/react-native/docs/textinput.html#keyboardtype) attribute | 'text' \| 'number' \| 'password' \| KeyboardTypeOptions | `text` | @@ -46,8 +48,8 @@ Same as Input, and more: ```typescript interface InputStyle { - container: ViewStyle - input: ViewStyle + container: ViewStyle // Same as `style` prop + input: ViewStyle // Same as `inputStyle` prop clearIcon: ViewStyle prefix: ViewStyle | TextStyle showCount: TextStyle @@ -56,6 +58,10 @@ interface InputStyle { error: TextStyle } ``` + +## Ref +Ref to [TextInput](http://facebook.github.io/react-native/docs/textinput.html) + ## FAQ ## When setting `allowClear` on the Android platform, why does it not work when I click clearIcon? diff --git a/components/input/index.zh-CN.md b/components/input/index.zh-CN.md index 4d7036e43..4cd06ef87 100644 --- a/components/input/index.zh-CN.md +++ b/components/input/index.zh-CN.md @@ -25,6 +25,8 @@ version: 5.2.0-rc.0 | prefix | 带有前缀图标的 input | ReactNode | - | | showCount | 是否展示字数 | `boolean` \| `{ formatter: (info: { value: string, count: number, maxLength?: number }) => ReactNode }` | false | | status | 设置校验状态 | 'error' \| 'warning' | - | +| inputStyle | TextInput style | `StyleProp` | - | +| style | 容器(container)样式 | `StyleProp` | - | | styles | 语义化结构 style | [InputStyle](#inputstyle-语义化样式) | - | | suffix | 带有后缀图标的 input | ReactNode | - | | type | 声明 Input 类型,同原生 [`keyboardType`](https://reactnative.dev/docs/textinput.html#keyboardtype) 属性 | 'text' \| 'number' \|'password' \| KeyboardTypeOptions | `text` | @@ -48,8 +50,8 @@ Input 的其他属性和 react-native 内置组件[TextInput](http://facebook.gi ```typescript interface InputStyle { - container: ViewStyle - input: ViewStyle + container: ViewStyle // 同 `style` prop + input: ViewStyle // 同 `inputStyle` prop clearIcon: ViewStyle prefix: ViewStyle | TextStyle showCount: TextStyle @@ -59,6 +61,9 @@ interface InputStyle { } ``` +## Ref +指向 [TextInput](http://facebook.github.io/react-native/docs/textinput.html) + ## FAQ ### 在Android平台设置`allowClear`,为什么我点击clearIcon无效? diff --git a/components/list/index.en-US.md b/components/list/index.en-US.md index 777862203..20302655f 100644 --- a/components/list/index.en-US.md +++ b/components/list/index.en-US.md @@ -37,7 +37,7 @@ Properties | Descrition | Type | Default | Version | ### TouchableHighlightProps -New in `5.2.0`. In addition, all attributes of [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) are supported; +New in `5.2.0`. In addition, all properties of [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) are supported; when setting `onPress` props, it has a default touch style:
`{ underlayColor:'#dddddd', activeOpacity: 0.5 }` diff --git a/components/popover/index.en-US.md b/components/popover/index.en-US.md index 1394551c4..161f6ce4a 100644 --- a/components/popover/index.en-US.md +++ b/components/popover/index.en-US.md @@ -2,8 +2,11 @@ category: Components type: Navigation title: Popover +subtitle: (archived) --- +> This package has been deprecated in `5.2.0`, recommend [components/Tooltip](/components/tooltip) + After clicking on a control or an area, a Popover menu appears for doing more. If set mask prop, it is recommended to exit by clicking on any of the mask layers. diff --git a/components/popover/index.zh-CN.md b/components/popover/index.zh-CN.md index 9abb3693f..4cc60abf4 100644 --- a/components/popover/index.zh-CN.md +++ b/components/popover/index.zh-CN.md @@ -2,9 +2,11 @@ category: Components type: Navigation title: Popover -subtitle: 气泡 +subtitle: 气泡(归档) --- +> `5.2.0`已停止更新,推荐使用 [components/Tooltip](/components/tooltip-cn) + 在点击控件或者某个区域后,浮出一个气泡菜单来做更多的操作。 如果设置了遮罩层,建议通过点击遮罩层的任一位置,进行退出。 diff --git a/components/textarea-item/index.en-US.md b/components/textarea-item/index.en-US.md index 4f24b3222..0a1f8ba00 100644 --- a/components/textarea-item/index.en-US.md +++ b/components/textarea-item/index.en-US.md @@ -5,7 +5,7 @@ title: TextareaItem subtitle: (archived) --- -> This package has been deprecated, recommend [components/Input.TextArea](/components/input#inputtextarea) +> This package has been deprecated in `5.2.0`, recommend [components/Input.TextArea](/components/input#inputtextarea) A foundational component for inputting multi-line text into the app via a keyboard. diff --git a/package.json b/package.json index 25b351828..e7179e59b 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,6 @@ "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", "@react-native-community/eslint-config": "^2.0.0", - "@react-native-community/segmented-control": "^2.2.2", - "@react-native-community/slider": "^3.0.3", "@react-navigation/native": "^6.1.1", "@react-navigation/stack": "^6.3.10", "@testing-library/jest-native": "^4.0.1", @@ -100,8 +98,6 @@ }, "peerDependencies": { "@ant-design/icons-react-native": ">= 2.3.1", - "@react-native-community/segmented-control": ">= 1.4.0", - "@react-native-community/slider": ">= 2.0.0", "react": ">=17.0.1", "react-native": ">=0.64.1", "react-native-gesture-handler": ">=2.2.1" diff --git a/rn-kitchen-sink/demoList.js b/rn-kitchen-sink/demoList.js index 9a9455983..01caf2d72 100644 --- a/rn-kitchen-sink/demoList.js +++ b/rn-kitchen-sink/demoList.js @@ -69,12 +69,6 @@ module.exports = { icon: 'https://os.alipayobjects.com/rmsportal/HzRRcuwtqUCCOBg.png', module: require('../components/modal/demo/basic'), }, - { - title: 'Popover', - description: '气泡', - icon: 'https://os.alipayobjects.com/rmsportal/iIOLFHTKUCxBgUV.png', - module: require('../components/popover/demo/basic'), - }, { title: 'Portal', description: 'portal', @@ -149,12 +143,12 @@ module.exports = { icon: 'https://os.alipayobjects.com/rmsportal/SdSqpihcSBoBrFO.png', module: require('../components/input/demo/basic'), }, - { - title: 'InputItem', - description: '文本输入', - icon: 'https://os.alipayobjects.com/rmsportal/SdSqpihcSBoBrFO.png', - module: require('../components/input-item/demo/basic'), - }, + // { + // title: 'InputItem', + // description: '文本输入', + // icon: 'https://os.alipayobjects.com/rmsportal/SdSqpihcSBoBrFO.png', + // module: require('../components/input-item/demo/basic'), + // }, { title: 'Picker', description: '选择器', @@ -203,12 +197,6 @@ module.exports = { icon: 'https://os.alipayobjects.com/rmsportal/nvQVDIUiTmXcJtO.png', module: require('../components/steps/demo/basic'), // 必须 }, - { - title: 'SegmentedControl', - description: '分段器', - icon: 'https://zos.alipayobjects.com/rmsportal/qCqRFuSbewqIWyv.png', - module: require('../components/segmented-control/demo/basic'), - }, { title: 'Switch', description: '滑动开关', @@ -217,7 +205,7 @@ module.exports = { }, { title: 'SwipeAction', // 必须 - description: '滑动或长按操作', + description: '手势滑动操作', icon: 'https://os.alipayobjects.com/rmsportal/nlDYwTsLwJdnaKc.png', module: require('../components/swipe-action/demo/basic'), }, @@ -233,12 +221,12 @@ module.exports = { icon: 'https://os.alipayobjects.com/rmsportal/gfstSzAhvXqKyEg.png', module: require('../components/tag/demo/basic'), }, - { - title: 'TextAreaItem', - description: '多行输入', - icon: 'https://os.alipayobjects.com/rmsportal/PfzuWlDVfndrQUK.png', - module: require('../components/textarea-item/demo/basic'), - }, + // { + // title: 'TextAreaItem', + // description: '多行输入', + // icon: 'https://os.alipayobjects.com/rmsportal/PfzuWlDVfndrQUK.png', + // module: require('../components/textarea-item/demo/basic'), + // }, ], OTHERS: [ { From 1f1a70971fb77469697623793061a1e2f2ffa71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 16:55:55 +0800 Subject: [PATCH 7/9] fix: picker-view style #1347 --- components/picker-view/picker-view.tsx | 2 +- components/picker-view/style/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/picker-view/picker-view.tsx b/components/picker-view/picker-view.tsx index 18a0ebd4e..b5bfb0b91 100644 --- a/components/picker-view/picker-view.tsx +++ b/components/picker-view/picker-view.tsx @@ -128,7 +128,7 @@ export default class RMCPickerView extends React.Component<
) : ( {(loading || columns?.length === 0) && loading !== false diff --git a/components/picker-view/style/index.tsx b/components/picker-view/style/index.tsx index 20d4bef47..9980e0bb4 100644 --- a/components/picker-view/style/index.tsx +++ b/components/picker-view/style/index.tsx @@ -16,11 +16,11 @@ export default () => flexDirection: 'column', justifyContent: 'center', overflow: 'hidden', + backgroundColor: '#fff', }, wheelWrapper: { display: 'flex', flexDirection: 'row', - backgroundColor: '#fff', }, mask: { position: 'absolute', From 91c8a9fda315fcf999f72ace5bf3216ae76b303a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 17:21:46 +0800 Subject: [PATCH 8/9] [5.2.0-rc.1]update REAME.md --- README.md | 4 +-- README.zh-CN.md | 4 +-- components/segmented-control/index.en-US.md | 26 +++++++++++++++-- components/segmented-control/index.zh-CN.md | 25 ++++++++++++++++- components/slider/index.en-US.md | 31 +++++++++++---------- components/slider/index.zh-CN.md | 31 +++++++++++---------- components/stepper/index.en-US.md | 1 + components/stepper/index.zh-CN.md | 1 + components/swipe-action/index.en-US.md | 1 + components/swipe-action/index.zh-CN.md | 1 + components/toast/index.en-US.md | 1 + components/toast/index.zh-CN.md | 1 + 12 files changed, 92 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index b4a45f73f..f3472117c 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ yarn add @ant-design/react-native ### Installing peer dependencies ```bash -npm install @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated +npm install @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler ``` or ```bash -yarn add @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated +yarn add @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler ``` > You need go to ios folder and run `pod install` (auto linking),Android will handle it by itself. diff --git a/README.zh-CN.md b/README.zh-CN.md index 2d2d19e6c..5ddede82f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -58,13 +58,13 @@ yarn add @ant-design/react-native ### 安装peer依赖 ```bash -npm install @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated +npm install @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler ``` or ```bash -yarn add @ant-design/icons-react-native react-native-gesture-handler react-native-reanimated +yarn add @react-native-community/segmented-control @react-native-community/slider @ant-design/icons-react-native react-native-gesture-handler ``` > 安装完依赖后需要到 iOS 目录 `pod install`(auto linking),Android 不需要手动处理 diff --git a/components/segmented-control/index.en-US.md b/components/segmented-control/index.en-US.md index 1e057c86a..cd7719c23 100644 --- a/components/segmented-control/index.en-US.md +++ b/components/segmented-control/index.en-US.md @@ -5,6 +5,28 @@ title: SegmentedControl subtitle: (deprecated) --- -Deprecated since `5.2.0`. +Will be deprecated in `5.2.0`. -Please use [@react-native-community/segmented-control](https://github.com/react-native-community/segmented-control#usage) instead. \ No newline at end of file +Please use [@react-native-community/segmented-control](https://github.com/react-native-community/segmented-control#usage) instead. + +------ + +`SegmentedControl` includes at least two segments, it is used to display diffrent views and recommended by `iOS`. + +### Rule +- It is similar to the functionality used for `Tabs`, so avoid to use them at same page as much as possible. +- You can use `SegmentedControl` with `NavBar` to display mutiple views. +- Generally there should be no more than 5 segments in one line, each segment has 2-4 words and needs simplified texts. +- Keep the length of the text consistent as much as possible. + +## API + +Properties | Descrition | Type | Default +-----------|------------|------|-------- +| style | style of component | Object | `{}` | +| tintColor | accent color of the control | String | `#2DB7F5` | +| disabled | whether the user is able to interact with the control | Boolean | false | +| selectedIndex | the index in `props.values` of the segment to be (pre)selected | Number | 0 | +| values | The labels for the control's segment buttons, in order | array | [] | +| onChange | callback that is called when the user taps a segment; passes the event object as an argument. `e.nativeEvent.selectedSegmentIndex` is selected index. `e.nativeEvent.value` is selected value. | (e): void | function(){} | +| onValueChange | callback that is called when the user taps a segment; passes the segment's value as an argument | (val): void | function(){} | diff --git a/components/segmented-control/index.zh-CN.md b/components/segmented-control/index.zh-CN.md index 504c68c4a..89406ce83 100644 --- a/components/segmented-control/index.zh-CN.md +++ b/components/segmented-control/index.zh-CN.md @@ -5,6 +5,29 @@ title: SegmentedControl subtitle: 分段器(已弃用) --- -`5.2.0`开始已弃用。 +即将从`5.2.0`中弃用。 请使用[@react-native-community/segmented-control](https://github.com/react-native-community/segmented-control#usage)替代。 + +----- + +由至少 2 个分段控件组成,用作不同视图的显示;是 iOS 的推荐组件。 + +### 规则 +- 和 Tabs 功能相似,尽可能避免一个页面中同时出现这两个组件。 +- 可以搭配 NavBar 一起使用,用于显示多个视图,分段数一般为 2 个。 +- 单独放置一行时,分段数最多为 5 个;文案需要精简,一般 2-4 个字。 +- 尽可能保持文案长度一致。 + +## API + +属性 | 说明 | 类型 | 默认值 +----|-----|------|------ +| style | 自定义样式 | Object | `{}` | +| tintColor | 组件主色调 | String | `#2DB7F5` | +| disabled | 是否启用 | Boolean | false | +| selectedIndex | 选中项在数组中的索引 | Number | 0 | +| values | 选项数组,值是字符串 | array | [] | +| onChange | 回调函数, 其中`e.nativeEvent.selectedSegmentIndex`是选中项索引, `e.nativeEvent.value`是选中的值. | (e): void | function(){} | +| onValueChange | 回调函数 | (val): void | function(){} | + diff --git a/components/slider/index.en-US.md b/components/slider/index.en-US.md index e5725fe4f..16f3539c2 100644 --- a/components/slider/index.en-US.md +++ b/components/slider/index.en-US.md @@ -2,6 +2,7 @@ category: Components type: Data Entry title: Slider +version: 5.2.0-rc.1 --- A Slider component for selecting particular value in range, eg: controls the display brightness of the screen. @@ -12,17 +13,19 @@ A Slider component for selecting particular value in range, eg: controls the dis ## API -| Properties | Description | Type | Default | -| --- | --- | --- | --- | -| defaultValue | Default value | `number \| [number, number]` | `range ? [0, 0] : 0` | -| disabled | Whether disabled | `boolean` | `false` | -| icon | The icon of slider | `ReactNode` | - | -| marks | Tick marks | `{ [key: number]: React.ReactNode }` | - | -| max | Max value | `number` | `100` | -| min | Min value | `number` | `0` | -| onAfterChange | Consistent with the trigger timing of `touchend`, pass the current value as a parameter | `(value: number \| [number, number]) => void` | - | -| onChange | Triggered when the slider is dragged, and the current dragged value is passed in as a parameter | `(value: number \| [number, number]) => void` | - | -| range | Whether it is a double sliders | `boolean` | `false` | -| step | Step distance, the value must be greater than `0`, and `(max-min)` can be divisible by `step`. When `marks` is not null, the configuration of `step` is invalid | `number` | `1` | -| ticks | Whether to display the scale | `boolean` | `false` | -| value | Current value | `number \| [number, number]` | - | \ No newline at end of file +| Properties | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| defaultValue | Default value | `number` \|
`[number, number]` | `range ? [0, 0] : 0` | | +| disabled | Whether disabled | `boolean` | `false` | | +| icon | The icon of slider | `ReactNode` | - | | +| marks | Tick marks | `{ [key: number]: React.ReactNode }` | - | `5.2.0` | +| max | Max value | `number` | `100` | | +| min | Min value | `number` | `0` | | +| onAfterChange | Consistent with the trigger timing of `touchend`, pass the current value as a parameter | `(value: number | [number, number]) => void` | - | | +| onChange | Triggered when the slider is dragged, and the current dragged value is passed in as a parameter | `(value: number | [number, number]) => void` | - | | +| range | Whether it is a double sliders | `boolean` | `false` | `5.2.0` | +| step | Step distance, the value must be greater than `0`, and `(max-min)` can be divisible by `step`. When `marks` is not null, the configuration of `step` is invalid | `number` | `1` | `5.2.0` | +| ticks | Whether to display the scale | `boolean` | `false` | `5.2.0` | +| value | Current value | `number` \|
`[number, number]` | - | | + +> The returned `value` format is `[number, number]` when `range={true}`, otherwise it is `number`. \ No newline at end of file diff --git a/components/slider/index.zh-CN.md b/components/slider/index.zh-CN.md index 4429d7e26..a9923c528 100644 --- a/components/slider/index.zh-CN.md +++ b/components/slider/index.zh-CN.md @@ -3,6 +3,7 @@ category: Components type: Data Entry title: Slider subtitle: 滑动输入条 +version: 5.2.0-rc.1 --- 允许用户在一个区间中选择特定值,eg:控制屏幕的显示亮度。 @@ -13,17 +14,19 @@ subtitle: 滑动输入条 ## API -| 属性 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| defaultValue | 默认值 | `number \| [number, number]` | `range ? [0, 0] : 0` | -| disabled | 是否禁用 | `boolean` | `false` | -| icon | 滑块的图标 | `ReactNode` | - | -| marks | 刻度标记 | `{ [key: number]: React.ReactNode }` | - | -| max | 最大值 | `number` | `100` | -| min | 最小值 | `number` | `0` | -| onAfterChange | 与 `touchend` 触发时机一致,把当前值作为参数传入 | `(value: number \| [number, number]) => void` | - | -| onChange | 拖拽滑块时触发,并把当前拖拽的值作为参数传入 | `(value: number \| [number, number]) => void` | - | -| range | 是否为双滑块 | `boolean` | `false` | -| step | 步距,取值必须大于 `0`,并且 `(max - min)` 可被 `step` 整除。当 `marks` 不为空对象时,`step` 的配置失效 | `number` | `1` | -| ticks | 是否显示刻度 | `boolean` | `false` | -| value | 当前值 | `number \| [number, number]` | - | +| 属性 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| defaultValue | 默认值 | `number` \|
`[number, number]` | `range ? [0, 0] : 0` | | +| disabled | 是否禁用 | `boolean` | `false` | | +| icon | 滑块的图标 | `ReactNode` | - | | +| marks | 刻度标记 | `{ [key: number]: React.ReactNode }` | - | `5.2.0` | +| max | 最大值 | `number` | `100` | | +| min | 最小值 | `number` | `0` | | +| onAfterChange | 与 `touchend` 触发时机一致,把当前值作为参数传入 | `(value: number | [number, number]) => void` | - | | +| onChange | 拖拽滑块时触发,并把当前拖拽的值作为参数传入 | `(value: number | [number, number]) => void` | - | | +| range | 是否为双滑块 | `boolean` | `false` | `5.2.0` | +| step | 步距,取值必须大于 `0`,并且 `(max - min)` 可被 `step` 整除。当 `marks` 不为空对象时,`step` 的配置失效 | `number` | `1` | `5.2.0` | +| ticks | 是否显示刻度 | `boolean` | `false` | `5.2.0` | +| value | 当前值 | `number` \|
`[number, number]` | - | | + +> 当 `range={true}` 时返回的 `value` 格式为 `[number, number]` ,否则为 `number` 。 \ No newline at end of file diff --git a/components/stepper/index.en-US.md b/components/stepper/index.en-US.md index 311702ffa..064131786 100644 --- a/components/stepper/index.en-US.md +++ b/components/stepper/index.en-US.md @@ -2,6 +2,7 @@ category: Components type: Data Entry title: Stepper +version: 5.2.0-rc.1 --- `Stepper` can be used to increase or decrease value step by step. diff --git a/components/stepper/index.zh-CN.md b/components/stepper/index.zh-CN.md index c3728e400..68c25b1ad 100644 --- a/components/stepper/index.zh-CN.md +++ b/components/stepper/index.zh-CN.md @@ -3,6 +3,7 @@ category: Components type: Data Entry title: Stepper subtitle: 步进器 +version: 5.2.0-rc.1 --- 用作增加或者减少当前数值。 diff --git a/components/swipe-action/index.en-US.md b/components/swipe-action/index.en-US.md index 58e3ff436..1a130b410 100644 --- a/components/swipe-action/index.en-US.md +++ b/components/swipe-action/index.en-US.md @@ -2,6 +2,7 @@ category: Components type: Gesture title: SwipeAction +version: 5.2.0-rc.1 --- iOS-style swipeout buttons that appear from behind a component. diff --git a/components/swipe-action/index.zh-CN.md b/components/swipe-action/index.zh-CN.md index c883fe757..0a89b9724 100644 --- a/components/swipe-action/index.zh-CN.md +++ b/components/swipe-action/index.zh-CN.md @@ -3,6 +3,7 @@ category: Components type: Gesture title: SwipeAction subtitle: 滑动操作 +version: 5.2.0-rc.1 --- 滑动操作组件。 diff --git a/components/toast/index.en-US.md b/components/toast/index.en-US.md index f6bc39812..0d306e099 100644 --- a/components/toast/index.en-US.md +++ b/components/toast/index.en-US.md @@ -2,6 +2,7 @@ category: Components type: Feedback title: Toast +version: 5.2.0-rc.1 --- A lightweight feedback or tips, used to display content that does not interrupt user operations. Suitable for page transitions, data interaction and other scenes. diff --git a/components/toast/index.zh-CN.md b/components/toast/index.zh-CN.md index c32677815..1b79c335c 100644 --- a/components/toast/index.zh-CN.md +++ b/components/toast/index.zh-CN.md @@ -3,6 +3,7 @@ category: Components type: Feedback title: Toast subtitle: 轻提示 +version: 5.2.0-rc.1 --- 一种轻量级反馈/提示,可以用来显示不会打断用户操作的内容,适合用于页面转场、数据交互的等场景中。 From b3665d6d6e97fa6e1cdd4f9e9ca4a38ac024e6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E5=9D=A4?= Date: Mon, 8 Jul 2024 18:01:48 +0800 Subject: [PATCH 9/9] [5.2.0-rc.1] chore: test --- .jest.config.js | 3 +- .../__snapshots__/index.test.js.snap | 27 +- .../__tests__/__snapshots__/demo.test.js.snap | 2552 +++++++++-- .../__tests__/__snapshots__/demo.test.js.snap | 3855 +++++++++++++++-- .../__tests__/__snapshots__/demo.test.js.snap | 515 ++- .../__tests__/__snapshots__/demo.test.js.snap | 2135 ++++----- package.json | 8 +- tests/__snapshots__/index.test.js.snap | 2 +- yarn.lock | 337 +- 9 files changed, 7664 insertions(+), 1770 deletions(-) diff --git a/.jest.config.js b/.jest.config.js index 9f9bed91f..cf06532b8 100644 --- a/.jest.config.js +++ b/.jest.config.js @@ -8,7 +8,8 @@ const transformPackages = [ 'react-native-collapsible', '@bang88/react-native-ultimate-listview', '@react-native-community', - 'react-native-gesture-handler' + 'react-native-gesture-handler', + 'react-native-reanimated' ]; module.exports = { diff --git a/components/picker/__tests__/__snapshots__/index.test.js.snap b/components/picker/__tests__/__snapshots__/index.test.js.snap index 9d99aad68..6057978c1 100644 --- a/components/picker/__tests__/__snapshots__/index.test.js.snap +++ b/components/picker/__tests__/__snapshots__/index.test.js.snap @@ -2,23 +2,28 @@ exports[`Picker test cascade props when cascade={true} should be render MultiPicker 1`] = ` Array [ - - - 省市选择 - - , + + + 省市选择 + + + , + - - Default settings - - + - - - - - Initial value: 0.5 - - - + 基础用法 + + + - - - - - min: 0, max: 1, current Value: - 0 - - - + + + + + + + + + + + + + + + + + + + + + + + /> + - - - - step: 0.25 - - + - - - - - disabled - - - + 显示刻度(ticks)并指定步距(step) + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - onChange value: - 0.25 - - + + 传入刻度标记(marks) + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 20 + + + + + 40 + + + + + 60 + + + + + 80 + + + + + 100 + + + + + + + + + + - - - - onAfterChange value: - 0.15 - - + + 最大(max)/最小值(min) + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - custom color: - - + + 双滑块(range) + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 20 + + + + + 40 + + + + + 60 + + + + + 80 + + + + + 100 + + + + + + + + + + - + `; diff --git a/components/stepper/__tests__/__snapshots__/demo.test.js.snap b/components/stepper/__tests__/__snapshots__/demo.test.js.snap index 757e814f0..4fb3ffcbc 100644 --- a/components/stepper/__tests__/__snapshots__/demo.test.js.snap +++ b/components/stepper/__tests__/__snapshots__/demo.test.js.snap @@ -2,594 +2,3281 @@ exports[`renders ./components/stepper/demo/basic.tsx correctly 1`] = ` - - - + + + + + + 基础用法 + + - - readOnly: true - + + + 基础用法 + + + + + + + - + + + + + + + + + + + + + + + + - + + 步长设置 + + + - - - + + + + - + + + + + + + + + + + + + - + + + + - + + + 限制输入范围 + + + + + + + - + + + + + + + + + + + + + + + + + + + - + + 格式化到一位小数 + + + - + - + + + + - + + + + + + + + + + + + + - - - - - - readOnly: false - + + + 自定义格式 + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + 状态/样式 + + + + - + + 禁用状态 + + + - - - + + + + - + + + + + + + + + + + + + - + + + + - + + + 输入框只读状态 + + + + + + + - + + + + + + + + + + + + + + + + + + + - + + 获得/失去焦点 + + + - + - + + + + - + + + + + + + + + + + + + - - - - - - Disabled - + + + 允许清空 + + + + + + + - + + + + + + + + + + + + + + + + - + + 自定义样式 + + + - - - + + + + - + + + + + + + + + + + + + - + + + + + + + + stringMode + + + + + + - + + + + + + - + + + + + + + + + + + + + + + + + + + + + stringMode control + + + + - - + - + + + + + + + - + + + + + + + + + + + + + + + + + + - - + `; diff --git a/components/swipe-action/__tests__/__snapshots__/demo.test.js.snap b/components/swipe-action/__tests__/__snapshots__/demo.test.js.snap index a5476f26b..5a1e543bc 100644 --- a/components/swipe-action/__tests__/__snapshots__/demo.test.js.snap +++ b/components/swipe-action/__tests__/__snapshots__/demo.test.js.snap @@ -48,17 +48,17 @@ exports[`renders ./components/swipe-action/demo/basic.tsx correctly 1`] = ` } > + Delete + + + + + + + + + + + + + Simple example: left and right buttons + + + + + extra content + + + + + + + + + + + + + + + + + + + + Read + + + + + + + + Reply + + + + + + + + + + + + + More + + + + + + + - + - - Enable Mask - - - - + Enable mask + + + + 是否显示透明蒙层,防止触摸穿透 + + + + - + + /> + + - - - - Enable Stack - - - - + Enable stackable + + + + 是否允许叠加显示 + + + + - + + /> + + + + + + + + + 图标 + - - - - - - - Without mask - - - - - - - - Stackable toast - - - - - - - - Text toast - - - - - - - + + + 成功 + + + + - Success toast - - - - - - - + + + 失败 + + + + + + + 网络失败 + + + + - Failed toast - - - - - - - + + + 加载中 + + + + + + + 自定义图标 + + + + - Network failure toast - + "backgroundColor": "#ffffff", + "borderBottomColor": "#dddddd", + "borderBottomWidth": 0.5, + "bottom": 0, + "height": 1, + "left": 0, + "position": "absolute", + "right": 0, + } + } + /> + - - - - - + + + 更多功能 + + + - Loading toast - - - - - - - + + + 顶部提示 + + + + - Toast.info with duration = 0 - - - - - - - + + + 底部提示 + + + + + + + 动态内容 + + + + - Toast.loading with duration = 0 - + "backgroundColor": "#ffffff", + "borderBottomColor": "#dddddd", + "borderBottomWidth": 0.5, + "bottom": 0, + "height": 1, + "left": 0, + "position": "absolute", + "right": 0, + } + } + /> + - - - - - + + + 手动清除 + + + - Toast with custom view - + + + + 显示 + + + + + + + 清除 + + + + + + + 关闭所有 + + + + + - + `; diff --git a/package.json b/package.json index e7179e59b..ea86b803b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ant-design/react-native", - "version": "5.2.0-rc.0", + "version": "5.2.0-rc.1", "description": "基于蚂蚁金服移动设计规范的 React Native 组件库", "keywords": [ "ant", @@ -31,6 +31,7 @@ "module": "es/index.js", "dependencies": { "@bang88/react-native-ultimate-listview": "^4.1.0", + "@rc-component/mini-decimal": "^1.1.0", "@types/shallowequal": "^1.1.1", "babel-runtime": "^6.x", "classnames": "^2.2.1", @@ -86,7 +87,7 @@ "react-native": "0.64.2", "react-native-gesture-handler": "~2.2.1", "react-native-mocker": "^0.0.12", - "react-native-reanimated": "^2.2.0", + "react-native-reanimated": "^2.8.0", "react-native-safe-area-context": "4.2.4", "react-native-screens": "^3.14.1", "react-native-web": "^0.17.6", @@ -100,7 +101,8 @@ "@ant-design/icons-react-native": ">= 2.3.1", "react": ">=17.0.1", "react-native": ">=0.64.1", - "react-native-gesture-handler": ">=2.2.1" + "react-native-gesture-handler": ">=2.2.1", + "react-native-reanimated": ">=2.8.0" }, "scripts": { "lint": "npm run tslint && npm run srclint && npm run applint", diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap index 6a148f3b5..243e39f2d 100644 --- a/tests/__snapshots__/index.test.js.snap +++ b/tests/__snapshots__/index.test.js.snap @@ -33,7 +33,6 @@ Array [ "Radio", "Result", "SearchBar", - "SegmentedControl", "Slider", "Stepper", "Steps", @@ -49,5 +48,6 @@ Array [ "WhiteSpace", "WingBlank", "ImagePicker", + "SegmentedControl", ] `; diff --git a/yarn.lock b/yarn.lock index a906560df..9f516181a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -116,6 +116,14 @@ dependencies: "@babel/highlight" "^7.12.13" +"@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4": version "7.14.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" @@ -151,6 +159,16 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" + integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== + dependencies: + "@babel/types" "^7.24.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -158,6 +176,13 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-annotate-as-pure@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" + integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz#6bc20361c88b0a74d05137a65cac8d3cbf6f61fc" @@ -188,6 +213,21 @@ "@babel/helper-replace-supers" "^7.14.4" "@babel/helper-split-export-declaration" "^7.12.13" +"@babel/helper-create-class-features-plugin@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz#2eaed36b3a1c11c53bdf80d53838b293c52f5b3b" + integrity sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.7" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.12.13": version "7.14.3" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.3.tgz#149aa6d78c016e318c43e2409a0ae9c136a86688" @@ -210,6 +250,13 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9" + integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -226,6 +273,14 @@ "@babel/template" "^7.12.13" "@babel/types" "^7.14.2" +"@babel/helper-function-name@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" + integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== + dependencies: + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" @@ -241,6 +296,13 @@ "@babel/traverse" "^7.13.15" "@babel/types" "^7.13.16" +"@babel/helper-hoist-variables@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" + integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" @@ -248,6 +310,14 @@ dependencies: "@babel/types" "^7.13.12" +"@babel/helper-member-expression-to-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz#67613d068615a70e4ed5101099affc7a41c5225f" + integrity sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" @@ -255,6 +325,14 @@ dependencies: "@babel/types" "^7.13.12" +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0", "@babel/helper-module-transforms@^7.14.2": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5" @@ -269,6 +347,17 @@ "@babel/traverse" "^7.14.2" "@babel/types" "^7.14.2" +"@babel/helper-module-transforms@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz#31b6c9a2930679498db65b685b1698bfd6c7daf8" + integrity sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" @@ -276,11 +365,23 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-optimise-call-expression@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" + integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== + dependencies: + "@babel/types" "^7.24.7" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== +"@babel/helper-plugin-utils@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0" + integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== + "@babel/helper-remap-async-to-generator@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz#376a760d9f7b4b2077a9dd05aa9c3927cadb2209" @@ -300,6 +401,15 @@ "@babel/traverse" "^7.14.2" "@babel/types" "^7.14.4" +"@babel/helper-replace-supers@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz#f933b7eed81a1c0265740edc91491ce51250f765" + integrity sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg== + dependencies: + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.7" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-simple-access@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6" @@ -307,6 +417,14 @@ dependencies: "@babel/types" "^7.13.12" +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -314,6 +432,14 @@ dependencies: "@babel/types" "^7.12.1" +"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" + integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-split-export-declaration@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" @@ -321,16 +447,38 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-split-export-declaration@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" + integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== + dependencies: + "@babel/types" "^7.24.7" + +"@babel/helper-string-parser@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2" + integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== +"@babel/helper-validator-option@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz#24c3bb77c7a425d1742eec8fb433b5a1b38e62f6" + integrity sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== + "@babel/helper-wrap-function@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz#bdb5c66fda8526ec235ab894ad53a1235c79fcc4" @@ -359,11 +507,26 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.1.6", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3", "@babel/parser@^7.7.0": version "7.14.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18" integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA== +"@babel/parser@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" + integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -602,6 +765,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-jsx@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" + integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -665,6 +835,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-typescript@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" + integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" @@ -801,6 +978,15 @@ "@babel/helper-simple-access" "^7.13.12" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz#9fd5f7fdadee9085886b183f1ad13d1ab260f4ab" + integrity sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== + dependencies: + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/plugin-transform-modules-systemjs@^7.13.8": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" @@ -834,13 +1020,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-transform-object-assign@^7.0.0", "@babel/plugin-transform-object-assign@^7.10.4", "@babel/plugin-transform-object-assign@^7.2.0": +"@babel/plugin-transform-object-assign@^7.0.0", "@babel/plugin-transform-object-assign@^7.2.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.13.tgz#d9b9200a69e03403a813e44a933ad9f4bddfd050" integrity sha512-4QxDMc0lAOkIBSfCrnSGbAJ+4epDBF2XXwcLXuBcG1xl9u7LrktNVD4+LwhL47XuKVPQ7R25e/WdcV+h97HyZA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-transform-object-assign@^7.16.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.24.7.tgz#9d2cc7ee1482bd208fcc51974ca4f7649662c899" + integrity sha512-DOzAi77P9jSyPijHS7Z8vH0wLRcZH6wWxuIZgLAiy8FWOkcKMJmnyHjy2JM94k6A0QxlA/hlLh+R9T3GEryjNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" @@ -981,6 +1174,16 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-typescript" "^7.12.13" +"@babel/plugin-transform-typescript@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz#b006b3e0094bf0813d505e0c5485679eeaf4a881" + integrity sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-create-class-features-plugin" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-typescript" "^7.24.7" + "@babel/plugin-transform-unicode-escapes@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74" @@ -1124,6 +1327,17 @@ "@babel/helper-validator-option" "^7.12.17" "@babel/plugin-transform-typescript" "^7.13.0" +"@babel/preset-typescript@^7.16.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz#66cd86ea8f8c014855671d5ea9a737139cbbfef1" + integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" + "@babel/plugin-syntax-jsx" "^7.24.7" + "@babel/plugin-transform-modules-commonjs" "^7.24.7" + "@babel/plugin-transform-typescript" "^7.24.7" + "@babel/register@^7.0.0": version "7.13.16" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.13.16.tgz#ae3ab0b55c8ec28763877383c454f01521d9a53d" @@ -1158,6 +1372,15 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" +"@babel/template@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" + integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.4": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" @@ -1172,6 +1395,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5" + integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-hoist-variables" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.14.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" @@ -1180,6 +1419,15 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" +"@babel/types@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" + integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== + dependencies: + "@babel/helper-string-parser" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + "@bang88/react-native-ultimate-listview@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@bang88/react-native-ultimate-listview/-/react-native-ultimate-listview-4.1.0.tgz#efff14ce5a09e8c52116580cdfa17f253faffde2" @@ -1467,6 +1715,38 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1503,6 +1783,13 @@ dependencies: "@babel/runtime" "^7.24.4" +"@rc-component/mini-decimal@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" + integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== + dependencies: + "@babel/runtime" "^7.18.0" + "@react-native-community/cli-debugger-ui@^5.0.1-alpha.1": version "5.0.1-alpha.1" resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1-alpha.1.tgz#09a856ccd2954cf16eea59b14dd26ae66720e4e6" @@ -1651,16 +1938,6 @@ resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc" integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ== -"@react-native-community/segmented-control@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.2.tgz#4014256819ab8f40f6bc3a3929ff14a9d149cf04" - integrity sha512-14+4HnGVrg3USqMzcHCPCqPmPmaEj0ogQH4pHRFXjoVvJokzidXBcYyXl5yrwFcKGW6zTXI6Fx9Qgt4ydtS6tw== - -"@react-native-community/slider@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@react-native-community/slider/-/slider-3.0.3.tgz#830167fd757ba70ac638747ba3169b2dbae60330" - integrity sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw== - "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" @@ -9100,6 +9377,11 @@ lodash.debounce@^4.0.0, lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -9920,11 +10202,6 @@ mockdate@^2.0.1: resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-2.0.5.tgz#70c6abf9ed4b2dae65c81dfc170dd1a5cec53620" integrity sha512-ST0PnThzWKcgSLyc+ugLVql45PvESt3Ul/wrdV/OPc/6Pr8dbLAIJsN1cIp41FLzbN+srVTNIRn+5Cju0nyV6A== -mockdate@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb" - integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ== - moment@2.x, moment@^2.22.2: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" @@ -10886,6 +11163,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" @@ -12470,14 +12752,16 @@ react-native-modal-popover@^2.0.1: lodash "^4.17.20" prop-types "^15.7.2" -react-native-reanimated@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.2.0.tgz#a6412c56b4e591d1f00fac949f62d0c72c357c78" - integrity sha512-lOJDd+5w1gY6DHGXG2jD1dsjzQmXQ2699HUc3IztvI2WP4zUT+UAA+zSG+5JiBS5DUnTL8YhhkmUQmr1KNGO5w== +react-native-reanimated@^2.8.0: + version "2.17.0" + resolved "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.17.0.tgz#eae2308235961cdd79810e01dfdd7e88b1ae5b5c" + integrity sha512-bVy+FUEaHXq4i+aPPqzGeor1rG4scgVNBbBz21ohvC7iMpB9IIgvGsmy1FAoodZhZ5sa3EPF67Rcec76F1PXlQ== dependencies: - "@babel/plugin-transform-object-assign" "^7.10.4" - fbjs "^3.0.0" - mockdate "^3.0.2" + "@babel/plugin-transform-object-assign" "^7.16.7" + "@babel/preset-typescript" "^7.16.7" + invariant "^2.2.4" + lodash.isequal "^4.5.0" + setimmediate "^1.0.5" string-hash-64 "^1.0.3" react-native-safe-area-context@4.2.4: @@ -13408,6 +13692,11 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" @@ -15468,10 +15757,8 @@ watchpack@^1.7.4: resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: - chokidar "^3.4.1" graceful-fs "^4.1.2" neo-async "^2.5.0" - watchpack-chokidar2 "^2.0.1" optionalDependencies: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1"