index.tsx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import {
  2. useImperativeHandle,
  3. forwardRef,
  4. useEffect,
  5. useRef
  6. } from "react";
  7. import {
  8. Animated,
  9. Easing,
  10. View
  11. } from "react-native";
  12. import type IDialogProps from "./type";
  13. import type {
  14. IDialogRef
  15. } from "./type";
  16. import stylesheet, {
  17. useStyles
  18. } from "./stylesheet";
  19. import {
  20. NCoreUIKitLocalize,
  21. NCoreUIKitTheme
  22. } from "../../core/hooks";
  23. import type {
  24. RefForwardingComponent
  25. } from "../../types";
  26. import type {
  27. IModalRef
  28. } from "../modal/type";
  29. import {
  30. X as XIcon
  31. } from "lucide-react-native";
  32. import Button from "../button";
  33. import Modal from "../modal";
  34. import Text from "../text";
  35. import {
  36. uuid
  37. } from "../../utils";
  38. const Dialog: RefForwardingComponent<IDialogRef, IDialogProps> = ({
  39. bottomContentContainerStyle,
  40. contentJustify = "centered",
  41. closeAnimationDelay = 100,
  42. openAnimationDelay = 100,
  43. contentContainerStyle,
  44. bottomContainerStyle,
  45. headerContainerStyle,
  46. secondaryButtonStyle,
  47. secondaryButtonProps,
  48. primaryButtonStyle,
  49. primaryButtonProps,
  50. closeIconProps = {
  51. color: "low",
  52. size: 22
  53. },
  54. isVisible = false,
  55. withModal = true,
  56. headerComponent,
  57. bottomComponent,
  58. onOverlayPress,
  59. variant = "ok",
  60. modalProps,
  61. id: outID,
  62. onClosed,
  63. children,
  64. content,
  65. style,
  66. title
  67. }, ref) => {
  68. const {
  69. radiuses,
  70. colors,
  71. spaces
  72. } = NCoreUIKitTheme.useContext();
  73. const {
  74. localize
  75. } = NCoreUIKitLocalize.useContext();
  76. const id = useRef(outID ? outID : uuid());
  77. const {
  78. bottomContentContainer: bottomContentContainerDynamicStyle,
  79. headerContainer: headerContainerDynamicStyle,
  80. bottomContainer: bottomContainerDynamicStyle,
  81. primaryButton: primaryButtonDynamicStyle,
  82. contentText: contentTextDynamicStyle,
  83. headerTitle: headerTitleDynamicStyle,
  84. container: containerDynamicStyle,
  85. closeIcon: closeIconDynamicStyle,
  86. content: contentDynamicStyle
  87. } = useStyles({
  88. contentJustify,
  89. isVisible,
  90. radiuses,
  91. variant,
  92. colors,
  93. spaces
  94. });
  95. const scaleAnim = useRef(new Animated.Value(0)).current;
  96. const modalRef = useRef<IModalRef>(null);
  97. useEffect(() => {
  98. if(isVisible) {
  99. Animated.timing(scaleAnim, {
  100. duration: openAnimationDelay,
  101. useNativeDriver: true,
  102. easing: Easing.linear,
  103. toValue: 1
  104. }).start();
  105. }
  106. }, [isVisible]);
  107. useImperativeHandle(
  108. ref,
  109. () => ({
  110. closeAnimation
  111. }),
  112. []
  113. );
  114. const closeAnimation = (_onClosed?: (props: {
  115. id: string;
  116. }) => void) => {
  117. Animated.timing(scaleAnim, {
  118. duration: closeAnimationDelay,
  119. useNativeDriver: true,
  120. easing: Easing.linear,
  121. toValue: 0
  122. }).start(({
  123. finished
  124. }) => {
  125. if(finished) {
  126. if(_onClosed) _onClosed({
  127. id: id.current
  128. });
  129. if(onClosed) onClosed({
  130. id: id.current
  131. });
  132. if(modalRef && modalRef.current) modalRef.current.closeAnimation();
  133. }
  134. });
  135. };
  136. const renderHeader = () => {
  137. return <View
  138. style={[
  139. headerContainerStyle,
  140. stylesheet.headerContainer,
  141. headerContainerDynamicStyle
  142. ]}
  143. >
  144. {headerComponent || <Text
  145. style={[
  146. stylesheet.headerTitle,
  147. headerTitleDynamicStyle
  148. ]}
  149. variant="titleLargeSize"
  150. color="mid"
  151. >
  152. {title}
  153. </Text>}
  154. </View>;
  155. };
  156. const renderBottom = () => {
  157. if(variant === "info") {
  158. return null;
  159. }
  160. return <View
  161. style={[
  162. bottomContainerStyle,
  163. stylesheet.bottomContainer,
  164. bottomContainerDynamicStyle
  165. ]}
  166. >
  167. {bottomComponent || <View
  168. style={[
  169. bottomContentContainerStyle,
  170. stylesheet.bottomContentContainer,
  171. bottomContentContainerDynamicStyle
  172. ]}
  173. >
  174. {secondaryButton()}
  175. {primaryButton()}
  176. </View>}
  177. </View>;
  178. };
  179. const secondaryButton = () => {
  180. if(variant !== "yes-no") {
  181. return null;
  182. }
  183. return <Button
  184. onPress={() => {
  185. if(secondaryButtonProps?.onPress) secondaryButtonProps.onPress({
  186. closeAnimation: (onClosed?: (props: {
  187. id: string;
  188. }) => void) => {
  189. closeAnimation(onClosed);
  190. }
  191. });
  192. }}
  193. spreadBehaviour={contentJustify === "centered" ? "stretch" : "free"}
  194. title={secondaryButtonProps?.title || localize("cancel")}
  195. isLoading={secondaryButtonProps?.isLoading}
  196. style={secondaryButtonStyle}
  197. variant="outline"
  198. type="neutral"
  199. />;
  200. };
  201. const primaryButton = () => {
  202. return <Button
  203. displayBehaviourWhileLoading={primaryButtonProps?.displayBehaviourWhileLoading}
  204. spreadBehaviour={contentJustify === "centered" ? "stretch" : "free"}
  205. onPress={() => {
  206. if(primaryButtonProps?.onPress) primaryButtonProps?.onPress({
  207. closeAnimation: (onClosed?: (props: {
  208. id: string;
  209. }) => void) => {
  210. closeAnimation(onClosed);
  211. }
  212. });
  213. }}
  214. title={primaryButtonProps?.title || localize("ok")}
  215. isLoading={primaryButtonProps?.isLoading}
  216. style={[
  217. primaryButtonStyle,
  218. stylesheet.primaryButton,
  219. primaryButtonDynamicStyle
  220. ]}
  221. variant="filled"
  222. type="primary"
  223. size="medium"
  224. />;
  225. };
  226. const renderCloseIcon = () => {
  227. if(variant !== "info") {
  228. return null;
  229. }
  230. if(contentJustify === "centered") {
  231. return null;
  232. }
  233. return <Button
  234. onPress={() => {
  235. if(onOverlayPress) onOverlayPress({
  236. closeAnimation: (onClosed?: (props: {
  237. id: string;
  238. }) => void) => {
  239. closeAnimation(onClosed);
  240. }
  241. });
  242. }}
  243. icon={() => <XIcon
  244. color={closeIconProps.color}
  245. size={closeIconProps.size}
  246. />}
  247. style={[
  248. stylesheet.closeIcon,
  249. closeIconDynamicStyle
  250. ]}
  251. isCustomPadding={true}
  252. variant="ghost"
  253. />;
  254. };
  255. const renderDialog = () => {
  256. if(!isVisible) {
  257. return null;
  258. }
  259. return <Animated.View
  260. style={[
  261. style,
  262. stylesheet.container,
  263. containerDynamicStyle,
  264. {
  265. transform: [{
  266. scale: scaleAnim
  267. }]
  268. }
  269. ]}
  270. >
  271. {renderCloseIcon()}
  272. {renderHeader()}
  273. <View
  274. style={[
  275. contentContainerStyle,
  276. stylesheet.content,
  277. contentDynamicStyle
  278. ]}
  279. >
  280. {children || <Text
  281. style={[
  282. stylesheet.contentText,
  283. contentTextDynamicStyle
  284. ]}
  285. variant="bodyLargeSize"
  286. >
  287. {content}
  288. </Text>}
  289. </View>
  290. {renderBottom()}
  291. </Animated.View>;
  292. };
  293. if(!isVisible) {
  294. return null;
  295. }
  296. return withModal ? <Modal
  297. {...modalProps}
  298. id={`${id.current}-modal`}
  299. ref={modalRef}
  300. onOverlayPress={() => {
  301. if(onOverlayPress) onOverlayPress({
  302. closeAnimation: (onClosed?: (props: {
  303. id: string;
  304. }) => void) => {
  305. closeAnimation(onClosed);
  306. }
  307. });
  308. }}
  309. >
  310. {renderDialog()}
  311. </Modal> : renderDialog();
  312. };
  313. export default forwardRef(Dialog);