@uiw/react-native
Version:
UIW for React Native
213 lines (210 loc) • 5.08 kB
JavaScript
import React, { useState } from 'react';
import { TouchableOpacity, Animated, View, StyleSheet } from 'react-native';
function Switch(props) {
const {
style,
size,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
checked = false,
color,
disabled,
thumbColor,
trackStyle,
thumbStyle,
...otherProps
} = props;
const [height, setHeight] = useState(16);
const [translateXValue, setTranslateXValue] = useState(2);
const animatedStart = checked => {
const obj = {
height: height,
number: 1,
translateXValue: translateXValue
};
if (!checked) {
obj.height = 2;
obj.number = 0;
obj.translateXValue = 2;
}
Animated.parallel([Animated.sequence([Animated.spring(state.borderValue, {
toValue: obj.height,
overshootClamping: true,
useNativeDriver: false
}), Animated.spring(state.bgOpacity, {
toValue: obj.number,
overshootClamping: true,
useNativeDriver: false
})]), Animated.spring(state.translateXValue, {
toValue: obj.translateXValue,
overshootClamping: true,
useNativeDriver: false
})]).start();
};
const [state, setState] = useState({
checked: checked,
containerSize: {
width: 0,
height: 0
},
borderValue: new Animated.Value(0),
translateXValue: new Animated.Value(2),
bgOpacity: new Animated.Value(props.value ? 1 : 0),
control: 'state',
animatedStart: animatedStart
});
const {
containerSize
} = state;
const onPress = () => {
const checked = !state.checked;
setState({
...state,
checked,
control: 'state'
});
animatedStart(checked);
props.onValueChange(checked);
};
const measureContainer = event => {
animatedStart(!!props.checked);
let {
checked,
translateXValue
} = state;
const {} = state;
const {
height: layoutHeight,
width: layoutWidth
} = event.nativeEvent.layout;
let height = layoutHeight - 4;
const width = height;
const size = {
width,
height
};
setHeight(height / 2);
setTranslateXValue(layoutWidth - 2 - width);
translateXValue.setValue(checked ? layoutWidth - 2 - width : 2);
setState({
...state,
containerSize: size,
control: 'state'
});
animatedStart(!!props.checked);
};
// if (state.control === 'state') {
// setState({ ...state, control: 'props' });
// }
// if (props.checked !== state.checked) {
// state.animatedStart(!!props.checked);
// setState({
// ...state,
// checked: !!props.checked,
// control: 'props',
// });
// }
const bgBorder = state.borderValue.interpolate({
inputRange: [2, height],
outputRange: [2, height]
// extrapolate: 'clamp',
});
const sizeStyl = {};
switch (size) {
case 'small':
sizeStyl.height = 20;
sizeStyl.width = 30;
break;
case 'large':
sizeStyl.height = 30;
sizeStyl.width = 48;
break;
default:
sizeStyl.height = 26;
sizeStyl.width = 38;
break;
}
return <View {...otherProps} onLayout={measureContainer} style={[styles.warpper, sizeStyl, style]}>
{disabled && <View style={[styles.position, styles.disabled]} />}
<Animated.View style={[styles.bg, styles.position, trackStyle, {
borderWidth: bgBorder
}]} />
<TouchableOpacity
// eslint-disable-next-line
style={[styles.position, {
zIndex: 1
}]} onPress={onPress} />
<Animated.View style={[styles.position,
// eslint-disable-next-line
{
backgroundColor: state.checked ? color : '',
borderRadius: 16,
opacity: state.bgOpacity
}]} />
<Animated.View style={[styles.grip, disabled ? styles.shadowDisable : styles.shadow, {
backgroundColor: thumbColor,
width: containerSize.width,
height: containerSize.height
}, thumbStyle, {
transform: [{
translateX: state.translateXValue
}]
}]} />
</View>;
}
Switch.defaultProps = {
checked: false,
size: 'default',
thumbColor: '#fff',
color: '#4DD964',
onValueChange: () => {}
};
export default Switch;
const styles = StyleSheet.create({
warpper: {
position: 'relative',
borderRadius: 16,
backgroundColor: '#E6E6E6'
},
disabled: {
// backgroundColor: 'rgba(255, 255, 255, 0.6)',
backgroundColor: '#0001',
borderRadius: 16,
zIndex: 22
},
bg: {
borderRadius: 16,
borderWidth: 2,
borderColor: '#E6E6E6'
},
position: {
position: 'absolute',
backgroundColor: 'transparent',
top: 0,
bottom: 0,
left: 0,
right: 0
},
grip: {
top: 2,
borderRadius: 16
},
shadowDisable: {
shadowColor: '#000',
background: '#0001',
shadowOffset: {
width: 5,
height: 4
},
shadowOpacity: 0.3,
shadowRadius: 2
},
shadow: {
shadowColor: '#000',
shadowOffset: {
width: 4,
height: 4
},
shadowOpacity: 0.2,
shadowRadius: 3
}
});