@moontra/moonui 2.3.9 → 2.4.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 +84 -7
- package/dist/index.d.ts +84 -7
- package/dist/index.global.js +30 -30
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +399 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +395 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/index.ts +8 -0
- package/src/components/ui/input.tsx +92 -19
- package/src/components/ui/pagination.tsx +1 -1
- package/src/components/ui/scroll-reveal.tsx +245 -0
- package/src/components/ui/table.tsx +40 -14
- package/src/components/ui/textarea.tsx +194 -20
package/package.json
CHANGED
|
@@ -218,6 +218,13 @@ export {
|
|
|
218
218
|
ScrollBar as MoonUIScrollBar,
|
|
219
219
|
} from "./scroll-area";
|
|
220
220
|
|
|
221
|
+
// ScrollReveal
|
|
222
|
+
export {
|
|
223
|
+
ScrollReveal as MoonUIScrollReveal,
|
|
224
|
+
ScrollRevealContainer as MoonUIScrollRevealContainer,
|
|
225
|
+
ScrollRevealItem as MoonUIScrollRevealItem,
|
|
226
|
+
} from "./scroll-reveal";
|
|
227
|
+
|
|
221
228
|
// Select
|
|
222
229
|
export {
|
|
223
230
|
Select as MoonUISelect,
|
|
@@ -342,6 +349,7 @@ export * from "./progress";
|
|
|
342
349
|
export * from "./radio-group";
|
|
343
350
|
export * from "./rich-text-editor";
|
|
344
351
|
export * from "./scroll-area";
|
|
352
|
+
export * from "./scroll-reveal";
|
|
345
353
|
export * from "./select";
|
|
346
354
|
export * from "./separator";
|
|
347
355
|
export * from "./simple-editor";
|
|
@@ -39,10 +39,10 @@ const inputVariants = cva(
|
|
|
39
39
|
{
|
|
40
40
|
variants: {
|
|
41
41
|
variant: {
|
|
42
|
-
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-
|
|
43
|
-
filled: "border border-transparent bg-gray-100 dark:bg-gray-800
|
|
42
|
+
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-800/80 dark:shadow-inner dark:shadow-gray-950/10",
|
|
43
|
+
filled: "border border-transparent bg-gray-100 dark:bg-gray-800 rounded-md px-3 py-2 hover:bg-gray-200 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/20 dark:shadow-inner dark:shadow-gray-950/10",
|
|
44
44
|
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",
|
|
45
|
-
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",
|
|
45
|
+
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 dark:bg-transparent",
|
|
46
46
|
},
|
|
47
47
|
size: {
|
|
48
48
|
sm: "h-8 text-xs",
|
|
@@ -90,6 +90,12 @@ const inputVariants = cva(
|
|
|
90
90
|
export interface InputProps
|
|
91
91
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">,
|
|
92
92
|
Omit<VariantProps<typeof inputVariants>, "isDisabled" | "hasLeftIcon" | "hasRightIcon" | "hasRightButton"> {
|
|
93
|
+
/** Label metni */
|
|
94
|
+
label?: string;
|
|
95
|
+
/** Floating label animasyonu (Material Design pattern) */
|
|
96
|
+
floatingLabel?: boolean;
|
|
97
|
+
/** Floating label için ek sınıflar */
|
|
98
|
+
floatingLabelClassName?: string;
|
|
93
99
|
/** Hata mesajı */
|
|
94
100
|
error?: string;
|
|
95
101
|
/** Başarı mesajı */
|
|
@@ -125,14 +131,14 @@ export interface InputProps
|
|
|
125
131
|
* @param props.alwaysShowMessage - Mesajın her zaman görünür olması
|
|
126
132
|
*/
|
|
127
133
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
128
|
-
({
|
|
129
|
-
className,
|
|
134
|
+
({
|
|
135
|
+
className,
|
|
130
136
|
wrapperClassName,
|
|
131
137
|
messageClassName,
|
|
132
|
-
variant,
|
|
133
|
-
size,
|
|
138
|
+
variant,
|
|
139
|
+
size,
|
|
134
140
|
isError,
|
|
135
|
-
disabled,
|
|
141
|
+
disabled,
|
|
136
142
|
error,
|
|
137
143
|
success,
|
|
138
144
|
loading,
|
|
@@ -140,8 +146,48 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
140
146
|
rightIcon,
|
|
141
147
|
rightButton,
|
|
142
148
|
alwaysShowMessage = false,
|
|
143
|
-
|
|
149
|
+
label,
|
|
150
|
+
floatingLabel = false,
|
|
151
|
+
floatingLabelClassName,
|
|
152
|
+
value,
|
|
153
|
+
defaultValue,
|
|
154
|
+
onChange,
|
|
155
|
+
onFocus,
|
|
156
|
+
onBlur,
|
|
157
|
+
placeholder,
|
|
158
|
+
...props
|
|
144
159
|
}, ref) => {
|
|
160
|
+
// Floating label state management
|
|
161
|
+
const [isFocused, setIsFocused] = React.useState(false);
|
|
162
|
+
const [internalValue, setInternalValue] = React.useState(value || defaultValue || "");
|
|
163
|
+
|
|
164
|
+
// Floating label aktif mi kontrolü
|
|
165
|
+
const hasValue = internalValue !== "" && internalValue !== null && internalValue !== undefined;
|
|
166
|
+
const isLabelActive = isFocused || hasValue;
|
|
167
|
+
|
|
168
|
+
// Event handler'lar
|
|
169
|
+
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
170
|
+
setIsFocused(true);
|
|
171
|
+
onFocus?.(e);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
175
|
+
setIsFocused(false);
|
|
176
|
+
onBlur?.(e);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
180
|
+
setInternalValue(e.target.value);
|
|
181
|
+
onChange?.(e);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Value değişikliklerini takip et
|
|
185
|
+
React.useEffect(() => {
|
|
186
|
+
if (value !== undefined) {
|
|
187
|
+
setInternalValue(value);
|
|
188
|
+
}
|
|
189
|
+
}, [value]);
|
|
190
|
+
|
|
145
191
|
// Mesajı göster/gizle
|
|
146
192
|
const showMessage = alwaysShowMessage || error || success;
|
|
147
193
|
const messageType = error ? "error" : success ? "success" : "normal";
|
|
@@ -149,18 +195,39 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
149
195
|
return (
|
|
150
196
|
<div className="space-y-1.5 w-full">
|
|
151
197
|
<div className={cn("moonui-theme", inputWrapperVariants({ size }), wrapperClassName)}>
|
|
198
|
+
{/* Floating Label */}
|
|
199
|
+
{floatingLabel && label && (
|
|
200
|
+
<label
|
|
201
|
+
htmlFor={props.id}
|
|
202
|
+
className={cn(
|
|
203
|
+
"absolute transition-all duration-200 pointer-events-none",
|
|
204
|
+
leftIcon || loading ? "left-10" : "left-3",
|
|
205
|
+
"text-gray-500 dark:text-gray-400",
|
|
206
|
+
isLabelActive
|
|
207
|
+
? "top-0 -translate-y-1/2 text-xs bg-background dark:bg-gray-800 px-1"
|
|
208
|
+
: "top-1/2 -translate-y-1/2 text-sm",
|
|
209
|
+
isFocused && "text-primary dark:text-primary",
|
|
210
|
+
!!error && "text-error",
|
|
211
|
+
disabled && "opacity-50",
|
|
212
|
+
floatingLabelClassName
|
|
213
|
+
)}
|
|
214
|
+
>
|
|
215
|
+
{label}
|
|
216
|
+
</label>
|
|
217
|
+
)}
|
|
218
|
+
|
|
152
219
|
{leftIcon && (
|
|
153
220
|
<div className="absolute left-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
154
221
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : leftIcon}
|
|
155
222
|
</div>
|
|
156
223
|
)}
|
|
157
|
-
|
|
224
|
+
|
|
158
225
|
<input
|
|
159
226
|
className={cn(
|
|
160
|
-
inputVariants({
|
|
161
|
-
variant,
|
|
162
|
-
size,
|
|
163
|
-
isError: !!error || isError,
|
|
227
|
+
inputVariants({
|
|
228
|
+
variant,
|
|
229
|
+
size,
|
|
230
|
+
isError: !!error || isError,
|
|
164
231
|
isSuccess: !!success,
|
|
165
232
|
isDisabled: disabled || loading,
|
|
166
233
|
hasLeftIcon: !!leftIcon || loading,
|
|
@@ -176,30 +243,36 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
176
243
|
data-success={!!success ? "" : undefined}
|
|
177
244
|
aria-invalid={!!error || !!isError || undefined}
|
|
178
245
|
aria-describedby={error ? `${props.id || ''}-error` : success ? `${props.id || ''}-success` : undefined}
|
|
246
|
+
value={value}
|
|
247
|
+
defaultValue={defaultValue}
|
|
248
|
+
onChange={handleChange}
|
|
249
|
+
onFocus={handleFocus}
|
|
250
|
+
onBlur={handleBlur}
|
|
251
|
+
placeholder={floatingLabel ? (isLabelActive ? placeholder : "") : placeholder}
|
|
179
252
|
{...props}
|
|
180
253
|
/>
|
|
181
|
-
|
|
254
|
+
|
|
182
255
|
{rightIcon && !loading && (
|
|
183
256
|
<div className="absolute right-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
184
257
|
{rightIcon}
|
|
185
258
|
</div>
|
|
186
259
|
)}
|
|
187
|
-
|
|
260
|
+
|
|
188
261
|
{rightButton && (
|
|
189
262
|
<div className="absolute right-3">
|
|
190
263
|
{rightButton}
|
|
191
264
|
</div>
|
|
192
265
|
)}
|
|
193
|
-
|
|
266
|
+
|
|
194
267
|
{loading && !leftIcon && (
|
|
195
268
|
<div className="absolute left-3 text-gray-500 flex items-center justify-center pointer-events-none">
|
|
196
269
|
<Loader2 className="h-4 w-4 animate-spin" aria-hidden="true" />
|
|
197
270
|
</div>
|
|
198
271
|
)}
|
|
199
272
|
</div>
|
|
200
|
-
|
|
273
|
+
|
|
201
274
|
{showMessage && (
|
|
202
|
-
<p
|
|
275
|
+
<p
|
|
203
276
|
className={cn(
|
|
204
277
|
"text-xs transition-all",
|
|
205
278
|
messageType === "error" && "text-error",
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { motion, useInView, useAnimation, Variants } from "framer-motion"
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
interface ScrollRevealProps {
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
direction?: "up" | "down" | "left" | "right"
|
|
10
|
+
delay?: number
|
|
11
|
+
duration?: number
|
|
12
|
+
distance?: number
|
|
13
|
+
threshold?: number
|
|
14
|
+
triggerOnce?: boolean
|
|
15
|
+
stagger?: number
|
|
16
|
+
className?: string
|
|
17
|
+
variants?: Variants
|
|
18
|
+
animate?: boolean
|
|
19
|
+
style?: React.CSSProperties
|
|
20
|
+
id?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const defaultVariants: Record<string, Variants> = {
|
|
24
|
+
up: {
|
|
25
|
+
hidden: { opacity: 0, y: 50 },
|
|
26
|
+
visible: { opacity: 1, y: 0 }
|
|
27
|
+
},
|
|
28
|
+
down: {
|
|
29
|
+
hidden: { opacity: 0, y: -50 },
|
|
30
|
+
visible: { opacity: 1, y: 0 }
|
|
31
|
+
},
|
|
32
|
+
left: {
|
|
33
|
+
hidden: { opacity: 0, x: 50 },
|
|
34
|
+
visible: { opacity: 1, x: 0 }
|
|
35
|
+
},
|
|
36
|
+
right: {
|
|
37
|
+
hidden: { opacity: 0, x: -50 },
|
|
38
|
+
visible: { opacity: 1, x: 0 }
|
|
39
|
+
},
|
|
40
|
+
fade: {
|
|
41
|
+
hidden: { opacity: 0 },
|
|
42
|
+
visible: { opacity: 1 }
|
|
43
|
+
},
|
|
44
|
+
scale: {
|
|
45
|
+
hidden: { opacity: 0, scale: 0.8 },
|
|
46
|
+
visible: { opacity: 1, scale: 1 }
|
|
47
|
+
},
|
|
48
|
+
blur: {
|
|
49
|
+
hidden: { opacity: 0, filter: "blur(10px)" },
|
|
50
|
+
visible: { opacity: 1, filter: "blur(0px)" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ScrollReveal = React.forwardRef<HTMLDivElement, ScrollRevealProps>(
|
|
55
|
+
({
|
|
56
|
+
children,
|
|
57
|
+
direction = "up",
|
|
58
|
+
delay = 0,
|
|
59
|
+
duration = 0.6,
|
|
60
|
+
distance = 50,
|
|
61
|
+
threshold = 0.1,
|
|
62
|
+
triggerOnce = true,
|
|
63
|
+
stagger = 0,
|
|
64
|
+
className,
|
|
65
|
+
variants,
|
|
66
|
+
animate = true,
|
|
67
|
+
style,
|
|
68
|
+
id
|
|
69
|
+
}, ref) => {
|
|
70
|
+
const controls = useAnimation()
|
|
71
|
+
const elementRef = React.useRef<HTMLDivElement>(null)
|
|
72
|
+
const isInView = useInView(elementRef, {
|
|
73
|
+
amount: threshold,
|
|
74
|
+
once: triggerOnce
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Custom variants with distance
|
|
78
|
+
const customVariants = React.useMemo(() => {
|
|
79
|
+
if (variants) return variants
|
|
80
|
+
|
|
81
|
+
const baseVariants = defaultVariants[direction] || defaultVariants.up
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
hidden: {
|
|
85
|
+
...baseVariants.hidden,
|
|
86
|
+
...(direction === "up" && { y: distance }),
|
|
87
|
+
...(direction === "down" && { y: -distance }),
|
|
88
|
+
...(direction === "left" && { x: distance }),
|
|
89
|
+
...(direction === "right" && { x: -distance })
|
|
90
|
+
},
|
|
91
|
+
visible: baseVariants.visible
|
|
92
|
+
}
|
|
93
|
+
}, [direction, distance, variants])
|
|
94
|
+
|
|
95
|
+
React.useEffect(() => {
|
|
96
|
+
if (!animate) return
|
|
97
|
+
|
|
98
|
+
if (isInView) {
|
|
99
|
+
controls.start("visible")
|
|
100
|
+
} else if (!triggerOnce) {
|
|
101
|
+
controls.start("hidden")
|
|
102
|
+
}
|
|
103
|
+
}, [isInView, controls, triggerOnce, animate])
|
|
104
|
+
|
|
105
|
+
React.useImperativeHandle(ref, () => elementRef.current!)
|
|
106
|
+
|
|
107
|
+
if (!animate) {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
ref={elementRef}
|
|
111
|
+
className={className}
|
|
112
|
+
style={style}
|
|
113
|
+
id={id}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<motion.div
|
|
122
|
+
ref={elementRef}
|
|
123
|
+
variants={customVariants}
|
|
124
|
+
initial="hidden"
|
|
125
|
+
animate={controls}
|
|
126
|
+
transition={{
|
|
127
|
+
duration,
|
|
128
|
+
delay: delay + stagger,
|
|
129
|
+
ease: "easeOut"
|
|
130
|
+
}}
|
|
131
|
+
className={cn(className)}
|
|
132
|
+
style={style}
|
|
133
|
+
id={id}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
</motion.div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
ScrollReveal.displayName = "ScrollReveal"
|
|
142
|
+
|
|
143
|
+
// ScrollReveal container for staggered animations
|
|
144
|
+
interface ScrollRevealContainerProps {
|
|
145
|
+
children: React.ReactNode
|
|
146
|
+
stagger?: number
|
|
147
|
+
className?: string
|
|
148
|
+
style?: React.CSSProperties
|
|
149
|
+
id?: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const ScrollRevealContainer = React.forwardRef<HTMLDivElement, ScrollRevealContainerProps>(
|
|
153
|
+
({ children, stagger = 0.1, className, style, id }, ref) => {
|
|
154
|
+
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
155
|
+
const isInView = useInView(containerRef, { amount: 0.1, once: true })
|
|
156
|
+
|
|
157
|
+
React.useImperativeHandle(ref, () => containerRef.current!)
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<motion.div
|
|
161
|
+
ref={containerRef}
|
|
162
|
+
initial="hidden"
|
|
163
|
+
animate={isInView ? "visible" : "hidden"}
|
|
164
|
+
variants={{
|
|
165
|
+
hidden: {},
|
|
166
|
+
visible: {
|
|
167
|
+
transition: {
|
|
168
|
+
staggerChildren: stagger
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}}
|
|
172
|
+
className={cn(className)}
|
|
173
|
+
style={style}
|
|
174
|
+
id={id}
|
|
175
|
+
>
|
|
176
|
+
{children}
|
|
177
|
+
</motion.div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
ScrollRevealContainer.displayName = "ScrollRevealContainer"
|
|
183
|
+
|
|
184
|
+
// ScrollReveal item for use within containers
|
|
185
|
+
interface ScrollRevealItemProps {
|
|
186
|
+
children: React.ReactNode
|
|
187
|
+
direction?: "up" | "down" | "left" | "right"
|
|
188
|
+
duration?: number
|
|
189
|
+
distance?: number
|
|
190
|
+
className?: string
|
|
191
|
+
variants?: Variants
|
|
192
|
+
style?: React.CSSProperties
|
|
193
|
+
id?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const ScrollRevealItem = React.forwardRef<HTMLDivElement, ScrollRevealItemProps>(
|
|
197
|
+
({
|
|
198
|
+
children,
|
|
199
|
+
direction = "up",
|
|
200
|
+
duration = 0.6,
|
|
201
|
+
distance = 50,
|
|
202
|
+
className,
|
|
203
|
+
variants,
|
|
204
|
+
style,
|
|
205
|
+
id
|
|
206
|
+
}, ref) => {
|
|
207
|
+
const customVariants = React.useMemo(() => {
|
|
208
|
+
if (variants) return variants
|
|
209
|
+
|
|
210
|
+
const baseVariants = defaultVariants[direction] || defaultVariants.up
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
hidden: {
|
|
214
|
+
...baseVariants.hidden,
|
|
215
|
+
...(direction === "up" && { y: distance }),
|
|
216
|
+
...(direction === "down" && { y: -distance }),
|
|
217
|
+
...(direction === "left" && { x: distance }),
|
|
218
|
+
...(direction === "right" && { x: -distance })
|
|
219
|
+
},
|
|
220
|
+
visible: baseVariants.visible
|
|
221
|
+
}
|
|
222
|
+
}, [direction, distance, variants])
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<motion.div
|
|
226
|
+
ref={ref}
|
|
227
|
+
variants={customVariants}
|
|
228
|
+
transition={{
|
|
229
|
+
duration,
|
|
230
|
+
ease: "easeOut"
|
|
231
|
+
}}
|
|
232
|
+
className={cn(className)}
|
|
233
|
+
style={style}
|
|
234
|
+
id={id}
|
|
235
|
+
>
|
|
236
|
+
{children}
|
|
237
|
+
</motion.div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
ScrollRevealItem.displayName = "ScrollRevealItem"
|
|
243
|
+
|
|
244
|
+
export { ScrollReveal, ScrollRevealContainer, ScrollRevealItem }
|
|
245
|
+
export type { ScrollRevealProps, ScrollRevealContainerProps, ScrollRevealItemProps }
|
|
@@ -207,10 +207,12 @@ interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
|
207
207
|
sorted?: SortDirection;
|
|
208
208
|
/** Sıralama değiştiğinde çağrılacak fonksiyon */
|
|
209
209
|
onSort?: () => void;
|
|
210
|
+
/** Text alignment */
|
|
211
|
+
align?: 'left' | 'center' | 'right';
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
213
|
-
({ className, sortable, sorted, onSort, children, ...props }, ref) => {
|
|
215
|
+
({ className, sortable, sorted, onSort, align = 'left', children, ...props }, ref) => {
|
|
214
216
|
// Sıralama için simgeler
|
|
215
217
|
const renderSortIcon = () => {
|
|
216
218
|
if (!sortable) return null;
|
|
@@ -265,37 +267,61 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
265
267
|
);
|
|
266
268
|
};
|
|
267
269
|
|
|
270
|
+
const alignmentClasses = {
|
|
271
|
+
left: 'text-left justify-start',
|
|
272
|
+
center: 'text-center justify-center',
|
|
273
|
+
right: 'text-right justify-end'
|
|
274
|
+
};
|
|
275
|
+
|
|
268
276
|
return (
|
|
269
277
|
<th
|
|
270
278
|
ref={ref}
|
|
271
279
|
className={cn(
|
|
272
|
-
"h-10 px-4
|
|
280
|
+
"h-10 px-4 align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
281
|
+
alignmentClasses[align].split(' ')[0], // text alignment
|
|
273
282
|
sortable && "cursor-pointer hover:text-foreground select-none",
|
|
274
283
|
className
|
|
275
284
|
)}
|
|
276
285
|
onClick={sortable ? onSort : undefined}
|
|
277
286
|
{...props}
|
|
278
287
|
>
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
288
|
+
{sortable || align !== 'left' ? (
|
|
289
|
+
<div className={cn("flex items-center", alignmentClasses[align].split(' ')[1])}>
|
|
290
|
+
{children}
|
|
291
|
+
{sortable && renderSortIcon()}
|
|
292
|
+
</div>
|
|
293
|
+
) : (
|
|
294
|
+
children
|
|
295
|
+
)}
|
|
283
296
|
</th>
|
|
284
297
|
);
|
|
285
298
|
}
|
|
286
299
|
);
|
|
287
300
|
TableHead.displayName = "TableHead";
|
|
288
301
|
|
|
302
|
+
interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
303
|
+
/** Text alignment */
|
|
304
|
+
align?: 'left' | 'center' | 'right';
|
|
305
|
+
}
|
|
306
|
+
|
|
289
307
|
const TableCell = React.forwardRef<
|
|
290
308
|
HTMLTableCellElement,
|
|
291
|
-
|
|
292
|
-
>(({ className, ...props }, ref) =>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
309
|
+
TableCellProps
|
|
310
|
+
>(({ className, align = 'left', ...props }, ref) => {
|
|
311
|
+
const alignmentClass = {
|
|
312
|
+
left: 'text-left',
|
|
313
|
+
center: 'text-center',
|
|
314
|
+
right: 'text-right'
|
|
315
|
+
}[align];
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<td
|
|
319
|
+
ref={ref}
|
|
320
|
+
className={cn("moonui-theme", "p-4 align-middle [&:has([role=checkbox])]:pr-0", alignmentClass, className)}
|
|
321
|
+
{...props}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
});
|
|
299
325
|
TableCell.displayName = "TableCell";
|
|
300
326
|
|
|
301
327
|
const TableCaption = React.forwardRef<
|