UNPKG

@audira/carbon-react-native

Version:

Build React Native apps with component and shared patterns using Carbon

291 lines (290 loc) 9.04 kB
"use strict"; import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { Animated, Easing, Pressable, StyleSheet, View } from 'react-native'; import { Color, Motion } from '@audira/carbon-react-native-elements'; import IconCheckmark from '@carbon/icons/svg/32/checkmark.svg'; import { GlobalConfigContext } from "../../_internal/contexts/index.js"; import { CommonStyleSheet, FlexStyleSheet } from "../../_internal/style-sheets/index.js"; import { CarbonStyleSheet } from "../../carbon-style-sheet/index.js"; import { ThemeContext } from "../../contexts/index.js"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; export const Switch = /*#__PURE__*/forwardRef(function ({ size = 'default', state = 'normal', defaultValue, value: valueProp, trackColor: trackColorProp, thumbColor: thumbColorProp, motion = { false: motionDefault, true: motionDefault }, style, role = 'switch', 'aria-checked': ariaChecked, onChange, onBlur, onFocus, onPress, ...props }, forwardedRef) { const viewRef = useRef(null), ref = useRef({ onChangeEffect: false, value: defaultValue ?? false }), globalConfigContext = useContext(GlobalConfigContext), themeContext = useContext(ThemeContext), [isFocused, setIsFocused] = useState(false), [valueSelf, setValueSelf] = useState(ref.current.value), controlled = typeof valueProp === 'boolean', value = controlled ? !!valueProp : valueSelf, /** * 0 -> false/inactive * 1 -> true/active */ sharedValue = useRef(new Animated.Value(value ? 1 : 0)), { trackColor, thumbColor } = useMemo(() => { const trackColor_ = mapSwitchTrackColorToken[state], thumbColor_ = mapSwitchThumbColorToken[state]; return { trackColor: trackColorProp ?? { false: trackColor_.false?.[themeContext.colorScheme] ?? 'transparent', true: trackColor_.true?.[themeContext.colorScheme] ?? 'transparent' }, thumbColor: thumbColorProp ?? { false: thumbColor_[themeContext.colorScheme], true: thumbColor_[themeContext.colorScheme] } }; }, [state, trackColorProp, thumbColorProp, themeContext.colorScheme]), blurHandler = useCallback(event => { onBlur?.(event); setIsFocused(false); }, [onBlur]), focusHandler = useCallback(event => { onFocus?.(event); setIsFocused(true); }, [onFocus]), pressHandler = useCallback(event => { onPress?.(event); if (!controlled) { ref.current.onChangeEffect = true; setValueSelf(self => !self); } else { onChange?.(!ref.current.value); } }, [controlled, onPress, onChange]), transformInterpolationOutputRange = useMemo(() => { const ltr = [ /** * i can't find how IBM tell this gap between the container and the thumb/handle * i have to look at the Carbon React just to find this padding (they are using inset-inline-start there) */ 3, sizeStyle[size].width - thumbSizeStyle[size].width - 3]; if (globalConfigContext.rtl) { return ltr.reverse(); } return ltr; }, [size, globalConfigContext.rtl]); useEffect(() => { if (value) { Animated.timing(sharedValue.current, { toValue: 1, duration: motion.true.duration, easing: motion.true.easing, useNativeDriver: true }).start(); } else { Animated.timing(sharedValue.current, { toValue: 0, duration: motion.false.duration, easing: motion.false.easing, useNativeDriver: true }).start(); } }, [motion, sharedValue, value]); useEffect(() => { if (ref.current.onChangeEffect) { ref.current.onChangeEffect = false; ref.current.value = value; onChange?.(value); } }, [value, onChange]); useImperativeHandle(forwardedRef, () => { return Object.assign(viewRef.current ?? {}, { get value() { return value; }, setValue(valueParam) { if (!controlled) { ref.current.onChangeEffect = true; if (typeof valueParam === 'boolean') { setValueSelf(valueParam); } else { setValueSelf(valueParam(ref.current.value)); } } } }); }, [controlled, value]); return /*#__PURE__*/_jsxs(PressableAnimated, { ...props, role: role, "aria-checked": ariaChecked ?? value, style: [FlexStyleSheet.justify_center, baseStyle.container, sizeStyle[size], { backgroundColor: sharedValue.current.interpolate({ inputRange: interpolationRange, outputRange: [trackColor.false, trackColor.true] }) }, state === 'read_only' ? baseStyleCarbon.containerReadonly : null, style], onBlur: blurHandler, onFocus: focusHandler, onPress: pressHandler, ref: viewRef, children: [/*#__PURE__*/_jsx(Animated.View, { style: [FlexStyleSheet.items_center, FlexStyleSheet.justify_center, baseStyle.thumb, thumbSizeStyle[size], // thumbAnimatedStyle, { transform: [{ translateX: sharedValue.current.interpolate({ inputRange: interpolationRange, outputRange: transformInterpolationOutputRange }) }], backgroundColor: sharedValue.current.interpolate({ inputRange: interpolationRange, outputRange: [thumbColor.false, thumbColor.true] }) }], children: size === 'small' && /*#__PURE__*/_jsx(IconCheckmarkAnimated, { fill: mapIconAnimatedFillColor[themeContext.colorScheme], style: { opacity: sharedValue.current } }) }), isFocused && /*#__PURE__*/_jsx(View, { style: [CommonStyleSheet.absolute, focusBoxSizeStyle[size], baseStyle.focusBox, baseStyleCarbon.focusBoxBorderColor] })] }); }); const baseStyle = StyleSheet.create({ container: { borderRadius: 24 }, thumb: { borderRadius: 18 }, focusBox: { borderWidth: 2, borderRadius: 24, transform: [{ translateX: -3 }] } }), baseStyleCarbon = CarbonStyleSheet.create({ containerReadonly: { borderWidth: 1, borderColor: CarbonStyleSheet.color.border_subtle_00 }, focusBoxBorderColor: { borderColor: CarbonStyleSheet.color.focus } }), sizeStyle = StyleSheet.create({ default: { width: 48, height: 24 }, small: { width: 32, height: 16 } }), thumbSizeStyle = StyleSheet.create({ default: { width: 18, height: 18 }, small: { width: 10, height: 10 } }), focusBoxSizeStyle = StyleSheet.create({ default: { width: sizeStyle.default.width + 6, height: sizeStyle.default.height + 6 }, small: { width: sizeStyle.small.width + 6, height: sizeStyle.small.height + 6 } }), mapSwitchTrackColorToken = { normal: { false: { gray_10: Color.Token.gray_10.toggle_off, gray_100: Color.Token.gray_100.toggle_off }, true: { gray_10: Color.Token.gray_10.support_success, gray_100: Color.Token.gray_100.support_success } }, disabled: { false: { gray_10: Color.Token.gray_10.button_disabled, gray_100: Color.Token.gray_100.button_disabled }, true: { gray_10: Color.Token.gray_10.button_disabled, gray_100: Color.Token.gray_100.button_disabled } }, read_only: {}, focused: { false: { gray_10: Color.Token.gray_10.toggle_off, gray_100: Color.Token.gray_100.toggle_off }, true: { gray_10: Color.Token.gray_10.support_success, gray_100: Color.Token.gray_100.support_success } } }, mapSwitchThumbColorToken = { normal: { gray_10: Color.Token.gray_10.icon_on_color, gray_100: Color.Token.gray_100.icon_on_color }, disabled: { gray_10: Color.Token.gray_10.icon_on_color_disabled, gray_100: Color.Token.gray_100.icon_on_color_disabled }, read_only: { gray_10: Color.Token.gray_10.icon_primary, gray_100: Color.Token.gray_100.icon_primary }, focused: { gray_10: Color.Token.gray_10.icon_on_color, gray_100: Color.Token.gray_100.icon_on_color } }, interpolationRange = [0, 1], motionDefault = { duration: Motion.Duration.fast_01, easing: Easing.bezier(Motion.Easing.exit.productive.x1, Motion.Easing.exit.productive.y1, Motion.Easing.exit.productive.x2, Motion.Easing.exit.productive.y2) }, PressableAnimated = Animated.createAnimatedComponent(Pressable), IconCheckmarkAnimated = Animated.createAnimatedComponent(IconCheckmark), mapIconAnimatedFillColor = { gray_10: Color.Token.gray_10.support_success, gray_100: Color.Token.gray_100.support_success }; //# sourceMappingURL=Switch.js.map