From d98c3747aad23c516837632b697c664d808830f1 Mon Sep 17 00:00:00 2001 From: soykje Date: Fri, 1 Dec 2023 14:26:40 +0100 Subject: [PATCH] feat(progress-tracker): add base markup for progress-tracker --- package-lock.json | 1 + .../src/ProgressTracker.doc.mdx | 20 ++++++-- .../src/ProgressTracker.stories.tsx | 8 ++- .../progress-tracker/src/ProgressTracker.tsx | 50 +++++++++++++++++-- .../src/ProgressTrackerContext.ts | 16 ++++++ .../src/ProgressTrackerStep.styles.ts | 20 ++++++++ .../src/ProgressTrackerStep.tsx | 39 +++++++++++++++ .../components/progress-tracker/src/index.ts | 16 +++++- 8 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 packages/components/progress-tracker/src/ProgressTrackerContext.ts create mode 100644 packages/components/progress-tracker/src/ProgressTrackerStep.styles.ts create mode 100644 packages/components/progress-tracker/src/ProgressTrackerStep.tsx diff --git a/package-lock.json b/package-lock.json index 5e22f81967..d6370f0ca3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2402,6 +2402,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/packages/components/progress-tracker/src/ProgressTracker.doc.mdx b/packages/components/progress-tracker/src/ProgressTracker.doc.mdx index 441c232d42..f88f419892 100644 --- a/packages/components/progress-tracker/src/ProgressTracker.doc.mdx +++ b/packages/components/progress-tracker/src/ProgressTracker.doc.mdx @@ -1,5 +1,5 @@ import { Meta, Canvas } from '@storybook/addon-docs' -import { ArgTypes } from '@storybook/blocks'; +import { ArgTypes } from '@docs/helpers/ArgTypes' import { ProgressTracker } from '.' @@ -20,12 +20,24 @@ npm install @spark-ui/progress-tracker ## Import ```tsx -import { ProgressTracker } from "@spark-ui/progress-tracker" +import { ProgressTracker } from '@spark-ui/progress-tracker' ``` ## Props - + + +## Usage + +### Default -## Variants diff --git a/packages/components/progress-tracker/src/ProgressTracker.stories.tsx b/packages/components/progress-tracker/src/ProgressTracker.stories.tsx index 3e39309dff..bb499e19dd 100644 --- a/packages/components/progress-tracker/src/ProgressTracker.stories.tsx +++ b/packages/components/progress-tracker/src/ProgressTracker.stories.tsx @@ -9,4 +9,10 @@ const meta: Meta = { export default meta -export const Default: StoryFn = _args => Hello World! +export const Default: StoryFn = _args => ( + console.log('Clicked on', id)}> + + + + +) diff --git a/packages/components/progress-tracker/src/ProgressTracker.tsx b/packages/components/progress-tracker/src/ProgressTracker.tsx index e2e18ce1aa..4fcb85786d 100644 --- a/packages/components/progress-tracker/src/ProgressTracker.tsx +++ b/packages/components/progress-tracker/src/ProgressTracker.tsx @@ -1,9 +1,51 @@ -import { ComponentPropsWithoutRef, forwardRef, PropsWithChildren } from 'react' +import { type ComponentPropsWithoutRef, forwardRef, type PropsWithChildren, useState } from 'react' -export type ProgressTrackerProps = ComponentPropsWithoutRef<'div'> +import { ProgressTrackerContext } from './ProgressTrackerContext' + +export interface ProgressTrackerProps extends ComponentPropsWithoutRef<'div'> { + /** + * The index of the current step. + * @default 0 + */ + stepIndex?: number + /** + * Event handler called when clicking on a step. + */ + onStepClick?: (stepId: string) => void + /** + * Sets the component as interactive or not. + * @default false + */ + readOnly?: boolean +} export const ProgressTracker = forwardRef>( - (props, ref) => { - return
+ ( + { + stepIndex: propStepIndex = 0, + onStepClick: onStepClickProp, + readOnly = false, + children, + ...rest + }, + ref + ) => { + const [steps, setSteps] = useState>(new Set()) + const [stepIndex, setStepIndex] = useState(propStepIndex) + + const onStepClick = (stepId: string) => { + setStepIndex([...steps].indexOf(stepId)) + onStepClickProp?.(stepId) + } + + const Component = readOnly ? 'div' : 'nav' + + return ( + + +
    {children}
+
+
+ ) } ) diff --git a/packages/components/progress-tracker/src/ProgressTrackerContext.ts b/packages/components/progress-tracker/src/ProgressTrackerContext.ts new file mode 100644 index 0000000000..ab231a7356 --- /dev/null +++ b/packages/components/progress-tracker/src/ProgressTrackerContext.ts @@ -0,0 +1,16 @@ +import { createContext, type Dispatch, type SetStateAction, useContext } from 'react' + +import type { ProgressTrackerProps } from './ProgressTracker' + +export type ProgressTrackerContextInterface = Required< + Pick +> & { + steps: Set + setSteps: Dispatch>> +} + +export const ProgressTrackerContext = createContext( + {} as ProgressTrackerContextInterface +) + +export const useProgressTrackerContext = () => useContext(ProgressTrackerContext) diff --git a/packages/components/progress-tracker/src/ProgressTrackerStep.styles.ts b/packages/components/progress-tracker/src/ProgressTrackerStep.styles.ts new file mode 100644 index 0000000000..f101965722 --- /dev/null +++ b/packages/components/progress-tracker/src/ProgressTrackerStep.styles.ts @@ -0,0 +1,20 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export const progressTrackerStepVariants = cva(['inline-flex', 'bg-main'], { + variants: { + active: { + true: 'bg-success', + false: '', + }, + disabled: { + true: 'bg-info', + false: '', + }, + }, + defaultVariants: { + active: false, + disabled: false, + }, +}) + +export type ProgressTrackerStepVariantsProps = VariantProps diff --git a/packages/components/progress-tracker/src/ProgressTrackerStep.tsx b/packages/components/progress-tracker/src/ProgressTrackerStep.tsx new file mode 100644 index 0000000000..0c622cf2d5 --- /dev/null +++ b/packages/components/progress-tracker/src/ProgressTrackerStep.tsx @@ -0,0 +1,39 @@ +import { type ComponentPropsWithoutRef, forwardRef, useEffect, useId, useState } from 'react' + +import { useProgressTrackerContext } from './ProgressTrackerContext' +import { progressTrackerStepVariants } from './ProgressTrackerStep.styles' + +export type ProgressTrackerStepProps = ComponentPropsWithoutRef<'li'> & { + label?: string +} + +export const ProgressTrackerStep = forwardRef( + ({ label, className, ...rest }, ref) => { + const { stepIndex: activeStepIndex, steps, onStepClick, setSteps } = useProgressTrackerContext() + + const ID = useId() + const stepId = `step-${ID}` + + const [isActive, setIsActive] = useState(false) + + useEffect(() => setSteps(steps => steps.add(stepId)), []) + + useEffect( + () => setIsActive([...steps].indexOf(stepId) === activeStepIndex), + [activeStepIndex, steps, stepId] + ) + + return ( +
  • + +
  • + ) + } +) diff --git a/packages/components/progress-tracker/src/index.ts b/packages/components/progress-tracker/src/index.ts index 1774d010f5..c1e25eb8b5 100644 --- a/packages/components/progress-tracker/src/index.ts +++ b/packages/components/progress-tracker/src/index.ts @@ -1 +1,15 @@ -export { ProgressTracker } from './ProgressTracker' +import type { FC } from 'react' + +import { ProgressTracker as Root, type ProgressTrackerProps } from './ProgressTracker' +import { ProgressTrackerStep as Step, type ProgressTrackerStepProps } from './ProgressTrackerStep' + +export const ProgressTracker: FC & { + Step: typeof Step +} = Object.assign(Root, { + Step, +}) + +ProgressTracker.displayName = 'ProgressTracker' +Step.displayName = 'ProgressTracker.Step' + +export type { ProgressTrackerProps, ProgressTrackerStepProps }