@mesgflow/ui-kit 0.6.0 → 0.7.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 (199) hide show
  1. package/README.md +56 -5
  2. package/fesm2022/mesgflow-ui-kit-accordion.mjs +3 -2
  3. package/fesm2022/mesgflow-ui-kit-accordion.mjs.map +1 -1
  4. package/fesm2022/mesgflow-ui-kit-agenda.mjs +208 -0
  5. package/fesm2022/mesgflow-ui-kit-agenda.mjs.map +1 -0
  6. package/fesm2022/mesgflow-ui-kit-aspect-ratio.mjs +37 -0
  7. package/fesm2022/mesgflow-ui-kit-aspect-ratio.mjs.map +1 -0
  8. package/fesm2022/mesgflow-ui-kit-blocks.mjs +207 -0
  9. package/fesm2022/mesgflow-ui-kit-blocks.mjs.map +1 -0
  10. package/fesm2022/mesgflow-ui-kit-box-input.mjs +2 -2
  11. package/fesm2022/mesgflow-ui-kit-box-input.mjs.map +1 -1
  12. package/fesm2022/mesgflow-ui-kit-button.mjs +188 -242
  13. package/fesm2022/mesgflow-ui-kit-button.mjs.map +1 -1
  14. package/fesm2022/mesgflow-ui-kit-card.mjs +37 -16
  15. package/fesm2022/mesgflow-ui-kit-card.mjs.map +1 -1
  16. package/fesm2022/mesgflow-ui-kit-carousel.mjs +308 -0
  17. package/fesm2022/mesgflow-ui-kit-carousel.mjs.map +1 -0
  18. package/fesm2022/mesgflow-ui-kit-checkbox.mjs +16 -21
  19. package/fesm2022/mesgflow-ui-kit-checkbox.mjs.map +1 -1
  20. package/fesm2022/mesgflow-ui-kit-chip.mjs +2 -2
  21. package/fesm2022/mesgflow-ui-kit-chip.mjs.map +1 -1
  22. package/fesm2022/mesgflow-ui-kit-code-block.mjs +9 -5
  23. package/fesm2022/mesgflow-ui-kit-code-block.mjs.map +1 -1
  24. package/fesm2022/mesgflow-ui-kit-color-picker.mjs +6 -4
  25. package/fesm2022/mesgflow-ui-kit-color-picker.mjs.map +1 -1
  26. package/fesm2022/mesgflow-ui-kit-combobox.mjs +145 -65
  27. package/fesm2022/mesgflow-ui-kit-combobox.mjs.map +1 -1
  28. package/fesm2022/mesgflow-ui-kit-context-menu.mjs +4 -6
  29. package/fesm2022/mesgflow-ui-kit-context-menu.mjs.map +1 -1
  30. package/fesm2022/mesgflow-ui-kit-core.mjs +441 -128
  31. package/fesm2022/mesgflow-ui-kit-core.mjs.map +1 -1
  32. package/fesm2022/mesgflow-ui-kit-countdown.mjs +149 -0
  33. package/fesm2022/mesgflow-ui-kit-countdown.mjs.map +1 -0
  34. package/fesm2022/mesgflow-ui-kit-date-picker.mjs +131 -10
  35. package/fesm2022/mesgflow-ui-kit-date-picker.mjs.map +1 -1
  36. package/fesm2022/mesgflow-ui-kit-dialog.mjs +117 -34
  37. package/fesm2022/mesgflow-ui-kit-dialog.mjs.map +1 -1
  38. package/fesm2022/mesgflow-ui-kit-drawer.mjs +109 -0
  39. package/fesm2022/mesgflow-ui-kit-drawer.mjs.map +1 -0
  40. package/fesm2022/mesgflow-ui-kit-dropzone.mjs +3 -3
  41. package/fesm2022/mesgflow-ui-kit-dropzone.mjs.map +1 -1
  42. package/fesm2022/mesgflow-ui-kit-field.mjs +53 -12
  43. package/fesm2022/mesgflow-ui-kit-field.mjs.map +1 -1
  44. package/fesm2022/mesgflow-ui-kit-file-upload.mjs +356 -0
  45. package/fesm2022/mesgflow-ui-kit-file-upload.mjs.map +1 -0
  46. package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs +131 -0
  47. package/fesm2022/mesgflow-ui-kit-i18n-tr.mjs.map +1 -1
  48. package/fesm2022/mesgflow-ui-kit-icon.mjs +6 -1
  49. package/fesm2022/mesgflow-ui-kit-icon.mjs.map +1 -1
  50. package/fesm2022/mesgflow-ui-kit-icons-lucide.mjs +371 -0
  51. package/fesm2022/mesgflow-ui-kit-icons-lucide.mjs.map +1 -0
  52. package/fesm2022/mesgflow-ui-kit-inline-edit.mjs +279 -0
  53. package/fesm2022/mesgflow-ui-kit-inline-edit.mjs.map +1 -0
  54. package/fesm2022/mesgflow-ui-kit-input.mjs +147 -4
  55. package/fesm2022/mesgflow-ui-kit-input.mjs.map +1 -1
  56. package/fesm2022/mesgflow-ui-kit-lightbox.mjs +299 -0
  57. package/fesm2022/mesgflow-ui-kit-lightbox.mjs.map +1 -0
  58. package/fesm2022/mesgflow-ui-kit-link.mjs +2 -2
  59. package/fesm2022/mesgflow-ui-kit-link.mjs.map +1 -1
  60. package/fesm2022/mesgflow-ui-kit-listbox.mjs +151 -0
  61. package/fesm2022/mesgflow-ui-kit-listbox.mjs.map +1 -0
  62. package/fesm2022/mesgflow-ui-kit-mention.mjs +283 -0
  63. package/fesm2022/mesgflow-ui-kit-mention.mjs.map +1 -0
  64. package/fesm2022/mesgflow-ui-kit-menu.mjs +169 -5
  65. package/fesm2022/mesgflow-ui-kit-menu.mjs.map +1 -1
  66. package/fesm2022/mesgflow-ui-kit-nav-menu.mjs +276 -0
  67. package/fesm2022/mesgflow-ui-kit-nav-menu.mjs.map +1 -0
  68. package/fesm2022/mesgflow-ui-kit-notification-center.mjs +281 -0
  69. package/fesm2022/mesgflow-ui-kit-notification-center.mjs.map +1 -0
  70. package/fesm2022/mesgflow-ui-kit-number-input.mjs +7 -4
  71. package/fesm2022/mesgflow-ui-kit-number-input.mjs.map +1 -1
  72. package/fesm2022/mesgflow-ui-kit-pagination.mjs +4 -3
  73. package/fesm2022/mesgflow-ui-kit-pagination.mjs.map +1 -1
  74. package/fesm2022/mesgflow-ui-kit-password-input.mjs +7 -2
  75. package/fesm2022/mesgflow-ui-kit-password-input.mjs.map +1 -1
  76. package/fesm2022/mesgflow-ui-kit-popover.mjs +9 -49
  77. package/fesm2022/mesgflow-ui-kit-popover.mjs.map +1 -1
  78. package/fesm2022/mesgflow-ui-kit-progress.mjs +162 -21
  79. package/fesm2022/mesgflow-ui-kit-progress.mjs.map +1 -1
  80. package/fesm2022/mesgflow-ui-kit-radio.mjs +4 -6
  81. package/fesm2022/mesgflow-ui-kit-radio.mjs.map +1 -1
  82. package/fesm2022/mesgflow-ui-kit-rating.mjs +2 -2
  83. package/fesm2022/mesgflow-ui-kit-rating.mjs.map +1 -1
  84. package/fesm2022/mesgflow-ui-kit-search-input.mjs +9 -5
  85. package/fesm2022/mesgflow-ui-kit-search-input.mjs.map +1 -1
  86. package/fesm2022/mesgflow-ui-kit-segmented-control.mjs +12 -8
  87. package/fesm2022/mesgflow-ui-kit-segmented-control.mjs.map +1 -1
  88. package/fesm2022/mesgflow-ui-kit-select.mjs +3 -2
  89. package/fesm2022/mesgflow-ui-kit-select.mjs.map +1 -1
  90. package/fesm2022/mesgflow-ui-kit-sidebar.mjs +16 -13
  91. package/fesm2022/mesgflow-ui-kit-sidebar.mjs.map +1 -1
  92. package/fesm2022/mesgflow-ui-kit-skeleton.mjs +4 -3
  93. package/fesm2022/mesgflow-ui-kit-skeleton.mjs.map +1 -1
  94. package/fesm2022/mesgflow-ui-kit-slider.mjs +250 -3
  95. package/fesm2022/mesgflow-ui-kit-slider.mjs.map +1 -1
  96. package/fesm2022/mesgflow-ui-kit-sortable.mjs +526 -0
  97. package/fesm2022/mesgflow-ui-kit-sortable.mjs.map +1 -0
  98. package/fesm2022/mesgflow-ui-kit-spinner.mjs +42 -13
  99. package/fesm2022/mesgflow-ui-kit-spinner.mjs.map +1 -1
  100. package/fesm2022/mesgflow-ui-kit-split-button.mjs +205 -0
  101. package/fesm2022/mesgflow-ui-kit-split-button.mjs.map +1 -0
  102. package/fesm2022/mesgflow-ui-kit-splitter.mjs +210 -3
  103. package/fesm2022/mesgflow-ui-kit-splitter.mjs.map +1 -1
  104. package/fesm2022/mesgflow-ui-kit-stat-tile.mjs +11 -4
  105. package/fesm2022/mesgflow-ui-kit-stat-tile.mjs.map +1 -1
  106. package/fesm2022/mesgflow-ui-kit-stepper.mjs +167 -60
  107. package/fesm2022/mesgflow-ui-kit-stepper.mjs.map +1 -1
  108. package/fesm2022/mesgflow-ui-kit-switch.mjs +2 -2
  109. package/fesm2022/mesgflow-ui-kit-switch.mjs.map +1 -1
  110. package/fesm2022/mesgflow-ui-kit-table.mjs +16 -10
  111. package/fesm2022/mesgflow-ui-kit-table.mjs.map +1 -1
  112. package/fesm2022/mesgflow-ui-kit-tabs.mjs +410 -169
  113. package/fesm2022/mesgflow-ui-kit-tabs.mjs.map +1 -1
  114. package/fesm2022/mesgflow-ui-kit-tag-input.mjs +7 -2
  115. package/fesm2022/mesgflow-ui-kit-tag-input.mjs.map +1 -1
  116. package/fesm2022/mesgflow-ui-kit-theming.mjs +4 -4
  117. package/fesm2022/mesgflow-ui-kit-theming.mjs.map +1 -1
  118. package/fesm2022/mesgflow-ui-kit-time-picker.mjs +3 -3
  119. package/fesm2022/mesgflow-ui-kit-time-picker.mjs.map +1 -1
  120. package/fesm2022/mesgflow-ui-kit-toast.mjs +113 -30
  121. package/fesm2022/mesgflow-ui-kit-toast.mjs.map +1 -1
  122. package/fesm2022/mesgflow-ui-kit-toggle.mjs +3 -2
  123. package/fesm2022/mesgflow-ui-kit-toggle.mjs.map +1 -1
  124. package/fesm2022/mesgflow-ui-kit-toolbar.mjs +4 -2
  125. package/fesm2022/mesgflow-ui-kit-toolbar.mjs.map +1 -1
  126. package/fesm2022/mesgflow-ui-kit-tooltip.mjs +73 -50
  127. package/fesm2022/mesgflow-ui-kit-tooltip.mjs.map +1 -1
  128. package/fesm2022/mesgflow-ui-kit-tour.mjs +8 -20
  129. package/fesm2022/mesgflow-ui-kit-tour.mjs.map +1 -1
  130. package/fesm2022/mesgflow-ui-kit-transfer-list.mjs +288 -0
  131. package/fesm2022/mesgflow-ui-kit-transfer-list.mjs.map +1 -0
  132. package/fesm2022/mesgflow-ui-kit-tree.mjs +1 -1
  133. package/fesm2022/mesgflow-ui-kit-tree.mjs.map +1 -1
  134. package/fesm2022/mesgflow-ui-kit-virtual-list.mjs +109 -0
  135. package/fesm2022/mesgflow-ui-kit-virtual-list.mjs.map +1 -0
  136. package/fesm2022/mesgflow-ui-kit.mjs +92 -12836
  137. package/fesm2022/mesgflow-ui-kit.mjs.map +1 -1
  138. package/package.json +87 -3
  139. package/schematics/collection.json +10 -0
  140. package/schematics/migration.json +10 -0
  141. package/schematics/migrations/v0-7.cjs +70 -0
  142. package/schematics/ng-add/index.cjs +234 -0
  143. package/schematics/ng-add/schema.json +43 -0
  144. package/tokens/color.css +96 -0
  145. package/tokens/controls.css +145 -0
  146. package/tokens/elevation.css +62 -6
  147. package/tokens/motion.css +304 -66
  148. package/tokens/radius.css +14 -0
  149. package/tokens.css +1 -0
  150. package/tokens.json +1583 -0
  151. package/types/mesgflow-ui-kit-agenda.d.ts +49 -0
  152. package/types/mesgflow-ui-kit-aspect-ratio.d.ts +18 -0
  153. package/types/mesgflow-ui-kit-blocks.d.ts +128 -0
  154. package/types/mesgflow-ui-kit-button.d.ts +23 -38
  155. package/types/mesgflow-ui-kit-card.d.ts +26 -16
  156. package/types/mesgflow-ui-kit-carousel.d.ts +51 -0
  157. package/types/mesgflow-ui-kit-checkbox.d.ts +8 -5
  158. package/types/mesgflow-ui-kit-code-block.d.ts +4 -1
  159. package/types/mesgflow-ui-kit-combobox.d.ts +47 -22
  160. package/types/mesgflow-ui-kit-core.d.ts +271 -21
  161. package/types/mesgflow-ui-kit-countdown.d.ts +47 -0
  162. package/types/mesgflow-ui-kit-date-picker.d.ts +31 -1
  163. package/types/mesgflow-ui-kit-dialog.d.ts +29 -6
  164. package/types/mesgflow-ui-kit-drawer.d.ts +45 -0
  165. package/types/mesgflow-ui-kit-field.d.ts +17 -8
  166. package/types/mesgflow-ui-kit-file-upload.d.ts +64 -0
  167. package/types/mesgflow-ui-kit-hover-card.d.ts +2 -2
  168. package/types/mesgflow-ui-kit-icons-lucide.d.ts +27 -0
  169. package/types/mesgflow-ui-kit-inline-edit.d.ts +42 -0
  170. package/types/mesgflow-ui-kit-input.d.ts +64 -2
  171. package/types/mesgflow-ui-kit-lightbox.d.ts +54 -0
  172. package/types/mesgflow-ui-kit-listbox.d.ts +30 -0
  173. package/types/mesgflow-ui-kit-mention.d.ts +59 -0
  174. package/types/mesgflow-ui-kit-menu.d.ts +41 -2
  175. package/types/mesgflow-ui-kit-nav-menu.d.ts +58 -0
  176. package/types/mesgflow-ui-kit-notification-center.d.ts +41 -0
  177. package/types/mesgflow-ui-kit-number-input.d.ts +3 -1
  178. package/types/mesgflow-ui-kit-password-input.d.ts +3 -1
  179. package/types/mesgflow-ui-kit-popover.d.ts +13 -5
  180. package/types/mesgflow-ui-kit-progress.d.ts +33 -12
  181. package/types/mesgflow-ui-kit-radio.d.ts +2 -2
  182. package/types/mesgflow-ui-kit-search-input.d.ts +3 -1
  183. package/types/mesgflow-ui-kit-segmented-control.d.ts +4 -0
  184. package/types/mesgflow-ui-kit-sidebar.d.ts +1 -1
  185. package/types/mesgflow-ui-kit-slider.d.ts +60 -1
  186. package/types/mesgflow-ui-kit-sortable.d.ts +103 -0
  187. package/types/mesgflow-ui-kit-spinner.d.ts +5 -8
  188. package/types/mesgflow-ui-kit-split-button.d.ts +47 -0
  189. package/types/mesgflow-ui-kit-splitter.d.ts +64 -1
  190. package/types/mesgflow-ui-kit-stat-tile.d.ts +3 -0
  191. package/types/mesgflow-ui-kit-stepper.d.ts +54 -32
  192. package/types/mesgflow-ui-kit-table.d.ts +2 -0
  193. package/types/mesgflow-ui-kit-tabs.d.ts +193 -93
  194. package/types/mesgflow-ui-kit-tag-input.d.ts +3 -1
  195. package/types/mesgflow-ui-kit-toast.d.ts +48 -22
  196. package/types/mesgflow-ui-kit-tooltip.d.ts +15 -12
  197. package/types/mesgflow-ui-kit-transfer-list.d.ts +34 -0
  198. package/types/mesgflow-ui-kit-virtual-list.d.ts +35 -0
  199. package/types/mesgflow-ui-kit.d.ts +87 -5444
@@ -1,51 +1,102 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, computed, Component } from '@angular/core';
3
- import { cn } from '@mesgflow/ui-kit/core';
3
+ import { focusRecipe, disabledRecipe, cn } from '@mesgflow/ui-kit/core';
4
4
  import { Icon } from '@mesgflow/ui-kit/icon';
5
5
  import { Spinner } from '@mesgflow/ui-kit/spinner';
6
6
  import { cva } from 'class-variance-authority';
7
7
 
8
+ const TONES = ['primary', 'neutral', 'success', 'warning', 'danger', 'info', 'help'];
9
+ /** Tonun "renkli metin" rolü — primary `accent`, diğerleri çıplak rol adı (moda göre takas olur). */
10
+ const INK = {
11
+ primary: 'accent',
12
+ neutral: 'fg-muted',
13
+ success: 'success',
14
+ warning: 'warning',
15
+ danger: 'danger',
16
+ info: 'info',
17
+ help: 'help',
18
+ };
8
19
  /**
9
- * İki bağımsız eksen: `tone` (anlamsal renk, bkz. `MfTone`) × `appearance`
10
- * (dolu/kenarlıklı/düz-metin). Her kombinasyon
11
- * `compoundVariants`'ta açık — okunabilirlik netlik demek, kısayola kaçma.
20
+ * Altı görünüm × yedi ton — her hücre `compoundVariants`'ta açık (üretilmiş,
21
+ * ama tek bir tabloda okunur). Görünüm hiyerarşisi (en vurgulu → en sessiz):
22
+ *
23
+ * - `filled` — birincil eylem. Dolu rol zemini (`X-fill`), primary'de dark
24
+ * modda emerald `shadow-glow`, diğer tonlarda `shadow-filled`.
25
+ * - `soft` — tonal (Radix "soft", M3 "tonal"): `X-tint` zemin + `X-tint-fg`
26
+ * metin. Kart içi ikincil eylemler, filtre çipleri, "Taslak kaydet".
27
+ * - `outlined` — rengin kendisinin düşük opaklıklı kenarlığı (jenerik gri
28
+ * DEĞİL — gri çizgi "90'lar form elemanı" hissi veriyordu).
29
+ * - `ghost` — kenarlıksız, zeminsiz; hover'da NÖTR state layer
30
+ * (`bg-state-hover`, cam/renkli zeminde de doğru). Araç çubuğu/ikon butonları.
31
+ * - `text` — `ghost`'un renkli-hover'lı hali (geriye dönük uyumluluk).
32
+ * - `link` — satır içi bağlantı gibi: yükseklik/dolgu yok, altı çizili hover.
12
33
  *
13
34
  * Gloss/iç-highlight YOK — 2 kez reddedildi.
14
- * `outlined` kenarlığı bilinçli olarak jenerik gri DEĞİL, rengin kendisinin
15
- * düşük opaklıklı hali (`border-accent/30` gibi) — düz gri çizgi "90'lar
16
- * form elemanı" hissi veriyordu, renkli-şeffaf kenarlık modern/kasıtlı durur.
17
- * Bu opaklık artık TEK bir class'ta iki modu birden doğru veriyor: `accent`/
18
- * `success`/`warning`/`danger`/`info` çıplak isimleri `styles.css`'te
19
- * moda göre takas olan rol token'ları (600 basamağı light, 400 basamağı
20
- * dark) — `dark:` ikizi yazmaya gerek YOK.
21
35
  */
22
- const buttonVariants = cva(
36
+ function toneMatrix() {
37
+ const rows = [];
38
+ for (const tone of TONES) {
39
+ const ink = INK[tone];
40
+ // ---- filled ----
41
+ const filled = tone === 'primary'
42
+ ? 'bg-accent-fill text-fg-on-accent shadow-glow hover:bg-accent-fill-hover'
43
+ : tone === 'neutral'
44
+ ? 'bg-neutral-fill text-neutral-fill-fg hover:bg-neutral-fill-hover'
45
+ : `bg-${tone}-fill text-${tone}-fill-fg shadow-filled hover:bg-${tone}-fill-hover`;
46
+ rows.push({ tone, appearance: 'filled', class: filled });
47
+ // ---- soft ----
48
+ const soft = tone === 'primary'
49
+ ? 'bg-primary-tint text-primary-tint-fg hover:bg-accent/20'
50
+ : tone === 'neutral'
51
+ ? 'bg-state-selected text-fg hover:bg-state-pressed'
52
+ : `bg-${tone}-tint text-${tone}-tint-fg hover:bg-${tone}/20`;
53
+ rows.push({ tone, appearance: 'soft', class: soft });
54
+ // ---- outlined ----
55
+ const outlined = tone === 'neutral'
56
+ ? 'border-border text-fg bg-surface shadow-control hover:bg-state-hover hover:border-border-strong'
57
+ : `border-${ink}/30 text-${ink} hover:bg-${ink}/5 hover:border-${ink}/50`;
58
+ rows.push({ tone, appearance: 'outlined', class: outlined });
59
+ // ---- ghost ----
60
+ const ghost = tone === 'neutral'
61
+ ? 'text-fg-muted hover:bg-state-hover hover:text-fg active:bg-state-pressed'
62
+ : `text-${ink} hover:bg-state-hover active:bg-state-pressed`;
63
+ rows.push({ tone, appearance: 'ghost', class: ghost });
64
+ // ---- text ----
65
+ const text = tone === 'neutral'
66
+ ? 'text-fg-muted hover:bg-state-hover hover:text-fg'
67
+ : `text-${ink} hover:bg-${ink}/5`;
68
+ rows.push({ tone, appearance: 'text', class: text });
69
+ // ---- link ----
70
+ const link = tone === 'neutral' ? 'text-fg hover:text-fg' : `text-${ink}`;
71
+ rows.push({ tone, appearance: 'link', class: link });
72
+ }
73
+ return rows;
74
+ }
23
75
  /*
24
- * `appearance-none border border-transparent`: Tailwind'in TAM `preflight.css`'i
25
- * (bkz. styles.css) native <button>'ın tarayıcı varsayılan görünümünü zaten
26
- * sıfırlıyor; `border-transparent` burada BİLEREK KALICI — `border` width'i
27
- * TEK KAYNAKTAN (buradan) gelsin diye netlik amaçlı bırakıldı. Outlined
28
- * compound variant'ları sadece border-RENGİNİ değiştiriyor (`cn()`/twMerge
29
- * ile çakışma güvenli çözülüyor, bkz. button.ts) — ayrıca bir yerde
30
- * `border-0`/`border` çifti YAZMA.
76
+ * Tailwind sınıf tarayıcısı üretilmiş string'leri GÖREMEZ (`bg-${tone}-fill`).
77
+ * Aşağıdaki liste yalnızca tarayıcı için — derlenen CSS'e girmelerini garanti eder.
78
+ * bg-success-fill bg-warning-fill bg-danger-fill bg-info-fill bg-help-fill
79
+ * text-success-fill-fg text-warning-fill-fg text-danger-fill-fg text-info-fill-fg text-help-fill-fg
80
+ * hover:bg-success-fill-hover hover:bg-warning-fill-hover hover:bg-danger-fill-hover hover:bg-info-fill-hover hover:bg-help-fill-hover
81
+ * bg-success-tint bg-warning-tint bg-danger-tint bg-info-tint bg-help-tint
82
+ * text-success-tint-fg text-warning-tint-fg text-danger-tint-fg text-info-tint-fg text-help-tint-fg
83
+ * hover:bg-success/20 hover:bg-warning/20 hover:bg-danger/20 hover:bg-info/20 hover:bg-help/20
84
+ * border-accent/30 border-success/30 border-warning/30 border-danger/30 border-info/30 border-help/30
85
+ * hover:border-accent/50 hover:border-success/50 hover:border-warning/50 hover:border-danger/50 hover:border-info/50 hover:border-help/50
86
+ * text-accent text-success text-warning text-danger text-info text-help
87
+ * hover:bg-accent/5 hover:bg-success/5 hover:bg-warning/5 hover:bg-danger/5 hover:bg-info/5 hover:bg-help/5
31
88
  */
89
+ const buttonVariants = cva(
32
90
  /*
33
- * Focus: box-shadow tabanlı `ring`+`ring-offset` YERİNE native `outline`+
34
- * `outline-offset` kullanılıyor (bkz. projects/ui-kit/README.md "Focus — tek
35
- * mekanizma") — Windows Yüksek Kontrast modunda box-shadow tamamen
36
- * kayboluyor ama outline korunuyor, outline `overflow:hidden` içinde
37
- * kırpılmıyor, ve modern tarayıcılar outline'ı elementin kendi
38
- * border-radius'una göre otomatik yuvarlıyor. Renk artık TÜM primitifler
39
- * genelinde TEK bir kaynak: `outline-focus-ring` (styles.css'te tanımlı,
40
- * moda göre 600/400 basamağı arasında takas olur) — eskiden her tone
41
- * kendi `focus-visible:outline-X/50`'sini taşıyordu (input'ta `ring-4`,
42
- * checkbox'ta `ring-2 ring-offset-2` gibi üç farklı mekanizma da vardı),
43
- * artık tüm primitifler bu tek mekanizmaya bağlı.
91
+ * `border border-transparent` — border width TEK kaynaktan; outlined
92
+ * yalnızca rengini değiştirir. Geçiş: renkler + gölge + `scale` (basma
93
+ * hissi artık anlık değil, `duration-fast` ile yumuşak).
94
+ * Focus: `outline` tabanlı TEK mekanizma (`focusRecipe`, bkz. core/recipes.ts).
44
95
  */
45
- 'inline-flex appearance-none items-center justify-center gap-2 border border-transparent font-medium tracking-label transition-colors duration-fast ease-emphasis ' +
46
- 'active:scale-[0.99] ' +
47
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring ' +
48
- 'disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none disabled:active:scale-100', {
96
+ 'relative inline-flex shrink-0 appearance-none items-center justify-center gap-2 border border-transparent font-medium tracking-label whitespace-nowrap select-none ' +
97
+ 'transition-[color,background-color,border-color,box-shadow,scale,opacity] duration-fast ease-emphasis ' +
98
+ 'active:scale-[0.98] disabled:active:scale-100 ' +
99
+ `${focusRecipe} ${disabledRecipe}`, {
49
100
  variants: {
50
101
  tone: {
51
102
  primary: '',
@@ -58,48 +109,33 @@ const buttonVariants = cva(
58
109
  },
59
110
  appearance: {
60
111
  filled: '',
112
+ soft: '',
61
113
  outlined: 'bg-transparent',
114
+ ghost: 'bg-transparent',
62
115
  text: 'bg-transparent',
116
+ link: 'bg-transparent underline-offset-4 hover:underline active:scale-100',
63
117
  },
64
118
  /*
65
- * Yoğunluk sıkılaştırıldı: md 40px→36px (kurumsal panellerde 40px iri
66
- * duruyordu, Linear/Vercel/GitHub yoğunluğuna çekildi). Radius
67
- * `rounded-lg`(eski 8px) → `rounded-md`(yeni 8px) — görünüş AYNI,
68
- * sadece isim değişti (bkz. radius.css'teki "nested radius" notu:
69
- * kart/panel gibi KAPSAYICILAR artık `rounded-lg`de kalıp otomatik
70
- * 12px'e büyüyor, kontroller bu satırda `rounded-md`ye taşınarak eski
71
- * görünümü koruyor).
72
- *
73
119
  * `h-control-*` (bkz. radius.css) — TEK kontrol-yükseklik kaynağı,
74
- * Input/Select/NumberInput/... ile PAYLAŞILAN aynı üç basamak (bkz.
75
- * `core/recipes.ts` `controlSize`). `lg` bu geçişte 44px→40px'e indi
76
- * (BREAKING — CHANGELOG'a bkz.): 44px sm/md'ye göre orantısız iriydi,
77
- * yeni üçlü tutarlı bir 32/36/40 merdiveni.
120
+ * Input/Select/... ile PAYLAŞILAN merdiven: xs 28 · sm 32 · md 36 ·
121
+ * lg 40 · xl 48 (density ile birlikte ölçeklenir).
78
122
  */
79
123
  size: {
80
- sm: 'h-control-sm px-3 text-sm rounded-md',
81
- md: 'h-control-md px-4 text-sm rounded-md',
82
- lg: 'h-control-lg px-5 text-base rounded-md',
124
+ xs: 'h-control-xs gap-1.5 rounded-md px-2.5 text-xs',
125
+ sm: 'h-control-sm gap-1.5 rounded-md px-3 text-sm',
126
+ md: 'h-control-md rounded-md px-4 text-sm',
127
+ lg: 'h-control-lg rounded-md px-5 text-base',
128
+ xl: 'h-control-xl rounded-lg px-6 text-base',
83
129
  /*
84
- * `text-xs font-semibold`: icon boyutu SADECE ikon değil metin de
85
- * taşıyabiliyor (ör. language-switcher'ın "EN"/"TR" etiketi) — boyut
86
- * tanımlanmadığında metin varsayılan (büyük) font'ta basılıyor,
87
- * yanındaki 18px ikonla görsel ağırlığı uyuşmuyordu.
88
- *
89
- * Şekil (yuvarlak/köşeli) ve dolayısıyla focus mekanizması artık
90
- * `shape` eksenine taşındı (aşağıda) — bu satır sadece boyut/tipografi
91
- * taşıyor, `rounded-*` YOK. Yükseklik `md` ile aynı (36px).
130
+ * Kare ikon butonları — yükseklik ilgili kontrol basamağıyla aynı.
131
+ * `text-xs font-semibold`: ikon butonu kısa metin de taşıyabilir
132
+ * (dil değiştiricinin "EN"/"TR" etiketi).
92
133
  */
93
- icon: 'relative aspect-square h-control-md shrink-0 text-xs font-semibold',
134
+ 'icon-sm': 'aspect-square h-control-sm text-xs font-semibold',
135
+ icon: 'aspect-square h-control-md text-xs font-semibold',
136
+ 'icon-lg': 'aspect-square h-control-lg text-sm font-semibold',
94
137
  },
95
- /**
96
- * `size="icon"` için şekil ekseni — diğer boyutlarda (sm/md/lg) etkisiz
97
- * (boş string). Kullanıcı geri bildirimi: yuvarlak ikon-buton hover'ı
98
- * "her yerde aynı" duruyordu, GitHub/Linear tarzı köşeli (rounded-md)
99
- * bir hover/şekil de istendi — toolbar aksiyonları (Filtreler, satır
100
- * düzenle/sil vb.) `square`, avatar-yanı ikonlar (tema/dil değiştirici)
101
- * `round` (varsayılan) kullanıyor.
102
- */
138
+ /** Yalnızca ikon boyutlarında etkili — `round` (avatar yanı) / `square` (araç çubuğu). */
103
139
  shape: {
104
140
  round: '',
105
141
  square: '',
@@ -107,173 +143,49 @@ const buttonVariants = cva(
107
143
  },
108
144
  compoundVariants: [
109
145
  /*
110
- * round: native `outline` `rounded-full`'u izlediği için (modern
111
- * tarayıcılar outline'ı border-radius'a göre otomatik yuvarlıyor)
112
- * doğrudan buton üzerinde bırakılsaydı focus göstergesi daire-içinde-
113
- * daire ("bullseye"/ripple'ı andıran) görünürdü. Bunun yerine kendi
114
- * `rounded-md` (köşeli) radius'una sahip bağımsız bir `::after` pseudo-
115
- * element kare/köşeli bir çerçeve çiziyor, rengi de artık tek kaynaklı
116
- * `outline-focus-ring` — eskiden "ink/paper" sabit zıtlığı (light:
117
- * dark-canvas, dark: slate-100) kullanılıyordu çünkü `outline-current`
118
- * filled+beyaz-metin butonlarda beyaz zeminde beyaza karışıyordu; yeni
119
- * `focus-ring` token'ı zaten primary-600/400 (arkaplandan bağımsız
120
- * yüksek kontrastlı bir marka rengi) olduğu için aynı sorunu yaşamıyor.
121
- *
122
- * square: buton zaten `rounded-md` olduğundan native `outline` fazladan
123
- * bir teknik gerektirmeden doğru köşeli çerçeveyi otomatik veriyor —
124
- * base class'taki `focus-visible:outline` mekanizması yeterli, ek bir
125
- * şey eklemeye GEREK YOK.
146
+ * round: native `outline` `rounded-full`'u izlediği için doğrudan buton
147
+ * üzerinde "daire içinde daire" görünürdü — köşeli bağımsız bir
148
+ * `::after` çerçeve çiziliyor. square: buton zaten `rounded-md`.
126
149
  */
127
- {
128
- size: 'icon',
129
- shape: 'round',
130
- class: "rounded-full focus-visible:outline-none after:pointer-events-none after:absolute after:-inset-1 after:rounded-md after:opacity-0 after:outline after:outline-2 after:outline-focus-ring after:transition-opacity after:duration-fast after:content-[''] focus-visible:after:opacity-100",
131
- },
132
- {
133
- size: 'icon',
134
- shape: 'square',
135
- class: 'rounded-md',
136
- },
137
- // ---- filled ----
138
- // NOT: primary'nin dolu bloğu `bg-accent-fill`/`hover:bg-accent-fill-hover`
139
- // (color.css'te tanımlı rol token'ları) KULLANIR, ham basamak DEĞİL —
140
- // LIGHT'ta primary-900 (`#062c22`, spec'in "Forest Emerald" CTA'sı),
141
- // DARK'ta primary-400 (`#10b981`, spec'in canlı zümrüt CTA'sı) + DARK'ta
142
- // mürekkep metin (`--mf-fg-on-accent`, beyaz `#10B981` üstünde 2.54:1
143
- // AA altı olduğundan). Diğer dört semantik ton (success/warning/danger/
144
- // info/help) da AYNI beşli rol desenini paylaşır: `bg-X-fill
145
- // text-X-fill-fg shadow-filled hover:bg-X-fill-hover` — light'ta
146
- // danger/help/info beyaz metin taşır, success/warning HER ZAMAN
147
- // mürekkep (spec'in beş rengi de dark modda beyazla AA'yı geçmiyor,
148
- // bu yüzden dark'ta hepsi mürekkep). `shadow-filled` (elevation.css) —
149
- // `shadow-1` kapsayıcı gölgesidir, kontrol ödünç almaz; nötr filled
150
- // (spec'in "Secondary"si, `neutral-fill` rol token'ı) gölgesiz kalır.
151
- {
152
- tone: 'primary',
153
- appearance: 'filled',
154
- class: 'bg-accent-fill text-fg-on-accent shadow-glow hover:bg-accent-fill-hover',
155
- },
156
- {
157
- tone: 'neutral',
158
- appearance: 'filled',
159
- class: 'bg-neutral-fill text-neutral-fill-fg hover:bg-neutral-fill-hover',
160
- },
161
- {
162
- tone: 'danger',
163
- appearance: 'filled',
164
- class: 'bg-danger-fill text-danger-fill-fg shadow-filled hover:bg-danger-fill-hover',
165
- },
166
- {
167
- tone: 'info',
168
- appearance: 'filled',
169
- class: 'bg-info-fill text-info-fill-fg shadow-filled hover:bg-info-fill-hover',
170
- },
171
- {
172
- tone: 'success',
173
- appearance: 'filled',
174
- class: 'bg-success-fill text-success-fill-fg shadow-filled hover:bg-success-fill-hover',
175
- },
176
- {
177
- tone: 'warning',
178
- appearance: 'filled',
179
- class: 'bg-warning-fill text-warning-fill-fg shadow-filled hover:bg-warning-fill-hover',
180
- },
181
- {
182
- tone: 'help',
183
- appearance: 'filled',
184
- class: 'bg-help-fill text-help-fill-fg shadow-filled hover:bg-help-fill-hover',
185
- },
186
- // ---- outlined ----
187
- {
188
- tone: 'primary',
189
- appearance: 'outlined',
190
- class: 'border-accent/30 text-accent hover:bg-accent/5',
191
- },
192
- {
193
- tone: 'neutral',
194
- appearance: 'outlined',
195
- class: 'border-border text-fg-muted hover:bg-surface-alt',
196
- },
197
- {
198
- tone: 'success',
199
- appearance: 'outlined',
200
- class: 'border-success/30 text-success hover:bg-success/5',
201
- },
202
- {
203
- tone: 'warning',
204
- appearance: 'outlined',
205
- class: 'border-warning/30 text-warning hover:bg-warning/5',
206
- },
207
- {
208
- tone: 'danger',
209
- appearance: 'outlined',
210
- class: 'border-danger/30 text-danger hover:bg-danger/5',
211
- },
212
- {
213
- tone: 'info',
214
- appearance: 'outlined',
215
- class: 'border-info/30 text-info hover:bg-info/5',
216
- },
217
- {
218
- tone: 'help',
219
- appearance: 'outlined',
220
- class: 'border-help/30 text-help hover:bg-help/5',
221
- },
222
- // ---- text ----
223
- {
224
- tone: 'primary',
225
- appearance: 'text',
226
- class: 'text-accent hover:bg-accent/5',
227
- },
228
- {
229
- tone: 'neutral',
230
- appearance: 'text',
231
- class: 'text-fg-muted hover:bg-surface-alt',
232
- },
233
- {
234
- tone: 'success',
235
- appearance: 'text',
236
- class: 'text-success hover:bg-success/5',
237
- },
238
- {
239
- tone: 'warning',
240
- appearance: 'text',
241
- class: 'text-warning hover:bg-warning/5',
242
- },
243
- {
244
- tone: 'danger',
245
- appearance: 'text',
246
- class: 'text-danger hover:bg-danger/5',
247
- },
248
- {
249
- tone: 'info',
250
- appearance: 'text',
251
- class: 'text-info hover:bg-info/5',
252
- },
253
- {
254
- tone: 'help',
255
- appearance: 'text',
256
- class: 'text-help hover:bg-help/5',
257
- },
150
+ ...['icon-sm', 'icon', 'icon-lg'].flatMap((size) => [
151
+ {
152
+ size,
153
+ shape: 'round',
154
+ class: "rounded-full focus-visible:outline-none after:pointer-events-none after:absolute after:-inset-1 after:rounded-md after:opacity-0 after:outline after:outline-2 after:outline-focus-ring after:transition-opacity after:duration-fast after:content-[''] focus-visible:after:opacity-100",
155
+ },
156
+ { size, shape: 'square', class: 'rounded-md' },
157
+ ]),
158
+ // `link` boyut ekseninin yüksekliğini/dolgusunu ezer — satır içi akar.
159
+ { appearance: 'link', class: 'h-auto rounded-sm p-0' },
160
+ ...toneMatrix(),
258
161
  ],
259
162
  defaultVariants: { tone: 'primary', appearance: 'filled', size: 'md', shape: 'round' },
260
163
  });
261
164
 
165
+ /** İkon boyutu buton boyutuyla ölçeklenir. */
166
+ const ICON_PX = {
167
+ xs: 14,
168
+ sm: 16,
169
+ md: 18,
170
+ lg: 20,
171
+ xl: 20,
172
+ 'icon-sm': 16,
173
+ icon: 18,
174
+ 'icon-lg': 20,
175
+ };
262
176
  /**
263
177
  * `<button mfButton>`/`<a mfButton>` — selector native elemanın KENDİSİNİ
264
178
  * hedefler (`button[mfButton], a[mfButton]`), bir SARMALAYICI etiket
265
- * (`<mf-button>`) DEĞİL (bkz. `projects/ui-kit/README.md` "Directive mi,
266
- * component mi?") — bu yüzden tüketicinin `class="..."` ile verdiği static
267
- * class GENE doğrudan gerçek native elemana düşer, eskiden directive'ken
268
- * olduğu gibi (host'ta kalıp içe düşmeme sorunu YOK). Directive'ten
269
- * component'e yükseltilmenin TEK sebebi: `loading`'i gerçek bir
270
- * `<mf-spinner>`'a, `iconStart`/`iconEnd`'i gerçek ikon element'lerine
271
- * bağlayabilmek için bir ŞABLONA ihtiyaç duyulması — tüketicinin kendi
272
- * içeriği `<ng-content />` ile AYNEN korunur.
179
+ * (`<mf-button>`) DEĞİL — tüketicinin `class="..."` ile verdiği static
180
+ * class doğrudan gerçek native elemana düşer. Component olmasının TEK
181
+ * sebebi: `loading`'i gerçek bir `<mf-spinner>`'a, `iconStart`/`iconEnd`'i
182
+ * gerçek ikon element'lerine bağlayabilmek için bir ŞABLONA ihtiyaç duyması.
183
+ *
184
+ * `tone` (renk) × `appearance` (filled/soft/outlined/ghost/text/link) ×
185
+ * `size` (xs…xl, icon-sm/icon/icon-lg) bağımsız eksenler.
273
186
  *
274
- * `tone` (renk) × `appearance` (dolu/kenarlıklı/düz-metin) bağımsız iki
275
- * girdi — ör. `tone="danger" appearance="text" size="icon"` sadece ikon
276
- * gösteren, kenarlıksız bir sil butonu için.
187
+ * Mikro-etkileşimler: basınca `scale(0.98)` (yumuşak geçişli), `iconEnd`
188
+ * hover'da 2px ileri kayar (RTL'de geri), loading genişliği korur.
277
189
  */
278
190
  class Button {
279
191
  tone = input('primary', /* @ts-ignore */
@@ -282,25 +194,24 @@ class Button {
282
194
  ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
283
195
  size = input('md', /* @ts-ignore */
284
196
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
285
- /** Sadece `size="icon"`'da etkili — bkz. button.variants.ts. */
197
+ /** Sadece ikon boyutlarında (`icon-sm`/`icon`/`icon-lg`) etkili — bkz. button.variants.ts. */
286
198
  shape = input('round', /* @ts-ignore */
287
199
  ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
288
200
  /** Native <button> varsayılan type="submit" sürprizini önlemek için — <a>'da yok sayılır. */
289
201
  type = input('button', /* @ts-ignore */
290
202
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
291
203
  /**
292
- * `true` iken içeriğin (`<ng-content />`) ÖNÜNE `<mf-spinner size="xs" />`
293
- * eklenir — `iconStart` varsa YERİNE geçer, etiket metni görünür KALIR
294
- * (buton "Kaydet" yazıp yanında dönen bir gösterge taşır, metin
295
- * kaybolmaz). `disabled`'ı KENDİLİĞİNDEN yönetmez — tüketici
296
- * `[disabled]="loading() || ..."` ile kendi bağlar.
204
+ * `true` iken: `iconStart` varsa spinner onun YERİNE geçer (metin görünür
205
+ * kalır); yoksa spinner ortaya biner ve içerik saydamlaşır — iki durumda da
206
+ * buton genişliği DEĞİŞMEZ. `disabled`'ı KENDİLİĞİNDEN yönetmez —
207
+ * tüketici `[disabled]="loading() || ..."` ile kendi bağlar.
297
208
  */
298
209
  loading = input(false, /* @ts-ignore */
299
210
  ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
300
- /** Material Symbol adı (bkz. `Icon`) — içeriğin ÖNÜNE eklenir. `loading=true` iken spinner onun YERİNE geçer. */
211
+ /** İkon adı (bkz. `Icon`) — içeriğin ÖNÜNE eklenir. `loading=true` iken spinner onun YERİNE geçer. */
301
212
  iconStart = input('', /* @ts-ignore */
302
213
  ...(ngDevMode ? [{ debugName: "iconStart" }] : /* istanbul ignore next */ []));
303
- /** Material Symbol adı — içeriğin ARDINA eklenir. `loading=true` iken GİZLENİR (tek bir dönen gösterge yeterli — `iconStart` ile aynı ilke). */
214
+ /** İkon adı — içeriğin ARDINA eklenir, hover'da ileri kayar. `loading=true` iken GİZLENİR. */
304
215
  iconEnd = input('', /* @ts-ignore */
305
216
  ...(ngDevMode ? [{ debugName: "iconEnd" }] : /* istanbul ignore next */ []));
306
217
  classes = computed(() => cn(buttonVariants({
@@ -308,20 +219,39 @@ class Button {
308
219
  appearance: this.appearance(),
309
220
  size: this.size(),
310
221
  shape: this.shape(),
311
- }), this.loading() ? 'cursor-wait' : ''), /* @ts-ignore */
222
+ }), 'group/btn', this.loading() ? 'cursor-wait' : ''), /* @ts-ignore */
312
223
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
313
224
  nativeType = computed(() => this.type(), /* @ts-ignore */
314
225
  ...(ngDevMode ? [{ debugName: "nativeType" }] : /* istanbul ignore next */ []));
226
+ iconPx = computed(() => ICON_PX[this.size() ?? 'md'], /* @ts-ignore */
227
+ ...(ngDevMode ? [{ debugName: "iconPx" }] : /* istanbul ignore next */ []));
228
+ contentClass = computed(() => cn('inline-flex min-w-0 items-center gap-[inherit] empty:hidden', this.loading() && !this.iconStart() ? 'opacity-0' : ''), /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "contentClass" }] : /* istanbul ignore next */ []));
315
230
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
316
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: Button, isStandalone: true, selector: "button[mfButton], a[mfButton]", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, iconStart: { classPropertyName: "iconStart", publicName: "iconStart", isSignal: true, isRequired: false, transformFunction: null }, iconEnd: { classPropertyName: "iconEnd", publicName: "iconEnd", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.type": "nativeType()", "attr.aria-busy": "loading() || null" } }, exportAs: ["mfButton"], ngImport: i0, template: `
317
- @if (loading()) {
231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: Button, isStandalone: true, selector: "button[mfButton], a[mfButton]", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, iconStart: { classPropertyName: "iconStart", publicName: "iconStart", isSignal: true, isRequired: false, transformFunction: null }, iconEnd: { classPropertyName: "iconEnd", publicName: "iconEnd", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.type": "nativeType()", "attr.aria-busy": "loading() || null", "attr.data-appearance": "appearance()" } }, exportAs: ["mfButton"], ngImport: i0, template: `
232
+ @if (loading() && !iconStart()) {
233
+ <!--
234
+ İkon yuvası yoksa spinner ORTAYA biner ve içerik saydamlaşır — buton
235
+ genişliği DEĞİŞMEZ (layout shift yok). Metin erişilebilirlik ağacında
236
+ KALIR (opacity, visibility değil).
237
+ -->
238
+ <span class="absolute inset-0 flex items-center justify-center" aria-hidden="true">
239
+ <mf-spinner size="xs" />
240
+ </span>
241
+ }
242
+ @if (loading() && iconStart()) {
318
243
  <mf-spinner size="xs" />
319
244
  } @else if (iconStart()) {
320
- <span mfIcon [size]="18">{{ iconStart() }}</span>
245
+ <span mfIcon [size]="iconPx()">{{ iconStart() }}</span>
321
246
  }
322
- <ng-content />
247
+ <span [class]="contentClass()"><ng-content /></span>
323
248
  @if (!loading() && iconEnd()) {
324
- <span mfIcon [size]="18">{{ iconEnd() }}</span>
249
+ <span
250
+ mfIcon
251
+ [size]="iconPx()"
252
+ class="transition-transform duration-fast ease-emphasis group-hover/btn:translate-x-0.5 rtl:-scale-x-100 rtl:group-hover/btn:-translate-x-0.5"
253
+ >{{ iconEnd() }}</span
254
+ >
325
255
  }
326
256
  `, isInline: true, dependencies: [{ kind: "directive", type: Icon, selector: "span[mfIcon]", inputs: ["size", "filled"] }, { kind: "component", type: Spinner, selector: "mf-spinner", inputs: ["size", "tone", "ariaLabel"], exportAs: ["mfSpinner"] }] });
327
257
  }
@@ -336,16 +266,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
336
266
  '[class]': 'classes()',
337
267
  '[attr.type]': 'nativeType()',
338
268
  '[attr.aria-busy]': 'loading() || null',
269
+ '[attr.data-appearance]': 'appearance()',
339
270
  },
340
271
  template: `
341
- @if (loading()) {
272
+ @if (loading() && !iconStart()) {
273
+ <!--
274
+ İkon yuvası yoksa spinner ORTAYA biner ve içerik saydamlaşır — buton
275
+ genişliği DEĞİŞMEZ (layout shift yok). Metin erişilebilirlik ağacında
276
+ KALIR (opacity, visibility değil).
277
+ -->
278
+ <span class="absolute inset-0 flex items-center justify-center" aria-hidden="true">
279
+ <mf-spinner size="xs" />
280
+ </span>
281
+ }
282
+ @if (loading() && iconStart()) {
342
283
  <mf-spinner size="xs" />
343
284
  } @else if (iconStart()) {
344
- <span mfIcon [size]="18">{{ iconStart() }}</span>
285
+ <span mfIcon [size]="iconPx()">{{ iconStart() }}</span>
345
286
  }
346
- <ng-content />
287
+ <span [class]="contentClass()"><ng-content /></span>
347
288
  @if (!loading() && iconEnd()) {
348
- <span mfIcon [size]="18">{{ iconEnd() }}</span>
289
+ <span
290
+ mfIcon
291
+ [size]="iconPx()"
292
+ class="transition-transform duration-fast ease-emphasis group-hover/btn:translate-x-0.5 rtl:-scale-x-100 rtl:group-hover/btn:-translate-x-0.5"
293
+ >{{ iconEnd() }}</span
294
+ >
349
295
  }
350
296
  `,
351
297
  }]