From 3d95f196433fd6e80148bebfcae083a4d0e99e7e Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:02:42 +0900 Subject: [PATCH 01/10] =?UTF-8?q?chore:=20react-native-anchor-point=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index eec9bea..f1b95b0 100644 --- a/package.json +++ b/package.json @@ -181,5 +181,8 @@ "create-react-native-library": { "type": "library", "version": "0.41.0" + }, + "dependencies": { + "react-native-anchor-point": "^1.0.6" } } From f14df92deac2cef0ac4570492bd14e9dc9757537 Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:02:57 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20tooltip=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/App.tsx | 195 ++++++++++++++++++-- src/index.tsx | 434 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 611 insertions(+), 18 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index c90d28b..bd2dfc4 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,30 +1,193 @@ -import { useState, useEffect } from 'react'; -import { StyleSheet, View, Text } from 'react-native'; -import { multiply } from 'react-native-good-tooltip'; +import { FlatList, SafeAreaView, StyleSheet, Text, View } from 'react-native'; +import { Tooltip } from 'react-native-good-tooltip'; export default function App() { - const [result, setResult] = useState(); - - useEffect(() => { - multiply(3, 7).then(setResult); - }, []); + const data = [ + 'In FlatList', + 'zIndex must be specified using', + 'CellRendererComponent.', + ]; return ( - - Result: {result} - + + {/* Header*/} + + + + + + + + + + + + + + + + + + This Tooltip component does not use modal, so you must specify + z-Index. Especially if the placement is bottom, you need to be + especially careful. + + + + + This Tooltip component does not use modal, so you must specify + z-Index. Especially if the placement is bottom, you need to be + especially careful. + + + + + {/* Body*/} + { + return ( + + ); + }} + renderItem={({ item, index }) => ( + { + if (index % 3 === 0) return 'right'; + if (index % 2 === 0) return 'center'; + + return 'left'; + })()} + text={'List Item Tooltip ' + index} + > + + {`${item}`} + + + )} + ListFooterComponent={() => ( + + + + + There may be situations where you need to add the overflow: + 'visible' style. + + + + + + + + + + + + + + + + + )} + /> + ); } const styles = StyleSheet.create({ container: { flex: 1, - alignItems: 'center', - justifyContent: 'center', + marginVertical: 8, + marginHorizontal: 16, }, box: { - width: 60, - height: 60, - marginVertical: 20, + width: 50, + height: 50, + }, + message: { + padding: 8, }, }); diff --git a/src/index.tsx b/src/index.tsx index 9e42cf3..8d1937c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,433 @@ -export function multiply(a: number, b: number): Promise { - return Promise.resolve(a * b); +import React, { useEffect, useState } from 'react'; +import { + Animated, + Dimensions, + Platform, + Text, + TouchableOpacity, + View, + type ColorValue, + type LayoutChangeEvent, + type ViewStyle, +} from 'react-native'; +import { withAnchorPoint } from 'react-native-anchor-point'; + +const SIDE_ARROW_INSET = 12; + +interface ToolTipProps { + placement: 'top' | 'bottom' | 'left' | 'right'; + anchor?: 'center' | 'left' | 'right' | 'top' | 'bottom'; + offset?: { + position?: { + x?: number; + y?: number; + }; + arrow?: { + x?: number; + y?: number; + }; + }; + arrowSize?: { + width?: number; + height?: number; + }; + width?: number; + containerStyle?: ViewStyle; + color?: ColorValue | string; + colorType?: 'primary' | 'black'; + text: string | React.ReactElement; + children?: React.ReactElement; + isVisible: boolean; + onPress?: () => void; + onVisibleChange?: (isVisible: boolean) => void; + disableAutoHide?: boolean; + delayShowTime?: number; + autoHideTime?: number; + requiredConfirmation?: boolean; } + +const DefaultArrowSize = { width: 10, height: 6 }; + +export const Tooltip = ({ + isVisible, + anchor = 'center', + width = Dimensions.get('window').width * 0.3, + containerStyle, + text, + children, + placement, + onPress, + color, + colorType = 'primary', + offset, + arrowSize = DefaultArrowSize, + onVisibleChange, + disableAutoHide = false, + delayShowTime = 0, + autoHideTime = 5000, + requiredConfirmation = true, +}: ToolTipProps) => { + const [currentIsVisible, setCurrentIsVisible] = useState(false); + const [tooltipPosition, setTooltipPosition] = useState({ + top: 0, + left: Platform.OS === 'android' && placement === 'right' ? -4 : 0, + }); + const [tooltipSize, setTooltipSize] = useState({ width: 0, height: 0 }); + const animatedValue = new Animated.Value(0); + + const isVerticalPlacement = placement === 'top' || placement === 'bottom'; + const tooltipColor = (() => { + if (color) return color; + if (colorType === 'black') return 'black'; + + return '#3Eb489'; + })(); + const tooltipPadding = { + paddingVertical: 8, + paddingHorizontal: 12, + }; + const arrowStyle = { + borderLeftWidth: (arrowSize?.width || DefaultArrowSize.width) / 2, + borderRightWidth: (arrowSize?.width || DefaultArrowSize.width) / 2, + borderBottomWidth: arrowSize?.height || DefaultArrowSize.height, + borderLeftColor: 'transparent', + borderRightColor: 'transparent', + borderBottomColor: tooltipColor, + }; + + useEffect(() => { + if ( + autoHideTime > 0 && + disableAutoHide === false && + requiredConfirmation === false + ) { + setTimeout(() => { + // 기본적으로 5초 뒤 사라짐 처리 + setCurrentIsVisible(false); + }, autoHideTime); + } + }, [autoHideTime, disableAutoHide, requiredConfirmation]); + + useEffect(() => { + if (onVisibleChange) { + onVisibleChange(isVisible); + } + }, [isVisible, onVisibleChange]); + + useEffect(() => { + setTimeout(() => { + setCurrentIsVisible(isVisible); + }, delayShowTime); + }, [delayShowTime, isVisible]); + + const handleVerticalTooltipLayout = (event: LayoutChangeEvent) => { + const { width, height } = event.nativeEvent.layout; + if (placement === 'top') { + setTooltipPosition({ top: -height, left: 0 }); + } + setTooltipSize({ + width, + height, + }); + }; + + const handleHorizontalTooltipLayout = (event: LayoutChangeEvent) => { + const { width, height } = event.nativeEvent.layout; + + const newLeft = width - 4; + if (placement === 'left') { + setTooltipPosition({ top: 0, left: -newLeft }); + } + setTooltipSize({ + width, + height, + }); + }; + + const renderVerticalArrow = () => ( + { + if (anchor === 'left') return 'flex-start'; + if (anchor === 'right') return 'flex-end'; + return 'center'; + })(), + }, + containerStyle, + ]} + > + { + let x = SIDE_ARROW_INSET + (offset?.arrow?.x || 0); + + if (!isVerticalPlacement) { + x = offset?.arrow?.x || 0; + } + if (anchor === 'center') { + x = offset?.arrow?.x || 0; + } + if (anchor === 'right') { + x = -x; + } + + return x; + })(), + top: (() => { + let y = SIDE_ARROW_INSET + (offset?.arrow?.y || 0); + + if (isVerticalPlacement) { + y = offset?.arrow?.y || 0; + } + if (anchor === 'center') { + y = offset?.arrow?.y || 0; + } + if (anchor === 'bottom') { + y = -y; + } + + // 은찬: AOS에서 간혈적으로 박스와 화살표 사이에 틈이 보이는 이슈가 있어서 0.1 추가 + return y + (Platform.OS === 'android' ? 0.1 : 0); + })(), + transform: (() => { + if (placement === 'bottom') return [{ rotate: '0deg' }]; + if (placement === 'top') return [{ rotate: '180deg' }]; + if (placement === 'left') return [{ rotate: '90deg' }]; + if (placement === 'right') return [{ rotate: '-90deg' }]; + return undefined; + })(), + }} + > + {/*{arrowSize === undefined && }*/} + + + ); + + const renderHorizontalArrow = () => ( + { + let left = 0; + if (placement === 'left') { + left = -2; + } + if (placement === 'right') { + left = 2; + } + + // 은찬: AOS에서 간혈적으로 박스와 화살표 사이에 틈이 보이는 이슈가 있어서 0.1 추가 + return left + (Platform.OS === 'android' ? 0.1 : 0); + })(), + top: (() => { + return 0; + })(), + transform: (() => { + if (placement === 'bottom') return [{ rotate: '0deg' }]; + if (placement === 'top') return [{ rotate: '180deg' }]; + if (placement === 'left') return [{ rotate: '90deg' }]; + if (placement === 'right') return [{ rotate: '-90deg' }]; + return undefined; + })(), + }} + > + {/*{arrowSize === undefined && }*/} + + ); + + const transformsStyle = (() => { + let x = 0; + let y = 0; + + // vertical + if (placement === 'top' && anchor === 'left') { + x = 0; + y = 1; + } + if (placement === 'top' && anchor === 'right') { + x = 1; + y = 1; + } + if (placement === 'top' && anchor === 'center') { + x = 0.5; + y = 1; + } + + if (placement === 'bottom' && anchor === 'left') { + x = 0; + y = 0; + } + if (placement === 'bottom' && anchor === 'center') { + x = 0.5; + y = 0; + } + if (placement === 'bottom' && anchor === 'right') { + x = 1; + y = 0; + } + + // horizontal + if (placement === 'left' && anchor === 'top') { + x = 1; + y = 0; + } + if (placement === 'left' && anchor === 'center') { + x = 1; + y = 0.5; + } + if (placement === 'left' && anchor === 'bottom') { + x = 1; + y = 1; + } + if (placement === 'right' && anchor === 'top') { + x = 0; + y = 0; + } + if (placement === 'right' && anchor === 'center') { + x = 0; + y = 0.5; + } + if (placement === 'right' && anchor === 'bottom') { + x = 0; + y = 1; + } + + if (currentIsVisible) { + Animated.spring(animatedValue, { + toValue: 1, + speed: 6, + useNativeDriver: true, + }).start(); + } else { + animatedValue.setValue(1); + Animated.timing(animatedValue, { + toValue: 0, + duration: 300, + useNativeDriver: true, + }).start(); + } + + return withAnchorPoint( + { transform: [{ scale: animatedValue }] }, + { x, y }, + { width: tooltipSize.width, height: tooltipSize.height } + ); + })(); + + const renderTooltipContent = () => ( + // View Overflow 영역에 있는 Tooltip을 선택하기 위해 TouchableOpacity 사용 + { + setCurrentIsVisible((prevState) => !prevState); + onPress && onPress(); + }} + style={{ + ...tooltipPadding, + backgroundColor: tooltipColor, + borderRadius: 10, + }} + > + + {/* content */} + {typeof text !== 'string' ? ( + text + ) : ( + + )} + + {requiredConfirmation && ( + <> + + {/**/} + + )} + + + ); + + const renderVerticalTooltip = () => ( + { + if (anchor === 'left') return 'flex-start'; + if (anchor === 'right') return 'flex-end'; + return 'center'; + })(), + top: tooltipPosition.top + (offset?.position?.y || 0), + left: tooltipPosition.left + (offset?.position?.x || 0), + }} + > + + {placement === 'top' && renderTooltipContent()} + {renderVerticalArrow()} + {placement === 'bottom' && renderTooltipContent()} + + + ); + const renderHorizontalTooltip = () => ( + // 툴팁 & 화살표 정렬 기준 ( top, center, bottom ) + { + if (anchor === 'top') return 'flex-start'; + if (anchor === 'bottom') return 'flex-end'; + return 'center'; + })(), + }} + > + + {placement === 'left' && renderTooltipContent()} + {renderHorizontalArrow()} + {placement === 'right' && renderTooltipContent()} + + + ); + + return ( + + {placement === 'top' && renderVerticalTooltip()} + {isVerticalPlacement ? ( + children + ) : ( + + {placement === 'left' && renderHorizontalTooltip()} + {children} + {placement === 'right' && renderHorizontalTooltip()} + + )} + {placement === 'bottom' && renderVerticalTooltip()} + + ); +}; From ad70b966b02cddfcc35fe0a3d354174c337889d7 Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:05:20 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=EC=99=B8=EB=B6=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20arrowElement=20=EC=B6=94=EA=B0=80=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EA=B2=8C=20=EC=98=B5=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 8d1937c..cd892c3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -31,6 +31,7 @@ interface ToolTipProps { width?: number; height?: number; }; + arrowElement?: React.ReactElement; width?: number; containerStyle?: ViewStyle; color?: ColorValue | string; @@ -61,6 +62,7 @@ export const Tooltip = ({ colorType = 'primary', offset, arrowSize = DefaultArrowSize, + arrowElement, onVisibleChange, disableAutoHide = false, delayShowTime = 0, @@ -200,6 +202,7 @@ export const Tooltip = ({ })(), }} > + {} {/*{arrowSize === undefined && }*/} @@ -233,7 +236,7 @@ export const Tooltip = ({ })(), }} > - {/*{arrowSize === undefined && }*/} + {arrowElement === undefined && arrowElement} ); From ffe49dbf26f644387ef3e9ba05fc4fd197b5922e Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:11:27 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=EC=99=B8=EB=B6=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A7=80=EC=A0=95?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/close.png | Bin 0 -> 386 bytes src/index.tsx | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 assets/close.png diff --git a/assets/close.png b/assets/close.png new file mode 100644 index 0000000000000000000000000000000000000000..81b467b7762924be83a2eb0043ec1d1c9469f846 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?393lL^>oo1K-6l5$8 za(7}_cTVOdki(Mh= zJvHi+`-G@0_4L5ibwXL92ljDq+1sMwIZ4HH-GNOy{&f?Lui2HKZC~=h`<(pJDQT75u**4S6pP-Z0gG(0p%<$28YL69wVe8x z%_Hf$fNe&z2ZQMO1H1;-Y7ZXGoM=$4^-2HA=l<@9tqU#|6i6?9w^yrssvE;6M=>j} z7QW(zk4s*#`o2(2ec`${U1i?NwWpjm8Hc+|WlV@#E4}L0+oKZ=o=?&;_erpP{VWgY dy-6+4n3?DDmicZv_750j44$rjF6*2UngC0skt6^B literal 0 HcmV?d00001 diff --git a/src/index.tsx b/src/index.tsx index cd892c3..7f3a789 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,10 +9,12 @@ import { type ColorValue, type LayoutChangeEvent, type ViewStyle, + Image, } from 'react-native'; import { withAnchorPoint } from 'react-native-anchor-point'; const SIDE_ARROW_INSET = 12; +const CLOSE_ICON_SIZE = 16; interface ToolTipProps { placement: 'top' | 'bottom' | 'left' | 'right'; @@ -31,8 +33,12 @@ interface ToolTipProps { width?: number; height?: number; }; + closeSize?: { + width?: number; + height?: number; + }; arrowElement?: React.ReactElement; - width?: number; + tooltipWidth?: number; containerStyle?: ViewStyle; color?: ColorValue | string; colorType?: 'primary' | 'black'; @@ -52,7 +58,7 @@ const DefaultArrowSize = { width: 10, height: 6 }; export const Tooltip = ({ isVisible, anchor = 'center', - width = Dimensions.get('window').width * 0.3, + tooltipWidth = Dimensions.get('window').width * 0.3, containerStyle, text, children, @@ -61,6 +67,7 @@ export const Tooltip = ({ color, colorType = 'primary', offset, + closeSize, arrowSize = DefaultArrowSize, arrowElement, onVisibleChange, @@ -351,7 +358,13 @@ export const Tooltip = ({ {requiredConfirmation && ( <> - {/**/} + )} @@ -374,7 +387,7 @@ export const Tooltip = ({ style={[ { position: 'absolute', - width, + width: tooltipWidth, opacity: animatedValue, }, transformsStyle, From e5c7e5a18edf47ba550b07a84da6eff8a4ac86f8 Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:16:59 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20getAnchorPoint=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/functions.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ src/index.tsx | 57 ++-------------------------------------------- 2 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 src/functions.ts diff --git a/src/functions.ts b/src/functions.ts new file mode 100644 index 0000000..65f7713 --- /dev/null +++ b/src/functions.ts @@ -0,0 +1,59 @@ +export const getAnchorPoint = (placement: string, anchor: string) => { + let x = 0; + let y = 0; + + // vertical + if (placement === 'top' && anchor === 'left') { + x = 0; + y = 1; + } + if (placement === 'top' && anchor === 'right') { + x = 1; + y = 1; + } + if (placement === 'top' && anchor === 'center') { + x = 0.5; + y = 1; + } + + if (placement === 'bottom' && anchor === 'left') { + x = 0; + y = 0; + } + if (placement === 'bottom' && anchor === 'center') { + x = 0.5; + y = 0; + } + if (placement === 'bottom' && anchor === 'right') { + x = 1; + y = 0; + } + + // horizontal + if (placement === 'left' && anchor === 'top') { + x = 1; + y = 0; + } + if (placement === 'left' && anchor === 'center') { + x = 1; + y = 0.5; + } + if (placement === 'left' && anchor === 'bottom') { + x = 1; + y = 1; + } + if (placement === 'right' && anchor === 'top') { + x = 0; + y = 0; + } + if (placement === 'right' && anchor === 'center') { + x = 0; + y = 0.5; + } + if (placement === 'right' && anchor === 'bottom') { + x = 0; + y = 1; + } + + return { x, y }; +}; diff --git a/src/index.tsx b/src/index.tsx index 7f3a789..c352d89 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,6 +12,7 @@ import { Image, } from 'react-native'; import { withAnchorPoint } from 'react-native-anchor-point'; +import { getAnchorPoint } from './functions'; const SIDE_ARROW_INSET = 12; const CLOSE_ICON_SIZE = 16; @@ -248,61 +249,7 @@ export const Tooltip = ({ ); const transformsStyle = (() => { - let x = 0; - let y = 0; - - // vertical - if (placement === 'top' && anchor === 'left') { - x = 0; - y = 1; - } - if (placement === 'top' && anchor === 'right') { - x = 1; - y = 1; - } - if (placement === 'top' && anchor === 'center') { - x = 0.5; - y = 1; - } - - if (placement === 'bottom' && anchor === 'left') { - x = 0; - y = 0; - } - if (placement === 'bottom' && anchor === 'center') { - x = 0.5; - y = 0; - } - if (placement === 'bottom' && anchor === 'right') { - x = 1; - y = 0; - } - - // horizontal - if (placement === 'left' && anchor === 'top') { - x = 1; - y = 0; - } - if (placement === 'left' && anchor === 'center') { - x = 1; - y = 0.5; - } - if (placement === 'left' && anchor === 'bottom') { - x = 1; - y = 1; - } - if (placement === 'right' && anchor === 'top') { - x = 0; - y = 0; - } - if (placement === 'right' && anchor === 'center') { - x = 0; - y = 0.5; - } - if (placement === 'right' && anchor === 'bottom') { - x = 0; - y = 1; - } + const { x, y } = getAnchorPoint(placement, anchor); if (currentIsVisible) { Animated.spring(animatedValue, { From 3e17feb7cdf1b4b34cd8f4b8826a2e920284987a Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:23:06 +0900 Subject: [PATCH 06/10] =?UTF-8?q?docs:=20=EC=98=88=EC=A0=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/App.tsx | 5 ++++- src/index.tsx | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index bd2dfc4..d1e4ddc 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -23,6 +23,7 @@ export default function App() { text="This is a tooltip" placement={'bottom'} anchor={'left'} + requiredConfirmation > @@ -41,6 +42,7 @@ export default function App() { text="This is a tooltip" placement={'bottom'} anchor={'right'} + requiredConfirmation > @@ -50,7 +52,6 @@ export default function App() { style={{ backgroundColor: 'yellow', opacity: 0.9, - // zIndex: -1, paddingTop: 40, flexDirection: 'row', zIndex: 0, @@ -133,6 +134,7 @@ export default function App() { anchor={'right'} text={'Top RIght'} isVisible={true} + requiredConfirmation > @@ -149,6 +151,7 @@ export default function App() { placement={'left'} anchor={'top'} colorType={'black'} + requiredConfirmation > diff --git a/src/index.tsx b/src/index.tsx index c352d89..5ad76ba 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -75,7 +75,7 @@ export const Tooltip = ({ disableAutoHide = false, delayShowTime = 0, autoHideTime = 5000, - requiredConfirmation = true, + requiredConfirmation = false, }: ToolTipProps) => { const [currentIsVisible, setCurrentIsVisible] = useState(false); const [tooltipPosition, setTooltipPosition] = useState({ @@ -210,8 +210,7 @@ export const Tooltip = ({ })(), }} > - {} - {/*{arrowSize === undefined && }*/} + {arrowElement === undefined && arrowElement} ); From 68140e5fb49c78f60eabcbd26667f05f6d17ad62 Mon Sep 17 00:00:00 2001 From: gim-eunchan Date: Sun, 25 Aug 2024 19:30:03 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20props=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/functions.ts | 10 ++++++++++ src/index.tsx | 27 +++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/functions.ts b/src/functions.ts index 65f7713..5f10376 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -57,3 +57,13 @@ export const getAnchorPoint = (placement: string, anchor: string) => { return { x, y }; }; + +export const createArrowShape = (width: number, height: number) => { + return { + borderLeftWidth: width / 2, + borderRightWidth: width / 2, + borderBottomWidth: height, + borderLeftColor: 'transparent', + borderRightColor: 'transparent', + }; +}; diff --git a/src/index.tsx b/src/index.tsx index 5ad76ba..15490fa 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,7 +12,7 @@ import { Image, } from 'react-native'; import { withAnchorPoint } from 'react-native-anchor-point'; -import { getAnchorPoint } from './functions'; +import { createArrowShape, getAnchorPoint } from './functions'; const SIDE_ARROW_INSET = 12; const CLOSE_ICON_SIZE = 16; @@ -39,7 +39,7 @@ interface ToolTipProps { height?: number; }; arrowElement?: React.ReactElement; - tooltipWidth?: number; + tooltipStyle?: ViewStyle; containerStyle?: ViewStyle; color?: ColorValue | string; colorType?: 'primary' | 'black'; @@ -59,7 +59,11 @@ const DefaultArrowSize = { width: 10, height: 6 }; export const Tooltip = ({ isVisible, anchor = 'center', - tooltipWidth = Dimensions.get('window').width * 0.3, + tooltipStyle = { + width: Dimensions.get('window').width * 0.3, + paddingVertical: 8, + paddingHorizontal: 12, + }, containerStyle, text, children, @@ -92,16 +96,11 @@ export const Tooltip = ({ return '#3Eb489'; })(); - const tooltipPadding = { - paddingVertical: 8, - paddingHorizontal: 12, - }; const arrowStyle = { - borderLeftWidth: (arrowSize?.width || DefaultArrowSize.width) / 2, - borderRightWidth: (arrowSize?.width || DefaultArrowSize.width) / 2, - borderBottomWidth: arrowSize?.height || DefaultArrowSize.height, - borderLeftColor: 'transparent', - borderRightColor: 'transparent', + ...createArrowShape( + arrowSize?.width || DefaultArrowSize.width, + arrowSize?.height || DefaultArrowSize.height + ), borderBottomColor: tooltipColor, }; @@ -281,9 +280,9 @@ export const Tooltip = ({ onPress && onPress(); }} style={{ - ...tooltipPadding, backgroundColor: tooltipColor, borderRadius: 10, + ...tooltipStyle, }} > Date: Sun, 25 Aug 2024 19:41:43 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20props=20=EB=AA=A8=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/App.tsx | 4 +-- src/index.tsx | 67 +++++++++++++++++++++++---------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index d1e4ddc..e552feb 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -32,7 +32,7 @@ export default function App() { isVisible={true} text="This is a tooltip" placement={'bottom'} - colorType={'black'} + styles={{ color: 'black' }} > @@ -150,7 +150,6 @@ export default function App() { text="Left Tooltip" placement={'left'} anchor={'top'} - colorType={'black'} requiredConfirmation > @@ -168,7 +167,6 @@ export default function App() { text="Center Tooltip" placement={'left'} anchor={'bottom'} - colorType={'black'} > diff --git a/src/index.tsx b/src/index.tsx index 15490fa..00ff9d0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,8 +14,15 @@ import { import { withAnchorPoint } from 'react-native-anchor-point'; import { createArrowShape, getAnchorPoint } from './functions'; +// DEFAULT VALUES const SIDE_ARROW_INSET = 12; -const CLOSE_ICON_SIZE = 16; +const CLOSE_ICON_SIZE = { width: 16, height: 16 }; +const ARROW_SIZE = { width: 10, height: 6 }; +const TOOLTIP_STYLE = { + width: Dimensions.get('window').width * 0.3, + paddingVertical: 8, + paddingHorizontal: 12, +}; interface ToolTipProps { placement: 'top' | 'bottom' | 'left' | 'right'; @@ -30,19 +37,20 @@ interface ToolTipProps { y?: number; }; }; - arrowSize?: { - width?: number; - height?: number; - }; - closeSize?: { - width?: number; - height?: number; - }; arrowElement?: React.ReactElement; - tooltipStyle?: ViewStyle; - containerStyle?: ViewStyle; - color?: ColorValue | string; - colorType?: 'primary' | 'black'; + styles?: { + color?: ColorValue | 'primary'; + containerStyle?: ViewStyle; + tooltipStyle?: ViewStyle; + arrowSize?: { + width?: number; + height?: number; + }; + closeSize?: { + width?: number; + height?: number; + }; + }; text: string | React.ReactElement; children?: React.ReactElement; isVisible: boolean; @@ -54,26 +62,19 @@ interface ToolTipProps { requiredConfirmation?: boolean; } -const DefaultArrowSize = { width: 10, height: 6 }; - export const Tooltip = ({ isVisible, anchor = 'center', - tooltipStyle = { - width: Dimensions.get('window').width * 0.3, - paddingVertical: 8, - paddingHorizontal: 12, + styles = { + tooltipStyle: TOOLTIP_STYLE, + arrowSize: ARROW_SIZE, + closeSize: CLOSE_ICON_SIZE, }, - containerStyle, text, children, placement, onPress, - color, - colorType = 'primary', offset, - closeSize, - arrowSize = DefaultArrowSize, arrowElement, onVisibleChange, disableAutoHide = false, @@ -91,15 +92,15 @@ export const Tooltip = ({ const isVerticalPlacement = placement === 'top' || placement === 'bottom'; const tooltipColor = (() => { - if (color) return color; - if (colorType === 'black') return 'black'; + if (styles?.color) return styles?.color; return '#3Eb489'; })(); + const arrowStyle = { ...createArrowShape( - arrowSize?.width || DefaultArrowSize.width, - arrowSize?.height || DefaultArrowSize.height + styles?.arrowSize?.width || ARROW_SIZE.width, + styles?.arrowSize?.height || ARROW_SIZE.height ), borderBottomColor: tooltipColor, }; @@ -163,7 +164,7 @@ export const Tooltip = ({ return 'center'; })(), }, - containerStyle, + styles?.containerStyle, ]} > @@ -333,7 +334,7 @@ export const Tooltip = ({ { position: 'absolute', opacity: animatedValue, - width: tooltipStyle?.width, + width: styles?.tooltipStyle?.width, }, transformsStyle, ]} From 37e76be8dde94635784fc4175f3c169730f273c3 Mon Sep 17 00:00:00 2001 From: WayneKim92 Date: Sun, 25 Aug 2024 20:34:34 +0900 Subject: [PATCH 09/10] docs: update README.md --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++----- example/src/App.tsx | 2 +- src/index.tsx | 4 +++- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3ae1ca2..30a72c8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# react-native-good-tooltip +# React Native Good Tooltip -This tooltip was created with the best UX in mind. +Tooltips do not interrupt the user's flow. I'm not positive about the flow of using the app after touching the tooltip to close it. +So this component doesn't use Modal. ## Installation @@ -10,15 +11,57 @@ npm install react-native-good-tooltip ## Usage +**⚠️Warning⚠️️** +This component will need to be used with styles (z-Index and Overflow). +You will immediately see the need for zIndex if you use "bottom" placement. +Please refer to the example project and video. + +```tsx +import Tooltip from 'react-native-good-tooltip'; -```js -import { multiply } from 'react-native-good-tooltip'; // ... -const result = await multiply(3, 7); + + {/* your component */} + ``` +## Video + + + + +
+
+ +## Props + + +## Props +| Prop | Type | Default | Description | +|------------------------|----------------------------------------------------------------------|----------------------------------------|-----------------------------------------------------------------------------------------------| +| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | **required** | The position of the tooltip relative to the anchor. | +| `anchor` | `'center' \| 'left' \| 'right' \| 'top' \| 'bottom'` | `'center'` | The alignment of the tooltip relative to the anchor. | +| `text` | `string \| React.ReactElement` | **required** | The content of the tooltip. | +| `isVisible` | `boolean` | **required** | Whether the tooltip is visible. | +| `offset` | `{ position?: { x?: number, y?: number }, arrow?: { x?: number, y?: number } }` | `undefined` | The offset for the tooltip and arrow position. | +| `arrowElement` | `React.ReactElement` | `undefined` | Custom arrow element. | +| `styles` | `{color?: ColorValue,containerStyle?: ViewStyle,tooltipStyle?: ViewStyle,arrowSize?: { width?: number, height?: number },closeSize?: { width?: number, height?: number} }` | `undefined` | Custom styles for the tooltip. | +| `children` | `React.ReactElement` | `undefined` | The element to which the tooltip is anchored. | +| `onPress` | `() => void` | `undefined` | Function to call when the tooltip is pressed. | +| `onVisibleChange` | `(isVisible: boolean) => void` | `undefined` | Function to call when the visibility of the tooltip changes. | +| `disableAutoHide` | `boolean` | `false` | Whether to disable the auto-hide feature. | +| `delayShowTime` | `number` | `0` | The delay time before showing the tooltip. | +| `autoHideTime` | `number` | `5000` | The time after which the tooltip will automatically hide. | +| `requiredConfirmation` | `boolean` | `false` | Whether the tooltip requires confirmation to hide. | ## Contributing diff --git a/example/src/App.tsx b/example/src/App.tsx index e552feb..73244aa 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,5 +1,5 @@ import { FlatList, SafeAreaView, StyleSheet, Text, View } from 'react-native'; -import { Tooltip } from 'react-native-good-tooltip'; +import Tooltip from 'react-native-good-tooltip'; export default function App() { const data = [ diff --git a/src/index.tsx b/src/index.tsx index 00ff9d0..b74e165 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -62,7 +62,7 @@ interface ToolTipProps { requiredConfirmation?: boolean; } -export const Tooltip = ({ +const Tooltip = ({ isVisible, anchor = 'center', styles = { @@ -393,3 +393,5 @@ export const Tooltip = ({
); }; + +export default Tooltip; From 6b0fabbc28f1c6303be83f2cfd65a6e867330526 Mon Sep 17 00:00:00 2001 From: WayneKim92 Date: Sun, 25 Aug 2024 20:53:16 +0900 Subject: [PATCH 10/10] =?UTF-8?q?chore:=20=ED=83=80=EC=9E=85=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=AC=B4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + patches/react-native-anchor-point+1.0.6.patch | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 patches/react-native-anchor-point+1.0.6.patch diff --git a/package.json b/package.json index f1b95b0..8f94fe7 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "scripts": { "example": "yarn workspace react-native-good-tooltip-example", "test": "jest", + "postinstall": "npx patch-package", "typecheck": "tsc", "lint": "eslint \"**/*.{js,ts,tsx}\"", "clean": "del-cli lib", diff --git a/patches/react-native-anchor-point+1.0.6.patch b/patches/react-native-anchor-point+1.0.6.patch new file mode 100644 index 0000000..fc086c3 --- /dev/null +++ b/patches/react-native-anchor-point+1.0.6.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/react-native-anchor-point/index.ts b/node_modules/react-native-anchor-point/index.ts +index f789fa7..a1a97c6 100644 +--- a/node_modules/react-native-anchor-point/index.ts ++++ b/node_modules/react-native-anchor-point/index.ts +@@ -33,8 +33,10 @@ export const withAnchorPoint = (transform: TransformsStyle, anchorPoint: Point, + shiftTranslateX.push({ + translateX: size.width * (anchorPoint.x - defaultAnchorPoint.x), + }); ++ // @ts-ignore + injectedTransform = [...shiftTranslateX, ...injectedTransform]; + // shift after rotation ++ // @ts-ignore + injectedTransform.push({ + translateX: size.width * (defaultAnchorPoint.x - anchorPoint.x), + });