tamga-ui 0.4.3 → 0.5.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 (204) hide show
  1. package/CHANGELOG.md +908 -0
  2. package/README.md +3 -1
  3. package/dist/blocks/count-row.d.ts +3 -8
  4. package/dist/blocks/count-row.d.ts.map +1 -1
  5. package/dist/blocks/count-row.js +3 -8
  6. package/dist/blocks/filter-bar.d.ts +42 -28
  7. package/dist/blocks/filter-bar.d.ts.map +1 -1
  8. package/dist/blocks/filter-bar.js +13 -5
  9. package/dist/blocks/index.d.ts +4 -17
  10. package/dist/blocks/index.d.ts.map +1 -1
  11. package/dist/blocks/index.js +4 -17
  12. package/dist/blocks/save-bar.d.ts +4 -12
  13. package/dist/blocks/save-bar.d.ts.map +1 -1
  14. package/dist/blocks/save-bar.js +4 -12
  15. package/dist/components/a11y.d.ts.map +1 -1
  16. package/dist/components/a11y.js +4 -15
  17. package/dist/components/advanced-input.d.ts +54 -48
  18. package/dist/components/advanced-input.d.ts.map +1 -1
  19. package/dist/components/advanced-input.js +64 -59
  20. package/dist/components/announcement.d.ts +34 -0
  21. package/dist/components/announcement.d.ts.map +1 -0
  22. package/dist/components/announcement.js +12 -0
  23. package/dist/components/appearance.d.ts +33 -1
  24. package/dist/components/appearance.d.ts.map +1 -1
  25. package/dist/components/appearance.js +78 -7
  26. package/dist/components/avatar.d.ts +28 -21
  27. package/dist/components/avatar.d.ts.map +1 -1
  28. package/dist/components/avatar.js +46 -22
  29. package/dist/components/badge.d.ts +54 -4
  30. package/dist/components/badge.d.ts.map +1 -1
  31. package/dist/components/badge.js +42 -9
  32. package/dist/components/bar.d.ts.map +1 -1
  33. package/dist/components/bar.js +4 -20
  34. package/dist/components/button.d.ts +15 -7
  35. package/dist/components/button.d.ts.map +1 -1
  36. package/dist/components/button.js +54 -8
  37. package/dist/components/chart.d.ts +5 -0
  38. package/dist/components/chart.d.ts.map +1 -1
  39. package/dist/components/chart.js +18 -11
  40. package/dist/components/checkbox.d.ts +23 -1
  41. package/dist/components/checkbox.d.ts.map +1 -1
  42. package/dist/components/checkbox.js +3 -4
  43. package/dist/components/chrome.d.ts +38 -54
  44. package/dist/components/chrome.d.ts.map +1 -1
  45. package/dist/components/chrome.js +55 -63
  46. package/dist/components/combobox.d.ts.map +1 -1
  47. package/dist/components/combobox.js +19 -3
  48. package/dist/components/data-table.d.ts +4 -4
  49. package/dist/components/data-table.d.ts.map +1 -1
  50. package/dist/components/data-table.js +15 -16
  51. package/dist/components/date-picker.d.ts.map +1 -1
  52. package/dist/components/date-picker.js +11 -4
  53. package/dist/components/disclosure.d.ts +17 -2
  54. package/dist/components/disclosure.d.ts.map +1 -1
  55. package/dist/components/disclosure.js +4 -8
  56. package/dist/components/display.d.ts +119 -94
  57. package/dist/components/display.d.ts.map +1 -1
  58. package/dist/components/display.js +103 -106
  59. package/dist/components/empty-state.d.ts +70 -52
  60. package/dist/components/empty-state.d.ts.map +1 -1
  61. package/dist/components/empty-state.js +40 -52
  62. package/dist/components/error-state.d.ts +21 -4
  63. package/dist/components/error-state.d.ts.map +1 -1
  64. package/dist/components/error-state.js +16 -6
  65. package/dist/components/file-upload.d.ts +45 -2
  66. package/dist/components/file-upload.d.ts.map +1 -1
  67. package/dist/components/file-upload.js +32 -7
  68. package/dist/components/gauge.d.ts +72 -9
  69. package/dist/components/gauge.d.ts.map +1 -1
  70. package/dist/components/gauge.js +52 -49
  71. package/dist/components/icons.d.ts +1 -1
  72. package/dist/components/icons.d.ts.map +1 -1
  73. package/dist/components/icons.js +4 -0
  74. package/dist/components/input.d.ts +21 -0
  75. package/dist/components/input.d.ts.map +1 -1
  76. package/dist/components/input.js +15 -1
  77. package/dist/components/layout.d.ts +47 -70
  78. package/dist/components/layout.d.ts.map +1 -1
  79. package/dist/components/layout.js +31 -73
  80. package/dist/components/link.d.ts +5 -10
  81. package/dist/components/link.d.ts.map +1 -1
  82. package/dist/components/live-scope.d.ts.map +1 -1
  83. package/dist/components/live-scope.js +5 -8
  84. package/dist/components/log-view.d.ts +32 -1
  85. package/dist/components/log-view.d.ts.map +1 -1
  86. package/dist/components/log-view.js +42 -15
  87. package/dist/components/number-input.d.ts +20 -2
  88. package/dist/components/number-input.d.ts.map +1 -1
  89. package/dist/components/number-input.js +32 -9
  90. package/dist/components/overlay.d.ts +69 -61
  91. package/dist/components/overlay.d.ts.map +1 -1
  92. package/dist/components/overlay.js +134 -77
  93. package/dist/components/pagination.d.ts.map +1 -1
  94. package/dist/components/pagination.js +12 -7
  95. package/dist/components/pie.d.ts.map +1 -1
  96. package/dist/components/pie.js +8 -7
  97. package/dist/components/primitives.d.ts +136 -6
  98. package/dist/components/primitives.d.ts.map +1 -1
  99. package/dist/components/primitives.js +73 -20
  100. package/dist/components/progress.d.ts +44 -9
  101. package/dist/components/progress.d.ts.map +1 -1
  102. package/dist/components/progress.js +21 -9
  103. package/dist/components/radio-group.d.ts.map +1 -1
  104. package/dist/components/radio-group.js +11 -12
  105. package/dist/components/rail-link.d.ts +17 -1
  106. package/dist/components/rail-link.d.ts.map +1 -1
  107. package/dist/components/rail-link.js +3 -3
  108. package/dist/components/rich-text.d.ts +8 -1
  109. package/dist/components/rich-text.d.ts.map +1 -1
  110. package/dist/components/rich-text.js +13 -19
  111. package/dist/components/says-bubble.d.ts +38 -0
  112. package/dist/components/says-bubble.d.ts.map +1 -0
  113. package/dist/components/says-bubble.js +6 -0
  114. package/dist/components/scroll-x.d.ts +23 -0
  115. package/dist/components/scroll-x.d.ts.map +1 -0
  116. package/dist/components/scroll-x.js +27 -0
  117. package/dist/components/section.d.ts +18 -1
  118. package/dist/components/section.d.ts.map +1 -1
  119. package/dist/components/section.js +12 -1
  120. package/dist/components/segmented.d.ts +17 -1
  121. package/dist/components/segmented.d.ts.map +1 -1
  122. package/dist/components/segmented.js +26 -6
  123. package/dist/components/skeleton.d.ts.map +1 -1
  124. package/dist/components/skeleton.js +1 -1
  125. package/dist/components/slider.d.ts +16 -2
  126. package/dist/components/slider.d.ts.map +1 -1
  127. package/dist/components/slider.js +12 -5
  128. package/dist/components/sparkline.d.ts +9 -2
  129. package/dist/components/sparkline.d.ts.map +1 -1
  130. package/dist/components/sparkline.js +9 -5
  131. package/dist/components/spinner.d.ts +16 -1
  132. package/dist/components/spinner.d.ts.map +1 -1
  133. package/dist/components/spinner.js +24 -1
  134. package/dist/components/square-picker.d.ts +4 -20
  135. package/dist/components/square-picker.d.ts.map +1 -1
  136. package/dist/components/square-picker.js +4 -20
  137. package/dist/components/stacked-bar.d.ts.map +1 -1
  138. package/dist/components/stacked-bar.js +5 -4
  139. package/dist/components/surface.d.ts +32 -3
  140. package/dist/components/surface.d.ts.map +1 -1
  141. package/dist/components/surface.js +11 -4
  142. package/dist/components/switch.d.ts +14 -1
  143. package/dist/components/switch.d.ts.map +1 -1
  144. package/dist/components/switch.js +2 -3
  145. package/dist/components/tabs.d.ts +35 -11
  146. package/dist/components/tabs.d.ts.map +1 -1
  147. package/dist/components/tabs.js +48 -5
  148. package/dist/components/timeline-strip.d.ts +28 -3
  149. package/dist/components/timeline-strip.d.ts.map +1 -1
  150. package/dist/components/timeline-strip.js +19 -8
  151. package/dist/components/tone.d.ts +51 -14
  152. package/dist/components/tone.d.ts.map +1 -1
  153. package/dist/components/tone.js +76 -14
  154. package/dist/components/tree-select.d.ts.map +1 -1
  155. package/dist/components/tree-select.js +10 -3
  156. package/dist/fonts/LICENSE-jetbrains-mono.txt +93 -0
  157. package/dist/fonts/LICENSE-onest.txt +93 -0
  158. package/dist/fonts/LICENSE-red-hat-display.txt +93 -0
  159. package/dist/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 +0 -0
  160. package/dist/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  161. package/dist/fonts/onest-latin-ext-wght-normal.woff2 +0 -0
  162. package/dist/fonts/onest-latin-wght-normal.woff2 +0 -0
  163. package/dist/fonts/red-hat-display-latin-ext-wght-normal.woff2 +0 -0
  164. package/dist/fonts/red-hat-display-latin-wght-normal.woff2 +0 -0
  165. package/dist/fonts.css +54 -0
  166. package/dist/index.d.ts +10 -7
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +9 -6
  169. package/dist/kit.css +3175 -645
  170. package/dist/lib/color.d.ts +8 -19
  171. package/dist/lib/color.d.ts.map +1 -1
  172. package/dist/lib/color.js +8 -19
  173. package/dist/lib/data-props.d.ts +4 -15
  174. package/dist/lib/data-props.d.ts.map +1 -1
  175. package/dist/lib/data-props.js +4 -15
  176. package/dist/lib/image.d.ts +4 -20
  177. package/dist/lib/image.d.ts.map +1 -1
  178. package/dist/lib/image.js +4 -20
  179. package/dist/lib/palette.d.ts +16 -20
  180. package/dist/lib/palette.d.ts.map +1 -1
  181. package/dist/lib/palette.js +136 -71
  182. package/dist/lib/scroll-lock.d.ts +4 -14
  183. package/dist/lib/scroll-lock.d.ts.map +1 -1
  184. package/dist/lib/scroll-lock.js +4 -14
  185. package/dist/patterns/app-shell.d.ts.map +1 -1
  186. package/dist/patterns/app-shell.js +4 -13
  187. package/dist/patterns/appearance-template.d.ts +20 -15
  188. package/dist/patterns/appearance-template.d.ts.map +1 -1
  189. package/dist/patterns/appearance-template.js +29 -8
  190. package/dist/patterns/list-template.d.ts.map +1 -1
  191. package/dist/patterns/list-template.js +1 -1
  192. package/dist/patterns/overview-template.d.ts.map +1 -1
  193. package/dist/patterns/overview-template.js +1 -1
  194. package/dist/patterns/settings-template.d.ts +1 -12
  195. package/dist/patterns/settings-template.d.ts.map +1 -1
  196. package/dist/patterns/settings-template.js +2 -2
  197. package/dist/patterns/shared.d.ts.map +1 -1
  198. package/dist/patterns/shared.js +8 -1
  199. package/dist/styles.css +2 -1
  200. package/dist/theme.css +96 -259
  201. package/package.json +4 -2
  202. package/dist/components/control-base.d.ts +0 -7
  203. package/dist/components/control-base.d.ts.map +0 -1
  204. package/dist/components/control-base.js +0 -6
@@ -17,57 +17,36 @@ export function Surface({ raised = false, className, children, ...props }) {
17
17
  return (_jsx("div", { className: cn(raised ? "tamga-raised" : "tamga-surface", className), ...props, children: children }));
18
18
  }
19
19
  /**
20
- * Yatay taşma kabı — ve bu bileşenin asıl işi erişilebilirlik.
20
+ * Yatay taşma kabı · asıl işi erişilebilirlik. `overflow-x: auto` tek başına
21
+ * yalnız FARE için çalışıyor; `tabIndex={0}` + `role="region"` + zorunlu bir ad
22
+ * o kutuyu klavyeye açıyor.
21
23
  *
22
- * `overflow-x: auto` tek başına FARE için çalışır. Klavye kullanan biri o
23
- * kutuya hiç giremez: kaydırılabilir bir alan odaklanabilir değilse içindeki
24
- * geniş tabloyu yana kaydırmanın yolu yoktur. Tarayıcılar bunu kendiliğinden
25
- * çözmüyor.
26
- *
27
- * `tabIndex={0}` + `role="region"` + bir ad, o kutuyu klavyeye açıyor. Ad
28
- * zorunlu: adsız bir `region` ekran okuyucunun landmark listesinde
29
- * "bölge, bölge, bölge" olarak birikir ve hiçbirinin ne olduğu bilinmez.
24
+ * Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
30
25
  */
31
- export function ScrollX({ label, className, children, ...props }) {
32
- return (_jsx("div", { className: cn("tamga-scroll-x", className), role: "region", "aria-label": label, tabIndex: 0, ...props, children: children }));
33
- }
34
26
  /**
35
- * Aşağıdan giren blok.
36
- *
37
- * `.tamga-rise` bir HOVER etkisi değil, bir GİRİŞ animasyonu: sekiz piksel
38
- * aşağıdan, sönükten. Azaltılmış harekette CSS onu tamamen kapatıyor.
27
+ * Aşağıdan giren blok · GİRİŞ animasyonu, hover etkisi değil.
39
28
  *
40
- * `delay` bir listeyi sırayla açmak için. Basamak küçük tutulmalı — otuz
41
- * satırlık bir listede 60ms'lik bir gecikme, sonuncuyu iki saniye sonra
42
- * gösterir ve bekleme hissi yaratır.
29
+ * Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
43
30
  */
44
- export function Rise({ delay = 0, className, children, ...props }) {
45
- return (_jsx("div", { className: cn("tamga-rise", className), style: delay ? { animationDelay: `${delay}ms` } : undefined, ...props, children: children }));
31
+ export function Reveal({ delay = 0, className, children, ...props }) {
32
+ return (_jsx("div", { className: cn("tamga-reveal", className), style: delay ? { animationDelay: `${delay}ms` } : undefined, ...props, children: children }));
46
33
  }
47
34
  /**
48
- * İki durumun aynı yerde durması.
49
- *
50
- * Bir düğmenin yazısı "Kaydet" iken "Kaydediliyor…" olduğunda düğme genişler
51
- * ve yanındaki her şey kayar. Swap ikisini de aynı ızgara hücresine koyuyor:
52
- * kutu her zaman UZUN olanın genişliğinde, yani hiçbir şey oynamıyor.
35
+ * İki durumun aynı yerde durması · kutu her zaman UZUN olanın genişliğinde,
36
+ * yani "Kaydet" → "Kaydediliyor…" yanındaki hiçbir şeyi kaydırmıyor. Gizlenen
37
+ * taraf `aria-hidden` alıyor.
53
38
  *
54
- * Gizlenen taraf `aria-hidden` alıyor. Almasaydı ekran okuyucu iki metni
55
- * arka arkaya okurdu — "Kaydet Kaydediliyor" — ve hangisinin geçerli olduğu
56
- * anlaşılmazdı.
39
+ * Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
57
40
  */
58
41
  export function Swap({ showing, a, b, className, ...rest }) {
59
42
  return (_jsxs("span", { ...dataProps(rest), className: cn("tamga-swap", className), children: [_jsx("span", { "data-hidden": showing !== "a", "aria-hidden": showing !== "a", children: a }), _jsx("span", { "data-hidden": showing !== "b", "aria-hidden": showing !== "b", children: b })] }));
60
43
  }
61
44
  /**
62
- * Liste satırı — tablo olmayan listeler için.
45
+ * Liste satırı · tablo olmayan listeler için. `href` verilirse bağlantı,
46
+ * `onClick` verilirse düğme, ikisi de yoksa düz satır: tıklanabilir bir `<div>`
47
+ * klavyeyle erişilemiyor.
63
48
  *
64
- * Bir tabloya yetmeyen ama bir listeden fazlası olan şey: ayarlar satırı,
65
- * entegrasyon satırı, üye satırı. Sabit yükseklik ve alt kural, satırların
66
- * taranabilir kalmasını sağlıyor.
67
- *
68
- * `href` verilirse bir bağlantıya, `onClick` verilirse bir düğmeye döner —
69
- * ikisi de yoksa düz bir satır kalır. Bu ayrım önemli: tıklanabilir bir
70
- * `<div>` klavyeyle erişilemez ve ekran okuyucuya hiçbir şey söylemez.
49
+ * Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
71
50
  */
72
51
  export function ListRow({ size = "base", href, onClick, className, children, ...rest }) {
73
52
  const cls = cn("tamga-list-row", size === "sm" && "tamga-list-row-sm", className);
@@ -80,18 +59,12 @@ export function ListRow({ size = "base", href, onClick, className, children, ...
80
59
  return _jsx("div", { className: cls, children: children });
81
60
  }
82
61
  /**
83
- * İçinde bir panel AÇILAN tablo hücresi.
62
+ * İçinde bir panel AÇILAN tablo hücresi. Tek işi kırpmayı kapatmak, ve
63
+ * unutulduğunda görünmeyen bir hata üretiyor: hücredeki menü hücre sınırında
64
+ * KESİLİYOR, konsol sessiz, ve ilk akla gelen z-index oluyor. Z-index bunu
65
+ * çözmez · hiçbir yığın sırası bir `overflow` kırpmasını aşamaz.
84
66
  *
85
- * Tek işi kırpmayı kapatmak — ve o tek iş, unutulduğunda görünmeyen bir hata
86
- * üretiyor: hücrenin içindeki bir menü, ipucu ya da açılır panel hücre
87
- * sınırında KESİLİYOR. Kontrol çalışıyor, tıklanıyor, ama yarısı yok. Hata
88
- * mesajı yok, konsol sessiz, ve ilk akla gelen z-index oluyor. Z-index bunu
89
- * çözmez: hiçbir yığın sırası bir `overflow` kırpmasını aşamaz.
90
- *
91
- * `align` VARSAYILAN OLARAK SAĞDA, çünkü en sık kullanımı satır sonundaki
92
- * eylem menüsü. Ama hiza ile kırpma AYRI iki karar: sola yaslı bir hücrede
93
- * ipucu barındırmak isteyen, hizasını feda etmek zorunda kalmasın diye
94
- * `align="start"` var. (Uzun süre tek parçaydı ve tam bu yüzden yakalandı.)
67
+ * `align` varsayılan olarak sağda: en sık kullanımı satır sonundaki eylem.
95
68
  */
96
69
  export function CellActions({ className, children, align = "end", ...props
97
70
  /* `Omit`: `<td>`in kendi `align` niteliği var (HTML 4'ten kalma, artık
@@ -100,37 +73,22 @@ export function CellActions({ className, children, align = "end", ...props
100
73
  return (_jsx("td", { className: cn("tamga-cell-open", align === "end" && "tamga-cell-actions", className), ...props, children: _jsx("span", { className: cn("flex items-center gap-1", align === "end" ? "justify-end" : "justify-start"), children: children }) }));
101
74
  }
102
75
  /**
103
- * Sayfa başlığı şeridi.
104
- *
105
- * Ürün bunu kendi yazmıştı (23 satır) ve doğru yazmıştı — ama her ekranın
106
- * tepesinde duran bir şeyin her projede yeniden yazılması, on panelde on
107
- * farklı boşluk demek.
76
+ * Sayfa başlığı şeridi. Eylem SAĞA yaslı ve başlığın taban çizgisinde.
108
77
  *
109
- * Eylem SAĞA yaslı ve başlığın taban çizgisinde. Başka yere koyan bir başlık
110
- * altındaki listeden kopmuş görünüyor — bu bir tercih değil, ölçülmüş bir şey.
78
+ * Doküman: /docs/page-band
111
79
  */
112
- export function PageBand({ title, subtitle, actions, className, ...rest }) {
113
- return (_jsxs("div", { ...dataProps(rest), className: cn("tamga-section tamga-gutter flex flex-wrap items-center gap-4 py-4", className), children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "text-title font-semibold text-ink", children: title }), subtitle ? _jsx("p", { className: "text-small text-ink-faint", children: subtitle }) : null] }), actions ? _jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: actions }) : null] }));
80
+ export function PageBand({ eyebrow, title, subtitle, actions, className, ...rest }) {
81
+ return (_jsxs("div", { ...dataProps(rest), className: cn("flex flex-wrap items-end gap-4", className), children: [_jsxs("div", { className: "min-w-0 flex-1 basis-65", children: [eyebrow ? (_jsx("p", { className: "mb-1.5 text-small font-semibold text-ink-faint", children: eyebrow })) : null, _jsx("h1", { className: "font-display text-display-lg leading-tight font-extrabold text-ink", children: title }), subtitle ? _jsx("p", { className: "mt-1.5 text-body text-ink-soft", children: subtitle }) : null] }), actions ? _jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: actions }) : null] }));
114
82
  }
115
83
  /**
116
- * Gezinen bağlantı.
117
- *
118
- * `.tamga-link` sınıfı kitte yıllarca vardı, bileşeni yoktu — ve doküman
119
- * sitesi kendi sarmalayıcısını yazmak zorunda kaldı. Sınıfın olup bileşenin
120
- * olmaması, tam olarak checkbox'ın yaşadığı boşluk.
121
- *
122
- * NEDEN `Button variant="link"` DEĞİL. O bir `<button>`; bir bağlantı gibi
123
- * GÖRÜNÜR ama gezinmez. Orta tuşla yeni sekmede açılamaz, sağ tıkla
124
- * kopyalanamaz, ve ekran okuyucuya "düğme" der. Görünüm aynı, sözleşme
125
- * ters.
126
- *
127
- * `external` verilince `rel="noopener noreferrer"` geliyor. `noopener`
128
- * olmadan açılan sayfa `window.opener` üzerinden seninkini yönlendirebilir —
129
- * eski ve hâlâ geçerli bir açık.
84
+ * Gezinen bağlantı. `Button variant="link"` DEĞİL: o bir `<button>`, yani orta
85
+ * tuşla yeni sekmede açılmıyor, kopyalanmıyor, ve ekran okuyucuya "düğme"
86
+ * diyor. `external` verilince `rel="noopener noreferrer"` geliyor · `noopener`
87
+ * olmadan açılan sayfa `window.opener` ile seninkini yönlendirebilir.
130
88
  */
131
- export function Link({ external = false, className, children, href, linkComponent: Router, ...props }) {
89
+ export function Link({ external = false, look = "inline", className, children, href, linkComponent: Router, ...props }) {
132
90
  const shared = {
133
- className: cn("tamga-link", className),
91
+ className: cn("tamga-link", look === "standalone" && "tamga-link-standalone", look === "quiet" && "tamga-link-quiet", look === "marked" && "tamga-link-marked", className),
134
92
  target: external ? "_blank" : undefined,
135
93
  rel: external ? "noopener noreferrer" : undefined,
136
94
  ...props,
@@ -1,16 +1,11 @@
1
1
  import type { ComponentType, ReactNode } from "react";
2
2
  /**
3
- * Yönlendiricinin bağlantısı, dışarıdan verilir.
3
+ * Yönlendiricinin bağlantısı, dışarıdan verilir: kit hangi yönlendiricinin
4
+ * kullanıldığını bilmiyor ve bilmemeli. `components/` altında, çünkü bir
5
+ * BİLEŞEN de (`Kpi`) bağlantı olabiliyor ve şablondan import etmesi katmanı
6
+ * ters çevirirdi; `patterns/shared.tsx` tipi yeniden dışa vuruyor.
4
7
  *
5
- * NEDEN KİTİN İÇİNDE DEĞİL: kit hangi yönlendiricinin kullanıldığını
6
- * bilmiyor ve bilmemeli (`next/link`, `react-router`, ya da düz `a`).
7
- * Bileşen bir `href` alıp `a` yazsaydı her tıklama tam sayfa yüklerdi.
8
- *
9
- * NEDEN `components/` ALTINDA, `patterns/` ALTINDA DEĞİL: burada doğmuştu,
10
- * ama bir BİLEŞEN de (`Kpi`) bağlantı olabildiği için şablon katmanına
11
- * bağımlı olması gerekiyordu. Bir bileşenin şablondan import etmesi katmanı
12
- * ters çevirir; tip aşağı indi, `patterns/shared.tsx` onu yeniden dışa
13
- * vuruyor ve dışarıdan bakan API hiç değişmedi.
8
+ * Gerekçe: docs/gerekce/05-yuzey-ve-kabuk.md
14
9
  */
15
10
  export type LinkComponent = ComponentType<{
16
11
  href: string;
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/components/link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH,eAAO,MAAM,SAAS,EAAE,aAIvB,CAAC"}
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/components/link.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH,eAAO,MAAM,SAAS,EAAE,aAIvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"live-scope.d.ts","sourceRoot":"","sources":["../../src/components/live-scope.tsx"],"names":[],"mappings":"AA0BA,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAuCpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAe7D"}
1
+ {"version":3,"file":"live-scope.d.ts","sourceRoot":"","sources":["../../src/components/live-scope.tsx"],"names":[],"mappings":"AA0BA,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAoCpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAe7D"}
@@ -19,14 +19,11 @@ export function LiveScope({ children }) {
19
19
  const winner = useMemo(() => {
20
20
  let best = null;
21
21
  let bestSeverity = -Infinity;
22
- /* Object key order is insertion order, and claims are registered in effect
23
- order — which is tree order. So a strict `>` keeps the FIRST of equals,
24
- and "first" means highest on the page. That is question 2's answer
25
- (SETTLED 2026-08-19, Ercüment: first of equals wins), and it lives here
26
- rather than in a doc nobody reads. The reasoning: the presence of the
27
- signal matters more than which row carries it, and every alternative
28
- (timestamp, domain priority) buys a nicer tie-break by making call sites
29
- thread data they do not otherwise need. */
22
+ /* Insertion order is tree order, so a strict `>` keeps the FIRST of equals, and
23
+ "first" means highest on the page. (SETTLED 2026-08-19, Ercüment.) Every
24
+ alternative buys a nicer tie-break by making call sites thread data they do
25
+ not otherwise need.
26
+ Gerekçe: docs/gerekce/06-isaret-ve-ton.md */
30
27
  for (const [id, severity] of Object.entries(claims)) {
31
28
  if (severity > bestSeverity) {
32
29
  best = id;
@@ -11,16 +11,47 @@ export type LogLine = {
11
11
  * bilmez.
12
12
  */
13
13
  time?: string;
14
+ /**
15
+ * The level badge's word: "INFO", "ERROR", "HATA". The kit draws the badge, the product
16
+ * writes it. TR: Seviye etiketinin sözcüğü: "INFO", "ERROR", "HATA". Rozeti kit çizer,
17
+ * sözcüğü ürün yazar.
18
+ */
19
+ level?: string;
14
20
  text: string;
15
21
  tone?: Tone;
16
22
  };
17
- export declare function LogView({ lines, label, height, follow, wrap, className, ...rest }: {
23
+ export declare function LogView({ lines, label, labels, filters, height, follow, wrap, className, ...rest }: {
18
24
  lines: readonly LogLine[];
19
25
  /**
20
26
  * The region's name; it shows in the landmark list for keyboard navigation. TR: Bölgenin adı;
21
27
  * klavyeyle gezinende landmark listesinde görünür.
22
28
  */
23
29
  label: string;
30
+ /**
31
+ * `newLines` is the button that appears when lines arrive while you are reading further up:
32
+ * "↓ 12 new lines". TR: `newLines`, yukarıda okurken satır geldiğinde beliren düğme: "↓ 12
33
+ * yeni satır".
34
+ */
35
+ labels: {
36
+ newLines: (n: number) => string;
37
+ };
38
+ /**
39
+ * The level filter bar over the stream. The kit draws the bar and marks the chosen one; WHICH
40
+ * levels exist and what they are called is the product's vocabulary, and the filtering itself
41
+ * happens on the caller's side · a log view that filters its own lines would have to know what
42
+ * a level means. TR: Akışın üstündeki seviye filtresi. Çubuğu kit çiziyor ve seçili olanı
43
+ * işaretliyor; hangi seviyelerin olduğu ve adları ürünün sözlüğü, ve süzme çağıranın tarafında
44
+ * kalıyor · kendi satırlarını süzen bir günlük, bir seviyenin ne demek olduğunu bilmek
45
+ * zorunda kalırdı.
46
+ */
47
+ filters?: {
48
+ options: readonly ({
49
+ value: string;
50
+ label: string;
51
+ } & Record<string, unknown>)[];
52
+ value: string;
53
+ onChange: (value: string) => void;
54
+ };
24
55
  height?: number;
25
56
  /** Scroll to the bottom when a new line arrives. TR: Yeni satır geldiğinde en alta kay. */
26
57
  follow?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"log-view.d.ts","sourceRoot":"","sources":["../../src/components/log-view.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAU,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AAEH,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,IAAI,CAAC;CACb,CAAC;AAEF,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,KAAK,EACL,MAAY,EACZ,MAAa,EACb,IAAY,EACZ,SAAS,EACT,GAAG,IAAI,EACR,EAAE;IACD,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,CAAC,CAAC,EAAE,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC;CAChC,+BAoDA"}
1
+ {"version":3,"file":"log-view.d.ts","sourceRoot":"","sources":["../../src/components/log-view.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAU,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;GAIG;AAEH,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,IAAI,CAAC;CACb,CAAC;AAEF,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,KAAK,EACL,MAAM,EACN,OAAO,EACP,MAAY,EACZ,MAAa,EACb,IAAY,EACZ,SAAS,EACT,GAAG,IAAI,EACR,EAAE;IACD,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,EAAE;QAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;IAC5C;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,SAAS,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;QACjF,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;KACnC,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,CAAC,CAAC,EAAE,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC;CAChC,+BAmHA"}
@@ -1,31 +1,58 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useRef } from "react";
3
+ import { createElement as _createElement } from "react";
4
+ import { useEffect, useRef, useState } from "react";
4
5
  import { cn } from "../lib/cn.js";
5
6
  import { dataProps } from "../lib/data-props.js";
6
7
  import { toneOf } from "./tone.js";
7
- export function LogView({ lines, label, height = 320, follow = true, wrap = false, className, ...rest }) {
8
+ export function LogView({ lines, label, labels, filters, height = 320, follow = true, wrap = false, className, ...rest }) {
8
9
  const box = useRef(null);
9
- useEffect(() => {
10
- if (!follow)
10
+ /* Okunmamış satırlar SAYILIYOR, sadece "yeni var mı" değil: sayı, aşağı
11
+ inmeye değer mi kararını veren şey. Son görülen uzunluk bir ref'te
12
+ tutuluyor çünkü render'ı tetiklemesi gereken şey sayı, uzunluk değil. */
13
+ const gorulen = useRef(lines.length);
14
+ const [yeni, setYeni] = useState(0);
15
+ function altta(el) {
16
+ /* 32px tolerans: tam altta olmayı beklemek, bir piksellik kaydırmada
17
+ takibi kapatıyor. */
18
+ return el.scrollHeight - el.scrollTop - el.clientHeight < 32;
19
+ }
20
+ function dibeGit() {
21
+ const el = box.current;
22
+ if (!el)
11
23
  return;
24
+ el.scrollTop = el.scrollHeight;
25
+ gorulen.current = lines.length;
26
+ setYeni(0);
27
+ }
28
+ useEffect(() => {
12
29
  const el = box.current;
13
30
  if (!el)
14
31
  return;
32
+ const eklenen = Math.max(0, lines.length - gorulen.current);
15
33
  /* SADECE ZATEN ALTTAYSA kaydır. Yukarıda bir şey okuyan kişiyi aşağı
16
34
  fırlatmak, bu bileşenin yapabileceği en can sıkıcı şey — ve "hep en
17
35
  alta git" davranışının varsayılan olduğu her log görüntüleyicide
18
- yaşanır. 32px'lik tolerans, tam altta olmayı beklemeden çalışsın diye. */
19
- const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 32;
20
- if (atBottom)
36
+ yaşanır. Fırlatmamanın bedeli, gelen satırın görünmemesi: düğme tam
37
+ onun için var. */
38
+ if (follow && altta(el)) {
21
39
  el.scrollTop = el.scrollHeight;
40
+ setYeni(0);
41
+ }
42
+ else if (eklenen > 0) {
43
+ setYeni((n) => n + eklenen);
44
+ }
45
+ gorulen.current = lines.length;
22
46
  }, [lines, follow]);
23
- return (_jsx("div", { ...dataProps(rest), ref: box, className: cn("tamga-surface overflow-y-auto p-3 font-mono text-caption", className), style: { height },
24
- /* `log` rolü: ekran okuyucu bunu bir günlük olarak tanır ve — kritik
25
- olarak — `aria-live` OLMADAN sessiz kalır. Akan bir bölgeyi
26
- duyurmak sesli okuyucuyu boğar; kullanıcı isterse kendisi okur. */
27
- role: "log", "aria-label": label, tabIndex: 0, children: lines.map((l) => {
28
- const c = l.tone ? toneOf(l.tone) : null;
29
- return (_jsxs("div", { className: cn("flex gap-3 py-0.5", wrap ? "break-words" : "whitespace-nowrap"), children: [l.time ? (_jsx("span", { className: "w-20 shrink-0 text-ink-faint tabular-nums", children: l.time })) : null, _jsx("span", { className: "min-w-0", style: c ? { color: c.mark } : undefined, children: l.text })] }, l.id));
30
- }) }));
47
+ return (_jsxs("div", { ...dataProps(rest), className: cn("tamga-log", className), style: { height }, children: [filters ? (_jsx("div", { className: "tamga-log-filtre", children: filters.options.map(({ value: v, label: etiket, ...kanca }) => (_createElement("button", { ...kanca, key: v, type: "button", className: `tamga-btn tamga-btn-sm ${v === filters.value ? "tamga-btn-primary" : ""}`, "aria-pressed": v === filters.value, onClick: () => filters.onChange(v) }, etiket))) })) : null, _jsx("div", { ref: box, className: "tamga-log-akis", "data-filtreli": filters ? true : undefined,
48
+ /* `log` rolü: ekran okuyucu bunu bir günlük olarak tanır ve — kritik
49
+ olarak — `aria-live` OLMADAN sessiz kalır. Akan bir bölgeyi
50
+ duyurmak sesli okuyucuyu boğar; kullanıcı isterse kendisi okur. */
51
+ role: "log", "aria-label": label, tabIndex: 0, onScroll: (e) => {
52
+ if (yeni > 0 && altta(e.currentTarget))
53
+ setYeni(0);
54
+ }, children: lines.map((l) => {
55
+ const c = l.tone ? toneOf(l.tone) : null;
56
+ return (_jsxs("div", { className: "tamga-log-satir", "data-wrap": wrap || undefined, children: [l.time ? (_jsx("span", { className: "tamga-log-saat tabular-nums", children: l.time })) : null, l.level ? (_jsx("span", { className: "tamga-log-yuva", children: _jsx("span", { className: "tamga-log-seviye", style: c ? { background: c.bg, color: c.fg } : undefined, children: l.level }) })) : null, _jsx("span", { className: "tamga-log-metin", style: c && !l.level ? { color: c.mark } : undefined, children: l.text })] }, l.id));
57
+ }) }), yeni > 0 ? (_jsxs("button", { type: "button", className: "tamga-log-yeni", onClick: dibeGit, children: ["\u2193 ", labels.newLines(yeni)] })) : null] }));
31
58
  }
@@ -3,13 +3,23 @@
3
3
  *
4
4
  * Gerekçe: docs/gerekce/01-form-ve-girdi.md
5
5
  */
6
- export declare function NumberInput({ value, onChange, min, max, step,
6
+ export declare function NumberInput({ value, onChange, look, min, max, step,
7
7
  /** Değerin sonuna yapışan birim: "₺", "kg", "%" */
8
8
  suffix, disabled, invalid, full,
9
9
  /** Artır/azalt düğmelerinin erişilebilir adları. Kit çeviri çekmez. */
10
- labels, className, ...rest }: {
10
+ labels, locale, className, ...rest }: {
11
11
  value: number | null;
12
12
  onChange?: (next: number | null) => void;
13
+ /**
14
+ * `field` a text field with the stepper on its right edge · for a threshold, a rate, a price:
15
+ * numbers that are TYPED and only nudged. `quantity` the joined control with the buttons
16
+ * flanking the value · for a count that is CLICKED into place ("3 adet"), where the number is
17
+ * small and the two buttons are the whole interaction. TR: `field` sağ kenarında sayaç olan
18
+ * metin alanı · eşik, oran, fiyat için: YAZILAN ve yalnız dürtülen sayılar. `quantity`
19
+ * düğmeleri değerin iki yanında duran birleşik kontrol · TIKLANARAK ayarlanan adet için
20
+ * ("3 adet"), sayı küçükken ve etkileşimin tamamı o iki düğmeyken.
21
+ */
22
+ look?: "field" | "quantity";
13
23
  min?: number;
14
24
  max?: number;
15
25
  step?: number;
@@ -21,6 +31,14 @@ labels, className, ...rest }: {
21
31
  increase: string;
22
32
  decrease: string;
23
33
  };
34
+ /**
35
+ * BCP-47 tag for how the number is SHOWN: "tr-TR" prints 249,9 and "en-US" prints 249.9. Not
36
+ * given, the number is shown as typed. This is formatting, not translation: the kit does not
37
+ * pick the tag, the caller does. TR: Sayının NASIL GÖSTERİLECEĞİ: "tr-TR" 249,9 yazar,
38
+ * "en-US" 249.9. Verilmezse sayı yazıldığı gibi görünür. Bu çeviri değil biçimleme: etiketi
39
+ * kit seçmiyor, çağıran veriyor.
40
+ */
41
+ locale?: string;
24
42
  className?: string;
25
43
  /** `data-*` hooks pass through. TR: `data-*` kancaları geçiyor. */
26
44
  [k: `data-${string}`]: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/components/number-input.tsx"],"names":[],"mappings":"AAQA;;;;GAIG;AAEH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,GAAG,EACH,GAAG,EACH,IAAQ;AACR,mDAAmD;AACnD,MAAM,EACN,QAAgB,EAChB,OAAe,EACf,IAAY;AACZ,uEAAuE;AACvE,MAAM,EACN,SAAS,EACT,GAAG,IAAI,EACR,EAAE;IACD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,CAAC,CAAC,EAAE,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC;CAChC,+BAoFA"}
1
+ {"version":3,"file":"number-input.d.ts","sourceRoot":"","sources":["../../src/components/number-input.tsx"],"names":[],"mappings":"AAQA;;;;GAIG;AAEH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,IAAc,EACd,GAAG,EACH,GAAG,EACH,IAAQ;AACR,mDAAmD;AACnD,MAAM,EACN,QAAgB,EAChB,OAAe,EACf,IAAY;AACZ,uEAAuE;AACvE,MAAM,EACN,MAAM,EACN,SAAS,EACT,GAAG,IAAI,EACR,EAAE;IACD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,CAAC,CAAC,EAAE,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAC;CAChC,+BAuIA"}
@@ -1,21 +1,31 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
3
+ import { useId, useState } from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { dataProps } from "../lib/data-props.js";
6
6
  import { Icon } from "./icon.js";
7
- import { CaretDown } from "./icons.js";
7
+ import { CaretDown, Minus, Plus } from "./icons.js";
8
8
  /**
9
9
  * NumberInput — fiyat, stok, ağırlık.
10
10
  *
11
11
  * Gerekçe: docs/gerekce/01-form-ve-girdi.md
12
12
  */
13
- export function NumberInput({ value, onChange, min, max, step = 1,
13
+ export function NumberInput({ value, onChange, look = "field", min, max, step = 1,
14
14
  /** Değerin sonuna yapışan birim: "₺", "kg", "%" */
15
15
  suffix, disabled = false, invalid = false, full = false,
16
16
  /** Artır/azalt düğmelerinin erişilebilir adları. Kit çeviri çekmez. */
17
- labels, className, ...rest }) {
17
+ labels, locale, className, ...rest }) {
18
18
  const id = useId();
19
+ /* YAZARKEN BİÇİMLENMİYOR, BIRAKINCA BİÇİMLENİYOR.
20
+ Her tuşta biçimlemek "249," yazan birinin virgülünü siliyor, yani ondalık
21
+ yazılamıyor. Taslak yalnız odaktayken yaşıyor; odak çıkınca değerin kendisi
22
+ yerel biçimde görünüyor. */
23
+ const [taslak, setTaslak] = useState(null);
24
+ const goster = (n) => {
25
+ if (n === null)
26
+ return "";
27
+ return locale ? n.toLocaleString(locale) : String(n);
28
+ };
19
29
  const clamp = (n) => {
20
30
  if (min !== undefined && n < min)
21
31
  return min;
@@ -34,14 +44,27 @@ labels, className, ...rest }) {
34
44
  };
35
45
  const atMin = min !== undefined && value !== null && value <= min;
36
46
  const atMax = max !== undefined && value !== null && value >= max;
37
- return (_jsxs("span", { ...dataProps(rest), className: cn("relative inline-flex items-center", full && "w-full", className), children: [_jsx("input", { id: id, type: "text", inputMode: "decimal", value: value ?? "", disabled: disabled, "aria-invalid": invalid || undefined, onChange: (e) => {
38
- const raw = e.target.value.replace(",", ".").trim();
47
+ if (look === "quantity") {
48
+ /* DEĞER BİR GİRDİ DEĞİL BİR METİN: adet kontrolünde sayı tıklanarak
49
+ ayarlanıyor, ve yazılabilir bir alan burada klavyeyle girilen geçersiz
50
+ değerin kapısını açıyor. Yazmak gerekiyorsa `field` var. */
51
+ return (_jsxs("span", { ...dataProps(rest), className: cn("tamga-qty", full && "w-full", className), "data-disabled": disabled || undefined, children: [_jsx("button", { type: "button", "aria-label": labels.decrease, disabled: disabled || atMin, onClick: () => bump(-1), children: _jsx(Icon, { icon: Minus, size: "sm", weight: "bold" }) }), _jsxs("span", { className: "tamga-qty-deger tabular-nums", "aria-live": "polite", children: [goster(value), suffix ? _jsx("span", { className: "tamga-qty-birim", children: suffix }) : null] }), _jsx("button", { type: "button", "aria-label": labels.increase, disabled: disabled || atMax, onClick: () => bump(1), children: _jsx(Icon, { icon: Plus, size: "sm", weight: "bold" }) })] }));
52
+ }
53
+ return (_jsxs("span", { ...dataProps(rest), className: cn("relative inline-flex items-center", full && "w-full", className), children: [_jsx("input", { id: id, type: "text", inputMode: "decimal", value: taslak ?? goster(value), disabled: disabled, "aria-invalid": invalid || undefined, onChange: (e) => {
54
+ setTaslak(e.target.value);
55
+ /* Binlik ayracı temizleniyor, ondalık ayracı noktaya çevriliyor:
56
+ kullanıcı "1.234,5" de yazabilir "1234.5" de. */
57
+ const raw = e.target.value.replace(/\s/g, "").replace(/\.(?=\d{3}\b)/g, "").replace(",", ".");
39
58
  if (raw === "")
40
59
  return onChange?.(null);
41
60
  const n = Number(raw);
42
61
  if (!Number.isNaN(n))
43
62
  onChange?.(n);
44
- }, onBlur: () => value !== null && onChange?.(clamp(value)), onKeyDown: (e) => {
63
+ }, onBlur: () => {
64
+ setTaslak(null);
65
+ if (value !== null)
66
+ onChange?.(clamp(value));
67
+ }, onKeyDown: (e) => {
45
68
  if (e.key === "ArrowUp") {
46
69
  e.preventDefault();
47
70
  bump(1);
@@ -51,8 +74,8 @@ labels, className, ...rest }) {
51
74
  bump(-1);
52
75
  }
53
76
  },
54
- /* Sağ boşluk spinner'ın genişliği (22px) artı kenar payı; birim varsa
77
+ /* Sağ boşluk spinner'ın genişliği (38px) artı kenar payı; birim varsa
55
78
  onun yeri de eklenir. Elle sayı yazmak yerine hesaplanıyor, çünkü
56
79
  birim "₺" da olabilir "adet" de. */
57
- className: cn("tamga-input w-full tabular-nums", invalid && "tamga-input-invalid"), style: { paddingRight: suffix ? 76 : 32 } }), suffix ? (_jsx("span", { className: "pointer-events-none absolute right-8 font-mono text-small text-ink-faint", children: suffix })) : null, _jsxs("span", { className: "tamga-stepper", children: [_jsx("button", { type: "button", "aria-label": labels.increase, "aria-controls": id, disabled: disabled || atMax, onClick: () => bump(1), children: _jsx(Icon, { icon: CaretDown, size: "xs", className: "rotate-180" }) }), _jsx("button", { type: "button", "aria-label": labels.decrease, "aria-controls": id, disabled: disabled || atMin, onClick: () => bump(-1), children: _jsx(Icon, { icon: CaretDown, size: "xs" }) })] })] }));
80
+ className: cn("tamga-input tamga-sayi w-full", invalid && "tamga-input-invalid"), style: { paddingRight: suffix ? 96 : 48 } }), suffix ? (_jsx("span", { className: "tamga-stepper-birim", children: suffix })) : null, _jsxs("span", { className: "tamga-stepper", children: [_jsx("button", { type: "button", "aria-label": labels.increase, "aria-controls": id, disabled: disabled || atMax, onClick: () => bump(1), children: _jsx(Icon, { icon: CaretDown, size: "xs", weight: "bold", className: "rotate-180" }) }), _jsx("button", { type: "button", "aria-label": labels.decrease, "aria-controls": id, disabled: disabled || atMin, onClick: () => bump(-1), children: _jsx(Icon, { icon: CaretDown, size: "xs", weight: "bold" }) })] })] }));
58
81
  }