@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,16 +1,19 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Component } from '@angular/core';
2
+ import { input, inject, ElementRef, computed, afterRenderEffect, Component } from '@angular/core';
3
+ import { nextId } from '@mesgflow/ui-kit/core';
3
4
 
4
5
  /**
5
- * `<mf-field label="E-posta" [error]="emailError()">
6
- * <input mfInput type="email" [id]="..." />
6
+ * `<mf-field label="E-posta" for="email" [error]="emailError()" hint="İş adresiniz">
7
+ * <input mfInput id="email" type="email" />
7
8
  * </mf-field>`
8
- * `for`/`id` eşleşmesi (a11y) ÇAĞIRAN ekranın sorumluluğunda — bu component
9
- * label'ı otomatik bir input'a bağlamıyor (projected content'in id'sini bilmiyor).
10
9
  *
11
- * `error`, `string | undefined` kabul eder (sadece `string` DEĞİL) — çağıran
12
- * genelde `shared/forms/http-error.ts`'teki `resolveFieldError()`'ı doğrudan
13
- * bağlar, o da hata yokken `undefined` döner (boş string'e zorlamaya gerek yok).
10
+ * `for` verildiğinde alan, içindeki `#for` kontrolünü OTOMATİK bağlar:
11
+ * - `aria-describedby` → ipucu/hata metninin id'si (kontrolün mevcut
12
+ * describedby değerleri korunur, yalnızca kendi id'lerini ekler/çıkarır),
13
+ * - `aria-invalid="true"` → hata varken,
14
+ * - `aria-required="true"` → `required` iken.
15
+ * Hata metni `role="alert"` DEĞİL, `aria-live="polite"` — her tuş vuruşunda
16
+ * ekran okuyucuyu kesmesin. `error` `string | undefined` kabul eder.
14
17
  */
15
18
  class Field {
16
19
  label = input('', /* @ts-ignore */
@@ -23,16 +26,54 @@ class Field {
23
26
  ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
24
27
  required = input(false, /* @ts-ignore */
25
28
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
26
- /** `true` ise kalan yüksekliği doldurur (`flex-1 min-h-0`) — projected içeriğin kendi tek scroll alanı olduğu durumlar için (ör. uzun bir liste içeren form, bkz. `Dialog.fillBody`). */
29
+ /** `true` ise kalan yüksekliği doldurur (`flex-1 min-h-0`). */
27
30
  grow = input(false, /* @ts-ignore */
28
31
  ...(ngDevMode ? [{ debugName: "grow" }] : /* istanbul ignore next */ []));
32
+ host = inject(ElementRef);
33
+ baseId = nextId('mf-field');
34
+ hintId = `${this.baseId}-hint`;
35
+ errorId = `${this.baseId}-error`;
36
+ /** Şu an görünen açıklama metninin id'si (hata ipucunun yerine geçer). */
37
+ describedId = computed(() => this.error() ? this.errorId : this.hint() ? this.hintId : null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "describedId" }] : /* istanbul ignore next */ []));
39
+ constructor() {
40
+ afterRenderEffect(() => {
41
+ const id = this.for();
42
+ const described = this.describedId();
43
+ const invalid = !!this.error();
44
+ const required = this.required();
45
+ if (!id)
46
+ return;
47
+ const control = this.host.nativeElement.querySelector(`#${CSS.escape(id)}`);
48
+ if (!control)
49
+ return;
50
+ const own = new Set([this.hintId, this.errorId]);
51
+ const ids = (control.getAttribute('aria-describedby') ?? '')
52
+ .split(/\s+/)
53
+ .filter((x) => x && !own.has(x));
54
+ if (described)
55
+ ids.push(described);
56
+ if (ids.length)
57
+ control.setAttribute('aria-describedby', ids.join(' '));
58
+ else
59
+ control.removeAttribute('aria-describedby');
60
+ if (invalid)
61
+ control.setAttribute('aria-invalid', 'true');
62
+ else
63
+ control.removeAttribute('aria-invalid');
64
+ if (required)
65
+ control.setAttribute('aria-required', 'true');
66
+ else
67
+ control.removeAttribute('aria-required');
68
+ });
69
+ }
29
70
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: Field, deps: [], target: i0.ɵɵFactoryTarget.Component });
30
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: Field, isStandalone: true, selector: "mf-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, for: { classPropertyName: "for", publicName: "for", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, grow: { classPropertyName: "grow", publicName: "grow", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n @if (error()) {\r\n <p class=\"text-xs text-danger\">{{ error() }}</p>\r\n } @else if (hint()) {\r\n <p class=\"text-xs text-fg-subtle\">{{ hint() }}</p>\r\n }\r\n</div>\r\n" });
71
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: Field, isStandalone: true, selector: "mf-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, for: { classPropertyName: "for", publicName: "for", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, grow: { classPropertyName: "grow", publicName: "grow", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\" aria-hidden=\"true\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n <div aria-live=\"polite\" class=\"empty:hidden\">\r\n @if (error()) {\r\n <p\r\n [id]=\"errorId\"\r\n class=\"m-0 flex items-center gap-1 text-xs text-danger animate-fade-in\"\r\n [textContent]=\"error()\"\r\n ></p>\r\n } @else if (hint()) {\r\n <p [id]=\"hintId\" class=\"m-0 text-xs text-fg-subtle\" [textContent]=\"hint()\"></p>\r\n }\r\n </div>\r\n</div>\r\n" });
31
72
  }
32
73
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: Field, decorators: [{
33
74
  type: Component,
34
- args: [{ selector: 'mf-field', host: { class: 'block' }, template: "<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n @if (error()) {\r\n <p class=\"text-xs text-danger\">{{ error() }}</p>\r\n } @else if (hint()) {\r\n <p class=\"text-xs text-fg-subtle\">{{ hint() }}</p>\r\n }\r\n</div>\r\n" }]
35
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], grow: [{ type: i0.Input, args: [{ isSignal: true, alias: "grow", required: false }] }] } });
75
+ args: [{ selector: 'mf-field', host: { class: 'block' }, template: "<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\" aria-hidden=\"true\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n <div aria-live=\"polite\" class=\"empty:hidden\">\r\n @if (error()) {\r\n <p\r\n [id]=\"errorId\"\r\n class=\"m-0 flex items-center gap-1 text-xs text-danger animate-fade-in\"\r\n [textContent]=\"error()\"\r\n ></p>\r\n } @else if (hint()) {\r\n <p [id]=\"hintId\" class=\"m-0 text-xs text-fg-subtle\" [textContent]=\"hint()\"></p>\r\n }\r\n </div>\r\n</div>\r\n" }]
76
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], grow: [{ type: i0.Input, args: [{ isSignal: true, alias: "grow", required: false }] }] } });
36
77
 
37
78
  /**
38
79
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"mesgflow-ui-kit-field.mjs","sources":["../../../projects/ui-kit/field/field.ts","../../../projects/ui-kit/field/field.html","../../../projects/ui-kit/field/mesgflow-ui-kit-field.ts"],"sourcesContent":["import { Component, input } from '@angular/core';\r\n\r\n/**\r\n * `<mf-field label=\"E-posta\" [error]=\"emailError()\">\r\n * <input mfInput type=\"email\" [id]=\"...\" />\r\n * </mf-field>`\r\n * `for`/`id` eşleşmesi (a11y) ÇAĞIRAN ekranın sorumluluğunda — bu component\r\n * label'ı otomatik bir input'a bağlamıyor (projected content'in id'sini bilmiyor).\r\n *\r\n * `error`, `string | undefined` kabul eder (sadece `string` DEĞİL) — çağıran\r\n * genelde `shared/forms/http-error.ts`'teki `resolveFieldError()`'ı doğrudan\r\n * bağlar, o da hata yokken `undefined` döner (boş string'e zorlamaya gerek yok).\r\n */\r\n@Component({\r\n selector: 'mf-field',\r\n host: { class: 'block' },\r\n templateUrl: './field.html',\r\n})\r\nexport class Field {\r\n readonly label = input<string>('');\r\n readonly for = input<string>('');\r\n readonly hint = input<string>('');\r\n readonly error = input<string | undefined>('');\r\n readonly required = input(false);\r\n /** `true` ise kalan yüksekliği doldurur (`flex-1 min-h-0`) — projected içeriğin kendi tek scroll alanı olduğu durumlar için (ör. uzun bir liste içeren form, bkz. `Dialog.fillBody`). */\r\n readonly grow = input(false);\r\n}\r\n","<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n @if (error()) {\r\n <p class=\"text-xs text-danger\">{{ error() }}</p>\r\n } @else if (hint()) {\r\n <p class=\"text-xs text-fg-subtle\">{{ hint() }}</p>\r\n }\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAEA;;;;;;;;;;AAUG;MAMU,KAAK,CAAA;IACP,KAAK,GAAG,KAAK,CAAS,EAAE;8EAAC;IACzB,GAAG,GAAG,KAAK,CAAS,EAAE;4EAAC;IACvB,IAAI,GAAG,KAAK,CAAS,EAAE;6EAAC;IACxB,KAAK,GAAG,KAAK,CAAqB,EAAE;8EAAC;IACrC,QAAQ,GAAG,KAAK,CAAC,KAAK;iFAAC;;IAEvB,IAAI,GAAG,KAAK,CAAC,KAAK;6EAAC;uGAPjB,KAAK,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAL,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAK,g0BClBlB,ohBAgBA,EAAA,CAAA;;2FDEa,KAAK,EAAA,UAAA,EAAA,CAAA;kBALjB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,UAAU,EAAA,IAAA,EACd,EAAE,KAAK,EAAE,OAAO,EAAE,EAAA,QAAA,EAAA,ohBAAA,EAAA;;;AEf1B;;AAEG;;"}
1
+ {"version":3,"file":"mesgflow-ui-kit-field.mjs","sources":["../../../projects/ui-kit/field/field.ts","../../../projects/ui-kit/field/field.html","../../../projects/ui-kit/field/mesgflow-ui-kit-field.ts"],"sourcesContent":["import { Component, ElementRef, afterRenderEffect, computed, inject, input } from '@angular/core';\r\nimport { nextId } from '@mesgflow/ui-kit/core';\r\n\r\n/**\r\n * `<mf-field label=\"E-posta\" for=\"email\" [error]=\"emailError()\" hint=\"İş adresiniz\">\r\n * <input mfInput id=\"email\" type=\"email\" />\r\n * </mf-field>`\r\n *\r\n * `for` verildiğinde alan, içindeki `#for` kontrolünü OTOMATİK bağlar:\r\n * - `aria-describedby` → ipucu/hata metninin id'si (kontrolün mevcut\r\n * describedby değerleri korunur, yalnızca kendi id'lerini ekler/çıkarır),\r\n * - `aria-invalid=\"true\"` → hata varken,\r\n * - `aria-required=\"true\"` → `required` iken.\r\n * Hata metni `role=\"alert\"` DEĞİL, `aria-live=\"polite\"` — her tuş vuruşunda\r\n * ekran okuyucuyu kesmesin. `error` `string | undefined` kabul eder.\r\n */\r\n@Component({\r\n selector: 'mf-field',\r\n host: { class: 'block' },\r\n templateUrl: './field.html',\r\n})\r\nexport class Field {\r\n readonly label = input<string>('');\r\n readonly for = input<string>('');\r\n readonly hint = input<string>('');\r\n readonly error = input<string | undefined>('');\r\n readonly required = input(false);\r\n /** `true` ise kalan yüksekliği doldurur (`flex-1 min-h-0`). */\r\n readonly grow = input(false);\r\n\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\r\n private readonly baseId = nextId('mf-field');\r\n protected readonly hintId = `${this.baseId}-hint`;\r\n protected readonly errorId = `${this.baseId}-error`;\r\n\r\n /** Şu an görünen açıklama metninin id'si (hata ipucunun yerine geçer). */\r\n protected readonly describedId = computed(() =>\r\n this.error() ? this.errorId : this.hint() ? this.hintId : null,\r\n );\r\n\r\n constructor() {\r\n afterRenderEffect(() => {\r\n const id = this.for();\r\n const described = this.describedId();\r\n const invalid = !!this.error();\r\n const required = this.required();\r\n if (!id) return;\r\n const control = this.host.nativeElement.querySelector<HTMLElement>(`#${CSS.escape(id)}`);\r\n if (!control) return;\r\n\r\n const own = new Set([this.hintId, this.errorId]);\r\n const ids = (control.getAttribute('aria-describedby') ?? '')\r\n .split(/\\s+/)\r\n .filter((x) => x && !own.has(x));\r\n if (described) ids.push(described);\r\n if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));\r\n else control.removeAttribute('aria-describedby');\r\n\r\n if (invalid) control.setAttribute('aria-invalid', 'true');\r\n else control.removeAttribute('aria-invalid');\r\n if (required) control.setAttribute('aria-required', 'true');\r\n else control.removeAttribute('aria-required');\r\n });\r\n }\r\n}\r\n","<div class=\"flex flex-col gap-1.5\" [class.min-h-0]=\"grow()\" [class.flex-1]=\"grow()\">\r\n @if (label()) {\r\n <label [attr.for]=\"for() || null\" class=\"text-label text-fg-muted\">\r\n {{ label() }}\r\n @if (required()) {\r\n <span class=\"text-danger\" aria-hidden=\"true\">*</span>\r\n }\r\n </label>\r\n }\r\n <ng-content />\r\n <div aria-live=\"polite\" class=\"empty:hidden\">\r\n @if (error()) {\r\n <p\r\n [id]=\"errorId\"\r\n class=\"m-0 flex items-center gap-1 text-xs text-danger animate-fade-in\"\r\n [textContent]=\"error()\"\r\n ></p>\r\n } @else if (hint()) {\r\n <p [id]=\"hintId\" class=\"m-0 text-xs text-fg-subtle\" [textContent]=\"hint()\"></p>\r\n }\r\n </div>\r\n</div>\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAGA;;;;;;;;;;;;AAYG;MAMU,KAAK,CAAA;IACP,KAAK,GAAG,KAAK,CAAS,EAAE;8EAAC;IACzB,GAAG,GAAG,KAAK,CAAS,EAAE;4EAAC;IACvB,IAAI,GAAG,KAAK,CAAS,EAAE;6EAAC;IACxB,KAAK,GAAG,KAAK,CAAqB,EAAE;8EAAC;IACrC,QAAQ,GAAG,KAAK,CAAC,KAAK;iFAAC;;IAEvB,IAAI,GAAG,KAAK,CAAC,KAAK;6EAAC;AAEX,IAAA,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC;AAClD,IAAA,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;AACzB,IAAA,MAAM,GAAG,CAAA,EAAG,IAAI,CAAC,MAAM,OAAO;AAC9B,IAAA,OAAO,GAAG,CAAA,EAAG,IAAI,CAAC,MAAM,QAAQ;;AAGhC,IAAA,WAAW,GAAG,QAAQ,CAAC,MACxC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI;oFAC/D;AAED,IAAA,WAAA,GAAA;QACE,iBAAiB,CAAC,MAAK;AACrB,YAAA,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;AACrB,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE;YACpC,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE;AAC9B,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;AAChC,YAAA,IAAI,CAAC,EAAE;gBAAE;AACT,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAc,CAAA,CAAA,EAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;AACxF,YAAA,IAAI,CAAC,OAAO;gBAAE;AAEd,YAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,IAAI,EAAE;iBACxD,KAAK,CAAC,KAAK;AACX,iBAAA,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAClC,YAAA,IAAI,SAAS;AAAE,gBAAA,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YAClC,IAAI,GAAG,CAAC,MAAM;AAAE,gBAAA,OAAO,CAAC,YAAY,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;AAClE,gBAAA,OAAO,CAAC,eAAe,CAAC,kBAAkB,CAAC;AAEhD,YAAA,IAAI,OAAO;AAAE,gBAAA,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC;;AACpD,gBAAA,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC;AAC5C,YAAA,IAAI,QAAQ;AAAE,gBAAA,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC;;AACtD,gBAAA,OAAO,CAAC,eAAe,CAAC,eAAe,CAAC;AAC/C,QAAA,CAAC,CAAC;IACJ;uGA1CW,KAAK,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAL,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAK,g0BCrBlB,4wBAsBA,EAAA,CAAA;;2FDDa,KAAK,EAAA,UAAA,EAAA,CAAA;kBALjB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,UAAU,EAAA,IAAA,EACd,EAAE,KAAK,EAAE,OAAO,EAAE,EAAA,QAAA,EAAA,4wBAAA,EAAA;;;AElB1B;;AAEG;;"}
@@ -0,0 +1,356 @@
1
+ import * as i0 from '@angular/core';
2
+ import { model, input, booleanAttribute, output, inject, computed, DestroyRef, Component } from '@angular/core';
3
+ import { Button } from '@mesgflow/ui-kit/button';
4
+ import { MF_FILE_UPLOAD_LABELS, nextId } from '@mesgflow/ui-kit/core';
5
+ import { Dropzone } from '@mesgflow/ui-kit/dropzone';
6
+ import { Icon } from '@mesgflow/ui-kit/icon';
7
+ import { Progress } from '@mesgflow/ui-kit/progress';
8
+
9
+ /** İnsan okunur boyut — birimler evrensel (B/KB/MB/GB), sayı biçimi yerele göre. */
10
+ function formatBytes(bytes, locale) {
11
+ const units = ['B', 'KB', 'MB', 'GB'];
12
+ let value = bytes;
13
+ let i = 0;
14
+ while (value >= 1024 && i < units.length - 1) {
15
+ value /= 1024;
16
+ i++;
17
+ }
18
+ const digits = i === 0 || value >= 10 ? 0 : 1;
19
+ return `${new Intl.NumberFormat(locale, { maximumFractionDigits: digits }).format(value)} ${units[i]}`;
20
+ }
21
+ const ICON_BY_TYPE = [
22
+ [/^image\//, 'image'],
23
+ [/^video\//, 'movie'],
24
+ [/^audio\//, 'music_note'],
25
+ [/pdf$/, 'picture_as_pdf'],
26
+ [/(zip|compressed|tar)/, 'folder_zip'],
27
+ ];
28
+ /**
29
+ * `<mf-file-upload [uploader]="upload" [(items)]="files" accept="image/*" multiple />`
30
+ *
31
+ * Sürükle-bırak alanı + dosya listesi. `uploader` verilirse bileşen yüklemeyi
32
+ * KENDİSİ yürütür: eşzamanlı en çok `concurrency` dosya, dosya başına ilerleme
33
+ * çubuğu, iptal (AbortSignal), hatada "Yeniden dene". Verilmezse yalnızca
34
+ * listeyi tutar (tüketici `items`'ı kendisi günceller).
35
+ *
36
+ * Her satırın durumu `aria-live` ile duyurulur; ilerleme `role="progressbar"`.
37
+ */
38
+ class FileUpload {
39
+ items = model([], /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
41
+ uploader = input(null, /* @ts-ignore */
42
+ ...(ngDevMode ? [{ debugName: "uploader" }] : /* istanbul ignore next */ []));
43
+ accept = input('', /* @ts-ignore */
44
+ ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
45
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
46
+ maxSizeBytes = input(null, /* @ts-ignore */
47
+ ...(ngDevMode ? [{ debugName: "maxSizeBytes" }] : /* istanbul ignore next */ []));
48
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
49
+ label = input(/* @ts-ignore */
50
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
51
+ /** Aynı anda yüklenecek en çok dosya. */
52
+ concurrency = input(3, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "concurrency" }] : /* istanbul ignore next */ []));
54
+ rejected = output();
55
+ removed = output();
56
+ /** Tüm kuyruk bittiğinde (başarılı + başarısız). */
57
+ settled = output();
58
+ labels = inject(MF_FILE_UPLOAD_LABELS);
59
+ controllers = new Map();
60
+ listLabel = computed(() => this.labels().files.replace('{count}', String(this.items().length)), /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "listLabel" }] : /* istanbul ignore next */ []));
62
+ constructor() {
63
+ inject(DestroyRef).onDestroy(() => this.controllers.forEach((c) => c.abort()));
64
+ }
65
+ size(bytes) {
66
+ return formatBytes(bytes);
67
+ }
68
+ iconFor(item) {
69
+ if (item.status === 'error')
70
+ return 'error';
71
+ if (item.status === 'done')
72
+ return 'check_circle';
73
+ return ICON_BY_TYPE.find(([re]) => re.test(item.file.type))?.[1] ?? 'description';
74
+ }
75
+ statusText(item) {
76
+ const l = this.labels();
77
+ switch (item.status) {
78
+ case 'queued':
79
+ return l.queued;
80
+ case 'uploading':
81
+ return `${l.uploading} ${item.progress}%`;
82
+ case 'done':
83
+ return l.done;
84
+ default:
85
+ return item.error || l.failed;
86
+ }
87
+ }
88
+ add(files) {
89
+ const next = files.map((file) => ({
90
+ id: nextId('mf-upload'),
91
+ file,
92
+ progress: 0,
93
+ status: this.uploader() ? 'queued' : 'done',
94
+ }));
95
+ this.items.update((list) => (this.multiple() ? [...list, ...next] : next));
96
+ this.pump();
97
+ }
98
+ retry(item) {
99
+ this.patch(item.id, { status: 'queued', progress: 0, error: undefined });
100
+ this.pump();
101
+ }
102
+ remove(item) {
103
+ this.controllers.get(item.id)?.abort();
104
+ this.controllers.delete(item.id);
105
+ this.items.update((list) => list.filter((i) => i.id !== item.id));
106
+ this.removed.emit(item);
107
+ this.pump();
108
+ }
109
+ /** Kuyruğu işletir — boş yuva kadar `queued` öğeyi başlatır. */
110
+ pump() {
111
+ const uploader = this.uploader();
112
+ if (!uploader)
113
+ return;
114
+ const active = this.items().filter((i) => i.status === 'uploading').length;
115
+ const waiting = this.items().filter((i) => i.status === 'queued');
116
+ for (const item of waiting.slice(0, Math.max(0, this.concurrency() - active))) {
117
+ this.start(item, uploader);
118
+ }
119
+ if (!active && !waiting.length && this.items().length)
120
+ this.settled.emit(this.items());
121
+ }
122
+ start(item, uploader) {
123
+ const controller = new AbortController();
124
+ this.controllers.set(item.id, controller);
125
+ this.patch(item.id, { status: 'uploading', progress: 0 });
126
+ uploader(item.file, (pct) => this.patch(item.id, { progress: Math.round(Math.min(100, Math.max(0, pct))) }), controller.signal)
127
+ .then(() => {
128
+ if (controller.signal.aborted)
129
+ return;
130
+ this.patch(item.id, { status: 'done', progress: 100 });
131
+ })
132
+ .catch((err) => {
133
+ if (controller.signal.aborted)
134
+ return;
135
+ this.patch(item.id, {
136
+ status: 'error',
137
+ error: err instanceof Error && err.message ? err.message : undefined,
138
+ });
139
+ })
140
+ .finally(() => {
141
+ this.controllers.delete(item.id);
142
+ if (!controller.signal.aborted)
143
+ this.pump();
144
+ });
145
+ }
146
+ patch(id, changes) {
147
+ this.items.update((list) => list.map((i) => (i.id === id ? { ...i, ...changes } : i)));
148
+ }
149
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: FileUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
150
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: FileUpload, isStandalone: true, selector: "mf-file-upload", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, uploader: { classPropertyName: "uploader", publicName: "uploader", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSizeBytes: { classPropertyName: "maxSizeBytes", publicName: "maxSizeBytes", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, concurrency: { classPropertyName: "concurrency", publicName: "concurrency", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { items: "itemsChange", rejected: "rejected", removed: "removed", settled: "settled" }, host: { classAttribute: "flex flex-col gap-3" }, exportAs: ["mfFileUpload"], ngImport: i0, template: `
151
+ <mf-dropzone
152
+ [accept]="accept()"
153
+ [multiple]="multiple()"
154
+ [maxSizeBytes]="maxSizeBytes()"
155
+ [disabled]="disabled()"
156
+ [label]="label()"
157
+ (filesSelected)="add($event)"
158
+ (rejected)="rejected.emit($event)"
159
+ />
160
+
161
+ @if (items().length) {
162
+ <ul
163
+ class="m-0 flex list-none flex-col divide-y divide-hairline overflow-hidden rounded-lg border border-hairline bg-surface p-0"
164
+ [attr.aria-label]="listLabel()"
165
+ >
166
+ @for (item of items(); track item.id) {
167
+ <li class="flex items-center gap-3 px-3 py-2.5 animate-list-in">
168
+ <span
169
+ class="flex h-9 w-9 shrink-0 items-center justify-center rounded-md"
170
+ [class]="
171
+ item.status === 'error'
172
+ ? 'bg-danger-tint text-danger'
173
+ : 'bg-surface-sunken text-fg-muted'
174
+ "
175
+ >
176
+ <span mfIcon [size]="18">{{ iconFor(item) }}</span>
177
+ </span>
178
+ <div class="min-w-0 flex-1">
179
+ <div class="flex items-baseline justify-between gap-3">
180
+ <p class="truncate text-sm font-medium text-fg">{{ item.file.name }}</p>
181
+ <span class="shrink-0 text-xs text-fg-subtle tabular-nums">{{
182
+ size(item.file.size)
183
+ }}</span>
184
+ </div>
185
+ @if (item.status === 'uploading' || item.status === 'queued') {
186
+ <mf-progress
187
+ class="mt-1.5"
188
+ size="sm"
189
+ [value]="item.progress"
190
+ [indeterminate]="item.status === 'queued'"
191
+ [ariaLabel]="item.file.name"
192
+ />
193
+ }
194
+ <p
195
+ class="mt-0.5 text-xs"
196
+ aria-live="polite"
197
+ [class]="
198
+ item.status === 'error'
199
+ ? 'text-danger'
200
+ : item.status === 'done'
201
+ ? 'text-success'
202
+ : 'text-fg-subtle'
203
+ "
204
+ >
205
+ {{ statusText(item) }}
206
+ </p>
207
+ </div>
208
+ <div class="flex shrink-0 items-center gap-1">
209
+ @if (item.status === 'error' && uploader()) {
210
+ <button mfButton size="xs" appearance="ghost" tone="neutral" (click)="retry(item)">
211
+ {{ labels().retry }}
212
+ </button>
213
+ }
214
+ @if (item.status === 'uploading' || item.status === 'queued') {
215
+ <button
216
+ mfButton
217
+ size="icon-sm"
218
+ shape="square"
219
+ appearance="ghost"
220
+ tone="neutral"
221
+ [attr.aria-label]="labels().cancel + ': ' + item.file.name"
222
+ (click)="remove(item)"
223
+ >
224
+ <span mfIcon [size]="16">close</span>
225
+ </button>
226
+ } @else {
227
+ <button
228
+ mfButton
229
+ size="icon-sm"
230
+ shape="square"
231
+ appearance="ghost"
232
+ tone="neutral"
233
+ [attr.aria-label]="labels().remove + ': ' + item.file.name"
234
+ (click)="remove(item)"
235
+ >
236
+ <span mfIcon [size]="16">delete</span>
237
+ </button>
238
+ }
239
+ </div>
240
+ </li>
241
+ }
242
+ </ul>
243
+ }
244
+ `, isInline: true, dependencies: [{ kind: "component", type: Dropzone, selector: "mf-dropzone", inputs: ["accept", "multiple", "maxSizeBytes", "disabled", "label"], outputs: ["filesSelected", "rejected"], exportAs: ["mfDropzone"] }, { kind: "component", type: Button, selector: "button[mfButton], a[mfButton]", inputs: ["tone", "appearance", "size", "shape", "type", "loading", "iconStart", "iconEnd"], exportAs: ["mfButton"] }, { kind: "directive", type: Icon, selector: "span[mfIcon]", inputs: ["size", "filled"] }, { kind: "component", type: Progress, selector: "mf-progress", inputs: ["value", "indeterminate", "size", "variant", "tone", "showValue", "ariaLabel"] }] });
245
+ }
246
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: FileUpload, decorators: [{
247
+ type: Component,
248
+ args: [{
249
+ selector: 'mf-file-upload',
250
+ exportAs: 'mfFileUpload',
251
+ imports: [Dropzone, Button, Icon, Progress],
252
+ host: { class: 'flex flex-col gap-3' },
253
+ template: `
254
+ <mf-dropzone
255
+ [accept]="accept()"
256
+ [multiple]="multiple()"
257
+ [maxSizeBytes]="maxSizeBytes()"
258
+ [disabled]="disabled()"
259
+ [label]="label()"
260
+ (filesSelected)="add($event)"
261
+ (rejected)="rejected.emit($event)"
262
+ />
263
+
264
+ @if (items().length) {
265
+ <ul
266
+ class="m-0 flex list-none flex-col divide-y divide-hairline overflow-hidden rounded-lg border border-hairline bg-surface p-0"
267
+ [attr.aria-label]="listLabel()"
268
+ >
269
+ @for (item of items(); track item.id) {
270
+ <li class="flex items-center gap-3 px-3 py-2.5 animate-list-in">
271
+ <span
272
+ class="flex h-9 w-9 shrink-0 items-center justify-center rounded-md"
273
+ [class]="
274
+ item.status === 'error'
275
+ ? 'bg-danger-tint text-danger'
276
+ : 'bg-surface-sunken text-fg-muted'
277
+ "
278
+ >
279
+ <span mfIcon [size]="18">{{ iconFor(item) }}</span>
280
+ </span>
281
+ <div class="min-w-0 flex-1">
282
+ <div class="flex items-baseline justify-between gap-3">
283
+ <p class="truncate text-sm font-medium text-fg">{{ item.file.name }}</p>
284
+ <span class="shrink-0 text-xs text-fg-subtle tabular-nums">{{
285
+ size(item.file.size)
286
+ }}</span>
287
+ </div>
288
+ @if (item.status === 'uploading' || item.status === 'queued') {
289
+ <mf-progress
290
+ class="mt-1.5"
291
+ size="sm"
292
+ [value]="item.progress"
293
+ [indeterminate]="item.status === 'queued'"
294
+ [ariaLabel]="item.file.name"
295
+ />
296
+ }
297
+ <p
298
+ class="mt-0.5 text-xs"
299
+ aria-live="polite"
300
+ [class]="
301
+ item.status === 'error'
302
+ ? 'text-danger'
303
+ : item.status === 'done'
304
+ ? 'text-success'
305
+ : 'text-fg-subtle'
306
+ "
307
+ >
308
+ {{ statusText(item) }}
309
+ </p>
310
+ </div>
311
+ <div class="flex shrink-0 items-center gap-1">
312
+ @if (item.status === 'error' && uploader()) {
313
+ <button mfButton size="xs" appearance="ghost" tone="neutral" (click)="retry(item)">
314
+ {{ labels().retry }}
315
+ </button>
316
+ }
317
+ @if (item.status === 'uploading' || item.status === 'queued') {
318
+ <button
319
+ mfButton
320
+ size="icon-sm"
321
+ shape="square"
322
+ appearance="ghost"
323
+ tone="neutral"
324
+ [attr.aria-label]="labels().cancel + ': ' + item.file.name"
325
+ (click)="remove(item)"
326
+ >
327
+ <span mfIcon [size]="16">close</span>
328
+ </button>
329
+ } @else {
330
+ <button
331
+ mfButton
332
+ size="icon-sm"
333
+ shape="square"
334
+ appearance="ghost"
335
+ tone="neutral"
336
+ [attr.aria-label]="labels().remove + ': ' + item.file.name"
337
+ (click)="remove(item)"
338
+ >
339
+ <span mfIcon [size]="16">delete</span>
340
+ </button>
341
+ }
342
+ </div>
343
+ </li>
344
+ }
345
+ </ul>
346
+ }
347
+ `,
348
+ }]
349
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], uploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploader", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSizeBytes: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSizeBytes", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], concurrency: [{ type: i0.Input, args: [{ isSignal: true, alias: "concurrency", required: false }] }], rejected: [{ type: i0.Output, args: ["rejected"] }], removed: [{ type: i0.Output, args: ["removed"] }], settled: [{ type: i0.Output, args: ["settled"] }] } });
350
+
351
+ /**
352
+ * Generated bundle index. Do not edit.
353
+ */
354
+
355
+ export { FileUpload, formatBytes };
356
+ //# sourceMappingURL=mesgflow-ui-kit-file-upload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesgflow-ui-kit-file-upload.mjs","sources":["../../../projects/ui-kit/file-upload/file-upload.ts","../../../projects/ui-kit/file-upload/mesgflow-ui-kit-file-upload.ts"],"sourcesContent":["import {\r\n Component,\r\n DestroyRef,\r\n booleanAttribute,\r\n computed,\r\n inject,\r\n input,\r\n model,\r\n output,\r\n} from '@angular/core';\r\nimport { Button } from '@mesgflow/ui-kit/button';\r\nimport { MF_FILE_UPLOAD_LABELS, nextId } from '@mesgflow/ui-kit/core';\r\nimport { Dropzone, type DropzoneRejection } from '@mesgflow/ui-kit/dropzone';\r\nimport { Icon } from '@mesgflow/ui-kit/icon';\r\nimport { Progress } from '@mesgflow/ui-kit/progress';\r\n\r\nexport type UploadStatus = 'queued' | 'uploading' | 'done' | 'error';\r\n\r\nexport interface UploadItem {\r\n id: string;\r\n file: File;\r\n /** 0–100 */\r\n progress: number;\r\n status: UploadStatus;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Yükleme işlevi — ilerlemeyi `report(0..100)` ile bildirir, iptal için\r\n * `signal`'i dinler; başarıda çözülür, hatada `Error` fırlatır.\r\n */\r\nexport type Uploader = (\r\n file: File,\r\n report: (percent: number) => void,\r\n signal: AbortSignal,\r\n) => Promise<void>;\r\n\r\n/** İnsan okunur boyut — birimler evrensel (B/KB/MB/GB), sayı biçimi yerele göre. */\r\nexport function formatBytes(bytes: number, locale?: string): string {\r\n const units = ['B', 'KB', 'MB', 'GB'];\r\n let value = bytes;\r\n let i = 0;\r\n while (value >= 1024 && i < units.length - 1) {\r\n value /= 1024;\r\n i++;\r\n }\r\n const digits = i === 0 || value >= 10 ? 0 : 1;\r\n return `${new Intl.NumberFormat(locale, { maximumFractionDigits: digits }).format(value)} ${units[i]}`;\r\n}\r\n\r\nconst ICON_BY_TYPE: [RegExp, string][] = [\r\n [/^image\\//, 'image'],\r\n [/^video\\//, 'movie'],\r\n [/^audio\\//, 'music_note'],\r\n [/pdf$/, 'picture_as_pdf'],\r\n [/(zip|compressed|tar)/, 'folder_zip'],\r\n];\r\n\r\n/**\r\n * `<mf-file-upload [uploader]=\"upload\" [(items)]=\"files\" accept=\"image/*\" multiple />`\r\n *\r\n * Sürükle-bırak alanı + dosya listesi. `uploader` verilirse bileşen yüklemeyi\r\n * KENDİSİ yürütür: eşzamanlı en çok `concurrency` dosya, dosya başına ilerleme\r\n * çubuğu, iptal (AbortSignal), hatada \"Yeniden dene\". Verilmezse yalnızca\r\n * listeyi tutar (tüketici `items`'ı kendisi günceller).\r\n *\r\n * Her satırın durumu `aria-live` ile duyurulur; ilerleme `role=\"progressbar\"`.\r\n */\r\n@Component({\r\n selector: 'mf-file-upload',\r\n exportAs: 'mfFileUpload',\r\n imports: [Dropzone, Button, Icon, Progress],\r\n host: { class: 'flex flex-col gap-3' },\r\n template: `\r\n <mf-dropzone\r\n [accept]=\"accept()\"\r\n [multiple]=\"multiple()\"\r\n [maxSizeBytes]=\"maxSizeBytes()\"\r\n [disabled]=\"disabled()\"\r\n [label]=\"label()\"\r\n (filesSelected)=\"add($event)\"\r\n (rejected)=\"rejected.emit($event)\"\r\n />\r\n\r\n @if (items().length) {\r\n <ul\r\n class=\"m-0 flex list-none flex-col divide-y divide-hairline overflow-hidden rounded-lg border border-hairline bg-surface p-0\"\r\n [attr.aria-label]=\"listLabel()\"\r\n >\r\n @for (item of items(); track item.id) {\r\n <li class=\"flex items-center gap-3 px-3 py-2.5 animate-list-in\">\r\n <span\r\n class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-md\"\r\n [class]=\"\r\n item.status === 'error'\r\n ? 'bg-danger-tint text-danger'\r\n : 'bg-surface-sunken text-fg-muted'\r\n \"\r\n >\r\n <span mfIcon [size]=\"18\">{{ iconFor(item) }}</span>\r\n </span>\r\n <div class=\"min-w-0 flex-1\">\r\n <div class=\"flex items-baseline justify-between gap-3\">\r\n <p class=\"truncate text-sm font-medium text-fg\">{{ item.file.name }}</p>\r\n <span class=\"shrink-0 text-xs text-fg-subtle tabular-nums\">{{\r\n size(item.file.size)\r\n }}</span>\r\n </div>\r\n @if (item.status === 'uploading' || item.status === 'queued') {\r\n <mf-progress\r\n class=\"mt-1.5\"\r\n size=\"sm\"\r\n [value]=\"item.progress\"\r\n [indeterminate]=\"item.status === 'queued'\"\r\n [ariaLabel]=\"item.file.name\"\r\n />\r\n }\r\n <p\r\n class=\"mt-0.5 text-xs\"\r\n aria-live=\"polite\"\r\n [class]=\"\r\n item.status === 'error'\r\n ? 'text-danger'\r\n : item.status === 'done'\r\n ? 'text-success'\r\n : 'text-fg-subtle'\r\n \"\r\n >\r\n {{ statusText(item) }}\r\n </p>\r\n </div>\r\n <div class=\"flex shrink-0 items-center gap-1\">\r\n @if (item.status === 'error' && uploader()) {\r\n <button mfButton size=\"xs\" appearance=\"ghost\" tone=\"neutral\" (click)=\"retry(item)\">\r\n {{ labels().retry }}\r\n </button>\r\n }\r\n @if (item.status === 'uploading' || item.status === 'queued') {\r\n <button\r\n mfButton\r\n size=\"icon-sm\"\r\n shape=\"square\"\r\n appearance=\"ghost\"\r\n tone=\"neutral\"\r\n [attr.aria-label]=\"labels().cancel + ': ' + item.file.name\"\r\n (click)=\"remove(item)\"\r\n >\r\n <span mfIcon [size]=\"16\">close</span>\r\n </button>\r\n } @else {\r\n <button\r\n mfButton\r\n size=\"icon-sm\"\r\n shape=\"square\"\r\n appearance=\"ghost\"\r\n tone=\"neutral\"\r\n [attr.aria-label]=\"labels().remove + ': ' + item.file.name\"\r\n (click)=\"remove(item)\"\r\n >\r\n <span mfIcon [size]=\"16\">delete</span>\r\n </button>\r\n }\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n }\r\n `,\r\n})\r\nexport class FileUpload {\r\n readonly items = model<UploadItem[]>([]);\r\n readonly uploader = input<Uploader | null>(null);\r\n readonly accept = input('');\r\n readonly multiple = input(false, { transform: booleanAttribute });\r\n readonly maxSizeBytes = input<number | null>(null);\r\n readonly disabled = input(false, { transform: booleanAttribute });\r\n readonly label = input<string>();\r\n /** Aynı anda yüklenecek en çok dosya. */\r\n readonly concurrency = input(3);\r\n\r\n readonly rejected = output<DropzoneRejection[]>();\r\n readonly removed = output<UploadItem>();\r\n /** Tüm kuyruk bittiğinde (başarılı + başarısız). */\r\n readonly settled = output<UploadItem[]>();\r\n\r\n protected readonly labels = inject(MF_FILE_UPLOAD_LABELS);\r\n private readonly controllers = new Map<string, AbortController>();\r\n\r\n protected readonly listLabel = computed(() =>\r\n this.labels().files.replace('{count}', String(this.items().length)),\r\n );\r\n\r\n constructor() {\r\n inject(DestroyRef).onDestroy(() => this.controllers.forEach((c) => c.abort()));\r\n }\r\n\r\n protected size(bytes: number): string {\r\n return formatBytes(bytes);\r\n }\r\n\r\n protected iconFor(item: UploadItem): string {\r\n if (item.status === 'error') return 'error';\r\n if (item.status === 'done') return 'check_circle';\r\n return ICON_BY_TYPE.find(([re]) => re.test(item.file.type))?.[1] ?? 'description';\r\n }\r\n\r\n protected statusText(item: UploadItem): string {\r\n const l = this.labels();\r\n switch (item.status) {\r\n case 'queued':\r\n return l.queued;\r\n case 'uploading':\r\n return `${l.uploading} ${item.progress}%`;\r\n case 'done':\r\n return l.done;\r\n default:\r\n return item.error || l.failed;\r\n }\r\n }\r\n\r\n protected add(files: File[]): void {\r\n const next = files.map<UploadItem>((file) => ({\r\n id: nextId('mf-upload'),\r\n file,\r\n progress: 0,\r\n status: this.uploader() ? 'queued' : 'done',\r\n }));\r\n this.items.update((list) => (this.multiple() ? [...list, ...next] : next));\r\n this.pump();\r\n }\r\n\r\n protected retry(item: UploadItem): void {\r\n this.patch(item.id, { status: 'queued', progress: 0, error: undefined });\r\n this.pump();\r\n }\r\n\r\n protected remove(item: UploadItem): void {\r\n this.controllers.get(item.id)?.abort();\r\n this.controllers.delete(item.id);\r\n this.items.update((list) => list.filter((i) => i.id !== item.id));\r\n this.removed.emit(item);\r\n this.pump();\r\n }\r\n\r\n /** Kuyruğu işletir — boş yuva kadar `queued` öğeyi başlatır. */\r\n private pump(): void {\r\n const uploader = this.uploader();\r\n if (!uploader) return;\r\n const active = this.items().filter((i) => i.status === 'uploading').length;\r\n const waiting = this.items().filter((i) => i.status === 'queued');\r\n for (const item of waiting.slice(0, Math.max(0, this.concurrency() - active))) {\r\n this.start(item, uploader);\r\n }\r\n if (!active && !waiting.length && this.items().length) this.settled.emit(this.items());\r\n }\r\n\r\n private start(item: UploadItem, uploader: Uploader): void {\r\n const controller = new AbortController();\r\n this.controllers.set(item.id, controller);\r\n this.patch(item.id, { status: 'uploading', progress: 0 });\r\n uploader(\r\n item.file,\r\n (pct) => this.patch(item.id, { progress: Math.round(Math.min(100, Math.max(0, pct))) }),\r\n controller.signal,\r\n )\r\n .then(() => {\r\n if (controller.signal.aborted) return;\r\n this.patch(item.id, { status: 'done', progress: 100 });\r\n })\r\n .catch((err: unknown) => {\r\n if (controller.signal.aborted) return;\r\n this.patch(item.id, {\r\n status: 'error',\r\n error: err instanceof Error && err.message ? err.message : undefined,\r\n });\r\n })\r\n .finally(() => {\r\n this.controllers.delete(item.id);\r\n if (!controller.signal.aborted) this.pump();\r\n });\r\n }\r\n\r\n private patch(id: string, changes: Partial<UploadItem>): void {\r\n this.items.update((list) => list.map((i) => (i.id === id ? { ...i, ...changes } : i)));\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}