@moontra/moonui 2.0.7 → 2.0.9
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/package.json +26 -19
- package/src/__tests__/use-intersection-observer.test.tsx +216 -0
- package/src/__tests__/use-local-storage.test.tsx +174 -0
- package/src/__tests__/use-pro-access.test.tsx +183 -0
- package/src/components/ui/__tests__/accordion.test.tsx +571 -0
- package/src/components/ui/__tests__/alert.test.tsx +484 -0
- package/src/components/ui/__tests__/aspect-ratio.test.tsx +492 -0
- package/src/components/ui/__tests__/avatar.test.tsx +382 -0
- package/src/components/ui/__tests__/badge.test.tsx +364 -0
- package/src/components/ui/__tests__/breadcrumb.test.tsx +687 -0
- package/src/components/ui/__tests__/button.test.tsx +91 -0
- package/src/components/ui/__tests__/card.test.tsx +104 -0
- package/src/components/ui/__tests__/checkbox.test.tsx +591 -0
- package/src/components/ui/__tests__/collapsible.test.tsx +592 -0
- package/src/components/ui/__tests__/color-picker.test.tsx +365 -0
- package/src/components/ui/__tests__/command.test.tsx +677 -0
- package/src/components/ui/__tests__/data-table.test.tsx +256 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +320 -0
- package/src/components/ui/__tests__/dialog.test.tsx +764 -0
- package/src/components/ui/__tests__/input.test.tsx +318 -0
- package/src/components/ui/__tests__/label.test.tsx +103 -0
- package/src/components/ui/__tests__/popover.test.tsx +637 -0
- package/src/components/ui/__tests__/progress.test.tsx +382 -0
- package/src/components/ui/__tests__/radio-group.test.tsx +555 -0
- package/src/components/ui/__tests__/select.test.tsx +705 -0
- package/src/components/ui/__tests__/skeleton.test.tsx +253 -0
- package/src/components/ui/__tests__/slider.test.tsx +493 -0
- package/src/components/ui/__tests__/switch.test.tsx +372 -0
- package/src/components/ui/__tests__/table.test.tsx +824 -0
- package/src/components/ui/__tests__/tabs.test.tsx +887 -0
- package/src/components/ui/__tests__/toast.test.tsx +458 -0
- package/src/components/ui/__tests__/tooltip.test.tsx +583 -0
- package/src/components/ui/accordion.tsx +63 -0
- package/src/components/ui/alert.tsx +130 -0
- package/src/components/ui/aspect-ratio.tsx +72 -0
- package/src/components/ui/avatar.tsx +135 -0
- package/src/components/ui/badge.tsx +225 -0
- package/src/components/ui/breadcrumb.tsx +207 -0
- package/src/components/ui/button.stories.tsx +162 -0
- package/src/components/ui/button.tsx +221 -0
- package/src/components/ui/calendar.tsx +262 -0
- package/src/components/ui/card.stories.tsx +208 -0
- package/src/components/ui/card.tsx +141 -0
- package/src/components/ui/checkbox.tsx +256 -0
- package/src/components/ui/collapsible.tsx +129 -0
- package/src/components/ui/color-picker.tsx +664 -0
- package/src/components/ui/command.tsx +218 -0
- package/src/components/ui/data-table.stories.tsx +509 -0
- package/src/components/ui/data-table.tsx +623 -0
- package/src/components/ui/date-picker.stories.tsx +321 -0
- package/src/components/ui/date-picker.tsx +603 -0
- package/src/components/ui/dialog.tsx +332 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/file-upload.tsx +400 -0
- package/src/components/ui/github-stars.tsx +201 -0
- package/src/components/ui/index.ts +12 -0
- package/src/components/ui/input.stories.tsx +255 -0
- package/src/components/ui/input.tsx +219 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/locked-component.tsx +215 -0
- package/src/components/ui/moon-logo.tsx +97 -0
- package/src/components/ui/pagination.tsx +116 -0
- package/src/components/ui/popover.tsx +183 -0
- package/src/components/ui/progress.tsx +182 -0
- package/src/components/ui/radio-group.tsx +243 -0
- package/src/components/ui/select.tsx +273 -0
- package/src/components/ui/separator.tsx +140 -0
- package/src/components/ui/simple-editor.tsx +652 -0
- package/src/components/ui/skeleton.tsx +351 -0
- package/src/components/ui/slider.tsx +351 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/table.tsx +322 -0
- package/src/components/ui/tabs.tsx +195 -0
- package/src/components/ui/textarea.tsx +46 -0
- package/src/components/ui/toast.tsx +313 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +152 -0
- package/src/docs/theme-system.md +1194 -0
- package/src/index.tsx +39 -0
- package/src/lib/micro-interactions.ts +255 -0
- package/src/lib/theme.tsx +398 -0
- package/src/lib/utils.ts +69 -0
- package/src/styles/design-system.css +365 -0
- package/src/styles/index.css +4 -0
- package/src/styles/tailwind.css +6 -0
- package/src/styles/tokens.css +453 -0
- package/src/use-intersection-observer.tsx +154 -0
- package/src/use-local-storage.tsx +71 -0
- package/src/use-paddle.ts +138 -0
- package/src/use-performance-optimizer.ts +379 -0
- package/src/use-pro-access.ts +141 -0
- package/src/use-scroll-animation.ts +221 -0
- package/src/use-subscription.ts +37 -0
- package/src/use-toast.ts +32 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Input } from './input';
|
|
3
|
+
import { Label } from './label';
|
|
4
|
+
import { Button } from './button';
|
|
5
|
+
import { Search, Mail, Lock, Eye, EyeOff } from 'lucide-react';
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Input> = {
|
|
9
|
+
title: 'Components/Input',
|
|
10
|
+
component: Input,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered',
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: 'Displays a form input field or a component that looks like an input field.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
argTypes: {
|
|
21
|
+
type: {
|
|
22
|
+
control: 'select',
|
|
23
|
+
options: ['text', 'email', 'password', 'number', 'search', 'tel', 'url'],
|
|
24
|
+
description: 'The type of input',
|
|
25
|
+
},
|
|
26
|
+
placeholder: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Placeholder text',
|
|
29
|
+
},
|
|
30
|
+
disabled: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Whether the input is disabled',
|
|
33
|
+
},
|
|
34
|
+
className: {
|
|
35
|
+
control: 'text',
|
|
36
|
+
description: 'Additional CSS classes',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default meta;
|
|
42
|
+
type Story = StoryObj<typeof meta>;
|
|
43
|
+
|
|
44
|
+
export const Default: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
placeholder: 'Enter text...',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const WithLabel: Story = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<div className="grid w-full max-w-sm items-center gap-1.5">
|
|
53
|
+
<Label htmlFor="email">Email</Label>
|
|
54
|
+
<Input type="email" id="email" placeholder="Email" />
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Email: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
type: 'email',
|
|
62
|
+
placeholder: 'Enter your email',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const Password: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
type: 'password',
|
|
69
|
+
placeholder: 'Enter your password',
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const Search: Story = {
|
|
74
|
+
args: {
|
|
75
|
+
type: 'search',
|
|
76
|
+
placeholder: 'Search...',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const Number: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
type: 'number',
|
|
83
|
+
placeholder: 'Enter a number',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const Disabled: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
disabled: true,
|
|
90
|
+
placeholder: 'Disabled input',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const WithIcon: Story = {
|
|
95
|
+
render: () => (
|
|
96
|
+
<div className="relative">
|
|
97
|
+
<Search className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
|
|
98
|
+
<Input
|
|
99
|
+
type="search"
|
|
100
|
+
placeholder="Search..."
|
|
101
|
+
className="pl-10"
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const WithButton: Story = {
|
|
108
|
+
render: () => (
|
|
109
|
+
<div className="flex w-full max-w-sm items-center space-x-2">
|
|
110
|
+
<Input type="email" placeholder="Email" />
|
|
111
|
+
<Button type="submit">Subscribe</Button>
|
|
112
|
+
</div>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const FormExample: Story = {
|
|
117
|
+
render: () => (
|
|
118
|
+
<div className="w-full max-w-sm space-y-4">
|
|
119
|
+
<div className="space-y-2">
|
|
120
|
+
<Label htmlFor="name">Name</Label>
|
|
121
|
+
<Input id="name" placeholder="Your name" />
|
|
122
|
+
</div>
|
|
123
|
+
<div className="space-y-2">
|
|
124
|
+
<Label htmlFor="email-form">Email</Label>
|
|
125
|
+
<div className="relative">
|
|
126
|
+
<Mail className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
|
|
127
|
+
<Input
|
|
128
|
+
id="email-form"
|
|
129
|
+
type="email"
|
|
130
|
+
placeholder="your@email.com"
|
|
131
|
+
className="pl-10"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
<div className="space-y-2">
|
|
136
|
+
<Label htmlFor="password-form">Password</Label>
|
|
137
|
+
<div className="relative">
|
|
138
|
+
<Lock className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
|
|
139
|
+
<Input
|
|
140
|
+
id="password-form"
|
|
141
|
+
type="password"
|
|
142
|
+
placeholder="••••••••"
|
|
143
|
+
className="pl-10"
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
<Button className="w-full">Sign Up</Button>
|
|
148
|
+
</div>
|
|
149
|
+
),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const PasswordWithToggle: Story = {
|
|
153
|
+
render: () => {
|
|
154
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div className="relative">
|
|
158
|
+
<Input
|
|
159
|
+
type={showPassword ? 'text' : 'password'}
|
|
160
|
+
placeholder="Enter your password"
|
|
161
|
+
className="pr-10"
|
|
162
|
+
/>
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
className="absolute right-3 top-3 h-4 w-4 text-muted-foreground hover:text-foreground"
|
|
166
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
167
|
+
>
|
|
168
|
+
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const Sizes: Story = {
|
|
176
|
+
render: () => (
|
|
177
|
+
<div className="w-full max-w-sm space-y-4">
|
|
178
|
+
<div className="space-y-2">
|
|
179
|
+
<Label>Small</Label>
|
|
180
|
+
<Input placeholder="Small input" className="h-8 text-sm" />
|
|
181
|
+
</div>
|
|
182
|
+
<div className="space-y-2">
|
|
183
|
+
<Label>Default</Label>
|
|
184
|
+
<Input placeholder="Default input" />
|
|
185
|
+
</div>
|
|
186
|
+
<div className="space-y-2">
|
|
187
|
+
<Label>Large</Label>
|
|
188
|
+
<Input placeholder="Large input" className="h-12 text-lg" />
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const States: Story = {
|
|
195
|
+
render: () => (
|
|
196
|
+
<div className="w-full max-w-sm space-y-4">
|
|
197
|
+
<div className="space-y-2">
|
|
198
|
+
<Label>Default</Label>
|
|
199
|
+
<Input placeholder="Default state" />
|
|
200
|
+
</div>
|
|
201
|
+
<div className="space-y-2">
|
|
202
|
+
<Label>Focused</Label>
|
|
203
|
+
<Input placeholder="Focused state" autoFocus />
|
|
204
|
+
</div>
|
|
205
|
+
<div className="space-y-2">
|
|
206
|
+
<Label>Disabled</Label>
|
|
207
|
+
<Input placeholder="Disabled state" disabled />
|
|
208
|
+
</div>
|
|
209
|
+
<div className="space-y-2">
|
|
210
|
+
<Label>Error</Label>
|
|
211
|
+
<Input
|
|
212
|
+
placeholder="Error state"
|
|
213
|
+
className="border-red-500 focus:border-red-500 focus:ring-red-500"
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
),
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const WithValidation: Story = {
|
|
221
|
+
render: () => {
|
|
222
|
+
const [email, setEmail] = useState('');
|
|
223
|
+
const [error, setError] = useState('');
|
|
224
|
+
|
|
225
|
+
const validateEmail = (value: string) => {
|
|
226
|
+
if (!value) {
|
|
227
|
+
setError('Email is required');
|
|
228
|
+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
229
|
+
setError('Please enter a valid email address');
|
|
230
|
+
} else {
|
|
231
|
+
setError('');
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div className="w-full max-w-sm space-y-2">
|
|
237
|
+
<Label htmlFor="email-validation">Email</Label>
|
|
238
|
+
<Input
|
|
239
|
+
id="email-validation"
|
|
240
|
+
type="email"
|
|
241
|
+
placeholder="Enter your email"
|
|
242
|
+
value={email}
|
|
243
|
+
onChange={(e) => {
|
|
244
|
+
setEmail(e.target.value);
|
|
245
|
+
validateEmail(e.target.value);
|
|
246
|
+
}}
|
|
247
|
+
className={error ? 'border-red-500' : ''}
|
|
248
|
+
/>
|
|
249
|
+
{error && (
|
|
250
|
+
<p className="text-sm text-red-500">{error}</p>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
},
|
|
255
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
import { Loader2 } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Premium Input Component
|
|
8
|
+
*
|
|
9
|
+
* Erişilebilir, estetik ve işlevsel input bileşeni.
|
|
10
|
+
* İkon desteği, hata gösterimi ve çeşitli varyantlar sunar.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const inputWrapperVariants = cva(
|
|
14
|
+
"group relative flex items-center w-full transition-colors",
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
sm: "",
|
|
19
|
+
md: "",
|
|
20
|
+
lg: "",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
size: "md",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const inputVariants = cva(
|
|
30
|
+
[
|
|
31
|
+
"w-full bg-background transition-all duration-200",
|
|
32
|
+
"text-foreground placeholder:text-muted-foreground dark:placeholder:text-gray-500",
|
|
33
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
34
|
+
"file:border-0 file:bg-transparent file:font-medium",
|
|
35
|
+
"focus-visible:outline-none dark:text-gray-200"
|
|
36
|
+
],
|
|
37
|
+
{
|
|
38
|
+
variants: {
|
|
39
|
+
variant: {
|
|
40
|
+
standard: "border border-gray-300 dark:border-gray-700 rounded-md px-3 py-2 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 dark:bg-gray-900/60 dark:shadow-inner dark:shadow-gray-950/10",
|
|
41
|
+
filled: "border border-transparent bg-gray-100 dark:bg-gray-800/90 rounded-md px-3 py-2 hover:bg-gray-200 dark:hover:bg-gray-700/90 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/20 dark:shadow-inner dark:shadow-gray-950/10",
|
|
42
|
+
ghost: "border-none bg-transparent shadow-none px-1 dark:text-gray-300 dark:placeholder:text-gray-500 hover:bg-gray-100/50 dark:hover:bg-gray-800/30 focus-visible:bg-transparent",
|
|
43
|
+
underline: "border-t-0 border-l-0 border-r-0 border-b border-gray-300 dark:border-gray-600 rounded-none px-0 py-2 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 dark:text-gray-300",
|
|
44
|
+
},
|
|
45
|
+
size: {
|
|
46
|
+
sm: "h-8 text-xs",
|
|
47
|
+
md: "h-10 text-sm",
|
|
48
|
+
lg: "h-12 text-base",
|
|
49
|
+
},
|
|
50
|
+
hasLeftIcon: {
|
|
51
|
+
true: "pl-10",
|
|
52
|
+
false: "",
|
|
53
|
+
},
|
|
54
|
+
hasRightIcon: {
|
|
55
|
+
true: "pr-10",
|
|
56
|
+
false: "",
|
|
57
|
+
},
|
|
58
|
+
hasRightButton: {
|
|
59
|
+
true: "pr-10",
|
|
60
|
+
false: "",
|
|
61
|
+
},
|
|
62
|
+
isError: {
|
|
63
|
+
true: "border-error focus-visible:ring-error/30 focus-visible:border-error hover:border-error/80 dark:hover:border-error/80",
|
|
64
|
+
false: "",
|
|
65
|
+
},
|
|
66
|
+
isSuccess: {
|
|
67
|
+
true: "border-success focus-visible:ring-success/30 focus-visible:border-success hover:border-success/80 dark:hover:border-success/80",
|
|
68
|
+
false: "",
|
|
69
|
+
},
|
|
70
|
+
isDisabled: {
|
|
71
|
+
true: "opacity-50 cursor-not-allowed bg-gray-50 dark:bg-gray-800/50 pointer-events-none",
|
|
72
|
+
false: "",
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
defaultVariants: {
|
|
76
|
+
variant: "standard",
|
|
77
|
+
size: "md",
|
|
78
|
+
isError: false,
|
|
79
|
+
isSuccess: false,
|
|
80
|
+
isDisabled: false,
|
|
81
|
+
hasLeftIcon: false,
|
|
82
|
+
hasRightIcon: false,
|
|
83
|
+
hasRightButton: false,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
export interface InputProps
|
|
89
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">,
|
|
90
|
+
Omit<VariantProps<typeof inputVariants>, "isDisabled" | "hasLeftIcon" | "hasRightIcon" | "hasRightButton"> {
|
|
91
|
+
/** Hata mesajı */
|
|
92
|
+
error?: string;
|
|
93
|
+
/** Başarı mesajı */
|
|
94
|
+
success?: string;
|
|
95
|
+
/** Yükleniyor durumu */
|
|
96
|
+
loading?: boolean;
|
|
97
|
+
/** Sol tarafta gösterilecek ikon */
|
|
98
|
+
leftIcon?: React.ReactNode;
|
|
99
|
+
/** Sağ tarafta gösterilecek ikon */
|
|
100
|
+
rightIcon?: React.ReactNode;
|
|
101
|
+
/** Sağ tarafta gösterilecek buton (password show/hide vb. için) */
|
|
102
|
+
rightButton?: React.ReactNode;
|
|
103
|
+
/** Mesajın görünürlüğü (true: her zaman görünür, false: sadece hata/başarı durumunda) */
|
|
104
|
+
alwaysShowMessage?: boolean;
|
|
105
|
+
/** Input wrapper için ek sınıflar */
|
|
106
|
+
wrapperClassName?: string;
|
|
107
|
+
/** Mesaj için ek sınıflar */
|
|
108
|
+
messageClassName?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Premium Input Component
|
|
113
|
+
*
|
|
114
|
+
* @param props - Input bileşeni özellikleri
|
|
115
|
+
* @param props.variant - Görsel varyant (standard, filled, ghost, underline)
|
|
116
|
+
* @param props.size - Boyut (sm, md, lg)
|
|
117
|
+
* @param props.error - Hata mesajı
|
|
118
|
+
* @param props.success - Başarı mesajı
|
|
119
|
+
* @param props.loading - Yükleniyor durumu
|
|
120
|
+
* @param props.leftIcon - Sol tarafta gösterilecek ikon
|
|
121
|
+
* @param props.rightIcon - Sağ tarafta gösterilecek ikon
|
|
122
|
+
* @param props.rightButton - Sağ tarafta gösterilecek buton
|
|
123
|
+
* @param props.alwaysShowMessage - Mesajın her zaman görünür olması
|
|
124
|
+
*/
|
|
125
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
126
|
+
({
|
|
127
|
+
className,
|
|
128
|
+
wrapperClassName,
|
|
129
|
+
messageClassName,
|
|
130
|
+
variant,
|
|
131
|
+
size,
|
|
132
|
+
isError,
|
|
133
|
+
disabled,
|
|
134
|
+
error,
|
|
135
|
+
success,
|
|
136
|
+
loading,
|
|
137
|
+
leftIcon,
|
|
138
|
+
rightIcon,
|
|
139
|
+
rightButton,
|
|
140
|
+
alwaysShowMessage = false,
|
|
141
|
+
...props
|
|
142
|
+
}, ref) => {
|
|
143
|
+
// Mesajı göster/gizle
|
|
144
|
+
const showMessage = alwaysShowMessage || error || success;
|
|
145
|
+
const messageType = error ? "error" : success ? "success" : "normal";
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="space-y-1.5 w-full">
|
|
149
|
+
<div className={cn(inputWrapperVariants({ size }), wrapperClassName)}>
|
|
150
|
+
{leftIcon && (
|
|
151
|
+
<div className="absolute left-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
152
|
+
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : leftIcon}
|
|
153
|
+
</div>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
<input
|
|
157
|
+
className={cn(
|
|
158
|
+
inputVariants({
|
|
159
|
+
variant,
|
|
160
|
+
size,
|
|
161
|
+
isError: !!error || isError,
|
|
162
|
+
isSuccess: !!success,
|
|
163
|
+
isDisabled: disabled || loading,
|
|
164
|
+
hasLeftIcon: !!leftIcon || loading,
|
|
165
|
+
hasRightIcon: !!rightIcon,
|
|
166
|
+
hasRightButton: !!rightButton,
|
|
167
|
+
}),
|
|
168
|
+
className
|
|
169
|
+
)}
|
|
170
|
+
ref={ref}
|
|
171
|
+
disabled={disabled || loading}
|
|
172
|
+
data-loading={loading ? "" : undefined}
|
|
173
|
+
data-error={!!error ? "" : undefined}
|
|
174
|
+
data-success={!!success ? "" : undefined}
|
|
175
|
+
aria-invalid={!!error || !!isError || undefined}
|
|
176
|
+
aria-describedby={error ? `${props.id || ''}-error` : success ? `${props.id || ''}-success` : undefined}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
{rightIcon && !loading && (
|
|
181
|
+
<div className="absolute right-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
182
|
+
{rightIcon}
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{rightButton && (
|
|
187
|
+
<div className="absolute right-3">
|
|
188
|
+
{rightButton}
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
|
|
192
|
+
{loading && !leftIcon && (
|
|
193
|
+
<div className="absolute left-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
194
|
+
<Loader2 className="h-4 w-4 animate-spin" aria-hidden="true" />
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
{showMessage && (
|
|
200
|
+
<p
|
|
201
|
+
className={cn(
|
|
202
|
+
"text-xs transition-all",
|
|
203
|
+
messageType === "error" && "text-error",
|
|
204
|
+
messageType === "success" && "text-success",
|
|
205
|
+
messageType === "normal" && "text-muted-foreground",
|
|
206
|
+
messageClassName
|
|
207
|
+
)}
|
|
208
|
+
id={error ? `${props.id || ''}-error` : success ? `${props.id || ''}-success` : undefined}
|
|
209
|
+
>
|
|
210
|
+
{error || success || ""}
|
|
211
|
+
</p>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
Input.displayName = "Input";
|
|
218
|
+
|
|
219
|
+
export { Input };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils"
|
|
8
|
+
|
|
9
|
+
const labelVariants = cva(
|
|
10
|
+
"text-sm font-medium leading-none text-gray-900 dark:text-gray-200 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:peer-disabled:opacity-60 transition-colors duration-200"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const Label = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
16
|
+
VariantProps<typeof labelVariants>
|
|
17
|
+
>(({ className, ...props }, ref) => (
|
|
18
|
+
<LabelPrimitive.Root
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(labelVariants(), className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
))
|
|
24
|
+
Label.displayName = LabelPrimitive.Root.displayName
|
|
25
|
+
|
|
26
|
+
export { Label }
|