index.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import {
  2. type FC,
  3. useEffect,
  4. useRef
  5. } from "react";
  6. import {
  7. TouchableOpacity,
  8. Animated,
  9. Easing,
  10. View
  11. } from "react-native";
  12. import type ISwitchProps from "./type";
  13. import stylesheet, {
  14. getSwitchType,
  15. useStyles
  16. } from "./stylesheet";
  17. import {
  18. NCoreUIKitLocalize,
  19. NCoreUIKitTheme
  20. } from "../../core/hooks";
  21. import type ITextProps from "../text/type";
  22. import Loading from "../loading";
  23. import Text from "../text";
  24. const Switch: FC<ISwitchProps> = ({
  25. displayBehaviourWhileLoading = "disabled",
  26. spreadBehaviour = "baseline",
  27. animationDuration = 100,
  28. isOptional = false,
  29. isDisabled = false,
  30. type = "neutral",
  31. isFlip = false,
  32. customLocalize,
  33. subTitleStyle,
  34. optionalText,
  35. customTheme,
  36. titleStyle,
  37. isLoading,
  38. isActive,
  39. subTitle,
  40. onPress,
  41. title,
  42. style,
  43. ...props
  44. }) => {
  45. const {
  46. inlineSpaces,
  47. colors,
  48. spaces
  49. } = NCoreUIKitTheme.useContext(customTheme);
  50. const {
  51. localize
  52. } = NCoreUIKitLocalize.useContext(customLocalize);
  53. const indicatorAnim = useRef(new Animated.Value(0)).current;
  54. const currentType = getSwitchType({
  55. type
  56. });
  57. const SWITCH_INDICATOR_SIZE = 20;
  58. const {
  59. indicatorContainer: indicatorContainerDynamicStyle,
  60. contentContainer: contentContainerDynamicStyle,
  61. titleContainer: titleContainerDynamicStyle,
  62. optionalText: optionalTextDynamicStyle,
  63. container: containerDynamicStyle,
  64. indicator: indicatorDynamicStyle,
  65. subTitle: subTitleDynamicStyle,
  66. loading: loadingDynamicStyle,
  67. overlay: overlayDynamicStyle,
  68. title: titleDynamicStyle
  69. } = useStyles({
  70. displayBehaviourWhileLoading,
  71. SWITCH_INDICATOR_SIZE,
  72. spreadBehaviour,
  73. inlineSpaces,
  74. currentType,
  75. isDisabled,
  76. isLoading,
  77. isActive,
  78. isFlip,
  79. colors,
  80. spaces,
  81. type
  82. });
  83. useEffect(() => {
  84. if(isActive) {
  85. Animated.timing(indicatorAnim, {
  86. toValue: (SWITCH_INDICATOR_SIZE / 2) + spaces.spacingSm,
  87. duration: animationDuration,
  88. useNativeDriver: true,
  89. easing: Easing.linear
  90. }).start();
  91. } else {
  92. Animated.timing(indicatorAnim, {
  93. duration: animationDuration,
  94. useNativeDriver: true,
  95. easing: Easing.linear,
  96. toValue: 0
  97. }).start();
  98. }
  99. }, [isActive]);
  100. const titleProps: ITextProps = {
  101. color: currentType.titleColor,
  102. };
  103. const subTitleProps: ITextProps = {
  104. color: currentType.subTitleColor,
  105. };
  106. const indicatorIconProps: {
  107. color: keyof NCoreUIKit.IconContentColors;
  108. customColor?: string;
  109. } = {
  110. color: currentType.indicatorColor
  111. };
  112. if (isDisabled || isLoading) {
  113. const stateType = type === "danger" ? "error" : type;
  114. subTitleProps.customColor = colors.system.state.content.disabled[stateType];
  115. titleProps.customColor = colors.system.state.content.disabled[stateType];
  116. if(isActive && stateType === "neutral") {
  117. indicatorIconProps.customColor = colors.system.state.border.disabled.primary;
  118. } else {
  119. indicatorIconProps.customColor = colors.system.state.border.disabled[stateType];
  120. }
  121. }
  122. const renderOptionalText = () => {
  123. if(!isOptional && !optionalText) {
  124. return null;
  125. }
  126. return <Text
  127. variant="labelLargeSize"
  128. color={titleProps.color}
  129. style={[
  130. optionalTextDynamicStyle
  131. ]}
  132. {...titleProps}
  133. >
  134. ( {isOptional ? localize("is-optional") : optionalText} )
  135. </Text>;
  136. };
  137. const renderTitle = () => {
  138. if (!title) {
  139. return null;
  140. }
  141. return <View
  142. style={[
  143. stylesheet.titleContainer,
  144. titleContainerDynamicStyle
  145. ]}
  146. >
  147. <Text
  148. variant="bodyMediumSize"
  149. style={[
  150. titleStyle,
  151. stylesheet.title,
  152. titleDynamicStyle
  153. ]}
  154. {...titleProps}
  155. >
  156. {title}
  157. </Text>
  158. {renderOptionalText()}
  159. </View>;
  160. };
  161. const renderIndicator = () => {
  162. return <Animated.View
  163. style={[
  164. stylesheet.indicator,
  165. indicatorDynamicStyle,
  166. {
  167. transform: [{
  168. translateX: indicatorAnim
  169. }]
  170. }
  171. ]}
  172. />;
  173. };
  174. const renderIndicatorContainer = () => {
  175. if (isLoading) {
  176. return <Loading
  177. style={[
  178. loadingDynamicStyle
  179. ]}
  180. />;
  181. }
  182. return <View
  183. style={[
  184. stylesheet.indicatorContainer,
  185. indicatorContainerDynamicStyle
  186. ]}
  187. >
  188. {renderIndicator()}
  189. {renderOverlay()}
  190. </View>;
  191. };
  192. const renderOverlay = () => {
  193. return <View
  194. style={[
  195. stylesheet.overlay,
  196. overlayDynamicStyle
  197. ]}
  198. />;
  199. };
  200. const renderSubtitle = () => {
  201. if(!subTitle) {
  202. return null;
  203. }
  204. return <Text
  205. {...subTitleProps}
  206. style={[
  207. subTitleStyle,
  208. stylesheet.subTitle,
  209. subTitleDynamicStyle
  210. ]}
  211. >
  212. {subTitle}
  213. </Text>;
  214. };
  215. const renderContent = () => {
  216. if(!title) {
  217. return null;
  218. }
  219. return <View
  220. style={[
  221. stylesheet.contentContainer,
  222. contentContainerDynamicStyle
  223. ]}
  224. >
  225. {renderTitle()}
  226. {renderSubtitle()}
  227. </View>;
  228. };
  229. return (
  230. <TouchableOpacity
  231. {...props}
  232. onPress={isDisabled || isLoading ? () => null : onPress ? onPress : undefined}
  233. disabled={isDisabled || isLoading || !onPress}
  234. style={[
  235. style,
  236. stylesheet.container,
  237. containerDynamicStyle
  238. ]}
  239. >
  240. {isFlip ? null : renderIndicatorContainer()}
  241. {renderContent()}
  242. {isFlip ? renderIndicatorContainer() : null}
  243. </TouchableOpacity>
  244. );
  245. };
  246. export default Switch;