@moontra/moonui 6.10.0 → 6.12.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/hooks/index.global.js +1 -25
- package/dist/hooks/index.global.js.map +1 -1
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.global.js +365 -52
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +62 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -37
- package/dist/index.mjs.map +1 -1
- package/dist/lib/theme.global.js +14 -1
- package/dist/lib/theme.global.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/__tests__/card-size-aschild.test.tsx +119 -0
- package/src/components/ui/__tests__/preset-color-shadowing.test.ts +71 -0
- package/src/components/ui/__tests__/semantic-token-usage.test.ts +120 -0
- package/src/components/ui/__tests__/toast.test.tsx +6 -3
- package/src/components/ui/__tests__/tooltip.test.tsx +8 -4
- package/src/components/ui/card.tsx +96 -41
- package/src/components/ui/toast.tsx +4 -4
- package/src/components/ui/tooltip.tsx +3 -3
- package/src/styles/tokens.css +43 -2
- package/tailwind-preset.js +17 -7
- package/src/use-toast.ts +0 -32
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #313 / #314 / #323 — semantik durum varyantlarında ham palet bekçisi.
|
|
3
|
+
*
|
|
4
|
+
* Denetimin bulgusu: `destructive`/`success`/`warning`/`info` durum varyantları paketin
|
|
5
|
+
* kendi token'ları yerine ham Tailwind paletine (`bg-red-100 text-red-900`) düşmüştü.
|
|
6
|
+
* Sonuç görsel değil **işlevsel**: marka/tema özelleştirmesi bu varyantlara İŞLEMİYORDU —
|
|
7
|
+
* tüketici `--destructive`'i değiştirdiğinde menü item'ı ve toast eski kırmızıda kalıyordu.
|
|
8
|
+
*
|
|
9
|
+
* Çözüm `--*-subtle` token ailesiydi (düz `--destructive` "dolu zemin + açık metin" içindir;
|
|
10
|
+
* yumuşak tint + koyu metin için karşılığı yoktu). Bu test o dönüşümün geri gelmemesini korur.
|
|
11
|
+
*
|
|
12
|
+
* KAPSAM NOTU: kural yalnızca **durum varyantı** tanımlayan cva bloklarını hedefler.
|
|
13
|
+
* `premium` gibi DEKORATİF aksanlar (altın/gradient) kapsam dışıdır — bir "premium" rozetini
|
|
14
|
+
* `--warning`e bağlamak semantik olarak yanlış olurdu; onlar ayrı bir token ailesi ister.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs'
|
|
17
|
+
import * as path from 'path'
|
|
18
|
+
|
|
19
|
+
/** Ham Tailwind palet ailesi — semantik durumların yerine geçmemeli. */
|
|
20
|
+
const HAM_PALET = /\b(?:bg|text|border|ring|from|to|via)-(red|green|yellow|orange|blue)-\d{2,3}\b/
|
|
21
|
+
|
|
22
|
+
/** Durum varyantı adları — cva içinde bu anahtarların değerleri token kullanmalı. */
|
|
23
|
+
const DURUM_ANAHTARLARI = ['destructive', 'success', 'warning', 'info', 'error']
|
|
24
|
+
|
|
25
|
+
interface Ihlal {
|
|
26
|
+
file: string
|
|
27
|
+
line: number
|
|
28
|
+
anahtar: string
|
|
29
|
+
parca: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function tara(root: string): Ihlal[] {
|
|
33
|
+
const files: string[] = []
|
|
34
|
+
;(function walk(dir: string) {
|
|
35
|
+
if (!fs.existsSync(dir)) return
|
|
36
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
37
|
+
const p = path.join(dir, e.name)
|
|
38
|
+
if (e.isDirectory()) {
|
|
39
|
+
if (!/node_modules|__tests__|dist/.test(p)) walk(p)
|
|
40
|
+
} else if (/\.tsx?$/.test(e.name) && !/\.stories\./.test(e.name)) files.push(p)
|
|
41
|
+
}
|
|
42
|
+
})(root)
|
|
43
|
+
|
|
44
|
+
const ihlaller: Ihlal[] = []
|
|
45
|
+
for (const f of files) {
|
|
46
|
+
const src = fs.readFileSync(f, 'utf8')
|
|
47
|
+
for (const anahtar of DURUM_ANAHTARLARI) {
|
|
48
|
+
// `destructive: "…"` biçimindeki cva varyant girdisini yakala
|
|
49
|
+
const re = new RegExp(`\\n[ \\t]*${anahtar}:\\s*(["'])([\\s\\S]*?)\\1`, 'g')
|
|
50
|
+
for (const m of src.matchAll(re)) {
|
|
51
|
+
const deger = m[2]
|
|
52
|
+
const hit = deger.match(HAM_PALET)
|
|
53
|
+
if (hit) {
|
|
54
|
+
ihlaller.push({
|
|
55
|
+
file: path.relative(process.cwd(), f),
|
|
56
|
+
line: src.slice(0, m.index).split('\n').length + 1,
|
|
57
|
+
anahtar,
|
|
58
|
+
parca: hit[0],
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ihlaller
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* BİLİNEN KALAN İHLALLER — cırcır (ratchet) listesi.
|
|
69
|
+
*
|
|
70
|
+
* Bu PR (#313/#314/#323) menü + toast ailesini dönüştürdü. Bekçi kurulunca aynı desenin
|
|
71
|
+
* kütüphanede DAHA GENİŞ olduğu ortaya çıktı: 7 dosyada 21 ihlal daha. Onları bu PR'a
|
|
72
|
+
* çekmek kapsamı ilgisiz component'lere (dashboard widget'ları, timeline, avatar-pro…)
|
|
73
|
+
* yayardı; bu yüzden burada AÇIKÇA listeleniyorlar.
|
|
74
|
+
*
|
|
75
|
+
* Kural cırcır gibi çalışır: listedekiler tolere edilir, **yeni** ihlal kırmızı olur.
|
|
76
|
+
* Takip işi bu dosyaları dönüştürüp satırları buradan siler — liste boşalınca kural
|
|
77
|
+
* tam sıkı hale gelir.
|
|
78
|
+
*/
|
|
79
|
+
const BILINEN_KALANLAR: string[] = []
|
|
80
|
+
|
|
81
|
+
const SRC_ROOT = path.resolve(__dirname, '../../..')
|
|
82
|
+
const tumIhlaller = tara(SRC_ROOT)
|
|
83
|
+
const ihlaller = tumIhlaller.filter(
|
|
84
|
+
(i) => !BILINEN_KALANLAR.some((b) => i.file.replace(/\\/g, '/').endsWith(b))
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
describe('#313/#314/#323 semantik durum varyantları token kullanır', () => {
|
|
88
|
+
it('tarayıcı gerçekten dosya buluyor (sessiz yeşil koruması)', () => {
|
|
89
|
+
// Regex/yol bozulursa test "0 ihlal" ile yeşil geçerdi. Bilinen bir durum
|
|
90
|
+
// varyantının var olduğunu doğrulayarak tarayıcının canlı olduğunu kanıtlıyoruz.
|
|
91
|
+
const toast = fs.readFileSync(
|
|
92
|
+
path.join(SRC_ROOT, 'components/ui/toast.tsx'),
|
|
93
|
+
'utf8'
|
|
94
|
+
)
|
|
95
|
+
expect(toast).toContain('success:')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('durum varyantlarında ham Tailwind paleti YOK (bilinen kalanlar hariç)', () => {
|
|
99
|
+
const rapor = ihlaller.map((i) => `${i.file}:${i.line} → ${i.anahtar} içinde "${i.parca}"`)
|
|
100
|
+
expect(rapor).toEqual([])
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('cırcır listesi ÇALIŞIYOR — listelenen dosyalar gerçekten hâlâ ihlalli', () => {
|
|
104
|
+
// Bir dosya dönüştürüldüğünde bu test kırmızı olur ve satırı listeden silmeyi hatırlatır.
|
|
105
|
+
// Aksi hâlde liste sonsuza dek şişer ve kural sessizce gevşer.
|
|
106
|
+
const halaIhlalli = new Set(
|
|
107
|
+
tumIhlaller.map((i) => BILINEN_KALANLAR.find((b) => i.file.replace(/\\/g, '/').endsWith(b)))
|
|
108
|
+
)
|
|
109
|
+
const gereksizListelenen = BILINEN_KALANLAR.filter((b) => !halaIhlalli.has(b))
|
|
110
|
+
expect(gereksizListelenen).toEqual([])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('toast success/warning/info gerçekten `--*-subtle` token\'larını kullanıyor', () => {
|
|
114
|
+
const toast = fs.readFileSync(path.join(SRC_ROOT, 'components/ui/toast.tsx'), 'utf8')
|
|
115
|
+
expect(toast).toContain('bg-success-subtle')
|
|
116
|
+
expect(toast).toContain('text-success-subtle-foreground')
|
|
117
|
+
expect(toast).toContain('bg-warning-subtle')
|
|
118
|
+
expect(toast).toContain('bg-info-subtle')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
@@ -146,9 +146,12 @@ describe('Toast Components', () => {
|
|
|
146
146
|
const variants = [
|
|
147
147
|
{ variant: 'default', classes: ['bg-background', 'text-foreground'] },
|
|
148
148
|
{ variant: 'destructive', classes: ['bg-destructive', 'text-destructive-foreground'] },
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
// #323: bu üç varyant ham Tailwind paleti kullanıyordu (`bg-green-50` vb.) →
|
|
150
|
+
// marka/tema özelleştirmesi onlara İŞLEMİYORDU. Artık `--*-subtle` semantik
|
|
151
|
+
// token'ları. Kontrast korundu (8.70 / 8.83 / 9.52 — ölçüldü, bkz. changelog).
|
|
152
|
+
{ variant: 'success', classes: ['bg-success-subtle', 'text-success-subtle-foreground'] },
|
|
153
|
+
{ variant: 'warning', classes: ['bg-warning-subtle', 'text-warning-subtle-foreground'] },
|
|
154
|
+
{ variant: 'info', classes: ['bg-info-subtle', 'text-info-subtle-foreground'] },
|
|
152
155
|
] as const
|
|
153
156
|
|
|
154
157
|
variants.forEach(({ variant, classes }) => {
|
|
@@ -156,10 +156,14 @@ describe('Tooltip Components', () => {
|
|
|
156
156
|
const variants = [
|
|
157
157
|
{ variant: 'default', className: 'bg-primary', textClassName: 'text-primary-foreground' },
|
|
158
158
|
{ variant: 'secondary', className: 'bg-secondary', textClassName: 'text-secondary-foreground' },
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
// #313/#314/#323: bu üçü ham paletti ve KONTRASTI FELAKETTİ — ölçüldü:
|
|
160
|
+
// bg-green-500 + text-white = 2.28, bg-yellow-500 + text-white = 1.92 (AA 4.5).
|
|
161
|
+
// Semantik token'a geçince 4.67 / 8.18 oldu. Test eski (kırık) değerleri
|
|
162
|
+
// sabitliyordu; artık token çiftini doğruluyor.
|
|
163
|
+
{ variant: 'success', className: 'bg-success', textClassName: 'text-success-foreground' },
|
|
164
|
+
{ variant: 'warning', className: 'bg-warning', textClassName: 'text-warning-foreground' },
|
|
161
165
|
{ variant: 'destructive', className: 'bg-destructive', textClassName: 'text-destructive-foreground' },
|
|
162
|
-
{ variant: 'info', className: 'bg-
|
|
166
|
+
{ variant: 'info', className: 'bg-info', textClassName: 'text-info-foreground' },
|
|
163
167
|
] as const
|
|
164
168
|
|
|
165
169
|
variants.forEach(({ variant, className, textClassName }) => {
|
|
@@ -659,7 +663,7 @@ describe('Tooltip Components', () => {
|
|
|
659
663
|
|
|
660
664
|
const tooltip = getTooltipContent()
|
|
661
665
|
expect(tooltip).toBeInTheDocument()
|
|
662
|
-
expect(tooltip).toHaveClass('bg-
|
|
666
|
+
expect(tooltip).toHaveClass('bg-success')
|
|
663
667
|
expect(tooltip).toHaveClass('text-base')
|
|
664
668
|
expect(tooltip).toHaveClass('custom-tooltip')
|
|
665
669
|
expect(tooltip).toHaveAttribute('data-side', 'bottom')
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
import { motion, type HTMLMotionProps } from "framer-motion";
|
|
6
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
6
7
|
import { cn } from "../../lib/utils";
|
|
7
8
|
import { microInteractionVariants, hoverAnimations, tapAnimations } from "@/lib/micro-interactions";
|
|
8
9
|
|
|
@@ -11,16 +12,14 @@ import { microInteractionVariants, hoverAnimations, tapAnimations } from "@/lib/
|
|
|
11
12
|
// hiç uygulanmıyordu; kart kökün `dark:bg-gray-900` değerinde kalıyordu.
|
|
12
13
|
// Semantik `bg-card` token'ına bağlandı.
|
|
13
14
|
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// Radix `Slot` bağımlılığı + child-tek-eleman sözleşmesi demek.
|
|
23
|
-
// İkisi de #310'da açık kalıyor; bu PR ölü SINIF kısmını kapatıyor.
|
|
15
|
+
// #310 (ikinci tur): `size` ve `asChild` ARTIK GERÇEK.
|
|
16
|
+
// • `size`: CVA'daki dört değer de boş stringdi ve `compoundVariants` yoktu — prop
|
|
17
|
+
// kabul ediliyor, hiçbir görsel fark üretmiyordu. Kökte uygulanacak doğal bir hedefi
|
|
18
|
+
// de yok, çünkü padding `CardHeader/Content/Footer`'ın kendi `p-6`'sında. Bu yüzden
|
|
19
|
+
// kök `size`'ı CONTEXT ile alt bölümlere dağıtıyor (aşağıda).
|
|
20
|
+
// KRİTİK: `md` bugünkü `p-6`'yı KORUR → varsayılan kullanımda görsel değişim YOK.
|
|
21
|
+
// • `asChild`: yalnız tipteydi, gövdede hiç okunmuyordu → `...props` ile native div'e
|
|
22
|
+
// sızıyor (`<div aschild="true">`, React uyarısı). Radix `Slot`a bağlandı.
|
|
24
23
|
|
|
25
24
|
const cardVariants = cva(
|
|
26
25
|
"rounded-lg border bg-card text-card-foreground shadow-sm dark:shadow-gray-900/20 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 transition-all duration-200",
|
|
@@ -62,6 +61,29 @@ const cardVariants = cva(
|
|
|
62
61
|
}
|
|
63
62
|
);
|
|
64
63
|
|
|
64
|
+
/**
|
|
65
|
+
* #310: Kart bölümlerinin padding ölçeği. `md` = bugünkü `p-6` — bu, mevcut tüketicide
|
|
66
|
+
* görsel değişim OLMAMASI için zorunlu (varsayılan `size` zaten `md`).
|
|
67
|
+
*/
|
|
68
|
+
type CardSize = "sm" | "md" | "lg" | "xl";
|
|
69
|
+
|
|
70
|
+
const CARD_SECTION_PADDING: Record<CardSize, string> = {
|
|
71
|
+
sm: "p-4",
|
|
72
|
+
md: "p-6",
|
|
73
|
+
lg: "p-8",
|
|
74
|
+
xl: "p-10",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Kök `Card`ın çözülmüş `size`ını alt bölümlere taşıyan tek kaynak. Alt component'ler
|
|
79
|
+
* `Card` dışında da tek başına kullanılabildiği için context varsayılanı `md`dir.
|
|
80
|
+
*/
|
|
81
|
+
const CardSizeContext = React.createContext<CardSize>("md");
|
|
82
|
+
|
|
83
|
+
/** `default` deprecated alias'tır (#319); kanonik `md`ye eşlenir. */
|
|
84
|
+
const resolveCardSize = (size: CardProps["size"]): CardSize =>
|
|
85
|
+
size && size !== "default" ? size : "md";
|
|
86
|
+
|
|
65
87
|
export interface CardProps
|
|
66
88
|
extends React.HTMLAttributes<HTMLDivElement>,
|
|
67
89
|
VariantProps<typeof cardVariants> {
|
|
@@ -70,7 +92,25 @@ export interface CardProps
|
|
|
70
92
|
}
|
|
71
93
|
|
|
72
94
|
const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|
73
|
-
({ className, variant, size, radius, interactive, microInteraction = "lift", ...props }, ref) => {
|
|
95
|
+
({ className, variant, size, radius, interactive, asChild = false, microInteraction = "lift", ...props }, ref) => {
|
|
96
|
+
const resolvedSize = resolveCardSize(size);
|
|
97
|
+
const rootClassName = cn(
|
|
98
|
+
"moonui-theme",
|
|
99
|
+
cardVariants({ variant, size, radius, interactive, className })
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// #310: `asChild` Radix `Slot`a bağlandı. Slot ile motion sarmalayıcı BİRLİKTE
|
|
103
|
+
// kullanılamaz (Slot tek çocuğa prop birleştirir, motion kendi elemanını ister) —
|
|
104
|
+
// bu yüzden asChild verildiğinde micro-interaction atlanır ve bu davranış
|
|
105
|
+
// belgelenir. Görsel etkileşimi tüketici kendi elemanında kurar.
|
|
106
|
+
if (asChild) {
|
|
107
|
+
return (
|
|
108
|
+
<CardSizeContext.Provider value={resolvedSize}>
|
|
109
|
+
<Slot ref={ref} className={rootClassName} {...props} />
|
|
110
|
+
</CardSizeContext.Provider>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
74
114
|
// Interactive özelliği varsa motion.div, yoksa normal div kullan
|
|
75
115
|
if (interactive && microInteraction !== "none") {
|
|
76
116
|
const interactionProps = {
|
|
@@ -81,22 +121,22 @@ const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
|
|
81
121
|
};
|
|
82
122
|
|
|
83
123
|
return (
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
<CardSizeContext.Provider value={resolvedSize}>
|
|
125
|
+
<motion.div
|
|
126
|
+
ref={ref}
|
|
127
|
+
className={rootClassName}
|
|
128
|
+
{...interactionProps}
|
|
129
|
+
{...(props as any)}
|
|
130
|
+
/>
|
|
131
|
+
</CardSizeContext.Provider>
|
|
90
132
|
);
|
|
91
133
|
}
|
|
92
|
-
|
|
134
|
+
|
|
93
135
|
// Non-interactive version
|
|
94
136
|
return (
|
|
95
|
-
<
|
|
96
|
-
ref={ref}
|
|
97
|
-
|
|
98
|
-
{...props}
|
|
99
|
-
/>
|
|
137
|
+
<CardSizeContext.Provider value={resolvedSize}>
|
|
138
|
+
<div ref={ref} className={rootClassName} {...props} />
|
|
139
|
+
</CardSizeContext.Provider>
|
|
100
140
|
);
|
|
101
141
|
}
|
|
102
142
|
);
|
|
@@ -105,13 +145,17 @@ Card.displayName = "Card";
|
|
|
105
145
|
const CardHeader = React.forwardRef<
|
|
106
146
|
HTMLDivElement,
|
|
107
147
|
React.HTMLAttributes<HTMLDivElement>
|
|
108
|
-
>(({ className, ...props }, ref) =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
148
|
+
>(({ className, ...props }, ref) => {
|
|
149
|
+
// #310: padding artık kök `Card`ın `size`ından geliyor (eskiden sabit `p-6`).
|
|
150
|
+
const padding = CARD_SECTION_PADDING[React.useContext(CardSizeContext)];
|
|
151
|
+
return (
|
|
152
|
+
<div
|
|
153
|
+
ref={ref}
|
|
154
|
+
className={cn("moonui-theme", "flex flex-col space-y-1.5", padding, className)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
});
|
|
115
159
|
CardHeader.displayName = "CardHeader";
|
|
116
160
|
|
|
117
161
|
const CardTitle = React.forwardRef<
|
|
@@ -141,21 +185,32 @@ CardDescription.displayName = "CardDescription";
|
|
|
141
185
|
const CardContent = React.forwardRef<
|
|
142
186
|
HTMLDivElement,
|
|
143
187
|
React.HTMLAttributes<HTMLDivElement>
|
|
144
|
-
>(({ className, ...props }, ref) =>
|
|
145
|
-
|
|
146
|
-
|
|
188
|
+
>(({ className, ...props }, ref) => {
|
|
189
|
+
const padding = CARD_SECTION_PADDING[React.useContext(CardSizeContext)];
|
|
190
|
+
return (
|
|
191
|
+
<div
|
|
192
|
+
ref={ref}
|
|
193
|
+
className={cn("moonui-theme", padding, "pt-0", className)}
|
|
194
|
+
{...props}
|
|
195
|
+
/>
|
|
196
|
+
);
|
|
197
|
+
});
|
|
147
198
|
CardContent.displayName = "CardContent";
|
|
148
199
|
|
|
149
200
|
const CardFooter = React.forwardRef<
|
|
150
201
|
HTMLDivElement,
|
|
151
202
|
React.HTMLAttributes<HTMLDivElement>
|
|
152
|
-
>(({ className, ...props }, ref) =>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
203
|
+
>(({ className, ...props }, ref) => {
|
|
204
|
+
const padding = CARD_SECTION_PADDING[React.useContext(CardSizeContext)];
|
|
205
|
+
return (
|
|
206
|
+
<div
|
|
207
|
+
ref={ref}
|
|
208
|
+
className={cn("moonui-theme", "flex items-center", padding, "pt-0", className)}
|
|
209
|
+
{...props}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
});
|
|
159
213
|
CardFooter.displayName = "CardFooter";
|
|
160
214
|
|
|
161
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
215
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, cardVariants };
|
|
216
|
+
export type { CardSize };
|
|
@@ -32,11 +32,11 @@ const toastVariants = cva(
|
|
|
32
32
|
destructive:
|
|
33
33
|
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
|
34
34
|
success:
|
|
35
|
-
"border-
|
|
35
|
+
"border-success-subtle-foreground/20 bg-success-subtle text-success-subtle-foreground",
|
|
36
36
|
warning:
|
|
37
|
-
"border-
|
|
37
|
+
"border-warning-subtle-foreground/20 bg-warning-subtle text-warning-subtle-foreground",
|
|
38
38
|
info:
|
|
39
|
-
"border-
|
|
39
|
+
"border-info-subtle-foreground/20 bg-info-subtle text-info-subtle-foreground",
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
defaultVariants: {
|
|
@@ -86,7 +86,7 @@ const ToastClose = React.forwardRef<
|
|
|
86
86
|
<ToastPrimitives.Close
|
|
87
87
|
ref={ref}
|
|
88
88
|
className={cn(
|
|
89
|
-
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-
|
|
89
|
+
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-destructive-foreground/70 group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive-foreground/40 group-[.destructive]:focus:ring-offset-destructive",
|
|
90
90
|
className
|
|
91
91
|
)}
|
|
92
92
|
toast-close=""
|
|
@@ -15,10 +15,10 @@ const tooltipVariants = cva(
|
|
|
15
15
|
variant: {
|
|
16
16
|
default: "bg-primary text-primary-foreground",
|
|
17
17
|
secondary: "bg-secondary text-secondary-foreground",
|
|
18
|
-
success: "bg-
|
|
19
|
-
warning: "bg-
|
|
18
|
+
success: "bg-success text-success-foreground border-success",
|
|
19
|
+
warning: "bg-warning text-warning-foreground border-warning",
|
|
20
20
|
destructive: "bg-destructive text-destructive-foreground",
|
|
21
|
-
info: "bg-
|
|
21
|
+
info: "bg-info text-info-foreground border-info",
|
|
22
22
|
},
|
|
23
23
|
size: {
|
|
24
24
|
sm: "px-2 py-1 text-xs",
|
package/src/styles/tokens.css
CHANGED
|
@@ -146,7 +146,11 @@
|
|
|
146
146
|
--muted-foreground: 215.4 16.3% 45%;
|
|
147
147
|
--accent: 217.2 32.6% 94%;
|
|
148
148
|
--accent-foreground: 217.2 91.2% 51%;
|
|
149
|
-
|
|
149
|
+
/* #327: 60.2% idi. HİÇBİR kullanımda AA'yı geçmiyordu — beyaz zeminde metin 3.76,
|
|
150
|
+
kendi foreground'uyla düz dolgu 3.59. 48%, her İKİ testi de geçen en açık değer
|
|
151
|
+
(4.86 / 4.64), yani marka rengine en yakın kalan seçenek. Koyu tema (0 63% 31%)
|
|
152
|
+
zaten 9.44 → dokunulmadı. #284'ün --primary onarımıyla aynı desen. */
|
|
153
|
+
--destructive: 0 84.2% 48%;
|
|
150
154
|
--destructive-foreground: 210 40% 98%;
|
|
151
155
|
--border: 214.3 31.8% 91.4%;
|
|
152
156
|
/* #284: form kontrolü kenarlığı beyaz zeminde 1.23 idi (WCAG 1.4.11 eşiği 3.0,
|
|
@@ -167,11 +171,34 @@
|
|
|
167
171
|
--warning-foreground: 24 10% 10%;
|
|
168
172
|
--error: 0 84% 60%;
|
|
169
173
|
--error-foreground: 210 40% 98%;
|
|
170
|
-
|
|
174
|
+
/* #313/#314/#323 dönüşümü sırasında çıktı: `--info` düz dolgu olarak kendi
|
|
175
|
+
foreground'uyla 3.47 veriyordu — `--destructive`in (#327) birebir aynı kusuru.
|
|
176
|
+
Kullanıcının #327'de seçtiği ilke burada da uygulandı: her iki testi geçen EN AÇIK
|
|
177
|
+
değer. 50% → düz 4.83, metin/beyaz 5.05. */
|
|
178
|
+
--info: 217 91% 50%;
|
|
171
179
|
--info-foreground: 210 40% 98%;
|
|
172
180
|
--caution: 38 92% 50%;
|
|
173
181
|
--caution-foreground: 24 10% 10%;
|
|
174
182
|
|
|
183
|
+
/* ---------------------------------------------------------------------------
|
|
184
|
+
#313 / #314 / #323 — YUMUŞAK-TON (subtle) semantik yüzeyler
|
|
185
|
+
---------------------------------------------------------------------------
|
|
186
|
+
`--destructive` gibi DÜZ token'lar "dolu zemin + açık metin" içindir. Menü
|
|
187
|
+
item'ı, toast, rozet gibi yerlerde ise yumuşak bir tint + koyu metin gerekir.
|
|
188
|
+
Böyle bir token OLMADIĞI için 25 dosya ham Tailwind paletine (`bg-red-100
|
|
189
|
+
text-red-900`) düşmüştü → marka/tema özelleştirmesi bu varyantlara İŞLEMİYORDU.
|
|
190
|
+
|
|
191
|
+
Değerler bugünkü ham palet çiftlerinin birebir HSL karşılığıdır; yani dönüşüm
|
|
192
|
+
kontrastı DÜŞÜRMEZ (ölçüldü: 8.20 / 8.70 / 8.83 / 9.52 — hepsi korunur). */
|
|
193
|
+
--destructive-subtle: 0 93% 94%;
|
|
194
|
+
--destructive-subtle-foreground: 0 63% 31%; /* 8.20:1 */
|
|
195
|
+
--success-subtle: 138 77% 97%;
|
|
196
|
+
--success-subtle-foreground: 144 61% 20%; /* 8.70:1 */
|
|
197
|
+
--warning-subtle: 33 100% 97%;
|
|
198
|
+
--warning-subtle-foreground: 15 75% 28%; /* 8.83:1 */
|
|
199
|
+
--info-subtle: 214 100% 97%;
|
|
200
|
+
--info-subtle-foreground: 224 64% 33%; /* 9.52:1 */
|
|
201
|
+
|
|
175
202
|
--primary-50: 240 249 255;
|
|
176
203
|
|
|
177
204
|
/* Tailwind preset'i rounded-lg/md/sm'yi var(--radius)'a bagliyor.
|
|
@@ -433,6 +460,20 @@
|
|
|
433
460
|
metin olarak okunmuyordu (3.30) — burada 36% korunur (4.86). */
|
|
434
461
|
--success: 142 76% 36%;
|
|
435
462
|
--destructive-foreground: 210 40% 98%;
|
|
463
|
+
|
|
464
|
+
/* #313/#314/#323 — koyu tema subtle yüzeyleri.
|
|
465
|
+
Bunlar bugünkü `dark:bg-red-900/20` kompozisyonunun ARİTMETİK karşılığı DEĞİL:
|
|
466
|
+
çok koyu lacivert zemine %20 tint bindirince hue zemine kayıyor ve ortaya
|
|
467
|
+
semantik olmayan bir renk çıkıyor (destructive için 312° = macenta). Bunun
|
|
468
|
+
yerine semantik hue KORUNARAK kasıtlı koyu tint'ler seçildi; kontrast ölçüldü. */
|
|
469
|
+
--destructive-subtle: 0 63% 12%;
|
|
470
|
+
--destructive-subtle-foreground: 0 91% 71%; /* 6.43:1 */
|
|
471
|
+
--success-subtle: 142 70% 10%;
|
|
472
|
+
--success-subtle-foreground: 141 84% 93%; /* 14.02:1 */
|
|
473
|
+
--warning-subtle: 25 80% 11%;
|
|
474
|
+
--warning-subtle-foreground: 34 100% 92%; /* 14.43:1 */
|
|
475
|
+
--info-subtle: 217 80% 13%;
|
|
476
|
+
--info-subtle-foreground: 214 95% 93%; /* 14.11:1 */
|
|
436
477
|
--border: 216 34% 17%;
|
|
437
478
|
/* #284: koyu temada form kontrolü kenarlığı 1.36 idi -> 40% ile ~3.1.
|
|
438
479
|
Not: --input switch.tsx:27'de kapalı track ZEMİNİ olarak da kullanılıyor;
|
package/tailwind-preset.js
CHANGED
|
@@ -92,15 +92,23 @@ module.exports = {
|
|
|
92
92
|
destructive: {
|
|
93
93
|
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
|
|
94
94
|
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
|
|
95
|
+
// #313/#314/#323: yumuşak-ton yüzey — `bg-destructive-subtle` +
|
|
96
|
+
// `text-destructive-subtle-foreground` olarak kullanılır.
|
|
97
|
+
subtle: "hsl(var(--destructive-subtle) / <alpha-value>)",
|
|
98
|
+
"subtle-foreground": "hsl(var(--destructive-subtle-foreground) / <alpha-value>)",
|
|
95
99
|
},
|
|
96
100
|
error: "hsl(var(--error) / <alpha-value>)",
|
|
97
101
|
success: {
|
|
98
102
|
DEFAULT: "hsl(var(--success) / <alpha-value>)",
|
|
99
103
|
foreground: "hsl(var(--success-foreground) / <alpha-value>)",
|
|
104
|
+
subtle: "hsl(var(--success-subtle) / <alpha-value>)",
|
|
105
|
+
"subtle-foreground": "hsl(var(--success-subtle-foreground) / <alpha-value>)",
|
|
100
106
|
},
|
|
101
107
|
warning: {
|
|
102
108
|
DEFAULT: "hsl(var(--warning) / <alpha-value>)",
|
|
103
109
|
foreground: "hsl(var(--warning-foreground) / <alpha-value>)",
|
|
110
|
+
subtle: "hsl(var(--warning-subtle) / <alpha-value>)",
|
|
111
|
+
"subtle-foreground": "hsl(var(--warning-subtle-foreground) / <alpha-value>)",
|
|
104
112
|
},
|
|
105
113
|
caution: {
|
|
106
114
|
DEFAULT: "hsl(var(--caution) / <alpha-value>)",
|
|
@@ -109,6 +117,8 @@ module.exports = {
|
|
|
109
117
|
info: {
|
|
110
118
|
DEFAULT: "hsl(var(--info) / <alpha-value>)",
|
|
111
119
|
foreground: "hsl(var(--info-foreground) / <alpha-value>)",
|
|
120
|
+
subtle: "hsl(var(--info-subtle) / <alpha-value>)",
|
|
121
|
+
"subtle-foreground": "hsl(var(--info-subtle-foreground) / <alpha-value>)",
|
|
112
122
|
},
|
|
113
123
|
card: {
|
|
114
124
|
DEFAULT: "hsl(var(--card) / <alpha-value>)",
|
|
@@ -168,13 +178,13 @@ module.exports = {
|
|
|
168
178
|
"gradient-to-br": "linear-gradient(to bottom right, var(--tw-gradient-stops))",
|
|
169
179
|
"gradient-to-bl": "linear-gradient(to bottom left, var(--tw-gradient-stops))",
|
|
170
180
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
/* #313/#314/#323: `borderColor` extend'i KALDIRILDI.
|
|
182
|
+
Buradaki DÜZ string değerler, `theme.colors` altındaki İÇ İÇE nesneleri
|
|
183
|
+
`border-*` ailesi için EZİYORDU — o beş isimde yalnız DEFAULT hayatta kalıyor,
|
|
184
|
+
`border-success-subtle-foreground` gibi alt anahtarlar hiç CSS ÜRETMİYORDU.
|
|
185
|
+
(Deterministik Tailwind CLI probe'uyla doğrulandı: destructive bu listede
|
|
186
|
+
olmadığı için çalışıyor, diğer üçü çalışmıyordu.)
|
|
187
|
+
`border-*` sınıfları `theme.colors`tan zaten türer; blok gereksizdi. */
|
|
178
188
|
},
|
|
179
189
|
},
|
|
180
190
|
plugins: [],
|
package/src/use-toast.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Toast hook - şimdilik basit bir implementasyon
|
|
2
|
-
// Gerçek implementasyonda Toaster component'i ile entegre olacak
|
|
3
|
-
|
|
4
|
-
interface ToastOptions {
|
|
5
|
-
title: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
variant?: 'default' | 'destructive';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function toast(options: ToastOptions) {
|
|
11
|
-
// Şimdilik console'a yazdıralım
|
|
12
|
-
// Gerçek implementasyonda toast notification gösterilecek
|
|
13
|
-
console.log('Toast:', options);
|
|
14
|
-
|
|
15
|
-
// Browser'da alert gösterelim (geçici çözüm)
|
|
16
|
-
if (typeof window !== 'undefined') {
|
|
17
|
-
const message = options.description
|
|
18
|
-
? `${options.title}\n\n${options.description}`
|
|
19
|
-
: options.title;
|
|
20
|
-
|
|
21
|
-
// Variant'a göre stil belirle
|
|
22
|
-
if (options.variant === 'destructive') {
|
|
23
|
-
console.error(message);
|
|
24
|
-
} else {
|
|
25
|
-
console.log(message);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const useToast = () => {
|
|
31
|
-
return { toast };
|
|
32
|
-
};
|