@refraktor/core 0.0.4 → 0.0.5

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 (334) hide show
  1. package/build/components/progress-circle/progress-circle.d.ts.map +1 -1
  2. package/build/components/progress-circle/progress-circle.js +6 -6
  3. package/build/components/progress-circle/progress-circle.styles.d.ts +2 -7
  4. package/build/components/progress-circle/progress-circle.styles.d.ts.map +1 -1
  5. package/build/components/progress-circle/progress-circle.styles.js +2 -23
  6. package/build/components/progress-circle/progress-circle.types.d.ts +4 -3
  7. package/build/components/progress-circle/progress-circle.types.d.ts.map +1 -1
  8. package/build/components/slider/slider.d.ts.map +1 -1
  9. package/build/components/slider/slider.js +69 -20
  10. package/build/components/tooltip/tooltip.js +1 -1
  11. package/build/style.css +2 -2
  12. package/package.json +30 -3
  13. package/.turbo/turbo-build.log +0 -4
  14. package/refraktor-core-0.0.1-alpha.0.tgz +0 -0
  15. package/src/components/accordion/accordion-control/accordion-control.tsx +0 -156
  16. package/src/components/accordion/accordion-control/index.ts +0 -1
  17. package/src/components/accordion/accordion-item/accordion-item.tsx +0 -40
  18. package/src/components/accordion/accordion-item/index.ts +0 -1
  19. package/src/components/accordion/accordion-panel/accordion-panel.tsx +0 -58
  20. package/src/components/accordion/accordion-panel/index.ts +0 -1
  21. package/src/components/accordion/accordion.context.ts +0 -93
  22. package/src/components/accordion/accordion.styles.ts +0 -60
  23. package/src/components/accordion/accordion.test.tsx +0 -174
  24. package/src/components/accordion/accordion.tsx +0 -203
  25. package/src/components/accordion/accordion.types.ts +0 -126
  26. package/src/components/accordion/index.ts +0 -17
  27. package/src/components/avatar/avatar-group/avatar-group.tsx +0 -73
  28. package/src/components/avatar/avatar-group/index.ts +0 -1
  29. package/src/components/avatar/avatar.styles.ts +0 -11
  30. package/src/components/avatar/avatar.tsx +0 -142
  31. package/src/components/avatar/avatar.types.ts +0 -86
  32. package/src/components/avatar/index.ts +0 -8
  33. package/src/components/badge/badge.styles.ts +0 -21
  34. package/src/components/badge/badge.tsx +0 -62
  35. package/src/components/badge/badge.types.ts +0 -40
  36. package/src/components/badge/index.ts +0 -2
  37. package/src/components/breadcrumbs/breadcrumbs.styles.ts +0 -55
  38. package/src/components/breadcrumbs/breadcrumbs.test.tsx +0 -136
  39. package/src/components/breadcrumbs/breadcrumbs.tsx +0 -199
  40. package/src/components/breadcrumbs/breadcrumbs.types.ts +0 -78
  41. package/src/components/breadcrumbs/breadcrumbs.utils.ts +0 -70
  42. package/src/components/breadcrumbs/index.ts +0 -6
  43. package/src/components/button/button.styles.ts +0 -28
  44. package/src/components/button/button.test.tsx +0 -57
  45. package/src/components/button/button.tsx +0 -110
  46. package/src/components/button/button.types.ts +0 -69
  47. package/src/components/button/index.ts +0 -2
  48. package/src/components/checkbox/checkbox-group/checkbox-group.tsx +0 -93
  49. package/src/components/checkbox/checkbox-group/index.ts +0 -1
  50. package/src/components/checkbox/checkbox.context.ts +0 -14
  51. package/src/components/checkbox/checkbox.styles.ts +0 -49
  52. package/src/components/checkbox/checkbox.test.tsx +0 -80
  53. package/src/components/checkbox/checkbox.tsx +0 -192
  54. package/src/components/checkbox/checkbox.types.ts +0 -125
  55. package/src/components/checkbox/index.ts +0 -10
  56. package/src/components/chip/chip-group/chip-group.tsx +0 -107
  57. package/src/components/chip/chip-group/index.ts +0 -1
  58. package/src/components/chip/chip.context.ts +0 -15
  59. package/src/components/chip/chip.styles.ts +0 -36
  60. package/src/components/chip/chip.test.tsx +0 -197
  61. package/src/components/chip/chip.tsx +0 -208
  62. package/src/components/chip/chip.types.ts +0 -134
  63. package/src/components/chip/index.ts +0 -10
  64. package/src/components/color-swatch/color-swatch.styles.ts +0 -11
  65. package/src/components/color-swatch/color-swatch.test.tsx +0 -80
  66. package/src/components/color-swatch/color-swatch.tsx +0 -84
  67. package/src/components/color-swatch/color-swatch.types.ts +0 -46
  68. package/src/components/color-swatch/index.ts +0 -2
  69. package/src/components/drawer/drawer-body/drawer-body.tsx +0 -29
  70. package/src/components/drawer/drawer-body/index.ts +0 -1
  71. package/src/components/drawer/drawer-close/drawer-close.tsx +0 -43
  72. package/src/components/drawer/drawer-close/index.ts +0 -1
  73. package/src/components/drawer/drawer-content/drawer-content.tsx +0 -135
  74. package/src/components/drawer/drawer-content/index.ts +0 -1
  75. package/src/components/drawer/drawer-header/drawer-header.tsx +0 -40
  76. package/src/components/drawer/drawer-header/index.ts +0 -1
  77. package/src/components/drawer/drawer-overlay/drawer-overlay.tsx +0 -87
  78. package/src/components/drawer/drawer-overlay/index.ts +0 -1
  79. package/src/components/drawer/drawer-root/drawer-root.tsx +0 -93
  80. package/src/components/drawer/drawer-root/index.ts +0 -1
  81. package/src/components/drawer/drawer.context.ts +0 -26
  82. package/src/components/drawer/drawer.styles.ts +0 -32
  83. package/src/components/drawer/drawer.test.tsx +0 -274
  84. package/src/components/drawer/drawer.tsx +0 -58
  85. package/src/components/drawer/drawer.types.ts +0 -192
  86. package/src/components/drawer/index.ts +0 -18
  87. package/src/components/drawer/use-drawer.ts +0 -94
  88. package/src/components/file-input/file-input.test.tsx +0 -134
  89. package/src/components/file-input/file-input.tsx +0 -224
  90. package/src/components/file-input/file-input.types.ts +0 -78
  91. package/src/components/file-input/file-input.utils.test.ts +0 -36
  92. package/src/components/file-input/file-input.utils.ts +0 -130
  93. package/src/components/file-input/index.ts +0 -2
  94. package/src/components/for/for.test.tsx +0 -66
  95. package/src/components/for/for.tsx +0 -53
  96. package/src/components/for/for.types.ts +0 -40
  97. package/src/components/for/index.ts +0 -2
  98. package/src/components/index.ts +0 -35
  99. package/src/components/input/index.ts +0 -2
  100. package/src/components/input/input-description/index.ts +0 -2
  101. package/src/components/input/input-description/input-description.tsx +0 -36
  102. package/src/components/input/input-error/index.ts +0 -2
  103. package/src/components/input/input-error/input-error.tsx +0 -36
  104. package/src/components/input/input-field/index.ts +0 -1
  105. package/src/components/input/input-field/input-field.styles.ts +0 -23
  106. package/src/components/input/input-field/input-field.tsx +0 -94
  107. package/src/components/input/input-label/index.ts +0 -2
  108. package/src/components/input/input-label/input-label.tsx +0 -47
  109. package/src/components/input/input-wrapper/index.ts +0 -2
  110. package/src/components/input/input-wrapper/input-wrapper.tsx +0 -43
  111. package/src/components/input/input.tsx +0 -71
  112. package/src/components/input/input.types.ts +0 -111
  113. package/src/components/loader/index.ts +0 -2
  114. package/src/components/loader/loader.tsx +0 -89
  115. package/src/components/loader/loader.types.ts +0 -33
  116. package/src/components/menu/index.ts +0 -24
  117. package/src/components/menu/menu-dropdown/index.ts +0 -1
  118. package/src/components/menu/menu-dropdown/menu-dropdown.tsx +0 -220
  119. package/src/components/menu/menu-item/index.ts +0 -1
  120. package/src/components/menu/menu-item/menu-item.tsx +0 -126
  121. package/src/components/menu/menu-label/index.ts +0 -1
  122. package/src/components/menu/menu-label/menu-label.tsx +0 -30
  123. package/src/components/menu/menu-separator/index.ts +0 -1
  124. package/src/components/menu/menu-separator/menu-separator.tsx +0 -28
  125. package/src/components/menu/menu-sub/index.ts +0 -1
  126. package/src/components/menu/menu-sub/menu-sub.tsx +0 -117
  127. package/src/components/menu/menu-sub-dropdown/index.ts +0 -1
  128. package/src/components/menu/menu-sub-dropdown/menu-sub-dropdown.tsx +0 -221
  129. package/src/components/menu/menu-sub-trigger/index.ts +0 -1
  130. package/src/components/menu/menu-sub-trigger/menu-sub-trigger.tsx +0 -164
  131. package/src/components/menu/menu-trigger/index.ts +0 -1
  132. package/src/components/menu/menu-trigger/menu-trigger.tsx +0 -98
  133. package/src/components/menu/menu.context.ts +0 -162
  134. package/src/components/menu/menu.test.tsx +0 -136
  135. package/src/components/menu/menu.tsx +0 -133
  136. package/src/components/menu/menu.types.ts +0 -291
  137. package/src/components/menu/use-menu.ts +0 -204
  138. package/src/components/modal/index.ts +0 -18
  139. package/src/components/modal/modal-body/index.ts +0 -1
  140. package/src/components/modal/modal-body/modal-body.tsx +0 -29
  141. package/src/components/modal/modal-close/index.ts +0 -1
  142. package/src/components/modal/modal-close/modal-close.tsx +0 -43
  143. package/src/components/modal/modal-content/index.ts +0 -1
  144. package/src/components/modal/modal-content/modal-content.tsx +0 -119
  145. package/src/components/modal/modal-header/index.ts +0 -1
  146. package/src/components/modal/modal-header/modal-header.tsx +0 -40
  147. package/src/components/modal/modal-overlay/index.ts +0 -1
  148. package/src/components/modal/modal-overlay/modal-overlay.tsx +0 -87
  149. package/src/components/modal/modal-root/index.ts +0 -1
  150. package/src/components/modal/modal-root/modal-root.tsx +0 -93
  151. package/src/components/modal/modal.context.ts +0 -26
  152. package/src/components/modal/modal.test.tsx +0 -316
  153. package/src/components/modal/modal.tsx +0 -58
  154. package/src/components/modal/modal.types.ts +0 -189
  155. package/src/components/modal/use-modal.ts +0 -94
  156. package/src/components/number-input/index.ts +0 -2
  157. package/src/components/number-input/number-input.styles.ts +0 -37
  158. package/src/components/number-input/number-input.test.tsx +0 -22
  159. package/src/components/number-input/number-input.tsx +0 -381
  160. package/src/components/number-input/number-input.types.ts +0 -58
  161. package/src/components/pagination/index.ts +0 -7
  162. package/src/components/pagination/pagination.styles.ts +0 -84
  163. package/src/components/pagination/pagination.test.tsx +0 -117
  164. package/src/components/pagination/pagination.tsx +0 -371
  165. package/src/components/pagination/pagination.types.ts +0 -95
  166. package/src/components/pagination/pagination.utils.ts +0 -88
  167. package/src/components/password-input/index.ts +0 -2
  168. package/src/components/password-input/password-input.test.tsx +0 -72
  169. package/src/components/password-input/password-input.tsx +0 -85
  170. package/src/components/password-input/password-input.types.ts +0 -30
  171. package/src/components/pin-input/index.ts +0 -2
  172. package/src/components/pin-input/pin-input.test.tsx +0 -149
  173. package/src/components/pin-input/pin-input.tsx +0 -473
  174. package/src/components/pin-input/pin-input.types.ts +0 -78
  175. package/src/components/popover/index.ts +0 -12
  176. package/src/components/popover/popover-dropdown/index.ts +0 -1
  177. package/src/components/popover/popover-dropdown/popover-dropdown.tsx +0 -94
  178. package/src/components/popover/popover-trigger/index.ts +0 -1
  179. package/src/components/popover/popover-trigger/popover-trigger.tsx +0 -49
  180. package/src/components/popover/popover.context.ts +0 -20
  181. package/src/components/popover/popover.tsx +0 -113
  182. package/src/components/popover/popover.types.ts +0 -137
  183. package/src/components/popover/use-popover.ts +0 -219
  184. package/src/components/portal/index.ts +0 -2
  185. package/src/components/portal/portal.test.tsx +0 -39
  186. package/src/components/portal/portal.tsx +0 -104
  187. package/src/components/portal/portal.types.ts +0 -31
  188. package/src/components/progress/index.ts +0 -2
  189. package/src/components/progress/progress.styles.ts +0 -25
  190. package/src/components/progress/progress.test.tsx +0 -107
  191. package/src/components/progress/progress.tsx +0 -93
  192. package/src/components/progress/progress.types.ts +0 -52
  193. package/src/components/progress-circle/index.ts +0 -5
  194. package/src/components/progress-circle/progress-circle.styles.ts +0 -31
  195. package/src/components/progress-circle/progress-circle.test.tsx +0 -140
  196. package/src/components/progress-circle/progress-circle.tsx +0 -124
  197. package/src/components/progress-circle/progress-circle.types.ts +0 -52
  198. package/src/components/radio/index.ts +0 -10
  199. package/src/components/radio/radio-group/index.ts +0 -1
  200. package/src/components/radio/radio-group/radio-group.tsx +0 -89
  201. package/src/components/radio/radio.context.ts +0 -14
  202. package/src/components/radio/radio.styles.ts +0 -49
  203. package/src/components/radio/radio.test.tsx +0 -75
  204. package/src/components/radio/radio.tsx +0 -173
  205. package/src/components/radio/radio.types.ts +0 -126
  206. package/src/components/scroll-area/index.ts +0 -6
  207. package/src/components/scroll-area/scroll-area.test.tsx +0 -72
  208. package/src/components/scroll-area/scroll-area.tsx +0 -70
  209. package/src/components/scroll-area/scroll-area.types.ts +0 -37
  210. package/src/components/segmented-control/index.ts +0 -6
  211. package/src/components/segmented-control/segmented-control.styles.ts +0 -37
  212. package/src/components/segmented-control/segmented-control.test.tsx +0 -170
  213. package/src/components/segmented-control/segmented-control.tsx +0 -255
  214. package/src/components/segmented-control/segmented-control.types.ts +0 -78
  215. package/src/components/select/index.ts +0 -20
  216. package/src/components/select/select-dropdown/index.ts +0 -1
  217. package/src/components/select/select-dropdown/select-dropdown.tsx +0 -299
  218. package/src/components/select/select-group/index.ts +0 -1
  219. package/src/components/select/select-group/select-group.tsx +0 -47
  220. package/src/components/select/select-item/index.ts +0 -1
  221. package/src/components/select/select-item/select-item.tsx +0 -157
  222. package/src/components/select/select-root/index.ts +0 -1
  223. package/src/components/select/select-root/select-root.tsx +0 -333
  224. package/src/components/select/select-trigger/index.ts +0 -1
  225. package/src/components/select/select-trigger/select-trigger.tsx +0 -123
  226. package/src/components/select/select.context.ts +0 -140
  227. package/src/components/select/select.test.tsx +0 -190
  228. package/src/components/select/select.tsx +0 -82
  229. package/src/components/select/select.types.ts +0 -272
  230. package/src/components/select/use-select.ts +0 -170
  231. package/src/components/slider/index.ts +0 -7
  232. package/src/components/slider/slider.styles.ts +0 -37
  233. package/src/components/slider/slider.tsx +0 -275
  234. package/src/components/slider/slider.types.ts +0 -82
  235. package/src/components/switch/index.ts +0 -2
  236. package/src/components/switch/switch.styles.ts +0 -31
  237. package/src/components/switch/switch.tsx +0 -132
  238. package/src/components/switch/switch.types.ts +0 -62
  239. package/src/components/table/index.ts +0 -24
  240. package/src/components/table/table-body/index.ts +0 -1
  241. package/src/components/table/table-body/table-body.tsx +0 -37
  242. package/src/components/table/table-caption/index.ts +0 -1
  243. package/src/components/table/table-caption/table-caption.tsx +0 -32
  244. package/src/components/table/table-cell/index.ts +0 -1
  245. package/src/components/table/table-cell/table-cell.tsx +0 -33
  246. package/src/components/table/table-head/index.ts +0 -1
  247. package/src/components/table/table-head/table-head.tsx +0 -29
  248. package/src/components/table/table-header-cell/index.ts +0 -1
  249. package/src/components/table/table-header-cell/table-header-cell.tsx +0 -33
  250. package/src/components/table/table-row/index.ts +0 -1
  251. package/src/components/table/table-row/table-row.tsx +0 -30
  252. package/src/components/table/table.context.ts +0 -18
  253. package/src/components/table/table.styles.ts +0 -62
  254. package/src/components/table/table.test.tsx +0 -145
  255. package/src/components/table/table.tsx +0 -91
  256. package/src/components/table/table.types.ts +0 -145
  257. package/src/components/tabs/index.ts +0 -18
  258. package/src/components/tabs/tabs-list/index.ts +0 -1
  259. package/src/components/tabs/tabs-list/tabs-list.tsx +0 -42
  260. package/src/components/tabs/tabs-panel/index.ts +0 -1
  261. package/src/components/tabs/tabs-panel/tabs-panel.tsx +0 -58
  262. package/src/components/tabs/tabs-tab/index.ts +0 -1
  263. package/src/components/tabs/tabs-tab/tabs-tab.tsx +0 -210
  264. package/src/components/tabs/tabs.context.ts +0 -88
  265. package/src/components/tabs/tabs.styles.ts +0 -92
  266. package/src/components/tabs/tabs.test.tsx +0 -167
  267. package/src/components/tabs/tabs.tsx +0 -137
  268. package/src/components/tabs/tabs.types.ts +0 -128
  269. package/src/components/textarea/index.ts +0 -2
  270. package/src/components/textarea/textarea-field/index.ts +0 -1
  271. package/src/components/textarea/textarea-field/textarea-field.styles.ts +0 -34
  272. package/src/components/textarea/textarea-field/textarea-field.tsx +0 -96
  273. package/src/components/textarea/textarea.tsx +0 -64
  274. package/src/components/textarea/textarea.types.ts +0 -80
  275. package/src/components/timeline/index.ts +0 -11
  276. package/src/components/timeline/timeline-item/index.ts +0 -1
  277. package/src/components/timeline/timeline-item/timeline-item.tsx +0 -158
  278. package/src/components/timeline/timeline.context.ts +0 -21
  279. package/src/components/timeline/timeline.styles.ts +0 -135
  280. package/src/components/timeline/timeline.test.tsx +0 -150
  281. package/src/components/timeline/timeline.tsx +0 -124
  282. package/src/components/timeline/timeline.types.ts +0 -91
  283. package/src/components/tooltip/index.ts +0 -8
  284. package/src/components/tooltip/tooltip.tsx +0 -164
  285. package/src/components/tooltip/tooltip.types.ts +0 -104
  286. package/src/components/tooltip/use-tooltip.ts +0 -200
  287. package/src/components/transition/index.ts +0 -11
  288. package/src/components/transition/transition.test.tsx +0 -126
  289. package/src/components/transition/transition.tsx +0 -441
  290. package/src/components/transition/transition.types.ts +0 -97
  291. package/src/components/transition/transitions.ts +0 -140
  292. package/src/icons/check.tsx +0 -25
  293. package/src/icons/chevron.tsx +0 -46
  294. package/src/icons/eye-off.tsx +0 -30
  295. package/src/icons/eye.tsx +0 -24
  296. package/src/icons/index.ts +0 -7
  297. package/src/icons/minus.tsx +0 -24
  298. package/src/icons/types.ts +0 -9
  299. package/src/icons/user.tsx +0 -18
  300. package/src/icons/x.tsx +0 -35
  301. package/src/index.ts +0 -5
  302. package/src/style.css +0 -334
  303. package/src/theme/ThemeProvider/ThemeProvider.tsx +0 -111
  304. package/src/theme/ThemeProvider/context.ts +0 -7
  305. package/src/theme/ThemeProvider/generateVariables.test.ts +0 -49
  306. package/src/theme/ThemeProvider/generateVariables.ts +0 -124
  307. package/src/theme/ThemeProvider/index.ts +0 -3
  308. package/src/theme/ThemeProvider/types.ts +0 -37
  309. package/src/theme/createTheme/createTheme.ts +0 -41
  310. package/src/theme/createTheme/index.ts +0 -7
  311. package/src/theme/createTheme/types.ts +0 -62
  312. package/src/theme/defaults/colors.ts +0 -203
  313. package/src/theme/defaults/settings.ts +0 -8
  314. package/src/theme/index.ts +0 -4
  315. package/src/theme/types.ts +0 -31
  316. package/src/theme/utils/get-radius.ts +0 -23
  317. package/src/theme/utils/get-shade.ts +0 -16
  318. package/src/theme/utils/index.ts +0 -3
  319. package/src/theme/utils/use-theme.ts +0 -27
  320. package/src/utils/configs/create-config.ts +0 -15
  321. package/src/utils/configs/index.ts +0 -3
  322. package/src/utils/configs/use-class-names.ts +0 -41
  323. package/src/utils/configs/use-props.ts +0 -55
  324. package/src/utils/cx/index.ts +0 -6
  325. package/src/utils/factory/factory.ts +0 -23
  326. package/src/utils/factory/index.ts +0 -14
  327. package/src/utils/factory/types.ts +0 -92
  328. package/src/utils/index.ts +0 -3
  329. package/src/vitest/index.ts +0 -1
  330. package/src/vitest/setup.tsx +0 -20
  331. package/src/vitest/utils.tsx +0 -59
  332. package/tsconfig.json +0 -13
  333. package/tsconfig.tsbuildinfo +0 -1
  334. package/vitest.config.js +0 -19
@@ -1,134 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { render, screen, userEvent } from "../../vitest";
3
- import FileInput from "./file-input";
4
-
5
- const createFile = (name: string, type: string, size: number) =>
6
- new File([new Uint8Array(size)], name, { type });
7
-
8
- describe("@refraktor/core/FileInput", () => {
9
- it("supports input wrapper props", async () => {
10
- await render(
11
- <FileInput
12
- label="Documents"
13
- description="Upload PDF files"
14
- error="At least one file is required"
15
- />
16
- );
17
-
18
- const input = screen.getByLabelText("Documents");
19
-
20
- expect(input).toHaveAttribute("type", "file");
21
- expect(input).toHaveAttribute("aria-invalid", "true");
22
- expect(screen.getByText("Upload PDF files")).toBeInTheDocument();
23
- expect(
24
- screen.getByText("At least one file is required")
25
- ).toBeInTheDocument();
26
- });
27
-
28
- it("handles single file selection", async () => {
29
- const user = userEvent.setup();
30
- const onChange = vi.fn();
31
-
32
- await render(<FileInput label="Upload" onChange={onChange} />);
33
-
34
- const input = screen.getByLabelText("Upload") as HTMLInputElement;
35
- const file = new File(["hello"], "hello.txt", { type: "text/plain" });
36
-
37
- await user.upload(input, file);
38
-
39
- expect(onChange).toHaveBeenLastCalledWith([file]);
40
- expect(screen.getByText("hello.txt")).toBeInTheDocument();
41
- });
42
-
43
- it("enforces max files in multiple mode", async () => {
44
- const user = userEvent.setup({ applyAccept: false });
45
- const onChange = vi.fn();
46
- const onReject = vi.fn();
47
-
48
- await render(
49
- <FileInput
50
- label="Attachments"
51
- multiple
52
- maxFiles={2}
53
- onChange={onChange}
54
- onReject={onReject}
55
- />
56
- );
57
-
58
- const input = screen.getByLabelText("Attachments") as HTMLInputElement;
59
- const first = createFile("first.txt", "text/plain", 2);
60
- const second = createFile("second.txt", "text/plain", 3);
61
- const third = createFile("third.txt", "text/plain", 4);
62
-
63
- await user.upload(input, [first, second, third]);
64
-
65
- expect(onChange).toHaveBeenLastCalledWith([first, second]);
66
- expect(onReject).toHaveBeenCalledWith(
67
- expect.arrayContaining([
68
- expect.objectContaining({
69
- file: third,
70
- code: "too-many-files"
71
- })
72
- ])
73
- );
74
- });
75
-
76
- it("rejects invalid type and invalid size files", async () => {
77
- const user = userEvent.setup();
78
- const onChange = vi.fn();
79
- const onReject = vi.fn();
80
-
81
- await render(
82
- <FileInput
83
- label="Media"
84
- multiple
85
- accept="image/*,.pdf"
86
- minSize={2}
87
- maxSize={4}
88
- onChange={onChange}
89
- onReject={onReject}
90
- />
91
- );
92
-
93
- const input = screen.getByLabelText("Media") as HTMLInputElement;
94
- const valid = createFile("photo.png", "image/png", 3);
95
- const invalidType = createFile("script.js", "application/javascript", 3);
96
- const tooSmall = createFile("tiny.png", "image/png", 1);
97
- const tooLarge = createFile("large.pdf", "application/pdf", 6);
98
-
99
- await user.upload(input, [valid, invalidType, tooSmall, tooLarge]);
100
-
101
- expect(onChange).toHaveBeenLastCalledWith([valid]);
102
-
103
- const rejectionPayload =
104
- onReject.mock.calls[onReject.mock.calls.length - 1]?.[0] ?? [];
105
- const rejectionCodes = rejectionPayload.map(
106
- (item: { code: string }) => item.code
107
- );
108
-
109
- expect(rejectionCodes).toEqual(
110
- expect.arrayContaining([
111
- "file-too-small",
112
- "file-too-large"
113
- ])
114
- );
115
- });
116
-
117
- it("clears selected files", async () => {
118
- const user = userEvent.setup();
119
- const onChange = vi.fn();
120
-
121
- await render(<FileInput label="Receipt" onChange={onChange} />);
122
-
123
- const input = screen.getByLabelText("Receipt") as HTMLInputElement;
124
- const file = createFile("receipt.pdf", "application/pdf", 3);
125
-
126
- await user.upload(input, file);
127
-
128
- const clearButton = screen.getByRole("button", { name: "Clear file" });
129
- await user.click(clearButton);
130
-
131
- expect(onChange).toHaveBeenLastCalledWith([]);
132
- expect(screen.getByText("Select file")).toBeInTheDocument();
133
- });
134
- });
@@ -1,224 +0,0 @@
1
- import { useId, useMergedRefs } from "@refraktor/utils";
2
- import { useMemo, useRef, useState } from "react";
3
- import { XIcon } from "../../icons";
4
- import { useTheme } from "../../theme";
5
- import {
6
- createClassNamesConfig,
7
- createComponentConfig,
8
- factory,
9
- useClassNames,
10
- useProps
11
- } from "../../utils";
12
- import { getSize, getVariant } from "../input/input-field/input-field.styles";
13
- import { InputWrapper } from "../input/input-wrapper";
14
- import {
15
- FileInputClassNames,
16
- FileInputFactoryPayload,
17
- FileInputProps
18
- } from "./file-input.types";
19
- import { formatFileSummary, validateFiles } from "./file-input.utils";
20
-
21
- const defaultProps = {
22
- variant: "default",
23
- size: "md",
24
- radius: "default",
25
- clearable: true
26
- } satisfies Partial<FileInputProps>;
27
-
28
- const FileInput = factory<FileInputFactoryPayload>((_props, ref) => {
29
- const { cx, getRadius } = useTheme();
30
- const {
31
- id,
32
- label,
33
- description,
34
- error,
35
- required,
36
- withAsterisk,
37
- variant,
38
- size,
39
- radius,
40
- multiple,
41
- placeholder,
42
- clearable,
43
- maxSize,
44
- minSize,
45
- maxFiles,
46
- onChange,
47
- onReject,
48
- className,
49
- classNames,
50
- disabled,
51
- accept,
52
- ...props
53
- } = useProps("FileInput", defaultProps, _props);
54
-
55
- const classes = useClassNames<FileInputClassNames>("FileInput", classNames);
56
- const _id = useId(id);
57
-
58
- const localRef = useRef<HTMLInputElement>(null);
59
- const mergedRef = useMergedRefs(ref, localRef);
60
-
61
- const [files, setFiles] = useState<File[]>([]);
62
-
63
- const hasWrapper = label || description || error;
64
- const hasValue = files.length > 0;
65
- const describedBy = error
66
- ? `${_id}-error`
67
- : description
68
- ? `${_id}-description`
69
- : undefined;
70
-
71
- const summary = useMemo(() => formatFileSummary(files), [files]);
72
- const resolvedPlaceholder = placeholder ?? (multiple ? "Select files" : "Select file");
73
-
74
- const openPicker = () => {
75
- if (disabled) {
76
- return;
77
- }
78
-
79
- localRef.current?.click();
80
- };
81
-
82
- const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
83
- const selectedFiles = Array.from(event.currentTarget.files ?? []);
84
-
85
- if (selectedFiles.length === 0) {
86
- return;
87
- }
88
-
89
- const { accepted, rejections } = validateFiles(selectedFiles, {
90
- accept,
91
- minSize,
92
- maxSize,
93
- maxFiles,
94
- multiple
95
- });
96
-
97
- setFiles(accepted);
98
- onChange?.(accepted);
99
-
100
- if (rejections.length > 0) {
101
- onReject?.(rejections);
102
- }
103
- };
104
-
105
- const handleTriggerKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
106
- if (event.key === "Enter" || event.key === " ") {
107
- event.preventDefault();
108
- openPicker();
109
- }
110
- };
111
-
112
- const handleClear = (event: React.MouseEvent<HTMLButtonElement>) => {
113
- event.preventDefault();
114
- event.stopPropagation();
115
-
116
- if (disabled) {
117
- return;
118
- }
119
-
120
- if (localRef.current) {
121
- localRef.current.value = "";
122
- }
123
-
124
- setFiles([]);
125
- onChange?.([]);
126
- };
127
-
128
- const field = (
129
- <div className={cx("relative w-full", classes.root)}>
130
- <input
131
- {...props}
132
- id={_id}
133
- ref={mergedRef}
134
- type="file"
135
- accept={accept}
136
- multiple={multiple}
137
- disabled={disabled}
138
- required={required}
139
- aria-invalid={error ? true : undefined}
140
- aria-describedby={describedBy}
141
- className="pointer-events-none absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 opacity-0"
142
- onClick={(event) => {
143
- event.currentTarget.value = "";
144
- }}
145
- onChange={handleInputChange}
146
- />
147
-
148
- <div
149
- role="button"
150
- tabIndex={disabled ? -1 : 0}
151
- aria-disabled={disabled}
152
- aria-invalid={error ? true : undefined}
153
- aria-describedby={describedBy}
154
- className={cx(
155
- "w-full flex items-center gap-2 transition-all",
156
- "focus-visible:border-[var(--refraktor-primary)]",
157
- "select-none",
158
- getSize(size),
159
- getVariant(variant),
160
- getRadius(radius),
161
- error && "border-[var(--refraktor-colors-red-6)]",
162
- disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
163
- classes.trigger,
164
- className
165
- )}
166
- onClick={openPicker}
167
- onKeyDown={handleTriggerKeyDown}
168
- >
169
- <span
170
- className={cx(
171
- "min-w-0 flex-1 truncate",
172
- classes.value,
173
- hasValue
174
- ? cx("text-[var(--refraktor-text)]", classes.files)
175
- : cx(
176
- "text-[var(--refraktor-text-tertiary)]",
177
- classes.placeholder
178
- )
179
- )}
180
- >
181
- {hasValue ? summary : resolvedPlaceholder}
182
- </span>
183
-
184
- {clearable && hasValue && !disabled && (
185
- <button
186
- type="button"
187
- className={cx(
188
- "inline-flex h-4 w-4 items-center justify-center rounded-full border-0 bg-transparent p-0",
189
- "text-[var(--refraktor-text-secondary)] transition-colors hover:text-[var(--refraktor-text)]",
190
- classes.clear
191
- )}
192
- aria-label={files.length > 1 ? "Clear files" : "Clear file"}
193
- onClick={handleClear}
194
- >
195
- <XIcon size={12} />
196
- </button>
197
- )}
198
- </div>
199
- </div>
200
- );
201
-
202
- if (!hasWrapper) {
203
- return field;
204
- }
205
-
206
- return (
207
- <InputWrapper
208
- label={label}
209
- description={description}
210
- error={error}
211
- required={required}
212
- withAsterisk={withAsterisk}
213
- inputId={_id}
214
- >
215
- {field}
216
- </InputWrapper>
217
- );
218
- });
219
-
220
- FileInput.displayName = "@refraktor/core/FileInput";
221
- FileInput.configure = createComponentConfig<FileInputProps>();
222
- FileInput.classNames = createClassNamesConfig<FileInputClassNames>();
223
-
224
- export default FileInput;
@@ -1,78 +0,0 @@
1
- import {
2
- createClassNamesConfig,
3
- createComponentConfig,
4
- FactoryPayload
5
- } from "../../utils";
6
- import type { InputProps } from "../input";
7
-
8
- export type FileInputRejectCode =
9
- | "invalid-type"
10
- | "file-too-large"
11
- | "file-too-small"
12
- | "too-many-files";
13
-
14
- export interface FileInputRejection {
15
- /** Rejected file */
16
- file: File;
17
-
18
- /** Rejection reason code */
19
- code: FileInputRejectCode;
20
-
21
- /** Human-readable rejection reason */
22
- message: string;
23
- }
24
-
25
- export type FileInputClassNames = {
26
- root?: string;
27
- trigger?: string;
28
- value?: string;
29
- placeholder?: string;
30
- files?: string;
31
- clear?: string;
32
- };
33
-
34
- export interface FileInputProps
35
- extends Omit<
36
- InputProps,
37
- | "type"
38
- | "value"
39
- | "defaultValue"
40
- | "onChange"
41
- | "leftSection"
42
- | "rightSection"
43
- > {
44
- /** Placeholder shown when no files are selected */
45
- placeholder?: string;
46
-
47
- /** Whether to show clear button when value exists @default `true` */
48
- clearable?: boolean;
49
-
50
- /** Maximum allowed file size in bytes */
51
- maxSize?: number;
52
-
53
- /** Minimum allowed file size in bytes */
54
- minSize?: number;
55
-
56
- /** Maximum allowed number of files */
57
- maxFiles?: number;
58
-
59
- /** Callback called with accepted files */
60
- onChange?: (files: File[]) => void;
61
-
62
- /** Callback called with rejected files */
63
- onReject?: (rejections: FileInputRejection[]) => void;
64
-
65
- /** Used for styling different parts of the component */
66
- classNames?: FileInputClassNames;
67
- }
68
-
69
- export interface FileInputFactoryPayload extends FactoryPayload {
70
- props: FileInputProps;
71
- ref: HTMLInputElement;
72
- compound: {
73
- configure: ReturnType<typeof createComponentConfig<FileInputProps>>;
74
- classNames: ReturnType<
75
- typeof createClassNamesConfig<FileInputClassNames>
76
- >;
77
- };
78
- }
@@ -1,36 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { matchesAccept, validateFiles } from "./file-input.utils";
3
-
4
- const createFile = (name: string, type: string, size: number) =>
5
- new File([new Uint8Array(size)], name, { type });
6
-
7
- describe("@refraktor/core/FileInput utils", () => {
8
- it("matches files against accept rules", () => {
9
- const image = createFile("avatar.png", "image/png", 3);
10
- const pdf = createFile("sheet.pdf", "application/pdf", 3);
11
-
12
- expect(matchesAccept(image, "image/*")).toBe(true);
13
- expect(matchesAccept(pdf, "image/*")).toBe(false);
14
- expect(matchesAccept(pdf, ".pdf")).toBe(true);
15
- });
16
-
17
- it("rejects invalid file types", () => {
18
- const image = createFile("avatar.png", "image/png", 3);
19
- const script = createFile("script.js", "application/javascript", 3);
20
-
21
- const result = validateFiles([image, script], {
22
- multiple: true,
23
- accept: "image/*"
24
- });
25
-
26
- expect(result.accepted).toEqual([image]);
27
- expect(result.rejections).toEqual(
28
- expect.arrayContaining([
29
- expect.objectContaining({
30
- file: script,
31
- code: "invalid-type"
32
- })
33
- ])
34
- );
35
- });
36
- });
@@ -1,130 +0,0 @@
1
- import {
2
- FileInputRejectCode,
3
- FileInputRejection,
4
- FileInputProps
5
- } from "./file-input.types";
6
-
7
- type ValidationOptions = Pick<
8
- FileInputProps,
9
- "accept" | "maxFiles" | "maxSize" | "minSize" | "multiple"
10
- >;
11
-
12
- type ValidationResult = {
13
- accepted: File[];
14
- rejections: FileInputRejection[];
15
- };
16
-
17
- const createRejection = (
18
- file: File,
19
- code: FileInputRejectCode,
20
- message: string
21
- ): FileInputRejection => ({ file, code, message });
22
-
23
- export const matchesAccept = (file: File, accept?: string): boolean => {
24
- if (!accept) {
25
- return true;
26
- }
27
-
28
- const tokens = accept
29
- .split(",")
30
- .map((token) => token.trim().toLowerCase())
31
- .filter(Boolean);
32
-
33
- if (tokens.length === 0) {
34
- return true;
35
- }
36
-
37
- const fileType = file.type.toLowerCase();
38
- const fileName = file.name.toLowerCase();
39
-
40
- return tokens.some((token) => {
41
- if (token === "*/*") {
42
- return true;
43
- }
44
-
45
- if (token.startsWith(".")) {
46
- return fileName.endsWith(token);
47
- }
48
-
49
- if (token.endsWith("/*")) {
50
- return fileType.startsWith(token.slice(0, -1));
51
- }
52
-
53
- return fileType === token;
54
- });
55
- };
56
-
57
- export const validateFiles = (
58
- files: File[],
59
- { accept, minSize, maxSize, maxFiles, multiple }: ValidationOptions
60
- ): ValidationResult => {
61
- const accepted: File[] = [];
62
- const rejections: FileInputRejection[] = [];
63
-
64
- const isMinSizeDefined = typeof minSize === "number" && minSize >= 0;
65
- const isMaxSizeDefined = typeof maxSize === "number" && maxSize >= 0;
66
- const maxAllowedFiles =
67
- typeof maxFiles === "number"
68
- ? Math.max(0, Math.floor(maxFiles))
69
- : multiple
70
- ? Number.POSITIVE_INFINITY
71
- : 1;
72
-
73
- files.forEach((file, index) => {
74
- if (index >= maxAllowedFiles) {
75
- rejections.push(
76
- createRejection(
77
- file,
78
- "too-many-files",
79
- `Only ${maxAllowedFiles} file${maxAllowedFiles === 1 ? "" : "s"} allowed`
80
- )
81
- );
82
- return;
83
- }
84
-
85
- if (!matchesAccept(file, accept)) {
86
- rejections.push(
87
- createRejection(file, "invalid-type", "File type is not allowed")
88
- );
89
- return;
90
- }
91
-
92
- if (isMinSizeDefined && file.size < minSize) {
93
- rejections.push(
94
- createRejection(
95
- file,
96
- "file-too-small",
97
- `File is smaller than ${minSize} bytes`
98
- )
99
- );
100
- return;
101
- }
102
-
103
- if (isMaxSizeDefined && file.size > maxSize) {
104
- rejections.push(
105
- createRejection(
106
- file,
107
- "file-too-large",
108
- `File exceeds ${maxSize} bytes`
109
- )
110
- );
111
- return;
112
- }
113
-
114
- accepted.push(file);
115
- });
116
-
117
- return { accepted, rejections };
118
- };
119
-
120
- export const formatFileSummary = (files: File[]): string => {
121
- if (files.length === 0) {
122
- return "";
123
- }
124
-
125
- if (files.length === 1) {
126
- return files[0].name;
127
- }
128
-
129
- return `${files.length} files selected`;
130
- };
@@ -1,2 +0,0 @@
1
- export { default as FileInput } from "./file-input";
2
- export * from "./file-input.types";
@@ -1,66 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { render, screen } from "../../vitest";
3
- import For from "./for";
4
-
5
- describe("@refraktor/core/For", () => {
6
- it("renders fallback for empty collections", async () => {
7
- const children = vi.fn(() => <span>Should not render</span>);
8
-
9
- await render(
10
- <For each={[]} fallback={<span>No items</span>}>
11
- {children}
12
- </For>
13
- );
14
-
15
- expect(screen.getByText("No items")).toBeInTheDocument();
16
- expect(children).not.toHaveBeenCalled();
17
- });
18
-
19
- it("renders nothing when collection is empty and fallback is not set", async () => {
20
- const { container } = await render(
21
- <For each={[]}>{() => <span>Should not render</span>}</For>
22
- );
23
-
24
- expect(container).toBeEmptyDOMElement();
25
- });
26
-
27
- it("passes item metadata to children", async () => {
28
- await render(
29
- <For each={["alpha", "beta", "gamma"]}>
30
- {(item, meta) => (
31
- <div data-testid={item}>
32
- {item}:{meta.index}:{meta.length}:
33
- {String(meta.isFirst)}:{String(meta.isLast)}:
34
- {meta.previous ?? "none"}:{meta.next ?? "none"}
35
- </div>
36
- )}
37
- </For>
38
- );
39
-
40
- expect(screen.getByTestId("alpha")).toHaveTextContent(
41
- "alpha:0:3:true:false:none:beta"
42
- );
43
- expect(screen.getByTestId("beta")).toHaveTextContent(
44
- "beta:1:3:false:false:alpha:gamma"
45
- );
46
- expect(screen.getByTestId("gamma")).toHaveTextContent(
47
- "gamma:2:3:false:true:beta:none"
48
- );
49
- });
50
-
51
- it("supports iterable collections and calls key extractor", async () => {
52
- const values = new Set(["one", "two"]);
53
- const keyExtractor = vi.fn((item: string) => item);
54
-
55
- await render(
56
- <For each={values} keyExtractor={keyExtractor}>
57
- {(item) => <span>{item}</span>}
58
- </For>
59
- );
60
-
61
- expect(screen.getByText("one")).toBeInTheDocument();
62
- expect(screen.getByText("two")).toBeInTheDocument();
63
- expect(keyExtractor).toHaveBeenNthCalledWith(1, "one", 0);
64
- expect(keyExtractor).toHaveBeenNthCalledWith(2, "two", 1);
65
- });
66
- });