@uiw/react-native
Version:
UIW for React Native
194 lines (193 loc) • 6.32 kB
JavaScript
import React, { useState } from 'react';
import { TouchableOpacity, Animated, View, StyleSheet, useColorScheme, } from 'react-native';
import { useTheme } from '@shopify/restyle';
function Switch(props) {
const theme = useTheme();
const colorScheme = useColorScheme();
const styles = createStyles({
color: colorScheme === 'dark' ? theme.colors.mask : '#E6E6E6',
});
const { style, size,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
checked = false, color = theme.colors.primary_background || '#3578e5', disabled, thumbColor = theme.colors.text_active || '#fff', trackStyle, thumbStyle, ...otherProps } = props;
const [height, setHeight] = useState(11);
const [translateXValue, setTranslateXValue] = useState(14);
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',
onValueChange: () => { },
};
export default Switch;
function createStyles({ color }) {
return StyleSheet.create({
warpper: {
position: 'relative',
borderRadius: 16,
backgroundColor: color,
},
disabled: {
// backgroundColor: 'rgba(255, 255, 255, 0.6)',
backgroundColor: '#0001',
borderRadius: 16,
zIndex: 22,
},
bg: {
borderRadius: 16,
borderWidth: 2,
borderColor: color,
},
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,
},
});
}