|
|
@@ -0,0 +1,335 @@
|
|
|
+import {
|
|
|
+ useImperativeHandle,
|
|
|
+ forwardRef,
|
|
|
+ useEffect,
|
|
|
+ useRef
|
|
|
+} from "react";
|
|
|
+import {
|
|
|
+ Animated,
|
|
|
+ Easing,
|
|
|
+ View
|
|
|
+} from "react-native";
|
|
|
+import type IDialogProps from "./type";
|
|
|
+import type {
|
|
|
+ IDialogRef
|
|
|
+} from "./type";
|
|
|
+import stylesheet, {
|
|
|
+ useStyles
|
|
|
+} from "./stylesheet";
|
|
|
+import {
|
|
|
+ NCoreUIKitLocalize,
|
|
|
+ NCoreUIKitTheme
|
|
|
+} from "../../core/hooks";
|
|
|
+import type {
|
|
|
+ RefForwardingComponent
|
|
|
+} from "../../types";
|
|
|
+import type {
|
|
|
+ IModalRef
|
|
|
+} from "../modal/type";
|
|
|
+import {
|
|
|
+ X as XIcon
|
|
|
+} from "lucide-react-native";
|
|
|
+import Button from "../button";
|
|
|
+import Modal from "../modal";
|
|
|
+import Text from "../text";
|
|
|
+import {
|
|
|
+ uuid
|
|
|
+} from "../../utils";
|
|
|
+
|
|
|
+const Dialog: RefForwardingComponent<IDialogRef, IDialogProps> = ({
|
|
|
+ bottomContentContainerStyle,
|
|
|
+ contentJustify = "centered",
|
|
|
+ contentContainerStyle,
|
|
|
+ bottomContainerStyle,
|
|
|
+ headerContainerStyle,
|
|
|
+ secondaryButtonStyle,
|
|
|
+ secondaryButtonProps,
|
|
|
+ primaryButtonStyle,
|
|
|
+ primaryButtonProps,
|
|
|
+ closeIconProps = {
|
|
|
+ color: "low",
|
|
|
+ size: 22
|
|
|
+ },
|
|
|
+ isVisible = false,
|
|
|
+ withModal = true,
|
|
|
+ headerComponent,
|
|
|
+ bottomComponent,
|
|
|
+ onOverlayPress,
|
|
|
+ variant = "ok",
|
|
|
+ modalProps,
|
|
|
+ id: outID,
|
|
|
+ onClosed,
|
|
|
+ children,
|
|
|
+ content,
|
|
|
+ style,
|
|
|
+ title
|
|
|
+}, ref) => {
|
|
|
+ const {
|
|
|
+ radiuses,
|
|
|
+ colors,
|
|
|
+ spaces
|
|
|
+ } = NCoreUIKitTheme.useContext();
|
|
|
+
|
|
|
+ const {
|
|
|
+ localize
|
|
|
+ } = NCoreUIKitLocalize.useContext();
|
|
|
+
|
|
|
+ const id = useRef(outID ? outID : uuid());
|
|
|
+
|
|
|
+ const {
|
|
|
+ bottomContentContainer: bottomContentContainerDynamicStyle,
|
|
|
+ headerContainer: headerContainerDynamicStyle,
|
|
|
+ bottomContainer: bottomContainerDynamicStyle,
|
|
|
+ primaryButton: primaryButtonDynamicStyle,
|
|
|
+ contentText: contentTextDynamicStyle,
|
|
|
+ headerTitle: headerTitleDynamicStyle,
|
|
|
+ container: containerDynamicStyle,
|
|
|
+ closeIcon: closeIconDynamicStyle,
|
|
|
+ content: contentDynamicStyle
|
|
|
+ } = useStyles({
|
|
|
+ contentJustify,
|
|
|
+ isVisible,
|
|
|
+ radiuses,
|
|
|
+ variant,
|
|
|
+ colors,
|
|
|
+ spaces
|
|
|
+ });
|
|
|
+
|
|
|
+ const scaleAnim = useRef(new Animated.Value(0)).current;
|
|
|
+
|
|
|
+ const modalRef = useRef<IModalRef>(null);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if(isVisible) {
|
|
|
+ Animated.timing(scaleAnim, {
|
|
|
+ useNativeDriver: true,
|
|
|
+ easing: Easing.linear,
|
|
|
+ duration: 350,
|
|
|
+ toValue: 1
|
|
|
+ }).start();
|
|
|
+ }
|
|
|
+ }, [isVisible]);
|
|
|
+
|
|
|
+ useImperativeHandle(
|
|
|
+ ref,
|
|
|
+ () => ({
|
|
|
+ closeAnimation
|
|
|
+ }),
|
|
|
+ []
|
|
|
+ );
|
|
|
+
|
|
|
+ const closeAnimation = (_onClosed?: (props: {
|
|
|
+ id: string;
|
|
|
+ }) => void) => {
|
|
|
+ Animated.timing(scaleAnim, {
|
|
|
+ useNativeDriver: true,
|
|
|
+ easing: Easing.linear,
|
|
|
+ duration: 250,
|
|
|
+ toValue: 0
|
|
|
+ }).start(({
|
|
|
+ finished
|
|
|
+ }) => {
|
|
|
+ if(finished) {
|
|
|
+ if(_onClosed) _onClosed({
|
|
|
+ id: id.current
|
|
|
+ });
|
|
|
+
|
|
|
+ if(onClosed) onClosed({
|
|
|
+ id: id.current
|
|
|
+ });
|
|
|
+
|
|
|
+ if(modalRef && modalRef.current) modalRef.current.closeAnimation();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderHeader = () => {
|
|
|
+ return <View
|
|
|
+ style={[
|
|
|
+ headerContainerStyle,
|
|
|
+ stylesheet.headerContainer,
|
|
|
+ headerContainerDynamicStyle
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {headerComponent || <Text
|
|
|
+ style={[
|
|
|
+ stylesheet.headerTitle,
|
|
|
+ headerTitleDynamicStyle
|
|
|
+ ]}
|
|
|
+ variant="titleLargeSize"
|
|
|
+ color="mid"
|
|
|
+ >
|
|
|
+ {title}
|
|
|
+ </Text>}
|
|
|
+ </View>;
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderBottom = () => {
|
|
|
+ if(variant === "info") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <View
|
|
|
+ style={[
|
|
|
+ bottomContainerStyle,
|
|
|
+ stylesheet.bottomContainer,
|
|
|
+ bottomContainerDynamicStyle
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {bottomComponent || <View
|
|
|
+ style={[
|
|
|
+ bottomContentContainerStyle,
|
|
|
+ stylesheet.bottomContentContainer,
|
|
|
+ bottomContentContainerDynamicStyle
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {secondaryButton()}
|
|
|
+ {primaryButton()}
|
|
|
+ </View>}
|
|
|
+ </View>;
|
|
|
+ };
|
|
|
+
|
|
|
+ const secondaryButton = () => {
|
|
|
+ if(variant !== "yes-no") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <Button
|
|
|
+ onPress={() => {
|
|
|
+ if(secondaryButtonProps?.onPress) secondaryButtonProps.onPress({
|
|
|
+ closeAnimation: (onClosed?: (props: {
|
|
|
+ id: string;
|
|
|
+ }) => void) => {
|
|
|
+ closeAnimation(onClosed);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ spreadBehaviour={contentJustify === "centered" ? "stretch" : "free"}
|
|
|
+ title={secondaryButtonProps?.title || localize("cancel")}
|
|
|
+ isLoading={secondaryButtonProps?.isLoading}
|
|
|
+ style={secondaryButtonStyle}
|
|
|
+ variant="outline"
|
|
|
+ type="neutral"
|
|
|
+ />;
|
|
|
+ };
|
|
|
+
|
|
|
+ const primaryButton = () => {
|
|
|
+ return <Button
|
|
|
+ displayBehaviourWhileLoading={primaryButtonProps?.displayBehaviourWhileLoading}
|
|
|
+ spreadBehaviour={contentJustify === "centered" ? "stretch" : "free"}
|
|
|
+ onPress={() => {
|
|
|
+ if(primaryButtonProps?.onPress) primaryButtonProps?.onPress({
|
|
|
+ closeAnimation: (onClosed?: (props: {
|
|
|
+ id: string;
|
|
|
+ }) => void) => {
|
|
|
+ closeAnimation(onClosed);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ title={primaryButtonProps?.title || localize("ok")}
|
|
|
+ isLoading={primaryButtonProps?.isLoading}
|
|
|
+ style={[
|
|
|
+ primaryButtonStyle,
|
|
|
+ stylesheet.primaryButton,
|
|
|
+ primaryButtonDynamicStyle
|
|
|
+ ]}
|
|
|
+ variant="filled"
|
|
|
+ type="primary"
|
|
|
+ size="medium"
|
|
|
+ />;
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderCloseIcon = () => {
|
|
|
+ if(variant !== "info") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(contentJustify === "centered") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <Button
|
|
|
+ onPress={() => {
|
|
|
+ if(onOverlayPress) onOverlayPress({
|
|
|
+ closeAnimation: (onClosed?: (props: {
|
|
|
+ id: string;
|
|
|
+ }) => void) => {
|
|
|
+ closeAnimation(onClosed);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ icon={() => <XIcon
|
|
|
+ color={closeIconProps.color}
|
|
|
+ size={closeIconProps.size}
|
|
|
+ />}
|
|
|
+ style={[
|
|
|
+ stylesheet.closeIcon,
|
|
|
+ closeIconDynamicStyle
|
|
|
+ ]}
|
|
|
+ isCustomPadding={true}
|
|
|
+ variant="ghost"
|
|
|
+ />;
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderDialog = () => {
|
|
|
+ if(!isVisible) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <Animated.View
|
|
|
+ style={[
|
|
|
+ style,
|
|
|
+ stylesheet.container,
|
|
|
+ containerDynamicStyle,
|
|
|
+ {
|
|
|
+ transform: [{
|
|
|
+ scale: scaleAnim
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {renderCloseIcon()}
|
|
|
+ {renderHeader()}
|
|
|
+ <View
|
|
|
+ style={[
|
|
|
+ contentContainerStyle,
|
|
|
+ stylesheet.content,
|
|
|
+ contentDynamicStyle
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {children || <Text
|
|
|
+ style={[
|
|
|
+ stylesheet.contentText,
|
|
|
+ contentTextDynamicStyle
|
|
|
+ ]}
|
|
|
+ variant="bodyLargeSize"
|
|
|
+ >
|
|
|
+ {content}
|
|
|
+ </Text>}
|
|
|
+ </View>
|
|
|
+ {renderBottom()}
|
|
|
+ </Animated.View>;
|
|
|
+ };
|
|
|
+
|
|
|
+ if(!isVisible) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return withModal ? <Modal
|
|
|
+ {...modalProps}
|
|
|
+ id={`${id.current}-modal`}
|
|
|
+ ref={modalRef}
|
|
|
+ onOverlayPress={() => {
|
|
|
+ if(onOverlayPress) onOverlayPress({
|
|
|
+ closeAnimation: (onClosed?: (props: {
|
|
|
+ id: string;
|
|
|
+ }) => void) => {
|
|
|
+ closeAnimation(onClosed);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {renderDialog()}
|
|
|
+ </Modal> : renderDialog();
|
|
|
+};
|
|
|
+export default forwardRef(Dialog);
|