@moontra/moonui 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +25 -7
- package/dist/index.d.ts +25 -7
- package/dist/index.global.js +20 -20
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +503 -353
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +467 -318
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/__tests__/alert.test.tsx +5 -5
- package/src/components/ui/__tests__/avatar.test.tsx +12 -12
- package/src/components/ui/__tests__/badge.test.tsx +17 -1
- package/src/components/ui/__tests__/checkbox.test.tsx +16 -12
- package/src/components/ui/__tests__/color-picker.test.tsx +147 -331
- package/src/components/ui/__tests__/radio-group.test.tsx +60 -0
- package/src/components/ui/__tests__/select.test.tsx +24 -2
- package/src/components/ui/__tests__/slider.test.tsx +96 -0
- package/src/components/ui/__tests__/switch.test.tsx +36 -3
- package/src/components/ui/__tests__/toggle-group.test.tsx +30 -0
- package/src/components/ui/alert.tsx +5 -2
- package/src/components/ui/avatar.tsx +18 -6
- package/src/components/ui/badge.tsx +18 -11
- package/src/components/ui/checkbox.tsx +20 -18
- package/src/components/ui/color-picker.tsx +12 -0
- package/src/components/ui/index.ts +17 -0
- package/src/components/ui/radio-group.tsx +47 -8
- package/src/components/ui/select.tsx +59 -23
- package/src/components/ui/slider.tsx +56 -2
- package/src/components/ui/switch.tsx +108 -51
- package/src/components/ui/toggle-group.tsx +4 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
|
5
5
|
import { Check, ChevronDown, ChevronUp, Loader2 } from "lucide-react"
|
|
6
|
+
import { cva } from "class-variance-authority"
|
|
6
7
|
|
|
7
8
|
import { cn } from "../../lib/utils"
|
|
8
9
|
|
|
@@ -24,7 +25,51 @@ const SelectValue = SelectPrimitive.Value
|
|
|
24
25
|
type SelectTriggerVariant = "standard" | "outline" | "ghost" | "underline";
|
|
25
26
|
type SelectTriggerSize = "sm" | "md" | "lg";
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Trigger varyantları `cva()` ile yönetilir (kütüphane geneli desen). Önceden
|
|
30
|
+
* `variant === "x" && "..."` manuel zincirlemesi kullanılıyor ve tüm renkler ham
|
|
31
|
+
* `gray-*` paletiyle yazılıyordu — tema/karanlık mod bu değerleri etkileyemiyordu.
|
|
32
|
+
* Artık semantik token'lara bağlı (`border-input`, `bg-background`, `bg-muted`…).
|
|
33
|
+
*/
|
|
34
|
+
const selectTriggerVariants = cva(
|
|
35
|
+
[
|
|
36
|
+
"flex w-full items-center justify-between gap-1 rounded-md transition-all duration-200",
|
|
37
|
+
"disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
38
|
+
"focus-visible:outline-none text-foreground",
|
|
39
|
+
],
|
|
40
|
+
{
|
|
41
|
+
variants: {
|
|
42
|
+
variant: {
|
|
43
|
+
standard:
|
|
44
|
+
"border border-input bg-background hover:border-ring focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:border-primary",
|
|
45
|
+
outline:
|
|
46
|
+
"border border-input bg-transparent hover:border-ring focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:border-primary",
|
|
47
|
+
ghost:
|
|
48
|
+
"border-none bg-muted hover:bg-muted/80 focus-visible:ring-2 focus-visible:ring-primary/30",
|
|
49
|
+
underline:
|
|
50
|
+
"border-t-0 border-l-0 border-r-0 border-b border-input rounded-none px-0 bg-transparent hover:border-ring focus-visible:ring-0 focus-visible:border-b-2 focus-visible:border-primary",
|
|
51
|
+
},
|
|
52
|
+
size: {
|
|
53
|
+
sm: "h-8 text-xs px-2",
|
|
54
|
+
md: "h-10 text-sm px-3",
|
|
55
|
+
lg: "h-12 text-base px-4",
|
|
56
|
+
},
|
|
57
|
+
state: {
|
|
58
|
+
none: "",
|
|
59
|
+
error: "border-error focus-visible:ring-error/30 focus-visible:border-error",
|
|
60
|
+
success:
|
|
61
|
+
"border-success focus-visible:ring-success/30 focus-visible:border-success",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
defaultVariants: {
|
|
65
|
+
variant: "standard",
|
|
66
|
+
size: "md",
|
|
67
|
+
state: "none",
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
export interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
|
|
28
73
|
/** Visual variant */
|
|
29
74
|
variant?: SelectTriggerVariant;
|
|
30
75
|
/** Size */
|
|
@@ -44,45 +89,36 @@ interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof Selec
|
|
|
44
89
|
const SelectTrigger = React.forwardRef<
|
|
45
90
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
46
91
|
SelectTriggerProps
|
|
47
|
-
>(({ className, children, variant
|
|
92
|
+
>(({ className, children, variant, size, error, success, loading, leftIcon, rightIcon, ...props }, ref) => (
|
|
48
93
|
<SelectPrimitive.Trigger
|
|
49
94
|
ref={ref}
|
|
50
95
|
className={cn(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
error && "border-error focus-visible:ring-error/30 focus-visible:border-error",
|
|
57
|
-
/* Success state */
|
|
58
|
-
success && "border-success focus-visible:ring-success/30 focus-visible:border-success",
|
|
59
|
-
/* Size variants */
|
|
60
|
-
size === "sm" && "h-8 text-xs px-2",
|
|
61
|
-
size === "md" && "h-10 text-sm px-3",
|
|
62
|
-
size === "lg" && "h-12 text-base px-4",
|
|
63
|
-
/* Visual variants */
|
|
64
|
-
variant === "standard" && "border border-gray-300 dark:border-gray-700 bg-background dark:bg-gray-800/80 dark:shadow-inner dark:shadow-gray-950/10 dark:text-gray-200 hover:border-gray-400 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/20 focus-visible:border-primary dark:focus-visible:border-primary/80",
|
|
65
|
-
variant === "outline" && "border border-gray-300 dark:border-gray-700 bg-transparent dark:text-gray-200 hover:border-gray-400 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/20 focus-visible:border-primary dark:focus-visible:border-primary/80",
|
|
66
|
-
variant === "ghost" && "border-none bg-gray-100 dark:bg-gray-800 dark:shadow-inner dark:shadow-gray-950/10 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/20",
|
|
67
|
-
variant === "underline" && "border-t-0 border-l-0 border-r-0 border-b border-gray-300 dark:border-gray-600 rounded-none px-0 dark:text-gray-200 dark:bg-transparent hover:border-gray-400 dark:hover:border-gray-500 focus-visible:ring-0 focus-visible:border-b-2 focus-visible:border-primary dark:focus-visible:border-primary/80",
|
|
96
|
+
selectTriggerVariants({
|
|
97
|
+
variant,
|
|
98
|
+
size,
|
|
99
|
+
state: error ? "error" : success ? "success" : "none",
|
|
100
|
+
}),
|
|
68
101
|
className
|
|
69
102
|
)}
|
|
70
103
|
{...props}
|
|
71
104
|
disabled={props.disabled || loading}
|
|
105
|
+
// `error` görsel olarak işaretleniyordu ama yardımcı teknolojiye hiç
|
|
106
|
+
// duyurulmuyordu; Input/Textarea aynı kavramda `aria-invalid` set ediyor.
|
|
107
|
+
aria-invalid={error ? true : undefined}
|
|
72
108
|
data-error={error ? true : undefined}
|
|
73
109
|
data-success={success ? true : undefined}
|
|
74
110
|
data-loading={loading ? true : undefined}
|
|
75
111
|
>
|
|
76
112
|
<div className="flex items-center flex-1 gap-2">
|
|
77
113
|
{leftIcon && (
|
|
78
|
-
<span className="flex items-center justify-center text-
|
|
114
|
+
<span className="flex items-center justify-center text-muted-foreground">
|
|
79
115
|
{leftIcon}
|
|
80
116
|
</span>
|
|
81
117
|
)}
|
|
82
118
|
{loading ? (
|
|
83
119
|
<div className="flex items-center gap-2">
|
|
84
|
-
<Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground
|
|
85
|
-
<span className="text-muted-foreground
|
|
120
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
|
|
121
|
+
<span className="text-muted-foreground">Loading...</span>
|
|
86
122
|
</div>
|
|
87
123
|
) : children}
|
|
88
124
|
</div>
|
|
@@ -198,7 +234,7 @@ SelectLabel.displayName = SelectPrimitive.Label.displayName
|
|
|
198
234
|
type SelectItemVariant = "default" | "subtle" | "destructive" | "success" | "warning";
|
|
199
235
|
type SelectItemSize = "sm" | "md" | "lg";
|
|
200
236
|
|
|
201
|
-
interface SelectItemProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
|
|
237
|
+
export interface SelectItemProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
|
|
202
238
|
/** Visual variant */
|
|
203
239
|
variant?: SelectItemVariant;
|
|
204
240
|
/** Size */
|
|
@@ -97,7 +97,7 @@ const sliderThumbVariants = cva(
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
// Custom type definition for component properties
|
|
100
|
-
type SliderBaseProps = {
|
|
100
|
+
export type SliderBaseProps = {
|
|
101
101
|
/**
|
|
102
102
|
* Track variant
|
|
103
103
|
*/
|
|
@@ -161,7 +161,7 @@ type SliderBaseProps = {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// Merge HTML properties without defaultValue conflicts
|
|
164
|
-
type SliderProps = SliderBaseProps & Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue'>
|
|
164
|
+
export type SliderProps = SliderBaseProps & Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue'>
|
|
165
165
|
|
|
166
166
|
const Slider = React.forwardRef<
|
|
167
167
|
HTMLDivElement,
|
|
@@ -280,6 +280,59 @@ const Slider = React.forwardRef<
|
|
|
280
280
|
document.addEventListener('mouseup', handleMouseUp);
|
|
281
281
|
};
|
|
282
282
|
|
|
283
|
+
// Klavyeyle değer değiştirme (WCAG 2.1.1 Keyboard).
|
|
284
|
+
//
|
|
285
|
+
// Thumb'lar `role="slider"` + `aria-valuenow` + `tabIndex=0` taşıyıp
|
|
286
|
+
// odaklanabiliyordu, ancak hiçbir klavye handler'ı yoktu: bileşen ARIA
|
|
287
|
+
// sözleşmesinde slider olduğunu duyuruyor ama yalnızca fareyle çalışıyordu.
|
|
288
|
+
// Tuş eşlemesi Radix Slider davranışını izler.
|
|
289
|
+
const handleThumbKeyDown = (index: number) => (event: React.KeyboardEvent) => {
|
|
290
|
+
if (disabled) return;
|
|
291
|
+
|
|
292
|
+
const largeStep = step * 10;
|
|
293
|
+
const current = sliderValue[index];
|
|
294
|
+
let next: number;
|
|
295
|
+
|
|
296
|
+
switch (event.key) {
|
|
297
|
+
case "ArrowRight":
|
|
298
|
+
case "ArrowUp":
|
|
299
|
+
next = current + step;
|
|
300
|
+
break;
|
|
301
|
+
case "ArrowLeft":
|
|
302
|
+
case "ArrowDown":
|
|
303
|
+
next = current - step;
|
|
304
|
+
break;
|
|
305
|
+
case "PageUp":
|
|
306
|
+
next = current + largeStep;
|
|
307
|
+
break;
|
|
308
|
+
case "PageDown":
|
|
309
|
+
next = current - largeStep;
|
|
310
|
+
break;
|
|
311
|
+
case "Home":
|
|
312
|
+
next = min;
|
|
313
|
+
break;
|
|
314
|
+
case "End":
|
|
315
|
+
next = max;
|
|
316
|
+
break;
|
|
317
|
+
default:
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Yalnızca yukarıdaki tuşlarda varsayılan davranışı engelle (sayfa kaydırma).
|
|
322
|
+
event.preventDefault();
|
|
323
|
+
|
|
324
|
+
// Çoklu thumb'da komşu değerleri aşma; tek thumb'da min/max sınırları geçerli.
|
|
325
|
+
const lowerBound = index > 0 ? sliderValue[index - 1] : min;
|
|
326
|
+
const upperBound =
|
|
327
|
+
index < sliderValue.length - 1 ? sliderValue[index + 1] : max;
|
|
328
|
+
const bounded = Math.max(lowerBound, Math.min(upperBound, next));
|
|
329
|
+
|
|
330
|
+
const newValues = [...sliderValue];
|
|
331
|
+
newValues[index] = bounded;
|
|
332
|
+
// handleValueChange, step hassasiyetine göre yuvarlamayı zaten uyguluyor.
|
|
333
|
+
handleValueChange(newValues);
|
|
334
|
+
};
|
|
335
|
+
|
|
283
336
|
return (
|
|
284
337
|
<div className="w-full" ref={ref} {...props}>
|
|
285
338
|
{showValueLabel && (
|
|
@@ -332,6 +385,7 @@ const Slider = React.forwardRef<
|
|
|
332
385
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
333
386
|
}}
|
|
334
387
|
onMouseDown={handleThumbMouseDown(i)}
|
|
388
|
+
onKeyDown={handleThumbKeyDown(i)}
|
|
335
389
|
aria-label={`Thumb ${i + 1}`}
|
|
336
390
|
tabIndex={disabled ? -1 : 0}
|
|
337
391
|
role="slider"
|
|
@@ -2,11 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
|
5
|
+
import { cva } from "class-variance-authority"
|
|
5
6
|
|
|
6
7
|
import { cn } from "../../lib/utils"
|
|
7
8
|
|
|
8
9
|
type SwitchSize = "sm" | "md" | "lg";
|
|
9
|
-
type SwitchVariant = "primary" | "success" | "warning" | "
|
|
10
|
+
type SwitchVariant = "primary" | "success" | "warning" | "destructive" | "secondary";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Boyut ve renk varyantları `cva()` ile yönetilir (kütüphane geneli desen).
|
|
14
|
+
* Önceden `size === "x" && "..."` biçiminde manuel zincirleme kullanılıyordu;
|
|
15
|
+
* `defaultVariants` yoktu ve varsayılanlar destructuring'de gizliydi.
|
|
16
|
+
*/
|
|
17
|
+
const switchVariants = cva(
|
|
18
|
+
[
|
|
19
|
+
"peer relative inline-flex shrink-0 cursor-pointer items-center rounded-full",
|
|
20
|
+
"border-2 border-transparent transition-colors",
|
|
21
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
22
|
+
"focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
|
+
// Kapalı durum token'a bağlı; `--input` karanlık modda kendi değerini taşır.
|
|
25
|
+
// (Önceden ayrıca `dark:data-[state=unchecked]:bg-gray-700/70` hardcode'u
|
|
26
|
+
// vardı ve token'ı eziyordu — kaldırıldı.)
|
|
27
|
+
"data-[state=unchecked]:bg-input",
|
|
28
|
+
"dark:focus-visible:ring-primary/40",
|
|
29
|
+
],
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
size: {
|
|
33
|
+
sm: "h-4 w-8",
|
|
34
|
+
md: "h-6 w-11",
|
|
35
|
+
lg: "h-7 w-14",
|
|
36
|
+
},
|
|
37
|
+
variant: {
|
|
38
|
+
primary: "data-[state=checked]:bg-primary",
|
|
39
|
+
success: "data-[state=checked]:bg-success",
|
|
40
|
+
warning: "data-[state=checked]:bg-warning",
|
|
41
|
+
destructive: "data-[state=checked]:bg-error",
|
|
42
|
+
secondary: "data-[state=checked]:bg-accent",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
size: "md",
|
|
47
|
+
variant: "primary",
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const switchThumbVariants = cva(
|
|
53
|
+
"pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
|
|
54
|
+
{
|
|
55
|
+
variants: {
|
|
56
|
+
size: {
|
|
57
|
+
sm: "h-3 w-3 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
|
|
58
|
+
md: "h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
|
|
59
|
+
lg: "h-6 w-6 data-[state=checked]:translate-x-7 data-[state=unchecked]:translate-x-0",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
defaultVariants: {
|
|
63
|
+
size: "md",
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
)
|
|
10
67
|
|
|
11
68
|
export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {
|
|
12
69
|
/** Switch boyutu */
|
|
@@ -26,58 +83,58 @@ export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof Switc
|
|
|
26
83
|
const Switch = React.forwardRef<
|
|
27
84
|
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
28
85
|
SwitchProps
|
|
29
|
-
>(({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
className
|
|
55
|
-
)}
|
|
56
|
-
disabled={loading || props.disabled}
|
|
57
|
-
{...props}
|
|
58
|
-
ref={ref}
|
|
59
|
-
>
|
|
60
|
-
{loading ? (
|
|
61
|
-
<div className="absolute inset-0 flex items-center justify-center">
|
|
62
|
-
<div className="h-3 w-3 animate-spin rounded-full border-2 border-gray-300 border-t-primary"></div>
|
|
63
|
-
</div>
|
|
64
|
-
) : null}
|
|
65
|
-
|
|
66
|
-
<SwitchPrimitives.Thumb
|
|
86
|
+
>(({
|
|
87
|
+
className,
|
|
88
|
+
size,
|
|
89
|
+
variant,
|
|
90
|
+
loading,
|
|
91
|
+
leftIcon,
|
|
92
|
+
rightIcon,
|
|
93
|
+
description,
|
|
94
|
+
"aria-describedby": ariaDescribedBy,
|
|
95
|
+
...props
|
|
96
|
+
}, ref) => {
|
|
97
|
+
// `description` görsel olarak render ediliyordu ama switch ile hiçbir
|
|
98
|
+
// programatik bağı yoktu — ekran okuyucu açıklamayı okuyamıyordu.
|
|
99
|
+
// Stabil bir id üretilip `aria-describedby` ile bağlanır; tüketicinin
|
|
100
|
+
// kendi verdiği `aria-describedby` korunur.
|
|
101
|
+
const reactId = React.useId()
|
|
102
|
+
const descriptionId = description ? `${reactId}-description` : undefined
|
|
103
|
+
const describedBy =
|
|
104
|
+
[ariaDescribedBy, descriptionId].filter(Boolean).join(" ") || undefined
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="moonui-theme inline-flex items-center gap-2">
|
|
108
|
+
{leftIcon && <span className="text-muted-foreground">{leftIcon}</span>}
|
|
109
|
+
<SwitchPrimitives.Root
|
|
67
110
|
className={cn(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
size === "md" && "h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
|
|
72
|
-
size === "lg" && "h-6 w-6 data-[state=checked]:translate-x-7 data-[state=unchecked]:translate-x-0",
|
|
111
|
+
switchVariants({ size, variant }),
|
|
112
|
+
loading && "opacity-80 cursor-wait",
|
|
113
|
+
className
|
|
73
114
|
)}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
115
|
+
disabled={loading || props.disabled}
|
|
116
|
+
aria-describedby={describedBy}
|
|
117
|
+
{...props}
|
|
118
|
+
ref={ref}
|
|
119
|
+
>
|
|
120
|
+
{loading ? (
|
|
121
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
122
|
+
<div className="h-3 w-3 animate-spin rounded-full border-2 border-muted border-t-primary" />
|
|
123
|
+
</div>
|
|
124
|
+
) : null}
|
|
125
|
+
|
|
126
|
+
<SwitchPrimitives.Thumb className={cn(switchThumbVariants({ size }))} />
|
|
127
|
+
</SwitchPrimitives.Root>
|
|
128
|
+
{rightIcon && <span className="text-muted-foreground">{rightIcon}</span>}
|
|
129
|
+
{description && (
|
|
130
|
+
<span id={descriptionId} className="text-sm text-muted-foreground">
|
|
131
|
+
{description}
|
|
132
|
+
</span>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
})
|
|
80
137
|
Switch.displayName = SwitchPrimitives.Root.displayName
|
|
81
138
|
|
|
82
|
-
export { Switch }
|
|
139
|
+
export { Switch, switchVariants }
|
|
83
140
|
export type { SwitchSize, SwitchVariant }
|
|
@@ -48,6 +48,10 @@ const ToggleGroup = React.forwardRef<
|
|
|
48
48
|
ref={ref}
|
|
49
49
|
className={cn(
|
|
50
50
|
"moonui-theme inline-flex items-center justify-center gap-1",
|
|
51
|
+
// Radix roving-focus kök elemana `data-orientation` yazar; dikey yerleşim
|
|
52
|
+
// buna bağlanır. Yatay/tanımsız durumda taban sınıflar geçerli kalır —
|
|
53
|
+
// yani mevcut render değişmez. Kardeş `button-group` ile desen paritesi.
|
|
54
|
+
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[orientation=vertical]:justify-start",
|
|
51
55
|
className
|
|
52
56
|
)}
|
|
53
57
|
{...props}
|