@mahiraltinkaya/me-ui 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -38,7 +38,7 @@ cannot cost you local changes.
38
38
  | `select` | Dropdown over a string list, matching `input`'s affordances. |
39
39
  | `button` | Button with `cva` variant and size sets. |
40
40
  | `tooltip` | Tooltip provider, root, trigger and content. |
41
- | `stepper` | Progress rail for multi-step flows. **Requires Next.js.** |
41
+ | `stepper` | Progress rail for multi-step flows. |
42
42
  | `stepper-provider` | Step state, next/back guards and the `useStepper` hook. |
43
43
  | `step-field` | Label, control and validation message. |
44
44
  | `form-fields` | `FormInput` and `FormSelect`, bound to react-hook-form. |
@@ -55,12 +55,11 @@ A `components.json` is honoured when the project has one, so files follow the
55
55
  aliases the rest of its UI already uses. Otherwise the CLI reads `tsconfig.json`
56
56
  paths, falling back to the `src/` convention:
57
57
 
58
- | Registry path | Default destination |
59
- | ----------------- | ------------------------ |
60
- | `components/ui/…` | `src/components/ui/…` |
61
- | `components/…` | `src/components/…` |
62
- | `lib/…` | `src/lib/…` |
63
- | `assets/…` | `public/assets/images/…` |
58
+ | Registry path | Default destination |
59
+ | ----------------- | --------------------- |
60
+ | `components/ui/…` | `src/components/ui/…` |
61
+ | `components/…` | `src/components/…` |
62
+ | `lib/…` | `src/lib/…` |
64
63
 
65
64
  Imports inside the copied files are rewritten to match, whatever the aliases are.
66
65
 
@@ -70,7 +69,6 @@ Imports inside the copied files are rewritten to match, whatever the aliases are
70
69
  - **A shadcn base theme.** The components style against `--border`, `--input`,
71
70
  `--muted-foreground` and friends. `add` warns about any that are missing —
72
71
  `npx shadcn@latest init` is the quickest way to get them.
73
- - **Next.js**, but only for `stepper`, which renders its divider with `next/image`.
74
72
 
75
73
  ## Publishing
76
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mahiraltinkaya/me-ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Copy React + Tailwind form components into any project — they become your files, not a dependency.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,13 +33,6 @@ export interface StepperProviderProps {
33
33
  children: React.ReactNode;
34
34
  }
35
35
 
36
- /**
37
- * Step state and nothing else — no markup of its own.
38
- *
39
- * The rail and the active step are rendered by `<StepperView>`, which reads
40
- * this context. Keeping them apart means a consumer can drive step state from
41
- * a layout this component knows nothing about.
42
- */
43
36
  function StepperProvider({
44
37
  steps,
45
38
  initialStep = 0,
@@ -8,22 +8,12 @@ import { cn } from "@/lib/utils";
8
8
 
9
9
  export interface StepperViewProps {
10
10
  className?: string;
11
- /** One child per step, in the same order as the provider's `steps`. */
12
11
  children: React.ReactNode;
13
12
  }
14
13
 
15
- /**
16
- * Renders the progress rail and the child sitting at the active index.
17
- *
18
- * The live region is what tells a screen reader the step changed: the rail is
19
- * `aria-hidden` decoration and swapping the panel underneath it is otherwise a
20
- * silent DOM update.
21
- */
22
14
  function StepperView({ className, children }: StepperViewProps) {
23
15
  const { steps, currentStep, totalSteps, goToStep } = useStepper();
24
16
 
25
- // Children only change when the caller re-renders with a different set, so
26
- // the array is not rebuilt on every step change.
27
17
  const stepChildren = React.useMemo(() => React.Children.toArray(children), [children]);
28
18
 
29
19
  return (
@@ -5,10 +5,6 @@ import { Info } from "lucide-react";
5
5
 
6
6
  import { cn } from "@/lib/utils";
7
7
 
8
- /**
9
- * Solid triangle on the popup edge nearest the field. Base UI places the element
10
- * along the correct edge; the rotations below point it outward per side.
11
- */
12
8
  const ARROW_CLASS =
13
9
  "relative block h-1.5 w-3 overflow-clip data-[side=bottom]:top-[-6px] data-[side=left]:right-[-9px] data-[side=left]:rotate-90 data-[side=right]:left-[-9px] data-[side=right]:-rotate-90 data-[side=top]:bottom-[-6px] data-[side=top]:rotate-180 before:absolute before:bottom-0 before:left-1/2 before:h-[calc(6px*sqrt(2))] before:w-[calc(6px*sqrt(2))] before:bg-ink before:content-[''] before:[transform:translate(-50%,50%)_rotate(45deg)]";
14
10
 
@@ -19,24 +15,11 @@ export interface FieldHintContent {
19
15
 
20
16
  export interface FieldHintProps {
21
17
  hint: FieldHintContent;
22
- /** Driven by the field's focus state — the field is the only trigger. */
23
18
  open: boolean;
24
- /** Ties the popup to the field through `aria-describedby`. */
25
19
  id: string;
26
20
  className?: string;
27
21
  }
28
22
 
29
- /**
30
- * Explanatory panel anchored to a field's trailing info mark.
31
- *
32
- * Built on Tooltip rather than Popover on purpose: a popover moves focus into
33
- * itself when it opens, which would pull focus straight back out of the field
34
- * that opened it. A tooltip never takes focus, and `role="tooltip"` is what this
35
- * content actually is.
36
- *
37
- * The mark is a plain span with pointer events off, so hovering it does nothing;
38
- * `open` is the single source of truth.
39
- */
40
23
  function FieldHint({ hint, open, id, className }: FieldHintProps) {
41
24
  return (
42
25
  <Tooltip.Root open={open}>
@@ -28,8 +28,6 @@ function Input({
28
28
  const hintId = id ? `${id}-hint` : undefined;
29
29
  const hintOpen = Boolean(tooltip) && focused;
30
30
 
31
- // Merged rather than replaced: the field can be described by both its
32
- // validation message and the hint panel at the same time.
33
31
  const describedByAll =
34
32
  [describedBy, hintOpen ? hintId : undefined].filter(Boolean).join(" ") || undefined;
35
33
 
@@ -3,7 +3,6 @@
3
3
  import type * as React from "react";
4
4
 
5
5
  import { cn } from "@/lib/utils";
6
- import { StepperProgress } from "@/components/ui/stepper/stepper-progress";
7
6
  import { StepperRail } from "@/components/ui/stepper/stepper-rail";
8
7
  import type { StepperLayoutProps } from "@/components/ui/stepper/types";
9
8
 
@@ -15,7 +14,6 @@ export interface StepperProps
15
14
  function Stepper({ steps, currentStep, onStepChange, className, ...props }: StepperProps) {
16
15
  return (
17
16
  <div className={cn("w-full", className)} {...props}>
18
- <StepperProgress steps={steps} currentStep={currentStep} />
19
17
  <StepperRail steps={steps} currentStep={currentStep} onStepChange={onStepChange} />
20
18
  </div>
21
19
  );
@@ -1,19 +1,20 @@
1
1
  import { memo } from "react";
2
- import Image from "next/image";
3
-
4
2
  const StepDivider = memo(function StepDivider() {
5
3
  return (
6
- <span className="relative block h-full w-12 shrink-0">
7
- <Image
8
- src="/assets/images/breadcrumb_arrow.svg"
9
- alt=""
10
- aria-hidden
11
- unoptimized
12
- fill
13
- sizes="21px"
14
- className="object-contain"
4
+ <svg
5
+ aria-hidden
6
+ viewBox="0 0 25 70"
7
+ preserveAspectRatio="xMidYMid meet"
8
+ className="block h-full w-5 shrink-0 text-zinc-300 md:w-12"
9
+ >
10
+ <path
11
+ d="M1 1L23.6667 35L1 69"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ strokeLinecap="round"
15
+ strokeLinejoin="round"
15
16
  />
16
- </span>
17
+ </svg>
17
18
  );
18
19
  });
19
20
 
@@ -21,7 +21,7 @@ const StepIndicator = memo(function StepIndicator({ status, index, icon }: StepI
21
21
  <span
22
22
  aria-hidden
23
23
  className={cn(
24
- "flex size-9 shrink-0 items-center justify-center rounded-full text-[0.9375rem] font-semibold transition-colors duration-200",
24
+ "flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-semibold transition-colors duration-200 md:size-9 md:text-[0.9375rem]",
25
25
  indicatorStyles[status],
26
26
  )}
27
27
  >
@@ -5,7 +5,7 @@ import type { StepStatus } from "@/components/ui/stepper/types";
5
5
 
6
6
  const labelStyles: Record<StepStatus, string> = {
7
7
  completed: "text-zinc-900 font-semibold",
8
- current: "text-zinc-900 font-semibold",
8
+ current: "text-brand md:text-zinc-900 font-semibold",
9
9
  upcoming: "text-zinc-400 font-medium",
10
10
  };
11
11
 
@@ -13,12 +13,24 @@ export interface StepLabelProps {
13
13
  label: string;
14
14
  description?: string;
15
15
  status: StepStatus;
16
+ className?: string;
16
17
  }
17
18
 
18
- const StepLabel = memo(function StepLabel({ label, description, status }: StepLabelProps) {
19
+ const StepLabel = memo(function StepLabel({
20
+ label,
21
+ description,
22
+ status,
23
+ className,
24
+ }: StepLabelProps) {
19
25
  return (
20
- <span className={cn("flex min-w-0 flex-col text-left transition-colors", labelStyles[status])}>
21
- <span className="truncate text-[0.9375rem] whitespace-nowrap">{label}</span>
26
+ <span
27
+ className={cn(
28
+ "flex min-w-0 flex-col text-left transition-colors",
29
+ labelStyles[status],
30
+ className,
31
+ )}
32
+ >
33
+ <span className="truncate text-sm whitespace-nowrap md:text-[0.9375rem]">{label}</span>
22
34
  {description ? (
23
35
  <span className="truncate text-xs whitespace-nowrap opacity-70">{description}</span>
24
36
  ) : null}
@@ -28,7 +28,7 @@ function StepperRailItem({ step, index, currentStep, onStepChange }: StepperRail
28
28
  const handleClick = useCallback(() => onStepChange?.(index), [onStepChange, index]);
29
29
 
30
30
  const className = cn(
31
- "flex w-full items-center gap-4 rounded-full px-2.5 py-2.5 transition-colors duration-200",
31
+ "flex w-full items-center gap-2 rounded-full px-2 py-2.5 transition-colors duration-200 md:gap-4 md:px-2.5",
32
32
  isCurrent && "bg-white",
33
33
  clickable &&
34
34
  "cursor-pointer hover:bg-white/60 focus-visible:outline-brand focus-visible:outline-2 focus-visible:outline-offset-2",
@@ -37,7 +37,15 @@ function StepperRailItem({ step, index, currentStep, onStepChange }: StepperRail
37
37
  const content = (
38
38
  <>
39
39
  <StepIndicator status={status} index={index} icon={step.icon} />
40
- <StepLabel label={step.label} description={step.description} status={status} />
40
+ <StepLabel
41
+ label={step.label}
42
+ description={step.description}
43
+ status={status}
44
+ // Below `md` only the active step is legible enough to be worth the
45
+ // width. `sr-only` rather than `hidden`, so the other steps keep their
46
+ // names in the accessibility tree instead of becoming bare numbers.
47
+ className={cn(!isCurrent && "sr-only md:not-sr-only md:flex")}
48
+ />
41
49
  </>
42
50
  );
43
51
 
@@ -76,15 +84,20 @@ const StepperRail = memo(function StepperRail({
76
84
  onStepChange,
77
85
  }: StepperLayoutProps) {
78
86
  return (
79
- <nav aria-label="İlerleme" data-slot="stepper" className="relative hidden md:block">
87
+ <nav aria-label="İlerleme" data-slot="stepper" className="relative block">
80
88
  <span
81
89
  aria-hidden
82
90
  className="pointer-events-none absolute top-1/2 left-1/2 h-px w-screen -translate-x-1/2 -translate-y-1/2 bg-zinc-200/80"
83
91
  />
84
- <ol className="relative flex w-full items-stretch rounded-full border border-zinc-200/90 bg-white px-0.75">
92
+ <ol className="relative flex h-17 w-full items-stretch rounded-full border border-zinc-200/90 bg-white px-0.75">
85
93
  {steps.map((step, index) => (
86
94
  <Fragment key={step.label}>
87
- <li className="flex min-w-0 flex-1">
95
+ <li
96
+ className={cn(
97
+ "flex min-w-0 md:flex-1",
98
+ index === currentStep ? "flex-1" : "flex-none",
99
+ )}
100
+ >
88
101
  <StepperRailItem
89
102
  step={step}
90
103
  index={index}
@@ -93,7 +106,7 @@ const StepperRail = memo(function StepperRail({
93
106
  />
94
107
  </li>
95
108
  {index < steps.length - 1 ? (
96
- <li aria-hidden className="mr-6 ml-1 flex shrink-0 items-stretch">
109
+ <li aria-hidden className="mr-1.5 ml-0.5 flex shrink-0 items-stretch md:mr-6 md:ml-1">
97
110
  <StepDivider />
98
111
  </li>
99
112
  ) : null}
package/registry.json CHANGED
@@ -128,14 +128,8 @@
128
128
  "name": "stepper",
129
129
  "title": "Stepper",
130
130
  "description": "Progress rail for multi-step flows: a mobile progress bar plus a clickable desktop rail.",
131
- "dependencies": ["next"],
132
131
  "registryDependencies": ["utils", "brand-theme"],
133
132
  "files": [
134
- {
135
- "source": "public/assets/images/breadcrumb_arrow.svg",
136
- "path": "assets/breadcrumb_arrow.svg",
137
- "target": "public/assets/images/breadcrumb_arrow.svg"
138
- },
139
133
  {
140
134
  "source": "src/components/ui/stepper/index.tsx",
141
135
  "path": "components/ui/stepper/index.tsx"
@@ -156,10 +150,6 @@
156
150
  "source": "src/components/ui/stepper/step-label.tsx",
157
151
  "path": "components/ui/stepper/step-label.tsx"
158
152
  },
159
- {
160
- "source": "src/components/ui/stepper/stepper-progress.tsx",
161
- "path": "components/ui/stepper/stepper-progress.tsx"
162
- },
163
153
  {
164
154
  "source": "src/components/ui/stepper/stepper-rail.tsx",
165
155
  "path": "components/ui/stepper/stepper-rail.tsx"
@@ -1,3 +0,0 @@
1
- <svg width="25" height="70" viewBox="0 0 25 70" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path id="Vector 269" d="M1 1L23.6667 35L1 69" stroke="#CBD6DE"/>
3
- </svg>
@@ -1,40 +0,0 @@
1
- "use client";
2
-
3
- import { memo } from "react";
4
-
5
- import { cn } from "@/lib/utils";
6
- import type { StepperLayoutProps } from "@/components/ui/stepper/types";
7
-
8
- /** Mobile layout: active step name, counter, and a segmented progress bar. */
9
- const StepperProgress = memo(function StepperProgress({
10
- steps,
11
- currentStep,
12
- }: Omit<StepperLayoutProps, "onStepChange">) {
13
- const totalSteps = steps.length;
14
- const activeIndex = Math.min(Math.max(currentStep, 0), totalSteps - 1);
15
-
16
- return (
17
- <div className="rounded-2xl border border-zinc-200 bg-white p-4 md:hidden">
18
- <div className="flex items-baseline justify-between gap-3">
19
- <span className="text-brand text-sm font-semibold">{steps[activeIndex]?.label}</span>
20
- <span className="shrink-0 text-xs font-medium text-zinc-400 tabular-nums">
21
- {activeIndex + 1} / {totalSteps}
22
- </span>
23
- </div>
24
- <div className="mt-3 flex gap-1.5" role="presentation">
25
- {steps.map((step, index) => (
26
- <span
27
- key={step.label}
28
- title={step.label}
29
- className={cn(
30
- "h-1.5 flex-1 rounded-full transition-colors duration-300",
31
- index <= currentStep ? "bg-brand" : "bg-zinc-200",
32
- )}
33
- />
34
- ))}
35
- </div>
36
- </div>
37
- );
38
- });
39
-
40
- export { StepperProgress };