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.
- package/README.md +11 -0
- package/dist/index.cjs +452 -0
- package/package.json +2 -2
- package/vendor/src/components/actions/action-menu.tsx +21 -18
- package/vendor/src/components/calendar/calendar.tsx +153 -102
- package/vendor/src/components/calendar/date-picker.tsx +24 -14
- package/vendor/src/components/calendar/date-range-picker.tsx +137 -58
- package/vendor/src/components/charts/charts.tsx +32 -21
- package/vendor/src/components/command/command-palette.tsx +68 -57
- package/vendor/src/components/data-table/data-table-bulk-actions.tsx +23 -20
- package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +21 -10
- package/vendor/src/components/data-table/data-table-pagination.tsx +6 -6
- package/vendor/src/components/data-table/data-table-toolbar.tsx +72 -44
- package/vendor/src/components/data-table/data-table.tsx +15 -11
- package/vendor/src/components/data-table/table-export-menu.tsx +1 -1
- package/vendor/src/components/data-table/table-import-button.tsx +1 -1
- package/vendor/src/components/display/data-state.tsx +20 -8
- package/vendor/src/components/display/index.ts +19 -15
- package/vendor/src/components/display/metric-card.tsx +35 -0
- package/vendor/src/components/display/progress-circle.tsx +24 -0
- package/vendor/src/components/display/smart-card.tsx +49 -27
- package/vendor/src/components/display/status-dot.tsx +45 -0
- package/vendor/src/components/display/user-card.tsx +30 -0
- package/vendor/src/components/feedback/alert.tsx +21 -11
- package/vendor/src/components/feedback/empty-state.tsx +2 -2
- package/vendor/src/components/feedback/loading-state.tsx +2 -2
- package/vendor/src/components/feedback/page-state.tsx +19 -15
- package/vendor/src/components/feedback/status-badge.tsx +43 -43
- package/vendor/src/components/form/form-app-input.tsx +147 -0
- package/vendor/src/components/form/form-date-input.tsx +16 -19
- package/vendor/src/components/form/form-field-shell.tsx +11 -8
- package/vendor/src/components/form/form-field-utils.ts +76 -0
- package/vendor/src/components/form/form-input.tsx +423 -44
- package/vendor/src/components/form/form-number-input.tsx +16 -15
- package/vendor/src/components/form/form-phone-input.tsx +15 -9
- package/vendor/src/components/form/form-search-input.tsx +16 -19
- package/vendor/src/components/form/form-select.tsx +4 -3
- package/vendor/src/components/form/public.ts +16 -14
- package/vendor/src/components/form/smart-form-shell.tsx +13 -12
- package/vendor/src/components/inputs/app-input.tsx +27 -0
- package/vendor/src/components/inputs/async-select.tsx +113 -84
- package/vendor/src/components/inputs/clearable-input.tsx +81 -61
- package/vendor/src/components/inputs/date-input.tsx +21 -17
- package/vendor/src/components/inputs/date-range-input.tsx +10 -10
- package/vendor/src/components/inputs/index.ts +1 -0
- package/vendor/src/components/inputs/input-decorator.tsx +101 -57
- package/vendor/src/components/inputs/masked-input.tsx +20 -20
- package/vendor/src/components/inputs/money-input.tsx +2 -2
- package/vendor/src/components/inputs/number-input.tsx +29 -19
- package/vendor/src/components/inputs/password-input.tsx +82 -45
- package/vendor/src/components/inputs/phone-input.tsx +24 -2
- package/vendor/src/components/inputs/quantity-input.tsx +2 -2
- package/vendor/src/components/inputs/search-input.tsx +54 -3
- package/vendor/src/components/inputs/simple-select.tsx +110 -22
- package/vendor/src/components/layout/app-shell.tsx +2 -2
- package/vendor/src/components/layout/index.ts +5 -4
- package/vendor/src/components/layout/page-header.tsx +79 -35
- package/vendor/src/components/layout/public.ts +12 -10
- package/vendor/src/components/layout/section-header.tsx +56 -0
- package/vendor/src/components/layout/stack.tsx +106 -0
- package/vendor/src/components/layout/stat-card.tsx +66 -29
- package/vendor/src/components/navigation/index.ts +1 -0
- package/vendor/src/components/navigation/nav-tabs.tsx +60 -0
- package/vendor/src/components/navigation/page-tabs.tsx +41 -26
- package/vendor/src/components/navigation/pagination.tsx +14 -10
- package/vendor/src/components/overlay/alert-dialog.tsx +65 -0
- package/vendor/src/components/overlay/drawer.tsx +71 -0
- package/vendor/src/components/overlay/index.ts +4 -2
- package/vendor/src/components/patterns/data-view.tsx +13 -8
- package/vendor/src/components/ui/badge.tsx +96 -52
- package/vendor/src/components/ui/button.tsx +99 -61
- package/vendor/src/components/ui/card.tsx +84 -25
- package/vendor/src/components/ui/checkbox.tsx +68 -68
- package/vendor/src/components/ui/command.tsx +32 -32
- package/vendor/src/components/ui/dialog.tsx +135 -138
- package/vendor/src/components/ui/dropdown-menu.tsx +21 -21
- package/vendor/src/components/ui/hover-card.tsx +49 -0
- package/vendor/src/components/ui/input-primitive.tsx +24 -0
- package/vendor/src/components/ui/input.tsx +191 -20
- package/vendor/src/components/ui/kbd.tsx +33 -0
- package/vendor/src/components/ui/popover.tsx +11 -11
- package/vendor/src/components/ui/radio-group.tsx +102 -0
- package/vendor/src/components/ui/right-click-menu.tsx +60 -0
- package/vendor/src/components/ui/scroll-box.tsx +27 -0
- package/vendor/src/components/ui/segmented-control.tsx +21 -17
- package/vendor/src/components/ui/select.tsx +187 -189
- package/vendor/src/components/ui/skeleton.tsx +2 -2
- package/vendor/src/components/ui/switch.tsx +60 -60
- package/vendor/src/components/ui/table.tsx +114 -114
- package/vendor/src/components/ui/tabs.tsx +2 -2
- package/vendor/src/components/ui/textarea.tsx +1 -1
- package/vendor/src/components/upload/file-dropzone.tsx +38 -0
- package/vendor/src/components/upload/file-upload.tsx +4 -4
- package/vendor/src/components/upload/image-upload.tsx +22 -19
- package/vendor/src/components/upload/index.ts +2 -0
- package/vendor/src/families/catalog.ts +1 -0
- package/vendor/src/families/docs-groups.ts +10 -1
- package/vendor/src/families/member-metadata.ts +24 -0
- package/vendor/src/families/member-snippets.ts +41 -2
- package/vendor/src/families/migration-map.ts +3 -0
- package/vendor/src/index.ts +23 -18
- package/vendor/templates/styles/globals.css +253 -0
- package/dist/index.js +0 -432
|
@@ -1,20 +1,191 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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/
|
|
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(
|
|
50
|
-
|
|
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-
|
|
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}
|