azamat-ui-kit-cli 0.3.13 → 0.3.14

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 (30) hide show
  1. package/dist/index.cjs +807 -107
  2. package/package.json +1 -1
  3. package/vendor/src/components/data-table/data-table-pagination.tsx +1 -1
  4. package/vendor/src/components/data-table/data-table-toolbar.tsx +13 -12
  5. package/vendor/src/components/data-table/data-table.tsx +14 -14
  6. package/vendor/src/components/display/smart-card.tsx +17 -14
  7. package/vendor/src/components/form/form-input.tsx +3 -1
  8. package/vendor/src/components/form/form-textarea.tsx +15 -12
  9. package/vendor/src/components/inputs/async-select.tsx +106 -47
  10. package/vendor/src/components/inputs/clearable-input.tsx +1 -0
  11. package/vendor/src/components/inputs/input-chrome.tsx +1 -1
  12. package/vendor/src/components/inputs/input-decorator.tsx +16 -8
  13. package/vendor/src/components/inputs/simple-select.tsx +28 -28
  14. package/vendor/src/components/layout/app-sidebar.tsx +454 -154
  15. package/vendor/src/components/layout/breadcrumbs.tsx +67 -22
  16. package/vendor/src/components/layout/sidebar-nav.tsx +316 -128
  17. package/vendor/src/components/overlay/confirm-dialog.tsx +31 -20
  18. package/vendor/src/components/ui/badge.tsx +33 -32
  19. package/vendor/src/components/ui/button.tsx +15 -17
  20. package/vendor/src/components/ui/card.tsx +26 -25
  21. package/vendor/src/components/ui/dialog.tsx +6 -3
  22. package/vendor/src/components/ui/dropdown-menu.tsx +9 -9
  23. package/vendor/src/components/ui/input-primitive.tsx +1 -1
  24. package/vendor/src/components/ui/input.tsx +105 -2
  25. package/vendor/src/components/ui/popover.tsx +1 -1
  26. package/vendor/src/components/ui/select.tsx +3 -3
  27. package/vendor/src/components/ui/table.tsx +4 -4
  28. package/vendor/src/components/ui/tabs.tsx +2 -2
  29. package/vendor/src/families/member-metadata.ts +3 -3
  30. package/vendor/templates/styles/globals.css +706 -6
@@ -6,28 +6,24 @@ import { cva, type VariantProps } from "class-variance-authority"
6
6
  import { cn } from "@/lib/utils"
7
7
 
8
8
  const badgeVariants = cva(
9
- "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-full border border-transparent font-semibold tracking-[0.01em] transition-[background-color,border-color,color,box-shadow,transform] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
9
+ "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-full border border-transparent font-semibold tracking-[0.01em] transition-[background-color,border-color,color,box-shadow,transform] focus-visible:ring-0 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive [&>svg]:pointer-events-none [&>svg]:size-3!",
10
10
  {
11
11
  variants: {
12
12
  variant: {
13
- default:
14
- "border-emerald-500/18 bg-emerald-500/14 text-emerald-700 shadow-none [a]:hover:bg-emerald-500/18 dark:border-emerald-400/18 dark:bg-emerald-400/16 dark:text-emerald-200",
15
- secondary:
16
- "border-border/70 bg-muted/68 text-foreground shadow-none [a]:hover:bg-muted/82 dark:bg-muted/38",
17
- destructive:
18
- "border-destructive/20 bg-destructive/10 text-destructive focus-visible:ring-destructive/20 [a]:hover:bg-destructive/14 dark:bg-destructive/16 dark:focus-visible:ring-destructive/40",
19
- outline:
20
- "border-border/72 bg-transparent text-foreground shadow-none [a]:hover:bg-muted/66 [a]:hover:text-foreground",
21
- ghost: "border-transparent bg-transparent text-muted-foreground shadow-none hover:bg-transparent hover:text-foreground",
22
- link: "border-transparent bg-transparent p-0 text-[color:var(--aui-brand-strong)] shadow-none underline-offset-4 hover:underline",
13
+ default: "",
14
+ secondary: "",
15
+ destructive: "",
16
+ outline: "",
17
+ ghost: "",
18
+ link: "border-transparent bg-transparent p-0 shadow-none underline-offset-4",
19
+ },
20
+ tone: {
21
+ neutral: "",
22
+ info: "",
23
+ success: "",
24
+ warning: "",
25
+ danger: "",
23
26
  },
24
- tone: {
25
- neutral: "",
26
- info: "border-blue-500/20 bg-blue-500/10 text-blue-700 dark:text-blue-300",
27
- success: "border-emerald-500/20 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
28
- warning: "border-amber-500/24 bg-amber-500/12 text-amber-700 dark:text-amber-300",
29
- danger: "border-destructive/20 bg-destructive/12 text-destructive dark:bg-destructive/20",
30
- },
31
27
  size: {
32
28
  sm: "min-h-5 px-2.5 py-0.5 text-[0.64rem]",
33
29
  default: "min-h-6 px-3 py-1 text-[0.68rem]",
@@ -53,7 +49,7 @@ type BadgeProps = useRender.ComponentProps<"span"> &
53
49
  rightIcon?: React.ReactNode
54
50
  }
55
51
 
56
- function Badge({
52
+ function Badge({
57
53
  className,
58
54
  variant = "default",
59
55
  tone = "neutral",
@@ -65,22 +61,27 @@ function Badge({
65
61
  render,
66
62
  ...props
67
63
  }: BadgeProps) {
68
- return useRender({
69
- defaultTagName: "span",
70
- props: mergeProps<"span">(
71
- {
72
- className: cn(badgeVariants({ variant, tone, size, dot }), className),
73
- children: (
74
- <>
75
- {dot ? <span data-slot="badge-dot" className="size-1.5 rounded-full bg-current opacity-75" /> : null}
64
+ return useRender({
65
+ defaultTagName: "span",
66
+ props: mergeProps<"span">(
67
+ {
68
+ className: cn(badgeVariants({ variant, tone, size, dot }), className),
69
+ children: (
70
+ <>
71
+ {dot ? <span data-slot="badge-dot" className="size-1.5 rounded-full bg-current opacity-75" /> : null}
76
72
  {leftIcon ? <span data-icon="inline-start" data-slot="badge-icon" className="inline-flex shrink-0 items-center">{leftIcon}</span> : null}
77
73
  {children ? <span data-slot="badge-label">{children}</span> : null}
78
74
  {rightIcon ? <span data-icon="inline-end" data-slot="badge-icon" className="inline-flex shrink-0 items-center">{rightIcon}</span> : null}
79
- </>
80
- ),
81
- },
82
- props
83
- ),
75
+ </>
76
+ ),
77
+ } as React.HTMLAttributes<HTMLSpanElement>,
78
+ {
79
+ "data-variant": variant ?? "default",
80
+ "data-tone": tone ?? "neutral",
81
+ "data-size": size ?? "default",
82
+ } as React.HTMLAttributes<HTMLSpanElement>,
83
+ props
84
+ ),
84
85
  render,
85
86
  state: {
86
87
  slot: "badge",
@@ -5,21 +5,17 @@ import { cva, type VariantProps } from "class-variance-authority"
5
5
  import { cn } from "@/lib/utils"
6
6
 
7
7
  const buttonVariants = cva(
8
- "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-semibold whitespace-nowrap transition-[transform,background-color,border-color,color,box-shadow,opacity] outline-none select-none focus-visible:border-ring/60 focus-visible:ring-2 focus-visible:ring-ring/35 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:cursor-not-allowed disabled:translate-y-0 disabled:shadow-none disabled:opacity-100 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:translate-y-0 aria-disabled:shadow-none aria-disabled:opacity-100 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/35 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ "group/button inline-flex shrink-0 items-center justify-center rounded-[var(--aui-control-radius,var(--radius-lg))] border border-[color:var(--aui-control-border-strong,var(--border))] bg-[color:var(--aui-control-surface,var(--background))] bg-clip-padding text-sm font-semibold text-foreground whitespace-nowrap shadow-[var(--aui-control-shadow,none)] transition-[transform,background-color,border-color,color,box-shadow,opacity] outline-none select-none hover:border-[color:var(--aui-control-hover-border,var(--ring))] hover:bg-[color:var(--aui-control-surface-hover,var(--muted))] focus-visible:ring-0 focus-visible:shadow-[var(--aui-control-shadow,none),0_0_0_1px_var(--aui-focus-ring,var(--ring)),0_0_0_5px_var(--aui-focus-ring-soft,transparent)] active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:cursor-not-allowed disabled:translate-y-0 disabled:border-[color:color-mix(in_oklch,var(--border),transparent_24%)] disabled:bg-[color:var(--aui-control-surface-disabled,var(--muted))] disabled:text-muted-foreground disabled:shadow-none disabled:opacity-100 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:translate-y-0 aria-disabled:opacity-100 aria-invalid:border-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9
9
  {
10
10
  variants: {
11
11
  variant: {
12
- default:
13
- "border-primary/20 bg-primary text-primary-foreground shadow-sm hover:-translate-y-px hover:bg-[color-mix(in_oklch,var(--primary),white_7%)] hover:shadow-md disabled:border-primary/10 disabled:bg-primary/45 disabled:text-primary-foreground/70 dark:disabled:bg-primary/34",
14
- outline:
15
- "border-border/80 bg-background text-foreground shadow-none hover:-translate-y-px hover:border-ring/38 hover:bg-accent/72 hover:text-foreground hover:shadow-sm aria-expanded:border-ring/38 aria-expanded:bg-accent/72 aria-expanded:text-foreground disabled:border-border/48 disabled:bg-muted/24 disabled:text-muted-foreground/70 dark:border-white/14 dark:bg-white/[0.045] dark:hover:bg-white/[0.08] dark:hover:text-foreground dark:disabled:bg-white/[0.035]",
16
- secondary:
17
- "border-border/70 bg-secondary text-secondary-foreground shadow-sm hover:-translate-y-px hover:border-border/86 hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] hover:text-secondary-foreground aria-expanded:bg-secondary aria-expanded:text-secondary-foreground disabled:border-border/44 disabled:bg-muted/28 disabled:text-muted-foreground/72 dark:bg-white/[0.08] dark:hover:bg-white/[0.12] dark:disabled:bg-white/[0.035]",
18
- ghost:
19
- "border-transparent bg-transparent text-foreground/88 shadow-none hover:border-border/48 hover:bg-accent/68 hover:text-foreground aria-expanded:border-border/48 aria-expanded:bg-accent/68 aria-expanded:text-foreground disabled:text-muted-foreground/64 dark:text-foreground/86 dark:hover:bg-white/[0.075] dark:disabled:text-muted-foreground/58",
20
- destructive:
21
- "border-destructive/20 bg-destructive text-destructive-foreground shadow-sm hover:-translate-y-px hover:bg-[color-mix(in_oklch,var(--destructive),white_7%)] hover:shadow-md focus-visible:border-destructive/45 focus-visible:ring-destructive/24 disabled:border-destructive/10 disabled:bg-destructive/42 disabled:text-destructive-foreground/70 dark:focus-visible:ring-destructive/35 dark:disabled:bg-destructive/34",
22
- link: "text-primary underline-offset-4 hover:underline",
12
+ default: "border-primary bg-primary text-primary-foreground shadow-[var(--aui-button-primary-shadow,var(--aui-control-shadow,none))] hover:bg-[color:color-mix(in_oklch,var(--primary),white_10%)]",
13
+ outline: "bg-[color:var(--aui-control-surface,var(--background))] text-foreground shadow-none",
14
+ secondary: "border-border bg-secondary text-secondary-foreground",
15
+ ghost: "border-transparent bg-transparent shadow-none hover:border-[color:var(--aui-control-border-strong,var(--border))]",
16
+ destructive: "border-destructive bg-destructive text-[color:var(--aui-danger-foreground,var(--primary-foreground))] shadow-[var(--aui-button-danger-shadow,var(--aui-control-shadow,none))] hover:bg-[color:color-mix(in_oklch,var(--destructive),white_10%)]",
17
+ warning: "border-[color:color-mix(in_oklch,var(--aui-warning,var(--primary)),transparent_64%)] bg-[color:color-mix(in_oklch,var(--aui-warning,var(--primary)),transparent_82%)] text-[color:var(--aui-warning-foreground,var(--foreground))]",
18
+ link: "rounded-none border-transparent bg-transparent p-0 shadow-none underline-offset-4",
23
19
  },
24
20
  size: {
25
21
  default:
@@ -67,11 +63,13 @@ function Button({
67
63
  const isDisabled = disabled || loading
68
64
 
69
65
  return (
70
- <ButtonPrimitive
71
- data-slot="button"
72
- data-loading={loading || undefined}
73
- disabled={isDisabled}
74
- aria-busy={loading || undefined}
66
+ <ButtonPrimitive
67
+ data-slot="button"
68
+ data-variant={variant ?? "default"}
69
+ data-size={size ?? "default"}
70
+ data-loading={loading || undefined}
71
+ disabled={isDisabled}
72
+ aria-busy={loading || undefined}
75
73
  className={cn(buttonVariants({ variant, size, className }))}
76
74
  {...props}
77
75
  >
@@ -8,13 +8,11 @@ const cardVariants = cva(
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
- default:
12
- "border-border/72 bg-card shadow-[0_10px_30px_rgba(15,23,42,0.05)] ring-1 ring-foreground/3",
13
- elevated:
14
- "border-border/68 bg-card shadow-[0_1px_2px_rgba(15,23,42,0.04),0_20px_46px_rgba(15,23,42,0.08)] ring-1 ring-foreground/4",
15
- outline: "border-border/72 bg-card shadow-none",
16
- soft: "border-border/40 bg-muted/28 shadow-none",
17
- ghost: "border-transparent bg-transparent shadow-none",
11
+ default: "",
12
+ elevated: "",
13
+ outline: "",
14
+ soft: "",
15
+ ghost: "border-transparent bg-transparent shadow-none ring-0",
18
16
  },
19
17
  size: {
20
18
  sm: "[--card-spacing:--spacing(4)] data-[has-footer=true]:pb-0",
@@ -26,21 +24,21 @@ const cardVariants = cva(
26
24
  default: "text-sm",
27
25
  comfortable: "text-base",
28
26
  },
29
- tone: {
30
- neutral: "",
31
- info: "border-blue-500/20 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),oklch(0.94_0.03_235)_32%),var(--card))]",
32
- success: "border-emerald-500/20 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),oklch(0.94_0.04_155)_34%),var(--card))]",
33
- warning: "border-amber-500/24 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),oklch(0.94_0.05_85)_34%),var(--card))]",
34
- danger: "border-destructive/24 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),var(--destructive)_10%),var(--card))]",
35
- },
36
- interactive: {
37
- true: "cursor-pointer hover:-translate-y-0.5 hover:border-ring/35 hover:shadow-[0_14px_34px_rgba(15,23,42,0.10)] focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/35",
38
- false: "",
39
- },
40
- selected: {
41
- true: "border-primary/40 ring-2 ring-primary/18",
42
- false: "",
43
- },
27
+ tone: {
28
+ neutral: "",
29
+ info: "",
30
+ success: "",
31
+ warning: "",
32
+ danger: "",
33
+ },
34
+ interactive: {
35
+ true: "cursor-pointer hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-0",
36
+ false: "",
37
+ },
38
+ selected: {
39
+ true: "",
40
+ false: "",
41
+ },
44
42
  disabled: {
45
43
  true: "pointer-events-none opacity-55",
46
44
  false: "",
@@ -75,9 +73,12 @@ function Card({
75
73
  return (
76
74
  <div
77
75
  data-slot="card"
78
- data-size={size ?? "default"}
79
- data-interactive={interactive || undefined}
80
- data-selected={selected || undefined}
76
+ data-size={size ?? "default"}
77
+ data-variant={variant ?? "default"}
78
+ data-tone={tone ?? "neutral"}
79
+ data-density={density ?? "default"}
80
+ data-interactive={interactive || undefined}
81
+ data-selected={selected || undefined}
81
82
  data-disabled={disabled || undefined}
82
83
  aria-disabled={disabled || undefined}
83
84
  tabIndex={interactive && !disabled ? tabIndex ?? 0 : tabIndex}
@@ -41,17 +41,20 @@ function DialogContent({
41
41
  className,
42
42
  children,
43
43
  showCloseButton = true,
44
+ size = "md",
44
45
  ...props
45
46
  }: DialogPrimitive.Popup.Props & {
46
47
  showCloseButton?: boolean
48
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | "full"
47
49
  }) {
48
50
  return (
49
51
  <DialogPortal>
50
52
  <DialogOverlay />
51
53
  <DialogPrimitive.Popup
52
54
  data-slot="dialog-content"
55
+ data-size={size}
53
56
  className={cn(
54
- "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-5 rounded-[var(--radius-3xl)] border border-border/72 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--popover),white_8%),var(--popover))] p-6 text-sm text-popover-foreground shadow-[0_28px_88px_color-mix(in_oklch,var(--foreground),transparent_86%)] ring-1 ring-foreground/6 backdrop-blur duration-100 outline-none sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
57
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-5 rounded-[var(--radius-3xl)] border p-6 text-sm text-popover-foreground backdrop-blur duration-100 outline-none data-[size=xs]:sm:max-w-sm data-[size=sm]:sm:max-w-md data-[size=md]:sm:max-w-lg data-[size=lg]:sm:max-w-2xl data-[size=xl]:sm:max-w-4xl data-[size=full]:h-[min(92vh,56rem)] data-[size=full]:max-w-[min(96vw,84rem)] data-[size=full]:grid-rows-[auto_1fr] data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
55
58
  className
56
59
  )}
57
60
  {...props}
@@ -96,10 +99,10 @@ function DialogFooter({
96
99
  showCloseButton?: boolean
97
100
  }) {
98
101
  return (
99
- <div
102
+ <div
100
103
  data-slot="dialog-footer"
101
104
  className={cn(
102
- "-mx-6 -mb-6 flex flex-col-reverse gap-2 rounded-b-[var(--radius-3xl)] border-t border-border/58 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--muted),transparent_38%),color-mix(in_oklch,var(--muted),transparent_18%))] p-5 sm:flex-row sm:justify-end",
105
+ "-mx-6 -mb-6 flex flex-col-reverse gap-2 rounded-b-[var(--radius-3xl)] border-t p-5 sm:flex-row sm:justify-end",
103
106
  className
104
107
  )}
105
108
  {...props}
@@ -39,7 +39,7 @@ function DropdownMenuContent({
39
39
  >
40
40
  <MenuPrimitive.Popup
41
41
  data-slot="dropdown-menu-content"
42
- className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius-2xl)] border border-border/70 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--popover),white_8%),var(--popover))] p-2 text-popover-foreground shadow-[0_18px_54px_color-mix(in_oklch,var(--foreground),transparent_88%)] ring-1 ring-foreground/6 backdrop-blur duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className)}
42
+ className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-48 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius-2xl)] border p-2 text-popover-foreground backdrop-blur duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className)}
43
43
  {...props}
44
44
  />
45
45
  </MenuPrimitive.Positioner>
@@ -86,7 +86,7 @@ function DropdownMenuItem({
86
86
  data-inset={inset}
87
87
  data-variant={variant}
88
88
  className={cn(
89
- "group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent px-3 py-2.5 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] focus:border-border/65 focus:bg-accent/65 focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[highlighted]:border-border/58 data-[highlighted]:bg-accent/58 data-[highlighted]:text-foreground data-[highlighted]:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
89
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent px-3 py-2.5 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] data-inset:pl-8 data-[variant=destructive]:text-destructive data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
90
90
  className
91
91
  )}
92
92
  {...props}
@@ -111,7 +111,7 @@ function DropdownMenuSubTrigger({
111
111
  data-slot="dropdown-menu-sub-trigger"
112
112
  data-inset={inset}
113
113
  className={cn(
114
- "flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent px-3 py-2.5 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] focus:border-border/65 focus:bg-accent/65 focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-popup-open:border-border/65 data-popup-open:bg-accent/65 data-popup-open:text-accent-foreground data-open:border-border/65 data-open:bg-accent/65 data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
114
+ "flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent px-3 py-2.5 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
115
115
  className
116
116
  )}
117
117
  {...props}
@@ -131,10 +131,10 @@ function DropdownMenuSubContent({
131
131
  ...props
132
132
  }: React.ComponentProps<typeof DropdownMenuContent>) {
133
133
  return (
134
- <DropdownMenuContent
135
- data-slot="dropdown-menu-sub-content"
136
- className={cn("w-auto min-w-[128px] rounded-[var(--radius-2xl)] bg-[linear-gradient(180deg,color-mix(in_oklch,var(--popover),white_8%),var(--popover))] p-2 text-popover-foreground shadow-[0_18px_48px_color-mix(in_oklch,var(--foreground),transparent_88%)] ring-1 ring-foreground/6 backdrop-blur duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
137
- align={align}
134
+ <DropdownMenuContent
135
+ data-slot="dropdown-menu-sub-content"
136
+ className={cn("w-auto min-w-[128px] rounded-[var(--radius-2xl)] p-2 text-popover-foreground backdrop-blur duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
137
+ align={align}
138
138
  alignOffset={alignOffset}
139
139
  side={side}
140
140
  sideOffset={sideOffset}
@@ -157,7 +157,7 @@ function DropdownMenuCheckboxItem({
157
157
  data-slot="dropdown-menu-checkbox-item"
158
158
  data-inset={inset}
159
159
  className={cn(
160
- "relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] focus:border-border/65 focus:bg-accent/65 focus:text-accent-foreground focus:**:text-accent-foreground data-[highlighted]:border-border/58 data-[highlighted]:bg-accent/58 data-[highlighted]:text-foreground data-[highlighted]:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
160
+ "relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
161
161
  className
162
162
  )}
163
163
  checked={checked}
@@ -199,7 +199,7 @@ function DropdownMenuRadioItem({
199
199
  data-slot="dropdown-menu-radio-item"
200
200
  data-inset={inset}
201
201
  className={cn(
202
- "relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] focus:border-border/65 focus:bg-accent/65 focus:text-accent-foreground focus:**:text-accent-foreground data-[highlighted]:border-border/58 data-[highlighted]:bg-accent/58 data-[highlighted]:text-foreground data-[highlighted]:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)] data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
202
+ "relative flex cursor-default items-center gap-2 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow] data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
203
203
  className
204
204
  )}
205
205
  {...props}
@@ -13,7 +13,7 @@ function InputPrimitive({ className, type, ...props }: InputPrimitiveProps) {
13
13
  type={type}
14
14
  data-slot="input"
15
15
  className={cn(
16
- "h-11 w-full min-w-0 rounded-[min(var(--radius-xl),18px)] border border-border/66 bg-background px-4 py-2.5 text-sm text-foreground shadow-[0_1px_2px_rgba(15,23,42,0.02)] transition-[background-color,border-color,box-shadow,color] outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground/68 hover:border-border/82 hover:bg-background focus-visible:border-ring/48 focus-visible:bg-background focus-visible:shadow-[0_0_0_1px_color-mix(in_oklch,var(--ring),transparent_56%),0_0_0_5px_color-mix(in_oklch,var(--ring),transparent_84%)] focus-visible:ring-0 disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-border/48 disabled:bg-muted/22 disabled:text-muted-foreground/72 disabled:shadow-none disabled:opacity-100 read-only:border-border/58 read-only:bg-muted/16 read-only:text-foreground/84 read-only:shadow-none aria-invalid:border-destructive/60 aria-invalid:shadow-[0_0_0_1px_color-mix(in_oklch,var(--destructive),transparent_64%),0_0_0_5px_color-mix(in_oklch,var(--destructive),transparent_88%)] dark:border-white/10 dark:bg-white/[0.045] dark:hover:border-white/14 dark:hover:bg-white/[0.06] dark:focus-visible:bg-white/[0.07] dark:disabled:bg-white/[0.032] dark:read-only:bg-white/[0.028] dark:aria-invalid:border-destructive/50 dark:aria-invalid:shadow-[0_0_0_1px_color-mix(in_oklch,var(--destructive),transparent_68%),0_0_0_5px_color-mix(in_oklch,var(--destructive),transparent_88%)]",
16
+ "h-11 w-full min-w-0 rounded-[var(--aui-control-radius,var(--radius-xl))] border border-[color:var(--aui-control-border-strong,var(--input))] bg-[color:var(--aui-control-surface,var(--background))] px-4 py-2.5 text-sm text-foreground shadow-[var(--aui-control-shadow,none)] transition-[background-color,border-color,box-shadow,color] outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground/72 hover:border-[color:var(--aui-control-hover-border,var(--ring))] hover:bg-[color:var(--aui-control-surface-hover,var(--background))] focus-visible:border-[color:var(--ring)] focus-visible:ring-0 focus-visible:shadow-[var(--aui-control-shadow,none),0_0_0_1px_var(--aui-focus-ring,var(--ring)),0_0_0_5px_var(--aui-focus-ring-soft,transparent)] disabled:pointer-events-none disabled:cursor-not-allowed disabled:border-[color:color-mix(in_oklch,var(--border),transparent_18%)] disabled:bg-[color:var(--aui-control-surface-disabled,var(--muted))] disabled:opacity-100 read-only:bg-[color:var(--aui-control-surface-readonly,var(--muted))]",
17
17
  className
18
18
  )}
19
19
  {...props}
@@ -1,6 +1,7 @@
1
1
  import * as React from "react"
2
2
 
3
3
  import { InputPrimitive, type InputPrimitiveProps } from "@/components/ui/input-primitive"
4
+ import { InputDecorator } from "@/components/inputs/input-decorator"
4
5
  import { ClearableInput, type ClearableInputProps } from "@/components/inputs/clearable-input"
5
6
  import { DateInput, type DateInputProps } from "@/components/inputs/date-input"
6
7
  import { DateRangeInput, type DateRangeInputProps } from "@/components/inputs/date-range-input"
@@ -11,6 +12,7 @@ import { PasswordInput, type PasswordInputProps } from "@/components/inputs/pass
11
12
  import { PhoneInput, type PhoneInputProps } from "@/components/inputs/phone-input"
12
13
  import { QuantityInput, type QuantityInputProps } from "@/components/inputs/quantity-input"
13
14
  import { SearchInput, type SearchInputProps } from "@/components/inputs/search-input"
15
+ import { cn } from "@/lib/utils"
14
16
 
15
17
  export type InputKind =
16
18
  | "text"
@@ -28,6 +30,17 @@ export type InputKind =
28
30
  export type InputTextProps = Omit<InputPrimitiveProps, "value"> & {
29
31
  kind?: "text"
30
32
  value?: string | number | readonly string[] | null
33
+ leading?: React.ReactNode
34
+ trailing?: React.ReactNode
35
+ helperText?: React.ReactNode
36
+ errorText?: React.ReactNode
37
+ showCharacterCount?: boolean
38
+ countFormatter?: (currentLength: number, maxLength?: number) => React.ReactNode
39
+ wrapperClassName?: string
40
+ inputClassName?: string
41
+ helperClassName?: string
42
+ leadingPointerEvents?: boolean
43
+ trailingPointerEvents?: boolean
31
44
  }
32
45
 
33
46
  export type InputClearableProps = Omit<ClearableInputProps, "kind"> & {
@@ -85,6 +98,13 @@ export type InputProps =
85
98
  | InputDateProps
86
99
  | InputDateRangeProps
87
100
 
101
+ function getInputTextLength(value: InputTextProps["value"] | InputTextProps["defaultValue"]) {
102
+ if (typeof value === "string") return value.length
103
+ if (typeof value === "number") return String(value).length
104
+ if (Array.isArray(value)) return value.join("").length
105
+ return 0
106
+ }
107
+
88
108
  const Input = React.forwardRef<HTMLInputElement | HTMLDivElement, InputProps>((props, ref) => {
89
109
  const kind = props.kind ?? "text"
90
110
 
@@ -174,17 +194,100 @@ const Input = React.forwardRef<HTMLInputElement | HTMLDivElement, InputProps>((p
174
194
  }
175
195
 
176
196
  const inputProps = props as Omit<InputTextProps, "kind">
177
- const { value, defaultValue, ...restInputProps } = inputProps
197
+ const {
198
+ value,
199
+ defaultValue,
200
+ leading,
201
+ trailing,
202
+ helperText,
203
+ errorText,
204
+ showCharacterCount = false,
205
+ countFormatter,
206
+ wrapperClassName,
207
+ inputClassName,
208
+ helperClassName,
209
+ leadingPointerEvents = false,
210
+ trailingPointerEvents = true,
211
+ onChange,
212
+ maxLength,
213
+ className,
214
+ ...restInputProps
215
+ } = inputProps
216
+ const isControlled = value !== undefined
217
+ const [uncontrolledLength, setUncontrolledLength] = React.useState(() => getInputTextLength(defaultValue))
218
+ const currentLength = isControlled ? getInputTextLength(value) : uncontrolledLength
219
+ const helperMessage = errorText ?? helperText
220
+ const helperTone = errorText ? "text-destructive" : "text-muted-foreground"
178
221
 
179
- return (
222
+ const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
223
+ if (!isControlled) {
224
+ setUncontrolledLength(event.target.value.length)
225
+ }
226
+ onChange?.(event)
227
+ }
228
+
229
+ const textInput = leading || trailing ? (
230
+ <InputDecorator
231
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
232
+ value={value ?? undefined}
233
+ defaultValue={value === undefined ? defaultValue : undefined}
234
+ type={restInputProps.type ?? "text"}
235
+ leading={leading}
236
+ trailing={trailing}
237
+ leadingPointerEvents={leadingPointerEvents}
238
+ trailingPointerEvents={trailingPointerEvents}
239
+ wrapperClassName={wrapperClassName}
240
+ inputClassName={inputClassName}
241
+ className={className}
242
+ onChange={handleChange}
243
+ maxLength={maxLength}
244
+ {...(restInputProps as React.ComponentProps<typeof InputDecorator>)}
245
+ />
246
+ ) : (
180
247
  <InputPrimitive
181
248
  ref={ref as React.ForwardedRef<HTMLInputElement>}
182
249
  value={value ?? undefined}
183
250
  defaultValue={value === undefined ? defaultValue : undefined}
184
251
  type={restInputProps.type ?? "text"}
252
+ className={cn(inputClassName, className)}
253
+ onChange={handleChange}
254
+ maxLength={maxLength}
185
255
  {...(restInputProps as React.ComponentProps<typeof InputPrimitive>)}
186
256
  />
187
257
  )
258
+
259
+ if (!helperMessage && !showCharacterCount) {
260
+ return textInput
261
+ }
262
+
263
+ return (
264
+ <div data-slot="input-field" className="grid gap-1.5">
265
+ {textInput}
266
+ <div
267
+ data-slot="input-meta"
268
+ className="flex items-start justify-between gap-3 px-1"
269
+ >
270
+ <div
271
+ data-slot="input-helper"
272
+ className={cn("min-w-0 text-xs leading-5", helperTone, helperClassName)}
273
+ >
274
+ {helperMessage}
275
+ </div>
276
+ {showCharacterCount ? (
277
+ <div
278
+ data-slot="input-count"
279
+ className={cn(
280
+ "shrink-0 text-[11px] font-medium tabular-nums",
281
+ errorText ? "text-destructive" : "text-muted-foreground"
282
+ )}
283
+ >
284
+ {countFormatter?.(currentLength, maxLength) ??
285
+ (typeof maxLength === "number" ? `${currentLength}/${maxLength}` : currentLength)}
286
+ </div>
287
+ ) : null}
288
+ </div>
289
+ </div>
290
+ )
188
291
  })
189
292
  Input.displayName = "Input"
190
293
 
@@ -37,7 +37,7 @@ function PopoverContent({
37
37
  <PopoverPrimitive.Popup
38
38
  data-slot="popover-content"
39
39
  className={cn(
40
- "z-50 flex w-80 origin-(--transform-origin) flex-col gap-3 rounded-[var(--radius-2xl)] border border-border/70 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--popover),white_8%),var(--popover))] p-4 text-sm text-popover-foreground shadow-[0_18px_54px_color-mix(in_oklch,var(--foreground),transparent_88%)] ring-1 ring-foreground/6 backdrop-blur outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
40
+ "z-50 flex w-80 origin-(--transform-origin) flex-col gap-3 rounded-[var(--radius-2xl)] border p-4 text-sm text-popover-foreground outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
41
41
  className
42
42
  )}
43
43
  {...props}
@@ -41,7 +41,7 @@ function SelectTrigger({
41
41
  data-slot="select-trigger"
42
42
  data-size={size}
43
43
  className={cn(
44
- "flex w-fit items-center justify-between gap-2 rounded-[min(var(--radius-xl),16px)] border border-input/88 bg-background py-2 pr-3 pl-3 text-sm whitespace-nowrap text-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.10)] transition-[background-color,border-color,box-shadow,color] outline-none select-none hover:border-ring/30 hover:bg-background focus-visible:border-ring focus-visible:bg-background focus-visible:shadow-[0_0_0_1px_color-mix(in_oklch,var(--ring),transparent_45%),0_10px_24px_rgba(15,23,42,0.08)] focus-visible:ring-3 focus-visible:ring-ring/45 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 data-[size=sm]:rounded-[min(var(--radius-lg),12px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:border-white/12 dark:bg-white/[0.04] dark:hover:bg-white/[0.08] dark:focus-visible:bg-white/[0.08] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
44
+ "flex w-fit items-center justify-between gap-2 rounded-[min(var(--radius-xl),16px)] border py-2 pr-3 pl-3 text-sm whitespace-nowrap text-foreground transition-[background-color,border-color,box-shadow,color] outline-none select-none focus-visible:ring-0 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 data-[size=sm]:rounded-[min(var(--radius-lg),12px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
45
45
  className
46
46
  )}
47
47
  {...props}
@@ -83,7 +83,7 @@ function SelectContent({
83
83
  <SelectPrimitive.Popup
84
84
  data-slot="select-content"
85
85
  data-align-trigger={alignItemWithTrigger}
86
- className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-44 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius-2xl)] border border-border/76 bg-popover p-1.5 text-popover-foreground shadow-[0_18px_48px_rgba(15,23,42,0.14)] ring-1 ring-foreground/6 backdrop-blur duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className)}
86
+ className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-44 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius-2xl)] border p-1.5 text-popover-foreground backdrop-blur duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className)}
87
87
  {...props}
88
88
  >
89
89
  <SelectScrollUpButton />
@@ -117,7 +117,7 @@ function SelectItem({
117
117
  <SelectPrimitive.Item
118
118
  data-slot="select-item"
119
119
  className={cn(
120
- "relative flex w-full cursor-default items-center gap-1.5 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow,transform] focus:border-border/80 focus:bg-accent/76 focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[selected]:border-primary/16 data-[selected]:bg-primary/8 data-[selected]:text-foreground data-[selected]:shadow-none data-disabled:pointer-events-none data-disabled:opacity-50 dark:data-[selected]:border-white/8 dark:data-[selected]:bg-white/6 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
120
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-[min(var(--radius-xl),16px)] border border-transparent py-2.5 pr-9 pl-3 text-sm outline-hidden select-none transition-[background-color,border-color,color,box-shadow,transform] data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
121
121
  className
122
122
  )}
123
123
  {...props}
@@ -11,7 +11,7 @@ function Table({ className, containerClassName, ...props }: TableProps) {
11
11
  <div
12
12
  data-slot="table-container"
13
13
  className={cn(
14
- "relative w-full overflow-x-auto rounded-[var(--radius-2xl)] border border-border/75 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--card),white_12%),var(--card))] shadow-sm ring-1 ring-foreground/5",
14
+ "relative w-full overflow-x-auto rounded-[var(--radius-2xl)] border",
15
15
  containerClassName
16
16
  )}
17
17
  >
@@ -28,7 +28,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
28
28
  return (
29
29
  <thead
30
30
  data-slot="table-header"
31
- className={cn("bg-muted/30 [&_tr]:border-b", className)}
31
+ className={cn("[&_tr]:border-b", className)}
32
32
  {...props}
33
33
  />
34
34
  )
@@ -49,7 +49,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
49
49
  <tfoot
50
50
  data-slot="table-footer"
51
51
  className={cn(
52
- "border-t bg-muted/58 font-medium [&>tr]:last:border-b-0",
52
+ "border-t font-medium [&>tr]:last:border-b-0",
53
53
  className
54
54
  )}
55
55
  {...props}
@@ -62,7 +62,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
62
62
  <tr
63
63
  data-slot="table-row"
64
64
  className={cn(
65
- "border-b border-border/70 transition-colors hover:bg-muted/28 has-aria-expanded:bg-muted/36 data-[state=selected]:bg-primary/8",
65
+ "border-b transition-colors",
66
66
  className
67
67
  )}
68
68
  {...props}
@@ -13,7 +13,7 @@ const TabsList = React.forwardRef<
13
13
  ref={ref}
14
14
  data-slot="tabs-list"
15
15
  className={cn(
16
- "inline-flex min-h-11 items-center justify-center gap-1 rounded-[var(--radius-2xl)] border border-border/72 bg-muted/34 p-1 text-muted-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.04),0_8px_24px_rgba(15,23,42,0.04)] backdrop-blur",
16
+ "inline-flex min-h-11 items-center justify-center gap-1 rounded-[var(--radius-2xl)] border p-1 text-muted-foreground backdrop-blur",
17
17
  className
18
18
  )}
19
19
  {...props}
@@ -29,7 +29,7 @@ const TabsTrigger = React.forwardRef<
29
29
  ref={ref}
30
30
  data-slot="tabs-trigger"
31
31
  className={cn(
32
- "inline-flex min-h-9 items-center justify-center whitespace-nowrap rounded-[calc(var(--radius-xl)-2px)] border border-transparent px-3.5 py-1.5 text-sm font-medium text-muted-foreground ring-offset-background transition-[background-color,color,box-shadow,border-color] hover:bg-background/52 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[selected]:border-border/72 data-[selected]:bg-background data-[selected]:text-foreground data-[selected]:shadow-[0_1px_0_rgba(255,255,255,0.18),0_8px_20px_rgba(15,23,42,0.06)] dark:data-[selected]:border-white/10 dark:data-[selected]:bg-white/7 dark:data-[selected]:shadow-[0_1px_0_rgba(255,255,255,0.06),0_10px_24px_rgba(0,0,0,0.18)]",
32
+ "inline-flex min-h-9 items-center justify-center whitespace-nowrap rounded-[calc(var(--radius-xl)-2px)] border border-transparent px-3.5 py-1.5 text-sm font-medium ring-offset-background transition-[background-color,color,box-shadow,border-color] focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50",
33
33
  className
34
34
  )}
35
35
  {...props}
@@ -242,7 +242,7 @@ export const componentMemberMetadata: ComponentMemberMetadata[] = [
242
242
  section: "presets",
243
243
  maturity: "member",
244
244
  summary: "Remote-search single select.",
245
- useWhen: "Use when options are loaded on demand from a server or large local source.",
245
+ useWhen: "Use when options are loaded on demand from a server or large local source, including cases where search and creation should stay in one remote-aware flow.",
246
246
  },
247
247
  {
248
248
  component: "AsyncMultiSelect",
@@ -250,7 +250,7 @@ export const componentMemberMetadata: ComponentMemberMetadata[] = [
250
250
  section: "presets",
251
251
  maturity: "member",
252
252
  summary: "Remote-search multi select.",
253
- useWhen: "Use when users need to pick many remote items and review selected tags inline.",
253
+ useWhen: "Use when users need to pick many remote items, review selected tags inline, or create new items without leaving the remote-search flow.",
254
254
  },
255
255
  {
256
256
  component: "Combobox",
@@ -258,7 +258,7 @@ export const componentMemberMetadata: ComponentMemberMetadata[] = [
258
258
  section: "presets",
259
259
  maturity: "member",
260
260
  summary: "Searchable command-style select surface.",
261
- useWhen: "Use when fast keyboard filtering matters more than strict select semantics.",
261
+ useWhen: "Use when fast local keyboard filtering matters more than strict select semantics; avoid using it as the main remote or creatable select path.",
262
262
  },
263
263
  {
264
264
  component: "FormSelect",