@djangocfg/ui-core 2.1.426 → 2.1.427

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 (40) hide show
  1. package/package.json +4 -4
  2. package/src/components/data/badge/index.tsx +1 -1
  3. package/src/components/data/calendar/calendar.tsx +2 -2
  4. package/src/components/data/stat/index.tsx +5 -5
  5. package/src/components/data/status/index.tsx +3 -3
  6. package/src/components/data/table/index.tsx +30 -11
  7. package/src/components/feedback/banner/index.tsx +5 -4
  8. package/src/components/forms/button/index.tsx +15 -5
  9. package/src/components/forms/button-download/index.tsx +2 -2
  10. package/src/components/forms/checkbox/index.tsx +1 -1
  11. package/src/components/forms/editable/index.tsx +19 -19
  12. package/src/components/forms/input/index.tsx +44 -9
  13. package/src/components/forms/otp/index.tsx +1 -1
  14. package/src/components/forms/setting-row/index.tsx +359 -0
  15. package/src/components/forms/switch/index.tsx +1 -1
  16. package/src/components/forms/tags-input/index.tsx +1 -1
  17. package/src/components/forms/textarea/index.tsx +3 -8
  18. package/src/components/index.ts +2 -0
  19. package/src/components/navigation/dropdown-menu/index.tsx +3 -1
  20. package/src/components/navigation/menu/menu-builder.tsx +7 -2
  21. package/src/components/navigation/stepper/index.tsx +1 -1
  22. package/src/components/navigation/tabs/index.tsx +3 -3
  23. package/src/components/overlay/dialog/index.tsx +8 -3
  24. package/src/components/overlay/sheet/index.tsx +1 -1
  25. package/src/components/overlay/tooltip/index.tsx +4 -1
  26. package/src/components/select/multi-select-pro-async.tsx +2 -2
  27. package/src/components/select/multi-select-pro.tsx +2 -2
  28. package/src/components/specialized/copy/index.tsx +2 -2
  29. package/src/components/specialized/item/index.tsx +1 -1
  30. package/src/styles/README.md +49 -10
  31. package/src/styles/base.css +18 -1
  32. package/src/styles/theme/dark.css +40 -26
  33. package/src/styles/theme/light.css +13 -7
  34. package/src/styles/theme/tokens.css +1 -0
  35. package/src/styles/utilities/controls.css +12 -0
  36. package/src/styles/utilities/divider.css +23 -0
  37. package/src/styles/utilities.css +2 -0
  38. package/src/theme/ThemeSegmented.tsx +73 -0
  39. package/src/theme/index.ts +2 -0
  40. package/src/types/index.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.426",
3
+ "version": "2.1.427",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -106,7 +106,7 @@
106
106
  "check": "tsc --noEmit"
107
107
  },
108
108
  "peerDependencies": {
109
- "@djangocfg/i18n": "^2.1.426",
109
+ "@djangocfg/i18n": "^2.1.427",
110
110
  "consola": "^3.4.2",
111
111
  "lucide-react": "^0.545.0",
112
112
  "moment": "^2.30.1",
@@ -180,8 +180,8 @@
180
180
  "@chenglou/pretext": "*"
181
181
  },
182
182
  "devDependencies": {
183
- "@djangocfg/i18n": "^2.1.426",
184
- "@djangocfg/typescript-config": "^2.1.426",
183
+ "@djangocfg/i18n": "^2.1.427",
184
+ "@djangocfg/typescript-config": "^2.1.427",
185
185
  "@types/node": "^25.2.3",
186
186
  "@types/react": "^19.2.15",
187
187
  "@types/react-dom": "^19.2.3",
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { cn } from '../../../lib/utils';
5
5
 
6
6
  const badgeVariants = cva(
7
- "inline-flex items-center rounded-[var(--radius)] border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7
+ "inline-flex items-center rounded-[var(--radius)] border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-1 focus:ring-ring",
8
8
  {
9
9
  variants: {
10
10
  variant: {
@@ -66,7 +66,7 @@ function Calendar({
66
66
  defaultClassNames.dropdowns
67
67
  ),
68
68
  dropdown_root: cn(
69
- "has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-[var(--radius)] border",
69
+ "has-focus:border-ring border-input shadow-xs has-focus:ring-ring has-focus:ring-1 relative rounded-[var(--radius)] border",
70
70
  defaultClassNames.dropdown_root
71
71
  ),
72
72
  dropdown: cn(
@@ -197,7 +197,7 @@ function CalendarDayButton({
197
197
  data-range-end={modifiers.range_end}
198
198
  data-range-middle={modifiers.range_middle}
199
199
  className={cn(
200
- "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70",
200
+ "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-1 [&>span]:text-xs [&>span]:opacity-70",
201
201
  defaultClassNames.day,
202
202
  className
203
203
  )}
@@ -45,10 +45,10 @@ const statIndicatorVariants = cva(
45
45
  color: {
46
46
  default: "bg-muted text-muted-foreground",
47
47
  success:
48
- "border-green-500/20 bg-green-500/10 text-green-600 dark:text-green-400",
49
- info: "border-blue-500/20 bg-blue-500/10 text-blue-600 dark:text-blue-400",
48
+ "border-success-border bg-success-background text-success",
49
+ info: "border-info-border bg-info-background text-info",
50
50
  warning:
51
- "border-orange-500/20 bg-orange-500/10 text-orange-600 dark:text-orange-400",
51
+ "border-warning-border bg-warning-background text-warning",
52
52
  error: "border-destructive/20 bg-destructive/10 text-destructive",
53
53
  },
54
54
  },
@@ -102,8 +102,8 @@ function StatTrend({
102
102
  className={cn(
103
103
  "inline-flex items-center gap-1 font-medium text-xs [&_svg:not([class*='size-'])]:size-3 [&_svg]:pointer-events-none [&_svg]:shrink-0",
104
104
  {
105
- "text-green-600 dark:text-green-400": trend === "up",
106
- "text-red-600 dark:text-red-400": trend === "down",
105
+ "text-success": trend === "up",
106
+ "text-destructive": trend === "down",
107
107
  "text-muted-foreground": trend === "neutral" || !trend,
108
108
  },
109
109
  className,
@@ -12,12 +12,12 @@ const statusVariants = cva(
12
12
  default:
13
13
  "border-transparent bg-muted text-muted-foreground **:data-[slot=status-indicator]:bg-muted-foreground",
14
14
  success:
15
- "border-green-500/20 bg-green-500/10 text-green-600 **:data-[slot=status-indicator]:bg-green-600 dark:text-green-400 **:data-[slot=status-indicator]:dark:bg-green-400",
15
+ "border-success-border bg-success-background text-success **:data-[slot=status-indicator]:bg-success",
16
16
  error:
17
17
  "border-destructive/20 bg-destructive/10 text-destructive **:data-[slot=status-indicator]:bg-destructive",
18
18
  warning:
19
- "border-orange-500/20 bg-orange-500/10 text-orange-600 **:data-[slot=status-indicator]:bg-orange-600 dark:text-orange-400 **:data-[slot=status-indicator]:dark:bg-orange-400",
20
- info: "border-blue-500/20 bg-blue-500/10 text-blue-600 **:data-[slot=status-indicator]:bg-blue-600 dark:text-blue-400 **:data-[slot=status-indicator]:dark:bg-blue-400",
19
+ "border-warning-border bg-warning-background text-warning **:data-[slot=status-indicator]:bg-warning",
20
+ info: "border-info-border bg-info-background text-info **:data-[slot=status-indicator]:bg-info",
21
21
  },
22
22
  },
23
23
  defaultVariants: {
@@ -4,15 +4,21 @@ import { cn } from '../../../lib/utils';
4
4
 
5
5
  const Table = React.forwardRef<
6
6
  HTMLTableElement,
7
- React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }
8
- >(({ className, containerClassName, ...props }, ref) => (
9
- // Card-style frame: a bordered, rounded `--card` surface so the table
10
- // reads as a contained data panel in both themes (the page `--background`
11
- // alone gives no separation). `overflow-hidden` clips the header band and
12
- // row borders to the rounded corners.
13
- <div className="relative w-full overflow-hidden rounded-[var(--radius)] border border-border bg-card text-card-foreground">
14
- {/* Scroll container. Cap its height via `containerClassName`
15
- * (e.g. "max-h-80") to get a scrollable body with a sticky header. */}
7
+ React.HTMLAttributes<HTMLTableElement> & {
8
+ containerClassName?: string
9
+ /**
10
+ * Wrap the table in the card-style frame (border + radius + `--card`
11
+ * surface). Default `true` a standalone `<Table>` reads as a contained
12
+ * data panel against the page background. Set `false` when an outer
13
+ * container (e.g. `DataTable`) already supplies the frame, to avoid a
14
+ * double border and a radius that doesn't match the parent's.
15
+ */
16
+ frame?: boolean
17
+ }
18
+ >(({ className, containerClassName, frame = true, ...props }, ref) => {
19
+ // Scroll container. Cap its height via `containerClassName`
20
+ // (e.g. "max-h-80") to get a scrollable body with a sticky header.
21
+ const scroll = (
16
22
  <div className={cn("w-full overflow-auto", containerClassName)}>
17
23
  <table
18
24
  ref={ref}
@@ -20,8 +26,21 @@ const Table = React.forwardRef<
20
26
  {...props}
21
27
  />
22
28
  </div>
23
- </div>
24
- ))
29
+ )
30
+
31
+ if (!frame) return scroll
32
+
33
+ // Card-style frame: matches ui-core `Card` (`--radius-popover` corner +
34
+ // `--shadow-card`) so a standalone table reads as the same kind of contained
35
+ // panel as every other surface in the app — the page `--background` alone
36
+ // gives no separation. `overflow-hidden` clips the header band and row
37
+ // borders to the rounded corners.
38
+ return (
39
+ <div className="relative w-full overflow-hidden rounded-[var(--radius-popover)] border border-border bg-card text-card-foreground [box-shadow:var(--shadow-card)]">
40
+ {scroll}
41
+ </div>
42
+ )
43
+ })
25
44
  Table.displayName = "Table"
26
45
 
27
46
  const TableHeader = React.forwardRef<
@@ -306,12 +306,13 @@ const bannerVariants = cva(
306
306
  variants: {
307
307
  variant: {
308
308
  default: "bg-card text-card-foreground",
309
- info: "bg-blue-50 text-blue-900 dark:bg-blue-950 dark:text-blue-50",
309
+ info: "bg-info-background text-info-foreground border-info-border",
310
310
  success:
311
- "bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-50",
311
+ "bg-success-background text-success-foreground border-success-border",
312
312
  warning:
313
- "bg-yellow-50 text-yellow-900 dark:bg-yellow-950 dark:text-yellow-50",
314
- destructive: "bg-red-50 text-red-900 dark:bg-red-950 dark:text-red-50",
313
+ "bg-warning-background text-warning-foreground border-warning-border",
314
+ destructive:
315
+ "bg-destructive-background text-destructive-foreground border-destructive-border",
315
316
  },
316
317
  },
317
318
  defaultVariants: {
@@ -33,10 +33,20 @@ const buttonVariants = cva(
33
33
  huge: "h-14 rounded-[var(--radius)] px-10 text-lg",
34
34
  icon: "h-9 w-9",
35
35
  },
36
+ // Most variants ship a baked-in `shadow`/`shadow-sm` for a lifted look.
37
+ // `elevated={false}` flattens that (e.g. controls grouped inside a card —
38
+ // a table's pagination — where a per-button shadow fights the panel).
39
+ // `shadow-none` wins over the variant's shadow: both set `--tw-shadow`
40
+ // and this class is appended after the variant in the utility order.
41
+ elevated: {
42
+ true: "",
43
+ false: "shadow-none",
44
+ },
36
45
  },
37
46
  defaultVariants: {
38
47
  variant: "default",
39
48
  size: "default",
49
+ elevated: true,
40
50
  },
41
51
  }
42
52
  )
@@ -64,7 +74,7 @@ function filterIcons(children: React.ReactNode): React.ReactNode {
64
74
  }
65
75
 
66
76
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
67
- ({ className, variant, size, asChild = false, loading = false, onClick, children, disabled, ...props }, ref) => {
77
+ ({ className, variant, size, elevated, asChild = false, loading = false, onClick, children, disabled, ...props }, ref) => {
68
78
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
69
79
  if (!loading) {
70
80
  onClick?.(e);
@@ -76,7 +86,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
76
86
  if (asChild) {
77
87
  return (
78
88
  <Slot
79
- className={cn(buttonVariants({ variant, size, className }))}
89
+ className={cn(buttonVariants({ variant, size, elevated, className }))}
80
90
  ref={ref}
81
91
  onClick={onClick}
82
92
  {...props}
@@ -88,7 +98,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
88
98
 
89
99
  return (
90
100
  <button
91
- className={cn(buttonVariants({ variant, size, className }))}
101
+ className={cn(buttonVariants({ variant, size, elevated, className }))}
92
102
  ref={ref}
93
103
  onClick={handleClick}
94
104
  disabled={disabled || loading}
@@ -122,14 +132,14 @@ export interface ButtonLinkProps
122
132
  }
123
133
 
124
134
  const ButtonLink = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
125
- ({ className, variant, size, href, replace, scroll, prefetch, children, ...props }, ref) => {
135
+ ({ className, variant, size, elevated, href, replace, scroll, prefetch, children, ...props }, ref) => {
126
136
  return (
127
137
  <Link
128
138
  href={href}
129
139
  replace={replace}
130
140
  scroll={scroll}
131
141
  prefetch={prefetch}
132
- className={cn(buttonVariants({ variant, size, className }))}
142
+ className={cn(buttonVariants({ variant, size, elevated, className }))}
133
143
  ref={ref}
134
144
  {...props}
135
145
  >
@@ -248,9 +248,9 @@ const DownloadButton = React.forwardRef<HTMLButtonElement, DownloadButtonProps>(
248
248
  case 'downloading':
249
249
  return <Loader2 className="animate-spin" />
250
250
  case 'success':
251
- return <CheckCircle2 className="text-green-600" />
251
+ return <CheckCircle2 className="text-success" />
252
252
  case 'error':
253
- return <AlertCircle className="text-red-600" />
253
+ return <AlertCircle className="text-destructive" />
254
254
  default:
255
255
  return <Download />
256
256
  }
@@ -16,7 +16,7 @@ const Checkbox = React.forwardRef<
16
16
  className={cn(
17
17
  "peer h-[1.125rem] w-[1.125rem] shrink-0 rounded-[4px] border border-input bg-background shadow-none",
18
18
  "transition-colors duration-150",
19
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
19
+ "focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
20
20
  "disabled:cursor-not-allowed disabled:opacity-40",
21
21
  "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
22
22
  className
@@ -3,6 +3,7 @@
3
3
  import * as React from "react";
4
4
 
5
5
  import { cn } from "../../../lib/utils";
6
+ import { inputClass, TEXTAREA_CLASS } from "../input";
6
7
 
7
8
  // =============================================================================
8
9
  // Types
@@ -42,7 +43,10 @@ export interface EditableRootProps
42
43
 
43
44
  export interface EditablePreviewProps extends React.ComponentPropsWithoutRef<"span"> {}
44
45
 
45
- export interface EditableInputProps extends Omit<React.ComponentPropsWithoutRef<"input">, "value" | "defaultValue"> {}
46
+ export interface EditableInputProps extends Omit<React.ComponentPropsWithoutRef<"input">, "value" | "defaultValue"> {
47
+ /** Density — matches the standalone Input's `inputSize`. Default: 'default'. */
48
+ inputSize?: 'default' | 'sm';
49
+ }
46
50
 
47
51
  export interface EditableTextareaProps extends Omit<React.ComponentPropsWithoutRef<"textarea">, "value" | "defaultValue"> {}
48
52
 
@@ -270,7 +274,7 @@ EditablePreview.displayName = "EditablePreview";
270
274
  // =============================================================================
271
275
 
272
276
  const EditableInput = React.forwardRef<HTMLInputElement, EditableInputProps>(
273
- (props, ref) => {
277
+ ({ inputSize = 'default', className, onChange, onKeyDown, onBlur, onFocus, ...props }, ref) => {
274
278
  const context = useEditable("EditableInput");
275
279
 
276
280
  const composedRef = React.useCallback(
@@ -294,18 +298,19 @@ const EditableInput = React.forwardRef<HTMLInputElement, EditableInputProps>(
294
298
  value={context.value}
295
299
  disabled={context.disabled}
296
300
  aria-label="Edit value"
297
- className={cn(
298
- "flex h-10 rounded-[var(--radius)] border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors",
299
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
300
- "disabled:cursor-not-allowed disabled:opacity-50",
301
- props.className
302
- )}
301
+ // Share the global Input styling (crisp focus, proper surface) so
302
+ // inline-edit fields match standalone inputs everywhere.
303
+ // NOTE: spread `props` FIRST, then our handlers/className, so the
304
+ // styled className + wrapped handlers always win (a trailing spread
305
+ // would clobber them — that bug left the input unstyled/borderless).
306
+ {...props}
307
+ className={cn(inputClass(inputSize), className)}
303
308
  onChange={(event) => {
304
- props.onChange?.(event);
309
+ onChange?.(event);
305
310
  context.setValue(event.target.value);
306
311
  }}
307
312
  onKeyDown={(event) => {
308
- props.onKeyDown?.(event);
313
+ onKeyDown?.(event);
309
314
  switch (event.key) {
310
315
  case "Enter":
311
316
  event.preventDefault();
@@ -318,18 +323,17 @@ const EditableInput = React.forwardRef<HTMLInputElement, EditableInputProps>(
318
323
  }
319
324
  }}
320
325
  onBlur={(event) => {
321
- props.onBlur?.(event);
326
+ onBlur?.(event);
322
327
  if (context.submitOnBlur) {
323
328
  context.submit();
324
329
  }
325
330
  }}
326
331
  onFocus={(event) => {
327
- props.onFocus?.(event);
332
+ onFocus?.(event);
328
333
  if (context.selectAllOnFocus) {
329
334
  event.target.select();
330
335
  }
331
336
  }}
332
- {...props}
333
337
  />
334
338
  );
335
339
  }
@@ -365,12 +369,8 @@ const EditableTextarea = React.forwardRef<HTMLTextAreaElement, EditableTextareaP
365
369
  value={context.value}
366
370
  disabled={context.disabled}
367
371
  aria-label="Edit value"
368
- className={cn(
369
- "flex min-h-[80px] w-full rounded-[var(--radius)] border border-input bg-transparent px-3 py-2 text-sm shadow-sm transition-colors",
370
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
371
- "disabled:cursor-not-allowed disabled:opacity-50 resize-none",
372
- props.className
373
- )}
372
+ // Share the global Textarea styling (crisp focus, proper surface).
373
+ className={cn(TEXTAREA_CLASS, 'resize-none', props.className)}
374
374
  onChange={(event) => {
375
375
  props.onChange?.(event);
376
376
  context.setValue(event.target.value);
@@ -29,10 +29,51 @@ export interface InputProps extends React.ComponentProps<"input"> {
29
29
  * TTL in ms. Stored value expires after this duration.
30
30
  */
31
31
  storageTtl?: number;
32
+ /**
33
+ * Control height/density.
34
+ * - `default` — 40px (forms, standalone fields).
35
+ * - `sm` — 36px, smaller text (inline edits, dense rows, settings chips).
36
+ */
37
+ inputSize?: 'default' | 'sm';
32
38
  }
33
39
 
40
+ /**
41
+ * Shared input styling — single source so controlled/uncontrolled branches
42
+ * stay identical.
43
+ *
44
+ * Surface: `bg-input` (a token a notch off the page/panel) instead of
45
+ * `bg-transparent`, so the field reads as a real input on any surface (cards,
46
+ * dialogs) rather than a near-black hole.
47
+ *
48
+ * Focus: a CRISP thin accent outline — the border turns the `--ring` colour
49
+ * and a tight 1px solid ring of the same colour doubles it to a clean ~2px
50
+ * edge (NO blurry translucent halo). `:focus-visible` only, so mouse users on
51
+ * other controls don't get rings. This is the sharp Vercel/Linear look.
52
+ */
53
+ const INPUT_BASE =
54
+ "flex w-full rounded-[var(--radius)] border border-border bg-input shadow-sm transition-[color,background-color,border-color,box-shadow] file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
55
+
56
+ const INPUT_SIZE: Record<NonNullable<InputProps['inputSize']>, string> = {
57
+ default: "h-10 px-3 py-2 text-base md:text-sm",
58
+ sm: "h-9 px-2.5 py-1.5 text-sm",
59
+ };
60
+
61
+ /** Resolve the full input class for a given size. */
62
+ export const inputClass = (size: NonNullable<InputProps['inputSize']> = 'default') =>
63
+ cn(INPUT_BASE, INPUT_SIZE[size]);
64
+
65
+ /** Default-size input styling — kept as a named export for reuse (e.g. Editable). */
66
+ export const INPUT_CLASS = inputClass('default');
67
+
68
+ /**
69
+ * Textarea styling — shares the SAME border/surface/focus base as inputs (one
70
+ * source of truth) with multi-line height/padding instead of a fixed height.
71
+ */
72
+ export const TEXTAREA_CLASS = cn(INPUT_BASE, 'min-h-[60px] px-3 py-2 text-sm');
73
+
34
74
  const Input = React.forwardRef<HTMLInputElement, InputProps>(
35
- ({ className, type, storageKey, storageType, storageTtl, onChange, defaultValue, value, ...props }, ref) => {
75
+ ({ className, type, storageKey, storageType, storageTtl, inputSize = 'default', onChange, defaultValue, value, ...props }, ref) => {
76
+ const resolvedClass = cn(inputClass(inputSize), className);
36
77
  const storageOptions: UseStoredValueOptions | undefined =
37
78
  storageKey ? { storage: storageType ?? 'local', ttl: storageTtl } : undefined;
38
79
 
@@ -62,10 +103,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
62
103
  return (
63
104
  <input
64
105
  type={type}
65
- className={cn(
66
- "flex h-10 w-full rounded-[var(--radius)] border border-input bg-transparent px-3 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
67
- className
68
- )}
106
+ className={resolvedClass}
69
107
  ref={ref}
70
108
  value={value}
71
109
  onChange={storageKey ? handleChange : onChange}
@@ -81,10 +119,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
81
119
  return (
82
120
  <input
83
121
  type={type}
84
- className={cn(
85
- "flex h-10 w-full rounded-[var(--radius)] border border-input bg-transparent px-3 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
86
- className
87
- )}
122
+ className={cn(INPUT_CLASS, className)}
88
123
  ref={ref}
89
124
  defaultValue={storageKey && storedValue ? storedValue : defaultValue}
90
125
  onChange={storageKey ? handleChange : onChange}
@@ -164,7 +164,7 @@ export const OTPInput = React.forwardRef<
164
164
  ? `flex-1 min-w-0 aspect-square ${sizeTextVariants[resolvedSize]}`
165
165
  : sizeVariants[resolvedSize],
166
166
  error && 'border-destructive ring-destructive/20',
167
- success && 'border-green-500 ring-green-500/20',
167
+ success && 'border-success ring-success/20',
168
168
  slotClassName
169
169
  )}
170
170
  />