@raintonic/formaui 0.2.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 (240) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +145 -0
  3. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +806 -0
  4. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -0
  5. package/fesm2022/raintonic-formaui-cdk-form-field.mjs +86 -0
  6. package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -0
  7. package/fesm2022/raintonic-formaui-cdk-overlay.mjs +1757 -0
  8. package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -0
  9. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +287 -0
  10. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -0
  11. package/fesm2022/raintonic-formaui-components-accordion.mjs +217 -0
  12. package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -0
  13. package/fesm2022/raintonic-formaui-components-alert.mjs +161 -0
  14. package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -0
  15. package/fesm2022/raintonic-formaui-components-autocomplete.mjs +726 -0
  16. package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -0
  17. package/fesm2022/raintonic-formaui-components-avatar.mjs +92 -0
  18. package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -0
  19. package/fesm2022/raintonic-formaui-components-badge.mjs +107 -0
  20. package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -0
  21. package/fesm2022/raintonic-formaui-components-big-menu.mjs +68 -0
  22. package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +1 -0
  23. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +55 -0
  24. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -0
  25. package/fesm2022/raintonic-formaui-components-button-group.mjs +103 -0
  26. package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -0
  27. package/fesm2022/raintonic-formaui-components-button.mjs +241 -0
  28. package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -0
  29. package/fesm2022/raintonic-formaui-components-card.mjs +270 -0
  30. package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -0
  31. package/fesm2022/raintonic-formaui-components-checkbox.mjs +295 -0
  32. package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -0
  33. package/fesm2022/raintonic-formaui-components-data-table.mjs +631 -0
  34. package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -0
  35. package/fesm2022/raintonic-formaui-components-date-picker.mjs +1331 -0
  36. package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -0
  37. package/fesm2022/raintonic-formaui-components-divider.mjs +41 -0
  38. package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -0
  39. package/fesm2022/raintonic-formaui-components-drawer.mjs +190 -0
  40. package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -0
  41. package/fesm2022/raintonic-formaui-components-dynamic-form.mjs +266 -0
  42. package/fesm2022/raintonic-formaui-components-dynamic-form.mjs.map +1 -0
  43. package/fesm2022/raintonic-formaui-components-empty-state.mjs +33 -0
  44. package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -0
  45. package/fesm2022/raintonic-formaui-components-file-upload.mjs +246 -0
  46. package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -0
  47. package/fesm2022/raintonic-formaui-components-form-field.mjs +482 -0
  48. package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -0
  49. package/fesm2022/raintonic-formaui-components-icon.mjs +117 -0
  50. package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -0
  51. package/fesm2022/raintonic-formaui-components-input.mjs +327 -0
  52. package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -0
  53. package/fesm2022/raintonic-formaui-components-list.mjs +149 -0
  54. package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -0
  55. package/fesm2022/raintonic-formaui-components-menu.mjs +896 -0
  56. package/fesm2022/raintonic-formaui-components-menu.mjs.map +1 -0
  57. package/fesm2022/raintonic-formaui-components-number-input.mjs +345 -0
  58. package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -0
  59. package/fesm2022/raintonic-formaui-components-paginator.mjs +139 -0
  60. package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -0
  61. package/fesm2022/raintonic-formaui-components-password-input.mjs +306 -0
  62. package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -0
  63. package/fesm2022/raintonic-formaui-components-popover.mjs +451 -0
  64. package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -0
  65. package/fesm2022/raintonic-formaui-components-progressbar.mjs +148 -0
  66. package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -0
  67. package/fesm2022/raintonic-formaui-components-radio.mjs +260 -0
  68. package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -0
  69. package/fesm2022/raintonic-formaui-components-select.mjs +1011 -0
  70. package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -0
  71. package/fesm2022/raintonic-formaui-components-side-panel.mjs +150 -0
  72. package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -0
  73. package/fesm2022/raintonic-formaui-components-sidebar.mjs +257 -0
  74. package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +1 -0
  75. package/fesm2022/raintonic-formaui-components-skeleton.mjs +50 -0
  76. package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -0
  77. package/fesm2022/raintonic-formaui-components-slider.mjs +347 -0
  78. package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -0
  79. package/fesm2022/raintonic-formaui-components-spinner.mjs +63 -0
  80. package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -0
  81. package/fesm2022/raintonic-formaui-components-stepper.mjs +317 -0
  82. package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -0
  83. package/fesm2022/raintonic-formaui-components-tab.mjs +197 -0
  84. package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -0
  85. package/fesm2022/raintonic-formaui-components-tag.mjs +78 -0
  86. package/fesm2022/raintonic-formaui-components-tag.mjs.map +1 -0
  87. package/fesm2022/raintonic-formaui-components-time-picker.mjs +644 -0
  88. package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -0
  89. package/fesm2022/raintonic-formaui-components-toggle.mjs +171 -0
  90. package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -0
  91. package/fesm2022/raintonic-formaui-components-toolbar.mjs +140 -0
  92. package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -0
  93. package/fesm2022/raintonic-formaui-components-tooltip.mjs +555 -0
  94. package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -0
  95. package/fesm2022/raintonic-formaui-components-tree-select.mjs +314 -0
  96. package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -0
  97. package/fesm2022/raintonic-formaui-components-tree-table.mjs +103 -0
  98. package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -0
  99. package/fesm2022/raintonic-formaui-components-tree.mjs +430 -0
  100. package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -0
  101. package/fesm2022/raintonic-formaui-core.mjs +62 -0
  102. package/fesm2022/raintonic-formaui-core.mjs.map +1 -0
  103. package/fesm2022/raintonic-formaui-services-dialog.mjs +798 -0
  104. package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -0
  105. package/fesm2022/raintonic-formaui-services-notification.mjs +391 -0
  106. package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -0
  107. package/fesm2022/raintonic-formaui-services-theme.mjs +248 -0
  108. package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -0
  109. package/fesm2022/raintonic-formaui-test-utils.mjs +66 -0
  110. package/fesm2022/raintonic-formaui-test-utils.mjs.map +1 -0
  111. package/fesm2022/raintonic-formaui.mjs +15 -0
  112. package/fesm2022/raintonic-formaui.mjs.map +1 -0
  113. package/llms-full.txt +1627 -0
  114. package/llms.txt +60 -0
  115. package/package.json +251 -0
  116. package/styles/_fonts-entry.scss +3 -0
  117. package/styles/fonts/dm-mono-400-latin.woff2 +0 -0
  118. package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
  119. package/styles/fonts/inter-tight-latin.woff2 +0 -0
  120. package/styles/index.scss +127 -0
  121. package/styles/partials/_constants.scss +29 -0
  122. package/styles/partials/_fonts.scss +36 -0
  123. package/styles/partials/_grid.scss +171 -0
  124. package/styles/partials/_mixins.scss +145 -0
  125. package/styles/partials/_motion.scss +252 -0
  126. package/styles/partials/_theme.scss +275 -0
  127. package/styles/partials/_typography.scss +112 -0
  128. package/styles/partials/_utilities.scss +480 -0
  129. package/styles/partials/themes/_dark.scss +254 -0
  130. package/styles/partials/themes/_light.scss +254 -0
  131. package/types/raintonic-formaui-cdk-drag-drop.d.ts +196 -0
  132. package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -0
  133. package/types/raintonic-formaui-cdk-form-field.d.ts +62 -0
  134. package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -0
  135. package/types/raintonic-formaui-cdk-overlay.d.ts +843 -0
  136. package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -0
  137. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +112 -0
  138. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -0
  139. package/types/raintonic-formaui-components-accordion.d.ts +124 -0
  140. package/types/raintonic-formaui-components-accordion.d.ts.map +1 -0
  141. package/types/raintonic-formaui-components-alert.d.ts +143 -0
  142. package/types/raintonic-formaui-components-alert.d.ts.map +1 -0
  143. package/types/raintonic-formaui-components-autocomplete.d.ts +193 -0
  144. package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -0
  145. package/types/raintonic-formaui-components-avatar.d.ts +52 -0
  146. package/types/raintonic-formaui-components-avatar.d.ts.map +1 -0
  147. package/types/raintonic-formaui-components-badge.d.ts +47 -0
  148. package/types/raintonic-formaui-components-badge.d.ts.map +1 -0
  149. package/types/raintonic-formaui-components-big-menu.d.ts +62 -0
  150. package/types/raintonic-formaui-components-big-menu.d.ts.map +1 -0
  151. package/types/raintonic-formaui-components-breadcrumb.d.ts +26 -0
  152. package/types/raintonic-formaui-components-breadcrumb.d.ts.map +1 -0
  153. package/types/raintonic-formaui-components-button-group.d.ts +61 -0
  154. package/types/raintonic-formaui-components-button-group.d.ts.map +1 -0
  155. package/types/raintonic-formaui-components-button.d.ts +116 -0
  156. package/types/raintonic-formaui-components-button.d.ts.map +1 -0
  157. package/types/raintonic-formaui-components-card.d.ts +191 -0
  158. package/types/raintonic-formaui-components-card.d.ts.map +1 -0
  159. package/types/raintonic-formaui-components-checkbox.d.ts +132 -0
  160. package/types/raintonic-formaui-components-checkbox.d.ts.map +1 -0
  161. package/types/raintonic-formaui-components-data-table.d.ts +368 -0
  162. package/types/raintonic-formaui-components-data-table.d.ts.map +1 -0
  163. package/types/raintonic-formaui-components-date-picker.d.ts +341 -0
  164. package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -0
  165. package/types/raintonic-formaui-components-divider.d.ts +21 -0
  166. package/types/raintonic-formaui-components-divider.d.ts.map +1 -0
  167. package/types/raintonic-formaui-components-drawer.d.ts +48 -0
  168. package/types/raintonic-formaui-components-drawer.d.ts.map +1 -0
  169. package/types/raintonic-formaui-components-dynamic-form.d.ts +412 -0
  170. package/types/raintonic-formaui-components-dynamic-form.d.ts.map +1 -0
  171. package/types/raintonic-formaui-components-empty-state.d.ts +14 -0
  172. package/types/raintonic-formaui-components-empty-state.d.ts.map +1 -0
  173. package/types/raintonic-formaui-components-file-upload.d.ts +77 -0
  174. package/types/raintonic-formaui-components-file-upload.d.ts.map +1 -0
  175. package/types/raintonic-formaui-components-form-field.d.ts +271 -0
  176. package/types/raintonic-formaui-components-form-field.d.ts.map +1 -0
  177. package/types/raintonic-formaui-components-icon.d.ts +61 -0
  178. package/types/raintonic-formaui-components-icon.d.ts.map +1 -0
  179. package/types/raintonic-formaui-components-input.d.ts +149 -0
  180. package/types/raintonic-formaui-components-input.d.ts.map +1 -0
  181. package/types/raintonic-formaui-components-list.d.ts +48 -0
  182. package/types/raintonic-formaui-components-list.d.ts.map +1 -0
  183. package/types/raintonic-formaui-components-menu.d.ts +403 -0
  184. package/types/raintonic-formaui-components-menu.d.ts.map +1 -0
  185. package/types/raintonic-formaui-components-number-input.d.ts +127 -0
  186. package/types/raintonic-formaui-components-number-input.d.ts.map +1 -0
  187. package/types/raintonic-formaui-components-paginator.d.ts +37 -0
  188. package/types/raintonic-formaui-components-paginator.d.ts.map +1 -0
  189. package/types/raintonic-formaui-components-password-input.d.ts +111 -0
  190. package/types/raintonic-formaui-components-password-input.d.ts.map +1 -0
  191. package/types/raintonic-formaui-components-popover.d.ts +131 -0
  192. package/types/raintonic-formaui-components-popover.d.ts.map +1 -0
  193. package/types/raintonic-formaui-components-progressbar.d.ts +111 -0
  194. package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -0
  195. package/types/raintonic-formaui-components-radio.d.ts +95 -0
  196. package/types/raintonic-formaui-components-radio.d.ts.map +1 -0
  197. package/types/raintonic-formaui-components-select.d.ts +307 -0
  198. package/types/raintonic-formaui-components-select.d.ts.map +1 -0
  199. package/types/raintonic-formaui-components-side-panel.d.ts +51 -0
  200. package/types/raintonic-formaui-components-side-panel.d.ts.map +1 -0
  201. package/types/raintonic-formaui-components-sidebar.d.ts +174 -0
  202. package/types/raintonic-formaui-components-sidebar.d.ts.map +1 -0
  203. package/types/raintonic-formaui-components-skeleton.d.ts +20 -0
  204. package/types/raintonic-formaui-components-skeleton.d.ts.map +1 -0
  205. package/types/raintonic-formaui-components-slider.d.ts +108 -0
  206. package/types/raintonic-formaui-components-slider.d.ts.map +1 -0
  207. package/types/raintonic-formaui-components-spinner.d.ts +42 -0
  208. package/types/raintonic-formaui-components-spinner.d.ts.map +1 -0
  209. package/types/raintonic-formaui-components-stepper.d.ts +126 -0
  210. package/types/raintonic-formaui-components-stepper.d.ts.map +1 -0
  211. package/types/raintonic-formaui-components-tab.d.ts +96 -0
  212. package/types/raintonic-formaui-components-tab.d.ts.map +1 -0
  213. package/types/raintonic-formaui-components-tag.d.ts +34 -0
  214. package/types/raintonic-formaui-components-tag.d.ts.map +1 -0
  215. package/types/raintonic-formaui-components-time-picker.d.ts +172 -0
  216. package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -0
  217. package/types/raintonic-formaui-components-toggle.d.ts +70 -0
  218. package/types/raintonic-formaui-components-toggle.d.ts.map +1 -0
  219. package/types/raintonic-formaui-components-toolbar.d.ts +128 -0
  220. package/types/raintonic-formaui-components-toolbar.d.ts.map +1 -0
  221. package/types/raintonic-formaui-components-tooltip.d.ts +268 -0
  222. package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -0
  223. package/types/raintonic-formaui-components-tree-select.d.ts +80 -0
  224. package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -0
  225. package/types/raintonic-formaui-components-tree-table.d.ts +90 -0
  226. package/types/raintonic-formaui-components-tree-table.d.ts.map +1 -0
  227. package/types/raintonic-formaui-components-tree.d.ts +104 -0
  228. package/types/raintonic-formaui-components-tree.d.ts.map +1 -0
  229. package/types/raintonic-formaui-core.d.ts +115 -0
  230. package/types/raintonic-formaui-core.d.ts.map +1 -0
  231. package/types/raintonic-formaui-services-dialog.d.ts +451 -0
  232. package/types/raintonic-formaui-services-dialog.d.ts.map +1 -0
  233. package/types/raintonic-formaui-services-notification.d.ts +221 -0
  234. package/types/raintonic-formaui-services-notification.d.ts.map +1 -0
  235. package/types/raintonic-formaui-services-theme.d.ts +126 -0
  236. package/types/raintonic-formaui-services-theme.d.ts.map +1 -0
  237. package/types/raintonic-formaui-test-utils.d.ts +24 -0
  238. package/types/raintonic-formaui-test-utils.d.ts.map +1 -0
  239. package/types/raintonic-formaui.d.ts +4 -0
  240. package/types/raintonic-formaui.d.ts.map +1 -0
@@ -0,0 +1,246 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, booleanAttribute, output, signal, viewChild, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { FuiIconComponent } from '@raintonic/formaui/components/icon';
4
+
5
+ /**
6
+ * # FuiFileUploadComponent
7
+ *
8
+ * A file upload component with drag-and-drop support, file previews, and validation.
9
+ *
10
+ * ## Features
11
+ * - Drag & drop file upload
12
+ * - Click to browse
13
+ * - File type, size, and count validation
14
+ * - Image preview thumbnails
15
+ * - Accessible (keyboard, ARIA)
16
+ *
17
+ * ## Usage
18
+ *
19
+ * ### Basic
20
+ * ```html
21
+ * <fui-file-upload
22
+ * accept="image/*,.pdf"
23
+ * [multiple]="true"
24
+ * [maxFileSize]="5242880"
25
+ * (filesSelected)="onFiles($event)"
26
+ * ></fui-file-upload>
27
+ * ```
28
+ */
29
+ class FuiFileUploadComponent {
30
+ // Inputs
31
+ accept = input('', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
32
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
33
+ maxFileSize = input(0, ...(ngDevMode ? [{ debugName: "maxFileSize" }] : /* istanbul ignore next */ []));
34
+ maxFiles = input(0, ...(ngDevMode ? [{ debugName: "maxFiles" }] : /* istanbul ignore next */ []));
35
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
36
+ showPreview = input(true, { ...(ngDevMode ? { debugName: "showPreview" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
37
+ // Outputs
38
+ filesSelected = output();
39
+ fileRemoved = output();
40
+ filesDropped = output();
41
+ validationErrors = output();
42
+ // Internal state
43
+ _files = signal([], ...(ngDevMode ? [{ debugName: "_files" }] : /* istanbul ignore next */ []));
44
+ _isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "_isDragOver" }] : /* istanbul ignore next */ []));
45
+ // ViewChild
46
+ fileInput = viewChild('fileInput', ...(ngDevMode ? [{ debugName: "fileInput" }] : /* istanbul ignore next */ []));
47
+ // Public methods
48
+ browse() {
49
+ if (this.disabled())
50
+ return;
51
+ this.fileInput()?.nativeElement.click();
52
+ }
53
+ removeFile(index) {
54
+ if (this.disabled())
55
+ return;
56
+ const files = [...this._files()];
57
+ const removed = files.splice(index, 1)[0];
58
+ if (removed) {
59
+ if (removed.previewUrl) {
60
+ URL.revokeObjectURL(removed.previewUrl);
61
+ }
62
+ this._files.set(files);
63
+ this.fileRemoved.emit(removed);
64
+ }
65
+ }
66
+ clearAll() {
67
+ const files = this._files();
68
+ for (const f of files) {
69
+ if (f.previewUrl) {
70
+ URL.revokeObjectURL(f.previewUrl);
71
+ }
72
+ }
73
+ this._files.set([]);
74
+ }
75
+ getFiles() {
76
+ return this._files();
77
+ }
78
+ // Template event handlers
79
+ _onFileInputChange(event) {
80
+ const input = event.target;
81
+ if (input.files && input.files.length > 0) {
82
+ this._processFiles(input.files);
83
+ }
84
+ // Reset input so the same file can be selected again
85
+ input.value = '';
86
+ }
87
+ _onDragEnter(event) {
88
+ event.preventDefault();
89
+ event.stopPropagation();
90
+ if (!this.disabled()) {
91
+ this._isDragOver.set(true);
92
+ }
93
+ }
94
+ _onDragOver(event) {
95
+ event.preventDefault();
96
+ event.stopPropagation();
97
+ if (!this.disabled()) {
98
+ this._isDragOver.set(true);
99
+ }
100
+ }
101
+ _onDragLeave(event) {
102
+ event.preventDefault();
103
+ event.stopPropagation();
104
+ this._isDragOver.set(false);
105
+ }
106
+ _onDrop(event) {
107
+ event.preventDefault();
108
+ event.stopPropagation();
109
+ this._isDragOver.set(false);
110
+ if (this.disabled())
111
+ return;
112
+ const files = event.dataTransfer?.files;
113
+ if (files && files.length > 0) {
114
+ this._processFiles(files);
115
+ this.filesDropped.emit(this._files());
116
+ }
117
+ }
118
+ _formatFileSize(bytes) {
119
+ if (bytes === 0)
120
+ return '0 B';
121
+ const units = ['B', 'KB', 'MB', 'GB'];
122
+ const k = 1024;
123
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
124
+ const size = parseFloat((bytes / Math.pow(k, i)).toFixed(1));
125
+ return `${size} ${units[i]}`;
126
+ }
127
+ // Private methods
128
+ _processFiles(fileList) {
129
+ const newFiles = [];
130
+ const errors = [];
131
+ const currentFiles = this._files();
132
+ const filesToProcess = Array.from(fileList);
133
+ for (const file of filesToProcess) {
134
+ // Max files check
135
+ const maxFiles = this.maxFiles();
136
+ if (maxFiles > 0 && currentFiles.length + newFiles.length >= maxFiles) {
137
+ errors.push({
138
+ file,
139
+ error: 'maxFiles',
140
+ message: `Maximum ${maxFiles} file(s) allowed`,
141
+ });
142
+ continue;
143
+ }
144
+ const validationError = this._validateFile(file);
145
+ if (validationError) {
146
+ errors.push(validationError);
147
+ continue;
148
+ }
149
+ const uploadFile = {
150
+ file,
151
+ name: file.name,
152
+ size: file.size,
153
+ type: file.type,
154
+ };
155
+ if (this.showPreview() && this._isImage(file)) {
156
+ uploadFile.previewUrl = this._generatePreview(file);
157
+ }
158
+ newFiles.push(uploadFile);
159
+ }
160
+ if (errors.length > 0) {
161
+ this.validationErrors.emit(errors);
162
+ }
163
+ if (newFiles.length > 0) {
164
+ if (this.multiple()) {
165
+ this._files.set([...currentFiles, ...newFiles]);
166
+ }
167
+ else {
168
+ // Single mode: replace existing
169
+ for (const f of currentFiles) {
170
+ if (f.previewUrl)
171
+ URL.revokeObjectURL(f.previewUrl);
172
+ }
173
+ this._files.set([newFiles[0]]);
174
+ }
175
+ this.filesSelected.emit(this._files());
176
+ }
177
+ }
178
+ _validateFile(file) {
179
+ // Type check
180
+ const accept = this.accept();
181
+ if (accept && !this._matchesAccept(file, accept)) {
182
+ return {
183
+ file,
184
+ error: 'type',
185
+ message: `File type "${file.type || file.name.split('.').pop()}" is not allowed`,
186
+ };
187
+ }
188
+ // Size check
189
+ const maxSize = this.maxFileSize();
190
+ if (maxSize > 0 && file.size > maxSize) {
191
+ return {
192
+ file,
193
+ error: 'size',
194
+ message: `File size ${this._formatFileSize(file.size)} exceeds maximum ${this._formatFileSize(maxSize)}`,
195
+ };
196
+ }
197
+ return null;
198
+ }
199
+ _matchesAccept(file, accept) {
200
+ const acceptTypes = accept.split(',').map((t) => t.trim().toLowerCase());
201
+ for (const acceptType of acceptTypes) {
202
+ // Wildcard MIME type (e.g. "image/*")
203
+ if (acceptType.endsWith('/*')) {
204
+ const category = acceptType.slice(0, acceptType.indexOf('/'));
205
+ if (file.type.toLowerCase().startsWith(category + '/')) {
206
+ return true;
207
+ }
208
+ }
209
+ // Extension match (e.g. ".pdf")
210
+ else if (acceptType.startsWith('.')) {
211
+ if (file.name.toLowerCase().endsWith(acceptType)) {
212
+ return true;
213
+ }
214
+ }
215
+ // Exact MIME match
216
+ else if (file.type.toLowerCase() === acceptType) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+ _isImage(file) {
223
+ return file.type.startsWith('image/');
224
+ }
225
+ _generatePreview(file) {
226
+ return URL.createObjectURL(file);
227
+ }
228
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiFileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
229
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiFileUploadComponent, isStandalone: true, selector: "fui-file-upload", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showPreview: { classPropertyName: "showPreview", publicName: "showPreview", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filesSelected: "filesSelected", fileRemoved: "fileRemoved", filesDropped: "filesDropped", validationErrors: "validationErrors" }, host: { properties: { "class.fui-file-upload--disabled": "disabled()", "class.fui-file-upload--drag-over": "_isDragOver()", "class.fui-file-upload--has-files": "_files().length > 0" }, classAttribute: "fui-file-upload" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #fileInput\n type=\"file\"\n class=\"fui-file-upload__native-input\"\n [attr.accept]=\"accept()\"\n [attr.multiple]=\"multiple() ? '' : null\"\n [attr.disabled]=\"disabled() ? '' : null\"\n (change)=\"_onFileInputChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n/>\n\n<div\n class=\"fui-file-upload__dropzone\"\n (click)=\"browse()\"\n (dragenter)=\"_onDragEnter($event)\"\n (dragover)=\"_onDragOver($event)\"\n (dragleave)=\"_onDragLeave($event)\"\n (drop)=\"_onDrop($event)\"\n (keydown.enter)=\"browse()\"\n (keydown.space)=\"browse(); $event.preventDefault()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n role=\"button\"\n [attr.aria-label]=\"'Drop files here or click to browse'\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <fui-icon name=\"upload-simple\" size=\"lg\" class=\"fui-file-upload__icon\"></fui-icon>\n <span class=\"fui-file-upload__text\"> Drag &amp; drop files here or <strong>browse</strong> </span>\n @if (accept()) {\n <span class=\"fui-file-upload__hint\">Accepted: {{ accept() }}</span>\n }\n @if (maxFileSize() > 0) {\n <span class=\"fui-file-upload__hint\">Max size: {{ _formatFileSize(maxFileSize()) }}</span>\n }\n</div>\n\n@if (_files().length > 0) {\n <ul class=\"fui-file-upload__file-list\" role=\"list\">\n @for (fileItem of _files(); track fileItem.name; let i = $index) {\n <li class=\"fui-file-upload__file-item\" [class.fui-file-upload__file-item--error]=\"fileItem.error\">\n @if (showPreview() && fileItem.previewUrl) {\n <img [src]=\"fileItem.previewUrl\" class=\"fui-file-upload__preview\" [alt]=\"fileItem.name\" />\n } @else {\n <fui-icon name=\"file\" size=\"sm\" class=\"fui-file-upload__file-icon\"></fui-icon>\n }\n <div class=\"fui-file-upload__file-info\">\n <span class=\"fui-file-upload__file-name\">{{ fileItem.name }}</span>\n <span class=\"fui-file-upload__file-size\">{{ _formatFileSize(fileItem.size) }}</span>\n @if (fileItem.error) {\n <span class=\"fui-file-upload__file-error\">{{ fileItem.error }}</span>\n }\n </div>\n <button\n type=\"button\"\n class=\"fui-file-upload__remove-btn\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"'Remove ' + fileItem.name\"\n [disabled]=\"disabled()\"\n >\n <fui-icon name=\"x\" size=\"sm\"></fui-icon>\n </button>\n </li>\n }\n </ul>\n}\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-file-upload{display:block;width:100%}.fui-file-upload__native-input{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.fui-file-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--fui-gap-8);padding:var(--fui-padding-32, 2rem);border:2px dashed var(--fui-border-color);border-radius:var(--fui-border-radius-md);background-color:var(--fui-surface-bg);cursor:pointer;text-align:center;transition:border-color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__dropzone:hover{border-color:var(--fui-primary-60, var(--fui-primary));background-color:var(--fui-primary-10, rgba(124, 58, 237, .04))}.fui-file-upload__dropzone:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-file-upload__icon{color:var(--fui-text-secondary)}.fui-file-upload__text{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);color:var(--fui-text-secondary)}.fui-file-upload__text strong{color:var(--fui-primary);font-weight:var(--fui-font-weight-semibold, 600)}.fui-file-upload__hint{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-text-disabled)}.fui-file-upload__file-list{list-style:none;margin:var(--fui-gap-12, .75rem) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--fui-gap-4)}.fui-file-upload__file-item{display:flex;align-items:center;gap:var(--fui-gap-12, .75rem);padding:var(--fui-padding-8, .5rem) var(--fui-padding-12, .75rem);border:1px solid var(--fui-border-color);border-radius:var(--fui-border-radius-sm);background-color:var(--fui-surface-card, var(--fui-surface-01));transition:border-color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__file-item--error{border-color:var(--fui-state-error);background-color:var(--fui-danger-10, rgba(239, 68, 68, .04))}.fui-file-upload__preview{width:48px;height:48px;object-fit:cover;border-radius:var(--fui-border-radius-sm);flex-shrink:0}.fui-file-upload__file-icon{flex-shrink:0;color:var(--fui-text-secondary)}.fui-file-upload__file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.fui-file-upload__file-name{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-medium, 500);color:var(--fui-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fui-file-upload__file-size{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-text-secondary)}.fui-file-upload__file-error{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-state-error)}.fui-file-upload__remove-btn{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-file-upload__remove-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.5rem;height:1.5rem;border-radius:var(--fui-border-radius-sm);color:var(--fui-text-secondary);opacity:.7;transition:opacity,color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__remove-btn:hover:not(:disabled){opacity:1;color:var(--fui-state-error)}.fui-file-upload__remove-btn:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-file-upload__remove-btn:disabled{opacity:.4;cursor:not-allowed}.fui-file-upload--drag-over .fui-file-upload__dropzone{border-color:var(--fui-primary);border-style:solid;background-color:var(--fui-primary-10, rgba(124, 58, 237, .08))}.fui-file-upload--disabled{opacity:var(--fui-opacity-disabled, .5)}.fui-file-upload--disabled .fui-file-upload__dropzone{cursor:not-allowed;pointer-events:none}.fui-file-upload--disabled .fui-file-upload__remove-btn{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.fui-file-upload__dropzone,.fui-file-upload__file-item,.fui-file-upload__remove-btn{transition:none}}\n"], dependencies: [{ kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
230
+ }
231
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiFileUploadComponent, decorators: [{
232
+ type: Component,
233
+ args: [{ selector: 'fui-file-upload', standalone: true, imports: [FuiIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
234
+ class: 'fui-file-upload',
235
+ '[class.fui-file-upload--disabled]': 'disabled()',
236
+ '[class.fui-file-upload--drag-over]': '_isDragOver()',
237
+ '[class.fui-file-upload--has-files]': '_files().length > 0',
238
+ }, template: "<input\n #fileInput\n type=\"file\"\n class=\"fui-file-upload__native-input\"\n [attr.accept]=\"accept()\"\n [attr.multiple]=\"multiple() ? '' : null\"\n [attr.disabled]=\"disabled() ? '' : null\"\n (change)=\"_onFileInputChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n/>\n\n<div\n class=\"fui-file-upload__dropzone\"\n (click)=\"browse()\"\n (dragenter)=\"_onDragEnter($event)\"\n (dragover)=\"_onDragOver($event)\"\n (dragleave)=\"_onDragLeave($event)\"\n (drop)=\"_onDrop($event)\"\n (keydown.enter)=\"browse()\"\n (keydown.space)=\"browse(); $event.preventDefault()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n role=\"button\"\n [attr.aria-label]=\"'Drop files here or click to browse'\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <fui-icon name=\"upload-simple\" size=\"lg\" class=\"fui-file-upload__icon\"></fui-icon>\n <span class=\"fui-file-upload__text\"> Drag &amp; drop files here or <strong>browse</strong> </span>\n @if (accept()) {\n <span class=\"fui-file-upload__hint\">Accepted: {{ accept() }}</span>\n }\n @if (maxFileSize() > 0) {\n <span class=\"fui-file-upload__hint\">Max size: {{ _formatFileSize(maxFileSize()) }}</span>\n }\n</div>\n\n@if (_files().length > 0) {\n <ul class=\"fui-file-upload__file-list\" role=\"list\">\n @for (fileItem of _files(); track fileItem.name; let i = $index) {\n <li class=\"fui-file-upload__file-item\" [class.fui-file-upload__file-item--error]=\"fileItem.error\">\n @if (showPreview() && fileItem.previewUrl) {\n <img [src]=\"fileItem.previewUrl\" class=\"fui-file-upload__preview\" [alt]=\"fileItem.name\" />\n } @else {\n <fui-icon name=\"file\" size=\"sm\" class=\"fui-file-upload__file-icon\"></fui-icon>\n }\n <div class=\"fui-file-upload__file-info\">\n <span class=\"fui-file-upload__file-name\">{{ fileItem.name }}</span>\n <span class=\"fui-file-upload__file-size\">{{ _formatFileSize(fileItem.size) }}</span>\n @if (fileItem.error) {\n <span class=\"fui-file-upload__file-error\">{{ fileItem.error }}</span>\n }\n </div>\n <button\n type=\"button\"\n class=\"fui-file-upload__remove-btn\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"'Remove ' + fileItem.name\"\n [disabled]=\"disabled()\"\n >\n <fui-icon name=\"x\" size=\"sm\"></fui-icon>\n </button>\n </li>\n }\n </ul>\n}\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-file-upload{display:block;width:100%}.fui-file-upload__native-input{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.fui-file-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--fui-gap-8);padding:var(--fui-padding-32, 2rem);border:2px dashed var(--fui-border-color);border-radius:var(--fui-border-radius-md);background-color:var(--fui-surface-bg);cursor:pointer;text-align:center;transition:border-color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__dropzone:hover{border-color:var(--fui-primary-60, var(--fui-primary));background-color:var(--fui-primary-10, rgba(124, 58, 237, .04))}.fui-file-upload__dropzone:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-file-upload__icon{color:var(--fui-text-secondary)}.fui-file-upload__text{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);color:var(--fui-text-secondary)}.fui-file-upload__text strong{color:var(--fui-primary);font-weight:var(--fui-font-weight-semibold, 600)}.fui-file-upload__hint{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-text-disabled)}.fui-file-upload__file-list{list-style:none;margin:var(--fui-gap-12, .75rem) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--fui-gap-4)}.fui-file-upload__file-item{display:flex;align-items:center;gap:var(--fui-gap-12, .75rem);padding:var(--fui-padding-8, .5rem) var(--fui-padding-12, .75rem);border:1px solid var(--fui-border-color);border-radius:var(--fui-border-radius-sm);background-color:var(--fui-surface-card, var(--fui-surface-01));transition:border-color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__file-item--error{border-color:var(--fui-state-error);background-color:var(--fui-danger-10, rgba(239, 68, 68, .04))}.fui-file-upload__preview{width:48px;height:48px;object-fit:cover;border-radius:var(--fui-border-radius-sm);flex-shrink:0}.fui-file-upload__file-icon{flex-shrink:0;color:var(--fui-text-secondary)}.fui-file-upload__file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.fui-file-upload__file-name{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-medium, 500);color:var(--fui-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fui-file-upload__file-size{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-text-secondary)}.fui-file-upload__file-error{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-01);color:var(--fui-state-error)}.fui-file-upload__remove-btn{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-file-upload__remove-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:1.5rem;height:1.5rem;border-radius:var(--fui-border-radius-sm);color:var(--fui-text-secondary);opacity:.7;transition:opacity,color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-file-upload__remove-btn:hover:not(:disabled){opacity:1;color:var(--fui-state-error)}.fui-file-upload__remove-btn:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-file-upload__remove-btn:disabled{opacity:.4;cursor:not-allowed}.fui-file-upload--drag-over .fui-file-upload__dropzone{border-color:var(--fui-primary);border-style:solid;background-color:var(--fui-primary-10, rgba(124, 58, 237, .08))}.fui-file-upload--disabled{opacity:var(--fui-opacity-disabled, .5)}.fui-file-upload--disabled .fui-file-upload__dropzone{cursor:not-allowed;pointer-events:none}.fui-file-upload--disabled .fui-file-upload__remove-btn{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.fui-file-upload__dropzone,.fui-file-upload__file-item,.fui-file-upload__remove-btn{transition:none}}\n"] }]
239
+ }], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreview", required: false }] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], fileRemoved: [{ type: i0.Output, args: ["fileRemoved"] }], filesDropped: [{ type: i0.Output, args: ["filesDropped"] }], validationErrors: [{ type: i0.Output, args: ["validationErrors"] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
240
+
241
+ /**
242
+ * Generated bundle index. Do not edit.
243
+ */
244
+
245
+ export { FuiFileUploadComponent };
246
+ //# sourceMappingURL=raintonic-formaui-components-file-upload.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raintonic-formaui-components-file-upload.mjs","sources":["../../../lib/components/file-upload/file-upload.component.ts","../../../lib/components/file-upload/file-upload.component.html","../../../lib/components/file-upload/raintonic-formaui-components-file-upload.ts"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n input,\n output,\n signal,\n viewChild,\n ElementRef,\n WritableSignal,\n booleanAttribute,\n} from '@angular/core';\n\nimport { FuiIconComponent } from '@raintonic/formaui/components/icon';\nimport { FileUploadFile, FileUploadValidationError } from './file-upload.types';\n\n/**\n * # FuiFileUploadComponent\n *\n * A file upload component with drag-and-drop support, file previews, and validation.\n *\n * ## Features\n * - Drag & drop file upload\n * - Click to browse\n * - File type, size, and count validation\n * - Image preview thumbnails\n * - Accessible (keyboard, ARIA)\n *\n * ## Usage\n *\n * ### Basic\n * ```html\n * <fui-file-upload\n * accept=\"image/*,.pdf\"\n * [multiple]=\"true\"\n * [maxFileSize]=\"5242880\"\n * (filesSelected)=\"onFiles($event)\"\n * ></fui-file-upload>\n * ```\n */\n@Component({\n selector: 'fui-file-upload',\n standalone: true,\n imports: [FuiIconComponent],\n templateUrl: './file-upload.component.html',\n styleUrls: ['./file-upload.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'fui-file-upload',\n '[class.fui-file-upload--disabled]': 'disabled()',\n '[class.fui-file-upload--drag-over]': '_isDragOver()',\n '[class.fui-file-upload--has-files]': '_files().length > 0',\n },\n})\nexport class FuiFileUploadComponent {\n // Inputs\n readonly accept = input('');\n readonly multiple = input<boolean, unknown>(false, { transform: booleanAttribute });\n readonly maxFileSize = input(0);\n readonly maxFiles = input(0);\n readonly disabled = input<boolean, unknown>(false, { transform: booleanAttribute });\n readonly showPreview = input<boolean, unknown>(true, { transform: booleanAttribute });\n\n // Outputs\n readonly filesSelected = output<FileUploadFile[]>();\n readonly fileRemoved = output<FileUploadFile>();\n readonly filesDropped = output<FileUploadFile[]>();\n readonly validationErrors = output<FileUploadValidationError[]>();\n\n // Internal state\n readonly _files: WritableSignal<FileUploadFile[]> = signal([]);\n readonly _isDragOver: WritableSignal<boolean> = signal(false);\n\n // ViewChild\n readonly fileInput = viewChild<ElementRef<HTMLInputElement>>('fileInput');\n\n // Public methods\n browse(): void {\n if (this.disabled()) return;\n this.fileInput()?.nativeElement.click();\n }\n\n removeFile(index: number): void {\n if (this.disabled()) return;\n const files = [...this._files()];\n const removed = files.splice(index, 1)[0];\n if (removed) {\n if (removed.previewUrl) {\n URL.revokeObjectURL(removed.previewUrl);\n }\n this._files.set(files);\n this.fileRemoved.emit(removed);\n }\n }\n\n clearAll(): void {\n const files = this._files();\n for (const f of files) {\n if (f.previewUrl) {\n URL.revokeObjectURL(f.previewUrl);\n }\n }\n this._files.set([]);\n }\n\n getFiles(): FileUploadFile[] {\n return this._files();\n }\n\n // Template event handlers\n _onFileInputChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files && input.files.length > 0) {\n this._processFiles(input.files);\n }\n // Reset input so the same file can be selected again\n input.value = '';\n }\n\n _onDragEnter(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n if (!this.disabled()) {\n this._isDragOver.set(true);\n }\n }\n\n _onDragOver(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n if (!this.disabled()) {\n this._isDragOver.set(true);\n }\n }\n\n _onDragLeave(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this._isDragOver.set(false);\n }\n\n _onDrop(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this._isDragOver.set(false);\n\n if (this.disabled()) return;\n\n const files = event.dataTransfer?.files;\n if (files && files.length > 0) {\n this._processFiles(files);\n this.filesDropped.emit(this._files());\n }\n }\n\n _formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const k = 1024;\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const size = parseFloat((bytes / Math.pow(k, i)).toFixed(1));\n return `${size} ${units[i]}`;\n }\n\n // Private methods\n private _processFiles(fileList: FileList): void {\n const newFiles: FileUploadFile[] = [];\n const errors: FileUploadValidationError[] = [];\n const currentFiles = this._files();\n\n const filesToProcess = Array.from(fileList);\n\n for (const file of filesToProcess) {\n // Max files check\n const maxFiles = this.maxFiles();\n if (maxFiles > 0 && currentFiles.length + newFiles.length >= maxFiles) {\n errors.push({\n file,\n error: 'maxFiles',\n message: `Maximum ${maxFiles} file(s) allowed`,\n });\n continue;\n }\n\n const validationError = this._validateFile(file);\n if (validationError) {\n errors.push(validationError);\n continue;\n }\n\n const uploadFile: FileUploadFile = {\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n };\n\n if (this.showPreview() && this._isImage(file)) {\n uploadFile.previewUrl = this._generatePreview(file);\n }\n\n newFiles.push(uploadFile);\n }\n\n if (errors.length > 0) {\n this.validationErrors.emit(errors);\n }\n\n if (newFiles.length > 0) {\n if (this.multiple()) {\n this._files.set([...currentFiles, ...newFiles]);\n } else {\n // Single mode: replace existing\n for (const f of currentFiles) {\n if (f.previewUrl) URL.revokeObjectURL(f.previewUrl);\n }\n this._files.set([newFiles[0]]);\n }\n this.filesSelected.emit(this._files());\n }\n }\n\n private _validateFile(file: File): FileUploadValidationError | null {\n // Type check\n const accept = this.accept();\n if (accept && !this._matchesAccept(file, accept)) {\n return {\n file,\n error: 'type',\n message: `File type \"${file.type || file.name.split('.').pop()}\" is not allowed`,\n };\n }\n\n // Size check\n const maxSize = this.maxFileSize();\n if (maxSize > 0 && file.size > maxSize) {\n return {\n file,\n error: 'size',\n message: `File size ${this._formatFileSize(file.size)} exceeds maximum ${this._formatFileSize(maxSize)}`,\n };\n }\n\n return null;\n }\n\n private _matchesAccept(file: File, accept: string): boolean {\n const acceptTypes = accept.split(',').map((t) => t.trim().toLowerCase());\n\n for (const acceptType of acceptTypes) {\n // Wildcard MIME type (e.g. \"image/*\")\n if (acceptType.endsWith('/*')) {\n const category = acceptType.slice(0, acceptType.indexOf('/'));\n if (file.type.toLowerCase().startsWith(category + '/')) {\n return true;\n }\n }\n // Extension match (e.g. \".pdf\")\n else if (acceptType.startsWith('.')) {\n if (file.name.toLowerCase().endsWith(acceptType)) {\n return true;\n }\n }\n // Exact MIME match\n else if (file.type.toLowerCase() === acceptType) {\n return true;\n }\n }\n\n return false;\n }\n\n private _isImage(file: File): boolean {\n return file.type.startsWith('image/');\n }\n\n private _generatePreview(file: File): string {\n return URL.createObjectURL(file);\n }\n}\n","<input\n #fileInput\n type=\"file\"\n class=\"fui-file-upload__native-input\"\n [attr.accept]=\"accept()\"\n [attr.multiple]=\"multiple() ? '' : null\"\n [attr.disabled]=\"disabled() ? '' : null\"\n (change)=\"_onFileInputChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n/>\n\n<div\n class=\"fui-file-upload__dropzone\"\n (click)=\"browse()\"\n (dragenter)=\"_onDragEnter($event)\"\n (dragover)=\"_onDragOver($event)\"\n (dragleave)=\"_onDragLeave($event)\"\n (drop)=\"_onDrop($event)\"\n (keydown.enter)=\"browse()\"\n (keydown.space)=\"browse(); $event.preventDefault()\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n role=\"button\"\n [attr.aria-label]=\"'Drop files here or click to browse'\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <fui-icon name=\"upload-simple\" size=\"lg\" class=\"fui-file-upload__icon\"></fui-icon>\n <span class=\"fui-file-upload__text\"> Drag &amp; drop files here or <strong>browse</strong> </span>\n @if (accept()) {\n <span class=\"fui-file-upload__hint\">Accepted: {{ accept() }}</span>\n }\n @if (maxFileSize() > 0) {\n <span class=\"fui-file-upload__hint\">Max size: {{ _formatFileSize(maxFileSize()) }}</span>\n }\n</div>\n\n@if (_files().length > 0) {\n <ul class=\"fui-file-upload__file-list\" role=\"list\">\n @for (fileItem of _files(); track fileItem.name; let i = $index) {\n <li class=\"fui-file-upload__file-item\" [class.fui-file-upload__file-item--error]=\"fileItem.error\">\n @if (showPreview() && fileItem.previewUrl) {\n <img [src]=\"fileItem.previewUrl\" class=\"fui-file-upload__preview\" [alt]=\"fileItem.name\" />\n } @else {\n <fui-icon name=\"file\" size=\"sm\" class=\"fui-file-upload__file-icon\"></fui-icon>\n }\n <div class=\"fui-file-upload__file-info\">\n <span class=\"fui-file-upload__file-name\">{{ fileItem.name }}</span>\n <span class=\"fui-file-upload__file-size\">{{ _formatFileSize(fileItem.size) }}</span>\n @if (fileItem.error) {\n <span class=\"fui-file-upload__file-error\">{{ fileItem.error }}</span>\n }\n </div>\n <button\n type=\"button\"\n class=\"fui-file-upload__remove-btn\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"'Remove ' + fileItem.name\"\n [disabled]=\"disabled()\"\n >\n <fui-icon name=\"x\" size=\"sm\"></fui-icon>\n </button>\n </li>\n }\n </ul>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}