Skip to content

Commit

Permalink
feat: refactor Checkbox & Radio
Browse files Browse the repository at this point in the history
  • Loading branch information
1uokun committed Sep 28, 2021
1 parent bf3ce02 commit 3bec0d3
Show file tree
Hide file tree
Showing 16 changed files with 874 additions and 431 deletions.
80 changes: 12 additions & 68 deletions components/checkbox/AgreeItem.tsx
Original file line number Diff line number Diff line change
@@ -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> {
checkboxStyle?: StyleProp<ImageStyle>
style?: StyleProp<ViewStyle>
const AgreeItem = (props: CheckboxProps) => {
return (
<Checkbox
{...props}
styles={{
checkbox_checked: { borderColor: 'red' },
checkbox_inner: { backgroundColor: 'red' },
}}
/>
)
}

export default class AgreeItem extends React.Component<AgreeItemProps, any> {
checkbox: Checkbox | null

handleClick = () => {
if (this.checkbox) {
this.checkbox.handleClick()
}
}

render() {
const {
style,
checkboxStyle,
children,
disabled,
checked,
defaultChecked,
onChange,
} = this.props

return (
<WithTheme styles={this.props.styles} themeStyles={AgreeItemstyles}>
{(styles) => {
const contentDom = !children ? null : React.isValidElement(
children,
) ? (
children
) : (
<Text>{children}</Text>
)

return (
<TouchableWithoutFeedback onPress={this.handleClick}>
<View style={[styles.agreeItem, style]}>
<Checkbox
ref={(ref) => (this.checkbox = ref)}
style={[styles.agreeItemCheckbox, checkboxStyle]}
disabled={disabled}
checked={checked}
defaultChecked={defaultChecked}
onChange={onChange}
/>
<View style={{ flex: 1 }}>{contentDom}</View>
</View>
</TouchableWithoutFeedback>
)
}}
</WithTheme>
)
}
}
export default AgreeItem
230 changes: 155 additions & 75 deletions components/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -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<CheckboxStyle> {
style?: StyleProp<TextStyle>
style?: StyleProp<ViewStyle>
}
//TODO: ref interface
export interface RefCheckboxProps {
onPress: () => void
}

export default class Checkbox extends React.Component<CheckboxProps, any> {
static CheckboxItem: any
static AgreeItem: any
const InternalCheckbox = (
{
prefixCls = 'checkbox',
style,
styles,
children,
defaultChecked,
disabled,
onChange,
indeterminate,
...restProps
}: CheckboxProps,
ref: React.Ref<any>,
) => {
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<boolean>(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 (
<WithTheme themeStyles={CheckboxStyles} styles={styles}>
{(_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 (
<WithTheme themeStyles={CheckboxStyles} styles={this.props.styles}>
{(styles, theme) => {
let icon
if (checked) {
icon = disabled ? (
<Icon name="check-square" style={[styles.icon, style]} />
) : (
<Icon
name="check-square"
color={theme.brand_primary}
style={[styles.icon, style]}
/>
)
} else {
icon = disabled ? (
<Icon name="border" style={[styles.icon, style]} />
) : (
<Icon
name="border"
color={theme.brand_primary}
style={[styles.icon, style]}
/>
)
}

return (
<TouchableWithoutFeedback onPress={this.handleClick}>
<View style={[styles.wrapper]}>
{icon}
{typeof children === 'string' ? (
// tslint:disable-next-line:jsx-no-multiline-js
<Text style={styles.iconRight}>{this.props.children}</Text>
) : (
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 (
<View style={[_styles[`${prefixCls}_wrapper`], style]}>
<View style={_styles.checkbox_wave}>

This comment has been minimized.

Copy link
@1uokun

1uokun Mar 7, 2022

Author Collaborator

Use checkbox_wave to replace Icon
So if you want to change icon style, you can rewrite styles.checkbox_wave:

<Checkbox 
   styles={{checkbox_wave:{...}}}
/>
<TouchableNativeFeedback
background={
Platform.Version >= 21
? TouchableNativeFeedback.Ripple(Color || '', true, 13)
: TouchableNativeFeedback.SelectableBackground()
}
useForeground={true}
disabled={disabled}
onPress={this.onPress}>
<View style={antd_checkbox}>
<Animated.View
style={[antd_checkbox_inner, transitionOpacity]}
/>
<Animated.View
style={[transitionTransform, antd_checkbox_inner_after]}
/>
</View>
</TouchableNativeFeedback>
</View>
<Pressable disabled={disabled} onPress={this.onPress}>
<AntmView style={antd_checlbox_label}>{children}</AntmView>
</Pressable>
</View>
</TouchableWithoutFeedback>
)
}}
</WithTheme>
)
)
}}
</WithTheme>
)
}
}

return <Checkbox ref={ref} {...restProps} />
}

const AntmCheckbox = React.forwardRef(InternalCheckbox)
AntmCheckbox.displayName = 'AntmCheckbox'

export default AntmCheckbox
Loading

0 comments on commit 3bec0d3

Please sign in to comment.