@moontra/moonui 3.4.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.
@@ -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" | "danger" | "secondary";
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
- >(({ className, size = "md", variant = "primary", loading, leftIcon, rightIcon, description, ...props }, ref) => (
30
- <div className="moonui-theme inline-flex items-center gap-2">
31
- {leftIcon && <span className="text-muted-foreground">{leftIcon}</span>}
32
- <SwitchPrimitives.Root
33
- className={cn(
34
- "peer relative inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 dark:data-[state=unchecked]:bg-gray-700/70 dark:focus-visible:ring-primary/40 dark:focus-visible:ring-offset-gray-950",
35
-
36
- // Boyutlar
37
- size === "sm" && "h-4 w-8",
38
- size === "md" && "h-6 w-11",
39
- size === "lg" && "h-7 w-14",
40
-
41
- // Varyantlar (checked durumunda)
42
- variant === "primary" && "data-[state=checked]:bg-primary",
43
- variant === "success" && "data-[state=checked]:bg-success",
44
- variant === "warning" && "data-[state=checked]:bg-warning",
45
- variant === "danger" && "data-[state=checked]:bg-error",
46
- variant === "secondary" && "data-[state=checked]:bg-accent",
47
-
48
- // Unchecked durumu
49
- "data-[state=unchecked]:bg-input",
50
-
51
- // Loading durumu
52
- loading && "opacity-80 cursor-wait",
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
- "pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
69
- // Boyuta göre thumb boyutları
70
- size === "sm" && "h-3 w-3 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
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
- </SwitchPrimitives.Root>
76
- {rightIcon && <span className="text-muted-foreground">{rightIcon}</span>}
77
- {description && <span className="text-sm text-muted-foreground">{description}</span>}
78
- </div>
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 }