From 8d1697d78f2652efa3b8efa830a7c41f9cdd8222 Mon Sep 17 00:00:00 2001 From: wuyujing Date: Tue, 16 Jul 2024 16:01:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20CopilotStep=20suppor?= =?UTF-8?q?t=20edge=20to=20custom=20rect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CopilotStep.tsx | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/components/CopilotStep.tsx b/src/components/CopilotStep.tsx index e0cdb588..27d83e7c 100644 --- a/src/components/CopilotStep.tsx +++ b/src/components/CopilotStep.tsx @@ -9,6 +9,12 @@ interface Props { text: string; children: React.ReactElement; active?: boolean; + edge?: { + top?: number; + left?: number; + right?: number; + bottom?: number; + }; } export const CopilotStep = ({ @@ -17,6 +23,12 @@ export const CopilotStep = ({ text, children, active = true, + edge = { + top: 0, + left: 0, + right: 0, + bottom: 0, + }, }: Props) => { const registeredName = useRef(null); const { registerStep, unregisterStep } = useCopilot(); @@ -33,11 +45,17 @@ export const CopilotStep = ({ // Wait until the wrapper element appears if (wrapperRef.current != null && "measure" in wrapperRef.current) { wrapperRef.current.measure((_ox, _oy, width, height, x, y) => { + const safeEdge = { + top: edge?.top ?? 0, + left: edge?.left ?? 0, + right: edge?.right ?? 0, + bottom: edge?.bottom ?? 0, + }; resolve({ - x, - y, - width, - height, + x: x - safeEdge.left, + y: y - safeEdge.top, + width: width + safeEdge.left + safeEdge.right, + height: height + safeEdge.top + safeEdge.bottom, }); }); } else { @@ -81,7 +99,7 @@ export const CopilotStep = ({ ref: wrapperRef, onLayout: () => {}, // Android hack }), - [] + [], ); return React.cloneElement(children, { copilot: copilotProps }); From ae0aae2e50da224ddd523ace33e367e962c21d31 Mon Sep 17 00:00:00 2001 From: wuyujing Date: Tue, 16 Jul 2024 15:59:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20custom=20style=20in?= =?UTF-8?q?=20CopilotProvider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/style.ts | 24 ++++++++++++++++++++---- src/contexts/CopilotProvider.tsx | 26 +++++++++++++------------- src/types.ts | 8 +++++--- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/components/style.ts b/src/components/style.ts index 9eab1ade..a5738869 100644 --- a/src/components/style.ts +++ b/src/components/style.ts @@ -1,5 +1,3 @@ -import { StyleSheet } from "react-native"; - export const STEP_NUMBER_RADIUS: number = 14; export const STEP_NUMBER_DIAMETER: number = STEP_NUMBER_RADIUS * 2; export const ZINDEX: number = 100; @@ -7,7 +5,7 @@ export const MARGIN: number = 13; export const OFFSET_WIDTH: number = 4; export const ARROW_SIZE: number = 6; -export const styles = StyleSheet.create({ +let styles = { container: { position: "absolute", left: 0, @@ -80,4 +78,22 @@ export const styles = StyleSheet.create({ bottom: 0, right: 0, }, -}); +}; + +const RNCSetStyle = (stylesToUpdate: { + [K in keyof typeof styles]?: Partial<(typeof styles)[K]>; +}) => { + styles = { + ...styles, + ...Object.keys(stylesToUpdate).reduce((acc, key) => { + const styleKey = key as keyof typeof styles; + acc[styleKey] = { + ...styles[styleKey], + ...stylesToUpdate[styleKey], + }; + return acc; + }, {}), + }; +}; + +export { styles, RNCSetStyle }; diff --git a/src/contexts/CopilotProvider.tsx b/src/contexts/CopilotProvider.tsx index 331c2735..ee30d74d 100644 --- a/src/contexts/CopilotProvider.tsx +++ b/src/contexts/CopilotProvider.tsx @@ -13,7 +13,7 @@ import { CopilotModal, type CopilotModalHandle, } from "../components/CopilotModal"; -import { OFFSET_WIDTH } from "../components/style"; +import { OFFSET_WIDTH, RNCSetStyle } from "../components/style"; import { useStateWithAwait } from "../hooks/useStateWithAwait"; import { useStepsMap } from "../hooks/useStepsMap"; import { type CopilotOptions, type Step } from "../types"; @@ -31,7 +31,7 @@ interface CopilotContextType { currentStep: Step | undefined; start: ( fromStep?: string, - suppliedScrollView?: ScrollView | null + suppliedScrollView?: ScrollView | null, ) => Promise; stop: () => Promise; goToNext: () => Promise; @@ -56,6 +56,7 @@ const CopilotContext = createContext(undefined); export const CopilotProvider = ({ verticalOffset = 0, children, + style = {}, ...rest }: PropsWithChildren) => { const startTries = useRef(0); @@ -65,6 +66,8 @@ export const CopilotProvider = ({ const [visible, setVisibility] = useStateWithAwait(false); const [scrollView, setScrollView] = useState(null); + RNCSetStyle(style); + const { currentStep, currentStepNumber, @@ -96,7 +99,7 @@ export const CopilotProvider = ({ y: size.y - OFFSET_WIDTH / 2 + verticalOffset, }); }, - [verticalOffset] + [verticalOffset], ); const setCurrentStep = useCallback( @@ -112,7 +115,7 @@ export const CopilotProvider = ({ (_x, y, _w, h) => { const yOffset = y > 0 ? y - h / 2 : 0; scrollView.scrollTo({ y: yOffset, animated: false }); - } + }, ); } } @@ -123,10 +126,10 @@ export const CopilotProvider = ({ void moveModalToStep(step); } }, - scrollView != null ? 100 : 0 + scrollView != null ? 100 : 0, ); }, - [copilotEvents, moveModalToStep, scrollView, setCurrentStepState] + [copilotEvents, moveModalToStep, scrollView, setCurrentStepState], ); const start = useCallback( @@ -163,7 +166,7 @@ export const CopilotProvider = ({ setCurrentStep, setVisibility, steps, - ] + ], ); const stop = useCallback(async () => { @@ -179,7 +182,7 @@ export const CopilotProvider = ({ async (n: number) => { await setCurrentStep(getNthStep(n)); }, - [getNthStep, setCurrentStep] + [getNthStep, setCurrentStep], ); const prev = useCallback(async () => { @@ -218,16 +221,13 @@ export const CopilotProvider = ({ isLastStep, currentStepNumber, totalStepsNumber, - ] + ], ); return ( <> - + {children} diff --git a/src/types.ts b/src/types.ts index 9ba8c31f..aed786bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ import type { NativeMethods, ViewStyle, } from "react-native"; +import type { styles } from "./components/style"; export type WalktroughedComponent = NativeMethods & React.ComponentType; @@ -69,15 +70,16 @@ export interface CopilotOptions { animationDuration?: number; tooltipComponent?: React.ComponentType; tooltipStyle?: ViewStyle; - stepNumberComponent?: React.ComponentType; + stepNumberComponent?: React.ComponentType; animated?: boolean; labels?: Labels; androidStatusBarVisible?: boolean; svgMaskPath?: SvgMaskPathFunction; verticalOffset?: number; arrowColor?: string; - arrowSize?: number - margin?: number + arrowSize?: number; + margin?: number; stopOnOutsideClick?: boolean; backdropColor?: string; + style?: { [K in keyof typeof styles]?: Partial<(typeof styles)[K]> }; }