@moontra/moonui 6.24.0 → 6.25.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/cdn/moonui.css +2 -2
- package/dist/cdn/moonui.esm.js +12 -12
- package/dist/cdn/moonui.global.js +12 -12
- package/dist/index.d.mts +24 -6
- package/dist/index.d.ts +24 -6
- package/dist/index.js +417 -87
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/issue-544-kontrast.test.tsx +153 -145
- package/src/__tests__/issue-645-overlay-tab-focus.test.tsx +262 -0
- package/src/__tests__/issue-646-dark-scope-override.test.tsx +297 -0
- package/src/__tests__/issue-661-a11y.test.tsx +113 -0
- package/src/__tests__/issue-661-token-kontrast.test.tsx +82 -0
- package/src/components/ui/__tests__/accent-foreground-contrast.test.ts +197 -0
- package/src/components/ui/__tests__/accordion.test.tsx +6 -4
- package/src/components/ui/__tests__/alert.test.tsx +4 -1
- package/src/components/ui/__tests__/button.test.tsx +6 -1
- package/src/components/ui/__tests__/dark-token-contrast.test.ts +18 -18
- package/src/components/ui/__tests__/heading-level-policy.test.tsx +4 -4
- package/src/components/ui/__tests__/issue-643-card-token-parite.test.tsx +113 -0
- package/src/components/ui/__tests__/issue-649-button-aschild.test.tsx +199 -0
- package/src/components/ui/__tests__/issue-664-separator-dekoratif-rol.test.tsx +41 -0
- package/src/components/ui/__tests__/preset-color-shadowing.test.ts +10 -10
- package/src/components/ui/__tests__/primary-subtle-token.test.ts +7 -7
- package/src/components/ui/__tests__/progress-secondary-contrast.test.ts +38 -38
- package/src/components/ui/__tests__/progress-warning-error-contrast.test.ts +64 -64
- package/src/components/ui/__tests__/quality-group-de.test.tsx +2 -2
- package/src/components/ui/__tests__/responsive-color-invariance.test.ts +20 -20
- package/src/components/ui/__tests__/select.test.tsx +6 -1
- package/src/components/ui/__tests__/semantic-token-usage.test.ts +23 -23
- package/src/components/ui/__tests__/size-scale-convention.test.ts +10 -10
- package/src/components/ui/__tests__/slider-accent-contrast.test.ts +142 -136
- package/src/components/ui/__tests__/slider-secondary-contrast.test.ts +60 -60
- package/src/components/ui/__tests__/slider-warning-error-contrast.test.ts +100 -100
- package/src/components/ui/__tests__/switch-variant-contrast.test.tsx +103 -103
- package/src/components/ui/__tests__/tooltip.test.tsx +122 -0
- package/src/components/ui/accordion.tsx +16 -3
- package/src/components/ui/alert.tsx +1 -1
- package/src/components/ui/badge.tsx +12 -1
- package/src/components/ui/button.tsx +76 -18
- package/src/components/ui/card.tsx +59 -5
- package/src/components/ui/dropdown-menu.tsx +99 -5
- package/src/components/ui/file-upload.tsx +4 -0
- package/src/components/ui/index.ts +4 -1
- package/src/components/ui/popover-pro.tsx +41 -0
- package/src/components/ui/popover.tsx +162 -7
- package/src/components/ui/rating.tsx +24 -2
- package/src/components/ui/select.tsx +4 -4
- package/src/components/ui/simple-editor.tsx +3 -18
- package/src/components/ui/tabs.tsx +16 -1
- package/src/components/ui/textarea.tsx +6 -1
- package/src/components/ui/toast.tsx +17 -2
- package/src/components/ui/tooltip.tsx +154 -2
- package/src/styles/tokens.css +34 -2
|
@@ -40,12 +40,12 @@ const KOK = path.resolve(__dirname, '../../..')
|
|
|
40
40
|
const tokens = fs.readFileSync(path.join(KOK, 'styles/tokens.css'), 'utf8')
|
|
41
41
|
|
|
42
42
|
/** "H S% L%" biçimindeki token değerini oku (light = :root, dark = .dark bloğu) */
|
|
43
|
-
function tokenOku(
|
|
44
|
-
const
|
|
43
|
+
function tokenOku(name: string, blok: 'light' | 'dark'): string | null {
|
|
44
|
+
const source =
|
|
45
45
|
blok === 'dark'
|
|
46
46
|
? tokens.slice(tokens.indexOf('.dark'))
|
|
47
47
|
: tokens.slice(0, tokens.indexOf('.dark'))
|
|
48
|
-
return
|
|
48
|
+
return source.match(new RegExp(`--${name}:\\s*([^;]+);`))?.[1].trim() ?? null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function hslToRgb(h: number, s: number, l: number): [number, number, number] {
|
|
@@ -75,7 +75,7 @@ const rgbTokenRgb = (v: string): [number, number, number] => {
|
|
|
75
75
|
return [p[0], p[1], p[2]]
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function
|
|
78
|
+
function relativeLuminance([r, g, b]: [number, number, number]): number {
|
|
79
79
|
const c = (v: number) => {
|
|
80
80
|
const x = v / 255
|
|
81
81
|
return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
|
|
@@ -84,8 +84,8 @@ function bagilParlaklik([r, g, b]: [number, number, number]): number {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function rgbKontrast(a: [number, number, number], b: [number, number, number]): number {
|
|
87
|
-
const la =
|
|
88
|
-
const lb =
|
|
87
|
+
const la = relativeLuminance(a)
|
|
88
|
+
const lb = relativeLuminance(b)
|
|
89
89
|
const [hi, lo] = la > lb ? [la, lb] : [lb, la]
|
|
90
90
|
return (hi + 0.05) / (lo + 0.05)
|
|
91
91
|
}
|
|
@@ -94,12 +94,12 @@ function rgbKontrast(a: [number, number, number], b: [number, number, number]):
|
|
|
94
94
|
function alfaBileske(
|
|
95
95
|
ust: [number, number, number],
|
|
96
96
|
alfa: number,
|
|
97
|
-
|
|
97
|
+
backgroundVal: [number, number, number]
|
|
98
98
|
): [number, number, number] {
|
|
99
99
|
return [
|
|
100
|
-
Math.round(alfa * ust[0] + (1 - alfa) *
|
|
101
|
-
Math.round(alfa * ust[1] + (1 - alfa) *
|
|
102
|
-
Math.round(alfa * ust[2] + (1 - alfa) *
|
|
100
|
+
Math.round(alfa * ust[0] + (1 - alfa) * backgroundVal[0]),
|
|
101
|
+
Math.round(alfa * ust[1] + (1 - alfa) * backgroundVal[1]),
|
|
102
|
+
Math.round(alfa * ust[2] + (1 - alfa) * backgroundVal[2]),
|
|
103
103
|
]
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -108,24 +108,24 @@ function alfaBileske(
|
|
|
108
108
|
* #284/#368 konvansiyonu: kontrast, --background'ın yanında bu gerçek yüzeye
|
|
109
109
|
* göre de ölçülür (tokens.css --input notu).
|
|
110
110
|
*/
|
|
111
|
-
const
|
|
111
|
+
const CARD_ACTUAL_DARK: [number, number, number] = [17, 24, 39]
|
|
112
112
|
|
|
113
113
|
/** Tema başına range (secondary-500) ve track (secondary@%20 ∘ zemin) renkleri */
|
|
114
|
-
function
|
|
115
|
-
const
|
|
114
|
+
function themeColors(blok: 'light' | 'dark') {
|
|
115
|
+
const fill = rgbTokenRgb(tokenOku('secondary-500', 'light')!) // skala yalnız :root'ta — iki temada aynı
|
|
116
116
|
const secondary = hslTokenRgb(tokenOku('secondary', blok)!)
|
|
117
117
|
const background = hslTokenRgb(tokenOku('background', blok)!)
|
|
118
118
|
const card = hslTokenRgb(tokenOku('card', blok)!)
|
|
119
|
-
const
|
|
119
|
+
const backgrounds: Array<[string, [number, number, number]]> = [
|
|
120
120
|
['--background', background],
|
|
121
121
|
['--card', card],
|
|
122
122
|
]
|
|
123
|
-
if (blok === 'dark')
|
|
124
|
-
return {
|
|
123
|
+
if (blok === 'dark') backgrounds.push(['gerçek kart #111827', CARD_ACTUAL_DARK])
|
|
124
|
+
return { fill, secondary, backgrounds }
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/** Slider DOM'u: kök > .moonui-theme > track > range (thumb'lar track'ten SONRA) */
|
|
128
|
-
function
|
|
128
|
+
function parts(container: HTMLElement) {
|
|
129
129
|
const kabuk = container.querySelector('.moonui-theme') as HTMLElement
|
|
130
130
|
expect(kabuk).not.toBeNull()
|
|
131
131
|
const track = kabuk.firstElementChild as HTMLElement
|
|
@@ -158,23 +158,23 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
describe('kaynak sözleşmesi: range ile track FARKLI token’lara basıyor', () => {
|
|
161
|
-
const
|
|
161
|
+
const source = fs.readFileSync(path.join(KOK, 'components/ui/slider.tsx'), 'utf8')
|
|
162
162
|
|
|
163
163
|
it('track secondary sınıfı `bg-secondary/20` (değişmedi)', () => {
|
|
164
|
-
expect(
|
|
164
|
+
expect(source).toMatch(/secondary:\s*"bg-secondary\/20"/)
|
|
165
165
|
})
|
|
166
166
|
|
|
167
167
|
it('range secondary sınıfı `bg-[rgb(var(--secondary-500))]` (artık --secondary DEĞİL)', () => {
|
|
168
|
-
expect(
|
|
168
|
+
expect(source).toMatch(/secondary:\s*"bg-\[rgb\(var\(--secondary-500\)\)\]"/)
|
|
169
169
|
})
|
|
170
170
|
|
|
171
171
|
it('#407: thumb secondary sınıfı `border-[rgb(var(--secondary-500))] bg-background`', () => {
|
|
172
172
|
// AYRI EKSEN (aşağıdaki "#407 thumb kenarlığı" bloğuna bakınız): #389 yalnız
|
|
173
173
|
// range↔track eksenini kapsıyordu, tutamacın kenarlığı bilerek dışarıda kalmıştı.
|
|
174
|
-
expect(
|
|
174
|
+
expect(source).toMatch(
|
|
175
175
|
/secondary:\s*"border-\[rgb\(var\(--secondary-500\)\)\] bg-background"/
|
|
176
176
|
)
|
|
177
|
-
expect(
|
|
177
|
+
expect(source).not.toMatch(/secondary:\s*"border-secondary bg-background"/)
|
|
178
178
|
})
|
|
179
179
|
|
|
180
180
|
it('zemin denkliği: `.moonui-theme` paket CSS’inde token yeniden tanımlamıyor', () => {
|
|
@@ -182,13 +182,13 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
182
182
|
// --background/--card olduğunu VARSAYAR. Biri `.moonui-theme`i token
|
|
183
183
|
// kapsamına çevirirse (ör. `.moonui-theme { --secondary: ... }`) hesap
|
|
184
184
|
// sessizce yanlışlanır — o an bu test kırmızı olur.
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
.readdirSync(
|
|
185
|
+
const styleDir = path.join(KOK, 'styles')
|
|
186
|
+
const styles = fs
|
|
187
|
+
.readdirSync(styleDir)
|
|
188
188
|
.filter((f) => f.endsWith('.css'))
|
|
189
|
-
.map((f) => fs.readFileSync(path.join(
|
|
189
|
+
.map((f) => fs.readFileSync(path.join(styleDir, f), 'utf8'))
|
|
190
190
|
.join('\n')
|
|
191
|
-
expect(
|
|
191
|
+
expect(styles).not.toMatch(/\.moonui-theme[^{]*\{[^}]*--secondary/)
|
|
192
192
|
})
|
|
193
193
|
})
|
|
194
194
|
|
|
@@ -197,7 +197,7 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
197
197
|
const { container } = render(
|
|
198
198
|
React.createElement(Slider, { trackVariant: 'secondary', rangeVariant: 'secondary' })
|
|
199
199
|
)
|
|
200
|
-
const { track, range } =
|
|
200
|
+
const { track, range } = parts(container)
|
|
201
201
|
expect(track).toHaveClass('bg-secondary/20')
|
|
202
202
|
expect(range.className).toContain('bg-[rgb(var(--secondary-500))]')
|
|
203
203
|
expect(range.className).not.toMatch(/(?:^|\s)bg-secondary(?:\s|$)/)
|
|
@@ -208,7 +208,7 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
208
208
|
// `rangeVariant || thumbVariant || "primary"` düşüşleri değişmedi;
|
|
209
209
|
// en yaygın kullanım (<Slider thumbVariant="secondary" />) bu yoldan geçer.
|
|
210
210
|
const { container } = render(React.createElement(Slider, { thumbVariant: 'secondary' }))
|
|
211
|
-
const { track, range } =
|
|
211
|
+
const { track, range } = parts(container)
|
|
212
212
|
expect(track).toHaveClass('bg-secondary/20')
|
|
213
213
|
expect(range.className).toContain('bg-[rgb(var(--secondary-500))]')
|
|
214
214
|
})
|
|
@@ -217,14 +217,14 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
217
217
|
const { container } = render(
|
|
218
218
|
React.createElement(Slider, { thumbVariant: 'secondary', rangeVariant: 'success' })
|
|
219
219
|
)
|
|
220
|
-
const { range } =
|
|
220
|
+
const { range } = parts(container)
|
|
221
221
|
expect(range.className).toContain('bg-success')
|
|
222
222
|
expect(range.className).not.toContain('bg-[rgb(var(--secondary-500))]')
|
|
223
223
|
})
|
|
224
224
|
|
|
225
225
|
it('diğer varyantlar ETKİLENMEDİ — primary range hâlâ `bg-primary`', () => {
|
|
226
226
|
const { container } = render(React.createElement(Slider, { rangeVariant: 'primary' }))
|
|
227
|
-
const { range } =
|
|
227
|
+
const { range } = parts(container)
|
|
228
228
|
expect(range.className).toContain('bg-primary')
|
|
229
229
|
expect(range.className).not.toContain('secondary')
|
|
230
230
|
})
|
|
@@ -234,11 +234,11 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
234
234
|
it('YENİ range (secondary-500), track’in her olası zemindeki bileşkesine ≥3:1', () => {
|
|
235
235
|
// Ölçülen değerler: light 4.63 (--background=--card=beyaz) · dark 4.23
|
|
236
236
|
// (--background=--card) / 3.83 (gerçek kart #111827).
|
|
237
|
-
const {
|
|
238
|
-
for (const [
|
|
239
|
-
const track = alfaBileske(secondary, 0.2,
|
|
240
|
-
const oran = rgbKontrast(
|
|
241
|
-
expect({
|
|
237
|
+
const { fill, secondary, backgrounds } = themeColors(blok)
|
|
238
|
+
for (const [name, backgroundVal] of backgrounds) {
|
|
239
|
+
const track = alfaBileske(secondary, 0.2, backgroundVal)
|
|
240
|
+
const oran = rgbKontrast(fill, track)
|
|
241
|
+
expect({ backgroundVal: name, oran }).toEqual({ backgroundVal: name, oran: expect.any(Number) })
|
|
242
242
|
expect(oran).toBeGreaterThanOrEqual(3.0)
|
|
243
243
|
}
|
|
244
244
|
})
|
|
@@ -246,9 +246,9 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
246
246
|
it('ESKİ range (bg-secondary) aynı ölçümde başarısızdı — bug’ın belgesi', () => {
|
|
247
247
|
// Düzeltmenin GEREKLİLİĞİNİ sabitler: eski değer üç zeminde de eşiğin
|
|
248
248
|
// çok altındaydı (light 1.12 · dark 1.01 · gerçek kart 1.10).
|
|
249
|
-
const { secondary,
|
|
250
|
-
for (const [,
|
|
251
|
-
const track = alfaBileske(secondary, 0.2,
|
|
249
|
+
const { secondary, backgrounds } = themeColors(blok)
|
|
250
|
+
for (const [, backgroundVal] of backgrounds) {
|
|
251
|
+
const track = alfaBileske(secondary, 0.2, backgroundVal)
|
|
252
252
|
expect(rgbKontrast(secondary, track)).toBeLessThan(3.0)
|
|
253
253
|
}
|
|
254
254
|
})
|
|
@@ -260,15 +260,15 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
260
260
|
// kademe dark track'e (near-black) yetişemiyor. Komşu kademeler bunu kanıtlar.
|
|
261
261
|
it('secondary-400 light temada eşiğin ALTINDA kalır (~2.50)', () => {
|
|
262
262
|
const k400 = rgbTokenRgb(tokenOku('secondary-400', 'light')!)
|
|
263
|
-
const { secondary,
|
|
264
|
-
const track = alfaBileske(secondary, 0.2,
|
|
263
|
+
const { secondary, backgrounds } = themeColors('light')
|
|
264
|
+
const track = alfaBileske(secondary, 0.2, backgrounds[0][1])
|
|
265
265
|
expect(rgbKontrast(k400, track)).toBeLessThan(3.0)
|
|
266
266
|
})
|
|
267
267
|
|
|
268
268
|
it('secondary-600 dark temada eşiğin ALTINDA kalır (~2.66)', () => {
|
|
269
269
|
const k600 = rgbTokenRgb(tokenOku('secondary-600', 'light')!)
|
|
270
|
-
const { secondary,
|
|
271
|
-
const track = alfaBileske(secondary, 0.2,
|
|
270
|
+
const { secondary, backgrounds } = themeColors('dark')
|
|
271
|
+
const track = alfaBileske(secondary, 0.2, backgrounds[0][1])
|
|
272
272
|
expect(rgbKontrast(k600, track)).toBeLessThan(3.0)
|
|
273
273
|
})
|
|
274
274
|
|
|
@@ -276,16 +276,16 @@ describe('#389 slider secondary range↔track kontrastı', () => {
|
|
|
276
276
|
// Tek testte tüm skalayı tarayıp "geçen kademe kümesi = {500}" iddiasını
|
|
277
277
|
// kilitler; ileride skala değerleri değişirse burası kırmızı olur.
|
|
278
278
|
const kademeler = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
|
|
279
|
-
const
|
|
280
|
-
const
|
|
279
|
+
const passingOnes = kademeler.filter((k) => {
|
|
280
|
+
const color = rgbTokenRgb(tokenOku(`secondary-${k}`, 'light')!)
|
|
281
281
|
return (['light', 'dark'] as const).every((blok) => {
|
|
282
|
-
const { secondary,
|
|
283
|
-
return
|
|
284
|
-
([,
|
|
282
|
+
const { secondary, backgrounds } = themeColors(blok)
|
|
283
|
+
return backgrounds.every(
|
|
284
|
+
([, backgroundVal]) => rgbKontrast(color, alfaBileske(secondary, 0.2, backgroundVal)) >= 3.0
|
|
285
285
|
)
|
|
286
286
|
})
|
|
287
287
|
})
|
|
288
|
-
expect(
|
|
288
|
+
expect(passingOnes).toEqual([500])
|
|
289
289
|
})
|
|
290
290
|
|
|
291
291
|
it('secondary-foreground kontrastı GEÇERDİ ama primary klonu üretirdi — kilit', () => {
|
|
@@ -343,18 +343,18 @@ describe('#407 slider secondary thumb kenarlığı ↔ zemin kontrastı', () =>
|
|
|
343
343
|
describe.each(['light', 'dark'] as const)('%s tema — kabul kriteri ≥3:1', (blok) => {
|
|
344
344
|
it('YENİ kenarlık (secondary-500) her zemine ≥3:1', () => {
|
|
345
345
|
// Ölçülen: light 4.76 · dark 4.23 (gerçek kart #111827 üstünde 3.73).
|
|
346
|
-
const {
|
|
347
|
-
for (const [
|
|
348
|
-
const oran = rgbKontrast(
|
|
349
|
-
expect({
|
|
346
|
+
const { fill, backgrounds } = themeColors(blok)
|
|
347
|
+
for (const [name, backgroundVal] of backgrounds) {
|
|
348
|
+
const oran = rgbKontrast(fill, backgroundVal)
|
|
349
|
+
expect({ backgroundVal: name, didPass: oran >= 3.0 }).toEqual({ backgroundVal: name, didPass: true })
|
|
350
350
|
}
|
|
351
351
|
})
|
|
352
352
|
|
|
353
353
|
it('ESKİ kenarlık (border-secondary) aynı ölçümde başarısızdı — bug’ın belgesi', () => {
|
|
354
354
|
// light 1.15 · dark 1.01 · gerçek kart 1.13 — tutamaç fiilen görünmüyordu.
|
|
355
|
-
const { secondary,
|
|
356
|
-
for (const [,
|
|
357
|
-
expect(rgbKontrast(secondary,
|
|
355
|
+
const { secondary, backgrounds } = themeColors(blok)
|
|
356
|
+
for (const [, backgroundVal] of backgrounds) {
|
|
357
|
+
expect(rgbKontrast(secondary, backgroundVal)).toBeLessThan(3.0)
|
|
358
358
|
}
|
|
359
359
|
})
|
|
360
360
|
})
|
|
@@ -364,12 +364,12 @@ describe('#407 slider secondary thumb kenarlığı ↔ zemin kontrastı', () =>
|
|
|
364
364
|
// alfa katmanı olmayan zemin, %20 alfa track'e çok yakın parlaklıkta olduğundan
|
|
365
365
|
// geçerli bant neredeyse aynıdır. Yine de bağımsız olarak taranır.
|
|
366
366
|
const kademeler = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
|
|
367
|
-
const
|
|
368
|
-
const
|
|
367
|
+
const passingOnes = kademeler.filter((k) => {
|
|
368
|
+
const color = rgbTokenRgb(tokenOku(`secondary-${k}`, 'light')!)
|
|
369
369
|
return (['light', 'dark'] as const).every((blok) =>
|
|
370
|
-
|
|
370
|
+
themeColors(blok).backgrounds.every(([, backgroundVal]) => rgbKontrast(color, backgroundVal) >= 3.0)
|
|
371
371
|
)
|
|
372
372
|
})
|
|
373
|
-
expect(
|
|
373
|
+
expect(passingOnes).toEqual([500])
|
|
374
374
|
})
|
|
375
375
|
})
|