@ahmetilhn/hanui 4.0.0 → 5.3.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 (75) hide show
  1. package/README.md +259 -39
  2. package/build/charts.cjs +2 -0
  3. package/build/charts.cjs.map +1 -0
  4. package/build/charts.css +1 -0
  5. package/build/charts.js +1843 -0
  6. package/build/charts.js.map +1 -0
  7. package/build/chat.cjs +3 -0
  8. package/build/chat.cjs.map +1 -0
  9. package/build/chat.css +1 -0
  10. package/build/chat.js +707 -0
  11. package/build/chat.js.map +1 -0
  12. package/build/index.cjs +7 -7
  13. package/build/index.cjs.map +1 -1
  14. package/build/index.d.ts +661 -717
  15. package/build/index.js +3396 -3303
  16. package/build/index.js.map +1 -1
  17. package/build/scss/_colors.generated.scss +119 -0
  18. package/build/scss/_index.scss +6 -0
  19. package/build/scss/_metrics.generated.scss +49 -0
  20. package/build/scss/_mixins.scss +675 -0
  21. package/build/scss/_variables.scss +51 -0
  22. package/build/styles.css +1 -1
  23. package/build/types/charts/AreaChart/index.d.ts +14 -0
  24. package/build/types/charts/BarChart/index.d.ts +16 -0
  25. package/build/types/charts/BoxPlot/index.d.ts +43 -0
  26. package/build/types/charts/ComposedChart/index.d.ts +16 -0
  27. package/build/types/charts/Heatmap/index.d.ts +30 -0
  28. package/build/types/charts/Histogram/index.d.ts +29 -0
  29. package/build/types/charts/LineChart/index.d.ts +14 -0
  30. package/build/types/charts/PieChart/index.d.ts +36 -0
  31. package/build/types/charts/RadarChart/index.d.ts +40 -0
  32. package/build/types/charts/ScatterPlot/index.d.ts +41 -0
  33. package/build/types/charts/core/CartesianChart.d.ts +43 -0
  34. package/build/types/charts/core/ChartFrame.d.ts +55 -0
  35. package/build/types/charts/core/axes.d.ts +34 -0
  36. package/build/types/charts/core/cartesian.d.ts +79 -0
  37. package/build/types/charts/core/format.d.ts +9 -0
  38. package/build/types/charts/core/margins.d.ts +9 -0
  39. package/build/types/charts/core/palette.d.ts +13 -0
  40. package/build/types/charts/core/path.d.ts +24 -0
  41. package/build/types/charts/core/scale.d.ts +40 -0
  42. package/build/types/charts/core/series.d.ts +11 -0
  43. package/build/types/charts/core/spatial.d.ts +12 -0
  44. package/build/types/charts/core/stats.d.ts +34 -0
  45. package/build/types/charts/core/types.d.ts +31 -0
  46. package/build/types/charts/core/useChartWidth.d.ts +7 -0
  47. package/build/types/charts/index.d.ts +21 -0
  48. package/build/types/chat/ActionList/index.d.ts +46 -0
  49. package/build/types/chat/AppBar/index.d.ts +21 -0
  50. package/build/types/chat/ChatMessage/index.d.ts +24 -0
  51. package/build/types/chat/ChatThread/index.d.ts +24 -0
  52. package/build/types/chat/Citation/index.d.ts +15 -0
  53. package/build/types/chat/CodeBlock/index.d.ts +27 -0
  54. package/build/types/chat/DescriptionList/index.d.ts +16 -0
  55. package/build/types/chat/Legend/index.d.ts +20 -0
  56. package/build/types/chat/MessageActions/index.d.ts +21 -0
  57. package/build/types/chat/PromptInput/index.d.ts +47 -0
  58. package/build/types/chat/PromptSuggestions/index.d.ts +25 -0
  59. package/build/types/chat/Prose/index.d.ts +15 -0
  60. package/build/types/chat/ReasoningBlock/index.d.ts +24 -0
  61. package/build/types/chat/ReasoningText/index.d.ts +14 -0
  62. package/build/types/chat/SegmentBar/index.d.ts +46 -0
  63. package/build/types/chat/SourceList/index.d.ts +25 -0
  64. package/build/types/chat/TerminalOutput/index.d.ts +25 -0
  65. package/build/types/chat/ToolCall/index.d.ts +41 -0
  66. package/build/types/chat/index.d.ts +26 -0
  67. package/build/types/helpers/class-name.helper.d.ts +3 -0
  68. package/build/types/helpers/component.helper.d.ts +13 -0
  69. package/build/types/helpers/label.helper.d.ts +12 -0
  70. package/build/types/theme/palette.d.ts +203 -0
  71. package/build/types/theme/tokens.d.ts +208 -0
  72. package/build/types/types/common-element-props.type.d.ts +8 -0
  73. package/build/types/types/link.type.d.ts +14 -0
  74. package/build/types/types/theme.type.d.ts +223 -0
  75. package/package.json +19 -2
package/README.md CHANGED
@@ -12,6 +12,10 @@ kurulu; tek çalışma zamanı bağımlılığı ikon seti (`react-bootstrap-ico
12
12
  - **Yerel öğeler korunur.** `<dialog>`, `<input type="range">`,
13
13
  `<input type="radio">` — odak tuzağı, klavye gezinmesi ve ekran okuyucu
14
14
  duyurusu tarayıcıdan gelir.
15
+ - **Üç alt giriş, ana pakete sıfır bayt.** `@ahmetilhn/hanui/chat` (sohbet ve
16
+ araç arayüzleri), `@ahmetilhn/hanui/charts` (bağımlılıksız SVG grafikleri),
17
+ `@ahmetilhn/hanui/scss` (token değişkenleri ve mixin'ler). Kullanmayan uygulama
18
+ onları indirmez.
15
19
 
16
20
  ```bash
17
21
  npm install @ahmetilhn/hanui @ahmetilhn/handy-utils react-bootstrap-icons
@@ -57,20 +61,24 @@ Sağlayıcı **zorunlu değil**: paket kurulup hiçbir yapılandırma yapılmada
57
61
  Öznitelik yoksa `prefers-color-scheme` yedeği devreye girer — ama kullanıcının
58
62
  **açık bir seçimi** varsa sistem tercihi onu ezmez.
59
63
 
60
- ### İlk boyamadan önce yazın
64
+ ### İlk boyamadan önce yazın — `HanuiThemeScript`
61
65
 
62
66
  ```tsx
63
- // app/layout.tsx — <head> içine.
64
- <script
65
- dangerouslySetInnerHTML={{
66
- __html: `document.documentElement.dataset.hanuiTheme =
67
- localStorage.getItem('theme') ?? 'light';`,
68
- }}
69
- />
67
+ // app/layout.tsx — sunucu düzeni, <head> içine.
68
+ import { HanuiThemeScript } from '@ahmetilhn/hanui';
69
+
70
+ <head>
71
+ <HanuiThemeScript storageKey="theme" />
72
+ </head>;
70
73
  ```
71
74
 
72
- Bu satır React ağacına konamaz: sağlayıcı ağaç monte olduktan sonra çalışıyor
73
- ve koyu tema seçmiş kullanıcı o ana kadar **bir kare beyaz ekran** görüyor.
75
+ Kayıtlı açık seçimi (`light` / `dark`) `<html data-hanui-theme>`e yazar; kayıt
76
+ yoksa sistem tercihi izlenir. Satır içi betik HTML ayrıştırılırken çalışır.
77
+
78
+ Bu iş sağlayıcıya bırakılamaz: sağlayıcı ağaç monte olduktan sonra çalışıyor ve
79
+ koyu tema seçmiş kullanıcı o ana kadar **bir kare beyaz ekran** görüyor.
80
+ ⚠ Bir **bileşen**, fonksiyon değil: paket `'use client'` taşıdığı için dışa
81
+ verilen düz bir fonksiyon Next'in sunucu düzeninde çağrılamıyor.
74
82
 
75
83
  ### Token'ları ezmek
76
84
 
@@ -80,8 +88,8 @@ Yalnızca değiştirdiğiniz token'ı verin; gerisi varsayılanında kalır.
80
88
  import { HanuiProvider } from '@ahmetilhn/hanui';
81
89
 
82
90
  const THEME = {
83
- light: { blue: '#0d6efd', 'blue-text': '#0a58ca' },
84
- dark: { blue: '#6ea8fe' },
91
+ light: { link: '#0a58ca', 'ring-color': '#0d6efd' },
92
+ dark: { link: '#6ea8fe' },
85
93
  fonts: { heading: 'Archivo, sans-serif', body: 'Inter, sans-serif' },
86
94
  // Ölçüler tema başına DEĞİL: bir markanın yuvarlaklığı açık temada
87
95
  // 12 px, koyu temada 8 px olmaz.
@@ -336,21 +344,27 @@ büyür, bir duyuru şeridi çıkıp kaybolduğunda değişir.
336
344
 
337
345
  ## Bileşen kataloğu
338
346
 
339
- | Grup | Bileşenler |
340
- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
341
- | Eylem | `Button` · `IconButton` · `TextLink` · `Tooltip` |
342
- | Seçim | `Chip` · `ChipGroup` · `Select` · `Combobox` · `Checkbox` · `Radio` · `RadioCard` · `RangeSlider` · `RatingInput` · `QuantityStepper` · `TableCheckbox` |
343
- | Girdi | `Input` · `Textarea` · `Field` |
344
- | Yüzey | `Card` (+ `CardMedia`, `CardOverlay`, `CardBody`, `CardFooter`) · `SummaryCard` · `Panel` (+ `PanelForm`, `PanelRow`) · `PageHeader` · `SectionHeader` · `Tile` · `Directory` |
345
- | Geri bild. | `Alert` · `Badge` · `CodeBadge` · `EmptyState` · `Skeleton` · `Spinner` · `Divider` · `Avatar` · `Rating` · `Price` · `CopyField` · `Breadcrumb` |
346
- | Kipsel | `Modal` · `BottomSheet` · `ConfirmDialog` · `PromptDialog` |
347
- | Veri | `Table` (+ `TableScroller`) · `DataTable` (+ `DataTableRow`) · `FilterBar` (+ `FilterBarField`) · `Pagination` |
347
+ | Grup | Bileşenler |
348
+ | --- | --- |
349
+ | Eylem | `Button` · `IconButton` · `TextLink` · `Tooltip` · `Menu` · `Popover` |
350
+ | Seçim | `Chip` · `ChipGroup` · `SegmentedControl` · `Select` · `Combobox` · `Checkbox` · `Switch` · `Radio` · `RadioCard` · `RangeSlider` · `RatingInput` · `QuantityStepper` · `TableCheckbox` |
351
+ | Girdi | `Input` · `PasswordInput` · `Textarea` · `TagInput` · `DateField` · `FileUpload` · `Field` |
352
+ | Yüzey | `Card` (+ `CardMedia`, `CardOverlay`, `CardBody`, `CardFooter`) · `SummaryCard` · `Panel` (+ `PanelForm`, `PanelRow`) · `PageHeader` · `SectionHeader` · `Tile` · `Directory` (+ `DirectoryGroup`, `DirectoryRow`, `DirectoryJump`) · `Carousel` · `ScrollArea` · `Disclosure` · `Accordion` · `Tabs` |
353
+ | Geri bild. | `Alert` · `Badge` · `StatusDot` · `CodeBadge` · `Kbd` · `EmptyState` · `Skeleton` (+ `SkeletonCard`, `SkeletonRows`, `SkeletonTable`) · `Spinner` · `Progress` · `Steps` · `Divider` · `Avatar` · `Rating` · `Price` · `Stat` · `CopyField` · `Breadcrumb` · `ToastHub` (+ `ToastPortal`) |
354
+ | Kipsel | `Modal` · `Drawer` · `BottomSheet` · `ConfirmDialog` · `PromptDialog` · `CommandPalette` |
355
+ | Veri | `Table` (+ `TableScroller`) · `DataTable` (+ `DataTableRow`) · `FilterBar` (+ `FilterBarField`) · `Pagination` · `Timeline` |
356
+ | Tema ve kancalar | `HanuiProvider` · `HanuiThemeScript` · `initHanui` · `useHanui` · `useHanuiTheme` · `useHanuiTokens` · `useAnnounce` · `useStickToBottom` · `usePositioning` · `useVirtualList` · `useListboxNavigation` · `hasOpenSurface` |
357
+ | Sohbet — `/chat` | `ChatThread` · `ChatMessage` · `PromptInput` · `PromptSuggestions` · `Prose` · `CodeBlock` · `ReasoningBlock` (+ `ReasoningText`) · `ToolCall` (+ `ToolCallSection`) · `TerminalOutput` · `SourceList` · `Citation` · `MessageActions` · `AppBar` · `ActionList` (+ `ActionListItem`) · `SegmentBar` · `Legend` · `DescriptionList` |
358
+ | Grafik — `/charts` | `LineChart` · `AreaChart` · `BarChart` · `ComposedChart` · `PieChart` · `ScatterPlot` · `Heatmap` · `BoxPlot` · `Histogram` · `RadarChart` |
359
+
360
+ Alt girişlerin stilleri ayrı dosyada ve `styles.css`ten **sonra** içe aktarılır:
361
+ `@ahmetilhn/hanui/chat.css`, `@ahmetilhn/hanui/charts.css`.
348
362
 
349
363
  ### Seçenek sayısı öğeyi belirler
350
364
 
351
365
  | Seçenek sayısı | Öğe |
352
366
  | -------------- | ----------------------------------------------- |
353
- | 2–4 | `ChipGroup` — hepsi ekranda, tek dokunuş |
367
+ | ≤7 | `ChipGroup` — hepsi ekranda, tek dokunuş |
354
368
  | 5–20 | `Select` — aramasız liste; mobilde alt sayfa |
355
369
  | 20+ | `Combobox` — yazarak ara |
356
370
  | Aralık | `RangeSlider` (+ kesin değer için sayı girdisi) |
@@ -363,14 +377,20 @@ da 3 seçeneği de aynı biçimde göstermek, ikisini de yanlış gösterir.
363
377
 
364
378
  ## Sistemin taşıdığı kararlar
365
379
 
380
+ **Zemin mürekkep ve kâğıt, yeşil eylemdir.** Nötrler sıcak mürekkep ekseninde
381
+ (açık sayfa `#faf9f5`, koyu sayfa `#141413`); markanın yeşili yalnızca birincil
382
+ eylemde, bağlantıda, odak halkasında ve marka işaretinde. Seçim — etkin sayfa,
383
+ seçili çip, sekme çizgisi, ilerleme, onay kutusu, anahtar — mürekkeptir.
384
+ Yeşilin yayılmasını `green-footprint.test.ts` derleme hatasına çevirir.
385
+
366
386
  **Doygun dolgu = tıklanabilir.** Durum etiketleri **her zaman** tint zeminli
367
387
  (`Badge`, `Alert`), eylemler **her zaman** dolgulu veya çerçeveli. Bu ayrım
368
388
  bozulduğunda kullanıcı durum etiketine tıklamayı deniyor. `Badge`in `solid`
369
389
  varyantı durum tonlarında bilinçli olarak `soft` ile aynı şeyi verir.
370
390
 
371
391
  **Bir ekranda tek `PRIMARY`.** `UIVariant` bir vurgu hiyerarşisi: iki dolu düğme
372
- yan yana durduğunda hiçbiri öne çıkmaz. `UIVariant.CART` ekrandaki tek doygun
373
- turuncu — dönüşüm eylemi.
392
+ yan yana durduğunda hiçbiri öne çıkmaz. `UIVariant.CART` ekrandaki en parlak
393
+ yeşil — dönüşüm eylemi; birincil eylemden (koyu çam) aydınlıkla ayrışır.
374
394
 
375
395
  **Renk tek başına anlam taşımaz** (WCAG 1.4.1). Her durum bir metin, ikon veya
376
396
  işaretle de bildirilir: `Alert`in her tonunun kendi ikonu var, `Field`in zorunlu
@@ -407,7 +427,8 @@ benzemiyor. Bedeli (odak yönetimi, ok tuşları, `aria-activedescendant`) bir k
407
427
  **Dokunma hedefi en az 44×44 px** (WCAG 2.5.8). Görsel kutu daha küçük olabilir;
408
428
  tıklanabilir alan görünmez bir örtüyle korunur.
409
429
 
410
- **Hareket 200 ms'yi geçmez** ve `prefers-reduced-motion` her animasyonda
430
+ **Geçişler 200 ms'yi geçmez** — tek istisna dipten açılan panel
431
+ (`duration-slow`, 320 ms) — ve `prefers-reduced-motion` her animasyonda
411
432
  karşılanır.
412
433
 
413
434
  **Odak halkası `:focus-visible` ile.** `:focus` fareyle tıklandığında da halka
@@ -421,12 +442,30 @@ Paket `'use client'` sınırını **kendi içinde** çizer: sunucu bileşenlerin
421
442
  doğrudan import edilebilir, `children` sunucuda çizilip aktarılır.
422
443
 
423
444
  ```tsx
424
- // app/layout.tsx
445
+ // app/layout.tsx — sunucu bileşeni
425
446
  import '@ahmetilhn/hanui/styles.css';
426
- import { HanuiProvider } from '@ahmetilhn/hanui';
447
+ import '@ahmetilhn/hanui/chat.css'; // yalnızca /chat kullanılıyorsa
448
+ import '@ahmetilhn/hanui/charts.css'; // yalnızca /charts kullanılıyorsa
449
+ import { HanuiProvider, HanuiThemeScript } from '@ahmetilhn/hanui';
427
450
  import NextLink from 'next/link';
451
+
452
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
453
+ return (
454
+ <html lang="tr" suppressHydrationWarning>
455
+ <head>
456
+ <HanuiThemeScript storageKey="theme" />
457
+ </head>
458
+ <body>
459
+ <HanuiProvider linkComponent={NextLink}>{children}</HanuiProvider>
460
+ </body>
461
+ </html>
462
+ );
463
+ }
428
464
  ```
429
465
 
466
+ `suppressHydrationWarning`: tema betiği `<html>`e sunucunun yazmadığı bir
467
+ öznitelik ekliyor; uyarı yalnızca o öğe için susturulur.
468
+
430
469
  ---
431
470
 
432
471
  ## Geliştirme
@@ -441,13 +480,14 @@ npm run playground # bileşen galerisi → http://localhost:5273
441
480
  | Komut | |
442
481
  | ------------------------- | --------------------------------------------------------------- |
443
482
  | `npm run tokens` | SCSS token dosyalarını `theme/tokens.ts`ten üretir |
444
- | `npm run build` | `build/` (ESM + CJS + `.d.ts` + `styles.css`) |
483
+ | `npm run build` | `build/` — ana giriş + `chat` + `charts` (ESM + CJS + `.d.ts` + CSS) ve `build/scss/` |
445
484
  | `npm run test` | Jest + Testing Library + `jest-axe` |
446
- | `npm run test:coverage` | kapsam kapısı — `helpers`/`hooks`/`theme` satır ≥ %80 |
485
+ | `npm run test:coverage` | kapsam kapısı — tüm `src/`: satır 89 · ifade 86 · fonksiyon 80 · dal 75 |
447
486
  | `npm run test:visual` | görsel regresyon (Playwright); `verify` İÇİNDE DEĞİL, aşağı bkz. |
448
487
  | `npm run test:device` | iOS (WebKit) + Android (Chromium) davranış nöbetçileri, aşağı bkz. |
449
- | `npm run check:contrast` | WCAG kontrast ölçümü, iki temada 120 çift |
450
- | `npm run check:api` | genel API nöbetçisi — `build/index.d.ts` ↔ `scripts/api-baseline.txt` (164 ad) |
488
+ | `npm run check:contrast` | WCAG kontrast ölçümü, iki temada 210 çift |
489
+ | `npm run check:palette` | palet tarifinin çıktısı mı + nötr chroma tavanı + tint ve kategori ayrışması |
490
+ | `npm run check:api` | genel API nöbetçisi — dört giriş, dört taban çizgisi (ana 176 · sohbet 30 · grafik 22 · SCSS 252 ad) |
451
491
  | `npm run size` | paket boyutu bütçesi (`size-limit`) |
452
492
  | `npm run playground` | bileşen galerisi (tema · RTL · yoğunluk anahtarı) |
453
493
  | `npm run lint` | ESLint (TS) + stylelint (SCSS) |
@@ -465,19 +505,32 @@ npm run playground # bileşen galerisi → http://localhost:5273
465
505
  - **Token sözleşmesi** — açık/koyu anahtar kümeleri ve üretilmiş SCSS kaynakla
466
506
  eşleşir. Ayrışma bugüne kadar sessizdi.
467
507
  - **Paket boyutu** — tek bileşen import eden bir uygulamanın ne indirdiği
468
- ölçülür. Ölçülen (2026-08-15): `Badge` **3,53 kB** · `Button` **4,16 kB** ·
469
- `styles.css` **21,11 kB** · tüm paket **37,54 kB** gzip.
470
- ⚠ **Bütçe 38 kB, yani başlıkta kalan pay %1,2.** Bu satır bir dönem
508
+ ölçülür. Ölçülen (2026-10-10, `size-limit`): `Badge` **4,33 kB** · `Button`
509
+ **5,12 kB** · `styles.css` **23,07 kB** · tüm paket **42,05 kB** gzip. Alt
510
+ girişler ana pakette SAYILMAZ: sohbet **6,32 kB**, grafikler **14,02 kB**
511
+ (yalnızca `LineChart` **7,19 kB**).
512
+ ⚠ **Bütçeler `.size-limit.js`te** (paket 43 kB, `styles.css` 24 kB) ve
513
+ başlıkta kalan pay dar. Bu satır bir dönem
471
514
  `Badge 2,7 / Button 3,4 / paket 22,2` diyordu — ölçüm değil, bayat bir
472
515
  kopyaydı ve okuyana **%70 boş alan** olduğunu düşündürüyordu. Yeni bir
473
516
  bileşen eklerken bütçenin fiilen dolu olduğu bilinerek girilir; `size-limit`
474
517
  kırmızıya döndüğünde doğru tepki limiti yükseltmek değil, ne eklendiğini
475
518
  ölçmektir (`npm run size`).
476
- - **Genel API** — `build/index.d.ts`teki dışa aktarım kümesi
477
- `scripts/api-baseline.txt` ile karşılaştırılır (`npm run check:api`, 164 ad).
478
- Düşen bir ad **kırıcı**dır ama bugün görünmez: iki tüketici de sürümü tam
479
- pinliyor, yani kırılma bir sonraki yükseltmede ortaya çıkar. Bilinçli
480
- değişiklikte `node scripts/check-api.mjs --update` + CLAUDE.md'ye gerekçe.
519
+ - **Genel API** — her girişin dışa aktarım kümesi kendi taban çizgisiyle
520
+ karşılaştırılır (`npm run check:api`): ana giriş `scripts/api-baseline.txt`
521
+ (176), `chat` (30), `charts` (22), `scss` (252 `$değişken` + `@mixin`). Düşen
522
+ bir ad **kırıcı**dır ve tüketiciler caret ile aldığı için bir sonraki
523
+ `npm install`da derleme hatası olarak çıkar. Bilinçli değişiklikte
524
+ `node scripts/check-api.mjs --update` + CLAUDE.md'ye gerekçe.
525
+ - **Alt giriş sınırı** — `chat` ve `charts` ana girişi yalnızca PAKET ADIYLA
526
+ alır (`subentry-boundary.test.ts`); göreli bir yol `HanuiProvider` bağlamının
527
+ ve kipsel yüzey yığınının ikinci bir kopyasını paketlerdi.
528
+ - **Yeşil ayak izi** — bileşen SCSS'inde yeşil token ailesine uzanan her
529
+ bileşen gerekçeli bir izin listesinde (`green-footprint.test.ts`).
530
+ - **Opacity ile soluklaştırma yasak** — `0`/`1` dışındaki her `opacity`
531
+ derlemeyi kırar (`passive-opacity.test.ts`); pasiflik token'larla çizilir.
532
+ - **iOS yakınlaştırması** — galerideki her metin girdisi dar ekranda ≥16px
533
+ (`e2e/mobile.spec.ts`, hesaplanmış stil).
481
534
  - **Odak halkası ve hareket ölçeği** — stylelint, `focus-ring` mixin'i dışında
482
535
  `outline` ve bileşen SCSS'inde ham süre değeri (`0.7s`) yazılmasını
483
536
  engelliyor. Üç bileşen kendi halkasını, iki bileşen kendi süresini
@@ -525,11 +578,178 @@ yol, Playwright'ın resmi konteynerinde koşup `linux` referanslarını orada
525
578
  > `=all` mod, pikseli değişen her dosyayı yazar (bayt bayt aynı olanlara
526
579
  > dokunmaz, o yüzden dosya sayısı toplamdan az çıkabilir). Doğrulama:
527
580
  > yenilemeden sonra bir referansın baskın renklerini örnekleyin.
581
+ > ⚠ `=changed` de yetmez: yalnızca eşiği (`maxDiffPixelRatio: 0.002`) aşan
582
+ > farkı yazar — iki küçük yazının kalktığı bir değişiklik eşiğin altında kalıp
583
+ > eski referans dosyada durur. Küçük bir değişiklikte bölümün referansını silip
584
+ > `=missing` ile yazdırın.
528
585
 
529
586
  `master`a her push `npm publish` çalıştırır — **`package.json` sürümünü
530
587
  yükseltmeyi unutmayın**, aynı sürüm ikinci kez yayımlanamaz ve iş akışı kırmızı
531
588
  döner.
532
589
 
590
+ ## Sohbet bileşenleri — `@ahmetilhn/hanui/chat`
591
+
592
+ Sohbet ve araç arayüzleri için ayrı bir giriş; ana paketi ve `styles.css`i
593
+ büyütmez.
594
+
595
+ ```tsx
596
+ import '@ahmetilhn/hanui/styles.css';
597
+ import '@ahmetilhn/hanui/chat.css'; // styles.css'TEN SONRA
598
+
599
+ import { HanuiProvider } from '@ahmetilhn/hanui';
600
+ import { ChatMessage, ChatThread, PromptInput } from '@ahmetilhn/hanui/chat';
601
+
602
+ <ChatThread
603
+ isStreaming={isStreaming}
604
+ dock={
605
+ <PromptInput
606
+ value={draft}
607
+ onChange={setDraft}
608
+ onSubmit={send}
609
+ onStop={stop}
610
+ isBusy={isStreaming}
611
+ placeholder="Mesajınızı yazın…"
612
+ />
613
+ }
614
+ >
615
+ {messages.map(message => (
616
+ <ChatMessage key={message.id} author={message.author}>
617
+ {message.content}
618
+ </ChatMessage>
619
+ ))}
620
+ </ChatThread>;
621
+ ```
622
+
623
+ Metinler aynı sağlayıcıdan, `labels.chat` altında:
624
+
625
+ ```tsx
626
+ <HanuiProvider
627
+ labels={{
628
+ chat: {
629
+ thread: 'Sohbet',
630
+ send: 'Gönder',
631
+ stop: 'Durdur',
632
+ scrollToLatest: 'En yeniye in',
633
+ replyReady: 'Yanıt hazır',
634
+ messageActions: 'Mesaj eylemleri',
635
+ sources: 'Kaynaklar',
636
+ citation: index => `Kaynak ${index}`,
637
+ stderr: 'hata çıktısı',
638
+ toolStatus: { running: 'çalışıyor', ok: 'tamamlandı', error: 'hata', aborted: 'durduruldu', interrupted: 'kesildi' },
639
+ codeCopy: language => `${language} kodunu kopyala`,
640
+ codeCopied: 'Kod kopyalandı',
641
+ },
642
+ }}
643
+ >
644
+ ```
645
+
646
+ Ayrıştırma ve vurgulama tüketicide kalır: `Prose` markdown ayrıştırmaz,
647
+ `CodeBlock` önceden vurgulanmış HTML alır (`html` prop'u — güvenliği sizin).
648
+ Bileşen listesi ve kararlar: `CLAUDE.md` → "`@ahmetilhn/hanui/chat`".
649
+
650
+ ## SCSS API — `@ahmetilhn/hanui/scss`
651
+
652
+ Token değişkenleri ve mixin'ler kopyalanmadan alınır. Değişkenler
653
+ `var(--hanui-…)` dizeleridir; tema çalışma zamanında döner. Modül kural basmaz,
654
+ `styles.css` yine gerekir.
655
+
656
+ ```scss
657
+ @use '@ahmetilhn/hanui/scss' as hanui;
658
+
659
+ .panel {
660
+ padding: hanui.$space-4;
661
+ border: 1px solid hanui.$border;
662
+
663
+ @include hanui.focus-ring;
664
+
665
+ @media (max-width: hanui.$breakpoint-tablet-portrait) {
666
+ padding: hanui.$space-3;
667
+ }
668
+ }
669
+ ```
670
+
671
+ Next.js (Turbopack ve webpack) paket `exports`'unu çözer. Başka bir derleyici
672
+ çözemezse yol `node_modules/@ahmetilhn/hanui/build/scss` ile `loadPaths`e verilip
673
+ `@use 'index' as hanui` yazılabilir.
674
+
675
+ ## Grafikler — `@ahmetilhn/hanui/charts`
676
+
677
+ On SVG grafiği, sıfır bağımlılık (çizim, ölçek ve istatistik kütüphanenin
678
+ kendisinde). Ana paketi ve `styles.css`i büyütmez.
679
+
680
+ ```tsx
681
+ import '@ahmetilhn/hanui/styles.css';
682
+ import '@ahmetilhn/hanui/charts.css'; // styles.css'TEN SONRA
683
+
684
+ import { BarChart, LineChart, PieChart } from '@ahmetilhn/hanui/charts';
685
+
686
+ <LineChart
687
+ label="Aylık gelir ve gider"
688
+ description="Gelir ekimde zirve yaptı"
689
+ xKey="month"
690
+ data={[
691
+ { month: 'Oca', revenue: 120, cost: 90 },
692
+ { month: 'Şub', revenue: 180, cost: null }, // null = veri yok: çizgi KESİLİR
693
+ ]}
694
+ series={[
695
+ { key: 'revenue', label: 'Gelir' },
696
+ { key: 'cost', label: 'Gider', format: value => `${value} ₺` },
697
+ ]}
698
+ />;
699
+ ```
700
+
701
+ | Grafik | Ne zaman |
702
+ |---|---|
703
+ | `LineChart` · `AreaChart` | Zaman içinde eğilim; `AreaChart isStacked` toplamın parçaları |
704
+ | `BarChart` | Kategorileri karşılaştırma; `orientation="horizontal"` uzun adlar için |
705
+ | `ComposedChart` | Çubuk + çizgi; farklı birim `axis: 'right'` |
706
+ | `PieChart` | Bir bütünün ≤6 parçası (`variant="donut"` varsayılan) |
707
+ | `ScatterPlot` | İki değişkenin ilişkisi; `size` ile balon |
708
+ | `Heatmap` | İki kategorik eksende yoğunluk (gün × saat) |
709
+ | `BoxPlot` · `Histogram` | Dağılım: grupları karşılaştırma · tek değişkenin şekli |
710
+ | `RadarChart` | Birkaç seriyi aynı eksen takımında; eksenler AYNI ölçekte |
711
+
712
+ Her grafik tek bir sekme durağıdır: oklar öğeleri gezer (gelinen öğe
713
+ duyurulur), `Home`/`End` uçlara gider, `Escape` vurguyu kaldırır. Ekran okuyucu
714
+ grafiği gizli bir **veri tablosu** olarak da okur. Genişlik kapsayıcıdan ölçülür
715
+ (`width` ile sabitlenebilir), yükseklik `height`. Sayılar `labels.locale`
716
+ yereliyle biçimlenir; `formatValue` / `format` ile ezilir. Renkler kategorik
717
+ paletten (`tone`: `cat-1`…`cat-5`, `series-1`, `series-2`, `muted`).
718
+
719
+ ## 5.0'a geçiş
720
+
721
+ 5.0 bir **renk** sürümü: nötr eksen yeşilden sıcak mürekkebe geçti, yeşil
722
+ yalnızca eylem, bağlantı ve odakta kaldı. Bileşen API'si değişmedi; kırılan şey
723
+ token adları ve görünüm.
724
+
725
+ | 4.x token | 5.0 |
726
+ |---|---|
727
+ | `role`, `role-hover`, `role-active` | `selected`, `selected-hover`, `selected-active` (mürekkep) |
728
+ | `on-role` | `on-selected` |
729
+ | `role-tint` / `role-line` | `selected-tint` / `selected-line` |
730
+ | `role-text` | `link` (+ `link-hover`) |
731
+ | `shell*` | aynı ad, değer çamdan mürekkebe; yeni `on-shell`, `on-shell-2`, `on-shell-danger`, `shell-line` |
732
+ | — | `cat-1…5`, `series-1/2` (kategorik palet) |
733
+
734
+ Eski adlara takma ad **yok**. Kendi CSS'inizde `--hanui-role*` okuyan ya da
735
+ `theme={{ light: { role: … } }}` ezmesi yazan yerler yeni adlara geçer.
736
+ Görünüm değişiklikleri: açık sayfa krem (`#faf9f5`), koyu sayfa sıcak siyah
737
+ (`#141413`), kart ve panel durgunken gölgesiz, `info` tonu nötr, `CodeBadge`
738
+ mürekkep. Ayrıntı ve ölçümler: `CLAUDE.md` → "Renk kararları (5.0)".
739
+
740
+ ## 5.1 – 5.3'te eklenenler
741
+
742
+ Hepsi ekleme; 5.0'ın token sözleşmesi dışında kırıcı değişiklik yok.
743
+
744
+ | Sürüm | Ne |
745
+ |---|---|
746
+ | 5.1 | `Disclosure`, `StatusDot`, `Kbd`; `useHanuiTokens`, `useStickToBottom`, `hasOpenSurface`; `Modal size="xl"`, `Button size={UISize.XSMALL}` + `shape="pill"`, `IconButton shape="round"`, `Textarea variant="bare"`, `Progress size="xs"`, etkileşimli `CodeBadge`, `Avatar` (`icon`, `shape="rounded"`, `tone="brand"`), `Stat variant="inline"`, `ScrollArea isFollowingEnd`; `CommandPalette` `hotkey` + komut kapanıştan SONRA çalışır |
747
+ | 5.2 | `HanuiThemeScript`; `@ahmetilhn/hanui/chat` — 20 sohbet bileşeni |
748
+ | 5.3 | `@ahmetilhn/hanui/charts` — 10 SVG grafiği; `@ahmetilhn/hanui/scss`; `Input --technical` dar ekranda ≥16px; `QuantityStepper` ve `DataTable` pasif/dinlenme hâli opacity yerine token |
749
+
750
+ Ayrıntı ve ölçülmüş gerekçeler: `CLAUDE.md` → "Göç ve `@deprecated` yollar",
751
+ "`@ahmetilhn/hanui/chat`", "`@ahmetilhn/hanui/charts`".
752
+
533
753
  ## Tasarım sözleşmesi
534
754
 
535
755
  Öğe seçimi (hangi durumda hangi bileşen), yüzey/yükseklik merdiveni, renk
@@ -0,0 +1,2 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("react/jsx-runtime"),f=require("react"),dt=require("@ahmetilhn/hanui"),at=(t,s)=>Object.assign(t,{displayName:s}),ue="hanui-axis-AqLAe",me="hanui-axis__label-dHxpo",de="hanui-axis__grid-RVxXW",xe="hanui-axis__baseline-jhG2z",st={axis:ue,axis__label:me,axis__grid:de,axis__baseline:xe},ce=6.4,ht=t=>t.length*ce,re=(t,s=8)=>{if(t.length<2)return[...t];const e=Math.max(...t.map(u=>ht(u.label))),n=Math.abs(t[1].position-t[0].position),r=n<=0?1:Math.max(1,Math.ceil((e+s)/n));return t.filter((u,m)=>m%r===0)},St=(t,s)=>{const e=Math.max(1,Math.floor(s/ce));return t.length<=e?t:`${t.slice(0,Math.max(1,e-1))}…`},ut=({area:t,ticks:s,side:e,hasGrid:n=!1})=>l.jsx("g",{className:st.axis,"aria-hidden":!0,children:s.map(r=>e==="bottom"?l.jsxs("g",{children:[n&&l.jsx("line",{className:st.axis__grid,x1:r.position,x2:r.position,y1:t.top,y2:t.bottom}),l.jsx("text",{className:st.axis__label,x:r.position,y:t.bottom+16,textAnchor:"middle",children:r.label})]},`${r.position}-${r.label}`):l.jsxs("g",{children:[n&&l.jsx("line",{className:st.axis__grid,x1:t.left,x2:t.right,y1:r.position,y2:r.position}),l.jsx("text",{className:st.axis__label,x:e==="left"?t.left-8:t.right+8,y:r.position,dy:"0.32em",textAnchor:e==="left"?"end":"start",children:r.label})]},`${r.position}-${r.label}`))}),qt=({area:t,ticks:s,side:e})=>l.jsx("g",{className:st.axis,"aria-hidden":!0,children:e==="bottom"?l.jsxs(l.Fragment,{children:[l.jsx("line",{className:st.axis__baseline,x1:t.left,x2:t.right,y1:t.bottom,y2:t.bottom}),re(s).map(n=>l.jsx("text",{className:st.axis__label,x:n.position,y:t.bottom+16,textAnchor:"middle",children:n.label},`${n.position}-${n.label}`))]}):l.jsxs(l.Fragment,{children:[l.jsx("line",{className:st.axis__baseline,x1:t.left,x2:t.left,y1:t.top,y2:t.bottom}),s.map(n=>l.jsx("text",{className:st.axis__label,x:t.left-8,y:n.position,dy:"0.32em",textAnchor:"end",children:St(n.label,t.left-12)},`${n.position}-${n.label}`))]})}),W=t=>Math.round(t*100)/100,fe=t=>{const s=[];let e=[];for(const n of t)n===null||!Number.isFinite(n.x)||!Number.isFinite(n.y)?(e.length>0&&s.push(e),e=[]):e.push(n);return e.length>0&&s.push(e),s},Ft=t=>t.map((s,e)=>`${e===0?"M":"L"}${W(s.x)},${W(s.y)}`).join(""),le=t=>{const s=t.length;if(s<3)return Ft(t);const e=[],n=[];for(let u=0;u<s-1;u+=1){const m=t[u+1].x-t[u].x;n.push(m===0?0:(t[u+1].y-t[u].y)/m)}e.push(n[0]);for(let u=1;u<s-1;u+=1)e.push(n[u-1]*n[u]<=0?0:(n[u-1]+n[u])/2);e.push(n[s-2]);for(let u=0;u<s-1;u+=1){if(n[u]===0){e[u]=0,e[u+1]=0;continue}const m=e[u]/n[u],d=e[u+1]/n[u],k=m*m+d*d;if(k>9){const T=3/Math.sqrt(k);e[u]=T*m*n[u],e[u+1]=T*d*n[u]}}let r=`M${W(t[0].x)},${W(t[0].y)}`;for(let u=0;u<s-1;u+=1){const m=t[u],d=t[u+1],k=(d.x-m.x)/3;r+=`C${W(m.x+k)},${W(m.y+e[u]*k)} ${W(d.x-k)},${W(d.y-e[u+1]*k)} ${W(d.x)},${W(d.y)}`}return r},pe=(t,s="linear")=>fe(t).map(e=>s==="monotone"?le(e):Ft(e)).join(""),ye=(t,s,e="linear")=>{const n=[];let r=[],u=[];const m=()=>{if(r.length>0){const d=e==="monotone"?le:Ft,k=d([...u].reverse()).replace(/^M/,"L");n.push(`${d(r)}${k}Z`)}r=[],u=[]};return t.forEach((d,k)=>{const T=s[k];d===null||T===null||T===void 0?m():(r.push(d),u.push(T))}),m(),n.join("")},tt=(t,s,e,n)=>({x:t+e*Math.sin(n),y:s-e*Math.cos(n)}),Xt=(t,s,e,n,r,u)=>{const m=u-r;if(m<=0||e<=0)return"";if(m>=Math.PI*2-1e-6){const M=(F,z)=>`M${W(t)},${W(s-F)}A${F},${F} 0 1 ${z} ${W(t)},${W(s+F)}A${F},${F} 0 1 ${z} ${W(t)},${W(s-F)}Z`;return M(e,1)+(n>0?M(n,0):"")}const d=m>Math.PI?1:0,k=tt(t,s,e,r),T=tt(t,s,e,u);if(n<=0)return`M${W(t)},${W(s)}L${W(k.x)},${W(k.y)}A${e},${e} 0 ${d} 1 ${W(T.x)},${W(T.y)}Z`;const A=tt(t,s,n,u),I=tt(t,s,n,r);return`M${W(k.x)},${W(k.y)}A${e},${e} 0 ${d} 1 ${W(T.x)},${W(T.y)}L${W(A.x)},${W(A.y)}A${n},${n} 0 ${d} 0 ${W(I.x)},${W(I.y)}Z`},Yt=t=>t.length===0?"":`${Ft(t)}Z`,mt=(t,s)=>{const[e,n]=t,[r,u]=s,m=n-e,d=k=>m===0?(r+u)/2:r+(k-e)/m*(u-r);return d.domain=t,d.range=s,d.invert=k=>u===r?e:e+(k-r)/(u-r)*m,d},Rt=(t,s,{paddingInner:e=.2,paddingOuter:n=.1}={})=>{const[r,u]=s,m=t.length,d=m===0?0:(u-r)/Math.max(1,m-e+n*2),k=d*(1-e),T=r+d*n,A=new Map(t.map((M,F)=>[M,F])),I=M=>{const F=A.get(M);return F===void 0?Number.NaN:T+F*d};return I.domain=t,I.range=s,I.bandwidth=k,I.step=d,I.indexAt=M=>m===0?-1:Math.min(m-1,Math.max(0,Math.floor((M-r-d*n+(d-k)/2)/d))),I},Ot=(t,s,e)=>{const n=Math.abs(s-t);if(n===0||!Number.isFinite(n)||e<=0)return 1;const r=n/e,u=10**Math.floor(Math.log10(r)),m=r/u;return(m>=7.5?10:m>=3.5?5:m>=1.5?2:1)*u},Gt=(t,s)=>{const e=Math.max(0,-Math.floor(Math.log10(s))+1);return Number(t.toFixed(Math.min(e,12)))},$t=(t,s,e=5)=>{if(!Number.isFinite(t)||!Number.isFinite(s))return[0,1];let n=Math.min(t,s),r=Math.max(t,s);if(n===r){if(n===0)return[0,1];const m=Math.abs(n)*.5;n=n>0?0:n-m,r=r<0?0:r+m}const u=Ot(n,r,e);return[Gt(Math.floor(n/u)*u,u),Gt(Math.ceil(r/u)*u,u)]},ot=(t,s,e=5)=>{if(!Number.isFinite(t)||!Number.isFinite(s))return[];if(t===s)return[t];const n=Ot(t,s,e),r=Math.ceil(t/n-1e-9),u=Math.floor(s/n+1e-9),m=[];for(let d=r;d<=u&&m.length<1e3;d+=1)m.push(Gt(d*n,n));return m},be=(t,s)=>{if(t.length===0)return-1;let e=0,n=t.length-1;for(;e<n;){const r=e+n>>1;t[r]<s?e=r+1:n=r}return e>0&&Math.abs(t[e-1]-s)<=Math.abs(t[e]-s)?e-1:e},Ht=t=>t.filter(s=>typeof s=="number"&&Number.isFinite(s)),xt=t=>{if(t.length===0)return[0,0];let s=t[0],e=t[0];for(const n of t)n<s&&(s=n),n>e&&(e=n);return[s,e]},zt=(t,s)=>{if(t.length===0)return Number.NaN;if(t.length===1)return t[0];const e=(t.length-1)*Math.min(1,Math.max(0,s)),n=Math.floor(e),r=e-n;return n+1<t.length?t[n]+(t[n+1]-t[n])*r:t[n]},ge=t=>{const s=Ht(t).sort((m,d)=>m-d);if(s.length===0)return null;const e=zt(s,.25),n=zt(s,.75),r=(n-e)*1.5,u=s.filter(m=>m>=e-r&&m<=n+r);return{count:s.length,min:s[0],q1:e,median:zt(s,.5),q3:n,max:s[s.length-1],whiskerLow:u[0]??e,whiskerHigh:u[u.length-1]??n,outliers:s.filter(m=>m<e-r||m>n+r)}},Me=(t,s)=>{const e=Ht(t);if(e.length===0)return[];const[n,r]=xt(e);if(n===r)return[{from:n,to:n+1,count:e.length}];const u=s??Math.ceil(Math.log2(e.length))+1,m=Ot(n,r,Math.max(1,u)),d=Math.floor(n/m)*m,k=Math.max(1,Math.ceil((r-d)/m-1e-9)),T=Array.from({length:k},(A,I)=>({from:d+I*m,to:d+(I+1)*m,count:0}));for(const A of e){const I=Math.min(T.length-1,Math.floor((A-d)/m));T[I].count+=1}return T},wt=t=>typeof t=="number"&&Number.isFinite(t)?t:null,ct={top:12,right:16,bottom:28,minLeft:32},_e=(t,s)=>{const e=t.map(()=>0),n=t.map(()=>0),r={};for(const u of s)r[u]=t.map((m,d)=>{const k=wt(m[u])??0;if(k>=0){const A=e[d];return e[d]+=k,{base:A,top:e[d]}}const T=n[d];return n[d]+=k,{base:T,top:n[d]}});return r},we=t=>{const{rows:s,xKey:e,series:n,width:r,height:u,isStacked:m,curve:d,formatX:k}=t,T=n.some(v=>v.type==="bar"),I=(T&&n.every(v=>v.type==="bar")?t.orientation:"vertical")==="horizontal",M=s.map(v=>v[e]),F=!T&&!I&&M.length>1&&M.every(v=>wt(v)!==null),z={};if(m)for(const v of["left","right"])for(const O of["bar","area"]){const B=n.filter(Y=>(Y.axis??"left")===v&&Y.type===O).map(Y=>Y.key);Object.assign(z,_e(s,B))}const H=(v,O)=>{if(z[v])return z[v][O];const B=wt(s[O][v]);return B===null?null:{base:0,top:B}},q=v=>{const O=n.filter(lt=>(lt.axis??"left")===v);if(O.length===0)return null;let B=Number.POSITIVE_INFINITY,Y=Number.NEGATIVE_INFINITY;for(const lt of O)for(let bt=0;bt<s.length;bt+=1){const it=H(lt.key,bt);it&&(B=Math.min(B,it.top,it.base),Y=Math.max(Y,it.top,it.base))}if(O.some(lt=>lt.type!=="line")&&(B=Math.min(B,0),Y=Math.max(Y,0)),!Number.isFinite(B)||!Number.isFinite(Y))return[0,1];const[et,J]=v==="left"?t.domain??[void 0,void 0]:[void 0,void 0],K=$t(et??B,J??Y);return[et??K[0],J??K[1]]},P=q("left")??[0,1],N=q("right"),R=N!==null&&!I,E=ot(P[0],P[1],5).map(t.formatLeft),w=N?ot(N[0],N[1],5).map(t.formatRight):[],j=s.map(v=>k(v[e])),h=I?Math.min(r*.4,Math.max(ct.minLeft,...j.map(ht))+12):Math.max(ct.minLeft,...E.map(ht))+10,y=R?Math.max(ct.minLeft,...w.map(ht))+10:ct.right,a={left:h,top:ct.top,right:Math.max(h+1,r-y),bottom:Math.max(ct.top+1,u-ct.bottom),width:Math.max(1,r-y-h),height:Math.max(1,u-ct.bottom-ct.top)},i=I?[a.top,a.bottom]:[a.left,a.right],c=I?[a.left,a.right]:[a.bottom,a.top],o=mt(P,c),x=N?mt(N,c):o,_=v=>(v.axis??"left")==="right"&&N?x:o;let $,b=0,p=null;if(F){const v=M.map(O=>wt(O));p=mt(xt(v),i),$=v.map(p)}else{const v=Rt(s.map((O,B)=>String(B)),i,T?{paddingInner:.24,paddingOuter:.12}:{paddingInner:0,paddingOuter:0});b=v.bandwidth,$=s.map((O,B)=>v(String(B))+v.bandwidth/2)}const C=(v,O)=>I?{x:O,y:v}:{x:v,y:O},G=[],V=n.filter(v=>v.type==="bar"),Z=m?1:Math.max(1,V.length),D=Z>1?Math.min(2,b*.04):0,g=Math.max(1,(b-D*(Z-1))/Z);V.forEach((v,O)=>{const B=_(v),Y=m?0:O;s.forEach((et,J)=>{const K=H(v.key,J);if(!K||K.top===K.base)return;const lt=$[J]-b/2+Y*(g+D),bt=B(K.base),it=B(K.top),Wt=Math.min(bt,it),Vt=Math.max(1,Math.abs(it-bt));G.push(I?{key:v.key,index:J,tone:v.tone,x:Wt,y:lt,width:Vt,height:g}:{key:v.key,index:J,tone:v.tone,x:lt,y:Wt,width:g,height:Vt})})});const S={},L=[],X=[];for(const v of n){if(v.type==="bar")continue;const O=_(v),B=s.map((Y,et)=>{const J=H(v.key,et);return J?C($[et],O(J.top)):null});if(S[v.key]=B,v.type==="area"){const Y=s.map((et,J)=>{const K=H(v.key,J);return K?C($[J],O(K.base)):null});X.push({key:v.key,tone:v.tone,d:ye(B,Y,d)})}L.push({key:v.key,tone:v.tone,d:pe(B,d)})}const rt=s.map((v,O)=>{let B=null;for(const et of n){const J=H(et.key,O);if(!J)continue;const K=_(et)(J.top);B=B===null?K:I?Math.max(B,K):Math.min(B,K)}const Y=I?a.left:a.bottom;return C($[O],B??Y)}),jt=(v,O,B)=>ot(O[0],O[1],I?Math.max(2,Math.floor(a.width/90)):Math.max(2,Math.floor(a.height/48))).map(Y=>({position:v(Y),label:B(Y)})),he=p?ot(p.domain[0],p.domain[1],Math.max(2,Math.floor(a.width/90))).map(v=>({position:p(v),label:k(v)})):s.map((v,O)=>({position:$[O],label:I?St(j[O],h-12):j[O]}));return{zero:P[0]<0&&P[1]>0?o(0):null,area:a,centers:$,bandwidth:b,isContinuous:F,categoryTicks:he,leftTicks:jt(o,P,t.formatLeft),rightTicks:R&&N?jt(x,N,t.formatRight):[],hasRightAxis:R,lines:L,areas:X,bars:G,points:S,anchors:rt}},Zt=(...t)=>{let s="";for(const e of t)e&&(s=s===""?e:`${s} ${e}`);return s},Ut=["cat-1","cat-2","cat-3","cat-4","cat-5","series-2"],vt=(t,s)=>s??Ut[t%Ut.length],U=t=>t==="muted"?"var(--hanui-text-3)":`var(--hanui-${t})`,$e="hanui-chart-iR-5u",je="hanui-chart__canvas-4wf8o",ke="hanui-chart__svg-yC5iw",Ne="hanui-chart__overlay-kbXEv",ve="hanui-chart__tooltip-wbhLY",Ae="hanui-chart__tooltipTitle-j-1Nw",Ce="hanui-chart__tooltipRow-phors",Te="hanui-chart__tooltipLabel-72x80",Ie="hanui-chart__tooltipValue-caeby",Re="hanui-chart__swatch--mk77",Se="hanui-chart__legend-EhXcQ",Fe="hanui-chart__legendItem--AaGe",He="hanui-chart__srOnly-Oyem2",Q={chart:$e,chart__canvas:je,chart__svg:ke,chart__overlay:Ne,chart__tooltip:ve,chart__tooltipTitle:Ae,chart__tooltipRow:Ce,chart__tooltipLabel:Te,chart__tooltipValue:Ie,chart__swatch:Re,chart__legend:Se,chart__legendItem:Fe,"chart__legendItem--hidden":"hanui-chart__legendItem--hidden-D3li8",chart__srOnly:He},Qt=12,ft=({label:t,description:s,containerRef:e,width:n,height:r,plot:u,overlay:m,underlay:d,itemCount:k,columns:T,pick:A,tooltip:I,anchor:M,tooltipSide:F="inward",announce:z,table:H,legend:q,className:P,testId:N})=>{const R=f.useId(),E=dt.useAnnounce(),[w,j]=f.useState(null),h=f.useRef(null),y=f.useRef(null),a=f.useRef(null);f.useEffect(()=>{w!==null&&w>=k&&j(k>0?k-1:null)},[w,k]),f.useEffect(()=>()=>{h.current!==null&&cancelAnimationFrame(h.current)},[]);const i=f.useCallback(b=>{const p=b.currentTarget.getBoundingClientRect();a.current={x:b.clientX-p.left,y:b.clientY-p.top},h.current===null&&(h.current=requestAnimationFrame(()=>{h.current=null;const C=a.current;C&&j(A(C.x,C.y))}))},[A]),c=b=>{const p=Math.min(k-1,Math.max(0,b));j(p),E(z(p))},o=b=>{if(k===0)return;const p=w??-1,C=T??1,G=b.key==="ArrowRight"?p+1:b.key==="ArrowLeft"?Math.max(0,p-1):b.key==="ArrowDown"?p<0?0:p+C:b.key==="ArrowUp"?Math.max(0,p-C):b.key==="Home"?0:b.key==="End"?k-1:null;if(b.key==="Escape"&&w!==null){b.preventDefault(),j(null);return}G!==null&&(b.preventDefault(),c(G))},x=w===null?null:I(w),_=w===null?null:M(w),$=_==null?void 0:_.x;return f.useLayoutEffect(()=>{const b=y.current;if(!b||$===void 0)return;const p=b.offsetWidth,C=$>n/2,G=F==="outward"?!C:C,V=$-Qt-p,Z=$+Qt,D=V>=0,g=Z+p<=n,S=F==="outward"?G?V:Z:G?D||!g?V:Z:g||!D?Z:V;b.style.left=`${Math.min(Math.max(0,S),Math.max(0,n-p))}px`},[w,$,n,F]),l.jsxs("figure",{className:Zt(Q.chart,P),"data-testid":N,children:[l.jsxs("div",{ref:e,className:Q.chart__canvas,style:{blockSize:r},children:[l.jsxs("svg",{className:Q.chart__svg,width:n,height:r,viewBox:`0 0 ${n} ${r}`,role:"img","aria-label":t,"aria-describedby":s?R:void 0,tabIndex:k>0?0:void 0,onPointerMove:i,onPointerDown:i,onPointerLeave:()=>j(null),onKeyDown:o,onBlur:()=>j(null),children:[w!==null&&d&&l.jsx("g",{className:Q.chart__overlay,children:d(w)}),u,w!==null&&l.jsx("g",{className:Q.chart__overlay,children:m(w)})]}),x&&_&&l.jsxs("div",{ref:y,"aria-hidden":!0,className:Q.chart__tooltip,style:{insetBlockStart:Math.max(24,Math.min(_.y,r-24))},children:[x.title&&l.jsx("span",{className:Q.chart__tooltipTitle,children:x.title}),x.rows.map(b=>l.jsxs("span",{className:Q.chart__tooltipRow,children:[b.tone&&l.jsx("span",{className:Q.chart__swatch,style:{backgroundColor:U(b.tone)}}),l.jsx("span",{className:Q.chart__tooltipLabel,children:b.label}),l.jsx("span",{className:Q.chart__tooltipValue,children:b.value})]},b.id))]})]}),q&&q.entries.length>1&&l.jsx("ul",{className:Q.chart__legend,children:q.entries.map(b=>{const p=q.hidden.has(b.id);return l.jsx("li",{children:l.jsxs("button",{type:"button",className:Zt(Q.chart__legendItem,p&&Q["chart__legendItem--hidden"]),"aria-pressed":!p,onClick:()=>q.onToggle(b.id),children:[l.jsx(Le,{tone:b.tone,isHidden:p}),b.label]})},b.id)})}),s&&l.jsx("p",{id:R,className:Q.chart__srOnly,children:s}),l.jsx("div",{className:Q.chart__srOnly,children:l.jsxs("table",{children:[l.jsx("caption",{children:t}),l.jsx("thead",{children:l.jsx("tr",{children:H.columns.map((b,p)=>l.jsx("th",{scope:"col",children:b},p))})}),l.jsx("tbody",{children:H.rows.map((b,p)=>l.jsx("tr",{children:b.map((C,G)=>G===0?l.jsx("th",{scope:"row",children:C},G):l.jsx("td",{children:C},G))},p))})]})})]})},Le=({tone:t,isHidden:s})=>l.jsx("span",{"aria-hidden":!0,className:Q.chart__swatch,style:s?{backgroundColor:"transparent",boxShadow:`inset 0 0 0 2px ${U(t)}`}:{backgroundColor:U(t)}}),Jt=new Map,ie=(t,s)=>{const e=`${t??""}|${JSON.stringify(s)}`;let n=Jt.get(e);return n||(n=new Intl.NumberFormat(t,s),Jt.set(e,n)),n},At=t=>s=>ie(t,{notation:"compact",maximumFractionDigits:1}).format(s),pt=t=>s=>ie(t,{maximumFractionDigits:2}).format(s),ze=640,Ee=typeof window>"u"?()=>{}:f.useLayoutEffect,yt=t=>{const s=f.useRef(null),[e,n]=f.useState(t??ze);return Ee(()=>{if(t!==void 0){n(t);return}const r=s.current;if(!r)return;const u=d=>{const k=Math.floor(d);k>0&&n(T=>T===k?T:k)};if(u(r.clientWidth),typeof ResizeObserver>"u")return;const m=new ResizeObserver(([d])=>u(d.contentRect.width));return m.observe(r),()=>m.disconnect()},[t]),[s,e]},Pe="hanui-line-CLpqU",De="hanui-area-LkWXB",qe="hanui-bar-260Bd",Ge="hanui-point-Vc-kX",Oe="hanui-marker-G-xZb",Be="hanui-crosshair-nV-xX",We="hanui-band-jnG7s",Ve="hanui-zero-GpHvw",nt={line:Pe,area:De,bar:qe,point:Ge,marker:Oe,crosshair:Be,band:We,zero:Ve},Xe=280,Kt="—",Ye=({label:t,description:s,data:e,xKey:n,xLabel:r,series:u,height:m=Xe,width:d,isStacked:k=!1,orientation:T="vertical",curve:A="monotone",hasPoints:I=!1,formatX:M=String,formatValue:F,domain:z,className:H,testId:q})=>{const{labels:P}=dt.useHanui(),N=P==null?void 0:P.locale,[R,E]=yt(d),[w,j]=f.useState(()=>new Set),h=f.useMemo(()=>F??pt(N),[F,N]),y=f.useMemo(()=>At(N),[N]),a=f.useCallback(g=>g==null?"":M(g),[M]),i=f.useMemo(()=>u.map((g,S)=>({...g,tone:vt(S,g.tone)})),[u]),c=f.useMemo(()=>i.filter(g=>!w.has(g.key)),[i,w]),o=f.useMemo(()=>we({rows:e,xKey:n,series:c,width:E,height:m,isStacked:k,orientation:T,curve:A,formatX:a,formatLeft:y,formatRight:y,domain:z}),[e,n,c,E,m,k,T,A,a,y,z]),x=T==="horizontal"&&c.length>0&&c.every(g=>g.type==="bar"),_=c.some(g=>g.type==="bar"),$=f.useMemo(()=>{const g=o.centers.map((S,L)=>({center:S,index:L})).sort((S,L)=>S.center-L.center);return{centers:g.map(S=>S.center),indices:g.map(S=>S.index)}},[o.centers]),b=f.useCallback((g,S)=>{const{area:L}=o;if(g<L.left-4||g>L.right+4||S<L.top-4||S>L.bottom+4)return null;const X=be($.centers,x?S:g);return X<0?null:$.indices[X]},[o,$,x]),p=f.useCallback(g=>{var S;return{title:a((S=e[g])==null?void 0:S[n]),rows:c.map(L=>{var rt;const X=wt((rt=e[g])==null?void 0:rt[L.key]);return{id:L.key,label:L.label,tone:L.tone,value:X===null?Kt:(L.format??h)(X)}})}},[e,n,c,h,a]),C=f.useCallback(g=>{const S=p(g);return`${S.title}: ${S.rows.map(L=>`${L.label} ${L.value}`).join(", ")}`},[p]),G=f.useMemo(()=>({columns:[r??n,...i.map(g=>g.label)],rows:e.map(g=>[a(g[n]),...i.map(S=>{const L=wt(g[S.key]);return L===null?Kt:(S.format??h)(L)})])}),[e,n,r,i,h,a]),V=f.useMemo(()=>l.jsxs("g",{children:[x?l.jsxs(l.Fragment,{children:[l.jsx(ut,{area:o.area,ticks:o.leftTicks,side:"bottom",hasGrid:!0}),l.jsx(qt,{area:o.area,ticks:o.categoryTicks,side:"left"})]}):l.jsxs(l.Fragment,{children:[l.jsx(ut,{area:o.area,ticks:o.leftTicks,side:"left",hasGrid:!0}),o.hasRightAxis&&l.jsx(ut,{area:o.area,ticks:o.rightTicks,side:"right"}),l.jsx(qt,{area:o.area,ticks:o.categoryTicks,side:"bottom"})]}),o.zero!==null&&(x?l.jsx("line",{className:nt.zero,x1:o.zero,x2:o.zero,y1:o.area.top,y2:o.area.bottom}):l.jsx("line",{className:nt.zero,x1:o.area.left,x2:o.area.right,y1:o.zero,y2:o.zero})),o.areas.map(g=>l.jsx("path",{d:g.d,className:nt.area,style:{fill:`color-mix(in srgb, ${U(g.tone)} 18%, transparent)`}},`area-${g.key}`)),o.bars.map(g=>l.jsx("rect",{className:nt.bar,x:g.x,y:g.y,width:g.width,height:g.height,rx:Math.min(3,g.width/4,g.height/4),style:{fill:U(g.tone)}},`bar-${g.key}-${g.index}`)),o.lines.map(g=>l.jsx("path",{d:g.d,className:nt.line,style:{stroke:U(g.tone)}},`line-${g.key}`)),c.filter(g=>g.type!=="bar").flatMap(g=>(o.points[g.key]??[]).map((S,L,X)=>S&&(I||!X[L-1]&&!X[L+1])?l.jsx("circle",{className:nt.point,cx:S.x,cy:S.y,r:3,style:{fill:U(g.tone)}},`point-${g.key}-${L}`):null))]}),[o,I,x,c]),Z=f.useCallback(g=>{if(!_||o.bandwidth<=0)return null;const{area:S}=o,L=o.centers[g],X=o.bandwidth*.12;return x?l.jsx("rect",{className:nt.band,x:S.left,width:S.width,y:L-o.bandwidth/2-X,height:o.bandwidth+X*2}):l.jsx("rect",{className:nt.band,y:S.top,height:S.height,x:L-o.bandwidth/2-X,width:o.bandwidth+X*2})},[o,x,_]),D=f.useCallback(g=>{const{area:S}=o,L=o.centers[g];return l.jsxs(l.Fragment,{children:[!_&&l.jsx("line",{className:nt.crosshair,x1:L,x2:L,y1:S.top,y2:S.bottom}),c.filter(X=>X.type!=="bar").map(X=>{var jt;const rt=(jt=o.points[X.key])==null?void 0:jt[g];return rt?l.jsx("circle",{className:nt.marker,cx:rt.x,cy:rt.y,r:4.5,style:{fill:U(X.tone)}},X.key):null})]})},[o,c,_]);return l.jsx(ft,{label:t,description:s,containerRef:R,width:E,height:m,plot:V,overlay:D,underlay:Z,itemCount:e.length,pick:b,tooltip:p,anchor:g=>o.anchors[g]??{x:0,y:0},announce:C,table:G,legend:{entries:i.map(g=>({id:g.key,label:g.label,tone:g.tone})),hidden:w,onToggle:g=>j(S=>{const L=new Set(S);return L.has(g)?L.delete(g):i.length-S.size>1&&L.add(g),L})},className:H,testId:q})},Lt=f.memo(Ye),Ze=({series:t,...s})=>{const e=f.useMemo(()=>t.map(n=>({...n,type:"line"})),[t]);return l.jsx(Lt,{...s,series:e})},Ue=f.memo(at(Ze,"LineChart")),Qe=({series:t,...s})=>{const e=f.useMemo(()=>t.map(n=>({...n,type:"area"})),[t]);return l.jsx(Lt,{...s,series:e})},Je=f.memo(at(Qe,"AreaChart")),Ke=({series:t,...s})=>{const e=f.useMemo(()=>t.map(n=>({...n,type:"bar"})),[t]);return l.jsx(Lt,{...s,series:e})},tn=f.memo(at(Ke,"BarChart")),en=t=>l.jsx(Lt,{...t}),nn=f.memo(at(en,"ComposedChart")),sn="hanui-slice-dFWuh",on="hanui-center__value-GnqKe",an="hanui-center__label-DsgTV",kt={slice:sn,center__value:on,center__label:an},Ct=6,te=1.5,cn=({label:t,description:s,data:e,variant:n="donut",centerValue:r,centerLabel:u,valueLabel:m,height:d=260,width:k,formatValue:T,className:A,testId:I})=>{const{labels:M}=dt.useHanui(),[F,z]=yt(k),[H,q]=f.useState(()=>new Set),P=f.useMemo(()=>pt(M==null?void 0:M.locale),[M==null?void 0:M.locale]),N=T??P,R=f.useMemo(()=>e.map((a,i)=>({...a,tone:vt(i,a.tone)})),[e]),E=f.useMemo(()=>{const a=R.filter(p=>!H.has(p.id)&&Number.isFinite(p.value)&&p.value>0),i=a.reduce((p,C)=>p+C.value,0),c=z/2,o=d/2,x=Math.max(8,Math.min(z,d)/2-Ct-4),_=n==="donut"?x*.62:0;let $=0;const b=a.map(p=>{const C=i>0?p.value/i*Math.PI*2:0,G=$;return $+=C,{...p,start:G,end:$,share:i>0?p.value/i:0}});return{cx:c,cy:o,outer:x,inner:_,total:i,slices:b}},[R,H,z,d,n]),w=f.useMemo(()=>{const a=new Intl.NumberFormat(M==null?void 0:M.locale,{style:"percent",maximumFractionDigits:1});return i=>a.format(i)},[M==null?void 0:M.locale]),j=f.useMemo(()=>{const{cx:a,cy:i,outer:c,inner:o,slices:x,total:_}=E;return l.jsxs("g",{children:[x.map($=>l.jsx("path",{className:kt.slice,d:Xt(a,i,c,o,$.start,$.end),style:{fill:U($.tone),strokeWidth:x.length>1?te:0}},$.id)),n==="donut"&&l.jsxs("g",{"aria-hidden":!0,className:kt.center,children:[l.jsx("text",{className:kt.center__value,x:a,y:u?i-4:i,dy:"0.32em",textAnchor:"middle",children:r??N(_)}),u&&l.jsx("text",{className:kt.center__label,x:a,y:i+16,dy:"0.32em",textAnchor:"middle",children:u})]})]})},[E,n,r,u,N]),h=f.useCallback((a,i)=>{const{cx:c,cy:o,outer:x,inner:_,slices:$}=E,b=Math.hypot(a-c,i-o);if(b>x+Ct||b<_)return null;let p=Math.atan2(a-c,-(i-o));p<0&&(p+=Math.PI*2);const C=$.findIndex(G=>p>=G.start&&p<G.end);return C<0?null:C},[E]),y=a=>{const i=E.slices[a];return`${N(i.value)} (${w(i.share)})`};return l.jsx(ft,{label:t,description:s,containerRef:F,width:z,height:d,plot:j,overlay:a=>{const{cx:i,cy:c,outer:o,inner:x,slices:_}=E,$=_[a],b=($.start+$.end)/2,p=tt(0,0,Ct,b);return l.jsx("path",{className:kt.slice,d:Xt(i+p.x,c+p.y,o,x,$.start,$.end),style:{fill:U($.tone),strokeWidth:te}})},itemCount:E.slices.length,pick:h,tooltip:a=>{const i=E.slices[a];return{rows:[{id:i.id,label:i.label,value:y(a),tone:i.tone}]}},anchor:a=>{const{cx:i,cy:c,outer:o,slices:x}=E,_=x[a];return tt(i,c,o+Ct,(_.start+_.end)/2)},tooltipSide:"outward",announce:a=>`${E.slices[a].label}: ${y(a)}`,table:{columns:[t,m],rows:R.map(a=>[a.label,Number.isFinite(a.value)?N(a.value):"—"])},legend:{entries:R.map(a=>({id:a.id,label:a.label,tone:a.tone})),hidden:H,onToggle:a=>q(i=>{const c=new Set(i);return c.has(a)?c.delete(a):R.length-i.size>1&&c.add(a),c})},className:A,testId:I})},rn=f.memo(at(cn,"PieChart")),Bt=(t,s,e,{top:n=12,right:r=16,bottom:u=28}={})=>{const m=Math.max(32,...e.map(ht))+10;return{left:m,top:n,right:Math.max(m+1,t-r),bottom:Math.max(n+1,s-u),width:Math.max(1,t-r-m),height:Math.max(1,s-n-u)}},Nt=(t,s)=>Math.max(2,Math.floor(t/(s?90:48))),ln=(t,s=24)=>{const e=new Map,n=(r,u)=>`${r}:${u}`;return t.forEach((r,u)=>{if(!Number.isFinite(r.x)||!Number.isFinite(r.y))return;const m=n(Math.floor(r.x/s),Math.floor(r.y/s)),d=e.get(m);d?d.push(u):e.set(m,[u])}),{nearest:(r,u,m)=>{const d=Math.ceil(m/s),k=Math.floor(r/s),T=Math.floor(u/s);let A=null,I=m*m;for(let M=-d;M<=d;M+=1)for(let F=-d;F<=d;F+=1)for(const z of e.get(n(k+M,T+F))??[]){const H=(t[z].x-r)**2+(t[z].y-u)**2;H<=I&&(I=H,A=z)}return A}}},hn="hanui-point-YKyMm",un="hanui-marker-ToO1T",ee={point:hn,marker:un},gt={plain:4,min:3,max:16},mn=22,dn=({label:t,description:s,series:e,xLabel:n,yLabel:r,sizeLabel:u,height:m=300,width:d,formatX:k,formatY:T,className:A,testId:I})=>{const{labels:M}=dt.useHanui(),[F,z]=yt(d),[H,q]=f.useState(()=>new Set),P=f.useMemo(()=>pt(M==null?void 0:M.locale),[M==null?void 0:M.locale]),N=f.useMemo(()=>At(M==null?void 0:M.locale),[M==null?void 0:M.locale]),R=k??P,E=T??P,w=f.useMemo(()=>e.map((c,o)=>({...c,tone:vt(o,c.tone)})),[e]),j=f.useMemo(()=>w.filter(c=>!H.has(c.id)).flatMap(c=>c.data.filter(o=>Number.isFinite(o.x)&&Number.isFinite(o.y)).map(o=>({...o,series:c}))).sort((c,o)=>c.x-o.x||c.y-o.y),[w,H]),h=f.useMemo(()=>{const c=$t(...xt(j.map(D=>D.x))),o=$t(...xt(j.map(D=>D.y))),x=ot(o[0],o[1],Nt(m,!1)),_=Bt(z,m,x.map(N),{right:20}),$=mt(c,[_.left,_.right]),b=mt(o,[_.bottom,_.top]),p=Ht(j.map(D=>D.size)),[C,G]=xt(p),V=D=>D===void 0||!Number.isFinite(D)||p.length===0?gt.plain:G===C?(gt.min+gt.max)/2:Math.sqrt(gt.min**2+(D-C)/(G-C)*(gt.max**2-gt.min**2)),Z=j.map(D=>({...D,cx:$(D.x),cy:b(D.y),r:V(D.size)}));return{area:_,placed:Z,index:ln(Z.map(D=>({x:D.cx,y:D.cy}))),xTicks:ot(c[0],c[1],Nt(_.width,!0)).map(D=>({position:$(D),label:N(D)})),yTicks:x.map(D=>({position:b(D),label:N(D)}))}},[j,z,m,N]),y=f.useMemo(()=>l.jsxs("g",{children:[l.jsx(ut,{area:h.area,ticks:h.yTicks,side:"left",hasGrid:!0}),l.jsx(ut,{area:h.area,ticks:h.xTicks,side:"bottom",hasGrid:!0}),h.placed.map((c,o)=>l.jsx("circle",{className:ee.point,cx:c.cx,cy:c.cy,r:c.r,style:{fill:`color-mix(in srgb, ${U(c.series.tone)} 72%, transparent)`,stroke:U(c.series.tone)}},o))]}),[h]),a=f.useCallback((c,o)=>h.index.nearest(c,o,mn),[h]),i=c=>{const o=h.placed[c];return{title:o.label??o.series.label,rows:[{id:"series",label:o.series.label,value:"",tone:o.series.tone},{id:"x",label:n,value:R(o.x)},{id:"y",label:r,value:E(o.y)},...o.size!==void 0&&u?[{id:"size",label:u,value:P(o.size)}]:[]]}};return l.jsx(ft,{label:t,description:s,containerRef:F,width:z,height:m,plot:y,overlay:c=>{const o=h.placed[c];return l.jsx("circle",{className:ee.marker,cx:o.cx,cy:o.cy,r:o.r+3,style:{stroke:U(o.series.tone)}})},itemCount:h.placed.length,pick:a,tooltip:i,anchor:c=>({x:h.placed[c].cx,y:h.placed[c].cy}),announce:c=>{const o=h.placed[c];return`${o.label?`${o.label}, `:""}${o.series.label}: ${n} ${R(o.x)}, ${r} ${E(o.y)}`},table:{columns:[t,n,r],rows:j.map(c=>[c.label??c.series.label,R(c.x),E(c.y)])},legend:{entries:w.map(c=>({id:c.id,label:c.label,tone:c.tone})),hidden:H,onToggle:c=>q(o=>{const x=new Set(o);return x.has(c)?x.delete(c):w.length-o.size>1&&x.add(c),x})},className:A,testId:I})},xn=f.memo(at(dn,"ScatterPlot")),fn="hanui-labels-MJWYF",pn="hanui-empty-AW7c9",yn="hanui-focus-hLQdB",Tt={labels:fn,empty:pn,focus:yn},Et=8,bn=28,Pt=140,ne=(t,s,e)=>{var r;const n=(r=t[s])==null?void 0:r[e];return typeof n=="number"&&Number.isFinite(n)?n:null},se=t=>t.map((s,e)=>String(e)),gn=({label:t,description:s,columns:e,rows:n,values:r,valueLabel:u,tone:m="cat-1",height:d=280,width:k,formatValue:T,className:A,testId:I})=>{const{labels:M}=dt.useHanui(),[F,z]=yt(k),H=f.useMemo(()=>pt(M==null?void 0:M.locale),[M==null?void 0:M.locale]),q=T??H,P=`hanui-heat-${f.useId().replace(/:/g,"")}`,N=f.useMemo(()=>{const y={left:Math.min(z*.3,Math.max(32,...n.map(ht))+12),top:8,right:z-8,bottom:d-28-bn},a=Rt(se(e),[y.left,y.right],{paddingInner:.06,paddingOuter:0}),i=Rt(se(n),[y.top,y.bottom],{paddingInner:.06,paddingOuter:0}),[c,o]=xt(Ht(r.flat())),x=$=>$===null?null:o===c?60:Et+($-c)/(o-c)*(100-Et),_=n.flatMap(($,b)=>e.map((p,C)=>{const G=ne(r,b,C);return{row:$,column:p,key:`${b}:${C}`,value:G,x:a(String(C)),y:i(String(b)),width:a.bandwidth,height:i.bandwidth,mix:x(G)}}));return{area:y,x:a,y:i,cells:_,min:c,max:o}},[e,n,r,z,d]),R=U(m),E=f.useMemo(()=>{const{area:h,x:y,y:a,cells:i}=N,c=q(N.max),o=h.right-ht(c)-8,x=re(e.map((_,$)=>({position:y(String($))+y.bandwidth/2,label:_})));return l.jsxs("g",{children:[i.map(_=>l.jsx("rect",{className:_.mix===null?Tt.empty:void 0,x:_.x,y:_.y,width:_.width,height:_.height,rx:2,style:_.mix===null?void 0:{fill:`color-mix(in srgb, ${R} ${_.mix}%, var(--hanui-surface))`}},_.key)),l.jsxs("g",{"aria-hidden":!0,className:Tt.labels,children:[n.map((_,$)=>l.jsx("text",{x:h.left-8,y:a(String($))+a.bandwidth/2,dy:"0.32em",textAnchor:"end",children:St(_,h.left-12)},$)),x.map(_=>l.jsx("text",{x:_.position,y:h.bottom+16,textAnchor:"middle",children:_.label},_.position))]}),l.jsx("defs",{children:l.jsxs("linearGradient",{id:P,children:[l.jsx("stop",{offset:"0%",style:{stopColor:`color-mix(in srgb, ${R} ${Et}%, var(--hanui-surface))`}}),l.jsx("stop",{offset:"100%",style:{stopColor:R}})]})}),l.jsxs("g",{"aria-hidden":!0,className:Tt.labels,children:[l.jsx("rect",{x:o-Pt,y:d-18,width:Pt,height:8,rx:4,style:{fill:`url(#${P})`}}),l.jsx("text",{x:o-Pt-8,y:d-14,dy:"0.32em",textAnchor:"end",children:q(N.min)}),l.jsx("text",{x:h.right,y:d-14,dy:"0.32em",textAnchor:"end",children:c})]})]})},[N,e,n,R,P,d,q]),w=f.useCallback((h,y)=>{const{area:a,x:i,y:c}=N;if(h<a.left||h>a.right||y<a.top||y>a.bottom)return null;const o=i.indexAt(h),x=c.indexAt(y);return o<0||x<0?null:x*e.length+o},[N,e.length]),j=h=>{const y=N.cells[h];return{cell:y,value:y.value===null?"—":q(y.value)}};return l.jsx(ft,{label:t,description:s,containerRef:F,width:z,height:d,plot:E,overlay:h=>{const y=N.cells[h];return l.jsx("rect",{className:Tt.focus,x:y.x-1,y:y.y-1,width:y.width+2,height:y.height+2,rx:3})},itemCount:N.cells.length,columns:e.length,pick:w,tooltip:h=>{const{cell:y,value:a}=j(h);return{title:`${y.row} · ${y.column}`,rows:[{id:"value",label:u,value:a,tone:m}]}},anchor:h=>{const y=N.cells[h];return{x:y.x+y.width/2,y:y.y+y.height/2}},announce:h=>{const{cell:y,value:a}=j(h);return`${y.row}, ${y.column}: ${u} ${a}`},table:{columns:[t,...e],rows:n.map((h,y)=>[h,...e.map((a,i)=>{const c=ne(r,y,i);return c===null?"—":q(c)})])},className:A,testId:I})},Mn=f.memo(at(gn,"Heatmap")),_n="hanui-box-7zEQ-",wn="hanui-median-J5g6o",$n="hanui-outlier-M0VN-",jn="hanui-band-kK09D",It={box:_n,median:wn,outlier:$n,band:jn},kn=({label:t,description:s,groups:e,groupLabel:n,labels:r,height:u=280,width:m,formatValue:d,className:k,testId:T})=>{const{labels:A}=dt.useHanui(),[I,M]=yt(m),F=f.useMemo(()=>pt(A==null?void 0:A.locale),[A==null?void 0:A.locale]),z=f.useMemo(()=>At(A==null?void 0:A.locale),[A==null?void 0:A.locale]),H=d??F,q=f.useMemo(()=>e.map((w,j)=>({group:w,tone:vt(j,w.tone),stats:ge(w.values)})),[e]),P=f.useMemo(()=>{const w=q.flatMap(x=>x.stats?[x.stats.min,x.stats.max]:[]),[j,h]=w.length?xt(w):[0,1],y=$t(j,h),a=ot(y[0],y[1],Nt(u,!1)),i=Bt(M,u,a.map(z)),c=Rt(e.map(x=>x.id),[i.left,i.right],{paddingInner:.4,paddingOuter:.2}),o=mt(y,[i.bottom,i.top]);return{area:i,band:c,y:o,yTicks:a.map(x=>({position:o(x),label:z(x)})),groupTicks:e.map(x=>({position:c(x.id)+c.bandwidth/2,label:x.label}))}},[q,e,M,u,z]),N=w=>[{id:"max",label:r.max,value:H(w.whiskerHigh)},{id:"q3",label:r.q3,value:H(w.q3)},{id:"median",label:r.median,value:H(w.median)},{id:"q1",label:r.q1,value:H(w.q1)},{id:"min",label:r.min,value:H(w.whiskerLow)},...w.outliers.length>0?[{id:"outliers",label:r.outliers,value:F(w.outliers.length)}]:[],{id:"count",label:r.count,value:F(w.count)}],R=f.useMemo(()=>{const{area:w,band:j,y:h}=P;return l.jsxs("g",{children:[l.jsx(ut,{area:w,ticks:P.yTicks,side:"left",hasGrid:!0}),l.jsx(qt,{area:w,ticks:P.groupTicks,side:"bottom"}),q.map(({group:y,tone:a,stats:i})=>{if(!i)return null;const c=j(y.id),o=c+j.bandwidth/2,x=j.bandwidth*.3,_=U(a);return l.jsxs("g",{style:{stroke:_},className:It.box,children:[l.jsx("line",{x1:o,x2:o,y1:h(i.whiskerHigh),y2:h(i.q3)}),l.jsx("line",{x1:o,x2:o,y1:h(i.q1),y2:h(i.whiskerLow)}),l.jsx("line",{x1:o-x,x2:o+x,y1:h(i.whiskerHigh),y2:h(i.whiskerHigh)}),l.jsx("line",{x1:o-x,x2:o+x,y1:h(i.whiskerLow),y2:h(i.whiskerLow)}),l.jsx("rect",{x:c,width:j.bandwidth,y:h(i.q3),height:Math.max(1,h(i.q1)-h(i.q3)),rx:2,style:{fill:`color-mix(in srgb, ${_} 22%, transparent)`}}),l.jsx("line",{className:It.median,x1:c,x2:c+j.bandwidth,y1:h(i.median),y2:h(i.median)}),i.outliers.map(($,b)=>l.jsx("circle",{className:It.outlier,cx:o,cy:h($),r:3},b))]},y.id)})]})},[P,q]),E=f.useCallback((w,j)=>{const{area:h,band:y}=P;if(w<h.left||w>h.right||j<h.top-4||j>h.bottom+4)return null;const a=y.indexAt(w);return a<0?null:a},[P]);return l.jsx(ft,{label:t,description:s,containerRef:I,width:M,height:u,plot:R,underlay:w=>{const{area:j,band:h}=P,y=h.step*.2;return l.jsx("rect",{className:It.band,x:h(e[w].id)-y,width:h.bandwidth+y*2,y:j.top,height:j.height})},overlay:()=>null,itemCount:e.length,pick:E,tooltip:w=>{const j=q[w];return{title:j.group.label,rows:j.stats?N(j.stats).map(h=>({...h,tone:h.id==="median"?j.tone:void 0})):[]}},anchor:w=>{const j=q[w],{band:h,y}=P;return{x:h(j.group.id)+h.bandwidth/2,y:j.stats?y(j.stats.q3):P.area.bottom}},announce:w=>{const j=q[w];return j.stats?`${j.group.label}: ${N(j.stats).map(h=>`${h.label} ${h.value}`).join(", ")}`:j.group.label},table:{columns:[n,r.min,r.q1,r.median,r.q3,r.max,r.count],rows:q.map(({group:w,stats:j})=>j?[w.label,H(j.whiskerLow),H(j.q1),H(j.median),H(j.q3),H(j.whiskerHigh),F(j.count)]:[w.label,"—","—","—","—","—","0"])},className:k,testId:T})},Nn=f.memo(at(kn,"BoxPlot")),vn="hanui-baseline-wWs7h",An="hanui-band-xvYtL",oe={baseline:vn,band:An},Mt=1,Cn=({label:t,description:s,values:e,bins:n,valueLabel:r,countLabel:u,tone:m="cat-1",height:d=260,width:k,formatValue:T,className:A,testId:I})=>{const{labels:M}=dt.useHanui(),[F,z]=yt(k),H=f.useMemo(()=>pt(M==null?void 0:M.locale),[M==null?void 0:M.locale]),q=f.useMemo(()=>At(M==null?void 0:M.locale),[M==null?void 0:M.locale]),P=T??H,N=f.useMemo(()=>Me(e,n),[e,n]),R=f.useMemo(()=>{var $,b;const h=Math.max(1,...N.map(p=>p.count)),y=$t(0,h),a=ot(0,y[1],Nt(d,!1)).filter(Number.isInteger),i=Bt(z,d,a.map(q)),c=(($=N[0])==null?void 0:$.from)??0,o=((b=N[N.length-1])==null?void 0:b.to)??1,x=mt([c,o],[i.left,i.right]),_=mt(y,[i.bottom,i.top]);return{area:i,columns:N.map(p=>({x:x(p.from)+Mt/2,width:Math.max(1,x(p.to)-x(p.from)-Mt),y:_(p.count),height:Math.max(0,i.bottom-_(p.count)),center:(x(p.from)+x(p.to))/2})),xTicks:ot(c,o,Nt(i.width,!0)).map(p=>({position:x(p),label:q(p)})),yTicks:a.map(p=>({position:_(p),label:q(p)}))}},[N,z,d,q]),E=h=>`${P(N[h].from)} – ${P(N[h].to)}`,w=f.useMemo(()=>l.jsxs("g",{children:[l.jsx(ut,{area:R.area,ticks:R.yTicks,side:"left",hasGrid:!0}),l.jsx(ut,{area:R.area,ticks:R.xTicks,side:"bottom"}),l.jsx("line",{className:oe.baseline,x1:R.area.left,x2:R.area.right,y1:R.area.bottom,y2:R.area.bottom}),R.columns.map((h,y)=>h.height>0?l.jsx("rect",{x:h.x,y:h.y,width:h.width,height:h.height,style:{fill:U(m)}},y):null)]}),[R,m]),j=f.useCallback((h,y)=>{const{area:a,columns:i}=R;if(h<a.left||h>a.right||y<a.top-4||y>a.bottom+4)return null;const c=i.findIndex(o=>h>=o.x-Mt&&h<=o.x+o.width+Mt);return c<0?null:c},[R]);return l.jsx(ft,{label:t,description:s,containerRef:F,width:z,height:d,plot:w,underlay:h=>{const y=R.columns[h];return l.jsx("rect",{className:oe.band,x:y.x-Mt/2,width:y.width+Mt,y:R.area.top,height:R.area.height})},overlay:()=>null,itemCount:N.length,pick:j,tooltip:h=>({title:E(h),rows:[{id:"count",label:u,value:H(N[h].count),tone:m}]}),anchor:h=>({x:R.columns[h].center,y:R.columns[h].y}),announce:h=>`${r} ${E(h)}: ${u} ${H(N[h].count)}`,table:{columns:[r,u],rows:N.map((h,y)=>[E(y),H(h.count)])},className:A,testId:I})},Tn=f.memo(at(Cn,"Histogram")),In="hanui-grid-fHioN",Rn="hanui-shape-Vbb9D",Sn="hanui-labels-nRfEk",Fn="hanui-axisLabel-UOZQ7",Hn="hanui-spoke-P-6cp",Ln="hanui-marker-xzn9h",_t={grid:In,shape:Rn,labels:Sn,axisLabel:Fn,spoke:Hn,marker:Ln},ae=10,zn=16,En=28,Dt=(t,s)=>{const e=t.values[s];return typeof e=="number"&&Number.isFinite(e)?e:null},Pn=({label:t,description:s,axes:e,series:n,max:r,height:u=320,width:m,formatValue:d,className:k,testId:T})=>{const{labels:A}=dt.useHanui(),[I,M]=yt(m),[F,z]=f.useState(()=>new Set),H=f.useMemo(()=>At(A==null?void 0:A.locale),[A==null?void 0:A.locale]),q=f.useMemo(()=>pt(A==null?void 0:A.locale),[A==null?void 0:A.locale]),P=d??q,N=f.useMemo(()=>n.map((a,i)=>({...a,tone:vt(i,a.tone)})),[n]),R=f.useMemo(()=>N.filter(a=>!F.has(a.id)),[N,F]),E=f.useMemo(()=>{const a=e.length,i=a>0?Math.PI*2/a:0,c=e.map((V,Z)=>Z*i),o=Math.min(M*.22,Math.max(0,...e.map(V=>ht(V.label)))),x=Math.max(24,Math.min(u/2-24,M/2-o-ae-4)),_=M/2,$=u/2;let b=0;for(const V of n)for(const Z of e){const D=Dt(V,Z.key);D!==null&&D>b&&(b=D)}const p=r!==void 0&&r>0?r:$t(0,b,4)[1],C=ot(0,p,4).filter(V=>V>0);return{cx:_,cy:$,radius:x,angles:c,step:i,top:p,rings:C,scale:V=>Math.min(Math.max(V,0),p)/p*x,labelRoom:o}},[e,n,r,M,u]),w=f.useMemo(()=>{const{cx:a,cy:i,angles:c,scale:o}=E;return R.map(x=>({...x,points:e.map((_,$)=>{const b=Dt(x,_.key);return{...tt(a,i,o(b??0),c[$]),value:b}})}))},[E,R,e]),j=f.useMemo(()=>{const{cx:a,cy:i,radius:c,angles:o,rings:x,scale:_,labelRoom:$}=E,b=x.length>0?_(x[0]):c,p=Math.max(1,Math.ceil(zn/Math.max(1,b)));return l.jsxs("g",{children:[l.jsxs("g",{"aria-hidden":!0,className:_t.grid,children:[x.map(C=>l.jsx("path",{d:Yt(o.map(G=>tt(a,i,_(C),G)))},C)),o.map((C,G)=>{const V=tt(a,i,c,C);return l.jsx("line",{x1:a,y1:i,x2:V.x,y2:V.y},e[G].key)})]}),w.map(C=>l.jsx("path",{className:_t.shape,d:Yt(C.points),style:{fill:`color-mix(in srgb, ${U(C.tone)} 14%, transparent)`,stroke:U(C.tone)}},C.id)),l.jsxs("g",{"aria-hidden":!0,className:_t.labels,children:[x.map((C,G)=>(x.length-1-G)%p===0?l.jsx("text",{x:a+4,y:i-_(C),dy:"1.1em",children:H(C)},C):null),e.map((C,G)=>{const V=o[G],Z=tt(a,i,c+ae,V),D=Math.sin(V),g=Math.cos(V),S=D>.3?"start":D<-.3?"end":"middle",L=g>.3?"-0.2em":g<-.3?"0.9em":"0.32em";return l.jsx("text",{className:_t.axisLabel,x:Z.x,y:Z.y,dy:L,textAnchor:S,children:St(C.label,S==="middle"?c*1.4:$)},C.key)})]})]})},[E,w,e,H]),h=f.useCallback((a,i)=>{const{cx:c,cy:o,radius:x,step:_}=E;if(e.length===0||Math.hypot(a-c,i-o)>x+En)return null;let $=Math.atan2(a-c,-(i-o));return $<0&&($+=Math.PI*2),Math.round($/_)%e.length},[E,e.length]),y=a=>w.map(i=>{const c=i.points[a].value;return{id:i.id,label:i.label,value:c===null?"—":P(c),tone:i.tone}});return l.jsx(ft,{label:t,description:s,containerRef:I,width:M,height:u,plot:j,overlay:a=>{const{cx:i,cy:c,radius:o,angles:x}=E,_=tt(i,c,o,x[a]);return l.jsxs("g",{children:[l.jsx("line",{className:_t.spoke,x1:i,y1:c,x2:_.x,y2:_.y}),w.map($=>{const b=$.points[a];return b.value===null?null:l.jsx("circle",{className:_t.marker,cx:b.x,cy:b.y,r:4.5,style:{fill:U($.tone)}},$.id)})]})},itemCount:e.length,pick:h,tooltip:a=>({title:e[a].label,rows:y(a)}),anchor:a=>{const{cx:i,cy:c,radius:o,angles:x}=E;let _=0;for(const $ of w){const b=$.points[a];_=Math.max(_,Math.hypot(b.x-i,b.y-c))}return tt(i,c,Math.min(o,_),x[a])},tooltipSide:"outward",announce:a=>`${e[a].label}: ${y(a).map(i=>`${i.label} ${i.value}`).join(", ")}`,table:{columns:[t,...N.map(a=>a.label)],rows:e.map(a=>[a.label,...N.map(i=>{const c=Dt(i,a.key);return c===null?"—":P(c)})])},legend:N.length>1?{entries:N.map(a=>({id:a.id,label:a.label,tone:a.tone})),hidden:F,onToggle:a=>z(i=>{const c=new Set(i);return c.has(a)?c.delete(a):N.length-i.size>1&&c.add(a),c})}:void 0,className:k,testId:T})},Dn=f.memo(at(Pn,"RadarChart"));exports.AreaChart=Je;exports.BarChart=tn;exports.BoxPlot=Nn;exports.ComposedChart=nn;exports.Heatmap=Mn;exports.Histogram=Tn;exports.LineChart=Ue;exports.PieChart=rn;exports.RadarChart=Dn;exports.ScatterPlot=xn;
2
+ //# sourceMappingURL=charts.cjs.map