azamat-ui-kit-cli 0.2.2 → 0.3.4

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 (103) hide show
  1. package/README.md +11 -0
  2. package/dist/index.cjs +452 -0
  3. package/package.json +2 -2
  4. package/vendor/src/components/actions/action-menu.tsx +21 -18
  5. package/vendor/src/components/calendar/calendar.tsx +153 -102
  6. package/vendor/src/components/calendar/date-picker.tsx +24 -14
  7. package/vendor/src/components/calendar/date-range-picker.tsx +137 -58
  8. package/vendor/src/components/charts/charts.tsx +32 -21
  9. package/vendor/src/components/command/command-palette.tsx +68 -57
  10. package/vendor/src/components/data-table/data-table-bulk-actions.tsx +23 -20
  11. package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +21 -10
  12. package/vendor/src/components/data-table/data-table-pagination.tsx +6 -6
  13. package/vendor/src/components/data-table/data-table-toolbar.tsx +72 -44
  14. package/vendor/src/components/data-table/data-table.tsx +15 -11
  15. package/vendor/src/components/data-table/table-export-menu.tsx +1 -1
  16. package/vendor/src/components/data-table/table-import-button.tsx +1 -1
  17. package/vendor/src/components/display/data-state.tsx +20 -8
  18. package/vendor/src/components/display/index.ts +19 -15
  19. package/vendor/src/components/display/metric-card.tsx +35 -0
  20. package/vendor/src/components/display/progress-circle.tsx +24 -0
  21. package/vendor/src/components/display/smart-card.tsx +49 -27
  22. package/vendor/src/components/display/status-dot.tsx +45 -0
  23. package/vendor/src/components/display/user-card.tsx +30 -0
  24. package/vendor/src/components/feedback/alert.tsx +21 -11
  25. package/vendor/src/components/feedback/empty-state.tsx +2 -2
  26. package/vendor/src/components/feedback/loading-state.tsx +2 -2
  27. package/vendor/src/components/feedback/page-state.tsx +19 -15
  28. package/vendor/src/components/feedback/status-badge.tsx +43 -43
  29. package/vendor/src/components/form/form-app-input.tsx +147 -0
  30. package/vendor/src/components/form/form-date-input.tsx +16 -19
  31. package/vendor/src/components/form/form-field-shell.tsx +11 -8
  32. package/vendor/src/components/form/form-field-utils.ts +76 -0
  33. package/vendor/src/components/form/form-input.tsx +423 -44
  34. package/vendor/src/components/form/form-number-input.tsx +16 -15
  35. package/vendor/src/components/form/form-phone-input.tsx +15 -9
  36. package/vendor/src/components/form/form-search-input.tsx +16 -19
  37. package/vendor/src/components/form/form-select.tsx +4 -3
  38. package/vendor/src/components/form/public.ts +16 -14
  39. package/vendor/src/components/form/smart-form-shell.tsx +13 -12
  40. package/vendor/src/components/inputs/app-input.tsx +27 -0
  41. package/vendor/src/components/inputs/async-select.tsx +113 -84
  42. package/vendor/src/components/inputs/clearable-input.tsx +81 -61
  43. package/vendor/src/components/inputs/date-input.tsx +21 -17
  44. package/vendor/src/components/inputs/date-range-input.tsx +10 -10
  45. package/vendor/src/components/inputs/index.ts +1 -0
  46. package/vendor/src/components/inputs/input-decorator.tsx +101 -57
  47. package/vendor/src/components/inputs/masked-input.tsx +20 -20
  48. package/vendor/src/components/inputs/money-input.tsx +2 -2
  49. package/vendor/src/components/inputs/number-input.tsx +29 -19
  50. package/vendor/src/components/inputs/password-input.tsx +82 -45
  51. package/vendor/src/components/inputs/phone-input.tsx +24 -2
  52. package/vendor/src/components/inputs/quantity-input.tsx +2 -2
  53. package/vendor/src/components/inputs/search-input.tsx +54 -3
  54. package/vendor/src/components/inputs/simple-select.tsx +110 -22
  55. package/vendor/src/components/layout/app-shell.tsx +2 -2
  56. package/vendor/src/components/layout/index.ts +5 -4
  57. package/vendor/src/components/layout/page-header.tsx +79 -35
  58. package/vendor/src/components/layout/public.ts +12 -10
  59. package/vendor/src/components/layout/section-header.tsx +56 -0
  60. package/vendor/src/components/layout/stack.tsx +106 -0
  61. package/vendor/src/components/layout/stat-card.tsx +66 -29
  62. package/vendor/src/components/navigation/index.ts +1 -0
  63. package/vendor/src/components/navigation/nav-tabs.tsx +60 -0
  64. package/vendor/src/components/navigation/page-tabs.tsx +41 -26
  65. package/vendor/src/components/navigation/pagination.tsx +14 -10
  66. package/vendor/src/components/overlay/alert-dialog.tsx +65 -0
  67. package/vendor/src/components/overlay/drawer.tsx +71 -0
  68. package/vendor/src/components/overlay/index.ts +4 -2
  69. package/vendor/src/components/patterns/data-view.tsx +13 -8
  70. package/vendor/src/components/ui/badge.tsx +96 -52
  71. package/vendor/src/components/ui/button.tsx +99 -61
  72. package/vendor/src/components/ui/card.tsx +84 -25
  73. package/vendor/src/components/ui/checkbox.tsx +68 -68
  74. package/vendor/src/components/ui/command.tsx +32 -32
  75. package/vendor/src/components/ui/dialog.tsx +135 -138
  76. package/vendor/src/components/ui/dropdown-menu.tsx +21 -21
  77. package/vendor/src/components/ui/hover-card.tsx +49 -0
  78. package/vendor/src/components/ui/input-primitive.tsx +24 -0
  79. package/vendor/src/components/ui/input.tsx +191 -20
  80. package/vendor/src/components/ui/kbd.tsx +33 -0
  81. package/vendor/src/components/ui/popover.tsx +11 -11
  82. package/vendor/src/components/ui/radio-group.tsx +102 -0
  83. package/vendor/src/components/ui/right-click-menu.tsx +60 -0
  84. package/vendor/src/components/ui/scroll-box.tsx +27 -0
  85. package/vendor/src/components/ui/segmented-control.tsx +21 -17
  86. package/vendor/src/components/ui/select.tsx +187 -189
  87. package/vendor/src/components/ui/skeleton.tsx +2 -2
  88. package/vendor/src/components/ui/switch.tsx +60 -60
  89. package/vendor/src/components/ui/table.tsx +114 -114
  90. package/vendor/src/components/ui/tabs.tsx +2 -2
  91. package/vendor/src/components/ui/textarea.tsx +1 -1
  92. package/vendor/src/components/upload/file-dropzone.tsx +38 -0
  93. package/vendor/src/components/upload/file-upload.tsx +4 -4
  94. package/vendor/src/components/upload/image-upload.tsx +22 -19
  95. package/vendor/src/components/upload/index.ts +2 -0
  96. package/vendor/src/families/catalog.ts +1 -0
  97. package/vendor/src/families/docs-groups.ts +10 -1
  98. package/vendor/src/families/member-metadata.ts +24 -0
  99. package/vendor/src/families/member-snippets.ts +41 -2
  100. package/vendor/src/families/migration-map.ts +3 -0
  101. package/vendor/src/index.ts +23 -18
  102. package/vendor/templates/styles/globals.css +253 -0
  103. package/dist/index.js +0 -432
@@ -1,20 +1,191 @@
1
- import * as React from "react"
2
- import { Input as InputPrimitive } from "@base-ui/react/input"
3
-
4
- import { cn } from "@/lib/utils"
5
-
6
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
7
- return (
8
- <InputPrimitive
9
- type={type}
10
- data-slot="input"
11
- className={cn(
12
- "h-9 w-full min-w-0 rounded-xl border border-input/85 bg-background/92 px-3 py-2 text-base shadow-sm 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/95 hover:border-border focus-visible:border-ring focus-visible:bg-background focus-visible:ring-3 focus-visible:ring-ring/45 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/55 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:hover:bg-input/45 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
13
- className
14
- )}
15
- {...props}
16
- />
17
- )
18
- }
19
-
20
- export { Input }
1
+ import * as React from "react"
2
+
3
+ import { InputPrimitive, type InputPrimitiveProps } from "@/components/ui/input-primitive"
4
+ import { ClearableInput, type ClearableInputProps } from "@/components/inputs/clearable-input"
5
+ import { DateInput, type DateInputProps } from "@/components/inputs/date-input"
6
+ import { DateRangeInput, type DateRangeInputProps } from "@/components/inputs/date-range-input"
7
+ import { MoneyInput, type MoneyInputProps } from "@/components/inputs/money-input"
8
+ import { MaskedInput, type MaskedInputProps } from "@/components/inputs/masked-input"
9
+ import { NumberInput, type NumberInputProps } from "@/components/inputs/number-input"
10
+ import { PasswordInput, type PasswordInputProps } from "@/components/inputs/password-input"
11
+ import { PhoneInput, type PhoneInputProps } from "@/components/inputs/phone-input"
12
+ import { QuantityInput, type QuantityInputProps } from "@/components/inputs/quantity-input"
13
+ import { SearchInput, type SearchInputProps } from "@/components/inputs/search-input"
14
+
15
+ export type InputKind =
16
+ | "text"
17
+ | "clearable"
18
+ | "search"
19
+ | "password"
20
+ | "number"
21
+ | "phone"
22
+ | "money"
23
+ | "quantity"
24
+ | "masked"
25
+ | "date"
26
+ | "date-range"
27
+
28
+ export type InputTextProps = Omit<InputPrimitiveProps, "value"> & {
29
+ kind?: "text"
30
+ value?: string | number | readonly string[] | null
31
+ }
32
+
33
+ export type InputClearableProps = Omit<ClearableInputProps, "kind"> & {
34
+ kind: "clearable"
35
+ }
36
+
37
+ export type InputSearchProps = Omit<SearchInputProps, "kind"> & {
38
+ kind: "search"
39
+ }
40
+
41
+ export type InputPasswordProps = Omit<PasswordInputProps, "kind"> & {
42
+ kind: "password"
43
+ }
44
+
45
+ export type InputNumberProps = Omit<NumberInputProps, "kind"> & {
46
+ kind: "number"
47
+ }
48
+
49
+ export type InputPhoneProps = Omit<PhoneInputProps, "kind"> & {
50
+ kind: "phone"
51
+ }
52
+
53
+ export type InputMoneyProps = Omit<MoneyInputProps, "kind"> & {
54
+ kind: "money"
55
+ }
56
+
57
+ export type InputQuantityProps = Omit<QuantityInputProps, "kind"> & {
58
+ kind: "quantity"
59
+ }
60
+
61
+ export type InputMaskedProps = Omit<MaskedInputProps, "kind"> & {
62
+ kind: "masked"
63
+ }
64
+
65
+ export type InputDateProps = Omit<DateInputProps, "kind"> & {
66
+ kind: "date"
67
+ }
68
+
69
+ export type InputDateRangeProps = Omit<DateRangeInputProps, "onChange" | "onValueChange" | "value"> & {
70
+ kind: "date-range"
71
+ onValueChange?: (value: { from?: string; to?: string }) => void
72
+ value?: DateRangeInputProps["value"]
73
+ }
74
+
75
+ export type InputProps =
76
+ | InputTextProps
77
+ | InputClearableProps
78
+ | InputSearchProps
79
+ | InputPasswordProps
80
+ | InputNumberProps
81
+ | InputPhoneProps
82
+ | InputMoneyProps
83
+ | InputQuantityProps
84
+ | InputMaskedProps
85
+ | InputDateProps
86
+ | InputDateRangeProps
87
+
88
+ const Input = React.forwardRef<HTMLInputElement | HTMLDivElement, InputProps>((props, ref) => {
89
+ const kind = props.kind ?? "text"
90
+
91
+ if (kind === "search") {
92
+ return (
93
+ <SearchInput
94
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
95
+ {...(props as Omit<SearchInputProps, "kind">)}
96
+ />
97
+ )
98
+ }
99
+
100
+ if (kind === "password") {
101
+ return (
102
+ <PasswordInput
103
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
104
+ {...(props as Omit<PasswordInputProps, "kind">)}
105
+ />
106
+ )
107
+ }
108
+
109
+ if (kind === "number") {
110
+ return (
111
+ <NumberInput
112
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
113
+ {...(props as Omit<NumberInputProps, "kind">)}
114
+ />
115
+ )
116
+ }
117
+
118
+ if (kind === "phone") {
119
+ return (
120
+ <PhoneInput
121
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
122
+ {...(props as Omit<PhoneInputProps, "kind">)}
123
+ />
124
+ )
125
+ }
126
+
127
+ if (kind === "money") {
128
+ return (
129
+ <MoneyInput
130
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
131
+ {...(props as Omit<MoneyInputProps, "kind">)}
132
+ />
133
+ )
134
+ }
135
+
136
+ if (kind === "quantity") {
137
+ return (
138
+ <QuantityInput
139
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
140
+ {...(props as Omit<QuantityInputProps, "kind">)}
141
+ />
142
+ )
143
+ }
144
+
145
+ if (kind === "masked") {
146
+ return (
147
+ <MaskedInput
148
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
149
+ {...(props as Omit<MaskedInputProps, "kind">)}
150
+ />
151
+ )
152
+ }
153
+
154
+ if (kind === "date") {
155
+ return (
156
+ <DateInput
157
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
158
+ {...(props as Omit<DateInputProps, "kind">)}
159
+ />
160
+ )
161
+ }
162
+
163
+ if (kind === "date-range") {
164
+ return <DateRangeInput {...(props as Omit<InputDateRangeProps, "kind">)} />
165
+ }
166
+
167
+ if (kind === "clearable") {
168
+ return (
169
+ <ClearableInput
170
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
171
+ {...(props as Omit<ClearableInputProps, "kind">)}
172
+ />
173
+ )
174
+ }
175
+
176
+ const inputProps = props as Omit<InputTextProps, "kind">
177
+ const { value, defaultValue, ...restInputProps } = inputProps
178
+
179
+ return (
180
+ <InputPrimitive
181
+ ref={ref as React.ForwardedRef<HTMLInputElement>}
182
+ value={value ?? undefined}
183
+ defaultValue={value === undefined ? defaultValue : undefined}
184
+ type={restInputProps.type ?? "text"}
185
+ {...(restInputProps as React.ComponentProps<typeof InputPrimitive>)}
186
+ />
187
+ )
188
+ })
189
+ Input.displayName = "Input"
190
+
191
+ export { Input }
@@ -0,0 +1,33 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type KbdProps = React.ComponentProps<"kbd"> & {
6
+ size?: "sm" | "default" | "lg"
7
+ variant?: "default" | "outline" | "ghost"
8
+ }
9
+
10
+ const sizeClassName = {
11
+ sm: "h-5 px-1.5 text-[10px]",
12
+ default: "h-6 px-2 text-xs",
13
+ lg: "h-7 px-2.5 text-sm",
14
+ }
15
+
16
+ const variantClassName = {
17
+ default: "border-border/80 bg-muted text-muted-foreground shadow-[inset_0_-1px_0_rgba(0,0,0,0.08)]",
18
+ outline: "border-border bg-background text-foreground shadow-sm",
19
+ ghost: "border-transparent bg-transparent text-muted-foreground shadow-none",
20
+ }
21
+
22
+ function Kbd({ className, size = "default", variant = "default", ...props }: KbdProps) {
23
+ return (
24
+ <kbd
25
+ data-slot="kbd"
26
+ data-size={size}
27
+ className={cn("inline-flex shrink-0 items-center justify-center rounded-md border font-mono font-medium leading-none", sizeClassName[size], variantClassName[variant], className)}
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export { Kbd }
@@ -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/75 bg-popover/98 p-3.5 text-sm text-popover-foreground shadow-[0_24px_70px_color-mix(in_oklch,var(--foreground),transparent_84%)] ring-1 ring-foreground/8 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 border-border/80 bg-[linear-gradient(180deg,color-mix(in_oklch,var(--popover),white_10%),var(--popover))] p-4 text-sm text-popover-foreground shadow-[0_24px_70px_color-mix(in_oklch,var(--foreground),transparent_84%)] ring-1 ring-foreground/8 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",
41
41
  className
42
42
  )}
43
43
  {...props}
@@ -49,12 +49,12 @@ function PopoverContent({
49
49
 
50
50
  function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
51
51
  return (
52
- <div
53
- data-slot="popover-header"
52
+ <div
53
+ data-slot="popover-header"
54
54
  className={cn("flex flex-col gap-1 text-sm", className)}
55
- {...props}
56
- />
57
- )
55
+ {...props}
56
+ />
57
+ )
58
58
  }
59
59
 
60
60
  function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
@@ -72,12 +72,12 @@ function PopoverDescription({
72
72
  ...props
73
73
  }: PopoverPrimitive.Description.Props) {
74
74
  return (
75
- <PopoverPrimitive.Description
76
- data-slot="popover-description"
75
+ <PopoverPrimitive.Description
76
+ data-slot="popover-description"
77
77
  className={cn("leading-6 text-muted-foreground", className)}
78
- {...props}
79
- />
80
- )
78
+ {...props}
79
+ />
80
+ )
81
81
  }
82
82
 
83
83
  export {
@@ -0,0 +1,102 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type RadioGroupOption = {
6
+ label: React.ReactNode
7
+ value: string
8
+ description?: React.ReactNode
9
+ disabled?: boolean
10
+ }
11
+
12
+ export type RadioGroupProps = Omit<React.ComponentProps<"div">, "onChange"> & {
13
+ name?: string
14
+ value?: string
15
+ defaultValue?: string
16
+ onValueChange?: (value: string) => void
17
+ options: RadioGroupOption[]
18
+ orientation?: "vertical" | "horizontal"
19
+ size?: "sm" | "default" | "lg"
20
+ itemClassName?: string
21
+ }
22
+
23
+ const sizeClassName = {
24
+ sm: "size-3.5",
25
+ default: "size-4",
26
+ lg: "size-5",
27
+ }
28
+
29
+ function RadioGroup({
30
+ name,
31
+ value,
32
+ defaultValue,
33
+ onValueChange,
34
+ options,
35
+ orientation = "vertical",
36
+ size = "default",
37
+ className,
38
+ itemClassName,
39
+ ...props
40
+ }: RadioGroupProps) {
41
+ const generatedName = React.useId()
42
+ const resolvedName = name ?? generatedName
43
+ const [internalValue, setInternalValue] = React.useState(defaultValue ?? "")
44
+ const isControlled = value !== undefined
45
+ const currentValue = isControlled ? value : internalValue
46
+
47
+ const setValue = (nextValue: string) => {
48
+ if (!isControlled) setInternalValue(nextValue)
49
+ onValueChange?.(nextValue)
50
+ }
51
+
52
+ return (
53
+ <div
54
+ role="radiogroup"
55
+ data-slot="radio-group"
56
+ data-orientation={orientation}
57
+ className={cn("grid gap-2", orientation === "horizontal" && "sm:flex sm:flex-wrap sm:items-center", className)}
58
+ {...props}
59
+ >
60
+ {options.map((option) => {
61
+ const checked = option.value === currentValue
62
+ return (
63
+ <label
64
+ key={option.value}
65
+ data-slot="radio-group-item"
66
+ data-checked={checked || undefined}
67
+ data-disabled={option.disabled || undefined}
68
+ className={cn(
69
+ "group flex cursor-pointer items-start gap-3 rounded-[min(var(--radius-xl),16px)] border border-border/80 bg-background/88 px-3 py-2.5 text-sm transition hover:border-ring/35 hover:bg-accent/60 data-[checked=true]:border-primary/35 data-[checked=true]:bg-primary/8 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-55",
70
+ itemClassName
71
+ )}
72
+ >
73
+ <input
74
+ type="radio"
75
+ name={resolvedName}
76
+ value={option.value}
77
+ checked={checked}
78
+ disabled={option.disabled}
79
+ className="sr-only"
80
+ onChange={() => setValue(option.value)}
81
+ />
82
+ <span
83
+ aria-hidden="true"
84
+ className={cn(
85
+ "mt-0.5 inline-flex shrink-0 items-center justify-center rounded-full border border-input bg-background shadow-sm transition group-data-[checked=true]:border-primary group-data-[checked=true]:bg-primary",
86
+ sizeClassName[size]
87
+ )}
88
+ >
89
+ <span className="size-1.5 rounded-full bg-primary-foreground opacity-0 transition group-data-[checked=true]:opacity-100" />
90
+ </span>
91
+ <span className="min-w-0">
92
+ <span className="block font-medium text-foreground">{option.label}</span>
93
+ {option.description ? <span className="mt-1 block text-sm leading-6 text-muted-foreground">{option.description}</span> : null}
94
+ </span>
95
+ </label>
96
+ )
97
+ })}
98
+ </div>
99
+ )
100
+ }
101
+
102
+ export { RadioGroup }
@@ -0,0 +1,60 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type RightClickMenuItem = {
6
+ label: React.ReactNode
7
+ onSelect?: () => void
8
+ disabled?: boolean
9
+ }
10
+
11
+ export type RightClickMenuProps = React.ComponentProps<"div"> & {
12
+ items: RightClickMenuItem[]
13
+ }
14
+
15
+ function RightClickMenu({ items, children, className, ...props }: RightClickMenuProps) {
16
+ const [open, setOpen] = React.useState(false)
17
+ const [point, setPoint] = React.useState({ x: 0, y: 0 })
18
+
19
+ React.useEffect(() => {
20
+ if (!open) return
21
+ const close = () => setOpen(false)
22
+ window.addEventListener("click", close)
23
+ return () => window.removeEventListener("click", close)
24
+ }, [open])
25
+
26
+ return (
27
+ <div
28
+ data-slot="right-click-menu"
29
+ className={cn("contents", className)}
30
+ onContextMenu={(event) => {
31
+ event.preventDefault()
32
+ setPoint({ x: event.clientX, y: event.clientY })
33
+ setOpen(true)
34
+ }}
35
+ {...props}
36
+ >
37
+ {children}
38
+ {open ? (
39
+ <div className="fixed z-50 min-w-40 rounded-xl border border-border bg-popover p-1.5 text-sm text-popover-foreground shadow-xl" style={{ left: point.x, top: point.y }}>
40
+ {items.map((item, index) => (
41
+ <button
42
+ key={index}
43
+ type="button"
44
+ disabled={item.disabled}
45
+ className="block w-full rounded-lg px-3 py-2 text-left hover:bg-accent disabled:pointer-events-none disabled:opacity-50"
46
+ onClick={() => {
47
+ item.onSelect?.()
48
+ setOpen(false)
49
+ }}
50
+ >
51
+ {item.label}
52
+ </button>
53
+ ))}
54
+ </div>
55
+ ) : null}
56
+ </div>
57
+ )
58
+ }
59
+
60
+ export { RightClickMenu }
@@ -0,0 +1,27 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type ScrollBoxProps = React.ComponentProps<"div"> & {
6
+ maxHeight?: string | number
7
+ axis?: "y" | "x" | "both"
8
+ }
9
+
10
+ function ScrollBox({ className, maxHeight, axis = "y", style, ...props }: ScrollBoxProps) {
11
+ return (
12
+ <div
13
+ data-slot="scroll-box"
14
+ className={cn(
15
+ "min-h-0 overscroll-contain",
16
+ axis === "y" && "overflow-y-auto overflow-x-hidden",
17
+ axis === "x" && "overflow-x-auto overflow-y-hidden",
18
+ axis === "both" && "overflow-auto",
19
+ className
20
+ )}
21
+ style={{ maxHeight, ...style }}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ export { ScrollBox }
@@ -18,11 +18,11 @@ export type SegmentedControlProps<TValue extends string = string> = Omit<React.C
18
18
  fullWidth?: boolean
19
19
  }
20
20
 
21
- const sizeClassName = {
22
- sm: "h-8 px-2 text-xs",
23
- md: "h-9 px-3 text-sm",
24
- lg: "h-10 px-4 text-sm",
25
- }
21
+ const sizeClassName = {
22
+ sm: "min-h-8 px-2.5 text-xs",
23
+ md: "min-h-9 px-3.5 text-sm",
24
+ lg: "min-h-10 px-4 text-sm",
25
+ }
26
26
 
27
27
  function SegmentedControl<TValue extends string = string>({
28
28
  value,
@@ -43,12 +43,16 @@ function SegmentedControl<TValue extends string = string>({
43
43
  }
44
44
 
45
45
  return (
46
- <div
47
- data-slot="segmented-control"
48
- role="radiogroup"
49
- className={cn("inline-flex rounded-lg border bg-muted p-1", fullWidth && "flex w-full", className)}
50
- {...props}
51
- >
46
+ <div
47
+ data-slot="segmented-control"
48
+ role="radiogroup"
49
+ className={cn(
50
+ "inline-flex gap-1 rounded-[var(--radius-2xl)] border border-border/80 bg-muted/72 p-1 text-muted-foreground shadow-[inset_0_1px_0_rgba(255,255,255,0.06),0_10px_26px_rgba(15,23,42,0.05)] backdrop-blur",
51
+ fullWidth && "flex w-full",
52
+ className
53
+ )}
54
+ {...props}
55
+ >
52
56
  {options.map((option) => {
53
57
  const selected = option.value === currentValue
54
58
  return (
@@ -58,12 +62,12 @@ function SegmentedControl<TValue extends string = string>({
58
62
  role="radio"
59
63
  aria-checked={selected}
60
64
  disabled={option.disabled}
61
- data-selected={selected || undefined}
62
- className={cn(
63
- "inline-flex items-center justify-center gap-1.5 rounded-md font-medium text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[selected=true]:bg-background data-[selected=true]:text-foreground data-[selected=true]:shadow-sm",
64
- sizeClassName[size],
65
- fullWidth && "flex-1"
66
- )}
65
+ data-selected={selected || undefined}
66
+ className={cn(
67
+ "inline-flex items-center justify-center gap-1.5 rounded-[calc(var(--radius-xl)-2px)] border border-transparent font-medium text-muted-foreground outline-none transition-[background-color,color,border-color,box-shadow,transform] hover:bg-background/58 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[selected=true]:-translate-y-px data-[selected=true]:border-border/85 data-[selected=true]:bg-background data-[selected=true]:text-foreground data-[selected=true]:shadow-[0_1px_0_rgba(255,255,255,0.24),0_12px_24px_rgba(15,23,42,0.12)]",
68
+ sizeClassName[size],
69
+ fullWidth && "flex-1"
70
+ )}
67
71
  onClick={() => selectValue(option.value)}
68
72
  >
69
73
  {option.icon}