tamga-ui 0.1.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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/blocks/count-row.d.ts +29 -0
  4. package/dist/blocks/count-row.d.ts.map +1 -0
  5. package/dist/blocks/count-row.js +19 -0
  6. package/dist/blocks/filter-bar.d.ts +123 -0
  7. package/dist/blocks/filter-bar.d.ts.map +1 -0
  8. package/dist/blocks/filter-bar.js +74 -0
  9. package/dist/blocks/index.d.ts +24 -0
  10. package/dist/blocks/index.d.ts.map +1 -0
  11. package/dist/blocks/index.js +23 -0
  12. package/dist/blocks/save-bar.d.ts +31 -0
  13. package/dist/blocks/save-bar.d.ts.map +1 -0
  14. package/dist/blocks/save-bar.js +21 -0
  15. package/dist/components/a11y.d.ts +21 -0
  16. package/dist/components/a11y.d.ts.map +1 -0
  17. package/dist/components/a11y.js +159 -0
  18. package/dist/components/advanced-input.d.ts +115 -0
  19. package/dist/components/advanced-input.d.ts.map +1 -0
  20. package/dist/components/advanced-input.js +189 -0
  21. package/dist/components/avatar.d.ts +63 -0
  22. package/dist/components/avatar.d.ts.map +1 -0
  23. package/dist/components/avatar.js +54 -0
  24. package/dist/components/badge.d.ts +37 -0
  25. package/dist/components/badge.d.ts.map +1 -0
  26. package/dist/components/badge.js +29 -0
  27. package/dist/components/bar.d.ts +32 -0
  28. package/dist/components/bar.d.ts.map +1 -0
  29. package/dist/components/bar.js +33 -0
  30. package/dist/components/button.d.ts +45 -0
  31. package/dist/components/button.d.ts.map +1 -0
  32. package/dist/components/button.js +56 -0
  33. package/dist/components/chart.d.ts +43 -0
  34. package/dist/components/chart.d.ts.map +1 -0
  35. package/dist/components/chart.js +41 -0
  36. package/dist/components/checkbox.d.ts +13 -0
  37. package/dist/components/checkbox.d.ts.map +1 -0
  38. package/dist/components/checkbox.js +16 -0
  39. package/dist/components/chrome.d.ts +87 -0
  40. package/dist/components/chrome.d.ts.map +1 -0
  41. package/dist/components/chrome.js +107 -0
  42. package/dist/components/combobox.d.ts +31 -0
  43. package/dist/components/combobox.d.ts.map +1 -0
  44. package/dist/components/combobox.js +62 -0
  45. package/dist/components/control-base.d.ts +7 -0
  46. package/dist/components/control-base.d.ts.map +1 -0
  47. package/dist/components/control-base.js +6 -0
  48. package/dist/components/data-table.d.ts +68 -0
  49. package/dist/components/data-table.d.ts.map +1 -0
  50. package/dist/components/data-table.js +50 -0
  51. package/dist/components/date-picker.d.ts +52 -0
  52. package/dist/components/date-picker.d.ts.map +1 -0
  53. package/dist/components/date-picker.js +73 -0
  54. package/dist/components/disclosure.d.ts +31 -0
  55. package/dist/components/disclosure.d.ts.map +1 -0
  56. package/dist/components/disclosure.js +32 -0
  57. package/dist/components/display.d.ts +188 -0
  58. package/dist/components/display.d.ts.map +1 -0
  59. package/dist/components/display.js +190 -0
  60. package/dist/components/empty-state.d.ts +150 -0
  61. package/dist/components/empty-state.d.ts.map +1 -0
  62. package/dist/components/empty-state.js +113 -0
  63. package/dist/components/error-state.d.ts +38 -0
  64. package/dist/components/error-state.d.ts.map +1 -0
  65. package/dist/components/error-state.js +16 -0
  66. package/dist/components/file-upload.d.ts +34 -0
  67. package/dist/components/file-upload.d.ts.map +1 -0
  68. package/dist/components/file-upload.js +34 -0
  69. package/dist/components/gauge.d.ts +47 -0
  70. package/dist/components/gauge.d.ts.map +1 -0
  71. package/dist/components/gauge.js +65 -0
  72. package/dist/components/icon.d.ts +32 -0
  73. package/dist/components/icon.d.ts.map +1 -0
  74. package/dist/components/icon.js +20 -0
  75. package/dist/components/icons.d.ts +19 -0
  76. package/dist/components/icons.d.ts.map +1 -0
  77. package/dist/components/icons.js +39 -0
  78. package/dist/components/input.d.ts +35 -0
  79. package/dist/components/input.d.ts.map +1 -0
  80. package/dist/components/input.js +41 -0
  81. package/dist/components/layout.d.ts +133 -0
  82. package/dist/components/layout.d.ts.map +1 -0
  83. package/dist/components/layout.js +132 -0
  84. package/dist/components/link.d.ts +22 -0
  85. package/dist/components/link.d.ts.map +1 -0
  86. package/dist/components/link.js +2 -0
  87. package/dist/components/live-scope.d.ts +15 -0
  88. package/dist/components/live-scope.d.ts.map +1 -0
  89. package/dist/components/live-scope.js +67 -0
  90. package/dist/components/log-view.d.ts +34 -0
  91. package/dist/components/log-view.d.ts.map +1 -0
  92. package/dist/components/log-view.js +30 -0
  93. package/dist/components/number-input.d.ts +26 -0
  94. package/dist/components/number-input.d.ts.map +1 -0
  95. package/dist/components/number-input.js +57 -0
  96. package/dist/components/overlay.d.ts +257 -0
  97. package/dist/components/overlay.d.ts.map +1 -0
  98. package/dist/components/overlay.js +276 -0
  99. package/dist/components/pagination.d.ts +38 -0
  100. package/dist/components/pagination.d.ts.map +1 -0
  101. package/dist/components/pagination.js +46 -0
  102. package/dist/components/pie.d.ts +40 -0
  103. package/dist/components/pie.d.ts.map +1 -0
  104. package/dist/components/pie.js +74 -0
  105. package/dist/components/primitives.d.ts +96 -0
  106. package/dist/components/primitives.d.ts.map +1 -0
  107. package/dist/components/primitives.js +81 -0
  108. package/dist/components/progress.d.ts +29 -0
  109. package/dist/components/progress.d.ts.map +1 -0
  110. package/dist/components/progress.js +16 -0
  111. package/dist/components/radio-group.d.ts +17 -0
  112. package/dist/components/radio-group.d.ts.map +1 -0
  113. package/dist/components/radio-group.js +23 -0
  114. package/dist/components/rail-link.d.ts +24 -0
  115. package/dist/components/rail-link.d.ts.map +1 -0
  116. package/dist/components/rail-link.js +24 -0
  117. package/dist/components/rich-text.d.ts +51 -0
  118. package/dist/components/rich-text.d.ts.map +1 -0
  119. package/dist/components/rich-text.js +147 -0
  120. package/dist/components/section.d.ts +15 -0
  121. package/dist/components/section.d.ts.map +1 -0
  122. package/dist/components/section.js +8 -0
  123. package/dist/components/segmented.d.ts +13 -0
  124. package/dist/components/segmented.d.ts.map +1 -0
  125. package/dist/components/segmented.js +12 -0
  126. package/dist/components/skeleton.d.ts +69 -0
  127. package/dist/components/skeleton.d.ts.map +1 -0
  128. package/dist/components/skeleton.js +67 -0
  129. package/dist/components/slider.d.ts +18 -0
  130. package/dist/components/slider.d.ts.map +1 -0
  131. package/dist/components/slider.js +15 -0
  132. package/dist/components/sparkline.d.ts +17 -0
  133. package/dist/components/sparkline.d.ts.map +1 -0
  134. package/dist/components/sparkline.js +19 -0
  135. package/dist/components/spinner.d.ts +15 -0
  136. package/dist/components/spinner.d.ts.map +1 -0
  137. package/dist/components/spinner.js +22 -0
  138. package/dist/components/stacked-bar.d.ts +21 -0
  139. package/dist/components/stacked-bar.d.ts.map +1 -0
  140. package/dist/components/stacked-bar.js +41 -0
  141. package/dist/components/surface.d.ts +76 -0
  142. package/dist/components/surface.d.ts.map +1 -0
  143. package/dist/components/surface.js +49 -0
  144. package/dist/components/switch.d.ts +12 -0
  145. package/dist/components/switch.d.ts.map +1 -0
  146. package/dist/components/switch.js +13 -0
  147. package/dist/components/tabs.d.ts +47 -0
  148. package/dist/components/tabs.d.ts.map +1 -0
  149. package/dist/components/tabs.js +12 -0
  150. package/dist/components/timeline-strip.d.ts +19 -0
  151. package/dist/components/timeline-strip.d.ts.map +1 -0
  152. package/dist/components/timeline-strip.js +14 -0
  153. package/dist/components/tone.d.ts +58 -0
  154. package/dist/components/tone.d.ts.map +1 -0
  155. package/dist/components/tone.js +70 -0
  156. package/dist/components/tree-select.d.ts +26 -0
  157. package/dist/components/tree-select.d.ts.map +1 -0
  158. package/dist/components/tree-select.js +59 -0
  159. package/dist/index.d.ts +54 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +70 -0
  162. package/dist/kit.css +2248 -0
  163. package/dist/lib/cn.d.ts +10 -0
  164. package/dist/lib/cn.d.ts.map +1 -0
  165. package/dist/lib/cn.js +12 -0
  166. package/dist/lib/color.d.ts +68 -0
  167. package/dist/lib/color.d.ts.map +1 -0
  168. package/dist/lib/color.js +134 -0
  169. package/dist/lib/palette.d.ts +84 -0
  170. package/dist/lib/palette.d.ts.map +1 -0
  171. package/dist/lib/palette.js +193 -0
  172. package/dist/lib/scroll-lock.d.ts +19 -0
  173. package/dist/lib/scroll-lock.d.ts.map +1 -0
  174. package/dist/lib/scroll-lock.js +33 -0
  175. package/dist/patterns/app-shell.d.ts +36 -0
  176. package/dist/patterns/app-shell.d.ts.map +1 -0
  177. package/dist/patterns/app-shell.js +14 -0
  178. package/dist/patterns/auth-template.d.ts +36 -0
  179. package/dist/patterns/auth-template.d.ts.map +1 -0
  180. package/dist/patterns/auth-template.js +40 -0
  181. package/dist/patterns/detail-template.d.ts +52 -0
  182. package/dist/patterns/detail-template.d.ts.map +1 -0
  183. package/dist/patterns/detail-template.js +11 -0
  184. package/dist/patterns/index.d.ts +18 -0
  185. package/dist/patterns/index.d.ts.map +1 -0
  186. package/dist/patterns/index.js +17 -0
  187. package/dist/patterns/list-template.d.ts +32 -0
  188. package/dist/patterns/list-template.d.ts.map +1 -0
  189. package/dist/patterns/list-template.js +8 -0
  190. package/dist/patterns/overview-template.d.ts +26 -0
  191. package/dist/patterns/overview-template.d.ts.map +1 -0
  192. package/dist/patterns/overview-template.js +8 -0
  193. package/dist/patterns/public-template.d.ts +28 -0
  194. package/dist/patterns/public-template.d.ts.map +1 -0
  195. package/dist/patterns/public-template.js +7 -0
  196. package/dist/patterns/settings-template.d.ts +50 -0
  197. package/dist/patterns/settings-template.d.ts.map +1 -0
  198. package/dist/patterns/settings-template.js +33 -0
  199. package/dist/patterns/shared.d.ts +40 -0
  200. package/dist/patterns/shared.d.ts.map +1 -0
  201. package/dist/patterns/shared.js +29 -0
  202. package/dist/patterns/wizard-template.d.ts +28 -0
  203. package/dist/patterns/wizard-template.d.ts.map +1 -0
  204. package/dist/patterns/wizard-template.js +15 -0
  205. package/dist/styles.css +5 -0
  206. package/dist/theme.css +812 -0
  207. package/package.json +87 -0
@@ -0,0 +1,159 @@
1
+ "use client";
2
+ import { useEffect, useRef } from "react";
3
+ /**
4
+ * The keyboard behaviour Radix would have given us for free.
5
+ *
6
+ * We hand-rolled the overlays, so we owe the keyboard the same contract a
7
+ * library would have honoured. Two hooks, used by every overlay, so the
8
+ * behaviour cannot drift between them.
9
+ */
10
+ const FOCUSABLE = [
11
+ "a[href]",
12
+ "button:not([disabled])",
13
+ "input:not([disabled])",
14
+ "select:not([disabled])",
15
+ "textarea:not([disabled])",
16
+ '[tabindex]:not([tabindex="-1"])',
17
+ ].join(",");
18
+ /**
19
+ * Visibility is judged by `hidden` and `aria-hidden`, deliberately not by
20
+ * `offsetParent`. offsetParent is null for anything inside a fixed-position
21
+ * ancestor in some engines — and always null under jsdom — so a trap built on
22
+ * it collapses to an empty list and silently stops trapping. The selector
23
+ * already excludes disabled controls and tabindex="-1".
24
+ */
25
+ function focusable(root) {
26
+ return Array.from(root.querySelectorAll(FOCUSABLE)).filter((el) => !el.hasAttribute("hidden") && el.getAttribute("aria-hidden") !== "true");
27
+ }
28
+ /**
29
+ * Traps Tab inside a modal surface and gives it back on close.
30
+ *
31
+ * Without this a dialog looks modal and is not: Tab walks straight out into
32
+ * the page behind the scrim, where a keyboard user is stuck operating a page
33
+ * they cannot see. The scrim only stops the mouse.
34
+ */
35
+ export function useFocusTrap(open) {
36
+ const ref = useRef(null);
37
+ const restoreTo = useRef(null);
38
+ useEffect(() => {
39
+ if (!open)
40
+ return;
41
+ const root = ref.current;
42
+ if (!root)
43
+ return;
44
+ restoreTo.current = document.activeElement;
45
+ /* İLK KONTROL, ya da AÇIKÇA İSTENEN kontrol.
46
+ *
47
+ * Varsayılan olarak ilk odaklanabilir eleman odağı alıyor ve çoğu panelde
48
+ * doğrusu bu. Ama bir onay diyaloğunda ilk kontrol kapatma çarpısı, ikinci
49
+ * sıradaki ise "Sil": Enter'a basan biri kaydı siliyor. Yıkıcı bir
50
+ * diyalogda güvenli olan varsayılan olmalı.
51
+ *
52
+ * `autoFocus` NİTELİĞİ BURADA ÇALIŞMIYOR: React onu bağlarken bu etki
53
+ * henüz koşmamış oluyor, sonra trap odağı ilk elemana çekiyor ve niteliğin
54
+ * etkisi siliniyor. O yüzden mekanizma DOM üzerinden: panelin içinde
55
+ * `data-autofocus` taşıyan bir eleman varsa odak onun.
56
+ *
57
+ * `focusable` listesinden seçiliyor ki gizli ya da devre dışı bir elemana
58
+ * odak verilmesin: kapalı bir düğmeye odaklanmak, odağı hiç vermemekle
59
+ * aynı şey ama sessizce. */
60
+ const adaylar = focusable(root);
61
+ const istenen = adaylar.find((el) => el.hasAttribute("data-autofocus"));
62
+ const first = istenen ?? adaylar[0];
63
+ if (first)
64
+ first.focus();
65
+ else {
66
+ root.setAttribute("tabindex", "-1");
67
+ root.focus();
68
+ }
69
+ const onKey = (e) => {
70
+ if (e.key !== "Tab")
71
+ return;
72
+ const items = focusable(root);
73
+ if (items.length === 0) {
74
+ e.preventDefault();
75
+ return;
76
+ }
77
+ const first = items[0];
78
+ const last = items[items.length - 1];
79
+ const active = document.activeElement;
80
+ /* v5 compiles with noUncheckedIndexedAccess, which the prototype did not. The guards below
81
+ are not ceremony: `focusable()` genuinely returns [] when a dialog opens with everything
82
+ inside it still disabled, and the old code would have thrown on the first Tab. */
83
+ if (!first || !last)
84
+ return;
85
+ /* wrap at both ends, and pull focus back if it escaped some other way */
86
+ if (e.shiftKey && (active === first || !root.contains(active))) {
87
+ e.preventDefault();
88
+ last.focus();
89
+ }
90
+ else if (!e.shiftKey && (active === last || !root.contains(active))) {
91
+ e.preventDefault();
92
+ first.focus();
93
+ }
94
+ };
95
+ document.addEventListener("keydown", onKey);
96
+ return () => {
97
+ document.removeEventListener("keydown", onKey);
98
+ /* give focus back to whatever opened this, not to the top of the page */
99
+ restoreTo.current?.focus?.();
100
+ };
101
+ }, [open]);
102
+ return ref;
103
+ }
104
+ /**
105
+ * Roving focus for a list of options — menus and selects.
106
+ *
107
+ * Arrow keys move, Home/End jump, Enter and Space choose, Escape closes.
108
+ * `data-tamga-option` marks the items so separators and labels are skipped
109
+ * without the hook needing to know what a separator is.
110
+ */
111
+ export function useListKeys({ open, containerRef, onClose, }) {
112
+ useEffect(() => {
113
+ if (!open)
114
+ return;
115
+ const root = containerRef.current;
116
+ if (!root)
117
+ return;
118
+ const items = () => Array.from(root.querySelectorAll("[data-tamga-option]:not([disabled])"));
119
+ /* focus the first option on open, so the arrows have somewhere to start */
120
+ const initial = items();
121
+ initial[0]?.focus();
122
+ const onKey = (e) => {
123
+ const list = items();
124
+ if (list.length === 0)
125
+ return;
126
+ const i = list.indexOf(document.activeElement);
127
+ switch (e.key) {
128
+ case "ArrowDown":
129
+ e.preventDefault();
130
+ list[i < 0 || i === list.length - 1 ? 0 : i + 1]?.focus();
131
+ break;
132
+ case "ArrowUp":
133
+ e.preventDefault();
134
+ list[i <= 0 ? list.length - 1 : i - 1]?.focus();
135
+ break;
136
+ case "Home":
137
+ e.preventDefault();
138
+ list[0]?.focus();
139
+ break;
140
+ case "End":
141
+ e.preventDefault();
142
+ list[list.length - 1]?.focus();
143
+ break;
144
+ case "Escape":
145
+ e.preventDefault();
146
+ onClose();
147
+ break;
148
+ case " ":
149
+ /* Enter fires a button natively; Space on a button does too, but only
150
+ on keyup — pre-empting it here keeps the two keys identical. */
151
+ e.preventDefault();
152
+ document.activeElement?.click?.();
153
+ break;
154
+ }
155
+ };
156
+ root.addEventListener("keydown", onKey);
157
+ return () => root.removeEventListener("keydown", onKey);
158
+ }, [open, containerRef, onClose]);
159
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Parola alanı — göster/gizle düğmesiyle.
3
+ *
4
+ * Gerekçe: docs/gerekce/01-form-ve-girdi.md
5
+ */
6
+ export declare function PasswordInput({ labels, className, ...props }: Omit<React.ComponentProps<"input">, "type"> & {
7
+ labels: {
8
+ show: string;
9
+ hide: string;
10
+ };
11
+ }): import("react").JSX.Element;
12
+ /**
13
+ * Maskeli sır — API anahtarı, webhook secret.
14
+ *
15
+ * `PasswordInput`'un kardeşi ama İŞİ TERS: parola alanına bir şey YAZILIR,
16
+ * buraya yazılmaz — okunur ve kopyalanır. O yüzden salt-okunur, ve asıl
17
+ * düğmesi kopyalama.
18
+ *
19
+ * Maske ilk ve son birkaç karakteri bırakıyor. Tamamen gizlemek, kullanıcının
20
+ * "hangi anahtar bu" sorusunu cevapsız bırakır; üç anahtarı olan biri
21
+ * hangisine baktığını bilemez.
22
+ */
23
+ export declare function SecretField({ value, labels, visibleChars, className, }: {
24
+ value: string;
25
+ labels: {
26
+ reveal: string;
27
+ hide: string;
28
+ copy: string;
29
+ copied: string;
30
+ failed: string;
31
+ };
32
+ /**
33
+ * How many characters stay visible at each end. TR: Baştan ve sondan açıkta kalan karakter
34
+ * sayısı.
35
+ */
36
+ visibleChars?: number;
37
+ className?: string;
38
+ }): import("react").JSX.Element;
39
+ /**
40
+ * Etiket girdisi.
41
+ *
42
+ * Enter ya da virgül bir etiketi kapatıyor; boşken Backspace sonuncuyu siliyor.
43
+ * İkincisi küçük görünür ama en çok kullanılan yoldur — yanlış yazılan bir
44
+ * etiketi silmek için fareye uzanmak, akışı kesen tek şeydir.
45
+ *
46
+ * Yinelenen ETİKET SESSİZCE YUTULUYOR, hata verilmiyor: aynı etiketi iki kez
47
+ * yazmak bir hata değil, bir tekrardır, ve kullanıcı zaten istediğini almış
48
+ * olur.
49
+ */
50
+ export declare function TagsInput({ value, onChange, placeholder, labels, max, className, }: {
51
+ value: readonly string[];
52
+ onChange: (next: string[]) => void;
53
+ placeholder: string;
54
+ labels: {
55
+ remove: (tag: string) => string;
56
+ };
57
+ /** The ceiling; the input closes once it is full. TR: Üst sınır; dolduğunda girdi kapanır. */
58
+ max?: number;
59
+ className?: string;
60
+ }): import("react").JSX.Element;
61
+ /**
62
+ * Çoklu seçim — aranabilir.
63
+ *
64
+ * `Combobox` tek seçim yapar; bu birden çok. Ayrı bileşen olmasının sebebi
65
+ * görünüm değil DAVRANIŞ: tek seçimde liste seçince kapanır, çoklu seçimde
66
+ * KAPANMAZ — üç şey seçecek biri listeyi üç kez açmak zorunda kalmamalı.
67
+ *
68
+ * Seçilenler girdinin İÇİNDE çip olarak duruyor, altında ayrı bir listede
69
+ * değil: seçim ile seçilenler arasındaki mesafe arttıkça, kullanıcı neyi
70
+ * seçtiğini görmek için gözünü iki yere birden koymak zorunda kalır.
71
+ */
72
+ export declare function MultiSelect({ options, value, onChange, placeholder, labels, className, }: {
73
+ options: readonly {
74
+ value: string;
75
+ label: string;
76
+ hint?: string;
77
+ }[];
78
+ value: readonly string[];
79
+ onChange: (next: string[]) => void;
80
+ placeholder: string;
81
+ labels: {
82
+ empty: string;
83
+ remove: (label: string) => string;
84
+ open: string;
85
+ };
86
+ className?: string;
87
+ }): import("react").JSX.Element;
88
+ /**
89
+ * Zamanlama girdisi — "her N dakikada bir".
90
+ *
91
+ * NEDEN CRON DEĞİL. Bir cron ifadesi (yıldız-eğik-beş biçimi) bir geliştirici
92
+ * için okunur, bir panel
93
+ * kullanıcısı için değildir — ve yanlış yazılan bir cron ifadesi hata
94
+ * vermez, sadece yanlış zamanda çalışır. Bu bileşen kürasyonlu bir liste
95
+ * sunuyor: kürasyonlu seçenek, serbestlik değil.
96
+ *
97
+ * Aralıklar dakika olarak veriliyor, çevirisi çağıranın: "5 dakika" ile
98
+ * "5 minutes" arasındaki farkı kit bilemez.
99
+ */
100
+ export declare function ScheduleInput({ value, onChange, options, label, className, }: {
101
+ /** The interval, in minutes. TR: Dakika cinsinden aralık. */
102
+ value: number;
103
+ onChange: (minutes: number) => void;
104
+ /**
105
+ * `[{ minutes: 5, label: "5 minutes" }, …]`: ordered and curated. TR: `[{ minutes: 5, label:
106
+ * "5 dakika" }, …]`: sıralı ve kürasyonlu.
107
+ */
108
+ options: readonly {
109
+ minutes: number;
110
+ label: string;
111
+ }[];
112
+ label: string;
113
+ className?: string;
114
+ }): import("react").JSX.Element;
115
+ //# sourceMappingURL=advanced-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advanced-input.d.ts","sourceRoot":"","sources":["../../src/components/advanced-input.tsx"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAC/C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC,+BAwBA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,EACN,YAAgB,EAChB,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BA4DA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,GAAG,EACH,SAAS,GACV,EAAE;IACD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;KAAE,CAAC;IAC5C,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAgDA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,SAAS,GACV,EAAE;IACD,OAAO,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAuJA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAK,EACL,SAAS,GACV,EAAE;IACD,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;OAGG;IACH,OAAO,EAAE,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAeA"}
@@ -0,0 +1,189 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { Icon } from "./icon.js";
6
+ import { Check, Close, Customize, Eye, EyeSlash, Search } from "./icons.js";
7
+ /* ------------------------------------------------------------------ *
8
+ * Gelişmiş girdiler.
9
+ *
10
+ * Hepsinin ortak yanı: bir `<input>` ile yapılabilir GÖRÜNMESİ, ve
11
+ * yapılamaması. Her biri tek bir somut hatanın karşılığı.
12
+ * ------------------------------------------------------------------ */
13
+ /**
14
+ * Parola alanı — göster/gizle düğmesiyle.
15
+ *
16
+ * Gerekçe: docs/gerekce/01-form-ve-girdi.md
17
+ */
18
+ export function PasswordInput({ labels, className, ...props }) {
19
+ const [shown, setShown] = useState(false);
20
+ return (_jsxs("span", { className: "relative flex items-center", children: [_jsx("input", { ...props, type: shown ? "text" : "password", className: cn("tamga-input w-full pr-10", className) }), _jsx("button", { type: "button", className: "tamga-mini-btn absolute right-1.5", "aria-label": shown ? labels.hide : labels.show, "aria-pressed": shown, onClick: () => setShown((v) => !v), children: _jsx(Icon, { icon: shown ? EyeSlash : Eye, size: "xs" }) })] }));
21
+ }
22
+ /**
23
+ * Maskeli sır — API anahtarı, webhook secret.
24
+ *
25
+ * `PasswordInput`'un kardeşi ama İŞİ TERS: parola alanına bir şey YAZILIR,
26
+ * buraya yazılmaz — okunur ve kopyalanır. O yüzden salt-okunur, ve asıl
27
+ * düğmesi kopyalama.
28
+ *
29
+ * Maske ilk ve son birkaç karakteri bırakıyor. Tamamen gizlemek, kullanıcının
30
+ * "hangi anahtar bu" sorusunu cevapsız bırakır; üç anahtarı olan biri
31
+ * hangisine baktığını bilemez.
32
+ */
33
+ export function SecretField({ value, labels, visibleChars = 4, className, }) {
34
+ const [shown, setShown] = useState(false);
35
+ const [copied, setCopied] = useState("idle");
36
+ const timer = useRef(undefined);
37
+ /* Zamanlayıcı bir ref'te tutuluyor ve her tıklamada sıfırlanıyor.
38
+
39
+ Öncesi çıplak bir `setTimeout`'tu ve iki şeyi birden bozuyordu: art arda
40
+ iki tıklamada ilk zamanlayıcı hâlâ çalıştığı için etiket erken geri
41
+ dönüyordu, ve bileşen 1.6 saniye içinde sökülürse zamanlayıcı ölü bir
42
+ bileşene yazmaya çalışıyordu. */
43
+ useEffect(() => () => window.clearTimeout(timer.current), []);
44
+ const masked = value.length <= visibleChars * 2
45
+ ? "•".repeat(value.length)
46
+ : `${value.slice(0, visibleChars)}${"•".repeat(8)}${value.slice(-visibleChars)}`;
47
+ async function copy() {
48
+ try {
49
+ await navigator.clipboard.writeText(value);
50
+ setCopied("done");
51
+ }
52
+ catch {
53
+ setCopied("failed");
54
+ }
55
+ window.clearTimeout(timer.current);
56
+ timer.current = window.setTimeout(() => setCopied("idle"), 1600);
57
+ }
58
+ return (_jsxs("span", { className: cn("relative flex items-center", className), children: [_jsx("input", { readOnly: true, value: shown ? value : masked, className: "tamga-input w-full pr-18 font-mono",
59
+ /* Salt-okunur bir alan yine de odaklanabilir olmalı: kullanıcı onu
60
+ seçip elle kopyalayabilsin. `disabled` bunu imkânsız kılardı. */
61
+ onFocus: (e) => e.currentTarget.select() }), _jsxs("span", { className: "absolute right-1.5 flex gap-0.5", children: [_jsx("button", { type: "button", className: "tamga-mini-btn", "aria-label": shown ? labels.hide : labels.reveal, "aria-pressed": shown, onClick: () => setShown((v) => !v), children: _jsx(Icon, { icon: shown ? EyeSlash : Eye, size: "xs" }) }), _jsx("button", { type: "button", className: "tamga-mini-btn", "aria-label": copied === "done" ? labels.copied : copied === "failed" ? labels.failed : labels.copy, onClick: copy, children: _jsx(Icon, { icon: copied === "done" ? Check : Customize, size: "xs" }) })] })] }));
62
+ }
63
+ /**
64
+ * Etiket girdisi.
65
+ *
66
+ * Enter ya da virgül bir etiketi kapatıyor; boşken Backspace sonuncuyu siliyor.
67
+ * İkincisi küçük görünür ama en çok kullanılan yoldur — yanlış yazılan bir
68
+ * etiketi silmek için fareye uzanmak, akışı kesen tek şeydir.
69
+ *
70
+ * Yinelenen ETİKET SESSİZCE YUTULUYOR, hata verilmiyor: aynı etiketi iki kez
71
+ * yazmak bir hata değil, bir tekrardır, ve kullanıcı zaten istediğini almış
72
+ * olur.
73
+ */
74
+ export function TagsInput({ value, onChange, placeholder, labels, max, className, }) {
75
+ const [draft, setDraft] = useState("");
76
+ const full = max !== undefined && value.length >= max;
77
+ function commit() {
78
+ const tag = draft.trim();
79
+ setDraft("");
80
+ if (!tag || value.includes(tag) || full)
81
+ return;
82
+ onChange([...value, tag]);
83
+ }
84
+ return (_jsxs("span", { className: cn("tamga-input h-auto min-h-10 w-full flex-wrap items-center gap-1.5 py-1.5", className), style: { display: "flex" }, children: [value.map((tag) => (_jsxs("span", { className: "tamga-chip gap-1", children: [tag, _jsx("button", { type: "button", "aria-label": labels.remove(tag), onClick: () => onChange(value.filter((x) => x !== tag)), className: "-mr-1 inline-flex items-center", children: _jsx(Icon, { icon: Close, size: "xs" }) })] }, tag))), !full ? (_jsx("input", { value: draft, placeholder: value.length === 0 ? placeholder : undefined, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
85
+ if (e.key === "Enter" || e.key === ",") {
86
+ e.preventDefault();
87
+ commit();
88
+ }
89
+ else if (e.key === "Backspace" && draft === "" && value.length > 0) {
90
+ onChange(value.slice(0, -1));
91
+ }
92
+ }, className: "min-w-24 flex-1 bg-transparent text-body text-ink outline-none" })) : null] }));
93
+ }
94
+ /**
95
+ * Çoklu seçim — aranabilir.
96
+ *
97
+ * `Combobox` tek seçim yapar; bu birden çok. Ayrı bileşen olmasının sebebi
98
+ * görünüm değil DAVRANIŞ: tek seçimde liste seçince kapanır, çoklu seçimde
99
+ * KAPANMAZ — üç şey seçecek biri listeyi üç kez açmak zorunda kalmamalı.
100
+ *
101
+ * Seçilenler girdinin İÇİNDE çip olarak duruyor, altında ayrı bir listede
102
+ * değil: seçim ile seçilenler arasındaki mesafe arttıkça, kullanıcı neyi
103
+ * seçtiğini görmek için gözünü iki yere birden koymak zorunda kalır.
104
+ */
105
+ export function MultiSelect({ options, value, onChange, placeholder, labels, className, }) {
106
+ const id = useId();
107
+ const [open, setOpen] = useState(false);
108
+ const [query, setQuery] = useState("");
109
+ const box = useRef(null);
110
+ const [active, setActive] = useState(0);
111
+ const shown = options.filter((o) => !query || o.label.toLocaleLowerCase().includes(query.toLocaleLowerCase()));
112
+ const chosen = options.filter((o) => value.includes(o.value));
113
+ function toggle(v) {
114
+ onChange(value.includes(v) ? value.filter((x) => x !== v) : [...value, v]);
115
+ }
116
+ /**
117
+ * KLAVYE, ve onsuz bu kontrol yarım.
118
+ *
119
+ * Etiket eklemenin doğal yolu YAZIP ENTER'A BASMAK: kullanıcı "adidas" yazıp
120
+ * Enter, "nike" yazıp Enter diyor ve iki çip ekliyor. Önce yalnız fareyle
121
+ * çalışıyordu; yazdıktan sonra listeye uzanıp tıklamak gerekiyordu, ve
122
+ * klavyeyle gezen biri seçim yapamıyordu.
123
+ *
124
+ * Enter vurgulanan seçeneği ekler/çıkarır, ve kutuyu boşaltır
125
+ * Ok tuşları vurguyu gezdirir
126
+ * Backspace kutu BOŞKEN son çipi kaldırır (yazarken silmeye karışmaz)
127
+ * Escape listeyi kapatır
128
+ */
129
+ function tus(e) {
130
+ if (e.key === "ArrowDown") {
131
+ e.preventDefault();
132
+ setOpen(true);
133
+ setActive((i) => Math.min(i + 1, shown.length - 1));
134
+ return;
135
+ }
136
+ if (e.key === "ArrowUp") {
137
+ e.preventDefault();
138
+ setActive((i) => Math.max(i - 1, 0));
139
+ return;
140
+ }
141
+ if (e.key === "Enter") {
142
+ const o = shown[active] ?? shown[0];
143
+ if (!o)
144
+ return;
145
+ e.preventDefault();
146
+ toggle(o.value);
147
+ setQuery("");
148
+ setActive(0);
149
+ return;
150
+ }
151
+ if (e.key === "Backspace" && query === "" && value.length > 0) {
152
+ /* Yazarken Backspace harf siler; yalnız kutu boşken çipe dokunuyor. */
153
+ e.preventDefault();
154
+ onChange(value.slice(0, -1));
155
+ return;
156
+ }
157
+ if (e.key === "Escape")
158
+ setOpen(false);
159
+ }
160
+ return (_jsxs("div", { ref: box, className: cn("relative", className),
161
+ /* Odak kutunun DIŞINA çıkınca kapanıyor — tıklama dinleyicisiyle değil.
162
+ Dışarı tıklamayı dinlemek klavyeyle çıkanı görmez ve liste açık kalır. */
163
+ onBlur: (e) => {
164
+ if (!box.current?.contains(e.relatedTarget))
165
+ setOpen(false);
166
+ }, children: [_jsxs("span", { className: "tamga-input relative h-auto min-h-10 w-full flex-wrap items-center gap-1.5 py-1.5", "data-leading": "true", style: { display: "flex" }, children: [_jsx(Icon, { icon: Search, size: "xs", "aria-hidden": true, className: "pointer-events-none absolute left-3 top-3 text-ink-faint" }), chosen.map((o) => (_jsxs("span", { className: "tamga-chip gap-1", children: [o.label, _jsx("button", { type: "button", "aria-label": labels.remove(o.label), onClick: () => toggle(o.value), className: "-mr-1 inline-flex items-center", children: _jsx(Icon, { icon: Close, size: "xs" }) })] }, o.value))), _jsx("input", { value: query, placeholder: chosen.length === 0 ? placeholder : undefined, onChange: (e) => {
167
+ setQuery(e.target.value);
168
+ setActive(0);
169
+ setOpen(true);
170
+ }, onKeyDown: tus, onFocus: () => setOpen(true), role: "combobox", "aria-expanded": open, "aria-controls": id, "aria-label": labels.open, className: "min-w-24 flex-1 bg-transparent text-body text-ink outline-none" })] }), open ? (_jsx("div", { id: id, role: "listbox", "aria-multiselectable": true, className: "tamga-overlay absolute z-30 mt-2 max-h-64 w-full overflow-y-auto py-1", children: shown.length === 0 ? (_jsx("p", { className: "px-4 py-3 text-small text-ink-faint", children: labels.empty })) : (shown.map((o, i) => {
171
+ const on = value.includes(o.value);
172
+ return (_jsxs("button", { type: "button", role: "option", "aria-selected": on, "data-selected": on, "data-active": i === active || undefined, className: "tamga-option w-full", onMouseEnter: () => setActive(i), onClick: () => toggle(o.value), children: [_jsx("span", { className: "flex-1 text-left", children: o.label }), o.hint ? _jsx("span", { className: "font-mono text-caption text-ink-faint", children: o.hint }) : null, on ? _jsx(Icon, { icon: Check, size: "xs" }) : null] }, o.value));
173
+ })) })) : null] }));
174
+ }
175
+ /**
176
+ * Zamanlama girdisi — "her N dakikada bir".
177
+ *
178
+ * NEDEN CRON DEĞİL. Bir cron ifadesi (yıldız-eğik-beş biçimi) bir geliştirici
179
+ * için okunur, bir panel
180
+ * kullanıcısı için değildir — ve yanlış yazılan bir cron ifadesi hata
181
+ * vermez, sadece yanlış zamanda çalışır. Bu bileşen kürasyonlu bir liste
182
+ * sunuyor: kürasyonlu seçenek, serbestlik değil.
183
+ *
184
+ * Aralıklar dakika olarak veriliyor, çevirisi çağıranın: "5 dakika" ile
185
+ * "5 minutes" arasındaki farkı kit bilemez.
186
+ */
187
+ export function ScheduleInput({ value, onChange, options, label, className, }) {
188
+ return (_jsx("select", { className: cn("tamga-input w-full", className), "aria-label": label, value: value, onChange: (e) => onChange(Number(e.target.value)), children: options.map((o) => (_jsx("option", { value: o.minutes, children: o.label }, o.minutes))) }));
189
+ }
@@ -0,0 +1,63 @@
1
+ /** Square, monochrome. Identity is text — color is reserved for health. */
2
+ export declare function Avatar({ name, size, src, bare, letters, }: {
3
+ name: string;
4
+ size?: number;
5
+ /**
6
+ * falls back to initials when absent or broken TR: yoksa ya da yüklenemezse baş harflere
7
+ * düşer
8
+ */
9
+ src?: string;
10
+ /**
11
+ * Drop the avatar's own frame. For when it sits INSIDE a control that already has one: a
12
+ * bordered tile inside a bordered button is a box in a box, and it made the account control
13
+ * the odd one out on a toolbar where every other button holds a bare glyph. TR: Avatarın
14
+ * kendi çerçevesini kaldır. Zaten çerçevesi olan bir kontrolün İÇİNDE duruyorsa: çerçeveli
15
+ * bir düğmenin içindeki çerçeveli bir karo kutu içinde kutudur, ve hesap kontrolünü her
16
+ * düğmesi çıplak bir simge taşıyan bir araç çubuğunda tek başına farklı gösteriyordu.
17
+ */
18
+ bare?: boolean;
19
+ /**
20
+ * How many initials to show. An avatar standing alone carries two, the least it takes to
21
+ * point at a person. But two do not fit in an OVERLAPPING stack: the tile above clips the
22
+ * right of the one below and "BS" reads as "B" plus half an "S". A photo does not have this
23
+ * problem, because half a face is still a face; half a letter cannot be read. `AvatarStack`
24
+ * passes 1 for exactly that reason. TR: Kaç baş harf gösterileceği. Tek başına duran bir
25
+ * avatar iki harf taşır; bir kişiyi işaret etmek için gereken en az şey o. Ama ÖRTÜŞEN bir
26
+ * yığında iki harf sığmaz: üstteki karo alttakinin sağını keser ve "BS", "B" ile yarım bir
27
+ * "S" olarak okunur. Fotoğrafta bu sorun değildir, çünkü yarım bir yüz hâlâ bir yüzdür; bir
28
+ * harf ise yarım okunamaz. `AvatarStack` bu yüzden 1 geçiyor.
29
+ */
30
+ letters?: 1 | 2;
31
+ }): import("react").JSX.Element;
32
+ /**
33
+ * Üst üste binen ekip.
34
+ *
35
+ * İKİ ŞEY AYRI AYRI YANLIŞTI ve ikisi de aynı sonucu veriyordu: karolar
36
+ * birbirine yapışıp tek bir karışık blok olarak okunuyordu.
37
+ *
38
+ * Birincisi ÖRTÜŞME SABİTTİ — her boyutta 8px. Bir 24px karoda bu üçte bir
39
+ * demek, ve iki baş harften biri kapanıyor. Şimdi boyuna oranlı: dörtte bir,
40
+ * yani karo büyüdükçe örtüşme de büyüyor ama oran sabit kalıyor.
41
+ *
42
+ * İkincisi AYIRICI HALKA YOKTU. Avatarın kendi 1px kenarı yeterli sanılmıştı
43
+ * ve değildi: iki karo bitişince o kenarlar tek bir çizgi oluyor, ve
44
+ * hangisinin önde olduğu okunmuyor. Üstteki karonun etrafına ARKA PLAN
45
+ * RENGİNDE bir halka koyunca, üstteki alttakini gerçekten kesiyor.
46
+ *
47
+ * `surface` bu yüzden bir prop: halka, yığının ÜSTÜNDE DURDUĞU zeminin rengi
48
+ * olmalı. Varsayılan kart zemini (`--color-shell`), çünkü bir ekip listesi
49
+ * neredeyse her zaman bir kartın içindedir — ama sayfa zemininde duracaksa
50
+ * çağıran onu söyler. Bulanıklık yok: `0 0 0` yayılımsız bir halka, gölge
51
+ * değil.
52
+ */
53
+ export declare function AvatarStack({ names, extra, size, surface, }: {
54
+ names: string[];
55
+ extra?: number;
56
+ size?: number;
57
+ /**
58
+ * The colour of the ground behind the stack; the separating ring is drawn in it. TR: Yığının
59
+ * arkasındaki zeminin rengi; ayırıcı halka bu renkte çizilir.
60
+ */
61
+ surface?: string;
62
+ }): import("react").JSX.Element;
63
+ //# sourceMappingURL=avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../src/components/avatar.tsx"],"names":[],"mappings":"AAUA,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,EACrB,IAAI,EACJ,IAAS,EACT,GAAG,EACH,IAAY,EACZ,OAAW,GACZ,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACjB,+BAiCA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,KAAK,EACL,IAAS,EACT,OAA8B,GAC/B,EAAE;IACD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,+BA4BA"}
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ function initials(name, count) {
4
+ return name
5
+ .split(" ")
6
+ .filter(Boolean)
7
+ .slice(0, count)
8
+ .map((p) => p[0]?.toUpperCase())
9
+ .join("");
10
+ }
11
+ /** Square, monochrome. Identity is text — color is reserved for health. */
12
+ export function Avatar({ name, size = 32, src, bare = false, letters = 2, }) {
13
+ const box = {
14
+ width: size,
15
+ height: size,
16
+ /* 0.42, not 0.34: at the old ratio a 26px avatar produced 9px initials, which read as a
17
+ smudge next to a 16px icon glyph. Initials ARE the glyph when an avatar sits in a control. */
18
+ fontSize: Math.max(11, Math.round(size * 0.42)),
19
+ };
20
+ if (src) {
21
+ return (_jsx("img", { src: src, alt: name, width: size, height: size, className: cn("shrink-0 object-cover", !bare && "rounded-[var(--radius-mark)] border border-edge"), style: box }));
22
+ }
23
+ return (_jsx("span", { className: cn("inline-flex shrink-0 items-center justify-center font-semibold text-ink", !bare && "rounded-[var(--radius-mark)] border border-edge bg-hover"), style: box, "aria-hidden": true, children: initials(name, letters) }));
24
+ }
25
+ /**
26
+ * Üst üste binen ekip.
27
+ *
28
+ * İKİ ŞEY AYRI AYRI YANLIŞTI ve ikisi de aynı sonucu veriyordu: karolar
29
+ * birbirine yapışıp tek bir karışık blok olarak okunuyordu.
30
+ *
31
+ * Birincisi ÖRTÜŞME SABİTTİ — her boyutta 8px. Bir 24px karoda bu üçte bir
32
+ * demek, ve iki baş harften biri kapanıyor. Şimdi boyuna oranlı: dörtte bir,
33
+ * yani karo büyüdükçe örtüşme de büyüyor ama oran sabit kalıyor.
34
+ *
35
+ * İkincisi AYIRICI HALKA YOKTU. Avatarın kendi 1px kenarı yeterli sanılmıştı
36
+ * ve değildi: iki karo bitişince o kenarlar tek bir çizgi oluyor, ve
37
+ * hangisinin önde olduğu okunmuyor. Üstteki karonun etrafına ARKA PLAN
38
+ * RENGİNDE bir halka koyunca, üstteki alttakini gerçekten kesiyor.
39
+ *
40
+ * `surface` bu yüzden bir prop: halka, yığının ÜSTÜNDE DURDUĞU zeminin rengi
41
+ * olmalı. Varsayılan kart zemini (`--color-shell`), çünkü bir ekip listesi
42
+ * neredeyse her zaman bir kartın içindedir — ama sayfa zemininde duracaksa
43
+ * çağıran onu söyler. Bulanıklık yok: `0 0 0` yayılımsız bir halka, gölge
44
+ * değil.
45
+ */
46
+ export function AvatarStack({ names, extra, size = 24, surface = "var(--color-shell)", }) {
47
+ const overlap = Math.round(size / 4);
48
+ const ring = { boxShadow: `0 0 0 2px ${surface}` };
49
+ /* Karoların kendi baş harfleri `aria-hidden` — üst üste binmiş harfler ekran
50
+ okuyucuda anlamsız bir dizi olurdu. Adlar bir kez, düz metin olarak
51
+ veriliyor: "Berika Sultan, Deniz Kara ve 7 kişi daha". */
52
+ const spoken = [...names, extra ? `+${extra}` : null].filter(Boolean).join(", ");
53
+ return (_jsxs("div", { className: "flex items-center", role: "img", "aria-label": spoken, children: [names.map((n, i) => (_jsx("span", { className: "rounded-[var(--radius-mark)]", style: { marginLeft: i === 0 ? 0 : -overlap, ...ring }, children: _jsx(Avatar, { name: n, size: size, letters: 1 }) }, n + i))), extra ? (_jsxs("span", { className: "inline-flex items-center justify-center rounded-[var(--radius-mark)] border border-edge bg-shell font-mono text-caption font-bold text-ink", style: { width: size, height: size, marginLeft: -overlap, ...ring }, children: ["+", extra] })) : null] }));
54
+ }
@@ -0,0 +1,37 @@
1
+ import type { Tone } from "./tone.js";
2
+ export declare function StatusChip({ label, state, dot, live, mono, severity, }: {
3
+ label: string;
4
+ state: Tone;
5
+ dot?: boolean;
6
+ live?: boolean;
7
+ /**
8
+ * identifier-shaped labels (codes, short keys) keep the mono face TR: tanımlayıcı biçimli
9
+ * etiketler (kodlar, kısa anahtarlar) mono yüzü korur
10
+ */
11
+ mono?: boolean;
12
+ /**
13
+ * explicit rank, once the API carries one; otherwise derived from state TR: API bir sıra
14
+ * taşıdığında açık sıra; yoksa durumdan türetiliyor
15
+ */
16
+ severity?: number | null;
17
+ }): import("react").JSX.Element;
18
+ export declare function Delta({ value, better, mono, }: {
19
+ value: string;
20
+ better: boolean;
21
+ mono?: boolean;
22
+ }): import("react").JSX.Element;
23
+ /**
24
+ * Solid square status mark used in dense tables. Uses `fg` rather than `mark`
25
+ * so a single 10px square still reads at rest — bars can afford to be quieter,
26
+ * a lone dot cannot.
27
+ */
28
+ export declare function Dot({ state, live, severity, }: {
29
+ state: Tone;
30
+ live?: boolean;
31
+ /**
32
+ * explicit rank, once the API carries one; otherwise derived from state TR: API bir sıra
33
+ * taşıdığında açık sıra; yoksa durumdan türetiliyor
34
+ */
35
+ severity?: number | null;
36
+ }): import("react").JSX.Element;
37
+ //# sourceMappingURL=badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../src/components/badge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAStC,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,GAAU,EACV,IAAY,EACZ,IAAY,EACZ,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,+BAiBA;AAED,wBAAgB,KAAK,CAAC,EACpB,KAAK,EACL,MAAM,EACN,IAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,+BAeA;AAED;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,EAClB,KAAK,EACL,IAAY,EACZ,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,+BAUA"}