| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- import {
- useEffect,
- useState,
- type FC,
- useRef
- } from "react";
- import {
- TouchableWithoutFeedback,
- type ViewStyle,
- Animated,
- Easing,
- View
- } from "react-native";
- import type ITooltipProps from "./type";
- import stylesheet, {
- getTooltipType,
- useStyles
- } from "./stylesheet";
- import {
- NCoreUIKitTheme
- } from "../../core/hooks";
- import {
- X as XIcon
- } from "lucide-react-native";
- import Button from "../button";
- import Text from "../text";
- const Tooltip: FC<ITooltipProps> = ({
- isCloseOnPressActionButton = true,
- location = {
- horizontal: "center",
- vertical: "top"
- },
- isCloseOnPress = false,
- icon: CustomIcon,
- type = "neutral",
- customTheme,
- isVisible,
- onClosed,
- subTitle,
- children,
- onClose,
- onOpen,
- content,
- action,
- style,
- title
- }) => {
- const {
- radiuses,
- colors,
- spaces
- } = NCoreUIKitTheme.useContext(customTheme);
- const [
- wrapperSize,
- setWrapperSize
- ] = useState({
- height: 0,
- width: 0
- });
- const [
- tooltipSize,
- setTooltipSize
- ] = useState({
- height: 0,
- width: 0
- });
- const [
- isInternalVisible,
- setIsInternalVisible
- ] = useState(false);
- const opacityAnim = useRef(new Animated.Value(0)).current;
- const currentType = getTooltipType({
- type
- });
- const {
- containerObject: containerObjectDynamicStyle,
- contentContainer: contentContainerDynamicStyle,
- iconContainer: iconContainerDynamicStyle,
- container: containerDynamicStyle,
- subTitle: subTitleDynamicStyle,
- action: actionDynamicStyle,
- title: titleDynamicStyle,
- caret: caretDynamicStyle
- } = useStyles({
- currentType,
- radiuses,
- colors,
- spaces,
- type
- });
- const isShow = isVisible !== undefined ? isVisible : isInternalVisible;
- useEffect(() => {
- if(isShow) {
- if(onOpen) onOpen();
- Animated.timing(opacityAnim, {
- useNativeDriver: true,
- easing: Easing.linear,
- duration: 200,
- toValue: 1
- }).start();
- } else {
- Animated.timing(opacityAnim, {
- useNativeDriver: true,
- easing: Easing.linear,
- duration: 200,
- toValue: 0
- }).start(({
- finished
- }) => {
- if(finished && onClosed) {
- onClosed();
- }
- });
- }
- }, [
- isShow
- ]);
- const handleClose = () => {
- if(isVisible === undefined) {
- setIsInternalVisible(false);
- }
- if(onClose) onClose();
- };
- const handleToggle = () => {
- if(isVisible === undefined) {
- setIsInternalVisible(!isInternalVisible);
- }
- if(!isShow && onOpen) {
- onOpen();
- } else if (isShow && onClose) {
- onClose();
- }
- };
- const renderIcon = () => {
- if(!CustomIcon) {
- return null;
- }
- return <View
- style={[
- stylesheet.iconContainer,
- iconContainerDynamicStyle
- ]}
- >
- <CustomIcon
- color={currentType.iconColor}
- size={16}
- />
- </View>;
- };
- const renderContent = () => {
- if(content) {
- if(typeof content === "function") {
- return content();
- }
- return content;
- }
- return <View
- style={[
- stylesheet.contentContainer,
- contentContainerDynamicStyle
- ]}
- >
- {
- title ?
- <Text
- style={{
- ...stylesheet.title,
- ...titleDynamicStyle
- }}
- color={currentType.titleColor}
- variant="labelMediumSize"
- numberOfLines={3}
- >
- {title}
- </Text>
- :
- null
- }
- {
- subTitle ?
- <Text
- style={{
- ...stylesheet.subTitle,
- ...subTitleDynamicStyle
- }}
- color={currentType.subTitleColor}
- variant="labelSmallSize"
- numberOfLines={2}
- >
- {subTitle}
- </Text>
- :
- null
- }
- </View>;
- };
- const renderAction = () => {
- if(!action) {
- return null;
- }
- return <Button
- onPress={() => {
- if(isCloseOnPressActionButton) handleClose();
- if(action.onPress) {
- action.onPress({
- closeAnimation: handleClose
- });
- }
- }}
- icon={() => {
- if(action.title) {
- return null;
- }
- return <XIcon
- color={colors.content.text[currentType.actionColor]}
- size={16}
- />;
- }}
- style={{
- ...action.style,
- ...actionDynamicStyle
- }}
- title={action.title ? action.title : undefined}
- spreadBehaviour="free"
- isCustomPadding={true}
- variant="ghost"
- size="small"
- type={type}
- />;
- };
- const getTooltipStyle = (): ViewStyle => {
- const _style: ViewStyle = {};
- if(tooltipSize.width > 0 && wrapperSize.width > 0) {
- if(location.vertical === "top") {
- _style.bottom = wrapperSize.height + 8;
- } else if (location.vertical === "bottom") {
- _style.top = wrapperSize.height + 8;
- } else {
- _style.top = (wrapperSize.height - tooltipSize.height) / 2;
- }
- if(location.horizontal === "left") {
- _style.right = wrapperSize.width + 8;
- } else if (location.horizontal === "right") {
- _style.left = wrapperSize.width + 8;
- } else {
- _style.left = (wrapperSize.width - tooltipSize.width) / 2;
- }
- } else {
- _style.opacity = 0;
- }
- return _style;
- };
- const getCaretStyle = (): ViewStyle => {
- const _style: ViewStyle = {};
- if(location.vertical === "top") {
- _style.bottom = -4;
- } else if (location.vertical === "bottom") {
- _style.top = -4;
- } else {
- _style.top = (tooltipSize.height - 12) / 2;
- }
- if(location.horizontal === "left") {
- _style.right = -4;
- } else if (location.horizontal === "right") {
- _style.left = -4;
- } else {
- _style.left = (tooltipSize.width - 12) / 2;
- }
- if (location.vertical === "top" && location.horizontal === "right") {
- _style.left = 12;
- } else if (location.vertical === "top" && location.horizontal === "left") {
- _style.right = 12;
- } else if (location.vertical === "bottom" && location.horizontal === "right") {
- _style.left = 12;
- } else if (location.vertical === "bottom" && location.horizontal === "left") {
- _style.right = 12;
- }
- return _style;
- };
- return <View
- onLayout={(e) => {
- const {
- height,
- width
- } = e.nativeEvent.layout;
- setWrapperSize(prev => {
- if (prev.height === height && prev.width === width) {
- return prev;
- }
- return {
- height,
- width
- };
- });
- }}
- style={[
- stylesheet.wrapper,
- style
- ]}
- >
- <TouchableWithoutFeedback
- onPress={() => {
- if(!isCloseOnPress) {
- handleToggle();
- } else if(isShow) {
- handleClose();
- } else {
- handleToggle();
- }
- }}
- >
- <View>
- {children}
- </View>
- </TouchableWithoutFeedback>
- {
- isShow || opacityAnim !== undefined ?
- <Animated.View
- onLayout={(e) => {
- const {
- height,
- width
- } = e.nativeEvent.layout;
- setTooltipSize(prev => {
- if (prev.height === height && prev.width === width) {
- return prev;
- }
- return {
- height,
- width
- };
- });
- }}
- style={[
- stylesheet.container,
- containerDynamicStyle,
- getTooltipStyle(),
- {
- opacity: opacityAnim
- }
- ]}
- pointerEvents={isShow ? "auto" : "none"}
- >
- <View
- style={[
- stylesheet.caret,
- caretDynamicStyle,
- getCaretStyle()
- ]}
- />
- <View
- style={[
- stylesheet.containerObject,
- containerObjectDynamicStyle
- ]}
- >
- {renderIcon()}
- {renderContent()}
- {renderAction()}
- </View>
- </Animated.View>
- :
- null
- }
- </View>;
- };
- export default Tooltip;
|