diff --git a/README.md b/README.md index bde988e..63d1aec 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ - [Advanced Usage](#advanced-usage) - [Fast Moving Objects](#fast-moving-objects) - [Modal Anchoring](#modal-anchoring) + - [Component Visibility](#component-visibility) - [Style Overrides](#style-overrides) - [Overriding With Custom Components](#overriding-with-custom-components) @@ -151,6 +152,43 @@ Want to highlight an element while anchoring the modal to its parent? No problem ``` +### Component Visibility + +You can force components to be hidden on a per step or per tour basis. + +The visibility prop on a step takes precedence over the hidden prop on the `ReactGrandTour` component. + +```jsx +export type ComponentVisibility = { + hideCloseButton?: boolean; + hideCurrentStepLabel?: boolean; + hideNextStepButton?: boolean; + hidePreviousStepButton?: boolean; + hideStepButtons?: boolean; +} + + + + +``` + ### Style Overrides To achieve the look you desire you don't have to override the CSS classes or provide custom components. You can also override a lot of colors using the `stylingOverrides` prop. diff --git a/playground/paths.json b/playground/paths.json index 2a02507..f602c71 100644 --- a/playground/paths.json +++ b/playground/paths.json @@ -2,7 +2,7 @@ "compilerOptions": { "baseUrl": "../", "paths": { - "react-grand-tour": ["./typings"], + "react-grand-tour": ["./dist"], "pages": ["./playground/src/pages"], "components": ["./playground/src/components"], "components/*": ["./playground/src/components/*"], diff --git a/playground/src/App.tsx b/playground/src/App.tsx index d612c09..c01aa7f 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -5,6 +5,7 @@ import { Routes } from 'routes'; import Home from './pages/Home'; import CustomExample from './pages/CustomExample'; import GitHubBtn from './components/GitHubBtn'; +import HiddenComponents from './pages/HiddenComponents'; const App = () => ( @@ -31,6 +32,9 @@ const App = () => ( + + + diff --git a/playground/src/pages/HiddenComponents.tsx b/playground/src/pages/HiddenComponents.tsx new file mode 100644 index 0000000..f917ec5 --- /dev/null +++ b/playground/src/pages/HiddenComponents.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Box } from '@material-ui/core'; +import { ReactGrandTour } from 'react-grand-tour'; +import KitchenSink from 'components/KitchenSink'; +import Hero from 'components/Hero'; +import { HiddenComponentsTour } from 'tours'; + +const HiddenComponents = () => ( + + + + + + + +); + +export default HiddenComponents; diff --git a/playground/src/pages/index.ts b/playground/src/pages/index.ts index 8794c86..d391109 100644 --- a/playground/src/pages/index.ts +++ b/playground/src/pages/index.ts @@ -1,2 +1,3 @@ export { default as Home } from './Home'; export { default as CustomExample } from './CustomExample'; +export { default as HiddenComponents } from './HiddenComponents'; diff --git a/playground/src/routes.ts b/playground/src/routes.ts index 5d50689..b1a8749 100644 --- a/playground/src/routes.ts +++ b/playground/src/routes.ts @@ -1,4 +1,5 @@ export const Routes = { home: '/ReactGrandTour', custom: '/ReactGrandTour/custom', + hiddenComponents: '/ReactGrandTour/hidden', }; diff --git a/playground/src/tours/Home.tsx b/playground/src/tours/Home.tsx index cfc025e..c11d67d 100644 --- a/playground/src/tours/Home.tsx +++ b/playground/src/tours/Home.tsx @@ -155,3 +155,107 @@ export const HomeSteps: ReactGrandTourStep[] = [ component: Bye, }, ]; + +export const HiddenComponentsTour: ReactGrandTourStep[] = [ + // { + // selector: `#${HomeStepIds.one}`, + // content: 'Welcome to React Grand Tour!', + // component: Hello, + // }, + { + selector: `#middle`, + content: + "Welcome to React Grand Tour! I'm hiding the current step label on this step. This is an example of how to show the backdrop instead of focusing on an element.", + preferredModalPosition: 'auto', + hideStepElementHighlight: true, + }, + { + selector: `#${HomeStepIds.two}`, + content: + "And this is a cool logo. Try scrolling down the page. You'll see an arrow appear. You can click anywhere in this modal to be taken back to the highlighted area.\n\n I'm hiding the next & previous step buttons, but you can still use the arrow keys.", + hideNextStepButton: true, + hidePreviousStepButton: true, + }, + { + selector: `#${HomeStepIds.three}`, + content: + "The highlighted area will track moving objects. I'm hiding the step buttons here for fun", + track: true, + preferredModalPosition: 'right', + hideStepButtons: true, + }, + { + selector: `#${HomeStepIds.four}`, + anchorSelector: `#${HomeStepIds.fourContainer}`, + content: + 'The highlighted area will track moving objects, but the modal stays anchored to its container.', + track: true, + }, + { + selector: `#${HomeStepIds.verticalOne}`, + content: 'The highlighted area will track the size of objects which change shape.', + track: true, + }, + { + selector: `#${HomeStepIds.horizontalOne}`, + content: + "The highlighted area will track the size of objects which change shape. Even when it's their width!", + track: true, + }, + { + selector: `#${HomeStepIds.horVertOne}`, + content: 'And when they change in both directions.', + track: true, + }, + { + selector: `#${HomeStepIds.verticalTwo}`, + anchorSelector: `#${HomeStepIds.expandCollapseContainerTwo}`, + content: 'The highlighted area will track the size of objects which change shape.', + track: true, + }, + { + selector: `#${HomeStepIds.horizontalTwo}`, + anchorSelector: `#${HomeStepIds.expandCollapseContainerTwo}`, + content: + "The highlighted area will track the size of objects which change shape. Even when it's their width!", + track: true, + }, + { + selector: `#${HomeStepIds.horVertTwo}`, + anchorSelector: `#${HomeStepIds.expandCollapseContainerTwo}`, + content: 'And when they change in both directions.', + track: true, + }, + { + selector: `#row-1-col-1`, + content: 'I can even handle really wide scrolling content.', + }, + { + selector: `#row-4-col-20`, + content: 'Just like that 😏', + }, + { + selector: `#row-1-col-1`, + content: 'And that 😱', + }, + { + selector: `#${HomeStepIds.prefRight}`, + content: 'I prefer to position this modal on the right', + preferredModalPosition: 'right', + }, + { + selector: `#${HomeStepIds.prefLeft}`, + content: "I prefer to position this modal on the left, but there isn't enough space 😞", + preferredModalPosition: 'left', + }, + { + selector: `#${HomeStepIds.prefTop}`, + content: 'I prefer to position this modal on top', + preferredModalPosition: 'top', + }, + { + selector: `#${HomeStepIds.bye}`, + content: '', + component: Bye, + }, +]; diff --git a/src/ReactGrandTour.tsx b/src/ReactGrandTour.tsx index 80417e8..7e398ea 100644 --- a/src/ReactGrandTour.tsx +++ b/src/ReactGrandTour.tsx @@ -2,6 +2,7 @@ import React, { useMemo, useCallback, useState, PropsWithChildren, useEffect } f import styles from './styles'; import { ComponentOverrides, + ComponentVisibility, ReactGrandTourCloseReason, ReactGrandTourProps, ReactGrandTourStep, @@ -28,6 +29,9 @@ const defaultShortcuts = { prevStep: ['ArrowLeft'], }; +const emptyStyles = {}; +const emptyVisibility = {}; + const ReactGrandTour: React.FC = ({ children, open: defaultOpen = false, @@ -49,8 +53,13 @@ const ReactGrandTour: React.FC = ({ disableCloseOnEscape = false, disableCloseBtn = false, disableCloseOnBackdropClick = false, - stylingOverrides = {}, + stylingOverrides = emptyStyles, keyboardShortcuts, + hideCloseButton, + hideCurrentStepLabel, + hideNextStepButton, + hidePreviousStepButton, + hideStepButtons, }) => { const [open, setOpen] = useState(defaultOpen); const [currentIndex, setCurrentIndex] = useState(openAt); @@ -167,6 +176,7 @@ const ReactGrandTour: React.FC = ({ }; }, [onKeyUp]); + console.log(steps[currentIndex]); return ( = ({ {' '}
(
{arrow} - {currentStepLabel} - {closeButton} + {!hideCurrentStepLabel && currentStepLabel} + {!hideCloseButton && closeButton} {content}
- {previousStepButton} - {stepButtonWrapper} - {nextStepButton} + {!hidePreviousStepButton && previousStepButton} + {!hideStepButtons && stepButtonWrapper} + {!hideNextStepButton && nextStepButton}
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a4dbd2e..d9c3305 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,19 +1,25 @@ import React, { useMemo } from 'react'; import FadeIn from './FadeIn'; -import { ArrowDirection, ComponentOverrides, ReactGrandTourProps } from '../types'; +import { + ArrowDirection, + ComponentOverrides, + ComponentVisibility, + ReactGrandTourProps, +} from '../types'; import CloseButton from './CloseButton'; import { isSafari } from '../lib'; -export type ModalProps = Partial & { - arrowDirection: ArrowDirection; - stepIndex: number; - changeStep: (index: number) => void; - allSteps: number[]; - close: ReactGrandTourProps['onClose']; - renderedContent: any; - scrollToElement: () => void; - track: boolean; -}; +export type ModalProps = Partial & + ComponentVisibility & { + arrowDirection: ArrowDirection; + stepIndex: number; + changeStep: (index: number) => void; + allSteps: number[]; + close: ReactGrandTourProps['onClose']; + renderedContent: any; + scrollToElement: () => void; + track: boolean; + }; const Modal = ({ arrowDirection, @@ -32,6 +38,12 @@ const Modal = ({ stepButtonWrapper: StepButtonWrapper, stepButton: StepButton, track, + + hideCloseButton, + hideCurrentStepLabel, + hideNextStepButton, + hidePreviousStepButton, + hideStepButtons, }: ModalProps) => { const arrow = useMemo(() => , [Arrow, arrowDirection]); const currentStepLabel = useMemo( @@ -45,9 +57,10 @@ const Modal = ({ goNext={() => changeStep(stepIndex + 1)} skipTo={changeStep} totalSteps={allSteps.length} + close={close} /> ), - [stepIndex, changeStep, allSteps.length, NextStepButton], + [stepIndex, changeStep, allSteps.length, NextStepButton, close], ); const previousStepButton = useMemo( () => ( @@ -56,9 +69,10 @@ const Modal = ({ goBack={() => changeStep(stepIndex - 1)} skipTo={changeStep} totalSteps={allSteps.length} + close={close} /> ), - [stepIndex, changeStep, allSteps.length, PreviousStepButton], + [stepIndex, changeStep, allSteps.length, PreviousStepButton, close], ); const stepButtonWrapper = useMemo( () => ( @@ -114,6 +128,12 @@ const Modal = ({ stepButtonWrapper, stepIndex, stepButtonComponent: StepButton, + + hideCloseButton, + hideCurrentStepLabel, + hideNextStepButton, + hidePreviousStepButton, + hideStepButtons, }} />
diff --git a/src/components/Step/Step.tsx b/src/components/Step/Step.tsx index f5d5f2d..d82cc43 100644 --- a/src/components/Step/Step.tsx +++ b/src/components/Step/Step.tsx @@ -1,12 +1,18 @@ import React, { useMemo, useState, useEffect } from 'react'; import { useCallback } from 'react'; -import { ComponentOverrides, ReactGrandTourProps, ReactGrandTourStep } from '../../types'; +import { + ComponentOverrides, + ComponentVisibility, + ReactGrandTourProps, + ReactGrandTourStep, +} from '../../types'; import Highlight from '../Highlight'; import Modal from '../Modal'; import { getArrowDirection } from '../../lib'; import StepPositioner from './StepPostioner'; type Props = Partial & + ComponentVisibility & ComponentOverrides & { stepIndex: number; changeStep: (index: number) => void; @@ -36,6 +42,14 @@ const Step = React.memo( track = false, trackFrequency = 10, preferredModalPosition = 'auto', + + hideStepElementHighlight = false, + + hideCloseButton, + hideCurrentStepLabel, + hideNextStepButton, + hidePreviousStepButton, + hideStepButtons, }: Omit & { element: Element; anchorElement?: Element; @@ -85,6 +99,7 @@ const Step = React.memo( anchorBoundaries={anchorBoundaries} differentAnchor={anchorElement != null} preferredPosition={preferredModalPosition} + hideStepElementHighlight={hideStepElementHighlight} /> diff --git a/src/components/Step/StepPostioner.tsx b/src/components/Step/StepPostioner.tsx index aad9a45..5d1fca6 100644 --- a/src/components/Step/StepPostioner.tsx +++ b/src/components/Step/StepPostioner.tsx @@ -9,7 +9,14 @@ const StepPositioner: React.FC<{ anchorBoundaries: DOMRect; differentAnchor: boolean; preferredPosition: ReactGrandTourStep['preferredModalPosition']; -}> = ({ boundaries, anchorBoundaries, differentAnchor, preferredPosition }) => { + hideStepElementHighlight: boolean; +}> = ({ + boundaries, + anchorBoundaries, + differentAnchor, + preferredPosition, + hideStepElementHighlight, +}) => { const modalContainer = document.querySelector('.__react-grand-tour__modal-container'); const modalPos = getModalPosition( anchorBoundaries, @@ -42,8 +49,8 @@ const StepPositioner: React.FC<{ }, highlight: { transform: `translate(${boundaries.left - 10}px, ${boundaries.top - 10}px)`, - height: `${boundaries.height + 20}px`, - width: `${boundaries.width + 20}px`, + height: `${!hideStepElementHighlight ? boundaries.height + 20 : 0}px`, + width: `${!hideStepElementHighlight ? boundaries.width + 20 : 0}px`, 'box-shadow': `0 0 0 calc(200vh + 200vw) rgba(0, 0, 0, ${ differentAnchor ? '.58' : '.7' })`, diff --git a/src/styles.ts b/src/styles.ts index 8af4d17..b6ce7f7 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -20,8 +20,9 @@ const styles = ({ animationSpeed = 0.5, primaryColor = '#f2c14e', dotBackgroundColor = 'inherit', - dotBorderColor = '#757575', + dotBorder = '1px solid #757575', dotHoverBackgroundColor = '#757575', + dotHoverBorder = '', chevronButtonColor = '#757575', chevronButtonHoverColor = '#212121', chevronButtonDisabledColor = '#e0e0e0', @@ -67,13 +68,14 @@ const styles = ({ width: '10px', height: '10px', 'background-color': dotBackgroundColor, - border: `1px solid ${dotBorderColor}`, + border: dotBorder, 'border-radius': '50%', 'margin-right': '7px', overflow: 'hidden', }, ':hover div': { 'background-color': dotHoverBackgroundColor, + border: dotHoverBorder, }, '-selected': { ' div': { diff --git a/src/types.ts b/src/types.ts index 84e56e5..ce23289 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,9 @@ import React, { PropsWithChildren, ReactNode } from 'react'; -export type ReactGrandTourProps = { +export type ReactGrandTourProps = ComponentVisibility & { open?: boolean; onOpen?: () => void; - onClose?: (reason: ReactGrandTourCloseReason) => void; + onClose?: (reason?: ReactGrandTourCloseReason) => void; onStepChange?: (props: OnStepChangeProps) => void; openAt?: number; steps?: ReactGrandTourStep[]; @@ -16,13 +16,22 @@ export type ReactGrandTourProps = { keyboardShortcuts?: ReactGrandTourShortcuts; }; +export type ComponentVisibility = { + hideCloseButton?: boolean; + hideCurrentStepLabel?: boolean; + hideNextStepButton?: boolean; + hidePreviousStepButton?: boolean; + hideStepButtons?: boolean; +}; + export type ReactGrandTourStylingOverrides = { primaryColor?: string; animationSpeed?: number; dotBackgroundColor?: string; - dotBorderColor?: string; + dotBorder?: string; dotHoverBackgroundColor?: string; + dotHoverBorder?: string; chevronButtonColor?: string; chevronButtonHoverColor?: string; @@ -103,7 +112,7 @@ export type ReactGrandTourContextType = { goBack: () => void; }; -export type ReactGrandTourStep = { +export type ReactGrandTourStep = ComponentVisibility & { content: ReactNode; /** * Use the `component` prop if you want to customise the content of the step. @@ -155,6 +164,11 @@ export type ReactGrandTourStep = { * Default: auto */ preferredModalPosition?: 'auto' | 'top' | 'right' | 'bottom' | 'left'; + + /** + * Hide the see through highlight box which focuses on the current step element + */ + hideStepElementHighlight?: boolean; }; export type ModalPosition = { @@ -175,12 +189,14 @@ export type NextStepButtonProps = { totalSteps: number; goNext: () => void; skipTo: (step: number) => void; + close: () => void; }; export type PreviousStepButtonProps = { currentStep: number; totalSteps: number; goBack: () => void; skipTo: (step: number) => void; + close: () => void; }; export type StepButtonProps = { currentStep: number; @@ -197,7 +213,7 @@ export type StepButtonWrapperProps = { skipTo: (step: number) => void; }; export type ArrowProps = { direction: ArrowDirection }; -export type DialogWrapperProps = { +export type DialogWrapperProps = ComponentVisibility & { [key in keyof Omit< ComponentOverrides, 'dialogWrapper' | 'stepButton' | 'contentWrapper'