diff --git a/components/checkbox/AgreeItem.tsx b/components/checkbox/AgreeItem.tsx index b4f5a545..5b7c19c9 100644 --- a/components/checkbox/AgreeItem.tsx +++ b/components/checkbox/AgreeItem.tsx @@ -1,72 +1,16 @@ import React from 'react' -import { - ImageStyle, - StyleProp, - Text, - TouchableWithoutFeedback, - View, - ViewStyle, -} from 'react-native' -import { WithTheme, WithThemeStyles } from '../style' -import Checkbox from './Checkbox' -import { CheckboxPropsType } from './PropsType' -import AgreeItemstyles, { CheckboxStyle } from './style/index' +import Checkbox, { CheckboxProps } from './Checkbox' -export interface AgreeItemProps - extends CheckboxPropsType, - WithThemeStyles { - checkboxStyle?: StyleProp - style?: StyleProp +const AgreeItem = (props: CheckboxProps) => { + return ( + + ) } -export default class AgreeItem extends React.Component { - checkbox: Checkbox | null - - handleClick = () => { - if (this.checkbox) { - this.checkbox.handleClick() - } - } - - render() { - const { - style, - checkboxStyle, - children, - disabled, - checked, - defaultChecked, - onChange, - } = this.props - - return ( - - {(styles) => { - const contentDom = !children ? null : React.isValidElement( - children, - ) ? ( - children - ) : ( - {children} - ) - - return ( - - - (this.checkbox = ref)} - style={[styles.agreeItemCheckbox, checkboxStyle]} - disabled={disabled} - checked={checked} - defaultChecked={defaultChecked} - onChange={onChange} - /> - {contentDom} - - - ) - }} - - ) - } -} +export default AgreeItem diff --git a/components/checkbox/Checkbox.tsx b/components/checkbox/Checkbox.tsx index a58b9899..aa62cb91 100644 --- a/components/checkbox/Checkbox.tsx +++ b/components/checkbox/Checkbox.tsx @@ -1,101 +1,181 @@ -import React from 'react' +import classNames from 'classnames' +import useMergedState from 'rc-util/lib/hooks/useMergedState' +import * as React from 'react' import { + Animated, + Easing, + Platform, + Pressable, StyleProp, - Text, - TextStyle, - TouchableWithoutFeedback, + TouchableNativeFeedback, View, + ViewStyle, } from 'react-native' -import Icon from '../icon' import { WithTheme, WithThemeStyles } from '../style' +import AntmView from '../view/index' +import devWarning from '../_util/devWarning' +import { useAnimatedTiming } from '../_util/hooks/useAnimations' import { CheckboxPropsType } from './PropsType' import CheckboxStyles, { CheckboxStyle } from './style/index' export interface CheckboxProps extends CheckboxPropsType, WithThemeStyles { - style?: StyleProp + style?: StyleProp +} +//TODO: ref interface +export interface RefCheckboxProps { + onPress: () => void } -export default class Checkbox extends React.Component { - static CheckboxItem: any - static AgreeItem: any +const InternalCheckbox = ( + { + prefixCls = 'checkbox', + style, + styles, + children, + defaultChecked, + disabled, + onChange, + indeterminate, + ...restProps + }: CheckboxProps, + ref: React.Ref, +) => { + devWarning( + 'checked' in restProps || !('value' in restProps), + 'Checkbox', + '`value` is not a valid prop, do you mean `checked`?', + ) - constructor(props: CheckboxProps, context: any) { - super(props, context) + const [innerChecked, setInnerChecked] = useMergedState(false, { + value: restProps.checked, + defaultValue: defaultChecked, + }) - this.state = { - checked: props.checked || props.defaultChecked || false, - } + const [animatedValue, animate] = useAnimatedTiming() + const transitionOpacity = { + opacity: animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1], + }), + } + const transitionTransform = { + transform: [ + { rotate: '45deg' }, + { + scale: animatedValue.interpolate({ + inputRange: [0, 1], + outputRange: [0, 1], + }), + }, + ], } - UNSAFE_componentWillReceiveProps(nextProps: CheckboxProps): void { - if (typeof nextProps.checked === 'boolean') { - this.setState({ - checked: !!nextProps.checked, + //initial animate or receive props + React.useEffect(() => { + animate({ + toValue: innerChecked ? 1 : 0, + duration: 300, + easing: Easing.bezier(0.68, -0.55, 0.27, 1.55), + }) + }, [animate, innerChecked]) + + function triggerChange(newChecked: boolean) { + let mergedChecked = innerChecked + + if (!disabled) { + mergedChecked = newChecked + !('checked' in restProps) && setInnerChecked(mergedChecked) + onChange?.({ + target: { + checked: mergedChecked, + }, }) } + + return mergedChecked + } + const onInternalClick = () => { + triggerChange(!innerChecked) } - handleClick = () => { - if (this.props.disabled) { - return - } - const checked = !this.state.checked - if (!(typeof this.props.checked === 'boolean')) { - this.setState({ - checked, - }) + class Checkbox extends React.Component { + onPress = () => { + onInternalClick() } - if (this.props.onChange) { - this.props.onChange({ target: { checked } }) - } - } + render() { + return ( + + {(_styles) => { + const antd_checkbox = classNames(`${prefixCls}`, { + [`${prefixCls}_checked`]: innerChecked, + [`${prefixCls}_disabled`]: disabled, + }) + .split(' ') + .map((a) => _styles[a]) - render() { - const { style, disabled, children } = this.props - const checked = this.state.checked - return ( - - {(styles, theme) => { - let icon - if (checked) { - icon = disabled ? ( - - ) : ( - - ) - } else { - icon = disabled ? ( - - ) : ( - - ) - } - - return ( - - - {icon} - {typeof children === 'string' ? ( - // tslint:disable-next-line:jsx-no-multiline-js - {this.props.children} - ) : ( - children - )} + const antd_checkbox_inner = classNames(`${prefixCls}_inner`, { + [`${prefixCls}_inner_indeterminate`]: indeterminate, + [`${prefixCls}_inner_disabled`]: disabled, + }) + .split(' ') + .map((a) => _styles[a]) + + const antd_checkbox_inner_after = classNames(undefined, { + [`${prefixCls}_inner_after`]: !indeterminate, + [`${prefixCls}_inner_after_indeterminate`]: indeterminate, + [`${prefixCls}_inner_after_disabled`]: disabled, + }) + .split(' ') + .map((a) => _styles[a]) + + const antd_checlbox_label = classNames(`${prefixCls}_label`, { + [`${prefixCls}_label_disabled`]: disabled, + }) + .split(' ') + .map((a) => _styles[a]) + + const Color = innerChecked + ? _styles.checkbox_checked?.borderColor + : _styles.checkbox?.borderColor + return ( + + + = 21 + ? TouchableNativeFeedback.Ripple(Color || '', true, 13) + : TouchableNativeFeedback.SelectableBackground() + } + useForeground={true} + disabled={disabled} + onPress={this.onPress}> + + + + + + + + {children} + - - ) - }} - - ) + ) + }} + + ) + } } + + return } + +const AntmCheckbox = React.forwardRef(InternalCheckbox) +AntmCheckbox.displayName = 'AntmCheckbox' + +export default AntmCheckbox diff --git a/components/checkbox/CheckboxItem.tsx b/components/checkbox/CheckboxItem.tsx index 6cc1f23b..fdf44fe2 100644 --- a/components/checkbox/CheckboxItem.tsx +++ b/components/checkbox/CheckboxItem.tsx @@ -1,28 +1,26 @@ +import classNames from 'classnames' import React from 'react' -import { StyleProp, TextStyle, ViewStyle } from 'react-native' +import { StyleProp, ViewStyle } from 'react-native' import List from '../list/index' -import { WithTheme, WithThemeStyles } from '../style' -import Checkbox from './Checkbox' +import { ListItemPropsType } from '../list/PropsType' +import { WithTheme } from '../style' +import Text from '../text' +import Checkbox, { RefCheckboxProps } from './Checkbox' import { CheckboxItemPropsType } from './PropsType' -import CheckboxItemStyles, { CheckboxStyle } from './style/index' +import CheckboxStyles from './style/index' const ListItem = List.Item -export interface CheckboxItemProps - extends CheckboxItemPropsType, - WithThemeStyles { - checkboxStyle?: StyleProp +interface CheckboxItemProps extends CheckboxItemPropsType, ListItemPropsType { style?: StyleProp + styles?: { [key: string]: StyleProp } } +export default class CheckboxItem extends React.PureComponent { + checkbox: RefCheckboxProps -export default class CheckboxItem extends React.Component< - CheckboxItemProps, - any -> { - checkbox: Checkbox | null handleClick = () => { if (this.checkbox) { - this.checkbox.handleClick() + this.checkbox.onPress() } if (this.props.onPress) { this.props.onPress() @@ -32,37 +30,48 @@ export default class CheckboxItem extends React.Component< render() { const { style, - checkboxStyle, - defaultChecked, - checked, disabled, children, - extra, - onChange, + right, + left = !right, + ...restProps } = this.props - const thumbNode = ( - - {(styles) => ( - (this.checkbox = ref)} - style={[styles.checkboxItemCheckbox, checkboxStyle]} - defaultChecked={defaultChecked} - checked={checked} - onChange={onChange} - disabled={disabled} - /> - )} - - ) return ( - - {children} - + + {(styles) => { + const thumbNode = ( + (this.checkbox = ref)} + disabled={disabled} + {...restProps} + /> + ) + const listProps = { + ...restProps, + thumb: left && !right ? thumbNode : undefined, + extra: right ? thumbNode : undefined, + } + const antd_checlbox_label = classNames( + `${restProps.prefixCls || 'checkbox'}_label`, + { + [`${restProps.prefixCls || 'checkbox'}_label_disabled`]: disabled, + }, + ) + .split(' ') + .map((a) => styles[a]) + return ( + + + {children} + + + ) + }} + ) } } diff --git a/components/checkbox/PropsType.tsx b/components/checkbox/PropsType.tsx index 0f0f7843..5cc2e686 100644 --- a/components/checkbox/PropsType.tsx +++ b/components/checkbox/PropsType.tsx @@ -9,11 +9,14 @@ export interface CheckboxPropsType { defaultChecked?: boolean checked?: boolean disabled?: boolean - onChange?: (params: OnChangeParams) => void + onChange?: (_e: OnChangeParams) => void + children?: React.ReactNode + indeterminate?: boolean + prefixCls?: string } export interface CheckboxItemPropsType extends CheckboxPropsType { - extra?: React.ReactNode - prefixCls?: string - onPress?: (e?: any) => void + right?: boolean + left?: boolean + onPress?: () => void } diff --git a/components/checkbox/demo/basic.tsx b/components/checkbox/demo/basic.tsx index 10b16d91..8c957b49 100644 --- a/components/checkbox/demo/basic.tsx +++ b/components/checkbox/demo/basic.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { Text, View } from 'react-native' -import { Checkbox, List, WhiteSpace } from '../../' +import { ScrollView } from 'react-native' +import { Button, Checkbox, Flex, List, WingBlank } from '../../' const AgreeItem = Checkbox.AgreeItem const CheckboxItem = Checkbox.CheckboxItem @@ -8,57 +8,91 @@ export default class BasicCheckboxExample extends React.Component { constructor(props: any, context: any) { super(props, context) this.state = { + checked: true, + disabled: false, + checkBox1: true, agreeItem1: true, checkboxItem1: true, } } + onChange = (e: { target: { checked: boolean } }) => { + console.log(`checked = ${e.target.checked}`) + } + + toggleChecked = () => { + this.setState({ checked: !this.state.checked }) + } + + toggleDisable = () => { + this.setState({ disabled: !this.state.disabled }) + } + onChange2 = (e: { target: { checked: boolean } }) => { + console.log('checked = ', e.target.checked) + this.setState({ + checked: e.target.checked, + }) + } + render() { + const label = `${this.state.checked ? 'Checked' : 'Unchecked'}-${ + this.state.disabled ? 'Disabled' : 'Enabled' + }` return ( - - - { - this.setState({ checkBox1: event.target.checked }) - }} + + + Checkbox} /> - - Checkbox - - - - - - - - - Agree agreement agreement agreement agreement agreement agreement - agreement - - - { - this.setState({ agreeItem1: event.target.checked }) - }}> - Agree agreement - - - Not selected. Not editable - - - Force selected. Not editable - - - - Multiple options + + + } /> + } /> + + + + + + + + + + }> + + {label} + + } + /> + + + + Agree agreement agreement agreement agreement agreement agreement + agreement + + + { + onChange={(event) => { this.setState({ checkboxItem1: event.target.checked }) }}> Option 1 @@ -69,7 +103,65 @@ export default class BasicCheckboxExample extends React.Component { Option 4 - + + + + + ) + } +} + +const plainOptions = ['Apple', 'Pear', 'Orange'] +const defaultCheckedList = ['Apple', 'Orange'] + +const CheckboxGroupExample = () => { + const [checkedList, setCheckedList] = React.useState( + new Set(defaultCheckedList), + ) + const [indeterminate, setIndeterminate] = React.useState(true) + const [checkAll, setCheckAll] = React.useState(false) + + const onChange = (value: any, e: { target: { checked: boolean } }) => { + if (e.target.checked) { + checkedList.add(value) + } else { + checkedList.delete(value) + } + + setCheckedList(new Set(checkedList)) + setIndeterminate( + !!checkedList.size && checkedList.size < plainOptions.length, ) + setCheckAll(checkedList.size === plainOptions.length) + } + + const onCheckAllChange = (e: { target: { checked: boolean } }) => { + setCheckedList(e.target.checked ? new Set(plainOptions) : new Set()) + setIndeterminate(false) + setCheckAll(e.target.checked) } + + return ( + <> + + Check all + + + {plainOptions.map((a) => ( + + {a} + + ))} + + + ) } diff --git a/components/checkbox/index.tsx b/components/checkbox/index.tsx index b9140a53..07470b3c 100644 --- a/components/checkbox/index.tsx +++ b/components/checkbox/index.tsx @@ -1,8 +1,19 @@ +import * as React from 'react' import AgreeItem from './AgreeItem' -import Checkbox from './Checkbox' +import InternalCheckbox, { CheckboxProps } from './Checkbox' import CheckboxItem from './CheckboxItem' +export interface CompoundedComponent + extends React.ForwardRefExoticComponent { + AgreeItem: typeof AgreeItem + CheckboxItem: typeof CheckboxItem + __ANTM_CHECKBOX: boolean +} + +const Checkbox = InternalCheckbox as CompoundedComponent + Checkbox.AgreeItem = AgreeItem Checkbox.CheckboxItem = CheckboxItem +Checkbox.__ANTM_CHECKBOX = true export default Checkbox diff --git a/components/checkbox/style/index.tsx b/components/checkbox/style/index.tsx index 22f6953e..1c99d9a7 100644 --- a/components/checkbox/style/index.tsx +++ b/components/checkbox/style/index.tsx @@ -1,38 +1,103 @@ -import { StyleSheet, TextStyle, ViewStyle } from 'react-native' +import { StyleSheet, ViewStyle } from 'react-native' import { Theme } from '../../style' export interface CheckboxStyle { - wrapper: ViewStyle - icon: TextStyle - iconRight: TextStyle - agreeItem: ViewStyle - agreeItemCheckbox: TextStyle - checkboxItemCheckbox: TextStyle + checkbox_wrapper: ViewStyle + checkbox_wave: ViewStyle + checkbox: ViewStyle + checkbox_checked: ViewStyle + checkbox_disabled: ViewStyle + checkbox_inner: ViewStyle + checkbox_inner_disabled: ViewStyle + checkbox_inner_after: ViewStyle + checkbox_inner_after_disabled: ViewStyle + checkbox_label: ViewStyle + checkbox_label_disabled: ViewStyle + checkbox_inner_indeterminate: ViewStyle + checkbox_inner_after_indeterminate: ViewStyle } export default (theme: Theme) => StyleSheet.create({ - wrapper: { + checkbox_wrapper: { + margin: 0, + padding: 0, + position: 'relative', + display: 'flex', flexDirection: 'row', alignItems: 'center', }, - icon: { - width: theme.icon_size_sm, - height: theme.icon_size_sm, + checkbox_wave: { + width: 20, + height: 20, + padding: 2, + overflow: 'hidden', }, - iconRight: { - marginLeft: theme.h_spacing_md, - }, - agreeItem: { - flexDirection: 'row', + checkbox: { + position: 'relative', + width: '100%', + height: '100%', + borderRadius: 2, + borderWidth: 1, + borderStyle: 'solid', + borderColor: theme.checkbox_border, + backgroundColor: 'transparent', + justifyContent: 'center', alignItems: 'center', }, - agreeItemCheckbox: { - marginLeft: theme.h_spacing_lg, - marginRight: theme.h_spacing_md, + checkbox_checked: { + borderColor: theme.checkbox_fill, + }, + checkbox_disabled: { + borderColor: theme.checkbox_border_disabled, + backgroundColor: theme.checkbox_fill_disabled, + }, + + // ==========checkbox Inner PrefixCls============ + checkbox_inner: { + //box-sizing not support in RN + width: '103%', + height: '103%', + backgroundColor: theme.checkbox_fill, + }, + checkbox_inner_indeterminate: { + backgroundColor: 'transparent', }, - checkboxItemCheckbox: { + checkbox_inner_disabled: { + backgroundColor: theme.checkbox_fill_disabled, + }, + + // ==========inner::after============ + checkbox_inner_after: { + // transform: [{ rotate: '45deg' }], + position: 'absolute', + width: 6, + height: 9, + bottom: '20%', + borderWidth: 2, + borderColor: '#ffffff', + borderTopWidth: 0, + borderLeftWidth: 0, + }, + // 半选状态样式 + checkbox_inner_after_indeterminate: { + transform: [{ rotate: '0deg' }], + position: 'absolute', + width: 8, + height: 8, + backgroundColor: theme.checkbox_fill, + }, + checkbox_inner_after_disabled: { + borderColor: theme.checkbox_border_disabled, + }, + + // ==========label============ + checkbox_label: { + backgroundColor: 'transparent', marginRight: theme.h_spacing_md, - alignSelf: 'center', + marginLeft: theme.h_spacing_md, + }, + checkbox_label_disabled: { + color: theme.color_text_disabled, }, }) diff --git a/components/radio/PropsType.tsx b/components/radio/PropsType.tsx index 7579980d..304024a6 100644 --- a/components/radio/PropsType.tsx +++ b/components/radio/PropsType.tsx @@ -1,13 +1,42 @@ -export interface RadioPropsType { - defaultChecked?: boolean - checked?: boolean - disabled?: boolean - onChange?: (e: { target: { checked: boolean } }) => void +import React from 'react' +import { CheckboxPropsType, OnChangeParams } from '../checkbox/PropsType' + +export interface RadioPropsType extends CheckboxPropsType { name?: string - wrapLabel?: boolean + value?: any } export interface RadioItemPropsType extends RadioPropsType { - radioProps?: object onPress?: () => any } + +export type RadioValueType = string | number | boolean +export interface RadioOptionType { + label: React.ReactNode + value: RadioValueType + style?: React.CSSProperties + disabled?: boolean + onChange?: (_e: OnChangeParams) => void +} +export interface OnGroupChangeParams { + target: { + value: any + } +} +export interface RadioGroupPropsType { + children?: React.ReactNode + defaultValue?: any + value?: any + onChange?: (_e: OnGroupChangeParams) => void + options?: Array + disabled?: boolean +} +export interface RadioGroupContextProps { + onChange: (_e: RadioChangeEvent) => void + value: any + disabled?: boolean + name?: string +} +export interface RadioChangeEvent { + target: { value: any } +} diff --git a/components/radio/Radio.tsx b/components/radio/Radio.tsx index 30a68039..998228f7 100644 --- a/components/radio/Radio.tsx +++ b/components/radio/Radio.tsx @@ -1,90 +1,62 @@ -import React from 'react' -import { - StyleProp, - Text, - TextStyle, - TouchableWithoutFeedback, - View, -} from 'react-native' -import Icon from '../icon' +import * as React from 'react' +import { StyleProp, ViewStyle } from 'react-native' +import Checkbox from '../checkbox/Checkbox' +import { OnChangeParams } from '../checkbox/PropsType' +import { CheckboxStyle } from '../checkbox/style' import { WithTheme, WithThemeStyles } from '../style' -import variables from '../style/themes/default' +import devWarning from '../_util/devWarning' import { RadioPropsType } from './PropsType' -import RadioStyles, { RadioStyle } from './style/index' +import RadioGroupContext from './RadioContext' +import RadioStyle from './style' -export interface RadioNativeProps +export interface RadioProps extends RadioPropsType, - WithThemeStyles { - style?: StyleProp + WithThemeStyles { + style?: StyleProp + children?: React.ReactNode } -export default class Radio extends React.Component { - static RadioItem: any - - constructor(props: RadioNativeProps, context: any) { - super(props, context) - - this.state = { - checked: props.checked || props.defaultChecked || false, - } +const InternalRadio = ( + { styles, onChange, value, ...restProps }: RadioProps, + ref: any, +) => { + const context = React.useContext(RadioGroupContext) + devWarning( + 'checked' in restProps || !('value' in restProps && context), + 'Radio', + '`value` is always used with Radio.Group., do you mean `checked`?', + ) + if (context) { + restProps.checked = value === context.value + restProps.disabled = restProps.disabled || context.disabled } - UNSAFE_componentWillReceiveProps(nextProps: RadioNativeProps): void { - if ('checked' in nextProps) { - this.setState({ - checked: !!nextProps.checked, - }) - } + const onInternalChange = (e: OnChangeParams) => { + e.target.checked && triggerChange(e.target.checked) } - handleClick = () => { - if (this.props.disabled) { - return - } - if (!('checked' in this.props)) { - this.setState({ - checked: true, - }) - } - if (this.props.onChange) { - this.props.onChange({ target: { checked: true } }) - } + function triggerChange(newChecked: boolean) { + onChange?.({ target: { checked: newChecked } }) + context?.onChange?.({ target: { value } }) } - render() { - const { style, disabled, children } = this.props - - return ( - - {(styles) => { - const checked = this.state.checked - let icon - if (checked) { - icon = disabled ? ( - - ) : ( - - ) - } - return ( - - - {icon} - {typeof children === 'string' ? ( - // tslint:disable-next-line:jsx-no-multiline-js - {this.props.children} - ) : ( - children - )} - - - ) - }} - - ) - } + return ( + + {(_styles) => ( + + )} + + ) } + +const AntmRadio = React.forwardRef(InternalRadio) + +AntmRadio.displayName = 'AntmRadio' + +export default AntmRadio diff --git a/components/radio/RadioContext.tsx b/components/radio/RadioContext.tsx new file mode 100644 index 00000000..1b72e756 --- /dev/null +++ b/components/radio/RadioContext.tsx @@ -0,0 +1,10 @@ +import * as React from 'react' +import { RadioGroupContextProps } from './PropsType' + +const RadioGroupContext = React.createContext( + null, +) + +export const RadioGroupContextProvider = RadioGroupContext.Provider + +export default RadioGroupContext diff --git a/components/radio/RadioGroup.tsx b/components/radio/RadioGroup.tsx new file mode 100644 index 00000000..730bc7e5 --- /dev/null +++ b/components/radio/RadioGroup.tsx @@ -0,0 +1,95 @@ +import useMergedState from 'rc-util/lib/hooks/useMergedState' +import * as React from 'react' +import { StyleProp, ViewStyle } from 'react-native' +import { CheckboxStyle } from '../checkbox/style' +import { WithThemeStyles } from '../style' +import View from '../view' +import { RadioGroupPropsType } from './PropsType' +import Radio from './Radio' +import { RadioGroupContextProvider } from './RadioContext' + +export interface RadioGroupProps + extends RadioGroupPropsType, + WithThemeStyles { + style?: StyleProp +} +const RadioGroup = React.forwardRef( + ( + { + defaultValue, + onChange, + options, + disabled, + children, + style, + ...restProps + }: RadioGroupProps, + ref, + ) => { + const [value, setValue] = useMergedState(defaultValue, { + value: restProps.value, + }) + + const onRadioChange = (ev: any) => { + const lastValue = value + const val = ev.target.value + if (!('value' in restProps)) { + setValue(val) + } + if (onChange && val !== lastValue) { + onChange(ev) + } + } + + const renderGroup = () => { + let childrenToRender = children + // 如果存在 options, 优先使用 + if (options && options.length > 0) { + childrenToRender = options.map((option: any) => { + if (typeof option === 'string') { + // 此处类型自动推导为 string + return ( + + {option} + + ) + } + // 此处类型自动推导为 { label: string value: string } + return ( + + {option.label} + + ) + }) + } + + return ( + + + {childrenToRender} + + + ) + } + + return renderGroup() + }, +) + +export default React.memo(RadioGroup) diff --git a/components/radio/RadioItem.tsx b/components/radio/RadioItem.tsx index 0ed6746f..0bf88cd3 100644 --- a/components/radio/RadioItem.tsx +++ b/components/radio/RadioItem.tsx @@ -1,45 +1,44 @@ import React from 'react' -import { ImageStyle, StyleProp, Text, View, ViewStyle } from 'react-native' +import { StyleProp, Text, View, ViewStyle } from 'react-native' +import { RefCheckboxProps } from '../checkbox/Checkbox' +import { CheckboxStyle } from '../checkbox/style' import List from '../list/index' import { WithTheme, WithThemeStyles } from '../style' import { RadioItemPropsType } from './PropsType' import Radio from './Radio' -import RadioItemStyles, { RadioStyle } from './style/index' +import RadioStyles from './style/index' const ListItem = List.Item -export interface RadioItemNativeProps +export interface RadioItemProps extends RadioItemPropsType, - WithThemeStyles { + WithThemeStyles { + right?: boolean + left?: boolean style?: StyleProp - radioStyle?: StyleProp } -export default class RadioItem extends React.Component< - RadioItemNativeProps, - any -> { - radio: Radio | null +export default class RadioItem extends React.Component { + radio: RefCheckboxProps handleClick = () => { if (this.radio) { - this.radio.handleClick() + this.radio.onPress() } } render() { const { style, - radioStyle, - defaultChecked, - checked, disabled, children, - onChange, + right, + left = !right, + ...restProps } = this.props return ( - + {(styles) => { let contentDom: React.ReactElement | null = null if (children && React.isValidElement(children)) { @@ -58,20 +57,21 @@ export default class RadioItem extends React.Component< const radioEl = ( (this.radio = ref)} - style={radioStyle} - defaultChecked={defaultChecked} - checked={checked} - onChange={onChange} + ref={(ref: RefCheckboxProps) => (this.radio = ref)} disabled={disabled} + {...restProps} /> ) + const listProps = { + thumb: left && !right ? radioEl : undefined, + extra: right ? radioEl : undefined, + } return ( + {...listProps}> {contentDom} ) diff --git a/components/radio/demo/basic.tsx b/components/radio/demo/basic.tsx index 6672fabc..36512f0b 100644 --- a/components/radio/demo/basic.tsx +++ b/components/radio/demo/basic.tsx @@ -1,69 +1,200 @@ import React from 'react' -import { Text, View } from 'react-native' -import { List, Radio, WhiteSpace } from '../../' +import { ScrollView } from 'react-native' +import { Button, Flex, List, Radio, WhiteSpace, WingBlank } from '../../' const RadioItem = Radio.RadioItem export default class BasicRadioExample extends React.Component { - state = { - part1Value: 1, - part2Value: 1, + constructor(props: any, context: any) { + super(props, context) + this.state = { + disabled: false, + part1Value: 1, + part2Value: 1, + part3Value: 1, + } + } + + toggleDisabled = () => { + this.setState({ + disabled: !this.state.disabled, + }) + } + + onChange2 = (e: { target: { value: any } }) => { + this.setState({ + part2Value: e.target.value, + }) + } + + onChange3 = (e: { target: { value: any } }) => { + this.setState({ + part3Value: e.target.value, + }) } render() { return ( - - - + + Radio} /> + + + Toggle disabled + + }> + + + + Disabled + + + Disabled + + + + + { + onChange={(event) => { if (event.target.checked) { this.setState({ part1Value: 1 }) } - }} - style={{ borderWidth: 1, borderColor: '#999', margin: 10 }}> - Support - - - { - if (event.target.checked) { - this.setState({ part1Value: 2 }) - } - }} - style={{ borderWidth: 1, borderColor: '#999', margin: 10 }} - /> - - - - - - Form radio, radio in general list. - - { - if (event.target.checked) { - this.setState({ part2Value: 1 }) - } }}> Use Ant Design Component { + right + checked={this.state.part1Value === 2} + onChange={(event) => { if (event.target.checked) { - this.setState({ part2Value: 2 }) + this.setState({ part1Value: 2 }) } }}> Use Ant Design Component - Set disabled - - Set disabled - - + + + A + B + C + D + + + + + Option A + Option B + Option C + More... + + + + + + + ) + } +} + +const plainOptions = ['Apple', 'Pear', 'Orange'] +const options = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange' }, +] +const optionsWithDisabled = [ + { label: 'Apple', value: 'Apple' }, + { label: 'Pear', value: 'Pear' }, + { label: 'Orange', value: 'Orange', disabled: true }, +] + +class RadioGroupExample extends React.Component { + state = { + value1: 'Apple', + value2: 'Apple', + value3: 'Apple', + value4: 'Apple', + } + + onChange1 = (e: any) => { + console.log('radio1 checked', e.target.value) + this.setState({ + value1: e.target.value, + }) + } + + onChange2 = (e: any) => { + console.log('radio2 checked', e.target.value) + this.setState({ + value2: e.target.value, + }) + } + + onChange3 = (e: any) => { + console.log('radio3 checked', e.target.value) + this.setState({ + value3: e.target.value, + }) + } + + onChange4 = (e: any) => { + console.log('radio4 checked', e.target.value) + this.setState({ + value4: e.target.value, + }) + } + + render() { + const { value1, value2, value3, value4 } = this.state + return ( + + + + + + + + + + + + + + ) } } diff --git a/components/radio/index.tsx b/components/radio/index.tsx index 7031cdd3..2b914fd9 100644 --- a/components/radio/index.tsx +++ b/components/radio/index.tsx @@ -1,6 +1,19 @@ -import Radio from './Radio' +import * as React from 'react' +import InternalRadio, { RadioProps } from './Radio' +import RadioGroup from './RadioGroup' import RadioItem from './RadioItem' +export interface CompoundedComponent + extends React.ForwardRefExoticComponent { + RadioItem: typeof RadioItem + Group: typeof RadioGroup + __ANTM_CHECKBOX: boolean +} + +const Radio = InternalRadio as CompoundedComponent Radio.RadioItem = RadioItem +Radio.Group = RadioGroup + +Radio.__ANTM_CHECKBOX = true export default Radio diff --git a/components/radio/style/index.tsx b/components/radio/style/index.tsx index a7b473c1..03f8b049 100644 --- a/components/radio/style/index.tsx +++ b/components/radio/style/index.tsx @@ -1,36 +1,25 @@ -import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from 'react-native' +import { StyleSheet } from 'react-native' import { Theme } from '../../style' -export interface RadioStyle { - wrapper: ViewStyle - icon: ImageStyle - radioItem: ViewStyle - radioItemRadio: ViewStyle - radioItemContent: TextStyle - radioItemContentDisable: TextStyle -} - export default (theme: Theme) => - StyleSheet.create({ - wrapper: { - flexDirection: 'row', - alignItems: 'center', - }, - icon: { - // width: variables.icon_size_xxs, - // height: variables.icon_size_xxs * 0.8, + StyleSheet.create({ + checkbox_wave: { borderRadius: 999 }, + checkbox: { borderRadius: 999 }, + checkbox_inner: { width: 0, height: 0 }, + checkbox_inner_after: { + width: 8, + height: 8, + borderRadius: 999, + backgroundColor: theme.checkbox_fill, + borderWidth: 0, }, - radioItem: { - flexDirection: 'row', - alignItems: 'center', - }, - radioItemRadio: { - marginLeft: theme.h_spacing_lg, - marginRight: theme.h_spacing_md, + checkbox_inner_after_disabled: { + backgroundColor: '#0003', }, radioItemContent: { color: theme.color_text_base, - fontSize: theme.font_size_heading, + marginRight: theme.h_spacing_md, + marginLeft: theme.h_spacing_md, }, radioItemContentDisable: { color: theme.color_text_disabled, diff --git a/rn-kitchen-sink/demoList.js b/rn-kitchen-sink/demoList.js index d3d9cb5b..b52d0570 100644 --- a/rn-kitchen-sink/demoList.js +++ b/rn-kitchen-sink/demoList.js @@ -122,7 +122,7 @@ module.exports = { { title: 'Checkbox', // 必须 description: '复选框', - icon: 'https://os.alipayobjects.com/rmsportal/dWPGltvdjaanrRd.png', + icon: 'https://os.alipayobjects.com/rmsportal/MJszdVSBKhtGmIP.png', module: require('../components/checkbox/demo/basic'), // 必须 }, { @@ -176,7 +176,7 @@ module.exports = { { title: 'Radio', description: '单选框', - icon: 'https://os.alipayobjects.com/rmsportal/MJszdVSBKhtGmIP.png', + icon: 'https://os.alipayobjects.com/rmsportal/dWPGltvdjaanrRd.png', module: require('../components/radio/demo/basic'), // 必须 }, {