@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.
Files changed (56) hide show
  1. package/dist/cdn/moonui.css +2 -2
  2. package/dist/cdn/moonui.esm.js +12 -12
  3. package/dist/cdn/moonui.global.js +12 -12
  4. package/dist/index.d.mts +24 -6
  5. package/dist/index.d.ts +24 -6
  6. package/dist/index.js +417 -87
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +418 -89
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/src/__tests__/issue-544-kontrast.test.tsx +153 -145
  12. package/src/__tests__/issue-645-overlay-tab-focus.test.tsx +262 -0
  13. package/src/__tests__/issue-646-dark-scope-override.test.tsx +297 -0
  14. package/src/__tests__/issue-661-a11y.test.tsx +113 -0
  15. package/src/__tests__/issue-661-token-kontrast.test.tsx +82 -0
  16. package/src/components/ui/__tests__/accent-foreground-contrast.test.ts +197 -0
  17. package/src/components/ui/__tests__/accordion.test.tsx +6 -4
  18. package/src/components/ui/__tests__/alert.test.tsx +4 -1
  19. package/src/components/ui/__tests__/button.test.tsx +6 -1
  20. package/src/components/ui/__tests__/dark-token-contrast.test.ts +18 -18
  21. package/src/components/ui/__tests__/heading-level-policy.test.tsx +4 -4
  22. package/src/components/ui/__tests__/issue-643-card-token-parite.test.tsx +113 -0
  23. package/src/components/ui/__tests__/issue-649-button-aschild.test.tsx +199 -0
  24. package/src/components/ui/__tests__/issue-664-separator-dekoratif-rol.test.tsx +41 -0
  25. package/src/components/ui/__tests__/preset-color-shadowing.test.ts +10 -10
  26. package/src/components/ui/__tests__/primary-subtle-token.test.ts +7 -7
  27. package/src/components/ui/__tests__/progress-secondary-contrast.test.ts +38 -38
  28. package/src/components/ui/__tests__/progress-warning-error-contrast.test.ts +64 -64
  29. package/src/components/ui/__tests__/quality-group-de.test.tsx +2 -2
  30. package/src/components/ui/__tests__/responsive-color-invariance.test.ts +20 -20
  31. package/src/components/ui/__tests__/select.test.tsx +6 -1
  32. package/src/components/ui/__tests__/semantic-token-usage.test.ts +23 -23
  33. package/src/components/ui/__tests__/size-scale-convention.test.ts +10 -10
  34. package/src/components/ui/__tests__/slider-accent-contrast.test.ts +142 -136
  35. package/src/components/ui/__tests__/slider-secondary-contrast.test.ts +60 -60
  36. package/src/components/ui/__tests__/slider-warning-error-contrast.test.ts +100 -100
  37. package/src/components/ui/__tests__/switch-variant-contrast.test.tsx +103 -103
  38. package/src/components/ui/__tests__/tooltip.test.tsx +122 -0
  39. package/src/components/ui/accordion.tsx +16 -3
  40. package/src/components/ui/alert.tsx +1 -1
  41. package/src/components/ui/badge.tsx +12 -1
  42. package/src/components/ui/button.tsx +76 -18
  43. package/src/components/ui/card.tsx +59 -5
  44. package/src/components/ui/dropdown-menu.tsx +99 -5
  45. package/src/components/ui/file-upload.tsx +4 -0
  46. package/src/components/ui/index.ts +4 -1
  47. package/src/components/ui/popover-pro.tsx +41 -0
  48. package/src/components/ui/popover.tsx +162 -7
  49. package/src/components/ui/rating.tsx +24 -2
  50. package/src/components/ui/select.tsx +4 -4
  51. package/src/components/ui/simple-editor.tsx +3 -18
  52. package/src/components/ui/tabs.tsx +16 -1
  53. package/src/components/ui/textarea.tsx +6 -1
  54. package/src/components/ui/toast.tsx +17 -2
  55. package/src/components/ui/tooltip.tsx +154 -2
  56. 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(ad: string, blok: 'light' | 'dark'): string | null {
44
- const kaynak =
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 kaynak.match(new RegExp(`--${ad}:\\s*([^;]+);`))?.[1].trim() ?? null
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 bagilParlaklik([r, g, b]: [number, number, number]): number {
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 = bagilParlaklik(a)
88
- const lb = bagilParlaklik(b)
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
- zemin: [number, number, number]
97
+ backgroundVal: [number, number, number]
98
98
  ): [number, number, number] {
99
99
  return [
100
- Math.round(alfa * ust[0] + (1 - alfa) * zemin[0]),
101
- Math.round(alfa * ust[1] + (1 - alfa) * zemin[1]),
102
- Math.round(alfa * ust[2] + (1 - alfa) * zemin[2]),
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 KART_GERCEK_DARK: [number, number, number] = [17, 24, 39]
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 temaRenkleri(blok: 'light' | 'dark') {
115
- const dolgu = rgbTokenRgb(tokenOku('secondary-500', 'light')!) // skala yalnız :root'ta — iki temada aynı
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 zeminler: Array<[string, [number, number, number]]> = [
119
+ const backgrounds: Array<[string, [number, number, number]]> = [
120
120
  ['--background', background],
121
121
  ['--card', card],
122
122
  ]
123
- if (blok === 'dark') zeminler.push(['gerçek kart #111827', KART_GERCEK_DARK])
124
- return { dolgu, secondary, zeminler }
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 parcalar(container: HTMLElement) {
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 kaynak = fs.readFileSync(path.join(KOK, 'components/ui/slider.tsx'), 'utf8')
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(kaynak).toMatch(/secondary:\s*"bg-secondary\/20"/)
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(kaynak).toMatch(/secondary:\s*"bg-\[rgb\(var\(--secondary-500\)\)\]"/)
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(kaynak).toMatch(
174
+ expect(source).toMatch(
175
175
  /secondary:\s*"border-\[rgb\(var\(--secondary-500\)\)\] bg-background"/
176
176
  )
177
- expect(kaynak).not.toMatch(/secondary:\s*"border-secondary bg-background"/)
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 stilDizini = path.join(KOK, 'styles')
186
- const stiller = fs
187
- .readdirSync(stilDizini)
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(stilDizini, f), 'utf8'))
189
+ .map((f) => fs.readFileSync(path.join(styleDir, f), 'utf8'))
190
190
  .join('\n')
191
- expect(stiller).not.toMatch(/\.moonui-theme[^{]*\{[^}]*--secondary/)
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 } = parcalar(container)
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 } = parcalar(container)
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 } = parcalar(container)
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 } = parcalar(container)
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 { dolgu, secondary, zeminler } = temaRenkleri(blok)
238
- for (const [ad, zemin] of zeminler) {
239
- const track = alfaBileske(secondary, 0.2, zemin)
240
- const oran = rgbKontrast(dolgu, track)
241
- expect({ zemin: ad, oran }).toEqual({ zemin: ad, oran: expect.any(Number) })
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, zeminler } = temaRenkleri(blok)
250
- for (const [, zemin] of zeminler) {
251
- const track = alfaBileske(secondary, 0.2, zemin)
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, zeminler } = temaRenkleri('light')
264
- const track = alfaBileske(secondary, 0.2, zeminler[0][1])
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, zeminler } = temaRenkleri('dark')
271
- const track = alfaBileske(secondary, 0.2, zeminler[0][1])
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 gecenler = kademeler.filter((k) => {
280
- const renk = rgbTokenRgb(tokenOku(`secondary-${k}`, 'light')!)
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, zeminler } = temaRenkleri(blok)
283
- return zeminler.every(
284
- ([, zemin]) => rgbKontrast(renk, alfaBileske(secondary, 0.2, zemin)) >= 3.0
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(gecenler).toEqual([500])
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 { dolgu, zeminler } = temaRenkleri(blok)
347
- for (const [ad, zemin] of zeminler) {
348
- const oran = rgbKontrast(dolgu, zemin)
349
- expect({ zemin: ad, gecti: oran >= 3.0 }).toEqual({ zemin: ad, gecti: true })
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, zeminler } = temaRenkleri(blok)
356
- for (const [, zemin] of zeminler) {
357
- expect(rgbKontrast(secondary, zemin)).toBeLessThan(3.0)
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 gecenler = kademeler.filter((k) => {
368
- const renk = rgbTokenRgb(tokenOku(`secondary-${k}`, 'light')!)
367
+ const passingOnes = kademeler.filter((k) => {
368
+ const color = rgbTokenRgb(tokenOku(`secondary-${k}`, 'light')!)
369
369
  return (['light', 'dark'] as const).every((blok) =>
370
- temaRenkleri(blok).zeminler.every(([, zemin]) => rgbKontrast(renk, zemin) >= 3.0)
370
+ themeColors(blok).backgrounds.every(([, backgroundVal]) => rgbKontrast(color, backgroundVal) >= 3.0)
371
371
  )
372
372
  })
373
- expect(gecenler).toEqual([500])
373
+ expect(passingOnes).toEqual([500])
374
374
  })
375
375
  })