Skip to content

Commit

Permalink
feat: add tour
Browse files Browse the repository at this point in the history
  • Loading branch information
cschroeter committed Dec 29, 2024
1 parent 6b5a053 commit b039ce1
Show file tree
Hide file tree
Showing 30 changed files with 748 additions and 0 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions packages/react/.storybook/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
@import url("./styles/toast.css");
@import url("./styles/toggle-group.css");
@import url("./styles/tooltip.css");
@import url("./styles/tour.css");
@import url("./styles/tree-view.css");
130 changes: 130 additions & 0 deletions packages/react/.storybook/styles/tour.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
[data-scope="tour"][data-part="positioner"][data-type="floating"] {
position: absolute;
}

[data-scope="tour"][data-part="positioner"][data-type="floating"][data-placement*="bottom"] {
bottom: 24px;
}

[data-scope="tour"][data-part="positioner"][data-type="floating"][data-placement*="top"] {
top: 24px;
}

[data-scope="tour"][data-part="positioner"][data-type="floating"][data-placement*="end"] {
inset-inline-end: 24px;
}

[data-scope="tour"][data-part="positioner"][data-type="floating"][data-placement*="start"] {
inset-inline-start: 24px;
}

[data-scope="tour"][data-part="positioner"][data-type="dialog"] {
width: 100%;
position: fixed;
inset: 0;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
}

[data-scope="tour"][data-part="content"] {
--arrow-background: white;
--arrow-size: 10px;
background: white;
padding: 24px;
position: relative;
border-radius: 4px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 300px;
}

[data-scope="tour"][data-part="content"][data-type="dialog"] {
width: 500px;
background: lightblue;
}

[data-scope="tour"][data-part="content"][data-type="floating"] {
width: 500px;
background: rgb(15, 39, 136);
color: white;
}

[data-scope="tour"][data-part="arrow"] {
--arrow-background: white;
--arrow-shadow-color: #ebebeb;
box-shadow: var(--box-shadow);
}

[data-scope="tour"][data-part="title"] {
font-weight: 600;
}

[data-scope="tour"][data-part="description"] {
margin-bottom: 20px;
}

[data-scope="tour"][data-part="progress-text"] {
margin-bottom: 20px;
opacity: 0.72;
}

[data-scope="tour"][data-part="backdrop"] {
background-color: rgba(0, 0, 0, 0.5);
}

[data-scope="tour"][data-part="spotlight"] {
border: 3px solid pink;
}

[data-scope="tour"][data-part="close-trigger"] {
font-family: inherit;
height: 25px;
width: 25px;
display: inline-flex;
align-items: center;
justify-content: center;
position: absolute;
top: 10px;
right: 10px;
}

.tour.button__group {
display: flex;
align-items: flex-end;
gap: 10px;
}

.tour .steps__container {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 50vh;
}

.tour .overflow__container {
width: 500px;
height: 400px;
max-height: 200px;
overflow: auto;
border: 2px solid teal;
position: relative;
}

.tour .overflow__container::before {
content: "Overflow";
display: block;
position: sticky;
background-color: teal;
color: white;
padding: 2px 4px 3px;
top: 0px;
}

.tour .overflow__container .h-200px {
height: 200px;
}

.tour .overflow__container .h-100px {
height: 100px;
}
1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
"@zag-js/toast": "0.79.1",
"@zag-js/toggle-group": "0.79.1",
"@zag-js/tooltip": "0.79.1",
"@zag-js/tour": "0.79.1",
"@zag-js/tree-view": "0.79.1",
"@zag-js/types": "0.79.1"
},
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ export * from './toast'
export * from './toggle'
export * from './toggle-group'
export * from './tooltip'
export * from './tour'
export * from './tree-view'
29 changes: 29 additions & 0 deletions packages/react/src/components/tour/examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Frame } from '@ark-ui/react/frame'
import { DemoTour } from './tour'

export const Basic = () => {
return (
<main>
<DemoTour />
<div className="tour">
<div className="steps__container">
<h3 id="step-1">Step 1</h3>
<div className="overflow__container">
<div className="h-200px" />
<h3 id="step-2">Step 2</h3>
<div className="h-100px" />
</div>
<Frame>
<h1 id="step-2a">Iframe Content</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
</p>
</Frame>
<h3 id="step-3">Step 3</h3>
<h3 id="step-4">Step 4</h3>
</div>
</div>
</main>
)
}
90 changes: 90 additions & 0 deletions packages/react/src/components/tour/examples/steps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { TourStepDetails } from '@ark-ui/react/tour'

export const steps: TourStepDetails[] = [
{
type: 'dialog',
id: 'step-0',
title: 'Centered tour (no target)',
description: 'This is the center of the world. Ready to start the tour?',
actions: [{ label: 'Next', action: 'next' }],
},
{
type: 'tooltip',
id: 'step-1',
title: 'Step 1. Welcome',
description: 'To the new world',
target: () => document.querySelector<HTMLElement>('#step-1'),
actions: [
{ label: 'Prev', action: 'prev' },
{ label: 'Next', action: 'next' },
],
effect({ show, update }) {
const abort = new AbortController()

fetch('https://api.github.com/users/octocat', { signal: abort.signal })
.then((res) => res.json())
.then((data) => {
update({ title: data.name })
show()
})

return () => {
abort.abort()
}
},
},
{
type: 'tooltip',
id: 'step-2',
title: 'Step 2. Inside a scrollable container',
description: 'Using scrollIntoView(...) rocks!',
target: () => document.querySelector<HTMLElement>('#step-2'),
actions: [
{ label: 'Prev', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
type: 'tooltip',
id: 'step-2a',
title: 'Step 2a. Inside an Iframe container',
description: 'It calculates the offset rect correctly. Thanks to floating UI!',
target: () => {
const [frameEl] = Array.from(frames)
return frameEl?.document.querySelector<HTMLElement>('#step-2a')
},
actions: [
{ label: 'Prev', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
type: 'tooltip',
id: 'step-3',
title: 'Step 3. Normal scrolling',
description: 'The new world is a great place',
target: () => document.querySelector<HTMLElement>('#step-3'),
actions: [
{ label: 'Prev', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
type: 'tooltip',
id: 'step-4',
title: 'Step 4. Close to bottom',
description: 'So nice to see the scrolling works!',
target: () => document.querySelector<HTMLElement>('#step-4'),
actions: [
{ label: 'Prev', action: 'prev' },
{ label: 'Next', action: 'next' },
],
},
{
type: 'dialog',
id: 'step-5',
title: "You're all sorted! (no target)",
description: 'Thanks for trying out the tour. Enjoy the app!',
actions: [{ label: 'Finish', action: 'dismiss' }],
},
]
38 changes: 38 additions & 0 deletions packages/react/src/components/tour/examples/tour.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Tour, useTour } from '@ark-ui/react/tour'
import { XIcon } from 'lucide-react'
import { useEffect } from 'react'
import { steps } from './steps'

export const DemoTour = () => {
const tour = useTour({ steps })

// Start the tour when the component mounts
useEffect(() => {
tour.start()
}, [tour])

return (
<Tour.Root tour={tour}>
<Tour.Backdrop />
<Tour.Spotlight />
<Tour.Positioner>
<Tour.Content>
<Tour.Arrow>
<Tour.ArrowTip />
</Tour.Arrow>
<Tour.Title />
<Tour.Description />
<Tour.ProgressText />
<Tour.CloseTrigger>
<XIcon />
</Tour.CloseTrigger>
<Tour.Actions>
{(actions) =>
actions.map((action) => <Tour.ActionTrigger key={action.label} action={action} />)
}
</Tour.Actions>
</Tour.Content>
</Tour.Positioner>
</Tour.Root>
)
}
51 changes: 51 additions & 0 deletions packages/react/src/components/tour/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export type { StepDetails as TourStepDetails } from '@zag-js/tour'
export {
TourActionTrigger,
type TourActionTriggerBaseProps,
type TourActionTriggerProps,
} from './tour-action-trigger'
export {
TourActions,
type TourActionsProps,
} from './tour-actions'
export { TourArrow, type TourArrowBaseProps, type TourArrowProps } from './tour-arrow'
export {
TourArrowTip,
type TourArrowTipBaseProps,
type TourArrowTipProps,
} from './tour-arrow-tip'
export { TourBackdrop, type TourBackdropBaseProps, type TourBackdropProps } from './tour-backdrop'
export {
TourCloseTrigger,
type TourCloseTriggerBaseProps,
type TourCloseTriggerProps,
} from './tour-close-trigger'
export { TourContent, type TourContentBaseProps, type TourContentProps } from './tour-content'
export { TourContext, type TourContextProps } from './tour-context'
export {
TourDescription,
type TourDescriptionBaseProps,
type TourDescriptionProps,
} from './tour-description'
export {
TourPositioner,
type TourPositionerBaseProps,
type TourPositionerProps,
} from './tour-positioner'
export {
TourProgressText,
type TourProgressTextBaseProps,
type TourProgressTextProps,
} from './tour-progress-text'
export { TourRoot, type TourRootBaseProps, type TourRootProps } from './tour-root'
export {
TourSpotlight,
type TourSpotlightBaseProps,
type TourSpotlightProps,
} from './tour-spotlight'
export { TourTitle, type TourTitleBaseProps, type TourTitleProps } from './tour-title'
export { tourAnatomy } from './tour.anatomy'
export { useTour, type UseTourProps, type UseTourReturn } from './use-tour'
export { useTourContext, type UseTourContext } from './use-tour-context'

export * as Tour from './tour'
27 changes: 27 additions & 0 deletions packages/react/src/components/tour/tour-action-trigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { mergeProps } from '@zag-js/react'
import type { StepActionTriggerProps } from '@zag-js/tour'
import { forwardRef } from 'react'
import { createSplitProps } from '../../utils/create-split-props'
import { type HTMLProps, type PolymorphicProps, ark } from '../factory'
import { useTourContext } from './use-tour-context'

export interface TourActionTriggerBaseProps extends PolymorphicProps, StepActionTriggerProps {}
export interface TourActionTriggerProps extends HTMLProps<'button'>, TourActionTriggerBaseProps {}

export const TourActionTrigger = forwardRef<HTMLButtonElement, TourActionTriggerProps>(
(props, ref) => {
const [actionTriggerProps, localProps] = createSplitProps<StepActionTriggerProps>()(props, [
'action',
])
const tour = useTourContext()
const mergedProps = mergeProps(tour.getActionTriggerProps(actionTriggerProps), localProps)

return (
<ark.button {...mergedProps} ref={ref}>
{actionTriggerProps.action.label}
</ark.button>
)
},
)

TourActionTrigger.displayName = 'TourActionTrigger'
Loading

0 comments on commit b039ce1

Please sign in to comment.