@kapiuara/ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/README.md +764 -2
  2. package/dist/components/data/KapiPagination.d.ts +36 -0
  3. package/dist/components/data/KapiPagination.js +102 -0
  4. package/dist/components/data/KapiTable.d.ts +245 -0
  5. package/dist/components/data/KapiTable.js +748 -0
  6. package/dist/components/data/KapiTableFilter.d.ts +48 -0
  7. package/dist/components/data/KapiTableFilter.js +81 -0
  8. package/dist/components/data/index.d.ts +10 -0
  9. package/dist/components/data/tableLogic.d.ts +197 -0
  10. package/dist/components/data/tableLogic.js +145 -0
  11. package/dist/components/data/tableResizeLogic.d.ts +28 -0
  12. package/dist/components/data/tableResizeLogic.js +20 -0
  13. package/dist/components/display/KapiAnnotation.d.ts +32 -0
  14. package/dist/components/display/KapiAnnotation.js +130 -0
  15. package/dist/components/display/KapiAvatar.d.ts +27 -0
  16. package/dist/components/display/KapiAvatar.js +43 -0
  17. package/dist/components/display/KapiAvatarGroup.d.ts +19 -0
  18. package/dist/components/display/KapiAvatarGroup.js +38 -0
  19. package/dist/components/display/KapiBadge.d.ts +21 -0
  20. package/dist/components/display/KapiBadge.js +28 -0
  21. package/dist/components/display/KapiComment.d.ts +27 -0
  22. package/dist/components/display/KapiComment.js +66 -0
  23. package/dist/components/display/KapiCommentComposer.d.ts +49 -0
  24. package/dist/components/display/KapiCommentComposer.js +76 -0
  25. package/dist/components/display/KapiDescriptionList.d.ts +19 -0
  26. package/dist/components/display/KapiDescriptionList.js +35 -0
  27. package/dist/components/display/KapiFileItem.d.ts +39 -0
  28. package/dist/components/display/KapiFileItem.js +74 -0
  29. package/dist/components/display/KapiFileViewer.d.ts +51 -0
  30. package/dist/components/display/KapiFileViewer.js +107 -0
  31. package/dist/components/display/KapiIconTile.d.ts +16 -0
  32. package/dist/components/display/KapiIconTile.js +29 -0
  33. package/dist/components/display/KapiStat.d.ts +24 -0
  34. package/dist/components/display/KapiStat.js +41 -0
  35. package/dist/components/display/KapiTag.d.ts +27 -0
  36. package/dist/components/display/KapiTag.js +44 -0
  37. package/dist/components/display/KapiTimeline.d.ts +15 -0
  38. package/dist/components/display/KapiTimeline.js +30 -0
  39. package/dist/components/display/annotationLogic.d.ts +26 -0
  40. package/dist/components/display/annotationLogic.js +17 -0
  41. package/dist/components/display/avatarGroupLogic.d.ts +14 -0
  42. package/dist/components/display/avatarGroupLogic.js +13 -0
  43. package/dist/components/display/avatarLogic.d.ts +5 -0
  44. package/dist/components/display/avatarLogic.js +9 -0
  45. package/dist/components/display/commentLogic.d.ts +18 -0
  46. package/dist/components/display/commentLogic.js +7 -0
  47. package/dist/components/display/fileItemLogic.d.ts +9 -0
  48. package/dist/components/display/fileItemLogic.js +12 -0
  49. package/dist/components/display/iconTileLogic.d.ts +13 -0
  50. package/dist/components/display/iconTileLogic.js +13 -0
  51. package/dist/components/display/index.d.ts +40 -0
  52. package/dist/components/display/statLogic.d.ts +10 -0
  53. package/dist/components/display/statLogic.js +10 -0
  54. package/dist/components/display/tagLogic.d.ts +11 -0
  55. package/dist/components/display/tagLogic.js +10 -0
  56. package/dist/components/display/timelineLogic.d.ts +5 -0
  57. package/dist/components/display/timelineLogic.js +4 -0
  58. package/dist/components/feedback/KapiAlert.d.ts +28 -0
  59. package/dist/components/feedback/KapiAlert.js +53 -0
  60. package/dist/components/feedback/KapiBulkBar.d.ts +30 -0
  61. package/dist/components/feedback/KapiBulkBar.js +34 -0
  62. package/dist/components/feedback/KapiCountdown.d.ts +26 -0
  63. package/dist/components/feedback/KapiCountdown.js +43 -0
  64. package/dist/components/feedback/KapiEmpty.d.ts +14 -0
  65. package/dist/components/feedback/KapiEmpty.js +31 -0
  66. package/dist/components/feedback/KapiFab.d.ts +34 -0
  67. package/dist/components/feedback/KapiFab.js +116 -0
  68. package/dist/components/feedback/KapiOfflineBanner.d.ts +25 -0
  69. package/dist/components/feedback/KapiOfflineBanner.js +40 -0
  70. package/dist/components/feedback/KapiProgress.d.ts +24 -0
  71. package/dist/components/feedback/KapiProgress.js +94 -0
  72. package/dist/components/feedback/KapiSkeleton.d.ts +12 -0
  73. package/dist/components/feedback/KapiSkeleton.js +16 -0
  74. package/dist/components/feedback/KapiToastContainer.d.ts +16 -0
  75. package/dist/components/feedback/KapiToastContainer.js +81 -0
  76. package/dist/components/feedback/KapiTour.d.ts +40 -0
  77. package/dist/components/feedback/KapiTour.js +157 -0
  78. package/dist/components/feedback/KapiUnsavedBar.d.ts +35 -0
  79. package/dist/components/feedback/KapiUnsavedBar.js +47 -0
  80. package/dist/components/feedback/bulkBarLogic.d.ts +2 -0
  81. package/dist/components/feedback/bulkBarLogic.js +7 -0
  82. package/dist/components/feedback/countdownLogic.d.ts +74 -0
  83. package/dist/components/feedback/countdownLogic.js +70 -0
  84. package/dist/components/feedback/fabLogic.d.ts +17 -0
  85. package/dist/components/feedback/fabLogic.js +10 -0
  86. package/dist/components/feedback/index.d.ts +33 -0
  87. package/dist/components/feedback/offlineBannerLogic.d.ts +10 -0
  88. package/dist/components/feedback/offlineBannerLogic.js +8 -0
  89. package/dist/components/feedback/progressLogic.d.ts +17 -0
  90. package/dist/components/feedback/progressLogic.js +16 -0
  91. package/dist/components/feedback/toastStore.d.ts +62 -0
  92. package/dist/components/feedback/toastStore.js +38 -0
  93. package/dist/components/feedback/tourLogic.d.ts +64 -0
  94. package/dist/components/feedback/tourLogic.js +31 -0
  95. package/dist/components/feedback/unsavedBarLogic.d.ts +2 -0
  96. package/dist/components/feedback/unsavedBarLogic.js +8 -0
  97. package/dist/components/inputs/KapiAutocomplete.d.ts +81 -0
  98. package/dist/components/inputs/KapiAutocomplete.js +205 -0
  99. package/dist/components/inputs/KapiButton.d.ts +23 -0
  100. package/dist/components/inputs/KapiButton.js +25 -0
  101. package/dist/components/inputs/KapiCalendar.d.ts +35 -0
  102. package/dist/components/inputs/KapiCalendar.js +60 -0
  103. package/dist/components/inputs/KapiCalendarGrid.d.ts +29 -0
  104. package/dist/components/inputs/KapiCalendarGrid.js +65 -0
  105. package/dist/components/inputs/KapiCheckCard.d.ts +28 -0
  106. package/dist/components/inputs/KapiCheckCard.js +41 -0
  107. package/dist/components/inputs/KapiCheckbox.d.ts +7 -0
  108. package/dist/components/inputs/KapiCheckbox.js +5 -0
  109. package/dist/components/inputs/KapiDatePicker.d.ts +46 -0
  110. package/dist/components/inputs/KapiDatePicker.js +189 -0
  111. package/dist/components/inputs/KapiDateRangePicker.d.ts +49 -0
  112. package/dist/components/inputs/KapiDateRangePicker.js +179 -0
  113. package/dist/components/inputs/KapiFacetedSearch.d.ts +45 -0
  114. package/dist/components/inputs/KapiFacetedSearch.js +188 -0
  115. package/dist/components/inputs/KapiField.d.ts +33 -0
  116. package/dist/components/inputs/KapiField.js +60 -0
  117. package/dist/components/inputs/KapiFileUpload.d.ts +58 -0
  118. package/dist/components/inputs/KapiFileUpload.js +134 -0
  119. package/dist/components/inputs/KapiIconButton.d.ts +13 -0
  120. package/dist/components/inputs/KapiIconButton.js +16 -0
  121. package/dist/components/inputs/KapiInput.d.ts +92 -0
  122. package/dist/components/inputs/KapiInput.js +123 -0
  123. package/dist/components/inputs/KapiInputGroup.d.ts +37 -0
  124. package/dist/components/inputs/KapiInputGroup.js +29 -0
  125. package/dist/components/inputs/KapiMaskedInput.d.ts +52 -0
  126. package/dist/components/inputs/KapiMaskedInput.js +78 -0
  127. package/dist/components/inputs/KapiNumberStepper.d.ts +44 -0
  128. package/dist/components/inputs/KapiNumberStepper.js +100 -0
  129. package/dist/components/inputs/KapiRadio.d.ts +7 -0
  130. package/dist/components/inputs/KapiRadio.js +5 -0
  131. package/dist/components/inputs/KapiRating.d.ts +55 -0
  132. package/dist/components/inputs/KapiRating.js +99 -0
  133. package/dist/components/inputs/KapiSegmented.d.ts +41 -0
  134. package/dist/components/inputs/KapiSegmented.js +83 -0
  135. package/dist/components/inputs/KapiSelect.d.ts +109 -0
  136. package/dist/components/inputs/KapiSelect.js +443 -0
  137. package/dist/components/inputs/KapiSlider.d.ts +42 -0
  138. package/dist/components/inputs/KapiSlider.js +70 -0
  139. package/dist/components/inputs/KapiSwitch.d.ts +13 -0
  140. package/dist/components/inputs/KapiSwitch.js +28 -0
  141. package/dist/components/inputs/KapiTagInput.d.ts +79 -0
  142. package/dist/components/inputs/KapiTagInput.js +115 -0
  143. package/dist/components/inputs/KapiTextarea.d.ts +32 -0
  144. package/dist/components/inputs/KapiTextarea.js +67 -0
  145. package/dist/components/inputs/asyncSelectLogic.d.ts +42 -0
  146. package/dist/components/inputs/asyncSelectLogic.js +46 -0
  147. package/dist/components/inputs/autocompleteLogic.d.ts +73 -0
  148. package/dist/components/inputs/autocompleteLogic.js +67 -0
  149. package/dist/components/inputs/checkControl.d.ts +20 -0
  150. package/dist/components/inputs/checkControl.js +31 -0
  151. package/dist/components/inputs/datePickerLogic.d.ts +92 -0
  152. package/dist/components/inputs/datePickerLogic.js +174 -0
  153. package/dist/components/inputs/dateRangeLogic.d.ts +31 -0
  154. package/dist/components/inputs/dateRangeLogic.js +44 -0
  155. package/dist/components/inputs/facetedSearchLogic.d.ts +69 -0
  156. package/dist/components/inputs/facetedSearchLogic.js +39 -0
  157. package/dist/components/inputs/fieldLogic.d.ts +67 -0
  158. package/dist/components/inputs/fieldLogic.js +35 -0
  159. package/dist/components/inputs/fileUploadLogic.d.ts +13 -0
  160. package/dist/components/inputs/fileUploadLogic.js +9 -0
  161. package/dist/components/inputs/index.d.ts +55 -0
  162. package/dist/components/inputs/inputGroupLogic.d.ts +22 -0
  163. package/dist/components/inputs/inputGroupLogic.js +9 -0
  164. package/dist/components/inputs/inputLogic.d.ts +25 -0
  165. package/dist/components/inputs/inputLogic.js +10 -0
  166. package/dist/components/inputs/maskLogic.d.ts +34 -0
  167. package/dist/components/inputs/maskLogic.js +57 -0
  168. package/dist/components/inputs/numberStepLogic.d.ts +14 -0
  169. package/dist/components/inputs/numberStepLogic.js +21 -0
  170. package/dist/components/inputs/ratingLogic.d.ts +9 -0
  171. package/dist/components/inputs/ratingLogic.js +22 -0
  172. package/dist/components/inputs/segmentedLogic.d.ts +6 -0
  173. package/dist/components/inputs/segmentedLogic.js +14 -0
  174. package/dist/components/inputs/selectLogic.d.ts +89 -0
  175. package/dist/components/inputs/selectLogic.js +82 -0
  176. package/dist/components/inputs/sliderLogic.d.ts +9 -0
  177. package/dist/components/inputs/sliderLogic.js +17 -0
  178. package/dist/components/inputs/tagInputLogic.d.ts +40 -0
  179. package/dist/components/inputs/tagInputLogic.js +40 -0
  180. package/dist/components/inputs/textareaLogic.d.ts +17 -0
  181. package/dist/components/inputs/textareaLogic.js +13 -0
  182. package/dist/components/inputs/useCalendarFocus.d.ts +13 -0
  183. package/dist/components/inputs/useCalendarFocus.js +25 -0
  184. package/dist/components/inputs/useDatePopSide.d.ts +15 -0
  185. package/dist/components/inputs/useDatePopSide.js +20 -0
  186. package/dist/components/labelsLogic.d.ts +6 -0
  187. package/dist/components/labelsLogic.js +11 -0
  188. package/dist/components/layout/KapiAccessCard.d.ts +40 -0
  189. package/dist/components/layout/KapiAccessCard.js +66 -0
  190. package/dist/components/layout/KapiAccountMenu.d.ts +61 -0
  191. package/dist/components/layout/KapiAccountMenu.js +212 -0
  192. package/dist/components/layout/KapiCard.d.ts +43 -0
  193. package/dist/components/layout/KapiCard.js +53 -0
  194. package/dist/components/layout/KapiCluster.d.ts +15 -0
  195. package/dist/components/layout/KapiCluster.js +21 -0
  196. package/dist/components/layout/KapiContextSlot.d.ts +45 -0
  197. package/dist/components/layout/KapiContextSlot.js +92 -0
  198. package/dist/components/layout/KapiDivider.d.ts +16 -0
  199. package/dist/components/layout/KapiDivider.js +41 -0
  200. package/dist/components/layout/KapiGrid.d.ts +19 -0
  201. package/dist/components/layout/KapiGrid.js +25 -0
  202. package/dist/components/layout/KapiNotificationBell.d.ts +75 -0
  203. package/dist/components/layout/KapiNotificationBell.js +196 -0
  204. package/dist/components/layout/KapiPageHeader.d.ts +28 -0
  205. package/dist/components/layout/KapiPageHeader.js +47 -0
  206. package/dist/components/layout/KapiPanelParts.d.ts +35 -0
  207. package/dist/components/layout/KapiPanelParts.js +63 -0
  208. package/dist/components/layout/KapiRail.d.ts +40 -0
  209. package/dist/components/layout/KapiRail.js +102 -0
  210. package/dist/components/layout/KapiScrollArea.d.ts +40 -0
  211. package/dist/components/layout/KapiScrollArea.js +49 -0
  212. package/dist/components/layout/KapiShell.d.ts +40 -0
  213. package/dist/components/layout/KapiShell.js +48 -0
  214. package/dist/components/layout/KapiStack.d.ts +11 -0
  215. package/dist/components/layout/KapiStack.js +19 -0
  216. package/dist/components/layout/accessCardLogic.d.ts +18 -0
  217. package/dist/components/layout/accessCardLogic.js +11 -0
  218. package/dist/components/layout/cardLogic.d.ts +9 -0
  219. package/dist/components/layout/cardLogic.js +11 -0
  220. package/dist/components/layout/index.d.ts +33 -0
  221. package/dist/components/layout/layoutPrimitives.d.ts +51 -0
  222. package/dist/components/layout/layoutPrimitives.js +58 -0
  223. package/dist/components/layout/notificationBellLogic.d.ts +20 -0
  224. package/dist/components/layout/notificationBellLogic.js +14 -0
  225. package/dist/components/layout/railLogic.d.ts +52 -0
  226. package/dist/components/layout/railLogic.js +16 -0
  227. package/dist/components/locale/KapiLocaleProvider.d.ts +29 -0
  228. package/dist/components/locale/KapiLocaleProvider.js +27 -0
  229. package/dist/components/locale/index.d.ts +5 -0
  230. package/dist/components/locale/labelRegistry.d.ts +110 -0
  231. package/dist/components/locale/linkLabels.d.ts +6 -0
  232. package/dist/components/locale/linkLabels.js +4 -0
  233. package/dist/components/navigation/CtxSlidingList.d.ts +11 -0
  234. package/dist/components/navigation/CtxSlidingList.js +24 -0
  235. package/dist/components/navigation/KapiAccordion.d.ts +24 -0
  236. package/dist/components/navigation/KapiAccordion.js +68 -0
  237. package/dist/components/navigation/KapiBreadcrumb.d.ts +23 -0
  238. package/dist/components/navigation/KapiBreadcrumb.js +32 -0
  239. package/dist/components/navigation/KapiCollapsiblePanel.d.ts +63 -0
  240. package/dist/components/navigation/KapiCollapsiblePanel.js +83 -0
  241. package/dist/components/navigation/KapiContextPanel.d.ts +117 -0
  242. package/dist/components/navigation/KapiContextPanel.js +265 -0
  243. package/dist/components/navigation/KapiFilterChips.d.ts +30 -0
  244. package/dist/components/navigation/KapiFilterChips.js +40 -0
  245. package/dist/components/navigation/KapiNavBadge.d.ts +26 -0
  246. package/dist/components/navigation/KapiNavBadge.js +33 -0
  247. package/dist/components/navigation/KapiPanelGroup.d.ts +69 -0
  248. package/dist/components/navigation/KapiPanelGroup.js +149 -0
  249. package/dist/components/navigation/KapiSteps.d.ts +43 -0
  250. package/dist/components/navigation/KapiSteps.js +63 -0
  251. package/dist/components/navigation/KapiTabs.d.ts +56 -0
  252. package/dist/components/navigation/KapiTabs.js +81 -0
  253. package/dist/components/navigation/KapiTree.d.ts +56 -0
  254. package/dist/components/navigation/KapiTree.js +157 -0
  255. package/dist/components/navigation/KapiWizard.d.ts +49 -0
  256. package/dist/components/navigation/KapiWizard.js +104 -0
  257. package/dist/components/navigation/MiniNavItem.d.ts +35 -0
  258. package/dist/components/navigation/MiniNavItem.js +57 -0
  259. package/dist/components/navigation/accordionLogic.d.ts +4 -0
  260. package/dist/components/navigation/accordionLogic.js +4 -0
  261. package/dist/components/navigation/activeItemLogic.d.ts +7 -0
  262. package/dist/components/navigation/activeItemLogic.js +4 -0
  263. package/dist/components/navigation/breadcrumbLogic.d.ts +8 -0
  264. package/dist/components/navigation/breadcrumbLogic.js +4 -0
  265. package/dist/components/navigation/contextPanelLogic.d.ts +27 -0
  266. package/dist/components/navigation/contextPanelLogic.js +11 -0
  267. package/dist/components/navigation/filterChipsLogic.d.ts +23 -0
  268. package/dist/components/navigation/filterChipsLogic.js +20 -0
  269. package/dist/components/navigation/index.d.ts +39 -0
  270. package/dist/components/navigation/navBadgeLogic.d.ts +26 -0
  271. package/dist/components/navigation/navBadgeLogic.js +16 -0
  272. package/dist/components/navigation/panelGroupLogic.d.ts +15 -0
  273. package/dist/components/navigation/panelGroupLogic.js +13 -0
  274. package/dist/components/navigation/panelMini.d.ts +13 -0
  275. package/dist/components/navigation/panelMini.js +9 -0
  276. package/dist/components/navigation/panelMiniLogic.d.ts +19 -0
  277. package/dist/components/navigation/panelMiniLogic.js +6 -0
  278. package/dist/components/navigation/stepsLogic.d.ts +25 -0
  279. package/dist/components/navigation/stepsLogic.js +10 -0
  280. package/dist/components/navigation/tabsLogic.d.ts +12 -0
  281. package/dist/components/navigation/tabsLogic.js +14 -0
  282. package/dist/components/navigation/treeLogic.d.ts +80 -0
  283. package/dist/components/navigation/treeLogic.js +91 -0
  284. package/dist/components/navigation/wizardLogic.d.ts +11 -0
  285. package/dist/components/navigation/wizardLogic.js +7 -0
  286. package/dist/components/overlays/KapiCanvasForm.d.ts +42 -0
  287. package/dist/components/overlays/KapiCanvasForm.js +60 -0
  288. package/dist/components/overlays/KapiCommandPalette.d.ts +58 -0
  289. package/dist/components/overlays/KapiCommandPalette.js +152 -0
  290. package/dist/components/overlays/KapiConfirm.d.ts +48 -0
  291. package/dist/components/overlays/KapiConfirm.js +94 -0
  292. package/dist/components/overlays/KapiContextMenu.d.ts +51 -0
  293. package/dist/components/overlays/KapiContextMenu.js +90 -0
  294. package/dist/components/overlays/KapiDialogHost.d.ts +23 -0
  295. package/dist/components/overlays/KapiDialogHost.js +129 -0
  296. package/dist/components/overlays/KapiDropdown.d.ts +30 -0
  297. package/dist/components/overlays/KapiDropdown.js +84 -0
  298. package/dist/components/overlays/KapiLoading.d.ts +14 -0
  299. package/dist/components/overlays/KapiLoading.js +43 -0
  300. package/dist/components/overlays/KapiMenuButton.d.ts +25 -0
  301. package/dist/components/overlays/KapiMenuButton.js +109 -0
  302. package/dist/components/overlays/KapiModal.d.ts +61 -0
  303. package/dist/components/overlays/KapiModal.js +106 -0
  304. package/dist/components/overlays/KapiOffcanvas.d.ts +96 -0
  305. package/dist/components/overlays/KapiOffcanvas.js +209 -0
  306. package/dist/components/overlays/KapiOffcanvasStack.d.ts +58 -0
  307. package/dist/components/overlays/KapiOffcanvasStack.js +84 -0
  308. package/dist/components/overlays/KapiPhotoView.d.ts +60 -0
  309. package/dist/components/overlays/KapiPhotoView.js +122 -0
  310. package/dist/components/overlays/KapiPopover.d.ts +46 -0
  311. package/dist/components/overlays/KapiPopover.js +68 -0
  312. package/dist/components/overlays/KapiShapeSheet.d.ts +34 -0
  313. package/dist/components/overlays/KapiShapeSheet.js +82 -0
  314. package/dist/components/overlays/KapiSplitModal.d.ts +41 -0
  315. package/dist/components/overlays/KapiSplitModal.js +125 -0
  316. package/dist/components/overlays/KapiTooltip.d.ts +38 -0
  317. package/dist/components/overlays/KapiTooltip.js +105 -0
  318. package/dist/components/overlays/canvasFormLogic.d.ts +18 -0
  319. package/dist/components/overlays/canvasFormLogic.js +16 -0
  320. package/dist/components/overlays/commandPaletteLogic.d.ts +29 -0
  321. package/dist/components/overlays/commandPaletteLogic.js +19 -0
  322. package/dist/components/overlays/dialogStore.d.ts +114 -0
  323. package/dist/components/overlays/dialogStore.js +76 -0
  324. package/dist/components/overlays/dialogUtils.d.ts +72 -0
  325. package/dist/components/overlays/dialogUtils.js +62 -0
  326. package/dist/components/overlays/exitPresenceLogic.d.ts +14 -0
  327. package/dist/components/overlays/exitPresenceLogic.js +5 -0
  328. package/dist/components/overlays/index.d.ts +38 -0
  329. package/dist/components/overlays/menuButtonLogic.d.ts +58 -0
  330. package/dist/components/overlays/menuButtonLogic.js +9 -0
  331. package/dist/components/overlays/menuKeyboardLogic.d.ts +25 -0
  332. package/dist/components/overlays/menuKeyboardLogic.js +30 -0
  333. package/dist/components/overlays/offcanvasCompanion.d.ts +16 -0
  334. package/dist/components/overlays/offcanvasCompanion.js +6 -0
  335. package/dist/components/overlays/offcanvasResize.d.ts +22 -0
  336. package/dist/components/overlays/offcanvasResize.js +10 -0
  337. package/dist/components/overlays/offcanvasSide.d.ts +30 -0
  338. package/dist/components/overlays/offcanvasSide.js +29 -0
  339. package/dist/components/overlays/offcanvasStack.d.ts +76 -0
  340. package/dist/components/overlays/offcanvasStack.js +72 -0
  341. package/dist/components/overlays/overlayPortal.d.ts +9 -0
  342. package/dist/components/overlays/overlayPortal.js +6 -0
  343. package/dist/components/overlays/popoverLogic.d.ts +94 -0
  344. package/dist/components/overlays/popoverLogic.js +47 -0
  345. package/dist/components/overlays/splitModalLayout.d.ts +8 -0
  346. package/dist/components/overlays/splitModalLayout.js +5 -0
  347. package/dist/components/overlays/useExitPresence.d.ts +14 -0
  348. package/dist/components/overlays/useExitPresence.js +34 -0
  349. package/dist/components/overlays/useNativeDialog.d.ts +30 -0
  350. package/dist/components/overlays/useNativeDialog.js +56 -0
  351. package/dist/components/rootProps.d.ts +17 -0
  352. package/dist/components/rootProps.js +4 -0
  353. package/dist/hooks/breakpointOpenLogic.d.ts +7 -0
  354. package/dist/hooks/breakpointOpenLogic.js +4 -0
  355. package/dist/hooks/dateFormat.d.ts +12 -0
  356. package/dist/hooks/dateFormat.js +15 -0
  357. package/dist/hooks/downloadLogic.d.ts +31 -0
  358. package/dist/hooks/downloadLogic.js +85 -0
  359. package/dist/hooks/focusLogic.d.ts +18 -0
  360. package/dist/hooks/focusLogic.js +17 -0
  361. package/dist/hooks/growOriginLogic.d.ts +22 -0
  362. package/dist/hooks/growOriginLogic.js +10 -0
  363. package/dist/hooks/hotkeyLogic.d.ts +22 -0
  364. package/dist/hooks/hotkeyLogic.js +32 -0
  365. package/dist/hooks/index.d.ts +49 -0
  366. package/dist/hooks/kapiRoot.d.ts +16 -0
  367. package/dist/hooks/kapiRoot.js +9 -0
  368. package/dist/hooks/onlineStatusLogic.d.ts +31 -0
  369. package/dist/hooks/onlineStatusLogic.js +28 -0
  370. package/dist/hooks/persistentStateLogic.d.ts +12 -0
  371. package/dist/hooks/persistentStateLogic.js +17 -0
  372. package/dist/hooks/rafThrottle.d.ts +33 -0
  373. package/dist/hooks/rafThrottle.js +33 -0
  374. package/dist/hooks/recentLogic.d.ts +9 -0
  375. package/dist/hooks/recentLogic.js +10 -0
  376. package/dist/hooks/reducedMotion.d.ts +13 -0
  377. package/dist/hooks/reducedMotion.js +5 -0
  378. package/dist/hooks/rightClick.d.ts +37 -0
  379. package/dist/hooks/rightClick.js +30 -0
  380. package/dist/hooks/scrollCollapseLogic.d.ts +12 -0
  381. package/dist/hooks/scrollCollapseLogic.js +4 -0
  382. package/dist/hooks/scrollEdgesLogic.d.ts +49 -0
  383. package/dist/hooks/scrollEdgesLogic.js +19 -0
  384. package/dist/hooks/slidingIndicatorLogic.d.ts +25 -0
  385. package/dist/hooks/slidingIndicatorLogic.js +15 -0
  386. package/dist/hooks/storageLogic.d.ts +15 -0
  387. package/dist/hooks/storageLogic.js +24 -0
  388. package/dist/hooks/unsavedGuardLogic.d.ts +17 -0
  389. package/dist/hooks/unsavedGuardLogic.js +17 -0
  390. package/dist/hooks/useDragScroll.d.ts +12 -0
  391. package/dist/hooks/useDragScroll.js +32 -0
  392. package/dist/hooks/useGlobalRightClick.d.ts +14 -0
  393. package/dist/hooks/useGlobalRightClick.js +35 -0
  394. package/dist/hooks/useGrowOrigin.d.ts +13 -0
  395. package/dist/hooks/useGrowOrigin.js +16 -0
  396. package/dist/hooks/useHotkey.d.ts +15 -0
  397. package/dist/hooks/useHotkey.js +31 -0
  398. package/dist/hooks/useKapiTheme.d.ts +69 -0
  399. package/dist/hooks/useKapiTheme.js +88 -0
  400. package/dist/hooks/useMediaQuery.d.ts +6 -0
  401. package/dist/hooks/useMediaQuery.js +13 -0
  402. package/dist/hooks/useObjectUrl.d.ts +6 -0
  403. package/dist/hooks/useObjectUrl.js +19 -0
  404. package/dist/hooks/useOnlineStatus.d.ts +7 -0
  405. package/dist/hooks/useOnlineStatus.js +7 -0
  406. package/dist/hooks/usePersistentState.d.ts +19 -0
  407. package/dist/hooks/usePersistentState.js +37 -0
  408. package/dist/hooks/usePlacement.d.ts +36 -0
  409. package/dist/hooks/usePlacement.js +84 -0
  410. package/dist/hooks/useRecent.d.ts +17 -0
  411. package/dist/hooks/useRecent.js +16 -0
  412. package/dist/hooks/useScrollCollapse.d.ts +10 -0
  413. package/dist/hooks/useScrollCollapse.js +27 -0
  414. package/dist/hooks/useScrollEdges.d.ts +32 -0
  415. package/dist/hooks/useScrollEdges.js +91 -0
  416. package/dist/hooks/useSlidingIndicator.d.ts +23 -0
  417. package/dist/hooks/useSlidingIndicator.js +51 -0
  418. package/dist/hooks/useUnsavedGuard.d.ts +70 -0
  419. package/dist/hooks/useUnsavedGuard.js +47 -0
  420. package/dist/index.d.ts +12 -0
  421. package/dist/index.js +162 -0
  422. package/dist/kapi.css +2 -0
  423. package/dist/kapi.layered.css +4 -0
  424. package/package.json +86 -4
package/README.md CHANGED
@@ -1,3 +1,765 @@
1
- # Temporary Holding Version
1
+ # @kapiuara/ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Design system extraído del v2 de `ucugestionfront`. React 19 + TypeScript + Vite.
4
+ CSS nativo propio scopeado bajo `.kapi` (tokens como custom properties). Sin Radix / Bootstrap /
5
+ Tailwind / Mantine. Iconos: sólo `lucide-react`. Fuente: Nunito.
6
+
7
+ > Paquete privado del monorepo `kapiuara`. Todavía no se publica: es `private: true` y no hay nada publicado en el scope `@kapiuara` de npm. Ver `BRANDING.md` en la raíz del repo.
8
+
9
+ ## Comandos
10
+
11
+ | Comando | Qué hace |
12
+ | ------------------------------- | -------------------------------------------------------------------------------------- |
13
+ | `pnpm dev` | Playground (demo/) en dev server, usa `src/` directo vía alias `@kapiuara/ui` |
14
+ | `pnpm build` | Build de la LIBRERÍA: un `.js` + `.d.ts` por módulo (`dist/index.js`), `dist/kapi.css` |
15
+ | `pnpm build:demo` | Build estático del playground → `dist-demo/` |
16
+ | `pnpm test` / `pnpm test:watch` | Vitest (lógica pura, no markup) |
17
+ | `pnpm typecheck` | `tsc --noEmit` sobre src + demo + configs |
18
+ | `pnpm test:visual` | Regresión visual del catálogo (Playwright, en Docker); ver abajo |
19
+ | `pnpm test:visual:update` | Regenera las baselines de la regresión visual (en Docker) |
20
+
21
+ ### Regresión visual
22
+
23
+ `pnpm test:visual` compara capturas del demo compilado (`build:demo` + `vite preview`) contra las
24
+ baselines de `visual/__screenshots__/`: Chromium, viewport 1280×800, tema claro en cada sección del
25
+ catálogo (`demo/catalog.tsx`, una sección nueva entra sola) y oscuro en la cáscara, inputs, tabla,
26
+ diálogos y feedback. Reloj fijo, sin animaciones y con las fuentes cargadas. Tolerancia: ningún píxel
27
+ distinto (`maxDiffPixels: 0`) con umbral por píxel `0.005`, que alcanza para ver un gris corrido 2–3
28
+ niveles; `0` no es estable (el antialiasing varía entre corridas).
29
+
30
+ - Corre **siempre dentro de la imagen** `mcr.microsoft.com/playwright:v1.63.0-noble` (la misma del
31
+ job `visual` del CI). `visual/run.sh` la levanta con Docker con tu uid, instala las dependencias
32
+ dentro del contenedor y devuelve el reporte a `playwright-report/` (gitignored). Requiere Docker.
33
+ - Si un cambio visual es intencional, `pnpm test:visual:update` regenera las baselines; revisá los
34
+ PNG en el diff y commitealos junto al cambio. Borra las baselines huérfanas (de secciones que ya
35
+ no existen), reescribe solo las que cambiaron y, si Playwright falla, no toca las del repo. El
36
+ script no acepta otros argumentos. **Nunca** las regeneres fuera de la imagen (otro SO
37
+ rasteriza distinto y el CI fallaría).
38
+ - No forma parte de `pnpm check`. Si el job `visual` falla, el artefacto `visual-report` trae el
39
+ reporte con las diferencias. Un test de vitest (`visual/catalogRoutes.test.ts`) verifica que la
40
+ lista de rutas leída de `demo/catalog.tsx` coincida con `CATALOG`.
41
+
42
+ ## Estructura
43
+
44
+ ```
45
+ src/components/{inputs,overlays,data,feedback,navigation,layout}/ # un index.ts por tema
46
+ src/hooks/ # mecánicas: drag-scroll, click derecho global, tema, media query
47
+ src/styles/ # kapi.css (componentes; importa los tokens de @kapiuara/tokens), shell.css, fonts.css
48
+ src/index.ts # barrel: re-exporta todo
49
+ demo/ # playground: index.html, main.tsx, App.tsx, catalog.tsx, views/
50
+ ```
51
+
52
+ ## Convenciones
53
+
54
+ - Props tipadas explícitas (`XxxProps` exportado: los tipos SON el contrato). Defaults en los parámetros.
55
+ - Cada prop pública lleva una línea de JSDoc (con `@default` si el default no se ve en la desestructuración):
56
+ es la fuente de la [referencia de props](#referencia-de-props).
57
+ - JSX runtime automático: no hace falta `import React`.
58
+ - Nada hardcodeado inline: clases `.kapi-*` en `src/styles/` usando tokens `--kapi-*`. Inline sólo si es dinámico
59
+ (p. ej. la posición `left/top` del menú contextual).
60
+ - Componentes de comportamiento sobre `<dialog>` nativo cuando aplique.
61
+ - Tests: lógica pura exportada (ej. `buttonClasses`), colocados como `Xxx.test.ts`.
62
+ - Todo CSS nuevo va anidado bajo `.kapi { … }`. Es CSS plano: comentarios `/* */` (nunca `//`).
63
+ - Los overlays se montan DENTRO de `.kapi` (hijos del `KapiShell` o vía su prop `overlays`) para heredar tokens.
64
+ - Overlays controlados: `open` + `onClose()` (Escape, fondo, X y Cancelar piden cierre); paneles plegables:
65
+ `collapsed`/`defaultCollapsed`/`onCollapsedChange`. Ej.: `<KapiModal open={open} onClose={() => setOpen(false)}>…</KapiModal>`.
66
+ - `.kapi-row` ya existe (space-between); para filas con gap usá `.kapi-cluster`.
67
+ - Para sumar un componente al playground: una sección en `demo/catalog.tsx`.
68
+
69
+ ## Referencia de props
70
+
71
+ El playground trae una vista **Referencia → Referencia de props** (`#/referencia/props/KapiButton`): elige un
72
+ componente y ves la tabla de sus props (nombre, tipo, si es requerida, valor por defecto y descripción) más un
73
+ resumen de los atributos nativos que pasa al elemento.
74
+
75
+ La tabla se genera desde el código TypeScript en cada `pnpm dev` / `pnpm build:demo` (`scripts/propsDoc.mjs`,
76
+ servido como módulo virtual `virtual:kapi-props-doc` por `scripts/vitePropsDoc.mjs`); no hay nada generado en
77
+ el repo, así que no se desactualiza. **La fuente es el JSDoc de cada prop** en el tipo `XxxProps`:
78
+
79
+ ```ts
80
+ export type KapiFooProps = {
81
+ /** Escala del control. */
82
+ size?: "sm" | "md" | "lg";
83
+ /**
84
+ * Cuántos ítems se muestran antes de "Ver más".
85
+ * @default 5
86
+ */
87
+ limit?: number;
88
+ /** @deprecated Usa `size`. */
89
+ compact?: boolean;
90
+ };
91
+ ```
92
+
93
+ - El valor por defecto sale de `@default` o, si no hay, del default en la desestructuración del componente.
94
+ - `@deprecated` marca la prop como obsoleta en la tabla.
95
+ - Las props que vienen de `HTMLAttributes` y compañía no se listan una por una: se resumen en una línea.
96
+ - El JSDoc tiene que ir en un tipo de varias líneas: TypeScript no asocia el comentario a miembros de un tipo
97
+ escrito en una sola línea (`{ /** … */ a: string }`).
98
+
99
+ ## Consumo de estilos
100
+
101
+ Todo el CSS (los tokens de `@kapiuara/tokens`, el shell y el CSS co-locado de cada componente:
102
+ `KapiSelect.css`, `overlays.css`, `KapiOffcanvas.css`, `feedback.css`, `navigation.css`, `KapiTable.css`,
103
+ etc.) se emite en UN solo archivo: `dist/kapi.css`, expuesto como `@kapiuara/ui/styles.css`. El build
104
+ inlinea el `@import` de los tokens, así que la app host **no** instala `@kapiuara/tokens`. Se importa
105
+ una vez en la app host:
106
+
107
+ ```ts
108
+ import "@kapiuara/ui/styles.css";
109
+ import "@fontsource-variable/nunito"; // opcional: la fuente NO viene en la lib (ver "Fuente")
110
+ ```
111
+
112
+ Los componentes también importan su CSS (por eso `sideEffects: ["**/*.css"]`), pero en modo lib Vite lo
113
+ extrae a `kapi.css`; los `.js` de `dist/` no contienen imports de CSS.
114
+
115
+ ## React Server Components
116
+
117
+ Cada módulo que usa React abre con `"use client"` en `dist`, así que los componentes se pueden importar desde un Server Component (Next.js App Router) sin envolverlos. La lógica pura (`*Logic`, helpers de clases) no lleva la directiva y se puede usar en el servidor. El build falla si un módulo que importa `react` queda sin ella.
118
+
119
+ ## Tree-shaking
120
+
121
+ El build de la librería emite un módulo por archivo fuente (`preserveModules`, raíz `src/`), con su `.d.ts`
122
+ al lado. El bundler de la app descarta lo que no importa: una app que sólo usa `KapiButton` suma ~0,6 kB
123
+ minificados en lugar de ~11 kB con el archivo único anterior.
124
+
125
+ ## Shell composable (con y sin rail)
126
+
127
+ `KapiShell` es la raíz `.kapi` (tema, acento, fuente, click derecho global) y compone
128
+ `[rail?] [panel?] <main.kapi-content>`. Rail y panel son props OPCIONALES e independientes.
129
+
130
+ ```tsx
131
+ import { KapiShell, KapiRail, KapiContextSlot, KapiPanelItem, KapiPanelList } from "@kapiuara/ui";
132
+ import "@kapiuara/ui/styles.css"; // + importá la fuente: @fontsource-variable/nunito
133
+
134
+ // 1) Con rail + panel contextual
135
+ <KapiShell
136
+ rail={<KapiRail brand="S" items={items} activeId={activeId} onSelect={go} />}
137
+ panel={
138
+ <KapiContextSlot title="Personas">
139
+ <KapiPanelList><KapiPanelItem active>Listado</KapiPanelItem></KapiPanelList>
140
+ </KapiContextSlot>
141
+ }
142
+ >
143
+ <Pagina />
144
+ </KapiShell>
145
+
146
+ // 2) Sin rail, con panel (otra navegación va en el contenido)
147
+ <KapiShell panel={<KapiContextSlot title="Sección">…</KapiContextSlot>}>
148
+ <MiTopNav />
149
+ <Pagina />
150
+ </KapiShell>
151
+
152
+ // 3) Sólo contenido
153
+ <KapiShell><Pagina /></KapiShell>
154
+ ```
155
+
156
+ - `KapiRail` es presentacional: la app le pasa `activeId` + `onSelect` (deriva el activo de su router). `footer` para cuenta/tema.
157
+ - Zonas fijas del rail: `brand` (arriba) y `footer` (abajo) no se mueven nunca. Cada ítem decide con `pinned`:
158
+ `"start"` lo fija arriba bajo la marca, `"end"` abajo sobre el footer, y sin `pinned` va a la zona del medio, la
159
+ única que scrollea cuando no entra en el alto (en móvil, la barra inferior scrollea de costado con las fijas en
160
+ los extremos). Los divisores (`{ separator: true, pinned }`) siguen la misma regla.
161
+
162
+ ```tsx
163
+ <KapiRail
164
+ brand="S"
165
+ items={[
166
+ { id: "inicio", label: "Inicio", icon: <Home size={21} />, pinned: "start" },
167
+ { id: "tramites", label: "Trámites", icon: <Inbox size={21} /> },
168
+ { id: "config", label: "Configuración", icon: <Settings size={21} />, pinned: "end" }
169
+ ]}
170
+ activeId={activeId}
171
+ onSelect={go}
172
+ footer={<AccountButton />}
173
+ />
174
+ ```
175
+
176
+ - `KapiContextSlot`: plegable. Controlado (`open`/`onOpenChange`) o no (`defaultOpen`; por defecto abierto en desktop y cerrado en móvil).
177
+ Animación: el ancho colapsa 258px ↔ 44px (`.kapi-ctxslot`), el panel entra con `kapi-slide-in`.
178
+ - `contextMenuItems` en `KapiShell` habilita el menú del sistema (doble click derecho). El arrastre con click derecho scrollea siempre.
179
+ - Avisos de atención (vencimientos, pendientes): `KapiRailItem`, `KapiPanelItem`, los ítems de
180
+ `KapiContextSections` y `KapiPanelGroup` (cabecera y subítems) aceptan `badge` (número o `"dot"`), `badgeTone`
181
+ (variantes de `KapiBadge`: `danger` para vencidos, `warning` para por vencer) y `badgeLabel` (lo que lee un lector
182
+ de pantalla: "3 trámites vencidos"). Un 0 no dibuja nada; en el rail es una burbuja sobre el ícono (tope "99+") y
183
+ en los paneles va al final (tope "999+"). En `KapiPanelGroup` convive con `count`, que es el total neutro. La
184
+ pieza suelta es `KapiNavBadge`.
185
+ - Integración de los avisos: el rail y el panel son presentacionales, ninguna vista los muta. Los avisos salen de
186
+ una sola fuente compartida (un hook o store al nivel del shell que lee el backend) y los ítems se derivan de ella,
187
+ igual que `activeId` sale del router. Una vista que resuelve algo cambia esa fuente (o invalida su consulta) y la
188
+ navegación se recalcula sola:
189
+
190
+ ```tsx
191
+ const avisos = useAvisos(); // { tramites: { demorados: 3 } }, del backend, compartido por contexto
192
+ const items = BASE_ITEMS.map((i) =>
193
+ i.id === "tramites"
194
+ ? {
195
+ ...i,
196
+ badge: avisos.tramites.demorados,
197
+ badgeTone: "danger",
198
+ badgeLabel: `${avisos.tramites.demorados} vencidos`
199
+ }
200
+ : i
201
+ );
202
+ ```
203
+
204
+ Ejemplo armado en el demo de Trámites (`demo/views/screens/tramites/avisos.ts`): el rail, el panel y "Mi
205
+ trabajo" muestran el mismo número.
206
+
207
+ - Panel plegado en modo mini: `KapiContextSlot` y `KapiContextPanel` aceptan `collapsedMode="mini"` (default
208
+ `"hidden"`). Plegado en desktop, el panel queda como una tira de íconos: cada `KapiPanelItem`, ítem de
209
+ `KapiContextSections` y cabecera de `KapiPanelGroup` muestra sólo su ícono (o sus iniciales, o la inicial de la
210
+ etiqueta), el aviso como burbuja en la esquina y un tooltip con el título y el aviso al pasar el mouse o llegar con
211
+ Tab. El clic navega sin desplegar; la cabecera de un grupo despliega el panel y abre el grupo. Se ocultan el
212
+ título, las etiquetas de sección, el buscador, la ayuda y el contenido propio de la app (lo que no sea una pieza
213
+ de navegación de kapi). Un componente propio dentro del panel puede leer el estado con `useKapiPanelMini()`. En
214
+ móvil el panel sigue siendo un drawer.
215
+
216
+ ```tsx
217
+ <KapiContextSlot title="Trámites" collapsedMode="mini">
218
+ <KapiPanelList>
219
+ <KapiPanelItem icon={<Inbox size={15} />} badge={22} badgeLabel="22 esperando tu acción">
220
+ Mi trabajo
221
+ </KapiPanelItem>
222
+ </KapiPanelList>
223
+ </KapiContextSlot>
224
+ ```
225
+
226
+ - Responsive: ≤ 1024px reduce padding; ≤ 768px el rail pasa a barra inferior y el panel a drawer con backdrop
227
+ (botón flotante para reabrirlo). Sin rail el drawer funciona igual. Breakpoint en `shell.css` y `SHELL_MOBILE_QUERY`.
228
+
229
+ ## Selección e ids tipados
230
+
231
+ - Estado controlado con nombre: `X` / `defaultX` / `onXChange(x)` (`KapiTree`: `selected`/`onSelectedChange`,
232
+ `expanded`/`onExpandedChange`; `KapiTable`: `selectedIds`/`onSelectedIdsChange`,
233
+ `expandedIds`/`onExpandedIdsChange`; `KapiAccordion`: `openIds`/`defaultOpenIds`/`onOpenIdsChange`).
234
+ - El valor principal de un control de elección es `value` / `onChange(value)` (`KapiTabs`, `KapiSegmented`,
235
+ `KapiFilterChips`, `KapiSelect`…).
236
+ - Las listas de navegación reciben `activeId` + `onSelect(id)` (`KapiRail`, `KapiPanelGroup`,
237
+ `KapiContextPanel`): la app es dueña del activo, normalmente desde el router.
238
+ - Los ids son genéricos y se infieren de los ítems: con un setter tipado, un id ajeno no compila.
239
+
240
+ ```tsx
241
+ type View = "list" | "grid";
242
+ const tabs: KapiTabItem<View>[] = [
243
+ { id: "list", label: "Lista" },
244
+ { id: "grid", label: "Grilla" }
245
+ ];
246
+ const [view, setView] = useState<View>("list");
247
+
248
+ <KapiTabs tabs={tabs} value={view} onChange={setView} aria-label="Vista" />;
249
+
250
+ // `data` vuelve tipado en los callbacks del árbol.
251
+ const nodes: KapiTreeNode<{ code: number }>[] = [{ id: "fcye", label: "FCyE", data: { code: 12 } }];
252
+ <KapiTree nodes={nodes} onSelectedChange={(ids, node) => console.log(ids, node.data?.code)} />;
253
+
254
+ // Sin `render`, `key` tiene que ser una propiedad de la fila.
255
+ type Persona = { id: number; nombre: string };
256
+ const columns: KapiTableColumn<Persona>[] = [
257
+ { key: "nombre", label: "Nombre" },
258
+ { key: "acciones", label: "", render: (p) => <KapiButton>Ver {p.nombre}</KapiButton> }
259
+ ];
260
+ ```
261
+
262
+ ## Diálogos imperativos
263
+
264
+ Para el flujo "preguntar y seguir" (lo que en otros sistemas resuelve SweetAlert) no hace falta estado
265
+ `open` en cada pantalla: monta `KapiDialogHost` una vez (junto a `KapiToastContainer`) y llama a las
266
+ funciones desde cualquier handler.
267
+
268
+ ```tsx
269
+ import { KapiDialogHost, kapiAlert, kapiCloseDialogs, kapiConfirm, kapiLoading } from "@kapiuara/ui";
270
+
271
+ // Una vez, en la raíz de la app (dentro de `.kapi`):
272
+ <KapiDialogHost />;
273
+
274
+ const eliminar = async (persona: Persona) => {
275
+ if (!(await kapiConfirm({ title: `¿Eliminar a ${persona.nombre}?`, message: "No se puede deshacer." })))
276
+ return;
277
+ await kapiLoading.run(api.eliminar(persona.id), "Eliminando…");
278
+ await kapiAlert({ title: "Eliminado", variant: "success" });
279
+ };
280
+ ```
281
+
282
+ - `kapiConfirm(options)` → `Promise<boolean>`: `true` al confirmar, `false` ante Cancelar, X, Escape o fondo.
283
+ Mismas opciones que `KapiConfirm` (`title`, `message`, `variant` `danger` · `warning` · `success` · `info` ·
284
+ `question`, `confirmLabel`, `cancelLabel`).
285
+ - `onConfirm` (opcional, en `kapiConfirm`): acción que corre al confirmar, como `preConfirm` de SweetAlert. Mientras
286
+ corre, el botón de confirmar muestra un spinner, los dos botones quedan deshabilitados y Escape/fondo/X no cierran.
287
+ Si resuelve, el diálogo se cierra y la promesa da `true`; si rechaza, el diálogo sigue abierto con el error adentro
288
+ (`role="alert"`: `error.message`, o `labels.error` de `confirm`, "No se pudo completar la acción") y se puede
289
+ reintentar o cancelar (`false`). Un rechazo que no es `Error` ni string muestra el texto por defecto. La promesa
290
+ tiene que terminar: mientras corre no hay forma de cerrar el diálogo; `kapiCloseDialogs()` lo cierra igual
291
+ (resuelve `false`) pero la acción ya iniciada sigue.
292
+
293
+ ```tsx
294
+ const ok = await kapiConfirm({
295
+ title: "¿Enviar la solicitud?",
296
+ variant: "question",
297
+ onConfirm: () => api.enviar(solicitud) // si falla, el mensaje queda en el diálogo
298
+ });
299
+ ```
300
+
301
+ - `kapiAlert(options)` → `Promise<void>`: aviso con un solo botón (`title`, `message`, `variant` `success` ·
302
+ `error` · `info` · `warning`, `acceptLabel`, default `labels.accept`: "Aceptar").
303
+ - `kapiLoading(message?)` → `{ update, close }`: carga bloqueante (sin Escape ni fondo). Varias abiertas a la vez
304
+ se muestran como una sola con el mensaje de la más reciente. `kapiLoading.run(promesa | fn, message?)` la cierra
305
+ en el `finally`.
306
+ - Abrir una confirmación o un aviso **cierra las cargas abiertas con `kapiLoading()`** (como SweetAlert, que muestra
307
+ uno por vez): `kapiLoading(); api().then(() => kapiAlert({ variant: "success", … }))` no deja ninguna carga
308
+ detrás, y los handles de esas cargas quedan sin efecto. La de `kapiLoading.run` no se cierra (la cierra su
309
+ `finally`): se oculta mientras haya un diálogo y vuelve al responderlo, así
310
+ `kapiLoading.run(async () => { if (await kapiConfirm(…)) await guardar(); })` sigue cubriendo `guardar()`.
311
+ Toda carga abierta con un diálogo visible tampoco se dibuja (quedaría encima y no se podría responder): aparece
312
+ al vaciarse la cola.
313
+ - `kapiCloseDialogs()`: cierra todo. Resuelve los diálogos en cola como cerrados (`kapiConfirm` → `false`,
314
+ `kapiAlert` → `undefined`) y cierra todas las cargas (p. ej. al cambiar de ruta o cerrar sesión).
315
+ - Las confirmaciones y avisos se encolan y se muestran de a uno; los pedidos hechos antes de montar el host esperan.
316
+ Si se encola sin ningún host montado, la consola avisa una vez (`console.warn`) para que la promesa no quede
317
+ colgada en silencio.
318
+ - Textos: clave `dialog` (`accept`, `loading`) y, para la confirmación, la clave `confirm` de `KapiConfirm`.
319
+
320
+ ## Select async y creatable
321
+
322
+ `KapiSelect` cubre los modos de react-select sin dependencias: grupos (`{ label, options }`), búsqueda remota y
323
+ creación de opciones.
324
+
325
+ ```tsx
326
+ <KapiSelect
327
+ aria-label="Ciudad"
328
+ value={ciudad}
329
+ onChange={setCiudad}
330
+ loadOptions={(query, { signal }) => api.ciudades(query, { signal })}
331
+ />
332
+
333
+ <KapiSelect multiple value={tags} onChange={setTags} options={opciones} onCreate={(text) => crearTag(text)} />
334
+ ```
335
+
336
+ - `loadOptions(query, { signal })`: se llama tras `loadDelay` ms sin tipear (default 300); el pedido anterior se
337
+ aborta y una respuesta vieja nunca pisa a una nueva. `options` pasa a ser la lista inicial (query vacía). Si falla
338
+ (rechazo o `throw`), el menú muestra `labels.loadError`; al cerrar y reabrir se vuelve a cargar.
339
+ - Los resultados quedan asociados a su búsqueda: pasar otra identidad de `loadOptions` (p. ej. una función inline)
340
+ no los invalida ni recarga. Para forzar una recarga, remonta el select con otra `key`.
341
+ - Las opciones elegidas conservan su label aunque ya no estén en la lista cargada.
342
+ - `onCreate(text)`: ofrece `Crear "texto"` cuando ninguna opción coincide exactamente. Si devuelve (o resuelve) una
343
+ opción, queda elegida; si no devuelve nada, el padre actualiza `options`/`value`. Mientras una promesa de
344
+ `onCreate` corre, la fila se oculta (no hay duplicados) y el control queda `aria-busy`; lo tipeado mientras tanto se
345
+ conserva. Si la promesa se rechaza, el menú muestra `labels.createError` y se puede reintentar.
346
+
347
+ ## Archivos: visor y descarga
348
+
349
+ `KapiFileViewer` muestra un archivo a partir de una URL o de un Blob (la respuesta de una API), sin dependencias:
350
+
351
+ ```tsx
352
+ <KapiFileViewer src={blob} fileName="constancia.pdf" />
353
+ <KapiFileViewer src="/archivos/foto.jpg" title="Foto del DNI" height="240px" />
354
+ ```
355
+
356
+ - PDF → `<object>`; si el navegador no tiene visor (p. ej. en móviles) se ve un aviso con la descarga. Imágenes →
357
+ `<img>` (alt = `title` o el nombre); texto plano y JSON → `<iframe sandbox>`; el resto (CSV, Excel, ZIP…) → una
358
+ tarjeta con el nombre y el botón de descarga.
359
+ - El tipo sale de `type`, del tipo del Blob o de la extensión del nombre/URL. Un Blob recibe una URL temporal que se
360
+ revoca al cambiarlo o al desmontar. `downloadable={false}` quita la descarga; con una URL hay además un enlace
361
+ para abrirla en otra pestaña. Textos: clave `fileViewer`.
362
+ - Solo acepta URLs relativas, `http(s):`, `blob:` o `data:` de imagen/PDF; otra (`javascript:`…) muestra el aviso
363
+ sin enlaces. El PDF por URL va en un `<object>` sin sandbox: si la URL es del mismo origen y el servidor devuelve
364
+ HTML (un archivo subido con un Content-Type errado), ese HTML corre en el origen de la app. Para archivos subidos
365
+ por usuarios, pasa un Blob con su `type`.
366
+
367
+ Para descargar sin mostrar, los helpers de `hooks` reemplazan a `file-saver` y al `<a download>` manual:
368
+
369
+ ```ts
370
+ const res = await fetch(url);
371
+ const name = fileNameFromContentDisposition(res.headers.get("Content-Disposition")) ?? "reporte.xlsx";
372
+ downloadBlob(await res.blob(), name);
373
+ ```
374
+
375
+ `fileNameFromContentDisposition` sigue la RFC 6266 (`filename*=UTF-8''…` gana sobre `filename`) y descarta rutas;
376
+ `fileKind(mimeOrName)` devuelve `"pdf" | "image" | "text" | "other"`; `useObjectUrl(blob)` devuelve una URL temporal
377
+ para usar en tu propio markup.
378
+
379
+ ### Adjuntos en lista
380
+
381
+ `KapiFileList` (`<ul>`) y `KapiFileItem` (`<li>`) muestran una fila por adjunto: ícono según el tipo (MIME o
382
+ extensión), nombre recortado, metadatos («detalles · tamaño · fecha»), un `status` (badge) y `actions`. Con
383
+ `onOpen`, el nombre es un botón (p. ej. para abrir la previa en un `KapiFileViewer`).
384
+
385
+ ```tsx
386
+ <KapiFileList aria-label="Adjuntos">
387
+ <KapiFileItem
388
+ name="nota.pdf"
389
+ size={248320}
390
+ date={fecha}
391
+ details={["Nota", "Ana Pérez"]}
392
+ status={
393
+ <KapiBadge variant="success" size="sm">
394
+ Verificado
395
+ </KapiBadge>
396
+ }
397
+ onOpen={verPrevia}
398
+ actions={<KapiIconButton aria-label="Descargar nota.pdf" icon={<Download />} />}
399
+ />
400
+ </KapiFileList>
401
+ ```
402
+
403
+ Formateo sin dependencias (en `hooks`): `formatFileSize(1536)` → "1,5 KB" (base 1024, un decimal como máximo);
404
+ `formatRelativeTime(fecha, ahora?, locale?)` → "hace 5 minutos" (trunca, nunca "hace 60 minutos");
405
+ `formatDate(fecha)` → "12 mar 2026"; `isoDateTime(fecha)` da el valor de `<time dateTime>`. Una fecha inválida
406
+ devuelve "" (o `null` en `isoDateTime`).
407
+
408
+ ## Comentarios
409
+
410
+ `KapiComment` es un comentario de un hilo: avatar, autor, fecha relativa (con la fecha completa en el `title`),
411
+ `body`, `meta` («editado»), `actions` y respuestas como `children` (se indentan un solo nivel). `now` fija la hora
412
+ de referencia. `KapiCommentComposer` es la caja para escribir: `onSubmit(texto)` recibe el texto recortado (si devuelve una promesa, el compositor queda ocupado y se vacía solo si resuelve: un rechazo conserva el borrador),
413
+ Ctrl/⌘ + Enter envía, Enter solo agrega un salto de línea y el botón queda deshabilitado con el texto vacío. Cumple
414
+ el contrato de campo (`name`, `onBlur`, `ref` al textarea, `invalid`/`required`); textos en la clave
415
+ `commentComposer`.
416
+
417
+ ```tsx
418
+ <KapiComment author="Ana Pérez" date={fecha} body="Falta la firma." meta="editado">
419
+ <KapiComment author="Bruno Díaz" date={otra} body="Ya la pedí." />
420
+ </KapiComment>
421
+ <KapiCommentComposer onSubmit={(texto) => agregar(texto)} />
422
+ ```
423
+
424
+ ### Toasts con acción
425
+
426
+ `kapiToast.success|error|info|warning(mensaje, opciones?)` acepta `{ duration, action: { label, onClick } }`
427
+ (sigue aceptando un número como duración). La acción es un botón dentro del toast: ejecuta y lo cierra, sin mover
428
+ el foco. Con acción y sin `duration`, el toast dura 6 s (`TOAST_ACTION_DURATION`).
429
+
430
+ ```ts
431
+ kapiToast.success("Trámite creado", { action: { label: "Ver trámite", onClick: () => abrir(id) } });
432
+ ```
433
+
434
+ ## Campana de notificaciones
435
+
436
+ `KapiNotificationBell` (layout) es la campana de la barra superior: badge con las no leídas y un panel con la
437
+ lista. Los datos y el "leído" son de la app; la campana solo avisa.
438
+
439
+ ```tsx
440
+ <KapiNotificationBell
441
+ items={notificaciones} // { id, title, description?, date?, read?, icon?, href? }
442
+ onItemSelect={(item) => marcarLeida(item.id)}
443
+ onMarkAllRead={marcarTodas}
444
+ onViewAll={() => navigate("/notificaciones")}
445
+ loading={cargando}
446
+ />
447
+ ```
448
+
449
+ - Todo ítem sin `read: true` cuenta como no leído: va resaltado y suma al badge (`max`, default 9, muestra "9+").
450
+ El nombre accesible de la campana lleva el conteo real: "Notificaciones, 3 sin leer".
451
+ - `date` (`Date` o ISO) se muestra relativa con `Intl.RelativeTimeFormat` ("hace 5 minutos", "ayer") en el
452
+ `locale` del provider (`es` sin provider).
453
+ - Un ítem con `href` es un enlace; `onItemSelect(item, event)` recibe el evento para hacer `preventDefault` y
454
+ navegar con el router. Elegir un ítem o "Ver todas" cierra el panel.
455
+ - Teclado: al abrir enfoca la primera notificación; flechas, Home y End recorren la lista; Escape cierra y devuelve
456
+ el foco a la campana. Apertura `open`/`defaultOpen`/`onOpenChange`. Textos: clave `notificationBell`.
457
+
458
+ ## Pasos de un circuito
459
+
460
+ `KapiSteps` muestra el avance de un circuito de solo lectura (por ejemplo, los estados de un trámite). Para un
461
+ formulario por pasos usa `KapiWizard`. Con `current`, los pasos anteriores quedan completos y los siguientes
462
+ pendientes; un `status` explícito (`complete` · `current` · `upcoming` · `error`) siempre gana. El paso actual lleva
463
+ `aria-current="step"` y cada estado se anuncia con un texto oculto (clave de textos `steps`). En horizontal se
464
+ acomoda en varias líneas si no entra.
465
+
466
+ ```tsx
467
+ <KapiSteps
468
+ aria-label="Circuito del trámite"
469
+ current="revision"
470
+ steps={[
471
+ { id: "iniciado", label: "Iniciado", description: "Mesa de Entradas" },
472
+ { id: "revision", label: "En revisión" },
473
+ { id: "firma", label: "A la firma" }
474
+ ]}
475
+ />
476
+ ```
477
+
478
+ ## Menús, pestañas y modales
479
+
480
+ - **Ítem de menú deshabilitado:** `disabled` en un `KapiMenuItem` lo atenúa, lo anuncia con `aria-disabled`, no
481
+ ejecuta su acción y las flechas lo saltean. Vale para `KapiMenuButton`, `KapiContextMenu` y el menú de fila de
482
+ `KapiTable`.
483
+ - **Contador en pestañas:** `count` en un `KapiTabItem` muestra una píldora tras el label (también el `0`); el
484
+ nombre accesible queda «Adjuntos 3».
485
+ - **Nombre accesible de modales:** `KapiModal` y `KapiSplitModal` aceptan `aria-label` y `aria-labelledby`
486
+ nativos, que van al `<dialog>`. Precedencia: `aria-labelledby` > `aria-label` > título visible.
487
+
488
+ ```tsx
489
+ <KapiMenuButton
490
+ label="Acciones"
491
+ items={[
492
+ { label: "Derivar", onClick: derivar },
493
+ { label: "Anular", disabled: !puedeAnular, onClick: anular }
494
+ ]}
495
+ />
496
+ <KapiTabs
497
+ aria-label="Secciones"
498
+ value={tab}
499
+ onChange={setTab}
500
+ tabs={[{ id: "datos", label: "Datos" }, { id: "adjuntos", label: "Adjuntos", count: 3 }]}
501
+ />
502
+ ```
503
+
504
+ ## Búsqueda facetada y tabla paginada en el cliente
505
+
506
+ `KapiFacetedSearch` se maneja con `chips` y el `apply` de cada faceta. Cada opción puede mostrar cuántos
507
+ resultados tiene con `count` («Pendiente, 4»). Una faceta `kind: "dateRange"` pide desde/hasta en un solo
508
+ calendario y entrega `yyyy-mm-dd/yyyy-mm-dd` (se lee con `parseDateRangeValue`). Con `onClearAll`, la barra
509
+ muestra «Limpiar todo» mientras haya filtros activos.
510
+
511
+ ```tsx
512
+ const facets: KapiFacet[] = [
513
+ {
514
+ key: "estado",
515
+ label: "Estado",
516
+ kind: "list",
517
+ options: [{ value: "pend", label: "Pendiente", count: 4 }],
518
+ apply: (v) => set({ estado: v })
519
+ },
520
+ { key: "creado", label: "Creado", kind: "dateRange", apply: (v) => set({ creado: parseDateRangeValue(v) }) }
521
+ ];
522
+ <KapiFacetedSearch facets={facets} chips={chips} onText={(t) => set({ texto: t })} onClearAll={limpiar} />;
523
+ ```
524
+
525
+ La `pagination` de `KapiTable` solo dibuja el pie: los datos los recorta quien la usa (sirve igual para paginar
526
+ en el servidor). Para paginar en el cliente, `sortRowsByColumn` ordena con la misma lógica que la tabla y
527
+ `paginateRows` recorta la página. `exportCsv.rows` (arreglo o función) exporta el conjunto completo, no solo la
528
+ página visible.
529
+
530
+ ```tsx
531
+ const ordenadas = sortRowsByColumn(filas, columnas, sort);
532
+ <KapiTable
533
+ columns={columnas}
534
+ rows={paginateRows(ordenadas, page, pageSize)}
535
+ manualSort
536
+ sort={sort}
537
+ onSortChange={setSort}
538
+ exportCsv={{ fileName: "tramites.csv", rows: () => ordenadas }}
539
+ pagination={{ page, pageSize, total: filas.length, onPageChange: setPage }}
540
+ />;
541
+ ```
542
+
543
+ ## Tarjetas radio y atajos de teclado
544
+
545
+ `KapiCheckCard type="radio"` es un radio nativo con forma de tarjeta: las que comparten `name` forman un grupo y
546
+ las flechas mueven la elección. `KapiInput shortcut="/"` muestra la tecla como `<kbd>` mientras el campo está
547
+ vacío y la anuncia con `aria-keyshortcuts`; la tecla la registra la app (por ejemplo con `useHotkey`).
548
+
549
+ ```tsx
550
+ <div role="radiogroup" aria-label="Tipo de trámite">
551
+ {tipos.map((t) => (
552
+ <KapiCheckCard
553
+ key={t.id}
554
+ type="radio"
555
+ name="tipo"
556
+ value={t.id}
557
+ label={t.nombre}
558
+ checked={tipo === t.id}
559
+ onChange={(e) => setTipo(e.target.value)}
560
+ />
561
+ ))}
562
+ </div>;
563
+
564
+ const ref = useRef<HTMLInputElement>(null);
565
+ useHotkey("/", () => ref.current?.focus());
566
+ <KapiInput ref={ref} type="search" aria-label="Buscar" shortcut="/" clearable />;
567
+ ```
568
+
569
+ `KapiNotificationBell now={fecha}` fija la hora de referencia de las fechas relativas (tests y capturas).
570
+ `KapiPanelItem icon={<Inbox size={15} />}` suma un ícono decorativo antes del texto.
571
+
572
+ ## Tema y color de marca
573
+
574
+ Todos los colores salen de tokens `--kapi-*` en `.kapi`; el tema oscuro es `data-theme="dark"` en esa
575
+ raíz. Para usar el color de la marca alcanza con un solo token, `--kapi-primary`:
576
+
577
+ ```tsx
578
+ const { setAccent } = useKapiThemeState(); // KapiShell lo aplica en su raíz (y lo guarda en kapi:accent)
579
+ setAccent("#0f766e");
580
+ ```
581
+
582
+ ```css
583
+ .kapi {
584
+ --kapi-primary: #0f766e;
585
+ } /* o directo en CSS, sobre la misma raíz */
586
+ ```
587
+
588
+ - `--kapi-primary-ink` (texto), `--kapi-primary-soft` / `--kapi-primary-bg` (fondos tonales),
589
+ `--kapi-primary-border`, el anillo de foco `--kapi-ring` y las sombras teñidas (`--kapi-shadow-primary`,
590
+ `-fab`, `-shape`…) se derivan de `--kapi-primary` con `color-mix`, en claro y en oscuro: no hace falta
591
+ pisarlos. Con el violeta por defecto se ven igual que antes.
592
+ - `--kapi-primary-fg` (texto sobre el primario) no se deriva: si el acento es claro, pisalo con un color
593
+ oscuro para mantener el contraste.
594
+ - Los derivados se resuelven en `.kapi`: redefinir `--kapi-primary` en un elemento interno no los
595
+ recolorea. El acento va en la raíz.
596
+ - Un mismo acento sirve para los dos temas; para afinar el oscuro, redefiní `--kapi-primary` en
597
+ `.kapi[data-theme="dark"]`.
598
+
599
+ ## Preferencias guardadas (namespace)
600
+
601
+ Las preferencias del design system van a `localStorage` con namespace: `kapi:theme`, `kapi:accent`, `kapi:font`,
602
+ `kapi:motion` y `kapi:tour-seen`. Si dos apps del design system comparten origen, cada una pasa el suyo para no pisarse:
603
+
604
+ ```tsx
605
+ <KapiShell storageNamespace="tramites">…</KapiShell> // tramites:theme, tramites:font…
606
+ useKapiThemeState({ namespace: "tramites" }); // si armás tu propia raíz
607
+ <KapiTour markSeen="tramites:tour-seen" … />
608
+ usePersistentState("table-cols", {}, { namespace: "tramites" }); // tramites:table-cols
609
+ ```
610
+
611
+ - Las claves viejas sin prefijo (`v2_theme`, `v2_accent`, `v2_font`, `v2_motion`, `v2_tour_seen`) se migran solas en la
612
+ primera lectura (se copian a la clave nueva y se borran): nadie pierde su tema.
613
+ - `usePersistentState` sin `namespace` sigue usando la clave tal cual (compatible con lo ya guardado).
614
+
615
+ ## Sin conexión
616
+
617
+ `useOnlineStatus()` devuelve `{ online, since }` (eventos `online`/`offline` de la ventana; en SSR, conectado).
618
+ `KapiOfflineBanner` lo usa para mostrar un aviso flotante mientras no hay conexión y uno breve al volver;
619
+ con conexión no renderiza nada. Textos por `labels`, `forceOffline` para simular o para una verificación propia.
620
+
621
+ ```tsx
622
+ <KapiShell overlays={<KapiOfflineBanner />}>…</KapiShell>
623
+ ```
624
+
625
+ ## Formularios
626
+
627
+ El DS no trae motor de validación: la validación es del host (react-hook-form o estado propio). Lo que
628
+ garantiza es un **contrato de campo** común, para cablear cualquier control sin adaptadores:
629
+
630
+ - `name`: llega al envío nativo del `<form>`. Los controles nativos lo llevan en su `<input>`; los de
631
+ valor que no lo son (`KapiSegmented`, `KapiRating`, `KapiSelect`) renderizan un `<input type="hidden">`.
632
+ - `onBlur` y `ref` al elemento enfocable (para que la librería marque `touched` y enfoque el primer error).
633
+ - `invalid`/`required` por prop o desde el `KapiField` que envuelve al control. Precedencia: `aria-*`
634
+ propios > prop > `KapiField`. Los inputs de texto (`KapiInput`, `KapiTextarea`, `KapiAutocomplete`)
635
+ usan `status="err"` en vez de `invalid`; `KapiSelect`, `KapiDatePicker` y `KapiDateRangePicker` toman
636
+ el estado solo del `KapiField`.
637
+ - `onChange`: los wrappers nativos (`KapiInput`, `KapiTextarea`, `KapiCheckbox`, `KapiSwitch`, `KapiRadio`)
638
+ pasan el evento; los de valor (`KapiSelect`, `KapiDatePicker`, `KapiSlider`, `KapiSegmented`,
639
+ `KapiRating`, `KapiFileUpload`) pasan el valor ya tipado.
640
+
641
+ ### `KapiField`
642
+
643
+ Envuelve un control y le conecta label, ayuda y estado sin cablear ids: el control toma del contexto su id
644
+ (o `aria-labelledby` si no es etiquetable), `aria-describedby` hacia la ayuda, `aria-invalid` con
645
+ `status="err"` y `aria-required` con `required`.
646
+
647
+ ```tsx
648
+ <KapiField label="Turno" help="Elige un turno" status="err" required>
649
+ <KapiSegmented name="turno" items={turnos} value={turno} onChange={setTurno} />
650
+ </KapiField>
651
+ ```
652
+
653
+ Los radios sueltos dentro de un `KapiField` toman ayuda e `invalid`, pero no el id (un grupo comparte el
654
+ campo): cada uno se nombra con su `label`.
655
+
656
+ ### Con react-hook-form
657
+
658
+ `react-hook-form` no es dependencia del DS; estos son los dos patrones del lado del host.
659
+
660
+ **Wrappers nativos → `register`.** `register` devuelve `name`, `onChange(event)`, `onBlur` y `ref`, que es
661
+ justo lo que aceptan:
662
+
663
+ ```tsx
664
+ const { register, formState: { errors } } = useForm<Alta>();
665
+
666
+ <KapiField label="Email" help={errors.email?.message} status={errors.email ? "err" : undefined} required>
667
+ <KapiInput type="email" {...register("email", { required: "Ingresa tu email" })} />
668
+ </KapiField>
669
+
670
+ <KapiField label="Acepto los términos" help={errors.terms?.message} status={errors.terms ? "err" : undefined}>
671
+ <KapiCheckbox {...register("terms", { required: "Tienes que aceptarlos" })} />
672
+ </KapiField>
673
+ ```
674
+
675
+ **Controles de valor → `Controller`.** Se mapea `field` (`value`, `onChange`, `onBlur`, `ref`, `name`) y
676
+ `fieldState.error` al `KapiField`:
677
+
678
+ ```tsx
679
+ <Controller
680
+ control={control}
681
+ name="sede"
682
+ rules={{ required: "Elige una sede" }}
683
+ render={({ field, fieldState }) => (
684
+ <KapiField label="Sede" help={fieldState.error?.message} status={fieldState.error ? "err" : undefined} required>
685
+ <KapiSelect
686
+ name={field.name}
687
+ ref={field.ref}
688
+ value={field.value ?? null}
689
+ onChange={field.onChange}
690
+ options={sedes}
691
+ />
692
+ </KapiField>
693
+ )}
694
+ />
695
+
696
+ <Controller
697
+ control={control}
698
+ name="volumen"
699
+ render={({ field, fieldState }) => (
700
+ <KapiField label="Volumen" help={fieldState.error?.message} status={fieldState.error ? "err" : undefined}>
701
+ <KapiSlider {...field} />
702
+ </KapiField>
703
+ )}
704
+ />
705
+ ```
706
+
707
+ `KapiDatePicker` sigue el mismo molde que `KapiSelect` (`value: Date | null`). `onBlur` de `KapiSelect`,
708
+ `KapiDatePicker` y `KapiDateRangePicker` se dispara solo cuando el foco sale del control: pasar a su menú
709
+ o calendario no cuenta. `KapiFileUpload` sincroniza su `<input type="file">` con la lista vía
710
+ `DataTransfer`; donde no exista, arma el `FormData` con lo que emite `onChange`.
711
+
712
+ **Qué envía cada control con `name`** (inputs ocultos salvo los nativos):
713
+
714
+ | Control | Valor en el `FormData` |
715
+ | ------------------------------------------- | --------------------------------------------------------------------------------- |
716
+ | `KapiSelect` | el `value` elegido (vacío sin valor); con `multiple`, un input por valor |
717
+ | `KapiDatePicker` | `yyyy-mm-dd`, `hh:mm` o `yyyy-mm-ddThh:mm` según `mode` (como los inputs nativos) |
718
+ | `KapiDateRangePicker` | `name.from` y `name.to`, cada uno `yyyy-mm-dd` (vacíos sin rango) |
719
+ | `KapiSlider` / `KapiRating` | el número |
720
+ | `KapiSegmented` | el `id` elegido, siempre (no tiene `disabled`) |
721
+ | `KapiCheckbox` / `KapiSwitch` / `KapiRadio` | `value` (o `on`) solo si está marcado |
722
+
723
+ ## Textos y locale
724
+
725
+ Cada componente con copy propio trae defaults en español y acepta `labels` (parcial, por clave).
726
+ `KapiLocaleProvider` pone el copy de toda una app en un solo lugar. Precedencia por clave:
727
+ defaults del componente < provider < prop `labels`. Una clave en `undefined` no borra el valor anterior.
728
+ Sin provider no cambia nada (costo cero).
729
+
730
+ ```tsx
731
+ // Fuera del render, o memoizado: un objeto nuevo por render re-renderiza a todos.
732
+ const EN: KapiLabelOverrides = {
733
+ pagination: { of: "of", nextPage: "Next page" },
734
+ calendar: { pickStart: "Pick a start date" } // lo comparten KapiDatePicker y KapiDateRangePicker
735
+ };
736
+
737
+ <KapiLocaleProvider locale="en" labels={EN}>
738
+ <App />
739
+ </KapiLocaleProvider>;
740
+ ```
741
+
742
+ - `link` es una clave compartida: el aviso "(se abre en una pestaña nueva)" de los enlaces con `newTab`
743
+ (`KapiPanelGroup`, `KapiContextPanel`, `KapiRail`, `KapiAccessCard`).
744
+ - Las props de texto sueltas que ya existían (`cancelLabel` de `KapiWizard`, `label` de `KapiFab`, `aria-label`,
745
+ `invalidMessage` de `KapiCuitInput`…) siguen ganando sobre `labels` y el provider. En las de contenido
746
+ (`ReactNode`, como `emptyMessage` de `KapiTable` o `footerHint` de `KapiCommandPalette`) solo `undefined` cae
747
+ al provider: `null` no muestra nada.
748
+ - `canvasForm` y `unsavedGuard` se configuran solo desde el provider (`KapiCanvasForm` ya usa `labels` para su
749
+ `KapiOffcanvas`; el hook tiene sus `options`).
750
+ - Las claves están en `KapiLabelRegistry` (una `interface`: una app puede sumar las suyas por module augmentation).
751
+ - `useLabels(key, defaults, labels)` resuelve los textos en un componente; `useKapiLocale()` devuelve el locale
752
+ (`undefined` = el del runtime). `KapiTable` lo usa para ordenar textos.
753
+ - `KapiTable` le pasa sus textos ya resueltos a su barra de filtro: adentro de una tabla manda la clave `table`.
754
+ - Fuera de alcance por ahora: la pluralización de `formatDuration` (countdown) sigue en español.
755
+
756
+ ## Fuente
757
+
758
+ `src/styles/fonts.css` importa `@fontsource-variable/nunito` (familia `'Nunito Variable'`, token `--kapi-font`).
759
+ El CSS de la librería NO la incluye (en modo lib Vite inlinea los woff2 en base64 → 240 kB): la app consumidora
760
+ importa `@fontsource-variable/nunito` (o `fonts.css` desde el código fuente).
761
+
762
+ - Es una `peerDependency` **opcional** (y `devDependency` para el demo): `@kapiuara/ui` no la instala. Para usar
763
+ Nunito, la app la agrega (`pnpm add @fontsource-variable/nunito`) y la importa una vez.
764
+ - Sin ella, `--kapi-font` cae a `system-ui`. Otra fuente: cargarla en la app y pisar `--kapi-font` en la raíz
765
+ (`setFont` de `useKapiThemeState` elige entre las pilas predefinidas de `FONTS`).