@vielzeug/refine 26.10.0 → 26.10.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 (468) hide show
  1. package/README.md +1 -1
  2. package/dist/accordion-item.cjs.map +1 -1
  3. package/dist/accordion-item.js.map +1 -1
  4. package/dist/accordion.cjs.map +1 -1
  5. package/dist/accordion.js.map +1 -1
  6. package/dist/async.cjs.map +1 -1
  7. package/dist/async.js.map +1 -1
  8. package/dist/badge.cjs.map +1 -1
  9. package/dist/badge.js.map +1 -1
  10. package/dist/box.cjs.map +1 -1
  11. package/dist/box.js.map +1 -1
  12. package/dist/button.cjs +3 -3
  13. package/dist/button.cjs.map +1 -1
  14. package/dist/button.js +3 -3
  15. package/dist/button.js.map +1 -1
  16. package/dist/calendar.cjs.map +1 -1
  17. package/dist/calendar.js.map +1 -1
  18. package/dist/carousel.cjs.map +1 -1
  19. package/dist/carousel.js.map +1 -1
  20. package/dist/chat-message.cjs.map +1 -1
  21. package/dist/chat-message.js.map +1 -1
  22. package/dist/chat-panel.cjs.map +1 -1
  23. package/dist/chat-panel.js.map +1 -1
  24. package/dist/checkbox-group.cjs.map +1 -1
  25. package/dist/checkbox-group.js.map +1 -1
  26. package/dist/chip.cjs.map +1 -1
  27. package/dist/chip.js.map +1 -1
  28. package/dist/code-window.cjs.map +1 -1
  29. package/dist/code-window.js.map +1 -1
  30. package/dist/combobox.cjs.map +1 -1
  31. package/dist/combobox.js.map +1 -1
  32. package/dist/command-palette.cjs +9 -1
  33. package/dist/command-palette.cjs.map +1 -1
  34. package/dist/command-palette.js +9 -1
  35. package/dist/command-palette.js.map +1 -1
  36. package/dist/content/card/card.css.cjs.map +1 -1
  37. package/dist/content/card/card.css.js.map +1 -1
  38. package/dist/content/carousel/carousel-slide.css.cjs.map +1 -1
  39. package/dist/content/carousel/carousel-slide.css.js.map +1 -1
  40. package/dist/content/carousel/carousel.css.cjs.map +1 -1
  41. package/dist/content/carousel/carousel.css.js.map +1 -1
  42. package/dist/content/carousel/carousel.d.ts +5 -5
  43. package/dist/content/chat-message/chat-message.css.cjs +1 -1
  44. package/dist/content/chat-message/chat-message.css.cjs.map +1 -1
  45. package/dist/content/chat-message/chat-message.css.js +1 -1
  46. package/dist/content/chat-message/chat-message.css.js.map +1 -1
  47. package/dist/content/chat-message/chat-message.d.ts +4 -4
  48. package/dist/content/chat-message/chat-message.d.ts.map +1 -1
  49. package/dist/content/chat-panel/chat-panel.d.ts +1 -1
  50. package/dist/content/code-window/code-window.css.cjs.map +1 -1
  51. package/dist/content/code-window/code-window.css.js.map +1 -1
  52. package/dist/content/code-window/code-window.d.ts +4 -4
  53. package/dist/content/index.d.ts +4 -0
  54. package/dist/content/index.d.ts.map +1 -1
  55. package/dist/content/list/_list-item-reveal.cjs.map +1 -1
  56. package/dist/content/list/_list-item-reveal.d.ts +4 -4
  57. package/dist/content/list/_list-item-reveal.d.ts.map +1 -1
  58. package/dist/content/list/_list-item-reveal.js.map +1 -1
  59. package/dist/content/list/list-item.css.cjs.map +1 -1
  60. package/dist/content/list/list-item.css.js.map +1 -1
  61. package/dist/content/list/list-item.d.ts +8 -8
  62. package/dist/content/list/list-item.d.ts.map +1 -1
  63. package/dist/content/list/list.d.ts +8 -8
  64. package/dist/content/list/list.d.ts.map +1 -1
  65. package/dist/content/qr-code/qr-code.d.ts +1 -1
  66. package/dist/content/rank-list/rank-item.css.cjs +2 -0
  67. package/dist/content/rank-list/rank-item.css.cjs.map +1 -0
  68. package/dist/content/rank-list/rank-item.css.js +2 -0
  69. package/dist/content/rank-list/rank-item.css.js.map +1 -0
  70. package/dist/content/rank-list/rank-item.d.ts +68 -0
  71. package/dist/content/rank-list/rank-item.d.ts.map +1 -0
  72. package/dist/content/rank-list/rank-list.css.cjs +2 -0
  73. package/dist/content/rank-list/rank-list.css.cjs.map +1 -0
  74. package/dist/content/rank-list/rank-list.css.js +2 -0
  75. package/dist/content/rank-list/rank-list.css.js.map +1 -0
  76. package/dist/content/rank-list/rank-list.d.ts +44 -0
  77. package/dist/content/rank-list/rank-list.d.ts.map +1 -0
  78. package/dist/content/separator/separator.d.ts +1 -1
  79. package/dist/content/separator/separator.d.ts.map +1 -1
  80. package/dist/content/speech-player/speech-player.css.cjs.map +1 -1
  81. package/dist/content/speech-player/speech-player.css.js.map +1 -1
  82. package/dist/content/speech-player/speech-player.d.ts +12 -12
  83. package/dist/content/speech-player/speech-player.d.ts.map +1 -1
  84. package/dist/content/stepper/step.css.cjs.map +1 -1
  85. package/dist/content/stepper/step.css.js.map +1 -1
  86. package/dist/content/stepper/step.d.ts +3 -3
  87. package/dist/content/stepper/step.d.ts.map +1 -1
  88. package/dist/content/stepper/stepper.css.cjs.map +1 -1
  89. package/dist/content/stepper/stepper.css.js.map +1 -1
  90. package/dist/content/stepper/stepper.d.ts +3 -3
  91. package/dist/content/stepper/stepper.d.ts.map +1 -1
  92. package/dist/content/table/table.css.cjs.map +1 -1
  93. package/dist/content/table/table.css.js.map +1 -1
  94. package/dist/content/table/table.d.ts +3 -3
  95. package/dist/content/table/table.d.ts.map +1 -1
  96. package/dist/content/text/text.css.cjs.map +1 -1
  97. package/dist/content/text/text.css.js.map +1 -1
  98. package/dist/content/text/text.d.ts +2 -2
  99. package/dist/content/text/text.d.ts.map +1 -1
  100. package/dist/cookie-banner.cjs.map +1 -1
  101. package/dist/cookie-banner.js.map +1 -1
  102. package/dist/copy-command.cjs +1 -1
  103. package/dist/copy-command.cjs.map +1 -1
  104. package/dist/copy-command.js +1 -1
  105. package/dist/copy-command.js.map +1 -1
  106. package/dist/core/announcer.cjs.map +1 -1
  107. package/dist/core/announcer.d.ts +2 -2
  108. package/dist/core/announcer.js.map +1 -1
  109. package/dist/core/aria-reflection.cjs.map +1 -1
  110. package/dist/core/aria-reflection.d.ts +3 -3
  111. package/dist/core/aria-reflection.js.map +1 -1
  112. package/dist/core/auto-resize.cjs.map +1 -1
  113. package/dist/core/auto-resize.d.ts +1 -1
  114. package/dist/core/auto-resize.js.map +1 -1
  115. package/dist/core/checkable.cjs.map +1 -1
  116. package/dist/core/checkable.d.ts +2 -2
  117. package/dist/core/checkable.d.ts.map +1 -1
  118. package/dist/core/checkable.js.map +1 -1
  119. package/dist/core/choice-field.cjs.map +1 -1
  120. package/dist/core/choice-field.d.ts +3 -3
  121. package/dist/core/choice-field.d.ts.map +1 -1
  122. package/dist/core/choice-field.js.map +1 -1
  123. package/dist/core/composer.cjs.map +1 -1
  124. package/dist/core/composer.d.ts +4 -4
  125. package/dist/core/composer.d.ts.map +1 -1
  126. package/dist/core/composer.js.map +1 -1
  127. package/dist/core/date-picker.cjs.map +1 -1
  128. package/dist/core/date-picker.d.ts +5 -5
  129. package/dist/core/date-picker.d.ts.map +1 -1
  130. package/dist/core/date-picker.js.map +1 -1
  131. package/dist/core/direction.cjs.map +1 -1
  132. package/dist/core/direction.d.ts +1 -1
  133. package/dist/core/direction.js.map +1 -1
  134. package/dist/core/field-base.cjs.map +1 -1
  135. package/dist/core/field-base.d.ts +9 -9
  136. package/dist/core/field-base.d.ts.map +1 -1
  137. package/dist/core/field-base.js.map +1 -1
  138. package/dist/core/focus.cjs.map +1 -1
  139. package/dist/core/focus.d.ts +1 -1
  140. package/dist/core/focus.js.map +1 -1
  141. package/dist/core/icon-registry.cjs.map +1 -1
  142. package/dist/core/icon-registry.d.ts +1 -1
  143. package/dist/core/icon-registry.js.map +1 -1
  144. package/dist/core/icons.cjs.map +1 -1
  145. package/dist/core/icons.js.map +1 -1
  146. package/dist/core/keyboard.cjs.map +1 -1
  147. package/dist/core/keyboard.d.ts +1 -1
  148. package/dist/core/keyboard.js.map +1 -1
  149. package/dist/core/numbers.cjs.map +1 -1
  150. package/dist/core/numbers.d.ts +1 -1
  151. package/dist/core/numbers.js.map +1 -1
  152. package/dist/core/overlay.cjs.map +1 -1
  153. package/dist/core/overlay.d.ts +6 -6
  154. package/dist/core/overlay.js.map +1 -1
  155. package/dist/core/parse.cjs.map +1 -1
  156. package/dist/core/parse.d.ts +1 -1
  157. package/dist/core/parse.js.map +1 -1
  158. package/dist/core/positioner.cjs.map +1 -1
  159. package/dist/core/positioner.d.ts +3 -3
  160. package/dist/core/positioner.js.map +1 -1
  161. package/dist/core/ref-callback.cjs.map +1 -1
  162. package/dist/core/ref-callback.d.ts +2 -2
  163. package/dist/core/ref-callback.d.ts.map +1 -1
  164. package/dist/core/ref-callback.js.map +1 -1
  165. package/dist/core/text-field.cjs.map +1 -1
  166. package/dist/core/text-field.d.ts +6 -6
  167. package/dist/core/text-field.d.ts.map +1 -1
  168. package/dist/core/text-field.js.map +1 -1
  169. package/dist/counter.cjs +1 -0
  170. package/dist/counter.cjs.map +1 -1
  171. package/dist/counter.js +1 -0
  172. package/dist/counter.js.map +1 -1
  173. package/dist/custom-elements.json +334 -87
  174. package/dist/datagrid.cjs.map +1 -1
  175. package/dist/datagrid.js.map +1 -1
  176. package/dist/date-picker.cjs +1 -1
  177. package/dist/date-picker.cjs.map +1 -1
  178. package/dist/date-picker.js +1 -1
  179. package/dist/date-picker.js.map +1 -1
  180. package/dist/dialog.cjs.map +1 -1
  181. package/dist/dialog.js.map +1 -1
  182. package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
  183. package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
  184. package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
  185. package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
  186. package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
  187. package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
  188. package/dist/drawer.cjs.map +1 -1
  189. package/dist/drawer.js.map +1 -1
  190. package/dist/feedback/alert/alert.css.cjs.map +1 -1
  191. package/dist/feedback/alert/alert.css.js.map +1 -1
  192. package/dist/feedback/badge/badge.css.cjs.map +1 -1
  193. package/dist/feedback/badge/badge.css.js.map +1 -1
  194. package/dist/feedback/badge/badge.d.ts +1 -1
  195. package/dist/feedback/badge/badge.d.ts.map +1 -1
  196. package/dist/feedback/chip/chip.css.cjs.map +1 -1
  197. package/dist/feedback/chip/chip.css.js.map +1 -1
  198. package/dist/feedback/chip/chip.d.ts +3 -3
  199. package/dist/feedback/chip/chip.d.ts.map +1 -1
  200. package/dist/feedback/cookie-banner/cookie-banner.css.cjs.map +1 -1
  201. package/dist/feedback/cookie-banner/cookie-banner.css.js.map +1 -1
  202. package/dist/feedback/cookie-banner/cookie-banner.d.ts +8 -8
  203. package/dist/feedback/cookie-banner/cookie-banner.d.ts.map +1 -1
  204. package/dist/feedback/progress/progress.css.cjs.map +1 -1
  205. package/dist/feedback/progress/progress.css.js.map +1 -1
  206. package/dist/feedback/progress/progress.d.ts +1 -1
  207. package/dist/feedback/progress/progress.d.ts.map +1 -1
  208. package/dist/feedback/skeleton/skeleton.css.cjs.map +1 -1
  209. package/dist/feedback/skeleton/skeleton.css.js.map +1 -1
  210. package/dist/feedback/skeleton/skeleton.d.ts +1 -1
  211. package/dist/feedback/skeleton/skeleton.d.ts.map +1 -1
  212. package/dist/feedback/toast/toast.css.cjs +1 -1
  213. package/dist/feedback/toast/toast.css.cjs.map +1 -1
  214. package/dist/feedback/toast/toast.css.js +1 -1
  215. package/dist/feedback/toast/toast.css.js.map +1 -1
  216. package/dist/feedback/toast/toast.d.ts +1 -1
  217. package/dist/feedback/toast/toast.d.ts.map +1 -1
  218. package/dist/feedback/typing-indicator/typing-indicator.d.ts +2 -2
  219. package/dist/file-input.cjs +1 -1
  220. package/dist/file-input.cjs.map +1 -1
  221. package/dist/file-input.js +1 -1
  222. package/dist/file-input.js.map +1 -1
  223. package/dist/frameworks/elements.d.ts +1 -1
  224. package/dist/grid-item.cjs.map +1 -1
  225. package/dist/grid-item.js.map +1 -1
  226. package/dist/grid.cjs.map +1 -1
  227. package/dist/grid.js.map +1 -1
  228. package/dist/icon.cjs.map +1 -1
  229. package/dist/icon.js.map +1 -1
  230. package/dist/input.cjs.map +1 -1
  231. package/dist/input.js.map +1 -1
  232. package/dist/inputs/button/button.css.cjs.map +1 -1
  233. package/dist/inputs/button/button.css.js.map +1 -1
  234. package/dist/inputs/button/button.d.ts +3 -3
  235. package/dist/inputs/button/button.d.ts.map +1 -1
  236. package/dist/inputs/calendar/calendar.css.cjs.map +1 -1
  237. package/dist/inputs/calendar/calendar.css.js.map +1 -1
  238. package/dist/inputs/calendar/calendar.d.ts +2 -2
  239. package/dist/inputs/checkbox-group/checkbox-group.d.ts +2 -2
  240. package/dist/inputs/checkbox-group/checkbox-group.d.ts.map +1 -1
  241. package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
  242. package/dist/inputs/combobox/combobox.css.js.map +1 -1
  243. package/dist/inputs/counter/counter.css.cjs +1 -1
  244. package/dist/inputs/counter/counter.css.cjs.map +1 -1
  245. package/dist/inputs/counter/counter.css.js +1 -1
  246. package/dist/inputs/counter/counter.css.js.map +1 -1
  247. package/dist/inputs/counter/counter.d.ts +2 -1
  248. package/dist/inputs/counter/counter.d.ts.map +1 -1
  249. package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
  250. package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
  251. package/dist/inputs/datagrid/datagrid.d.ts +5 -5
  252. package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
  253. package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
  254. package/dist/inputs/date-picker/date-picker.d.ts +1 -1
  255. package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
  256. package/dist/inputs/file-input/file-input-upload.d.ts +6 -6
  257. package/dist/inputs/file-input/file-input-upload.js.map +1 -1
  258. package/dist/inputs/file-input/file-input.css.cjs.map +1 -1
  259. package/dist/inputs/file-input/file-input.css.js.map +1 -1
  260. package/dist/inputs/file-input/file-input.d.ts +6 -6
  261. package/dist/inputs/input/input.css.cjs +1 -1
  262. package/dist/inputs/input/input.css.cjs.map +1 -1
  263. package/dist/inputs/input/input.css.js +1 -1
  264. package/dist/inputs/input/input.css.js.map +1 -1
  265. package/dist/inputs/input/input.d.ts +6 -6
  266. package/dist/inputs/input/input.d.ts.map +1 -1
  267. package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -1
  268. package/dist/inputs/message-composer/message-composer.css.js.map +1 -1
  269. package/dist/inputs/message-composer/message-composer.d.ts +7 -7
  270. package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
  271. package/dist/inputs/number-input/number-input.css.cjs.map +1 -1
  272. package/dist/inputs/number-input/number-input.css.js.map +1 -1
  273. package/dist/inputs/number-input/number-input.d.ts +2 -2
  274. package/dist/inputs/otp-input/otp-input.css.cjs.map +1 -1
  275. package/dist/inputs/otp-input/otp-input.css.js.map +1 -1
  276. package/dist/inputs/qr-scanner/qr-scanner.d.ts +1 -1
  277. package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -1
  278. package/dist/inputs/rating/rating.d.ts +1 -1
  279. package/dist/inputs/rating/rating.d.ts.map +1 -1
  280. package/dist/inputs/select/select.css.cjs +1 -1
  281. package/dist/inputs/select/select.css.cjs.map +1 -1
  282. package/dist/inputs/select/select.css.js +1 -1
  283. package/dist/inputs/select/select.css.js.map +1 -1
  284. package/dist/inputs/select/select.d.ts +1 -1
  285. package/dist/inputs/select/select.d.ts.map +1 -1
  286. package/dist/inputs/shared/field-binding.cjs.map +1 -1
  287. package/dist/inputs/shared/field-binding.d.ts +2 -2
  288. package/dist/inputs/shared/field-binding.d.ts.map +1 -1
  289. package/dist/inputs/shared/field-binding.js.map +1 -1
  290. package/dist/inputs/shared/native-field-event.cjs.map +1 -1
  291. package/dist/inputs/shared/native-field-event.d.ts +1 -1
  292. package/dist/inputs/shared/native-field-event.js.map +1 -1
  293. package/dist/inputs/shared/templates.cjs.map +1 -1
  294. package/dist/inputs/shared/templates.d.ts +3 -3
  295. package/dist/inputs/shared/templates.js.map +1 -1
  296. package/dist/inputs/slider/slider.css.cjs.map +1 -1
  297. package/dist/inputs/slider/slider.css.js.map +1 -1
  298. package/dist/inputs/slider/slider.d.ts +1 -1
  299. package/dist/inputs/slider/slider.d.ts.map +1 -1
  300. package/dist/inputs/textarea/textarea.css.cjs.map +1 -1
  301. package/dist/inputs/textarea/textarea.css.js.map +1 -1
  302. package/dist/inputs/textarea/textarea.d.ts +1 -1
  303. package/dist/inputs/time-picker/time-picker.css.cjs.map +1 -1
  304. package/dist/inputs/time-picker/time-picker.css.js.map +1 -1
  305. package/dist/inputs/time-picker/time-picker.d.ts +1 -1
  306. package/dist/layout/box/box.css.cjs.map +1 -1
  307. package/dist/layout/box/box.css.js.map +1 -1
  308. package/dist/layout/box/box.d.ts +1 -1
  309. package/dist/layout/grid/grid.d.ts +1 -1
  310. package/dist/layout/grid-item/grid-item.d.ts +3 -3
  311. package/dist/layout/grid-item/grid-item.d.ts.map +1 -1
  312. package/dist/layout/navbar/navbar.css.cjs +1 -1
  313. package/dist/layout/navbar/navbar.css.cjs.map +1 -1
  314. package/dist/layout/navbar/navbar.css.js +1 -1
  315. package/dist/layout/navbar/navbar.css.js.map +1 -1
  316. package/dist/layout/navbar/navbar.d.ts +3 -3
  317. package/dist/layout/sidebar/sidebar-item.css.cjs.map +1 -1
  318. package/dist/layout/sidebar/sidebar-item.css.js.map +1 -1
  319. package/dist/layout/sidebar/sidebar.d.ts +5 -5
  320. package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
  321. package/dist/list-item.cjs.map +1 -1
  322. package/dist/list-item.js.map +1 -1
  323. package/dist/list.cjs.map +1 -1
  324. package/dist/list.js.map +1 -1
  325. package/dist/menu.cjs +6 -5
  326. package/dist/menu.cjs.map +1 -1
  327. package/dist/menu.js +6 -5
  328. package/dist/menu.js.map +1 -1
  329. package/dist/message-composer.cjs.map +1 -1
  330. package/dist/message-composer.js.map +1 -1
  331. package/dist/navbar.cjs.map +1 -1
  332. package/dist/navbar.js.map +1 -1
  333. package/dist/number-input.cjs.map +1 -1
  334. package/dist/number-input.js.map +1 -1
  335. package/dist/otp-input.cjs.map +1 -1
  336. package/dist/otp-input.js.map +1 -1
  337. package/dist/overlay/command-palette/command-palette-items.cjs.map +1 -1
  338. package/dist/overlay/command-palette/command-palette-items.d.ts +1 -1
  339. package/dist/overlay/command-palette/command-palette-items.js.map +1 -1
  340. package/dist/overlay/command-palette/command-palette.css.cjs +1 -1
  341. package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
  342. package/dist/overlay/command-palette/command-palette.css.js +1 -1
  343. package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
  344. package/dist/overlay/command-palette/command-palette.d.ts +7 -3
  345. package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
  346. package/dist/overlay/command-palette/command-palette.types.d.ts +16 -6
  347. package/dist/overlay/command-palette/command-palette.types.d.ts.map +1 -1
  348. package/dist/overlay/dialog/dialog.css.cjs +1 -1
  349. package/dist/overlay/dialog/dialog.css.cjs.map +1 -1
  350. package/dist/overlay/dialog/dialog.css.js +1 -1
  351. package/dist/overlay/dialog/dialog.css.js.map +1 -1
  352. package/dist/overlay/dialog/dialog.d.ts +2 -2
  353. package/dist/overlay/dialog/dialog.d.ts.map +1 -1
  354. package/dist/overlay/drawer/drawer.css.cjs.map +1 -1
  355. package/dist/overlay/drawer/drawer.css.js.map +1 -1
  356. package/dist/overlay/drawer/drawer.d.ts +1 -1
  357. package/dist/overlay/drawer/drawer.d.ts.map +1 -1
  358. package/dist/overlay/menu/menu-item.css.cjs.map +1 -1
  359. package/dist/overlay/menu/menu-item.css.js.map +1 -1
  360. package/dist/overlay/menu/menu.d.ts.map +1 -1
  361. package/dist/overlay/shared/use-dialog.cjs.map +1 -1
  362. package/dist/overlay/shared/use-dialog.d.ts +6 -6
  363. package/dist/overlay/shared/use-dialog.d.ts.map +1 -1
  364. package/dist/overlay/shared/use-dialog.js.map +1 -1
  365. package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
  366. package/dist/overlay/shared/use-floating-trigger.d.ts +1 -1
  367. package/dist/overlay/shared/use-floating-trigger.d.ts.map +1 -1
  368. package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
  369. package/dist/overlay/tooltip/tooltip.css.cjs.map +1 -1
  370. package/dist/overlay/tooltip/tooltip.css.js.map +1 -1
  371. package/dist/progress.cjs.map +1 -1
  372. package/dist/progress.js.map +1 -1
  373. package/dist/qr-code.cjs.map +1 -1
  374. package/dist/qr-code.js.map +1 -1
  375. package/dist/qr-scanner.cjs.map +1 -1
  376. package/dist/qr-scanner.js.map +1 -1
  377. package/dist/radio-group.cjs.map +1 -1
  378. package/dist/radio-group.js.map +1 -1
  379. package/dist/radio.cjs.map +1 -1
  380. package/dist/radio.js.map +1 -1
  381. package/dist/rank-item.cjs +21 -0
  382. package/dist/rank-item.cjs.map +1 -0
  383. package/dist/rank-item.js +21 -0
  384. package/dist/rank-item.js.map +1 -0
  385. package/dist/rank-list.cjs +4 -0
  386. package/dist/rank-list.cjs.map +1 -0
  387. package/dist/rank-list.js +4 -0
  388. package/dist/rank-list.js.map +1 -0
  389. package/dist/rating.cjs.map +1 -1
  390. package/dist/rating.js.map +1 -1
  391. package/dist/refine.cjs +111 -80
  392. package/dist/refine.cjs.map +1 -1
  393. package/dist/refine.iife.js +111 -80
  394. package/dist/refine.iife.js.map +1 -1
  395. package/dist/refine.js +112 -81
  396. package/dist/refine.js.map +1 -1
  397. package/dist/select.cjs.map +1 -1
  398. package/dist/select.js.map +1 -1
  399. package/dist/separator.cjs.map +1 -1
  400. package/dist/separator.js.map +1 -1
  401. package/dist/shared/index.d.ts +3 -3
  402. package/dist/shared/size-presets.cjs.map +1 -1
  403. package/dist/shared/size-presets.d.ts +4 -4
  404. package/dist/shared/size-presets.js.map +1 -1
  405. package/dist/shared/types.d.ts +1 -1
  406. package/dist/sidebar.cjs.map +1 -1
  407. package/dist/sidebar.js.map +1 -1
  408. package/dist/skeleton.cjs.map +1 -1
  409. package/dist/skeleton.js.map +1 -1
  410. package/dist/slider.cjs.map +1 -1
  411. package/dist/slider.js.map +1 -1
  412. package/dist/speech-player.cjs.map +1 -1
  413. package/dist/speech-player.js.map +1 -1
  414. package/dist/step.cjs.map +1 -1
  415. package/dist/step.js.map +1 -1
  416. package/dist/stepper.cjs.map +1 -1
  417. package/dist/stepper.js.map +1 -1
  418. package/dist/styles/fouc.css +2 -2
  419. package/dist/styles/mixins/accessibility.css.cjs.map +1 -1
  420. package/dist/styles/mixins/accessibility.css.d.ts +4 -4
  421. package/dist/styles/mixins/accessibility.css.js.map +1 -1
  422. package/dist/styles/mixins/animation.css.cjs +2 -2
  423. package/dist/styles/mixins/animation.css.cjs.map +1 -1
  424. package/dist/styles/mixins/animation.css.d.ts +4 -4
  425. package/dist/styles/mixins/animation.css.js +2 -2
  426. package/dist/styles/mixins/animation.css.js.map +1 -1
  427. package/dist/styles/mixins/field-variants.css.cjs +2 -2
  428. package/dist/styles/mixins/field-variants.css.cjs.map +1 -1
  429. package/dist/styles/mixins/field-variants.css.d.ts +6 -6
  430. package/dist/styles/mixins/field-variants.css.d.ts.map +1 -1
  431. package/dist/styles/mixins/field-variants.css.js +2 -2
  432. package/dist/styles/mixins/field-variants.css.js.map +1 -1
  433. package/dist/styles/mixins/shape.css.cjs.map +1 -1
  434. package/dist/styles/mixins/shape.css.d.ts +9 -9
  435. package/dist/styles/mixins/shape.css.d.ts.map +1 -1
  436. package/dist/styles/mixins/shape.css.js.map +1 -1
  437. package/dist/styles/mixins/variants.css.cjs.map +1 -1
  438. package/dist/styles/mixins/variants.css.d.ts +1 -1
  439. package/dist/styles/mixins/variants.css.js.map +1 -1
  440. package/dist/styles/theme.css +36 -28
  441. package/dist/tab-item.cjs.map +1 -1
  442. package/dist/tab-item.js.map +1 -1
  443. package/dist/tab-panel.cjs.map +1 -1
  444. package/dist/tab-panel.js.map +1 -1
  445. package/dist/table.cjs.map +1 -1
  446. package/dist/table.js.map +1 -1
  447. package/dist/text.cjs.map +1 -1
  448. package/dist/text.js.map +1 -1
  449. package/dist/textarea.cjs.map +1 -1
  450. package/dist/textarea.js.map +1 -1
  451. package/dist/theme.cjs.map +1 -1
  452. package/dist/theme.d.ts +2 -2
  453. package/dist/theme.js.map +1 -1
  454. package/dist/time-picker.cjs +1 -1
  455. package/dist/time-picker.cjs.map +1 -1
  456. package/dist/time-picker.js +1 -1
  457. package/dist/time-picker.js.map +1 -1
  458. package/dist/toast.cjs.map +1 -1
  459. package/dist/toast.js.map +1 -1
  460. package/dist/types/elements.d.ts +6 -2
  461. package/dist/typing-indicator.cjs.map +1 -1
  462. package/dist/typing-indicator.js.map +1 -1
  463. package/dist/utils/css.cjs.map +1 -1
  464. package/dist/utils/css.js.map +1 -1
  465. package/dist/utils/link.cjs.map +1 -1
  466. package/dist/utils/link.d.ts +2 -2
  467. package/dist/utils/link.js.map +1 -1
  468. package/package.json +31 -14
@@ -1 +1 @@
1
- {"version":3,"file":"datagrid.css.js","names":[],"sources":["../../../src/inputs/datagrid/datagrid.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n /* ── Datagrid-specific token overrides on top of tableBaseMixin ─────── */\n --_row-hover-bg: var(--datagrid-row-hover-bg, color-mix(in oklch, var(--color-contrast-500) 5%, transparent));\n --_row-selected-bg: var(--datagrid-row-selected-bg, color-mix(in oklch, var(--_accent) 10%, transparent));\n --_row-selected-border: var(--datagrid-row-selected-border, var(--_accent));\n }\n\n /* ── Scroll wrapper ───────────────────────────────────────────────────────── */\n\n .dg-scroll {\n max-height: var(--datagrid-max-height, none);\n overflow: auto;\n }\n\n /* ── Table ────────────────────────────────────────────────────────────────── */\n\n .dg-table {\n width: 100%;\n font-size: var(--_font-size);\n table-layout: auto;\n border-collapse: collapse;\n }\n\n /* ── Head ─────────────────────────────────────────────────────────────────── */\n\n .dg-head {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--_header-bg);\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-th {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n\n &:has(.dg-sort-btn) {\n padding: 0;\n }\n }\n\n .dg-sort-btn {\n --button-padding: var(--_cell-padding-y) var(--_cell-padding-x);\n --button-font-size: var(--text-xs);\n --button-font-weight: var(--font-semibold);\n --button-color: var(--color-contrast-500);\n --button-justify: flex-start;\n\n height: 100%;\n }\n\n .dg-th[data-align='center'] .dg-sort-btn {\n --button-justify: center;\n }\n\n .dg-th[data-align='right'] .dg-sort-btn {\n --button-justify: flex-end;\n }\n\n :host(:dir(rtl)) .dg-th,\n :dir(rtl) .dg-th {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-th:last-child,\n .dg-th.dg-th-check,\n .dg-th.dg-th-expand,\n .dg-th.dg-th-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-th:last-child,\n :host(:dir(rtl)) .dg-th.dg-th-check,\n :host(:dir(rtl)) .dg-th.dg-th-expand,\n :host(:dir(rtl)) .dg-th.dg-th-last,\n :dir(rtl) .dg-th:last-child,\n :dir(rtl) .dg-th.dg-th-check,\n :dir(rtl) .dg-th.dg-th-expand,\n :dir(rtl) .dg-th.dg-th-last {\n border-left: none;\n }\n\n .dg-th-inner {\n position: relative;\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-th[aria-sort]:has(.dg-sort-btn:hover):not(:has(.dg-sort-btn[aria-disabled='true'])) {\n background: color-mix(in oklch, var(--color-contrast-900) 5%, transparent);\n }\n\n .dg-th[aria-sort='ascending'] .dg-sort-icon,\n .dg-th[aria-sort='descending'] .dg-sort-icon {\n color: var(--_accent);\n }\n\n /* Select-all checkbox cell */\n .dg-th-check {\n width: var(--size-5);\n min-width: var(--size-5);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n }\n\n .dg-th-check,\n .dg-td-check {\n vertical-align: middle;\n text-align: center;\n }\n\n .dg-td.dg-td-check {\n padding: var(--size-1) 0 0;\n }\n\n /* ore-checkbox used for select-all and row checkboxes — label-less, compact */\n .dg-check {\n display: inline-flex;\n gap: 0;\n align-items: center;\n justify-content: center;\n min-height: 0;\n cursor: pointer;\n }\n\n /* ── Body rows ────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n .dg-body .dg-tr:hover:not([data-disabled], [aria-selected='true']) {\n background: var(--_row-hover-bg);\n }\n\n /* ── Selection ─────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n .dg-body .dg-tr[data-selectable] {\n cursor: pointer;\n outline: none;\n }\n\n .dg-body .dg-tr[data-selectable]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--_row-selected-border);\n }\n\n .dg-body .dg-tr[data-disabled] {\n pointer-events: none;\n opacity: 0.45;\n }\n\n /* Striped rows */\n :host([striped]) .dg-body .dg-tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n :host([striped]) .dg-body .dg-tr:nth-child(even)[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n /* ── Body cells ────────────────────────────────────────────────────────────── */\n\n .dg-td {\n max-width: var(--datagrid-cell-max-width, 32ch);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n color: var(--color-contrast-800);\n white-space: nowrap;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n }\n\n :host(:dir(rtl)) .dg-td,\n :dir(rtl) .dg-td {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-td:last-child,\n .dg-td.dg-td-check,\n .dg-td.dg-td-expand,\n .dg-td.dg-td-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-td:last-child,\n :host(:dir(rtl)) .dg-td.dg-td-check,\n :host(:dir(rtl)) .dg-td.dg-td-expand,\n :host(:dir(rtl)) .dg-td.dg-td-last,\n :dir(rtl) .dg-td:last-child,\n :dir(rtl) .dg-td.dg-td-check,\n :dir(rtl) .dg-td.dg-td-expand,\n :dir(rtl) .dg-td.dg-td-last {\n border-left: none;\n }\n\n /* ── Empty state ──────────────────────────────────────────────────────────── */\n\n .dg-empty {\n padding: var(--size-12) var(--_cell-padding-x);\n font-size: var(--text-sm);\n color: var(--color-contrast-400);\n text-align: center;\n }\n\n .dg-empty-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-4);\n align-items: center;\n justify-content: center;\n }\n\n .dg-empty-actions {\n margin-top: var(--size-2);\n }\n\n /* ── Controls bar (tabs + action row) ───────────────────────────────────── */\n\n .dg-controls {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n height: var(--size-8);\n padding: var(--size-1-5) var(--_cell-padding-x);\n background: var(--_header-bg);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n /* ── Tab system (left) ───────────────────────────────────────────────────── */\n\n .dg-tabs {\n display: flex;\n flex: 1;\n gap: var(--size-1);\n align-items: center;\n min-width: 0;\n overflow: hidden;\n }\n\n .dg-tab {\n --button-padding: var(--size-0-5) var(--size-2-5);\n --button-font-size: var(--text-xs);\n --button-color: var(--color-contrast-500);\n\n flex: 1;\n min-width: 0;\n }\n\n @media (max-width: 40rem) {\n .dg-tabs {\n overflow-x: auto;\n overscroll-behavior-inline: contain;\n scrollbar-width: none;\n }\n\n .dg-tabs::-webkit-scrollbar {\n display: none;\n }\n\n .dg-tab {\n flex: 0 0 auto;\n min-width: max-content;\n }\n }\n\n .dg-tab--active {\n --button-color: var(--color-contrast-900);\n --button-bg: color-mix(in oklch, var(--color-contrast-500) 10%, transparent);\n --button-border-color: var(--_border-color);\n --button-border: var(--border);\n\n font-weight: var(--font-semibold);\n }\n\n /* ── Action bar (right) ──────────────────────────────────────────────────── */\n\n .dg-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n align-items: center;\n }\n\n /* ── Shared icon button — used by action bar, search-expand clear, popover header icons ── */\n\n .dg-action-divider {\n display: inline-block;\n flex-shrink: 0;\n width: var(--border);\n height: var(--size-4);\n margin-inline: var(--size-1);\n background: var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-filter {\n display: block;\n width: 100%;\n }\n\n /* ── Search toggle (rightmost action) ───────────────────────────────────── */\n\n .dg-search {\n display: flex;\n flex-direction: row-reverse;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-search-input {\n --input-height: var(--size-7);\n display: block;\n flex-shrink: 0;\n width: 0;\n overflow: hidden;\n opacity: 0;\n transition:\n width var(--transition-fast),\n opacity var(--transition-fast);\n }\n\n .dg-search--open .dg-search-input {\n width: 12rem;\n overflow: visible;\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .dg-search-input {\n transition: none;\n }\n }\n\n /* ── ore-popover: action popover wrapper ─────────────────────────────────── */\n\n .dg-action-popover {\n display: inline-flex;\n }\n\n /* ── Popover content: shared header row ─────────────────────────────────── */\n\n .dg-pop-header {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n min-height: var(--size-10);\n padding: var(--size-2) var(--size-3);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .dg-pop-title {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-600);\n letter-spacing: 0.02em;\n }\n\n .dg-pop-heading,\n .dg-pop-heading > * {\n display: block;\n min-width: 0;\n }\n\n .dg-pop-heading small {\n margin-block-start: var(--size-0-5);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n }\n\n /* ── Sort / Group by popover content ────────────────────────────────────── */\n\n .dg-pop-sort {\n display: flex;\n flex-direction: column;\n }\n\n .dg-pop-sort-row {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-select,\n .dg-pop-dir-select {\n --input-min-width: 0;\n --select-min-width: 0;\n width: 0;\n min-width: 0;\n }\n\n .dg-pop-select {\n flex: 2 1 0;\n }\n\n .dg-pop-dir-select {\n flex: 0 0 var(--size-28);\n width: var(--size-28);\n }\n\n /* ── Filter popover content ──────────────────────────────────────────────── */\n\n .dg-pop-filter {\n display: flex;\n flex-direction: column;\n }\n\n /* Field picker: select columns to add filter rules. */\n .dg-pop-filter-fields {\n padding: var(--size-2) var(--size-3);\n }\n\n /* ── Filter rules section ────────────────────────────────────────────────── */\n\n .dg-pop-filter-rules {\n padding-block-start: var(--size-1);\n border-block-start: var(--border) solid var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-pop-filter-rule {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-filter-rule-header {\n display: flex;\n gap: var(--size-1-5);\n align-items: center;\n }\n\n .dg-pop-filter-field {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-800);\n white-space: nowrap;\n }\n\n .dg-pop-filter-op-select {\n --input-min-width: 0;\n --select-min-width: 0;\n --select-height: var(--size-6);\n --select-font-size: var(--text-2xs);\n --select-padding-x: var(--size-1-5);\n\n flex: 1 1 0;\n min-width: 6rem;\n }\n\n .dg-state-trigger {\n position: relative;\n }\n\n .dg-state-trigger[data-customized]::after {\n position: absolute;\n inset-block-start: var(--size-1);\n inset-inline-end: var(--size-1);\n width: var(--size-1-5);\n height: var(--size-1-5);\n content: '';\n background: var(--color-primary);\n border: var(--border) solid var(--_header-bg);\n border-radius: 50%;\n }\n\n .dg-pop-col {\n width: 18rem;\n }\n\n .dg-pop-col-list {\n display: grid;\n gap: var(--size-0-5);\n max-height: min(50vh, 24rem);\n padding: var(--size-2) var(--size-3);\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n\n .dg-pop-col-item {\n --checkbox-size: var(--size-4);\n --checkbox-font-size: var(--text-sm);\n\n width: 100%;\n padding: var(--size-1) var(--size-2);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .dg-pop-col-item:hover {\n background: var(--color-contrast-150);\n }\n\n .dg-pop-col-item::part(label) {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ── Footer / pagination bar ─────────────────────────────────────────────── */\n\n .dg-footer {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-1) var(--_cell-padding-x);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n background: var(--_header-bg);\n }\n\n .dg-footer-info {\n white-space: nowrap;\n }\n\n .dg-footer-start {\n display: flex;\n flex: 1 1 auto;\n gap: var(--size-2);\n align-items: center;\n min-width: 0;\n }\n\n .dg-page-size-wrap {\n flex-shrink: 0;\n order: -1;\n width: var(--size-20);\n }\n\n .dg-page-size-select {\n --input-height: var(--size-8);\n --input-min-width: 0;\n --select-min-width: 0;\n --select-font-size: var(--text-xs);\n --select-padding-x: var(--size-2);\n }\n\n .dg-pagination {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-1);\n align-items: center;\n margin-inline-start: auto;\n }\n\n :host(:dir(rtl)) .dg-page-btn,\n :dir(rtl) .dg-page-btn {\n transform: scaleX(-1);\n }\n\n .dg-page-btn {\n --button-color: var(--color-contrast-600);\n --button-radius: var(--rounded-md);\n\n width: var(--size-7);\n height: var(--size-7);\n }\n\n .dg-page-btn:hover {\n --button-color: var(--color-contrast-900);\n --button-bg: var(--color-contrast-300);\n }\n\n .dg-page-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n }\n\n .dg-page-label {\n padding-inline: var(--size-1);\n white-space: nowrap;\n }\n\n /* ── Column resize handle (F4) ───────────────────────────────────────────── */\n\n .dg-col-resize {\n position: absolute;\n inset-block: calc(var(--_cell-padding-y) * -1);\n inset-inline-end: calc((var(--_cell-padding-x) + var(--border) + 2px) * -1);\n width: var(--size-1);\n cursor: col-resize;\n user-select: none;\n background: transparent;\n border-radius: var(--rounded);\n transition: background var(--transition-fast);\n }\n\n .dg-col-resize:hover,\n .dg-col-resize:active {\n background: var(--_accent, var(--color-contrast-300));\n }\n\n /* ── Cell keyboard focus (ARIA grid roving tabindex) ─────────────────────── */\n\n .dg-td:focus-visible,\n .dg-th:focus-visible {\n outline: 2px solid var(--_row-selected-border);\n outline-offset: -2px;\n }\n\n /* ── Row expansion ────────────────────────────────────────────────────────── */\n\n .dg-tr-expanded .dg-td-expanded {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n border: none;\n\n /* Paint background + bottom separator via box-shadow so it is\n direction-agnostic and covers the host outer border on both sides. */\n box-shadow:\n inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-600) 4%, var(--_bg, transparent)),\n inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-tr-expanded:last-child .dg-td-expanded {\n box-shadow: inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-900) 4%, var(--_bg, transparent));\n }\n\n .dg-tr:has(+ .dg-tr-expanded) {\n border-block-end: none;\n }\n\n .dg-th-expand,\n .dg-td-expand {\n width: var(--size-8);\n min-width: var(--size-8);\n padding-block: var(--size-1) 0;\n padding-inline: 0 var(--size-2);\n text-align: center;\n white-space: nowrap;\n border-right: none;\n border-left: none;\n }\n\n .dg-expand-btn {\n --button-color: var(--color-contrast-500);\n --button-radius: var(--rounded-sm);\n\n width: var(--size-5);\n height: var(--size-5);\n }\n\n .dg-expand-btn:hover {\n --button-color: var(--color-contrast-900);\n }\n\n .dg-expand-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* ── Loading overlay ──────────────────────────────────────────────────────── */\n\n .dg-loading-overlay {\n position: absolute;\n inset: 0;\n z-index: 3;\n display: none;\n align-items: center;\n justify-content: center;\n background: color-mix(in oklch, var(--_bg, white) 40%, transparent);\n backdrop-filter: blur(1px);\n }\n\n :host([loading]) .dg-loading-overlay {\n display: flex;\n }\n\n :host([loading]) .dg-scroll table {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .dg-spin {\n animation: dg-spin 1s linear infinite;\n }\n\n @keyframes dg-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"datagrid.css.js","names":[],"sources":["../../../src/inputs/datagrid/datagrid.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n /* ── Datagrid-specific token overrides on top of tableBaseMixin ─────── */\n --_row-hover-bg: var(--datagrid-row-hover-bg, color-mix(in oklch, var(--color-contrast-500) 5%, transparent));\n --_row-selected-bg: var(--datagrid-row-selected-bg, color-mix(in oklch, var(--_accent) 10%, transparent));\n --_row-selected-border: var(--datagrid-row-selected-border, var(--_accent));\n }\n\n /* ── Scroll wrapper ───────────────────────────────────────────────────────── */\n\n .dg-scroll {\n max-height: var(--datagrid-max-height, none);\n overflow: auto;\n }\n\n /* ── Table ────────────────────────────────────────────────────────────────── */\n\n .dg-table {\n width: 100%;\n font-size: var(--_font-size);\n table-layout: auto;\n border-collapse: collapse;\n }\n\n /* ── Head ─────────────────────────────────────────────────────────────────── */\n\n .dg-head {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--_header-bg);\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-th {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n\n &:has(.dg-sort-btn) {\n padding: 0;\n }\n }\n\n .dg-sort-btn {\n --button-padding: var(--_cell-padding-y) var(--_cell-padding-x);\n --button-font-size: var(--text-xs);\n --button-font-weight: var(--font-semibold);\n --button-color: var(--color-contrast-500);\n --button-justify: flex-start;\n\n height: 100%;\n }\n\n .dg-th[data-align='center'] .dg-sort-btn {\n --button-justify: center;\n }\n\n .dg-th[data-align='right'] .dg-sort-btn {\n --button-justify: flex-end;\n }\n\n :host(:dir(rtl)) .dg-th,\n :dir(rtl) .dg-th {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-th:last-child,\n .dg-th.dg-th-check,\n .dg-th.dg-th-expand,\n .dg-th.dg-th-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-th:last-child,\n :host(:dir(rtl)) .dg-th.dg-th-check,\n :host(:dir(rtl)) .dg-th.dg-th-expand,\n :host(:dir(rtl)) .dg-th.dg-th-last,\n :dir(rtl) .dg-th:last-child,\n :dir(rtl) .dg-th.dg-th-check,\n :dir(rtl) .dg-th.dg-th-expand,\n :dir(rtl) .dg-th.dg-th-last {\n border-left: none;\n }\n\n .dg-th-inner {\n position: relative;\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-th[aria-sort]:has(.dg-sort-btn:hover):not(:has(.dg-sort-btn[aria-disabled='true'])) {\n background: color-mix(in oklch, var(--color-contrast-900) 5%, transparent);\n }\n\n .dg-th[aria-sort='ascending'] .dg-sort-icon,\n .dg-th[aria-sort='descending'] .dg-sort-icon {\n color: var(--_accent);\n }\n\n /* Select-all checkbox cell */\n .dg-th-check {\n width: var(--size-5);\n min-width: var(--size-5);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n }\n\n .dg-th-check,\n .dg-td-check {\n vertical-align: middle;\n text-align: center;\n }\n\n .dg-td.dg-td-check {\n padding: var(--size-1) 0 0;\n }\n\n /* ore-checkbox used for select-all and row checkboxes: label-less, compact */\n .dg-check {\n display: inline-flex;\n gap: 0;\n align-items: center;\n justify-content: center;\n min-height: 0;\n cursor: pointer;\n }\n\n /* ── Body rows ────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n .dg-body .dg-tr:hover:not([data-disabled], [aria-selected='true']) {\n background: var(--_row-hover-bg);\n }\n\n /* ── Selection ─────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n .dg-body .dg-tr[data-selectable] {\n cursor: pointer;\n outline: none;\n }\n\n .dg-body .dg-tr[data-selectable]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--_row-selected-border);\n }\n\n .dg-body .dg-tr[data-disabled] {\n pointer-events: none;\n opacity: 0.45;\n }\n\n /* Striped rows */\n :host([striped]) .dg-body .dg-tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n :host([striped]) .dg-body .dg-tr:nth-child(even)[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n /* ── Body cells ────────────────────────────────────────────────────────────── */\n\n .dg-td {\n max-width: var(--datagrid-cell-max-width, 32ch);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n color: var(--color-contrast-800);\n white-space: nowrap;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n }\n\n :host(:dir(rtl)) .dg-td,\n :dir(rtl) .dg-td {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-td:last-child,\n .dg-td.dg-td-check,\n .dg-td.dg-td-expand,\n .dg-td.dg-td-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-td:last-child,\n :host(:dir(rtl)) .dg-td.dg-td-check,\n :host(:dir(rtl)) .dg-td.dg-td-expand,\n :host(:dir(rtl)) .dg-td.dg-td-last,\n :dir(rtl) .dg-td:last-child,\n :dir(rtl) .dg-td.dg-td-check,\n :dir(rtl) .dg-td.dg-td-expand,\n :dir(rtl) .dg-td.dg-td-last {\n border-left: none;\n }\n\n /* ── Empty state ──────────────────────────────────────────────────────────── */\n\n .dg-empty {\n padding: var(--size-12) var(--_cell-padding-x);\n font-size: var(--text-sm);\n color: var(--color-contrast-400);\n text-align: center;\n }\n\n .dg-empty-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-4);\n align-items: center;\n justify-content: center;\n }\n\n .dg-empty-actions {\n margin-top: var(--size-2);\n }\n\n /* ── Controls bar (tabs + action row) ───────────────────────────────────── */\n\n .dg-controls {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n height: var(--size-8);\n padding: var(--size-1-5) var(--_cell-padding-x);\n background: var(--_header-bg);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n /* ── Tab system (left) ───────────────────────────────────────────────────── */\n\n .dg-tabs {\n display: flex;\n flex: 1;\n gap: var(--size-1);\n align-items: center;\n min-width: 0;\n overflow: hidden;\n }\n\n .dg-tab {\n --button-padding: var(--size-0-5) var(--size-2-5);\n --button-font-size: var(--text-xs);\n --button-color: var(--color-contrast-500);\n\n flex: 1;\n min-width: 0;\n }\n\n @media (max-width: 40rem) {\n .dg-tabs {\n overflow-x: auto;\n overscroll-behavior-inline: contain;\n scrollbar-width: none;\n }\n\n .dg-tabs::-webkit-scrollbar {\n display: none;\n }\n\n .dg-tab {\n flex: 0 0 auto;\n min-width: max-content;\n }\n }\n\n .dg-tab--active {\n --button-color: var(--color-contrast-900);\n --button-bg: color-mix(in oklch, var(--color-contrast-500) 10%, transparent);\n --button-border-color: var(--_border-color);\n --button-border: var(--border);\n\n font-weight: var(--font-semibold);\n }\n\n /* ── Action bar (right) ──────────────────────────────────────────────────── */\n\n .dg-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n align-items: center;\n }\n\n /* ── Shared icon button: used by action bar, search-expand clear, popover header icons ── */\n\n .dg-action-divider {\n display: inline-block;\n flex-shrink: 0;\n width: var(--border);\n height: var(--size-4);\n margin-inline: var(--size-1);\n background: var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-filter {\n display: block;\n width: 100%;\n }\n\n /* ── Search toggle (rightmost action) ───────────────────────────────────── */\n\n .dg-search {\n display: flex;\n flex-direction: row-reverse;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-search-input {\n --input-height: var(--size-7);\n display: block;\n flex-shrink: 0;\n width: 0;\n overflow: hidden;\n opacity: 0;\n transition:\n width var(--transition-fast),\n opacity var(--transition-fast);\n }\n\n .dg-search--open .dg-search-input {\n width: 12rem;\n overflow: visible;\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .dg-search-input {\n transition: none;\n }\n }\n\n /* ── ore-popover: action popover wrapper ─────────────────────────────────── */\n\n .dg-action-popover {\n display: inline-flex;\n }\n\n /* ── Popover content: shared header row ─────────────────────────────────── */\n\n .dg-pop-header {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n min-height: var(--size-10);\n padding: var(--size-2) var(--size-3);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .dg-pop-title {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-600);\n letter-spacing: 0.02em;\n }\n\n .dg-pop-heading,\n .dg-pop-heading > * {\n display: block;\n min-width: 0;\n }\n\n .dg-pop-heading small {\n margin-block-start: var(--size-0-5);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n }\n\n /* ── Sort / Group by popover content ────────────────────────────────────── */\n\n .dg-pop-sort {\n display: flex;\n flex-direction: column;\n }\n\n .dg-pop-sort-row {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-select,\n .dg-pop-dir-select {\n --input-min-width: 0;\n --select-min-width: 0;\n width: 0;\n min-width: 0;\n }\n\n .dg-pop-select {\n flex: 2 1 0;\n }\n\n .dg-pop-dir-select {\n flex: 0 0 var(--size-28);\n width: var(--size-28);\n }\n\n /* ── Filter popover content ──────────────────────────────────────────────── */\n\n .dg-pop-filter {\n display: flex;\n flex-direction: column;\n }\n\n /* Field picker: select columns to add filter rules. */\n .dg-pop-filter-fields {\n padding: var(--size-2) var(--size-3);\n }\n\n /* ── Filter rules section ────────────────────────────────────────────────── */\n\n .dg-pop-filter-rules {\n padding-block-start: var(--size-1);\n border-block-start: var(--border) solid var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-pop-filter-rule {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-filter-rule-header {\n display: flex;\n gap: var(--size-1-5);\n align-items: center;\n }\n\n .dg-pop-filter-field {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-800);\n white-space: nowrap;\n }\n\n .dg-pop-filter-op-select {\n --input-min-width: 0;\n --select-min-width: 0;\n --select-height: var(--size-6);\n --select-font-size: var(--text-2xs);\n --select-padding-x: var(--size-1-5);\n\n flex: 1 1 0;\n min-width: 6rem;\n }\n\n .dg-state-trigger {\n position: relative;\n }\n\n .dg-state-trigger[data-customized]::after {\n position: absolute;\n inset-block-start: var(--size-1);\n inset-inline-end: var(--size-1);\n width: var(--size-1-5);\n height: var(--size-1-5);\n content: '';\n background: var(--color-primary);\n border: var(--border) solid var(--_header-bg);\n border-radius: 50%;\n }\n\n .dg-pop-col {\n width: 18rem;\n }\n\n .dg-pop-col-list {\n display: grid;\n gap: var(--size-0-5);\n max-height: min(50vh, 24rem);\n padding: var(--size-2) var(--size-3);\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n\n .dg-pop-col-item {\n --checkbox-size: var(--size-4);\n --checkbox-font-size: var(--text-sm);\n\n width: 100%;\n padding: var(--size-1) var(--size-2);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .dg-pop-col-item:hover {\n background: var(--color-contrast-150);\n }\n\n .dg-pop-col-item::part(label) {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ── Footer / pagination bar ─────────────────────────────────────────────── */\n\n .dg-footer {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-1) var(--_cell-padding-x);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n background: var(--_header-bg);\n }\n\n .dg-footer-info {\n white-space: nowrap;\n }\n\n .dg-footer-start {\n display: flex;\n flex: 1 1 auto;\n gap: var(--size-2);\n align-items: center;\n min-width: 0;\n }\n\n .dg-page-size-wrap {\n flex-shrink: 0;\n order: -1;\n width: var(--size-20);\n }\n\n .dg-page-size-select {\n --input-height: var(--size-8);\n --input-min-width: 0;\n --select-min-width: 0;\n --select-font-size: var(--text-xs);\n --select-padding-x: var(--size-2);\n }\n\n .dg-pagination {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-1);\n align-items: center;\n margin-inline-start: auto;\n }\n\n :host(:dir(rtl)) .dg-page-btn,\n :dir(rtl) .dg-page-btn {\n transform: scaleX(-1);\n }\n\n .dg-page-btn {\n --button-color: var(--color-contrast-600);\n --button-radius: var(--rounded-md);\n\n width: var(--size-7);\n height: var(--size-7);\n }\n\n .dg-page-btn:hover {\n --button-color: var(--color-contrast-900);\n --button-bg: var(--color-contrast-300);\n }\n\n .dg-page-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n }\n\n .dg-page-label {\n padding-inline: var(--size-1);\n white-space: nowrap;\n }\n\n /* ── Column resize handle (F4) ───────────────────────────────────────────── */\n\n .dg-col-resize {\n position: absolute;\n inset-block: calc(var(--_cell-padding-y) * -1);\n inset-inline-end: calc((var(--_cell-padding-x) + var(--border) + 2px) * -1);\n width: var(--size-1);\n cursor: col-resize;\n user-select: none;\n background: transparent;\n border-radius: var(--rounded);\n transition: background var(--transition-fast);\n }\n\n .dg-col-resize:hover,\n .dg-col-resize:active {\n background: var(--_accent, var(--color-contrast-300));\n }\n\n /* ── Cell keyboard focus (ARIA grid roving tabindex) ─────────────────────── */\n\n .dg-td:focus-visible,\n .dg-th:focus-visible {\n outline: 2px solid var(--_row-selected-border);\n outline-offset: -2px;\n }\n\n /* ── Row expansion ────────────────────────────────────────────────────────── */\n\n .dg-tr-expanded .dg-td-expanded {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n border: none;\n\n /* Paint background + bottom separator via box-shadow so it is\n direction-agnostic and covers the host outer border on both sides. */\n box-shadow:\n inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-600) 4%, var(--_bg, transparent)),\n inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-tr-expanded:last-child .dg-td-expanded {\n box-shadow: inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-900) 4%, var(--_bg, transparent));\n }\n\n .dg-tr:has(+ .dg-tr-expanded) {\n border-block-end: none;\n }\n\n .dg-th-expand,\n .dg-td-expand {\n width: var(--size-8);\n min-width: var(--size-8);\n padding-block: var(--size-1) 0;\n padding-inline: 0 var(--size-2);\n text-align: center;\n white-space: nowrap;\n border-right: none;\n border-left: none;\n }\n\n .dg-expand-btn {\n --button-color: var(--color-contrast-500);\n --button-radius: var(--rounded-sm);\n\n width: var(--size-5);\n height: var(--size-5);\n }\n\n .dg-expand-btn:hover {\n --button-color: var(--color-contrast-900);\n }\n\n .dg-expand-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* ── Loading overlay ──────────────────────────────────────────────────────── */\n\n .dg-loading-overlay {\n position: absolute;\n inset: 0;\n z-index: 3;\n display: none;\n align-items: center;\n justify-content: center;\n background: color-mix(in oklch, var(--_bg, white) 40%, transparent);\n backdrop-filter: blur(1px);\n }\n\n :host([loading]) .dg-loading-overlay {\n display: flex;\n }\n\n :host([loading]) .dg-scroll table {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .dg-spin {\n animation: dg-spin 1s linear infinite;\n }\n\n @keyframes dg-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n }\n}\n"],"mappings":""}
@@ -12,12 +12,12 @@ export type { DataGridLabels } from './datagrid-labels';
12
12
  export type { DataGridColumn, DataGridView, FilterOperator, FilterOption } from './datagrid-model';
13
13
  /**
14
14
  * Returns the Lucide icon name for a column's sort state.
15
- * Pure function — no closure dependency on the grid model.
15
+ * Pure function: no closure dependency on the grid model.
16
16
  */
17
17
  export declare function sortIconName(state: SortState, key: string): string;
18
18
  /**
19
19
  * Returns the WAI-ARIA `aria-sort` value for a column.
20
- * Pure function — independently unit-testable.
20
+ * Pure function: independently unit-testable.
21
21
  */
22
22
  export declare function ariaSortValue(state: SortState, key: string): 'ascending' | 'descending' | 'none';
23
23
  export type OreDataGridEvents<T = Record<string, unknown>> = {
@@ -119,7 +119,7 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
119
119
  */
120
120
  pageSizeOptions?: number[];
121
121
  /**
122
- * Row data. Pass as a JS property — not serialisable to an HTML attribute.
122
+ * Row data. Pass as a JS property: not serialisable to an HTML attribute.
123
123
  * @example
124
124
  * ```js
125
125
  * grid.rows = [{ id: '1', name: 'Alice', email: 'alice@example.com' }];
@@ -149,7 +149,7 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
149
149
  sortMode?: SortMode;
150
150
  /**
151
151
  * A reactive data source from `@vielzeug/sourcerer` (or any compatible object).
152
- * When set, the source drives row data, pagination, and search — the `rows` prop is ignored.
152
+ * When set, the source drives row data, pagination, and search: the `rows` prop is ignored.
153
153
  * Client-side sort and filter are bypassed; update source params from grid events externally.
154
154
  * @example
155
155
  * ```js
@@ -248,7 +248,7 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
248
248
  /**
249
249
  * Minimal structural interface for a reactive data source accepted by `ore-datagrid`.
250
250
  *
251
- * Any page-shaped `@vielzeug/sourcerer` source satisfies this interface automatically — no direct
251
+ * Any page-shaped `@vielzeug/sourcerer` source satisfies this interface automatically: no direct
252
252
  * sourcerer import is required in refine.
253
253
  *
254
254
  * When `source` is set on the grid:
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.css.cjs","names":[],"sources":["../../../src/inputs/date-picker/date-picker.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-focus));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-focus));\n --_day-outside-opacity: var(--date-picker-day-outside-opacity, 0.35);\n --_bg: var(--date-picker-bg, var(--color-canvas));\n --_border-color: var(--date-picker-border-color, var(--color-contrast-200));\n --_radius: var(--date-picker-radius, var(--rounded-xl));\n\n display: inline-block;\n min-width: var(--date-picker-min-width, 12rem);\n }\n\n :host([fullwidth]) {\n display: block;\n min-width: 0;\n }\n\n :host([color='secondary']) {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-base));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-base));\n }\n\n /* Make the trigger look like a button — ore-input is readonly so pointer changes to text by\n default. `width: 100%` matters just as much — see ore-select's own `.trigger` for why:\n `:host` is `display: inline-block`, which never stretches its own children to fill its own\n box the way a flex/grid container with `stretch` alignment would. */\n .trigger {\n width: 100%;\n cursor: pointer;\n }\n\n .trigger::part(input) {\n cursor: pointer;\n caret-color: transparent;\n user-select: none;\n }\n\n /* ── Calendar popup ──────────────────────────────────────────────────────── */\n\n /* `fixed` (not `absolute`) — positioned via JS-computed top/left (see the positioner in\n date-picker.ts), same as ore-select's own `.dropdown`. Laid out relative to the viewport,\n so it never affects any ancestor's box size or scrollable-overflow area — `absolute`\n positioned relative to the host did, once the host sat near a scroll/clip ancestor's edge. */\n .calendar {\n position: fixed;\n inset: 0 auto auto 0;\n z-index: var(--z-dropdown, 8000);\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n min-width: 18rem;\n padding: var(--size-3);\n pointer-events: none;\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n box-shadow: var(--date-picker-shadow, var(--shadow-lg));\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: translateY(var(--overlay-enter-translate-y));\n transition: var(\n --_motion-transition,\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete\n );\n }\n\n .calendar[data-open] {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: translateY(var(--overlay-enter-translate-y));\n }\n }\n\n /* ── Calendar header ─────────────────────────────────────────────────────── */\n\n .cal-header {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n justify-content: space-between;\n padding-block-end: var(--size-2);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-8);\n height: var(--size-8);\n padding: 0;\n color: var(--color-contrast-600);\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .nav-btn:hover {\n color: var(--color-contrast-900);\n background: var(--color-contrast-300);\n }\n\n .nav-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-btn {\n flex: 1;\n padding: var(--size-1) var(--size-2);\n font-size: var(--text-sm);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-900);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition: background var(--transition-fast);\n }\n\n .cal-label-btn:hover {\n background: var(--color-contrast-300);\n }\n\n .cal-label-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-month,\n .cal-label-year {\n display: inline-block;\n }\n\n .cal-label-sep {\n display: inline-block;\n padding-inline: var(--size-1);\n font-weight: var(--font-normal);\n color: var(--color-contrast-400);\n }\n\n /* ── Day grid ────────────────────────────────────────────────────────────── */\n\n /* Prevent display:flex from overriding the [hidden] attribute */\n .cal-grid[hidden] {\n display: none;\n }\n\n .cal-grid {\n display: flex;\n flex-direction: column;\n gap: var(--size-0-5);\n }\n\n .cal-grid-days {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n gap: var(--size-0-5);\n min-height: calc(7 * var(--size-10) + 6 * var(--size-0-5));\n }\n\n /* role=\"row\" wrappers exist for ARIA grid structure only — `display: contents`\n removes them from layout so their children still participate directly in the\n parent's CSS grid tracks. */\n .cal-grid [role='row'] {\n display: contents;\n }\n\n .cal-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--text-sm);\n cursor: pointer;\n outline: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .cal-cell-head {\n height: var(--size-8);\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n color: var(--color-contrast-500);\n cursor: default;\n }\n\n .cal-cell-day {\n height: var(--size-8);\n color: var(--color-contrast-800);\n }\n\n .cal-cell-day:hover:not([data-disabled]) {\n color: var(--color-contrast-900);\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-day:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-today] {\n font-weight: var(--font-semibold);\n color: var(--_day-today-color);\n }\n\n .cal-cell-day[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-outside] {\n opacity: var(--_day-outside-opacity);\n }\n\n .cal-cell-day[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Month / Year grids ──────────────────────────────────────────────────── */\n\n .cal-grid-months,\n .cal-grid-years {\n /* 4-column grid — same width as cal-grid-days, natural height. */\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: var(--size-1);\n }\n\n .cal-cell-month,\n .cal-cell-year {\n height: var(--size-10);\n font-size: var(--text-sm);\n color: var(--color-contrast-800);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .cal-cell-month:hover:not([data-disabled]),\n .cal-cell-year:hover:not([data-disabled]) {\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-month:focus-visible,\n .cal-cell-year:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-selected],\n .cal-cell-year[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-disabled],\n .cal-cell-year[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n @media (prefers-reduced-motion: reduce) {\n .field,\n .nav-btn,\n .cal-label-btn,\n .cal-cell {\n transition: none;\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"date-picker.css.cjs","names":[],"sources":["../../../src/inputs/date-picker/date-picker.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-focus));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-focus));\n --_day-outside-opacity: var(--date-picker-day-outside-opacity, 0.35);\n --_bg: var(--date-picker-bg, var(--color-canvas));\n --_border-color: var(--date-picker-border-color, var(--color-contrast-200));\n --_radius: var(--date-picker-radius, var(--rounded-xl));\n\n display: inline-block;\n min-width: var(--date-picker-min-width, 12rem);\n }\n\n :host([fullwidth]) {\n display: block;\n min-width: 0;\n }\n\n :host([color='secondary']) {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-base));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-base));\n }\n\n /* Make the trigger look like a button: ore-input is readonly so pointer changes to text by\n default. `width: 100%` matters just as much: see ore-select's own `.trigger` for why:\n `:host` is `display: inline-block`, which never stretches its own children to fill its own\n box the way a flex/grid container with `stretch` alignment would. */\n .trigger {\n width: 100%;\n cursor: pointer;\n }\n\n .trigger::part(input) {\n cursor: pointer;\n caret-color: transparent;\n user-select: none;\n }\n\n /* ── Calendar popup ──────────────────────────────────────────────────────── */\n\n /* `fixed` (not `absolute`): positioned via JS-computed top/left (see the positioner in\n date-picker.ts), same as ore-select's own `.dropdown`. Laid out relative to the viewport,\n so it never affects any ancestor's box size or scrollable-overflow area: `absolute`\n positioned relative to the host did, once the host sat near a scroll/clip ancestor's edge. */\n .calendar {\n position: fixed;\n inset: 0 auto auto 0;\n z-index: var(--z-dropdown, 8000);\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n min-width: 18rem;\n padding: var(--size-3);\n pointer-events: none;\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n box-shadow: var(--date-picker-shadow, var(--shadow-lg));\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: translateY(var(--overlay-enter-translate-y));\n transition: var(\n --_motion-transition,\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete\n );\n }\n\n .calendar[data-open] {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: translateY(var(--overlay-enter-translate-y));\n }\n }\n\n /* ── Calendar header ─────────────────────────────────────────────────────── */\n\n .cal-header {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n justify-content: space-between;\n padding-block-end: var(--size-2);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-8);\n height: var(--size-8);\n padding: 0;\n color: var(--color-contrast-600);\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .nav-btn:hover {\n color: var(--color-contrast-900);\n background: var(--color-contrast-300);\n }\n\n .nav-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-btn {\n flex: 1;\n padding: var(--size-1) var(--size-2);\n font-size: var(--text-sm);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-900);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition: background var(--transition-fast);\n }\n\n .cal-label-btn:hover {\n background: var(--color-contrast-300);\n }\n\n .cal-label-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-month,\n .cal-label-year {\n display: inline-block;\n }\n\n .cal-label-sep {\n display: inline-block;\n padding-inline: var(--size-1);\n font-weight: var(--font-normal);\n color: var(--color-contrast-400);\n }\n\n /* ── Day grid ────────────────────────────────────────────────────────────── */\n\n /* Prevent display:flex from overriding the [hidden] attribute */\n .cal-grid[hidden] {\n display: none;\n }\n\n .cal-grid {\n display: flex;\n flex-direction: column;\n gap: var(--size-0-5);\n }\n\n .cal-grid-days {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n gap: var(--size-0-5);\n min-height: calc(7 * var(--size-10) + 6 * var(--size-0-5));\n }\n\n /* role=\"row\" wrappers exist for ARIA grid structure only: `display: contents`\n removes them from layout so their children still participate directly in the\n parent's CSS grid tracks. */\n .cal-grid [role='row'] {\n display: contents;\n }\n\n .cal-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--text-sm);\n cursor: pointer;\n outline: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .cal-cell-head {\n height: var(--size-8);\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n color: var(--color-contrast-500);\n cursor: default;\n }\n\n .cal-cell-day {\n height: var(--size-8);\n color: var(--color-contrast-800);\n }\n\n .cal-cell-day:hover:not([data-disabled]) {\n color: var(--color-contrast-900);\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-day:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-today] {\n font-weight: var(--font-semibold);\n color: var(--_day-today-color);\n }\n\n .cal-cell-day[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-outside] {\n opacity: var(--_day-outside-opacity);\n }\n\n .cal-cell-day[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Month / Year grids ──────────────────────────────────────────────────── */\n\n .cal-grid-months,\n .cal-grid-years {\n /* 4-column grid: same width as cal-grid-days, natural height. */\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: var(--size-1);\n }\n\n .cal-cell-month,\n .cal-cell-year {\n height: var(--size-10);\n font-size: var(--text-sm);\n color: var(--color-contrast-800);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .cal-cell-month:hover:not([data-disabled]),\n .cal-cell-year:hover:not([data-disabled]) {\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-month:focus-visible,\n .cal-cell-year:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-selected],\n .cal-cell-year[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-disabled],\n .cal-cell-year[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n @media (prefers-reduced-motion: reduce) {\n .field,\n .nav-btn,\n .cal-label-btn,\n .cal-cell {\n transition: none;\n }\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.css.js","names":[],"sources":["../../../src/inputs/date-picker/date-picker.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-focus));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-focus));\n --_day-outside-opacity: var(--date-picker-day-outside-opacity, 0.35);\n --_bg: var(--date-picker-bg, var(--color-canvas));\n --_border-color: var(--date-picker-border-color, var(--color-contrast-200));\n --_radius: var(--date-picker-radius, var(--rounded-xl));\n\n display: inline-block;\n min-width: var(--date-picker-min-width, 12rem);\n }\n\n :host([fullwidth]) {\n display: block;\n min-width: 0;\n }\n\n :host([color='secondary']) {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-base));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-base));\n }\n\n /* Make the trigger look like a button — ore-input is readonly so pointer changes to text by\n default. `width: 100%` matters just as much — see ore-select's own `.trigger` for why:\n `:host` is `display: inline-block`, which never stretches its own children to fill its own\n box the way a flex/grid container with `stretch` alignment would. */\n .trigger {\n width: 100%;\n cursor: pointer;\n }\n\n .trigger::part(input) {\n cursor: pointer;\n caret-color: transparent;\n user-select: none;\n }\n\n /* ── Calendar popup ──────────────────────────────────────────────────────── */\n\n /* `fixed` (not `absolute`) — positioned via JS-computed top/left (see the positioner in\n date-picker.ts), same as ore-select's own `.dropdown`. Laid out relative to the viewport,\n so it never affects any ancestor's box size or scrollable-overflow area — `absolute`\n positioned relative to the host did, once the host sat near a scroll/clip ancestor's edge. */\n .calendar {\n position: fixed;\n inset: 0 auto auto 0;\n z-index: var(--z-dropdown, 8000);\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n min-width: 18rem;\n padding: var(--size-3);\n pointer-events: none;\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n box-shadow: var(--date-picker-shadow, var(--shadow-lg));\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: translateY(var(--overlay-enter-translate-y));\n transition: var(\n --_motion-transition,\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete\n );\n }\n\n .calendar[data-open] {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: translateY(var(--overlay-enter-translate-y));\n }\n }\n\n /* ── Calendar header ─────────────────────────────────────────────────────── */\n\n .cal-header {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n justify-content: space-between;\n padding-block-end: var(--size-2);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-8);\n height: var(--size-8);\n padding: 0;\n color: var(--color-contrast-600);\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .nav-btn:hover {\n color: var(--color-contrast-900);\n background: var(--color-contrast-300);\n }\n\n .nav-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-btn {\n flex: 1;\n padding: var(--size-1) var(--size-2);\n font-size: var(--text-sm);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-900);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition: background var(--transition-fast);\n }\n\n .cal-label-btn:hover {\n background: var(--color-contrast-300);\n }\n\n .cal-label-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-month,\n .cal-label-year {\n display: inline-block;\n }\n\n .cal-label-sep {\n display: inline-block;\n padding-inline: var(--size-1);\n font-weight: var(--font-normal);\n color: var(--color-contrast-400);\n }\n\n /* ── Day grid ────────────────────────────────────────────────────────────── */\n\n /* Prevent display:flex from overriding the [hidden] attribute */\n .cal-grid[hidden] {\n display: none;\n }\n\n .cal-grid {\n display: flex;\n flex-direction: column;\n gap: var(--size-0-5);\n }\n\n .cal-grid-days {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n gap: var(--size-0-5);\n min-height: calc(7 * var(--size-10) + 6 * var(--size-0-5));\n }\n\n /* role=\"row\" wrappers exist for ARIA grid structure only — `display: contents`\n removes them from layout so their children still participate directly in the\n parent's CSS grid tracks. */\n .cal-grid [role='row'] {\n display: contents;\n }\n\n .cal-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--text-sm);\n cursor: pointer;\n outline: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .cal-cell-head {\n height: var(--size-8);\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n color: var(--color-contrast-500);\n cursor: default;\n }\n\n .cal-cell-day {\n height: var(--size-8);\n color: var(--color-contrast-800);\n }\n\n .cal-cell-day:hover:not([data-disabled]) {\n color: var(--color-contrast-900);\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-day:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-today] {\n font-weight: var(--font-semibold);\n color: var(--_day-today-color);\n }\n\n .cal-cell-day[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-outside] {\n opacity: var(--_day-outside-opacity);\n }\n\n .cal-cell-day[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Month / Year grids ──────────────────────────────────────────────────── */\n\n .cal-grid-months,\n .cal-grid-years {\n /* 4-column grid — same width as cal-grid-days, natural height. */\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: var(--size-1);\n }\n\n .cal-cell-month,\n .cal-cell-year {\n height: var(--size-10);\n font-size: var(--text-sm);\n color: var(--color-contrast-800);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .cal-cell-month:hover:not([data-disabled]),\n .cal-cell-year:hover:not([data-disabled]) {\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-month:focus-visible,\n .cal-cell-year:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-selected],\n .cal-cell-year[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-disabled],\n .cal-cell-year[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n @media (prefers-reduced-motion: reduce) {\n .field,\n .nav-btn,\n .cal-label-btn,\n .cal-cell {\n transition: none;\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"date-picker.css.js","names":[],"sources":["../../../src/inputs/date-picker/date-picker.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-focus));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-focus));\n --_day-outside-opacity: var(--date-picker-day-outside-opacity, 0.35);\n --_bg: var(--date-picker-bg, var(--color-canvas));\n --_border-color: var(--date-picker-border-color, var(--color-contrast-200));\n --_radius: var(--date-picker-radius, var(--rounded-xl));\n\n display: inline-block;\n min-width: var(--date-picker-min-width, 12rem);\n }\n\n :host([fullwidth]) {\n display: block;\n min-width: 0;\n }\n\n :host([color='secondary']) {\n --_day-selected-bg: var(--date-picker-day-selected-bg, var(--_theme-base));\n --_day-today-color: var(--date-picker-day-today-color, var(--_theme-base));\n }\n\n /* Make the trigger look like a button: ore-input is readonly so pointer changes to text by\n default. `width: 100%` matters just as much: see ore-select's own `.trigger` for why:\n `:host` is `display: inline-block`, which never stretches its own children to fill its own\n box the way a flex/grid container with `stretch` alignment would. */\n .trigger {\n width: 100%;\n cursor: pointer;\n }\n\n .trigger::part(input) {\n cursor: pointer;\n caret-color: transparent;\n user-select: none;\n }\n\n /* ── Calendar popup ──────────────────────────────────────────────────────── */\n\n /* `fixed` (not `absolute`): positioned via JS-computed top/left (see the positioner in\n date-picker.ts), same as ore-select's own `.dropdown`. Laid out relative to the viewport,\n so it never affects any ancestor's box size or scrollable-overflow area: `absolute`\n positioned relative to the host did, once the host sat near a scroll/clip ancestor's edge. */\n .calendar {\n position: fixed;\n inset: 0 auto auto 0;\n z-index: var(--z-dropdown, 8000);\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n min-width: 18rem;\n padding: var(--size-3);\n pointer-events: none;\n background: var(--_bg);\n border: var(--border) solid var(--_border-color);\n border-radius: var(--_radius);\n box-shadow: var(--date-picker-shadow, var(--shadow-lg));\n\n /* Hidden by default */\n opacity: 0;\n backdrop-filter: blur(var(--blur-sm));\n transform: translateY(var(--overlay-enter-translate-y));\n transition: var(\n --_motion-transition,\n opacity var(--transition-fast),\n transform var(--transition-fast),\n display var(--transition-fast) allow-discrete\n );\n }\n\n .calendar[data-open] {\n pointer-events: auto;\n opacity: 1;\n transform: translateY(0);\n\n @starting-style {\n opacity: 0;\n transform: translateY(var(--overlay-enter-translate-y));\n }\n }\n\n /* ── Calendar header ─────────────────────────────────────────────────────── */\n\n .cal-header {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n justify-content: space-between;\n padding-block-end: var(--size-2);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .nav-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-8);\n height: var(--size-8);\n padding: 0;\n color: var(--color-contrast-600);\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .nav-btn:hover {\n color: var(--color-contrast-900);\n background: var(--color-contrast-300);\n }\n\n .nav-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-btn {\n flex: 1;\n padding: var(--size-1) var(--size-2);\n font-size: var(--text-sm);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-900);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: transparent;\n border: none;\n border-radius: var(--rounded-md);\n transition: background var(--transition-fast);\n }\n\n .cal-label-btn:hover {\n background: var(--color-contrast-300);\n }\n\n .cal-label-btn:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-label-month,\n .cal-label-year {\n display: inline-block;\n }\n\n .cal-label-sep {\n display: inline-block;\n padding-inline: var(--size-1);\n font-weight: var(--font-normal);\n color: var(--color-contrast-400);\n }\n\n /* ── Day grid ────────────────────────────────────────────────────────────── */\n\n /* Prevent display:flex from overriding the [hidden] attribute */\n .cal-grid[hidden] {\n display: none;\n }\n\n .cal-grid {\n display: flex;\n flex-direction: column;\n gap: var(--size-0-5);\n }\n\n .cal-grid-days {\n display: grid;\n grid-template-columns: repeat(7, 1fr);\n gap: var(--size-0-5);\n min-height: calc(7 * var(--size-10) + 6 * var(--size-0-5));\n }\n\n /* role=\"row\" wrappers exist for ARIA grid structure only: `display: contents`\n removes them from layout so their children still participate directly in the\n parent's CSS grid tracks. */\n .cal-grid [role='row'] {\n display: contents;\n }\n\n .cal-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--text-sm);\n cursor: pointer;\n outline: none;\n border-radius: var(--rounded-md);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .cal-cell-head {\n height: var(--size-8);\n font-size: var(--text-xs);\n font-weight: var(--font-medium);\n color: var(--color-contrast-500);\n cursor: default;\n }\n\n .cal-cell-day {\n height: var(--size-8);\n color: var(--color-contrast-800);\n }\n\n .cal-cell-day:hover:not([data-disabled]) {\n color: var(--color-contrast-900);\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-day:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-today] {\n font-weight: var(--font-semibold);\n color: var(--_day-today-color);\n }\n\n .cal-cell-day[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-day[data-outside] {\n opacity: var(--_day-outside-opacity);\n }\n\n .cal-cell-day[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Month / Year grids ──────────────────────────────────────────────────── */\n\n .cal-grid-months,\n .cal-grid-years {\n /* 4-column grid: same width as cal-grid-days, natural height. */\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: var(--size-1);\n }\n\n .cal-cell-month,\n .cal-cell-year {\n height: var(--size-10);\n font-size: var(--text-sm);\n color: var(--color-contrast-800);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .cal-cell-month:hover:not([data-disabled]),\n .cal-cell-year:hover:not([data-disabled]) {\n background: color-mix(in oklch, var(--_day-selected-bg) 15%, transparent);\n }\n\n .cal-cell-month:focus-visible,\n .cal-cell-year:focus-visible {\n box-shadow: 0 0 0 2px var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-selected],\n .cal-cell-year[data-selected] {\n font-weight: var(--font-semibold);\n color: var(--color-white, #fff);\n background: var(--_day-selected-bg);\n }\n\n .cal-cell-month[data-disabled],\n .cal-cell-year[data-disabled] {\n pointer-events: none;\n cursor: not-allowed;\n opacity: 0.3;\n }\n\n /* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n @media (prefers-reduced-motion: reduce) {\n .field,\n .nav-btn,\n .cal-label-btn,\n .cal-cell {\n transition: none;\n }\n }\n}\n"],"mappings":""}
@@ -38,7 +38,7 @@ export type OreDatePickerProps = {
38
38
  size?: ComponentSize;
39
39
  /**
40
40
  * Shows an inline green check icon inside the field to confirm the value has
41
- * passed validation. Ignored while `error` is set — an error always wins.
41
+ * passed validation. Ignored while `error` is set: an error always wins.
42
42
  */
43
43
  success?: boolean;
44
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"file-input-upload.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\n/** Why a file never made it into the queue: wrong type for `accept`, or over `max-size`. */\nexport type FileRejectReason = 'size' | 'type';\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n /**\n * Called with the files an `addFiles` call refused — nothing was added for them — so the\n * consumer can say so instead of the pick failing silently. `files[i]` pairs with\n * `reasons[i]`.\n */\n onReject?: (files: File[], reasons: FileRejectReason[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n // Validation failures are reported, never swallowed: a pick the queue refuses must be\n // heard by the consumer, or the interaction reads as if nothing happened.\n const accepted: File[] = [];\n const rejectedFiles: File[] = [];\n const rejectReasons: FileRejectReason[] = [];\n\n for (const file of incoming) {\n if (!matchesAccept(file, acceptList)) {\n rejectedFiles.push(file);\n rejectReasons.push('type');\n } else if (!isFileSizeAllowed(file, maxSize)) {\n rejectedFiles.push(file);\n rejectReasons.push('size');\n } else {\n accepted.push(file);\n }\n }\n\n if (rejectedFiles.length) options.onReject?.(rejectedFiles, rejectReasons, originalEvent);\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of accepted) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-input-upload.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file: see `createFileQueue`: not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport: this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file: the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\n/** Why a file never made it into the queue: wrong type for `accept`, or over `max-size`. */\nexport type FileRejectReason = 'size' | 'type';\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n /**\n * Called with the files an `addFiles` call refused: nothing was added for them, so the\n * consumer can say so instead of the pick failing silently. `files[i]` pairs with\n * `reasons[i]`.\n */\n onReject?: (files: File[], reasons: FileRejectReason[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile`: a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together: they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain: nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average: the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0: see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files`: covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n // Validation failures are reported, never swallowed: a pick the queue refuses must be\n // heard by the consumer, or the interaction reads as if nothing happened.\n const accepted: File[] = [];\n const rejectedFiles: File[] = [];\n const rejectReasons: FileRejectReason[] = [];\n\n for (const file of incoming) {\n if (!matchesAccept(file, acceptList)) {\n rejectedFiles.push(file);\n rejectReasons.push('type');\n } else if (!isFileSizeAllowed(file, maxSize)) {\n rejectedFiles.push(file);\n rejectReasons.push('size');\n } else {\n accepted.push(file);\n }\n }\n\n if (rejectedFiles.length) options.onReject?.(rejectedFiles, rejectReasons, originalEvent);\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of accepted) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line: percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ export declare const formatBytes: (bytes: number) => string;
3
3
  /** Upload lifecycle status for a single file. `idle` covers both "no `upload` handler wired up"
4
4
  * (picker-only mode, fully supported) and "not started yet". */
5
5
  export type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';
6
- /** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one
6
+ /** Per-file upload snapshot. One reactive signal per file: see `createFileQueue`: not one
7
7
  * signal holding a map, so a progress tick for one file never invalidates another's bindings. */
8
8
  export type FileUploadState = {
9
9
  error: string | null;
@@ -13,7 +13,7 @@ export type FileUploadState = {
13
13
  total: number;
14
14
  };
15
15
  /**
16
- * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,
16
+ * Consumer-supplied transport: this component has no built-in network layer (zero-dependency,
17
17
  * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).
18
18
  * This function is the seam between the UI's upload lifecycle (progress, retry, per-file
19
19
  * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3
@@ -23,7 +23,7 @@ export type FileUploadState = {
23
23
  * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that
24
24
  * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use
25
25
  * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and
26
- * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either
26
+ * simply re-send the whole file: the UI still shows a resumed-from-N-bytes experience either
27
27
  * way, since the file itself was never dropped from memory.
28
28
  */
29
29
  export type FileUploadFn = (file: File, ctx: {
@@ -43,7 +43,7 @@ export type FileQueueOptions = {
43
43
  multiple: Readable<boolean>;
44
44
  onChange: (files: File[], originalEvent?: Event) => void;
45
45
  /**
46
- * Called with the files an `addFiles` call refused — nothing was added for them — so the
46
+ * Called with the files an `addFiles` call refused: nothing was added for them, so the
47
47
  * consumer can say so instead of the pick failing silently. `files[i]` pairs with
48
48
  * `reasons[i]`.
49
49
  */
@@ -62,7 +62,7 @@ export type FileQueue = {
62
62
  removeFile(file: File, originalEvent?: Event): void;
63
63
  /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place
64
64
  * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`
65
- * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a
65
+ * for `oldFile`: a replace is a removal-and-add that happen to land in the same slot, and a
66
66
  * consumer listening only for removal (e.g. to clean up a server-side record) still needs to
67
67
  * hear that the old file is gone. */
68
68
  replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;
@@ -72,7 +72,7 @@ export type FileQueue = {
72
72
  uploadPercent(file: File): number;
73
73
  };
74
74
  /**
75
- * Owns file selection *and* the opt-in upload lifecycle together — they were never really
75
+ * Owns file selection *and* the opt-in upload lifecycle together: they were never really
76
76
  * separable (adding a file starts its upload; removing one must abort it; replacing one is both
77
77
  * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s
78
78
  * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`
@@ -1 +1 @@
1
- {"version":3,"file":"file-input-upload.js","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\n/** Why a file never made it into the queue: wrong type for `accept`, or over `max-size`. */\nexport type FileRejectReason = 'size' | 'type';\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n /**\n * Called with the files an `addFiles` call refused — nothing was added for them — so the\n * consumer can say so instead of the pick failing silently. `files[i]` pairs with\n * `reasons[i]`.\n */\n onReject?: (files: File[], reasons: FileRejectReason[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n // Validation failures are reported, never swallowed: a pick the queue refuses must be\n // heard by the consumer, or the interaction reads as if nothing happened.\n const accepted: File[] = [];\n const rejectedFiles: File[] = [];\n const rejectReasons: FileRejectReason[] = [];\n\n for (const file of incoming) {\n if (!matchesAccept(file, acceptList)) {\n rejectedFiles.push(file);\n rejectReasons.push('type');\n } else if (!isFileSizeAllowed(file, maxSize)) {\n rejectedFiles.push(file);\n rejectReasons.push('size');\n } else {\n accepted.push(file);\n }\n }\n\n if (rejectedFiles.length) options.onReject?.(rejectedFiles, rejectReasons, originalEvent);\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of accepted) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-input-upload.js","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file: see `createFileQueue`: not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport: this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file: the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\n/** Why a file never made it into the queue: wrong type for `accept`, or over `max-size`. */\nexport type FileRejectReason = 'size' | 'type';\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n /**\n * Called with the files an `addFiles` call refused: nothing was added for them, so the\n * consumer can say so instead of the pick failing silently. `files[i]` pairs with\n * `reasons[i]`.\n */\n onReject?: (files: File[], reasons: FileRejectReason[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile`: a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together: they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain: nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average: the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0: see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files`: covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n // Validation failures are reported, never swallowed: a pick the queue refuses must be\n // heard by the consumer, or the interaction reads as if nothing happened.\n const accepted: File[] = [];\n const rejectedFiles: File[] = [];\n const rejectReasons: FileRejectReason[] = [];\n\n for (const file of incoming) {\n if (!matchesAccept(file, acceptList)) {\n rejectedFiles.push(file);\n rejectReasons.push('type');\n } else if (!isFileSizeAllowed(file, maxSize)) {\n rejectedFiles.push(file);\n rejectReasons.push('size');\n } else {\n accepted.push(file);\n }\n }\n\n if (rejectedFiles.length) options.onReject?.(rejectedFiles, rejectReasons, originalEvent);\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of accepted) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line: percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-input.css.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_font-size: var(--file-input-font-size, var(--text-sm));\n --_radius: var(--file-input-radius, var(--rounded-lg));\n --_bg: var(--file-input-bg, var(--color-contrast-100));\n --_border-color: var(--file-input-border-color, var(--color-contrast-300));\n --_min-height: var(--file-input-min-height, var(--size-36));\n\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n }\n\n .file-input-wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--size-1-5);\n width: 100%;\n }\n\n /* ========================================\n Label\n ======================================== */\n\n .label-outside {\n font-size: var(--text-sm);\n font-weight: var(--font-medium);\n line-height: var(--leading-none);\n color: var(--color-contrast-500);\n cursor: default;\n user-select: none;\n transition: color var(--transition-fast);\n }\n\n /* ========================================\n Dropzone\n ======================================== */\n\n .dropzone {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n align-items: center;\n justify-content: center;\n min-height: var(--_min-height);\n padding: var(--size-6) var(--size-4);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: var(--_bg);\n border: var(--border-2) dashed var(--_border-color);\n border-radius: var(--_radius);\n transition:\n background var(--transition-fast),\n border-color var(--transition-fast),\n box-shadow var(--transition-fast);\n }\n\n .dropzone-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n align-items: center;\n pointer-events: none;\n }\n\n .dropzone-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-contrast-400);\n transition:\n color var(--transition-fast),\n transform var(--transition-fast);\n }\n\n .dropzone-title {\n font-size: var(--_font-size);\n font-weight: var(--font-medium);\n line-height: var(--leading-snug);\n color: var(--color-contrast-700);\n }\n\n .dropzone-title u {\n color: var(--_theme-focus, var(--color-primary));\n text-decoration: underline;\n text-decoration-color: transparent;\n text-underline-offset: 2px;\n transition: text-decoration-color var(--transition-fast);\n }\n\n /* Signal 01: the copy itself during an active drag, not just the border/glow. */\n .dropzone-title-active {\n color: var(--_theme-focus, var(--color-primary));\n }\n\n .dropzone-hint {\n font-size: var(--text-xs);\n line-height: var(--leading-tight);\n color: var(--color-contrast-400);\n }\n\n /* ========================================\n Helper Text\n ======================================== */\n\n .helper-text[hidden] {\n display: none;\n }\n\n .helper-text {\n padding-inline: 2px;\n font-size: var(--text-xs);\n line-height: var(--leading-tight);\n color: color-mix(in oklch, var(--color-contrast-500) 80%, var(--color-canvas));\n }\n\n /* ========================================\n File List\n ======================================== */\n\n .file-list {\n display: flex;\n flex-direction: column;\n gap: var(--size-1);\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n .file-item {\n box-sizing: border-box;\n display: flex;\n gap: var(--size-3);\n align-items: center;\n padding: var(--size-2) var(--size-3);\n background: var(--color-contrast-50);\n border: var(--border) solid var(--color-contrast-200);\n border-radius: var(--_radius);\n transition: background var(--transition-fast);\n }\n\n .file-item:hover {\n background: var(--color-contrast-100);\n }\n\n .file-icon {\n flex-shrink: 0;\n color: var(--_theme-focus, var(--color-primary));\n }\n\n .file-meta {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--size-0-5);\n min-width: 0;\n }\n\n .file-name {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-sm);\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n white-space: nowrap;\n }\n\n .file-size {\n font-size: var(--text-xs);\n color: var(--color-contrast-400);\n }\n\n .file-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n }\n\n .file-remove,\n .file-action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: var(--size-1);\n color: var(--color-contrast-400);\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: var(--rounded-sm);\n transition:\n color var(--transition-fast),\n background var(--transition-fast);\n }\n\n .file-remove:hover {\n color: var(--color-error);\n background: var(--color-error-backdrop, color-mix(in oklch, var(--color-error) 12%, transparent));\n }\n\n /* Signal 03/04: retry and replace share one neutral-hover treatment — distinct from remove's\n error-red hover so the destructive action doesn't blend in with the recovery ones. */\n .file-action:hover {\n color: var(--_theme-focus, var(--color-primary));\n background: var(--_theme-backdrop, color-mix(in oklch, var(--color-primary) 12%, transparent));\n }\n\n /* ========================================\n Upload Status (Signals 02, 03, 04)\n ======================================== */\n\n .file-progress-meta {\n font-size: var(--text-xs);\n color: var(--color-contrast-400);\n }\n\n .file-error-text {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-error);\n white-space: nowrap;\n }\n\n .file-item[data-status='success'] .file-icon {\n color: var(--color-success);\n }\n\n .file-item[data-status='error'] .file-icon {\n color: var(--color-error);\n }\n\n .file-item[data-status='error'] {\n border-color: color-mix(in oklch, var(--color-error) 40%, var(--color-contrast-200));\n }\n\n /* ========================================\n File Gallery (preview grid)\n ======================================== */\n\n .file-grid {\n --_thumb-size: var(--file-input-thumb-size, var(--size-24));\n\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(var(--_thumb-size), 1fr));\n gap: var(--size-2);\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n .file-card {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--size-1);\n }\n\n .file-thumb-frame {\n position: relative;\n display: block;\n }\n\n .file-thumb {\n box-sizing: border-box;\n display: block;\n width: 100%;\n aspect-ratio: 1;\n object-fit: cover;\n background: var(--color-contrast-100);\n border: var(--border) solid var(--color-contrast-200);\n border-radius: var(--_radius);\n }\n\n .file-thumb-generic {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-contrast-400);\n }\n\n .file-card[data-status='uploading'] .file-thumb {\n opacity: 0.6;\n }\n\n .file-card[data-status='error'] .file-thumb {\n border-color: color-mix(in oklch, var(--color-error) 50%, var(--color-contrast-200));\n }\n\n .file-card-name {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-contrast-600);\n text-align: center;\n white-space: nowrap;\n }\n\n /* Signal 04: a small badge over the thumbnail corner — an explicit success/error indicator\n next to the visual proof itself, not just a caption line below it. */\n .file-status-badge {\n position: absolute;\n top: var(--size-1);\n left: var(--size-1);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-5);\n height: var(--size-5);\n color: var(--color-canvas);\n border-radius: var(--rounded-full);\n }\n\n .file-status-badge-success {\n background: var(--color-success);\n }\n\n .file-status-badge-error {\n background: var(--color-error);\n }\n\n .file-card-actions {\n position: absolute;\n top: var(--size-1);\n right: var(--size-1);\n display: flex;\n gap: var(--size-0-5);\n }\n\n .file-card-action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: var(--size-5);\n height: var(--size-5);\n padding: 0;\n color: var(--color-contrast-50);\n cursor: pointer;\n background: color-mix(in oklch, var(--color-contrast-900) 55%, transparent);\n border: none;\n border-radius: var(--rounded-full);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .file-card-remove:hover {\n color: var(--color-canvas);\n background: var(--color-error);\n }\n\n .file-card-action:not(.file-card-remove):hover {\n color: var(--color-canvas);\n background: var(--color-primary);\n }\n\n .file-card .file-progress-meta,\n .file-card .file-error-text {\n text-align: center;\n white-space: normal;\n }\n\n /* ========================================\n Hover & Focus\n ======================================== */\n\n :host(:not([disabled])) .dropzone:hover {\n border-color: var(--color-contrast-400);\n }\n\n :host(:not([disabled])) .dropzone:hover .dropzone-title u {\n text-decoration-color: var(--_theme-focus, var(--color-primary));\n }\n\n :host(:not([disabled])) .dropzone:focus-visible {\n border-color: var(--_theme-focus, var(--color-primary));\n box-shadow: var(--_theme-shadow, var(--color-primary-focus-shadow));\n }\n\n :host(:not([disabled])) .dropzone:hover .dropzone-icon,\n :host(:not([disabled])) .dropzone:focus-visible .dropzone-icon {\n color: var(--_theme-focus, var(--color-primary));\n transform: translateY(-1px);\n }\n\n /* ========================================\n Drag-Over State\n ======================================== */\n\n :host([drag-over]) .dropzone {\n background: color-mix(in oklch, var(--_theme-base, var(--color-primary)) 8%, transparent);\n border-color: var(--_theme-focus, var(--color-primary));\n border-style: solid;\n\n /* A genuine glow (not the same subtle ring `:focus-visible` uses above) — a soft inner\n ring plus a wider, brighter halo, so an active drag reads unmistakably differently from\n a merely-focused, idle dropzone. */\n box-shadow:\n 0 0 0 3px color-mix(in oklch, var(--_theme-focus, var(--color-primary)) 25%, transparent),\n 0 0 20px 2px color-mix(in oklch, var(--_theme-focus, var(--color-primary)) 45%, transparent);\n }\n\n :host([drag-over]) .dropzone .dropzone-icon {\n color: var(--_theme-focus, var(--color-primary));\n transform: translateY(-2px) scale(1.05);\n }\n\n /* ========================================\n Error State\n ======================================== */\n\n :host([invalid]) .dropzone {\n border-color: var(--color-error);\n }\n\n :host([invalid]) .label-outside {\n color: var(--color-error);\n }\n\n /* Signal 03: an in-field icon alongside the border shift — never color alone. */\n .dropzone-icon[data-status='error'] {\n color: var(--color-error);\n }\n\n /* ========================================\n Error Text\n ======================================== */\n\n .helper-text-error {\n color: var(--color-error);\n }\n}\n\n@layer refine.variants {\n /* Solid (Default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--file-input-bg, var(--color-contrast-50));\n --_border-color: var(--file-input-border-color, var(--color-contrast-300));\n }\n\n /* Flat — a near-invisible border, deliberately weaker than bordered's\n 70%-focus border (matches button.css's flat/bordered split) so the two\n don't read as the same box at a glance. */\n :host([variant='flat']) {\n --_bg: var(--file-input-bg, var(--color-contrast-100));\n --_border-color: var(--file-input-border-color, color-mix(in oklch, var(--_theme-focus) 20%, transparent));\n }\n\n :host([variant='flat']) .dropzone:hover {\n background: var(--file-input-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --file-input-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']) .dropzone:focus-visible,\n :host([variant='flat'][drag-over]) .dropzone {\n background: var(--file-input-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--file-input-focus-border-color, color-mix(in oklch, var(--_theme-focus) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--file-input-bg, var(--_theme-backdrop));\n --_border-color: var(--file-input-border-color, color-mix(in oklch, var(--_theme-focus) 70%, transparent));\n }\n\n :host([variant='bordered']) .dropzone:hover {\n border-color: var(--file-input-hover-border-color, var(--_theme-focus));\n }\n\n /* Outline */\n :host([variant='outline']) {\n --_bg: var(--file-input-bg, transparent);\n }\n\n /* Ghost */\n\n /* Borderless at rest — only reveals a border on hover (the border-*width* stays reserved via\n a transparent color, not `none`, so revealing it on hover never shifts layout). Focus\n already gets its own themed border + glow from the generic `:focus-visible` rule above,\n regardless of variant, so it needs no ghost-specific override here. */\n :host([variant='ghost']) {\n --_bg: var(--file-input-bg, transparent);\n --_border-color: var(--file-input-border-color, transparent);\n }\n\n :host(:not([disabled])[variant='ghost']) .dropzone:hover {\n background: var(--file-input-hover-bg, var(--color-contrast-100));\n border-color: var(--file-input-hover-border-color, var(--color-contrast-200));\n }\n}\n\n@layer refine.utilities {\n :host([fullwidth]) {\n width: 100%;\n }\n}\n\n/* ========================================\n Disabled State — intentionally unlayered\n ======================================== */\n\n/*\n * The shared `disabledLoadingMixin` (pulled in via `fieldMixins`) dims `:host([disabled])`\n * with `opacity: 0.5` as bare, unlayered CSS. Cascade layers always lose to unlayered rules\n * regardless of source order or specificity, so overriding that from inside any `@layer`\n * block in this file (including `refine.overrides`) would be a no-op — this has to stay\n * unlayered too, to actually win. See `ore-input`'s `input.css` for the same pattern.\n *\n * Per the field-state UX review: opacity-based dimming reads as a washed-out/loading look\n * and can mimic reduced-contrast content for low-vision users, rather than a clearly locked\n * field. Swapped for an explicit grayscale surface + `not-allowed` cursor (the cursor already\n * comes from the shared mixin, left untouched here).\n */\n:host([disabled]) {\n opacity: 1;\n}\n\n:host([disabled]) .dropzone {\n background: var(--color-contrast-100);\n border-color: var(--color-contrast-200);\n}\n\n:host([disabled]) .dropzone-icon,\n:host([disabled]) .dropzone-title,\n:host([disabled]) .dropzone-hint,\n:host([disabled]) .dropzone-title u {\n color: var(--color-contrast-400);\n}\n\n:host([disabled]) .label-outside {\n color: var(--color-contrast-400);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"file-input.css.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_font-size: var(--file-input-font-size, var(--text-sm));\n --_radius: var(--file-input-radius, var(--rounded-lg));\n --_bg: var(--file-input-bg, var(--color-contrast-100));\n --_border-color: var(--file-input-border-color, var(--color-contrast-300));\n --_min-height: var(--file-input-min-height, var(--size-36));\n\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n }\n\n .file-input-wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--size-1-5);\n width: 100%;\n }\n\n /* ========================================\n Label\n ======================================== */\n\n .label-outside {\n font-size: var(--text-sm);\n font-weight: var(--font-medium);\n line-height: var(--leading-none);\n color: var(--color-contrast-500);\n cursor: default;\n user-select: none;\n transition: color var(--transition-fast);\n }\n\n /* ========================================\n Dropzone\n ======================================== */\n\n .dropzone {\n position: relative;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n align-items: center;\n justify-content: center;\n min-height: var(--_min-height);\n padding: var(--size-6) var(--size-4);\n text-align: center;\n cursor: pointer;\n outline: none;\n background: var(--_bg);\n border: var(--border-2) dashed var(--_border-color);\n border-radius: var(--_radius);\n transition:\n background var(--transition-fast),\n border-color var(--transition-fast),\n box-shadow var(--transition-fast);\n }\n\n .dropzone-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n align-items: center;\n pointer-events: none;\n }\n\n .dropzone-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-contrast-400);\n transition:\n color var(--transition-fast),\n transform var(--transition-fast);\n }\n\n .dropzone-title {\n font-size: var(--_font-size);\n font-weight: var(--font-medium);\n line-height: var(--leading-snug);\n color: var(--color-contrast-700);\n }\n\n .dropzone-title u {\n color: var(--_theme-focus, var(--color-primary));\n text-decoration: underline;\n text-decoration-color: transparent;\n text-underline-offset: 2px;\n transition: text-decoration-color var(--transition-fast);\n }\n\n /* Signal 01: the copy itself during an active drag, not just the border/glow. */\n .dropzone-title-active {\n color: var(--_theme-focus, var(--color-primary));\n }\n\n .dropzone-hint {\n font-size: var(--text-xs);\n line-height: var(--leading-tight);\n color: var(--color-contrast-400);\n }\n\n /* ========================================\n Helper Text\n ======================================== */\n\n .helper-text[hidden] {\n display: none;\n }\n\n .helper-text {\n padding-inline: 2px;\n font-size: var(--text-xs);\n line-height: var(--leading-tight);\n color: color-mix(in oklch, var(--color-contrast-500) 80%, var(--color-canvas));\n }\n\n /* ========================================\n File List\n ======================================== */\n\n .file-list {\n display: flex;\n flex-direction: column;\n gap: var(--size-1);\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n .file-item {\n box-sizing: border-box;\n display: flex;\n gap: var(--size-3);\n align-items: center;\n padding: var(--size-2) var(--size-3);\n background: var(--color-contrast-50);\n border: var(--border) solid var(--color-contrast-200);\n border-radius: var(--_radius);\n transition: background var(--transition-fast);\n }\n\n .file-item:hover {\n background: var(--color-contrast-100);\n }\n\n .file-icon {\n flex-shrink: 0;\n color: var(--_theme-focus, var(--color-primary));\n }\n\n .file-meta {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--size-0-5);\n min-width: 0;\n }\n\n .file-name {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-sm);\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n white-space: nowrap;\n }\n\n .file-size {\n font-size: var(--text-xs);\n color: var(--color-contrast-400);\n }\n\n .file-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n }\n\n .file-remove,\n .file-action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n padding: var(--size-1);\n color: var(--color-contrast-400);\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: var(--rounded-sm);\n transition:\n color var(--transition-fast),\n background var(--transition-fast);\n }\n\n .file-remove:hover {\n color: var(--color-error);\n background: var(--color-error-backdrop, color-mix(in oklch, var(--color-error) 12%, transparent));\n }\n\n /* Signal 03/04: retry and replace share one neutral-hover treatment: distinct from remove's\n error-red hover so the destructive action doesn't blend in with the recovery ones. */\n .file-action:hover {\n color: var(--_theme-focus, var(--color-primary));\n background: var(--_theme-backdrop, color-mix(in oklch, var(--color-primary) 12%, transparent));\n }\n\n /* ========================================\n Upload Status (Signals 02, 03, 04)\n ======================================== */\n\n .file-progress-meta {\n font-size: var(--text-xs);\n color: var(--color-contrast-400);\n }\n\n .file-error-text {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-error);\n white-space: nowrap;\n }\n\n .file-item[data-status='success'] .file-icon {\n color: var(--color-success);\n }\n\n .file-item[data-status='error'] .file-icon {\n color: var(--color-error);\n }\n\n .file-item[data-status='error'] {\n border-color: color-mix(in oklch, var(--color-error) 40%, var(--color-contrast-200));\n }\n\n /* ========================================\n File Gallery (preview grid)\n ======================================== */\n\n .file-grid {\n --_thumb-size: var(--file-input-thumb-size, var(--size-24));\n\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(var(--_thumb-size), 1fr));\n gap: var(--size-2);\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n .file-card {\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: var(--size-1);\n }\n\n .file-thumb-frame {\n position: relative;\n display: block;\n }\n\n .file-thumb {\n box-sizing: border-box;\n display: block;\n width: 100%;\n aspect-ratio: 1;\n object-fit: cover;\n background: var(--color-contrast-100);\n border: var(--border) solid var(--color-contrast-200);\n border-radius: var(--_radius);\n }\n\n .file-thumb-generic {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-contrast-400);\n }\n\n .file-card[data-status='uploading'] .file-thumb {\n opacity: 0.6;\n }\n\n .file-card[data-status='error'] .file-thumb {\n border-color: color-mix(in oklch, var(--color-error) 50%, var(--color-contrast-200));\n }\n\n .file-card-name {\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: var(--text-xs);\n color: var(--color-contrast-600);\n text-align: center;\n white-space: nowrap;\n }\n\n /* Signal 04: a small badge over the thumbnail corner: an explicit success/error indicator\n next to the visual proof itself, not just a caption line below it. */\n .file-status-badge {\n position: absolute;\n top: var(--size-1);\n left: var(--size-1);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--size-5);\n height: var(--size-5);\n color: var(--color-canvas);\n border-radius: var(--rounded-full);\n }\n\n .file-status-badge-success {\n background: var(--color-success);\n }\n\n .file-status-badge-error {\n background: var(--color-error);\n }\n\n .file-card-actions {\n position: absolute;\n top: var(--size-1);\n right: var(--size-1);\n display: flex;\n gap: var(--size-0-5);\n }\n\n .file-card-action {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: var(--size-5);\n height: var(--size-5);\n padding: 0;\n color: var(--color-contrast-50);\n cursor: pointer;\n background: color-mix(in oklch, var(--color-contrast-900) 55%, transparent);\n border: none;\n border-radius: var(--rounded-full);\n transition:\n background var(--transition-fast),\n color var(--transition-fast);\n }\n\n .file-card-remove:hover {\n color: var(--color-canvas);\n background: var(--color-error);\n }\n\n .file-card-action:not(.file-card-remove):hover {\n color: var(--color-canvas);\n background: var(--color-primary);\n }\n\n .file-card .file-progress-meta,\n .file-card .file-error-text {\n text-align: center;\n white-space: normal;\n }\n\n /* ========================================\n Hover & Focus\n ======================================== */\n\n :host(:not([disabled])) .dropzone:hover {\n border-color: var(--color-contrast-400);\n }\n\n :host(:not([disabled])) .dropzone:hover .dropzone-title u {\n text-decoration-color: var(--_theme-focus, var(--color-primary));\n }\n\n :host(:not([disabled])) .dropzone:focus-visible {\n border-color: var(--_theme-focus, var(--color-primary));\n box-shadow: var(--_theme-shadow, var(--color-primary-focus-shadow));\n }\n\n :host(:not([disabled])) .dropzone:hover .dropzone-icon,\n :host(:not([disabled])) .dropzone:focus-visible .dropzone-icon {\n color: var(--_theme-focus, var(--color-primary));\n transform: translateY(-1px);\n }\n\n /* ========================================\n Drag-Over State\n ======================================== */\n\n :host([drag-over]) .dropzone {\n background: color-mix(in oklch, var(--_theme-base, var(--color-primary)) 8%, transparent);\n border-color: var(--_theme-focus, var(--color-primary));\n border-style: solid;\n\n /* A genuine glow (not the same subtle ring `:focus-visible` uses above): a soft inner\n ring plus a wider, brighter halo, so an active drag reads unmistakably differently from\n a merely-focused, idle dropzone. */\n box-shadow:\n 0 0 0 3px color-mix(in oklch, var(--_theme-focus, var(--color-primary)) 25%, transparent),\n 0 0 20px 2px color-mix(in oklch, var(--_theme-focus, var(--color-primary)) 45%, transparent);\n }\n\n :host([drag-over]) .dropzone .dropzone-icon {\n color: var(--_theme-focus, var(--color-primary));\n transform: translateY(-2px) scale(1.05);\n }\n\n /* ========================================\n Error State\n ======================================== */\n\n :host([invalid]) .dropzone {\n border-color: var(--color-error);\n }\n\n :host([invalid]) .label-outside {\n color: var(--color-error);\n }\n\n /* Signal 03: an in-field icon alongside the border shift: never color alone. */\n .dropzone-icon[data-status='error'] {\n color: var(--color-error);\n }\n\n /* ========================================\n Error Text\n ======================================== */\n\n .helper-text-error {\n color: var(--color-error);\n }\n}\n\n@layer refine.variants {\n /* Solid (Default) */\n :host(:not([variant])),\n :host([variant='solid']) {\n --_bg: var(--file-input-bg, var(--color-contrast-50));\n --_border-color: var(--file-input-border-color, var(--color-contrast-300));\n }\n\n /* Flat: a near-invisible border, deliberately weaker than bordered's\n 70%-focus border (matches button.css's flat/bordered split) so the two\n don't read as the same box at a glance. */\n :host([variant='flat']) {\n --_bg: var(--file-input-bg, var(--color-contrast-100));\n --_border-color: var(--file-input-border-color, color-mix(in oklch, var(--_theme-focus) 20%, transparent));\n }\n\n :host([variant='flat']) .dropzone:hover {\n background: var(--file-input-hover-bg, color-mix(in oklch, var(--_theme-base) 6%, var(--color-contrast-100)));\n border-color: var(\n --file-input-hover-border-color,\n color-mix(in oklch, var(--_theme-base) 35%, var(--color-contrast-300))\n );\n }\n\n :host([variant='flat']) .dropzone:focus-visible,\n :host([variant='flat'][drag-over]) .dropzone {\n background: var(--file-input-focus-bg, color-mix(in oklch, var(--_theme-base) 8%, var(--color-canvas)));\n border-color: var(--file-input-focus-border-color, color-mix(in oklch, var(--_theme-focus) 60%, transparent));\n box-shadow: var(--_theme-shadow);\n }\n\n /* Bordered */\n :host([variant='bordered']) {\n --_bg: var(--file-input-bg, var(--_theme-backdrop));\n --_border-color: var(--file-input-border-color, color-mix(in oklch, var(--_theme-focus) 70%, transparent));\n }\n\n :host([variant='bordered']) .dropzone:hover {\n border-color: var(--file-input-hover-border-color, var(--_theme-focus));\n }\n\n /* Outline */\n :host([variant='outline']) {\n --_bg: var(--file-input-bg, transparent);\n }\n\n /* Ghost */\n\n /* Borderless at rest: only reveals a border on hover (the border-*width* stays reserved via\n a transparent color, not `none`, so revealing it on hover never shifts layout). Focus\n already gets its own themed border + glow from the generic `:focus-visible` rule above,\n regardless of variant, so it needs no ghost-specific override here. */\n :host([variant='ghost']) {\n --_bg: var(--file-input-bg, transparent);\n --_border-color: var(--file-input-border-color, transparent);\n }\n\n :host(:not([disabled])[variant='ghost']) .dropzone:hover {\n background: var(--file-input-hover-bg, var(--color-contrast-100));\n border-color: var(--file-input-hover-border-color, var(--color-contrast-200));\n }\n}\n\n@layer refine.utilities {\n :host([fullwidth]) {\n width: 100%;\n }\n}\n\n/* ========================================\n Disabled State: intentionally unlayered\n ======================================== */\n\n/*\n * The shared `disabledLoadingMixin` (pulled in via `fieldMixins`) dims `:host([disabled])`\n * with `opacity: 0.5` as bare, unlayered CSS. Cascade layers always lose to unlayered rules\n * regardless of source order or specificity, so overriding that from inside any `@layer`\n * block in this file (including `refine.overrides`) would be a no-op: this has to stay\n * unlayered too, to actually win. See `ore-input`'s `input.css` for the same pattern.\n *\n * Per the field-state UX review: opacity-based dimming reads as a washed-out/loading look\n * and can mimic reduced-contrast content for low-vision users, rather than a clearly locked\n * field. Swapped for an explicit grayscale surface + `not-allowed` cursor (the cursor already\n * comes from the shared mixin, left untouched here).\n */\n:host([disabled]) {\n opacity: 1;\n}\n\n:host([disabled]) .dropzone {\n background: var(--color-contrast-100);\n border-color: var(--color-contrast-200);\n}\n\n:host([disabled]) .dropzone-icon,\n:host([disabled]) .dropzone-title,\n:host([disabled]) .dropzone-hint,\n:host([disabled]) .dropzone-title u {\n color: var(--color-contrast-400);\n}\n\n:host([disabled]) .label-outside {\n color: var(--color-contrast-400);\n}\n"],"mappings":""}