diff --git a/packages/primitives/src/Steps.stories.tsx b/packages/primitives/src/Steps.stories.tsx new file mode 100644 index 0000000000..4d575d3224 --- /dev/null +++ b/packages/primitives/src/Steps.stories.tsx @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2024-present, NDLA. + * + * This source code is licensed under the GPLv3 license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import { Meta, StoryObj } from "@storybook/react"; +import { StepsContent, StepsIndicator, StepsItem, StepsList, StepsRoot, StepsSeparator, StepsTrigger } from "./Steps"; + +export default { + title: "Primitives/Steps", + component: StepsRoot, + tags: ["autodocs"], + args: { + orientation: "horizontal", + defaultStep: 0, + count: 3, + }, + render: (args) => ( + + + + + {1} + Step 1 + + + + + + {2} + Step 2 + + + + + + {12} + Step 3 + + + + Step 1 + Step 2 + Step 3 + + ), +} satisfies Meta; + +export const Horizontal: StoryObj = {}; + +export const Vertical: StoryObj = { + args: { + orientation: "vertical", + }, +}; diff --git a/packages/primitives/src/Steps.tsx b/packages/primitives/src/Steps.tsx new file mode 100644 index 0000000000..4f4947af35 --- /dev/null +++ b/packages/primitives/src/Steps.tsx @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2024-present, NDLA. + * + * This source code is licensed under the GPLv3 license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import { Steps } from "@ark-ui/react"; +// TODO: Refactor this when ark exposes their own steps anatomy. I've opened a PR. +import { anatomy as stepsAnatomy } from "@zag-js/steps"; +import { sva } from "@ndla/styled-system/css"; +import { JsxStyleProps } from "@ndla/styled-system/types"; +import { createStyleContext } from "./createStyleContext"; + +// TODO: Consider creating a PR for lazy-loading step content. + +const stepsRecipe = sva({ + slots: stepsAnatomy.keys(), + base: { + root: { + display: "flex", + width: "100%", + height: "100%", + gap: "medium", + _horizontal: { + flexDirection: "column", + }, + }, + list: { + display: "flex", + whiteSpace: "nowrap", + _horizontal: { + alignItems: "center", + }, + _vertical: { + flexDirection: "column", + }, + }, + item: { + position: "relative", + display: "flex", + flex: "1 0", + alignItems: "center", + _vertical: { + flexDirection: "column", + }, + }, + trigger: { + display: "flex", + alignItems: "center", + gap: "xxsmall", + textAlign: "start", + cursor: "pointer", + _hover: { + "& [data-part='indicator']": { + background: "surface.actionSubtle.hover", + }, + }, + _active: { + "& [data-part='indicator']": { + background: "surface.actionSubtle.hover.strong", + }, + }, + }, + indicator: { + display: "flex", + alignItems: "center", + justifyContent: "center", + border: "1px solid", + borderColor: "stroke.default", + background: "surface.default", + flexShrink: "0", + width: "medium", + height: "medium", + borderRadius: "full", + textStyle: "label.medium", + transitionProperty: "background", + transitionTimingFunction: "default", + transitionDuration: "normal", + "&[data-current]": { + background: "surface.actionSubtle.active", + }, + }, + separator: { + background: "stroke.default", + _vertical: { + height: "100%", + minHeight: "medium", + marginBlock: "xxsmall", + width: "1px", + }, + _horizontal: { + width: "100%", + minWidth: "medium", + height: "1px", + marginInline: "xxsmall", + }, + }, + content: { + width: "100%", + }, + }, +}); + +const { withContext, withProvider } = createStyleContext(stepsRecipe); + +export const StepsRoot = withProvider(Steps.Root, "root", { + baseComponent: true, +}); + +export const StepsList = withContext(Steps.List, "list", { + baseComponent: true, +}); + +export const StepsItem = withContext(Steps.Item, "item", { + baseComponent: true, +}); + +export const StepsTrigger = withContext( + Steps.Trigger, + "trigger", + { baseComponent: true }, +); + +export const StepsIndicator = withContext( + Steps.Indicator, + "indicator", + { baseComponent: true }, +); + +export const StepsSeparator = withContext( + Steps.Separator, + "separator", + { baseComponent: true }, +); + +export const StepsContent = withContext(Steps.Content, "content", { + baseComponent: true, +}); + +export const StepsNextTrigger = withContext( + Steps.NextTrigger, + "nextTrigger", + { baseComponent: true }, +); + +export const StepsPrevTrigger = withContext( + Steps.PrevTrigger, + "prevTrigger", + { baseComponent: true }, +); + +export const StepsProgress = withContext( + Steps.Progress, + "progress", + { baseComponent: true }, +);