diff --git a/example/package.json b/example/package.json index 983ed7a..f6e60be 100644 --- a/example/package.json +++ b/example/package.json @@ -15,6 +15,8 @@ "react": "18.3.1", "react-dom": "18.3.1", "react-native": "0.76.6", + "react-native-reanimated": "^3.16.7", + "react-native-svg": "^15.11.1", "react-native-web": "~0.19.13" }, "devDependencies": { diff --git a/example/src/App.tsx b/example/src/App.tsx index 6403632..17c9b0c 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,12 +1,65 @@ -import { Text, View, StyleSheet } from 'react-native'; -import { multiply } from 'react-native-tipkit'; - -const result = multiply({ n1: 3, n2: 7 }); +import { View, StyleSheet, Pressable, Text } from 'react-native'; +import { TipKitInlineView, TipKitPopOverView } from 'react-native-tipkit'; +import CloseIcon from './CloseIcon'; export default function App() { + const onActionButtonPress = () => { + console.log('Action button pressed'); + }; + return ( - Result: {result} + TipKit Example + + + } + actionButtonOnPress={onActionButtonPress} + actionButtonTitle="Learn more" + // Popover Button Props + popoverButtonProps={{ title: 'Show Popover Top' }} + popoverButtonArrowDirection="top" + /> + + } + actionButtonOnPress={onActionButtonPress} + // Popover Button Props + popoverButtonProps={{ title: 'Show Popover Bottom' }} + popoverButtonArrowDirection="bottom" + /> + + {/* TODO: Fix the positioning of the arrow when the button is smaller than 100% */} + } + actionButtonOnPress={onActionButtonPress} + popoverButtonArrowDirection="bottom-end" + // Popover Button Props + popoverButton={ + {}}> + + Custom Popover button + + + } + /> ); } @@ -14,7 +67,31 @@ export default function App() { const styles = StyleSheet.create({ container: { flex: 1, - alignItems: 'center', + gap: 12, justifyContent: 'center', + paddingHorizontal: 12, + }, + inline: { + backgroundColor: '#d7eef3', + }, + tipContainer: { + backgroundColor: '#f1f4f2', + }, + customPopoverButton: { + width: '60%', + backgroundColor: '#66D210', + padding: 12, + borderRadius: 8, + }, + customPopoverButtonText: { + color: 'white', + textAlign: 'center', + fontWeight: 'bold', + }, + title: { + fontSize: 24, + fontWeight: 'bold', + textAlign: 'center', + color: '#333', }, }); diff --git a/example/src/CloseIcon.tsx b/example/src/CloseIcon.tsx new file mode 100644 index 0000000..1f160d7 --- /dev/null +++ b/example/src/CloseIcon.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import Svg, { Path, type SvgProps } from 'react-native-svg'; + +type CloseIconProps = SvgProps; + +const CloseIcon: React.FC = ({ ...props }) => { + return ( + + + + ); +}; + +export default CloseIcon; diff --git a/package.json b/package.json index aaa8f66..a4df950 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,8 @@ "react": "18.3.1", "react-native": "0.76.6", "react-native-builder-bob": "^0.32.0", + "react-native-reanimated": "^3.16.7", + "react-native-svg": "^15.11.1", "release-it": "^17.10.0", "typescript": "^5.2.2" }, @@ -88,7 +90,9 @@ }, "peerDependencies": { "react": "*", - "react-native": "*" + "react-native": "*", + "react-native-reanimated": "*", + "react-native-svg": "*" }, "workspaces": [ "example" @@ -196,8 +200,5 @@ "languages": "js", "type": "library", "version": "0.45.5" - }, - "dependencies": { - "react-native-reanimated": "^3.16.7" } } diff --git a/src/TipKitInlineView/TipKitInlineView.tsx b/src/TipKitInlineView/TipKitInlineView.tsx new file mode 100644 index 0000000..bf11bdc --- /dev/null +++ b/src/TipKitInlineView/TipKitInlineView.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import BaseTipKit, { type BaseTipKitProps } from '../components/BaseTipKit'; + +interface TipKitInlineViewProps extends BaseTipKitProps {} + +const TipKitInlineView: React.FC = ({ ...rest }) => { + return ; +}; + +export default TipKitInlineView; diff --git a/src/TipKitInlineView/index.tsx b/src/TipKitInlineView/index.tsx new file mode 100644 index 0000000..04f47f7 --- /dev/null +++ b/src/TipKitInlineView/index.tsx @@ -0,0 +1,2 @@ +import TipKitInlineView from './TipKitInlineView'; +export { TipKitInlineView }; diff --git a/src/TipKitPopOverView/TipKitPopOverView.tsx b/src/TipKitPopOverView/TipKitPopOverView.tsx new file mode 100644 index 0000000..7616ac9 --- /dev/null +++ b/src/TipKitPopOverView/TipKitPopOverView.tsx @@ -0,0 +1,115 @@ +import React, { + Fragment, + useMemo, + useRef, + useState, + type JSXElementConstructor, + type ReactElement, +} from 'react'; +import BaseTipKit, { type BaseTipKitProps } from '../components/BaseTipKit'; +import { Button, StyleSheet, View, type ButtonProps } from 'react-native'; + +export type TipKitPopOverArrowDirection = + | 'top-start' + | 'top' + | 'top-end' + | 'bottom-start' + | 'bottom' + | 'bottom-end'; + +interface TipKitPopOverViewProps extends BaseTipKitProps { + popoverButtonArrowDirection?: TipKitPopOverArrowDirection; + popoverButton?: ReactElement>; + popoverButtonOnPress?: () => void; + popoverButtonTitle?: string; + popoverButtonProps?: ButtonProps; +} + +const TipKitPopOverView: React.FC = ({ + popoverButton, + popoverButtonOnPress, + popoverButtonProps, + popoverButtonArrowDirection = 'bottom', + ...rest +}) => { + const buttonRef = useRef(null); + + const [visible, setVisible] = useState(false); + const [buttonPosition, setButtonPosition] = useState({ + x: 0, + y: 0, + width: 0, + height: 0, + }); + + const measureButtonPosition = () => { + buttonRef.current?.measure((_fx, _fy, width, height, px, py) => { + setButtonPosition({ x: px, y: py, width, height }); + }); + }; + + const popoverStyle = useMemo(() => { + const { y, x, height } = buttonPosition; + const isTop = popoverButtonArrowDirection?.includes('top'); + + return { + top: y + (isTop ? -height * 2.5 : height * 1.5), + left: x, + }; + }, [buttonPosition, popoverButtonArrowDirection]); + + const onDismiss = () => { + setVisible(false); + }; + + const handlePopoverButtonPress = () => { + measureButtonPosition(); + popoverButtonOnPress?.(); + setVisible(true); + }; + + return ( + + + {popoverButton ? ( + React.cloneElement(popoverButton, { + onPress: () => { + handlePopoverButtonPress(); + popoverButton.props.onPress?.(); + }, + }) + ) : ( +