@sixthshift/design-system 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/components/DataTable/DataTable.d.ts +89 -0
  2. package/dist/components/DataTable/DataTable.d.ts.map +1 -0
  3. package/dist/components/DataTable/DataTable.js +107 -0
  4. package/dist/components/DataTable/DataTable.js.map +1 -0
  5. package/dist/components/DataTable/index.d.ts +2 -0
  6. package/dist/components/DataTable/index.d.ts.map +1 -0
  7. package/dist/components/DataTable/index.js +2 -0
  8. package/dist/components/DataTable/index.js.map +1 -0
  9. package/dist/components/FileDropzone/FileDropzone.d.ts +87 -0
  10. package/dist/components/FileDropzone/FileDropzone.d.ts.map +1 -0
  11. package/dist/components/FileDropzone/FileDropzone.js +146 -0
  12. package/dist/components/FileDropzone/FileDropzone.js.map +1 -0
  13. package/dist/components/FileDropzone/index.d.ts +2 -0
  14. package/dist/components/FileDropzone/index.d.ts.map +1 -0
  15. package/dist/components/FileDropzone/index.js +2 -0
  16. package/dist/components/FileDropzone/index.js.map +1 -0
  17. package/dist/components/ImageGallery/ImageGallery.d.ts +93 -0
  18. package/dist/components/ImageGallery/ImageGallery.d.ts.map +1 -0
  19. package/dist/components/ImageGallery/ImageGallery.js +121 -0
  20. package/dist/components/ImageGallery/ImageGallery.js.map +1 -0
  21. package/dist/components/ImageGallery/index.d.ts +2 -0
  22. package/dist/components/ImageGallery/index.d.ts.map +1 -0
  23. package/dist/components/ImageGallery/index.js +2 -0
  24. package/dist/components/ImageGallery/index.js.map +1 -0
  25. package/dist/components/NumberStepper/NumberStepper.d.ts +78 -0
  26. package/dist/components/NumberStepper/NumberStepper.d.ts.map +1 -0
  27. package/dist/components/NumberStepper/NumberStepper.js +129 -0
  28. package/dist/components/NumberStepper/NumberStepper.js.map +1 -0
  29. package/dist/components/NumberStepper/index.d.ts +2 -0
  30. package/dist/components/NumberStepper/index.d.ts.map +1 -0
  31. package/dist/components/NumberStepper/index.js +2 -0
  32. package/dist/components/NumberStepper/index.js.map +1 -0
  33. package/dist/components/Steps/Steps.d.ts +65 -0
  34. package/dist/components/Steps/Steps.d.ts.map +1 -0
  35. package/dist/components/Steps/Steps.js +62 -0
  36. package/dist/components/Steps/Steps.js.map +1 -0
  37. package/dist/components/Steps/index.d.ts +2 -0
  38. package/dist/components/Steps/index.d.ts.map +1 -0
  39. package/dist/components/Steps/index.js +2 -0
  40. package/dist/components/Steps/index.js.map +1 -0
  41. package/package.json +21 -1
  42. package/src/components/DataTable/DataTable.tsx +261 -0
  43. package/src/components/DataTable/data-table.recipe.css +28 -0
  44. package/src/components/DataTable/index.ts +10 -0
  45. package/src/components/FileDropzone/FileDropzone.tsx +239 -0
  46. package/src/components/FileDropzone/file-dropzone.recipe.css +38 -0
  47. package/src/components/FileDropzone/index.ts +1 -0
  48. package/src/components/ImageGallery/ImageGallery.tsx +285 -0
  49. package/src/components/ImageGallery/image-gallery.recipe.css +50 -0
  50. package/src/components/ImageGallery/index.ts +7 -0
  51. package/src/components/NumberStepper/NumberStepper.tsx +227 -0
  52. package/src/components/NumberStepper/index.ts +1 -0
  53. package/src/components/NumberStepper/number-stepper.recipe.css +31 -0
  54. package/src/components/Steps/Steps.tsx +149 -0
  55. package/src/components/Steps/index.ts +1 -0
  56. package/src/components/Steps/steps.recipe.css +51 -0
  57. package/src/theming/tailwind.css +5 -0
@@ -0,0 +1,149 @@
1
+ "use client";
2
+
3
+ import { cn } from "@sixthshift/design-system/utils";
4
+ import { cva } from "class-variance-authority";
5
+ import { Check } from "lucide-react";
6
+ import * as React from "react";
7
+
8
+ /**
9
+ * Geometry only. Every colour reads a `--steps-*` component token whose value
10
+ * is decided by src/components/Steps/steps.recipe.css, selected by the
11
+ * `data-state` each item renders (`complete`, `current`, `upcoming`).
12
+ */
13
+ const stepsVariants = cva("steps flex", {
14
+ variants: {
15
+ orientation: {
16
+ horizontal: "w-full flex-row items-start",
17
+ vertical: "flex-col",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ orientation: "horizontal",
22
+ },
23
+ });
24
+
25
+ export type StepsOrientation = "horizontal" | "vertical";
26
+
27
+ /** Where a step sits relative to `current`. Rendered as `data-state`. */
28
+ export type StepState = "complete" | "current" | "upcoming";
29
+
30
+ export type StepItem = {
31
+ /** Visible step name. */
32
+ label: React.ReactNode;
33
+ /** Secondary line under the label. */
34
+ description?: React.ReactNode;
35
+ };
36
+
37
+ export type StepsProps = Omit<React.OlHTMLAttributes<HTMLOListElement>, "children"> & {
38
+ /** The steps, in order. */
39
+ steps: readonly StepItem[];
40
+ /** Zero-based index of the step in progress. `steps.length` marks every step complete. */
41
+ current: number;
42
+ /**
43
+ * Makes completed steps clickable, for going back in a wizard. Upcoming
44
+ * steps never are — skipping ahead is the flow's decision, not the
45
+ * indicator's.
46
+ */
47
+ onStepClick?: (index: number) => void;
48
+ /** Layout direction. `horizontal` collapses labels to the current step's on narrow screens. */
49
+ orientation?: StepsOrientation;
50
+ };
51
+
52
+ function stateOf(index: number, current: number): StepState {
53
+ if (index < current) return "complete";
54
+ if (index === current) return "current";
55
+ return "upcoming";
56
+ }
57
+
58
+ /**
59
+ * Progress through a fixed sequence — checkout, onboarding, a multi-page
60
+ * form. Display-only: the step in progress is the `current` prop, owned by
61
+ * whatever runs the flow, so there is no internal state to control.
62
+ *
63
+ * Renders an `<ol>` (pass `aria-label`, e.g. "Checkout progress"); the
64
+ * current item carries `aria-current="step"`, and each indicator has a
65
+ * visually hidden status ("completed", "current") so the sequence reads
66
+ * correctly without the colours.
67
+ *
68
+ * With `onStepClick`, completed steps become buttons. Current and upcoming
69
+ * steps stay plain text: a step indicator that let you jump forward would be
70
+ * making the flow's validation decisions for it.
71
+ *
72
+ * Horizontally, every label shows from `sm` up; below that only the current
73
+ * step's label does, so four steps still fit a phone.
74
+ */
75
+ export const Steps = React.forwardRef<HTMLOListElement, StepsProps>(({ className, steps, current, onStepClick, orientation = "horizontal", ...props }, ref) => {
76
+ const isHorizontal = orientation === "horizontal";
77
+
78
+ return (
79
+ <ol ref={ref} data-orientation={orientation} className={stepsVariants({ orientation, className })} {...props}>
80
+ {steps.map((step, index) => {
81
+ const state = stateOf(index, current);
82
+ const isLast = index === steps.length - 1;
83
+ const clickable = state === "complete" && onStepClick !== undefined;
84
+
85
+ const indicator = (
86
+ <span className="steps-indicator border-(color:--steps-indicator-border) relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full border-2 bg-(--steps-indicator-bg) font-semibold text-(--steps-indicator-fg) text-sm transition-colors">
87
+ {state === "complete" ? <Check aria-hidden="true" className="size-4" strokeWidth={3} /> : index + 1}
88
+ </span>
89
+ );
90
+
91
+ const text = (
92
+ <span
93
+ className={cn("flex min-w-0 flex-col", isHorizontal ? "items-center text-center" : "pt-1", isHorizontal && state !== "current" && "max-sm:sr-only")}
94
+ >
95
+ <span className="font-medium text-(--steps-label-fg) text-sm">{step.label}</span>
96
+ {step.description !== undefined && <span className="text-(--steps-description-fg) text-xs">{step.description}</span>}
97
+ {state !== "upcoming" && <span className="sr-only">{state === "complete" ? "(completed)" : "(current)"}</span>}
98
+ </span>
99
+ );
100
+
101
+ const body = (
102
+ <>
103
+ {indicator}
104
+ {text}
105
+ </>
106
+ );
107
+
108
+ const bodyClass = cn("flex gap-2", isHorizontal ? "flex-col items-center" : "flex-row items-start gap-3");
109
+
110
+ return (
111
+ <li
112
+ // biome-ignore lint/suspicious/noArrayIndexKey: a step is its position — the sequence is fixed and never reorders
113
+ key={index}
114
+ data-state={state}
115
+ aria-current={state === "current" ? "step" : undefined}
116
+ className={cn("steps-item relative", isHorizontal ? "flex flex-1 justify-center" : "flex pb-6 last:pb-0")}
117
+ >
118
+ {!isLast && (
119
+ <span
120
+ aria-hidden="true"
121
+ className={cn(
122
+ "steps-connector absolute bg-(--steps-connector-bg)",
123
+ isHorizontal ? "top-4 left-1/2 h-0.5 w-full -translate-y-1/2" : "top-8 bottom-0 left-4 w-0.5 -translate-x-1/2"
124
+ )}
125
+ />
126
+ )}
127
+ {clickable ? (
128
+ <button
129
+ type="button"
130
+ onClick={() => onStepClick(index)}
131
+ className={cn(
132
+ bodyClass,
133
+ "focus-visible:ring-(color:--steps-ring) cursor-pointer rounded-md focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 [&:hover_.steps-indicator]:bg-(--steps-indicator-bg-hovered)"
134
+ )}
135
+ >
136
+ {body}
137
+ </button>
138
+ ) : (
139
+ <span className={bodyClass}>{body}</span>
140
+ )}
141
+ </li>
142
+ );
143
+ })}
144
+ </ol>
145
+ );
146
+ });
147
+ Steps.displayName = "Steps";
148
+
149
+ export { stepsVariants };
@@ -0,0 +1 @@
1
+ export { type StepItem, type StepState, Steps, type StepsOrientation, type StepsProps, stepsVariants } from "./Steps";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Steps recipe — the component-token layer for Steps.
3
+ *
4
+ * One axis, and it is not `intent`: a step's colour says where it sits in the
5
+ * sequence, which the component renders as `data-state` on each item
6
+ * (`complete`, `current`, `upcoming`). Same shape as tabs.css keying its
7
+ * selected cell on `data-selected` rather than a variant.
8
+ *
9
+ * The grammar is `--steps-[{part}-]{context}[-{state}]`. Parts are the
10
+ * `indicator` (the numbered circle), the `connector` (the rule to the next
11
+ * step), and the `label`/`description` text. `-hovered` on the indicator only
12
+ * ever applies to a completed step with `onStepClick`, the one clickable case.
13
+ *
14
+ * The connector belongs to the step it leaves, so a completed step's
15
+ * connector takes the brand colour: the line fills up to the current step.
16
+ *
17
+ * `@layer components` is load-bearing, not tidiness — see button.css.
18
+ */
19
+
20
+ @layer components {
21
+ /* The floor, and also the upcoming look: an outlined, quiet step. An item
22
+ with a state a consumer added but did not style renders like this. */
23
+ .steps-item {
24
+ --steps-indicator-bg: var(--bg-normal);
25
+ --steps-indicator-bg-hovered: var(--steps-indicator-bg);
26
+ --steps-indicator-fg: var(--fg-subtle);
27
+ --steps-indicator-border: var(--border-normal);
28
+ --steps-connector-bg: var(--border-normal);
29
+ --steps-label-fg: var(--fg-subtle);
30
+ --steps-description-fg: var(--fg-subtle);
31
+ --steps-ring: var(--focus-ring);
32
+ }
33
+
34
+ /* current — outlined in brand, the label at full strength. */
35
+ .steps-item[data-state="current"] {
36
+ --steps-indicator-bg: var(--bg-brand-subtle);
37
+ --steps-indicator-fg: var(--fg-on-brand-subtle);
38
+ --steps-indicator-border: var(--border-brand);
39
+ --steps-label-fg: var(--fg-normal);
40
+ }
41
+
42
+ /* complete — a filled brand disc with the check reversed out. */
43
+ .steps-item[data-state="complete"] {
44
+ --steps-indicator-bg: var(--bg-brand);
45
+ --steps-indicator-bg-hovered: var(--bg-brand-hovered);
46
+ --steps-indicator-fg: var(--fg-on-brand);
47
+ --steps-indicator-border: var(--steps-indicator-bg);
48
+ --steps-connector-bg: var(--border-brand);
49
+ --steps-label-fg: var(--fg-normal);
50
+ }
51
+ }
@@ -41,6 +41,11 @@
41
41
  @import "../components/Sheet/sheet.recipe.css";
42
42
  @import "../components/Select/select.recipe.css";
43
43
  @import "../components/TagChip/tag-chip.recipe.css";
44
+ @import "../components/NumberStepper/number-stepper.recipe.css";
45
+ @import "../components/Steps/steps.recipe.css";
46
+ @import "../components/ImageGallery/image-gallery.recipe.css";
47
+ @import "../components/DataTable/data-table.recipe.css";
48
+ @import "../components/FileDropzone/file-dropzone.recipe.css";
44
49
 
45
50
  /* =============================================================================
46
51
  3. TAILWIND THEME