torch-glare 1.3.0 → 1.4.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.
@@ -5,15 +5,16 @@ import { cn } from "../utils/cn";
5
5
  import { ButtonVariant, Themes } from "../utils/types";
6
6
 
7
7
  interface Props
8
- extends ButtonHTMLAttributes<HTMLButtonElement>,
9
- VariantProps<typeof buttonVariants> {
8
+ extends
9
+ ButtonHTMLAttributes<HTMLButtonElement>,
10
+ VariantProps<typeof buttonVariants> {
10
11
  is_loading?: boolean;
11
12
  disabled?: boolean;
12
13
  asChild?: boolean;
13
14
  variant?: ButtonVariant;
14
15
  as?: React.ElementType;
15
- theme?: Themes
16
- containerClassName?: string
16
+ theme?: Themes;
17
+ containerClassName?: string;
17
18
  }
18
19
  export const Button = forwardRef<HTMLButtonElement, Props>(
19
20
  (
@@ -32,7 +33,7 @@ export const Button = forwardRef<HTMLButtonElement, Props>(
32
33
  containerClassName,
33
34
  ...props
34
35
  },
35
- ref
36
+ ref,
36
37
  ) => {
37
38
  const Component = asChild ? Slot : Tag;
38
39
 
@@ -51,34 +52,45 @@ export const Button = forwardRef<HTMLButtonElement, Props>(
51
52
  buttonType,
52
53
  disabled,
53
54
  }),
54
- className
55
+ className,
55
56
  )}
56
57
  >
57
58
  {asChild ? (
58
59
  React.cloneElement(
59
60
  children as React.ReactElement,
60
61
  {},
61
- <div className={cn("px-[3px] flex items-center justify-center gap-[3px] has-[>i]:p-0", containerClassName)}>
62
+ <div
63
+ className={cn(
64
+ "flex items-center justify-center [&>:is(i,svg):first-child]:mr-[3px] [&>:is(i,svg):last-child]:ml-[3px] [&>:is(i,svg):only-child]:m-0 [&:has(>:is(i,svg):last-child):not(:has(>:is(i,svg):first-child))]:pl-[6px] [&:has(>:is(i,svg):first-child):not(:has(>:is(i,svg):last-child))]:pr-[6px]",
65
+ containerClassName,
66
+ )}
67
+ >
62
68
  {(children as React.ReactElement<any>).props.children}
63
69
  {is_loading && <LoadingIcon size={size} />}
64
- </div>
70
+ </div>,
65
71
  )
66
72
  ) : (
67
- <div className={cn("px-[3px] flex items-center justify-center gap-[3px] has-[>i]:p-0", containerClassName)}>
73
+ <div
74
+ className={cn(
75
+ "flex items-center justify-center [&>:is(i,svg):first-child]:mr-[3px] [&>:is(i,svg):last-child]:ml-[3px] [&>:is(i,svg):only-child]:m-0 [&:has(>:is(i,svg):last-child):not(:has(>:is(i,svg):first-child))]:pl-[6px] [&:has(>:is(i,svg):first-child):not(:has(>:is(i,svg):last-child))]:pr-[6px]",
76
+ containerClassName,
77
+ )}
78
+ >
68
79
  {children}
69
80
  {is_loading && <LoadingIcon size={size} />}
70
81
  </div>
71
82
  )}
72
83
  </Component>
73
84
  );
74
- }
85
+ },
75
86
  );
76
87
 
77
- Button.displayName = "Button"
88
+ Button.displayName = "Button";
78
89
 
79
-
80
-
81
- export function LoadingIcon({ size, className }: {
90
+ export function LoadingIcon({
91
+ size,
92
+ className,
93
+ }: {
82
94
  size?: "S" | "M" | "L" | "XL" | null;
83
95
  className?: string;
84
96
  }) {
@@ -88,7 +100,7 @@ export function LoadingIcon({ size, className }: {
88
100
  S: "w-[12px] h-[12px]",
89
101
  M: "w-[18px] h-[18px]",
90
102
  L: "w-[20px] h-[20px]",
91
- XL: "w-[20px] h-[20px]"
103
+ XL: "w-[20px] h-[20px]",
92
104
  },
93
105
  },
94
106
  defaultVariants: {
@@ -115,91 +127,130 @@ export function LoadingIcon({ size, className }: {
115
127
  );
116
128
  }
117
129
 
118
-
119
-
120
-
121
130
  export const buttonVariants = cva(
122
131
  "flex items-center whitespace-nowrap justify-center transition-[background,color] duration-200 ease-in-out border border-transparent outline-none leading-none [&-i]:!leading-none",
123
132
  {
124
133
  variants: {
125
134
  variant: {
126
- // fucus styles for medium screens and above
135
+ // Sec variants: base = button-secondary, hover picks up fill color
127
136
  PrimeStyle: [
128
- "bg-background-presentation-action-secondary",
137
+ "bg-background-presentation-button-secondary",
129
138
  "text-content-presentation-action-light-primary",
130
- "hover:bg-background-presentation-action-hover",
131
- "hover:text-content-presentation-action-hover",
132
- "focus:lg:focus:md:border lg:focus:md:border-border-presentation-state-focus", // Focus style only for medium screens and above
133
- "active:bg-background-presentation-action-hover active:text-content-presentation-action-hover",
139
+ "hover:bg-background-presentation-button-hover",
140
+ "hover:text-content-presentation-global-hover",
141
+ "focus:lg:focus:md:border lg:focus:md:border-border-presentation-state-focus",
142
+ "active:bg-background-presentation-button-hover active:text-content-presentation-global-hover",
134
143
  ],
135
- BlueSecStyle: [
136
- "bg-background-presentation-action-secondary",
144
+ BluSecStyle: [
145
+ "bg-background-presentation-button-secondary",
137
146
  "text-content-presentation-action-light-primary",
138
- "hover:bg-background-presentation-state-information-primary",
139
- "hover:text-content-presentation-action-hover",
147
+ "hover:bg-background-presentation-button-fill-blue-primary",
148
+ "hover:text-content-presentation-global-hover",
140
149
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
141
- "active:bg-background-presentation-state-information-primary active:text-content-presentation-action-hover",
150
+ "active:bg-background-presentation-button-fill-blue-primary active:text-content-presentation-global-hover",
142
151
  ],
143
152
  YelSecStyle: [
144
- "bg-background-presentation-action-secondary",
153
+ "bg-background-presentation-button-secondary",
145
154
  "text-content-presentation-action-light-primary",
146
- "hover:bg-background-presentation-state-warning-primary",
155
+ "hover:bg-background-presentation-button-fill-yellow-primary",
147
156
  "hover:text-content-presentation-action-light-primary",
148
157
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
149
- "active:bg-background-presentation-state-warning-primary active:text-content-presentation-action-light-primary",
158
+ "active:bg-background-presentation-button-fill-yellow-primary active:text-content-presentation-action-light-primary",
150
159
  ],
151
160
  RedSecStyle: [
152
- "bg-background-presentation-action-secondary",
161
+ "bg-background-presentation-button-secondary",
153
162
  "text-content-presentation-action-light-primary",
154
- "hover:bg-background-presentation-state-negative-primary",
155
- "hover:text-content-presentation-action-hover",
163
+ "hover:bg-background-presentation-button-fill-red-primary",
164
+ "hover:text-content-presentation-global-hover",
156
165
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
157
- "active:bg-background-presentation-state-negative-primary active:text-content-presentation-action-hover",
166
+ "active:bg-background-presentation-button-fill-red-primary active:text-content-presentation-global-hover",
158
167
  ],
159
168
  BorderStyle: [
160
169
  "text-content-presentation-action-light-primary",
161
170
  "border border-border-presentation-action-disabled",
162
- "bg-background-presentation-action-borderstyle",
163
- "hover:bg-background-presentation-action-hover",
164
- "hover:text-content-presentation-action-hover",
171
+ "bg-background-presentation-button-borderstyle",
172
+ "hover:bg-background-presentation-button-hover",
173
+ "hover:text-content-presentation-global-hover",
165
174
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
166
175
  "focus:lg:text-content-presentation-action-light-primary",
167
- "focus:hover:lg:text-content-presentation-action-hover",
168
- "active:bg-background-presentation-action-hover active:text-content-presentation-action-hover",
176
+ "focus:hover:lg:text-content-presentation-global-hover",
177
+ "active:bg-background-presentation-button-hover active:text-content-presentation-global-hover",
169
178
  ],
179
+ // Cont variants: transparent base, hover shows button-contstyle-hover
170
180
  PrimeContStyle: [
171
181
  "text-content-presentation-action-light-primary",
172
182
  "border-transparent bg-transparent",
173
- "hover:bg-background-presentation-action-contstyle-hover",
183
+ "hover:bg-background-presentation-button-contstyle-hover",
174
184
  "hover:text-content-presentation-action-light-primary",
175
185
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
176
- "focus:lg:bg-background-presentation-action-borderstyle",
177
- "active:bg-background-presentation-action-contstyle-hover active:text-content-presentation-action-light-primary",
186
+ "focus:lg:bg-background-presentation-button-borderstyle",
187
+ "active:bg-background-presentation-button-contstyle-hover active:text-content-presentation-action-light-primary",
178
188
  ],
179
- BlueContStyle: [
189
+ BluContStyle: [
180
190
  "text-content-presentation-action-light-primary",
181
191
  "border-transparent bg-transparent",
182
- "hover:bg-background-presentation-action-contstyle-hover",
183
- "hover:text-content-presentation-action-information-hover",
192
+ "hover:bg-background-presentation-button-contstyle-hover",
193
+ "hover:text-content-presentation-global-information-hover",
184
194
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
185
- "focus:lg:bg-background-presentation-action-borderstyle",
186
- "active:bg-background-presentation-action-contstyle-hover active:text-content-presentation-action-information-hover",
195
+ "focus:lg:bg-background-presentation-button-borderstyle",
196
+ "active:bg-background-presentation-button-contstyle-hover active:text-content-presentation-global-information-hover",
187
197
  ],
188
198
  RedContStyle: [
189
199
  "text-content-presentation-action-light-primary",
190
200
  "border-transparent bg-transparent",
191
- "hover:bg-background-presentation-action-contstyle-hover",
192
- "hover:text-content-presentation-action-negative-hover",
201
+ "hover:bg-background-presentation-button-contstyle-hover",
202
+ "hover:text-content-presentation-global-negative-hover",
203
+ "focus:lg:border focus:lg:border-border-presentation-state-focus",
204
+ "focus:lg:bg-background-presentation-button-borderstyle",
205
+ "active:bg-background-presentation-button-contstyle-hover active:text-content-presentation-global-negative-hover",
206
+ ],
207
+ // Col variants: filled solid color base, hover deepens to -secondary
208
+ PrimeColStyle: [
209
+ "bg-background-presentation-button-primary",
210
+ "text-content-presentation-global-primary-inverse",
211
+ "hover:bg-background-presentation-button-hover",
212
+ "hover:text-content-presentation-global-primary-inverse",
213
+ "focus:lg:border focus:lg:border-border-presentation-state-focus",
214
+ "active:bg-background-presentation-button-hover active:text-content-presentation-global-primary-inverse",
215
+ ],
216
+ BluColStyle: [
217
+ "bg-background-presentation-button-fill-blue-primary",
218
+ "text-content-presentation-action-dark-primary",
219
+ "hover:bg-background-presentation-button-fill-blue-secondary",
220
+ "hover:text-content-presentation-action-dark-primary",
221
+ "focus:lg:border focus:lg:border-border-presentation-state-focus",
222
+ "active:bg-background-presentation-button-fill-blue-secondary active:text-content-presentation-action-dark-primary",
223
+ ],
224
+ RedColStyle: [
225
+ "bg-background-presentation-button-fill-red-primary",
226
+ "text-content-presentation-action-dark-primary",
227
+ "hover:bg-background-presentation-button-fill-red-secondary",
228
+ "hover:text-content-presentation-action-dark-primary",
229
+ "focus:lg:border focus:lg:border-border-presentation-state-focus",
230
+ "active:bg-background-presentation-button-fill-red-secondary active:text-content-presentation-action-dark-primary",
231
+ ],
232
+ GreenColStyle: [
233
+ "bg-background-presentation-button-fill-green-primary",
234
+ "text-content-presentation-action-dark-primary",
235
+ "hover:bg-background-presentation-button-fill-green-secondary",
236
+ "hover:text-content-presentation-action-dark-primary",
237
+ "focus:lg:border focus:lg:border-border-presentation-state-focus",
238
+ "active:bg-background-presentation-button-fill-green-secondary active:text-content-presentation-action-dark-primary",
239
+ ],
240
+ YelColStyle: [
241
+ "bg-background-presentation-button-fill-yellow-primary",
242
+ "text-content-presentation-global-primary",
243
+ "hover:bg-background-presentation-button-fill-yellow-secondary",
244
+ "hover:text-content-presentation-global-primary",
193
245
  "focus:lg:border focus:lg:border-border-presentation-state-focus",
194
- "focus:lg:bg-background-presentation-action-borderstyle",
195
- "active:bg-background-presentation-action-contstyle-hover active:text-content-presentation-action-negative-hover",
246
+ "active:bg-background-presentation-button-fill-yellow-secondary active:text-content-presentation-global-primary",
196
247
  ],
197
248
  },
198
249
  size: {
199
- S: "h-[22px] px-[6px] typography-body-small-medium rounded-[4px] [&_i]:text-[12px]",
200
- M: "h-[28px] px-[14px] typography-body-large-medium rounded-[4px] [&_i]:text-[18px]",
201
- L: "h-[34px] px-[22px] typography-body-large-medium rounded-[6px] [&_i]:text-[20px]",
202
- XL: "h-[40px] px-[30px] typography-headers-medium-medium rounded-[6px] [&_i]:text-[22px]",
250
+ S: "h-[22px] py-[2px] px-[6px] typography-body-small-medium rounded-[4px] [&_i]:text-[12px]",
251
+ M: "h-[28px] py-[2px] px-[14px] typography-body-large-medium rounded-[6px] [&_i]:text-[18px]",
252
+ L: "h-[34px] py-[5px] px-[22px] typography-body-large-medium rounded-[8px] [&_i]:text-[20px]",
253
+ XL: "h-[40px] py-[8px] px-[30px] typography-headers-medium-medium rounded-[8px] [&_i]:text-[22px]",
203
254
  },
204
255
  is_loading: {
205
256
  true: "[&_i]:hidden",
@@ -222,12 +273,12 @@ export const buttonVariants = cva(
222
273
  is_loading: true,
223
274
  className: [
224
275
  "cursor-wait",
225
- "bg-background-presentation-action-hover",
226
- "text-content-presentation-action-hover",
227
- "hover:bg-background-presentation-action-hover",
228
- "hover:text-content-presentation-action-hover",
276
+ "bg-background-presentation-button-hover",
277
+ "text-content-presentation-global-hover",
278
+ "hover:bg-background-presentation-button-hover",
279
+ "hover:text-content-presentation-global-hover",
229
280
  "focus:lg:border focus:lg:border-transparent",
230
- "active:bg-background-presentation-action-hover active:text-content-presentation-action-hover",
281
+ "active:bg-background-presentation-button-hover active:text-content-presentation-global-hover",
231
282
  ],
232
283
  },
233
284
  {
@@ -235,7 +286,7 @@ export const buttonVariants = cva(
235
286
  className: [
236
287
  "cursor-not-allowed",
237
288
  "pointer-events-none",
238
- "bg-background-presentation-action-disabled",
289
+ "bg-background-presentation-button-disabled",
239
290
  "text-content-presentation-state-disabled",
240
291
  "border-transparent",
241
292
  ],
@@ -261,5 +312,5 @@ export const buttonVariants = cva(
261
312
  className: "w-[40px] h-[40px] p-0 leading-[0]",
262
313
  },
263
314
  ],
264
- }
315
+ },
265
316
  );
@@ -0,0 +1,71 @@
1
+ import { forwardRef, HTMLAttributes, ReactNode } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../utils/cn";
4
+
5
+ const titleBadge = cva(
6
+ "flex pt-2 pb-2 pl-[16px] pr-[22px] justify-center items-center gap-[6px] rounded-[10px] self-start typography-headers-medium-medium text-[#F4F4F4]",
7
+ {
8
+ variants: {
9
+ color: {
10
+ Blue: "bg-blue-sparkle-900",
11
+ Yellow: "bg-yellow-950",
12
+ Green: "bg-green-cyan-900",
13
+ Red: "bg-medium-red-900",
14
+ Orange: "bg-red-orange-900",
15
+ Purple: "bg-violet-900",
16
+ Pink: "bg-medium-violet-red-900",
17
+ Gray: "bg-background-presentation-button-primary",
18
+ },
19
+ },
20
+ defaultVariants: { color: "Blue" },
21
+ },
22
+ );
23
+
24
+ export type SectionColor = NonNullable<VariantProps<typeof titleBadge>["color"]>;
25
+
26
+ export interface SectionCardProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
27
+ color?: SectionColor;
28
+ title?: ReactNode;
29
+ containerClassName?: string;
30
+ headerClassName?: string;
31
+ bodyClassName?: string;
32
+ }
33
+
34
+ export const SectionCard = forwardRef<HTMLDivElement, SectionCardProps>(
35
+ (
36
+ {
37
+ children,
38
+ color,
39
+ title,
40
+ className,
41
+ containerClassName,
42
+ headerClassName,
43
+ bodyClassName,
44
+ ...props
45
+ },
46
+ ref,
47
+ ) => {
48
+ return (
49
+ <div
50
+ ref={ref}
51
+ className={cn(
52
+ "flex w-[1100px] pt-[6px] pb-[24px] px-0 flex-col rounded-[16px] bg-background-presentation-form-base shadow-[0_0_32px_2px_rgba(0,0,0,0.05)]",
53
+ className,
54
+ containerClassName,
55
+ )}
56
+ {...props}
57
+ >
58
+ {title && (
59
+ <div className={cn("flex px-[6px] flex-col gap-[10px]", headerClassName)}>
60
+ <div className={cn(titleBadge({ color }))}>{title}</div>
61
+ </div>
62
+ )}
63
+ <div className={cn("flex px-[42px] flex-col gap-[2px]", bodyClassName)}>
64
+ {children}
65
+ </div>
66
+ </div>
67
+ );
68
+ },
69
+ );
70
+
71
+ SectionCard.displayName = "SectionCard";
@@ -0,0 +1,374 @@
1
+ "use client";
2
+
3
+ import React, {
4
+ forwardRef,
5
+ HTMLAttributes,
6
+ ReactNode,
7
+ createContext,
8
+ useContext,
9
+ } from "react";
10
+ import { cva, type VariantProps } from "class-variance-authority";
11
+ import { cn } from "../utils/cn";
12
+ import { Themes } from "../utils/types";
13
+
14
+ // ─── Context ─────────────────────────────────────────────────────────────────
15
+
16
+ interface StepperContextValue {
17
+ activeStep: number;
18
+ orientation: "horizontal" | "vertical";
19
+ size: "S" | "M" | "L";
20
+ }
21
+
22
+ const StepperContext = createContext<StepperContextValue>({
23
+ activeStep: 0,
24
+ orientation: "horizontal",
25
+ size: "M",
26
+ });
27
+
28
+ const useStepperContext = () => useContext(StepperContext);
29
+
30
+ // ─── Stepper Root ────────────────────────────────────────────────────────────
31
+
32
+ const stepperStyles = cva(["flex gap-0"], {
33
+ variants: {
34
+ orientation: {
35
+ horizontal: "flex-row items-start",
36
+ vertical: "flex-col",
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ orientation: "horizontal",
41
+ },
42
+ });
43
+
44
+ interface StepperProps
45
+ extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof stepperStyles> {
46
+ theme?: Themes;
47
+ activeStep?: number;
48
+ size?: "S" | "M" | "L";
49
+ }
50
+
51
+ const Stepper = forwardRef<HTMLDivElement, StepperProps>(
52
+ (
53
+ {
54
+ className,
55
+ orientation = "horizontal",
56
+ theme,
57
+ activeStep = 0,
58
+ size = "M",
59
+ ...props
60
+ },
61
+ ref,
62
+ ) => (
63
+ <StepperContext.Provider
64
+ value={{
65
+ activeStep,
66
+ orientation: orientation ?? "horizontal",
67
+ size: size ?? "M",
68
+ }}
69
+ >
70
+ <div
71
+ ref={ref}
72
+ data-theme={theme}
73
+ data-orientation={orientation}
74
+ className={cn(stepperStyles({ orientation }), className)}
75
+ {...props}
76
+ />
77
+ </StepperContext.Provider>
78
+ ),
79
+ );
80
+ Stepper.displayName = "Stepper";
81
+
82
+ // ─── Step ────────────────────────────────────────────────────────────────────
83
+
84
+ interface StepProps extends HTMLAttributes<HTMLDivElement> {
85
+ index?: number;
86
+ isCompleted?: boolean;
87
+ isActive?: boolean;
88
+ isError?: boolean;
89
+ }
90
+
91
+ const Step = forwardRef<HTMLDivElement, StepProps>(
92
+ (
93
+ {
94
+ className,
95
+ index = 0,
96
+ isCompleted,
97
+ isActive,
98
+ isError,
99
+ children,
100
+ ...props
101
+ },
102
+ ref,
103
+ ) => {
104
+ const { activeStep, orientation } = useStepperContext();
105
+
106
+ const computedActive = isActive ?? index === activeStep;
107
+ const computedCompleted = isCompleted ?? index < activeStep;
108
+ const computedError = isError ?? false;
109
+
110
+ return (
111
+ <div
112
+ ref={ref}
113
+ data-active={computedActive || undefined}
114
+ data-completed={computedCompleted || undefined}
115
+ data-error={computedError || undefined}
116
+ data-orientation={orientation}
117
+ className={cn(
118
+ "flex group/step",
119
+ orientation === "horizontal"
120
+ ? "shrink-0 flex-col items-center gap-2"
121
+ : "flex-row gap-3",
122
+ className,
123
+ )}
124
+ {...props}
125
+ >
126
+ {React.Children.map(children, (child) => {
127
+ if (!React.isValidElement(child)) return child;
128
+ return React.cloneElement(child as React.ReactElement<any>, {
129
+ _active: computedActive,
130
+ _completed: computedCompleted,
131
+ _error: computedError,
132
+ _index: index,
133
+ });
134
+ })}
135
+ </div>
136
+ );
137
+ },
138
+ );
139
+ Step.displayName = "Step";
140
+
141
+ // ─── Step Indicator ──────────────────────────────────────────────────────────
142
+
143
+ const stepIndicatorStyles = cva(
144
+ [
145
+ "flex items-center justify-center shrink-0 rounded-full",
146
+ "border",
147
+ "transition-all duration-200 ease-in-out",
148
+ "typography-body-small-medium",
149
+ "[&_i]:leading-none [&_i]:flex [&_i]:items-center [&_i]:justify-center",
150
+ ],
151
+ {
152
+ variants: {
153
+ state: {
154
+ pending: [
155
+ "bg-background-presentation-action-disabled",
156
+ "border-border-presentation-action-disabled",
157
+ "text-content-presentation-state-disabled",
158
+ ],
159
+ active: [
160
+ "bg-background-presentation-state-information-primary",
161
+ "border-border-presentation-state-focus",
162
+ "text-content-presentation-state-information",
163
+ ],
164
+ completed: [
165
+ "bg-background-presentation-state-success-primary",
166
+ "border-border-presentation-state-success",
167
+ "text-content-presentation-state-success",
168
+ ],
169
+ error: [
170
+ "bg-background-presentation-state-negative-primary",
171
+ "border-border-presentation-state-negative",
172
+ "text-content-presentation-state-negative",
173
+ ],
174
+ },
175
+ size: {
176
+ S: "w-[22px] h-[22px] text-[10px] [&_i]:text-[12px]",
177
+ M: "w-[28px] h-[28px] text-[12px] [&_i]:text-[14px]",
178
+ L: "w-[34px] h-[34px] text-[14px] [&_i]:text-[16px]",
179
+ },
180
+ },
181
+ defaultVariants: {
182
+ state: "pending",
183
+ size: "M",
184
+ },
185
+ },
186
+ );
187
+
188
+ interface StepIndicatorProps extends HTMLAttributes<HTMLDivElement> {
189
+ icon?: ReactNode;
190
+ completedIcon?: ReactNode;
191
+ errorIcon?: ReactNode;
192
+ _active?: boolean;
193
+ _completed?: boolean;
194
+ _error?: boolean;
195
+ _index?: number;
196
+ }
197
+
198
+ const StepIndicator = forwardRef<HTMLDivElement, StepIndicatorProps>(
199
+ (
200
+ {
201
+ className,
202
+ icon,
203
+ completedIcon,
204
+ errorIcon,
205
+ _active,
206
+ _completed,
207
+ _error,
208
+ _index = 0,
209
+ children,
210
+ ...props
211
+ },
212
+ ref,
213
+ ) => {
214
+ const { size } = useStepperContext();
215
+
216
+ const state = _error
217
+ ? "error"
218
+ : _completed
219
+ ? "completed"
220
+ : _active
221
+ ? "active"
222
+ : "pending";
223
+
224
+ const renderContent = () => {
225
+ if (_error && errorIcon) return errorIcon;
226
+ if (_error) return <i className="ri-close-line" />;
227
+ if (_completed && completedIcon) return completedIcon;
228
+ if (_completed) return <i className="ri-check-line" />;
229
+ if (icon) return icon;
230
+ if (children) return children;
231
+ return _index + 1;
232
+ };
233
+
234
+ return (
235
+ <div
236
+ ref={ref}
237
+ className={cn(stepIndicatorStyles({ state, size }), className)}
238
+ {...props}
239
+ >
240
+ {renderContent()}
241
+ </div>
242
+ );
243
+ },
244
+ );
245
+ StepIndicator.displayName = "StepIndicator";
246
+
247
+ // ─── Step Connector (the line between steps) ────────────────────────────────
248
+
249
+ const connectorStyles = cva(["transition-all duration-200 ease-in-out"], {
250
+ variants: {
251
+ orientation: {
252
+ horizontal: "h-[2px] flex-1 mx-2 mt-[13px]",
253
+ vertical: "w-[2px] flex-1 min-h-[24px] my-2",
254
+ },
255
+ state: {
256
+ pending: "bg-border-presentation-action-disabled",
257
+ completed: "bg-border-presentation-state-focus",
258
+ },
259
+ },
260
+ defaultVariants: {
261
+ orientation: "horizontal",
262
+ state: "pending",
263
+ },
264
+ compoundVariants: [
265
+ {
266
+ orientation: "horizontal",
267
+ className: "self-start",
268
+ },
269
+ ],
270
+ });
271
+
272
+ interface StepConnectorProps extends HTMLAttributes<HTMLDivElement> {
273
+ _completed?: boolean;
274
+ _active?: boolean;
275
+ _error?: boolean;
276
+ _index?: number;
277
+ }
278
+
279
+ const StepConnector = forwardRef<HTMLDivElement, StepConnectorProps>(
280
+ (
281
+ { className, _completed, _active: _a, _error: _e, _index: _i, ...props },
282
+ ref,
283
+ ) => {
284
+ const { orientation } = useStepperContext();
285
+
286
+ return (
287
+ <div
288
+ ref={ref}
289
+ className={cn(
290
+ connectorStyles({
291
+ orientation,
292
+ state: _completed ? "completed" : "pending",
293
+ }),
294
+ className,
295
+ )}
296
+ {...props}
297
+ />
298
+ );
299
+ },
300
+ );
301
+ StepConnector.displayName = "StepConnector";
302
+
303
+ // ─── Step Label ──────────────────────────────────────────────────────────────
304
+
305
+ interface StepLabelProps extends HTMLAttributes<HTMLDivElement> {
306
+ _active?: boolean;
307
+ _completed?: boolean;
308
+ _error?: boolean;
309
+ _index?: number;
310
+ }
311
+
312
+ const StepLabel = forwardRef<HTMLDivElement, StepLabelProps>(
313
+ ({ className, _active, _completed, _error, _index: _i, ...props }, ref) => (
314
+ <div
315
+ ref={ref}
316
+ className={cn(
317
+ "typography-body-small-medium transition-colors duration-200 ease-in-out",
318
+ _active
319
+ ? "text-content-presentation-state-information"
320
+ : _error
321
+ ? "text-content-presentation-state-negative"
322
+ : _completed
323
+ ? "text-content-presentation-global-primary"
324
+ : "text-content-presentation-state-disabled",
325
+ className,
326
+ )}
327
+ {...props}
328
+ />
329
+ ),
330
+ );
331
+ StepLabel.displayName = "StepLabel";
332
+
333
+ // ─── Step Description ────────────────────────────────────────────────────────
334
+
335
+ interface StepDescriptionProps extends HTMLAttributes<HTMLDivElement> {
336
+ _active?: boolean;
337
+ _completed?: boolean;
338
+ _error?: boolean;
339
+ _index?: number;
340
+ }
341
+
342
+ const StepDescription = forwardRef<HTMLDivElement, StepDescriptionProps>(
343
+ (
344
+ { className, _active, _completed: _c, _error: _e, _index: _i, ...props },
345
+ ref,
346
+ ) => (
347
+ <div
348
+ ref={ref}
349
+ className={cn(
350
+ "typography-body-small-regular",
351
+ _active
352
+ ? "text-content-presentation-global-secondary"
353
+ : "text-content-presentation-state-disabled",
354
+ className,
355
+ )}
356
+ {...props}
357
+ />
358
+ ),
359
+ );
360
+ StepDescription.displayName = "StepDescription";
361
+
362
+ // ─── Exports ─────────────────────────────────────────────────────────────────
363
+
364
+ export {
365
+ Stepper,
366
+ Step,
367
+ StepIndicator,
368
+ StepConnector,
369
+ StepLabel,
370
+ StepDescription,
371
+ stepperStyles,
372
+ stepIndicatorStyles,
373
+ connectorStyles,
374
+ };
@@ -0,0 +1,283 @@
1
+ import React, { forwardRef, HTMLAttributes, ReactNode } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../utils/cn";
4
+ import { Themes } from "../utils/types";
5
+
6
+ // ─── Timeline Root ───────────────────────────────────────────────────────────
7
+
8
+ const timelineStyles = cva(["flex gap-0"], {
9
+ variants: {
10
+ orientation: {
11
+ vertical: "flex-col",
12
+ horizontal: "flex-row items-start",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ orientation: "vertical",
17
+ },
18
+ });
19
+
20
+ interface TimelineProps
21
+ extends HTMLAttributes<HTMLDivElement>,
22
+ VariantProps<typeof timelineStyles> {
23
+ theme?: Themes;
24
+ }
25
+
26
+ const Timeline = forwardRef<HTMLDivElement, TimelineProps>(
27
+ ({ className, orientation, theme, ...props }, ref) => (
28
+ <div
29
+ ref={ref}
30
+ data-theme={theme}
31
+ data-orientation={orientation ?? "vertical"}
32
+ className={cn(timelineStyles({ orientation }), className)}
33
+ {...props}
34
+ />
35
+ )
36
+ );
37
+ Timeline.displayName = "Timeline";
38
+
39
+ // ─── Timeline Item ───────────────────────────────────────────────────────────
40
+
41
+ const timelineItemStyles = cva(["flex group/item"], {
42
+ variants: {
43
+ orientation: {
44
+ vertical: "flex-row gap-3",
45
+ horizontal: "flex-col items-center gap-3",
46
+ },
47
+ },
48
+ defaultVariants: {
49
+ orientation: "vertical",
50
+ },
51
+ });
52
+
53
+ interface TimelineItemProps extends HTMLAttributes<HTMLDivElement> {
54
+ orientation?: "vertical" | "horizontal";
55
+ }
56
+
57
+ const TimelineItem = forwardRef<HTMLDivElement, TimelineItemProps>(
58
+ ({ className, orientation = "vertical", ...props }, ref) => (
59
+ <div
60
+ ref={ref}
61
+ data-orientation={orientation}
62
+ className={cn(timelineItemStyles({ orientation }), className)}
63
+ {...props}
64
+ />
65
+ )
66
+ );
67
+ TimelineItem.displayName = "TimelineItem";
68
+
69
+ // ─── Timeline Indicator ──────────────────────────────────────────────────────
70
+
71
+ const indicatorStyles = cva(
72
+ [
73
+ "flex items-center justify-center shrink-0 rounded-full",
74
+ "border",
75
+ "transition-all duration-200 ease-in-out",
76
+ "[&_i]:leading-none [&_i]:flex [&_i]:items-center [&_i]:justify-center",
77
+ ],
78
+ {
79
+ variants: {
80
+ variant: {
81
+ default: [
82
+ "bg-background-presentation-action-secondary",
83
+ "border-border-presentation-action-primary",
84
+ "text-content-presentation-action-light-primary",
85
+ ],
86
+ active: [
87
+ "bg-background-presentation-state-information-primary",
88
+ "border-border-presentation-state-focus",
89
+ "text-content-presentation-state-information",
90
+ ],
91
+ completed: [
92
+ "bg-background-presentation-state-success-primary",
93
+ "border-border-presentation-state-success",
94
+ "text-content-presentation-state-success",
95
+ ],
96
+ error: [
97
+ "bg-background-presentation-state-negative-primary",
98
+ "border-border-presentation-state-negative",
99
+ "text-content-presentation-state-negative",
100
+ ],
101
+ warning: [
102
+ "bg-background-presentation-state-warning-primary",
103
+ "border-border-presentation-state-warning",
104
+ "text-content-presentation-state-warning",
105
+ ],
106
+ },
107
+ size: {
108
+ S: "w-[22px] h-[22px] [&_i]:text-[12px]",
109
+ M: "w-[28px] h-[28px] [&_i]:text-[14px]",
110
+ L: "w-[34px] h-[34px] [&_i]:text-[16px]",
111
+ },
112
+ },
113
+ defaultVariants: {
114
+ variant: "default",
115
+ size: "M",
116
+ },
117
+ }
118
+ );
119
+
120
+ interface TimelineIndicatorProps
121
+ extends HTMLAttributes<HTMLDivElement>,
122
+ VariantProps<typeof indicatorStyles> {
123
+ icon?: ReactNode;
124
+ }
125
+
126
+ const TimelineIndicator = forwardRef<HTMLDivElement, TimelineIndicatorProps>(
127
+ ({ className, variant, size, icon, children, ...props }, ref) => {
128
+ const renderContent = () => {
129
+ if (icon) return icon;
130
+ if (children) return children;
131
+ if (variant === "completed") return <i className="ri-check-line" />;
132
+ if (variant === "error") return <i className="ri-close-line" />;
133
+ if (variant === "warning") return <i className="ri-alert-line" />;
134
+ return <span className="block w-[6px] h-[6px] rounded-full bg-current" />;
135
+ };
136
+
137
+ return (
138
+ <div
139
+ ref={ref}
140
+ className={cn(indicatorStyles({ variant, size }), className)}
141
+ {...props}
142
+ >
143
+ {renderContent()}
144
+ </div>
145
+ );
146
+ }
147
+ );
148
+ TimelineIndicator.displayName = "TimelineIndicator";
149
+
150
+ // ─── Timeline Separator (the connecting line) ────────────────────────────────
151
+
152
+ const separatorStyles = cva(
153
+ [
154
+ "bg-border-presentation-global-primary",
155
+ "transition-all duration-200 ease-in-out",
156
+ ],
157
+ {
158
+ variants: {
159
+ orientation: {
160
+ vertical: "w-[1px] flex-1 min-h-[24px] mx-auto",
161
+ horizontal: "h-[1px] flex-1 min-w-[24px] my-auto",
162
+ },
163
+ active: {
164
+ true: "bg-border-presentation-state-focus",
165
+ },
166
+ },
167
+ defaultVariants: {
168
+ orientation: "vertical",
169
+ active: false,
170
+ },
171
+ }
172
+ );
173
+
174
+ interface TimelineSeparatorProps
175
+ extends HTMLAttributes<HTMLDivElement>,
176
+ VariantProps<typeof separatorStyles> {}
177
+
178
+ const TimelineSeparator = forwardRef<HTMLDivElement, TimelineSeparatorProps>(
179
+ ({ className, orientation = "vertical", active, ...props }, ref) => (
180
+ <div
181
+ ref={ref}
182
+ className={cn(separatorStyles({ orientation, active }), className)}
183
+ {...props}
184
+ />
185
+ )
186
+ );
187
+ TimelineSeparator.displayName = "TimelineSeparator";
188
+
189
+ // ─── Timeline Connector (indicator + line container) ─────────────────────────
190
+
191
+ interface TimelineConnectorProps extends HTMLAttributes<HTMLDivElement> {
192
+ orientation?: "vertical" | "horizontal";
193
+ }
194
+
195
+ const TimelineConnector = forwardRef<HTMLDivElement, TimelineConnectorProps>(
196
+ ({ className, orientation = "vertical", children, ...props }, ref) => (
197
+ <div
198
+ ref={ref}
199
+ className={cn(
200
+ "flex items-center",
201
+ orientation === "vertical"
202
+ ? "flex-col"
203
+ : "flex-row",
204
+ className
205
+ )}
206
+ {...props}
207
+ >
208
+ {children}
209
+ </div>
210
+ )
211
+ );
212
+ TimelineConnector.displayName = "TimelineConnector";
213
+
214
+ // ─── Timeline Content ────────────────────────────────────────────────────────
215
+
216
+ interface TimelineContentProps extends HTMLAttributes<HTMLDivElement> {}
217
+
218
+ const TimelineContent = forwardRef<HTMLDivElement, TimelineContentProps>(
219
+ ({ className, ...props }, ref) => (
220
+ <div
221
+ ref={ref}
222
+ className={cn(
223
+ "flex flex-col gap-1 pb-6 pt-[2px]",
224
+ "group-last/item:pb-0",
225
+ className
226
+ )}
227
+ {...props}
228
+ />
229
+ )
230
+ );
231
+ TimelineContent.displayName = "TimelineContent";
232
+
233
+ // ─── Timeline Heading ────────────────────────────────────────────────────────
234
+
235
+ interface TimelineHeadingProps extends HTMLAttributes<HTMLDivElement> {}
236
+
237
+ const TimelineHeading = forwardRef<HTMLDivElement, TimelineHeadingProps>(
238
+ ({ className, ...props }, ref) => (
239
+ <div
240
+ ref={ref}
241
+ className={cn(
242
+ "typography-body-medium-medium text-content-presentation-global-primary",
243
+ className
244
+ )}
245
+ {...props}
246
+ />
247
+ )
248
+ );
249
+ TimelineHeading.displayName = "TimelineHeading";
250
+
251
+ // ─── Timeline Description ────────────────────────────────────────────────────
252
+
253
+ interface TimelineDescriptionProps extends HTMLAttributes<HTMLDivElement> {}
254
+
255
+ const TimelineDescription = forwardRef<HTMLDivElement, TimelineDescriptionProps>(
256
+ ({ className, ...props }, ref) => (
257
+ <div
258
+ ref={ref}
259
+ className={cn(
260
+ "typography-body-small-regular text-content-presentation-global-secondary",
261
+ className
262
+ )}
263
+ {...props}
264
+ />
265
+ )
266
+ );
267
+ TimelineDescription.displayName = "TimelineDescription";
268
+
269
+ // ─── Exports ─────────────────────────────────────────────────────────────────
270
+
271
+ export {
272
+ Timeline,
273
+ TimelineItem,
274
+ TimelineIndicator,
275
+ TimelineSeparator,
276
+ TimelineConnector,
277
+ TimelineContent,
278
+ TimelineHeading,
279
+ TimelineDescription,
280
+ timelineStyles,
281
+ indicatorStyles,
282
+ separatorStyles,
283
+ };
@@ -2,11 +2,15 @@ export type Themes = "dark" | "light" | "default";
2
2
 
3
3
  export type ButtonVariant =
4
4
  | "PrimeStyle"
5
- | "BlueSecStyle"
5
+ | "BluSecStyle"
6
6
  | "YelSecStyle"
7
7
  | "RedSecStyle"
8
8
  | "BorderStyle"
9
9
  | "PrimeContStyle"
10
- | "BlueContStyle"
11
- | "RedContStyle";
12
-
10
+ | "BluContStyle"
11
+ | "RedContStyle"
12
+ | "PrimeColStyle"
13
+ | "BluColStyle"
14
+ | "RedColStyle"
15
+ | "GreenColStyle"
16
+ | "YelColStyle";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torch-glare",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "files": [