@stackcraftmx/calcita 0.32.2 → 0.51.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 (680) hide show
  1. package/README.md +426 -4
  2. package/cli/calcita.js +15 -0
  3. package/cli/checkTexts.js +209 -0
  4. package/cli/packageVersions.js +17 -3
  5. package/cli/projectFiles.js +7 -2
  6. package/cli/templates/module/__Name__Page.test.jsx.tpl +24 -0
  7. package/cli/templates/project/README.md.tpl +35 -2
  8. package/cli/templates/project/eslint.config.js.tpl +27 -0
  9. package/cli/templates/project/src/app/appRouter.jsx.tpl +5 -2
  10. package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
  11. package/cli/templates/project/src/core/accountConnector.js.tpl +4 -2
  12. package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
  13. package/cli/templates/project/src/modules/auth/SignInPage.test.jsx.tpl +20 -0
  14. package/cli/templates/project/src/modules/home/HomePage.test.jsx.tpl +16 -0
  15. package/cli/templates/project/src/testing/browserGaps.js.tpl +70 -0
  16. package/cli/templates/project/src/testing/openScreen.jsx.tpl +58 -0
  17. package/cli/templates/project/src/testing/setup.js.tpl +25 -0
  18. package/cli/templates/project/vite.config.js.tpl +20 -1
  19. package/dist/DataTable/AuditTrailDialog.js +3 -2
  20. package/dist/DataTable/AuditTrailDialog.js.map +1 -1
  21. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -2
  22. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
  23. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -2
  24. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
  25. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +3 -2
  26. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
  27. package/dist/DataTable/EmptyState.js +3 -2
  28. package/dist/DataTable/EmptyState.js.map +1 -1
  29. package/dist/DataTable/FilterChips.js +3 -2
  30. package/dist/DataTable/FilterChips.js.map +1 -1
  31. package/dist/DataTable/GoToPage.js +71 -14
  32. package/dist/DataTable/GoToPage.js.map +1 -1
  33. package/dist/DataTable/ServerTable.js +97 -28
  34. package/dist/DataTable/ServerTable.js.map +1 -1
  35. package/dist/DataTable/ServerTableFilterDrawer.js +57 -0
  36. package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -0
  37. package/dist/DataTable/StackedCell.js +60 -0
  38. package/dist/DataTable/StackedCell.js.map +1 -0
  39. package/dist/DataTable/TableCardsView.js +88 -0
  40. package/dist/DataTable/TableCardsView.js.map +1 -0
  41. package/dist/DataTable/columnWidths.js +30 -0
  42. package/dist/DataTable/columnWidths.js.map +1 -0
  43. package/dist/DataTable/columns.js +47 -1
  44. package/dist/DataTable/columns.js.map +1 -1
  45. package/dist/DataTable/filterContext.js.map +1 -1
  46. package/dist/DataTable/fixedColumns.js +24 -0
  47. package/dist/DataTable/fixedColumns.js.map +1 -0
  48. package/dist/DataTable/goToPageOptions.js +17 -0
  49. package/dist/DataTable/goToPageOptions.js.map +1 -0
  50. package/dist/DataTable/useTableView.js +29 -0
  51. package/dist/DataTable/useTableView.js.map +1 -0
  52. package/dist/access/PermissionMatrix.js +3 -2
  53. package/dist/access/PermissionMatrix.js.map +1 -1
  54. package/dist/account/AccountProvider.js +1 -1
  55. package/dist/account/AccountSecuritySection.js +3 -2
  56. package/dist/account/AccountSecuritySection.js.map +1 -1
  57. package/dist/account/DemoTotpCodeButton.js +3 -2
  58. package/dist/account/DemoTotpCodeButton.js.map +1 -1
  59. package/dist/account/ProfileScreen.js +3 -2
  60. package/dist/account/ProfileScreen.js.map +1 -1
  61. package/dist/account/SignInScreen.js +3 -2
  62. package/dist/account/SignInScreen.js.map +1 -1
  63. package/dist/appShell/AccountMenu.js +3 -2
  64. package/dist/appShell/AccountMenu.js.map +1 -1
  65. package/dist/appShell/AppLayout.js +3 -2
  66. package/dist/appShell/AppLayout.js.map +1 -1
  67. package/dist/appShell/AppearanceSettings.js +3 -2
  68. package/dist/appShell/AppearanceSettings.js.map +1 -1
  69. package/dist/appShell/AuthLayout.js +3 -2
  70. package/dist/appShell/AuthLayout.js.map +1 -1
  71. package/dist/appShell/BarActionButton.js +11 -4
  72. package/dist/appShell/BarActionButton.js.map +1 -1
  73. package/dist/appShell/BarActions.js +3 -2
  74. package/dist/appShell/BarActions.js.map +1 -1
  75. package/dist/appShell/BarCapsule.js +3 -2
  76. package/dist/appShell/BarCapsule.js.map +1 -1
  77. package/dist/appShell/BarSearch.js +3 -2
  78. package/dist/appShell/BarSearch.js.map +1 -1
  79. package/dist/appShell/HeaderTools.js +3 -2
  80. package/dist/appShell/HeaderTools.js.map +1 -1
  81. package/dist/appShell/MobileTabBar.js +3 -2
  82. package/dist/appShell/MobileTabBar.js.map +1 -1
  83. package/dist/appShell/SettingsMenu.js +3 -2
  84. package/dist/appShell/SettingsMenu.js.map +1 -1
  85. package/dist/appShell/SideMenu.js +3 -2
  86. package/dist/appShell/SideMenu.js.map +1 -1
  87. package/dist/appShell/SideMenuEntry.js +3 -2
  88. package/dist/appShell/SideMenuEntry.js.map +1 -1
  89. package/dist/appShell/SubmenuActionsMenu.js +3 -2
  90. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  91. package/dist/appShell/TabBarEditor.js +3 -2
  92. package/dist/appShell/TabBarEditor.js.map +1 -1
  93. package/dist/appShell/TopMenu.js +3 -2
  94. package/dist/appShell/TopMenu.js.map +1 -1
  95. package/dist/appShell/Wordmark.js +3 -2
  96. package/dist/appShell/Wordmark.js.map +1 -1
  97. package/dist/auth/PasswordChecklist.js +3 -2
  98. package/dist/auth/PasswordChecklist.js.map +1 -1
  99. package/dist/calendar/EventCalendar.js +3 -2
  100. package/dist/calendar/EventCalendar.js.map +1 -1
  101. package/dist/calendar/EventDetails.js +3 -2
  102. package/dist/calendar/EventDetails.js.map +1 -1
  103. package/dist/date-pickers.js +28 -2
  104. package/dist/date-pickers.js.map +1 -1
  105. package/dist/datePickers/DateInput.js +41 -54
  106. package/dist/datePickers/DateInput.js.map +1 -1
  107. package/dist/datePickers/DateTimeInput.js +81 -0
  108. package/dist/datePickers/DateTimeInput.js.map +1 -0
  109. package/dist/datePickers/FormDateTimeField.js +12 -0
  110. package/dist/datePickers/FormDateTimeField.js.map +1 -0
  111. package/dist/datePickers/FormMonthField.js +12 -0
  112. package/dist/datePickers/FormMonthField.js.map +1 -0
  113. package/dist/datePickers/FormTimeField.js +12 -0
  114. package/dist/datePickers/FormTimeField.js.map +1 -0
  115. package/dist/datePickers/MonthInput.js +64 -0
  116. package/dist/datePickers/MonthInput.js.map +1 -0
  117. package/dist/datePickers/PickerFrame.js +27 -0
  118. package/dist/datePickers/PickerFrame.js.map +1 -0
  119. package/dist/datePickers/TimeInput.js +61 -0
  120. package/dist/datePickers/TimeInput.js.map +1 -0
  121. package/dist/datePickers/dateTimeRules.js +24 -0
  122. package/dist/datePickers/dateTimeRules.js.map +1 -0
  123. package/dist/datePickers/pickerValues.js +106 -0
  124. package/dist/datePickers/pickerValues.js.map +1 -0
  125. package/dist/datePickers/useCurrentMinute.js +28 -0
  126. package/dist/datePickers/useCurrentMinute.js.map +1 -0
  127. package/dist/datePickers/usePickerField.js +23 -0
  128. package/dist/datePickers/usePickerField.js.map +1 -0
  129. package/dist/dictionaries/dictionaries.js +3 -1
  130. package/dist/dictionaries/dictionaries.js.map +1 -1
  131. package/dist/downloads/DownloadManager.js +3 -2
  132. package/dist/downloads/DownloadManager.js.map +1 -1
  133. package/dist/feedback/ConfirmDialog.js +3 -2
  134. package/dist/feedback/ConfirmDialog.js.map +1 -1
  135. package/dist/feedback/ModalWaiting.js +3 -2
  136. package/dist/feedback/ModalWaiting.js.map +1 -1
  137. package/dist/feedback/NewBuildNotice.js +3 -2
  138. package/dist/feedback/NewBuildNotice.js.map +1 -1
  139. package/dist/filepond/FileDropzone.js +3 -2
  140. package/dist/filepond/FileDropzone.js.map +1 -1
  141. package/dist/forms/FormAttachmentsField.js +12 -0
  142. package/dist/forms/FormAttachmentsField.js.map +1 -0
  143. package/dist/forms/FormCheckboxGroupField.js +12 -0
  144. package/dist/forms/FormCheckboxGroupField.js.map +1 -0
  145. package/dist/forms/FormFileField.js +12 -0
  146. package/dist/forms/FormFileField.js.map +1 -0
  147. package/dist/forms/FormFileUploadField.js +12 -0
  148. package/dist/forms/FormFileUploadField.js.map +1 -0
  149. package/dist/forms/FormImageField.js +12 -0
  150. package/dist/forms/FormImageField.js.map +1 -0
  151. package/dist/forms/FormMaskedField.js +12 -0
  152. package/dist/forms/FormMaskedField.js.map +1 -0
  153. package/dist/forms/FormProblemAlert.js +3 -2
  154. package/dist/forms/FormProblemAlert.js.map +1 -1
  155. package/dist/forms/FormRepeatingFields.js +54 -0
  156. package/dist/forms/FormRepeatingFields.js.map +1 -0
  157. package/dist/forms/FormTransferListField.js +12 -0
  158. package/dist/forms/FormTransferListField.js.map +1 -0
  159. package/dist/forms/SubmitButton.js +7 -2
  160. package/dist/forms/SubmitButton.js.map +1 -1
  161. package/dist/forms/applyServerProblem.js +10 -3
  162. package/dist/forms/applyServerProblem.js.map +1 -1
  163. package/dist/forms/auth/SignInForm.js +3 -2
  164. package/dist/forms/auth/SignInForm.js.map +1 -1
  165. package/dist/forms/fieldRules.js +30 -0
  166. package/dist/forms/fieldRules.js.map +1 -0
  167. package/dist/forms/listRules.js +111 -0
  168. package/dist/forms/listRules.js.map +1 -0
  169. package/dist/forms/security/SecondStepForm.js +3 -2
  170. package/dist/forms/security/SecondStepForm.js.map +1 -1
  171. package/dist/forms/security/TwoStepVerificationCard.js +3 -2
  172. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  173. package/dist/forms/serverProblemFields.js +17 -0
  174. package/dist/forms/serverProblemFields.js.map +1 -1
  175. package/dist/forms/useValidationText.js +18 -0
  176. package/dist/forms/useValidationText.js.map +1 -0
  177. package/dist/forms/validation.js +35 -31
  178. package/dist/forms/validation.js.map +1 -1
  179. package/dist/forms/validationMessages.js +64 -7
  180. package/dist/forms/validationMessages.js.map +1 -1
  181. package/dist/forms.js +54 -29
  182. package/dist/forms.js.map +1 -1
  183. package/dist/hookForm/FormAttachmentsField.js +12 -0
  184. package/dist/hookForm/FormAttachmentsField.js.map +1 -0
  185. package/dist/hookForm/FormCheckboxGroupField.js +12 -0
  186. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -0
  187. package/dist/hookForm/FormFileField.js +12 -0
  188. package/dist/hookForm/FormFileField.js.map +1 -0
  189. package/dist/hookForm/FormFileUploadField.js +12 -0
  190. package/dist/hookForm/FormFileUploadField.js.map +1 -0
  191. package/dist/hookForm/FormImageField.js +12 -0
  192. package/dist/hookForm/FormImageField.js.map +1 -0
  193. package/dist/hookForm/FormMaskedField.js +12 -0
  194. package/dist/hookForm/FormMaskedField.js.map +1 -0
  195. package/dist/hookForm/FormProblemAlert.js +3 -2
  196. package/dist/hookForm/FormProblemAlert.js.map +1 -1
  197. package/dist/hookForm/FormRepeatingFields.js +44 -0
  198. package/dist/hookForm/FormRepeatingFields.js.map +1 -0
  199. package/dist/hookForm/FormTransferListField.js +12 -0
  200. package/dist/hookForm/FormTransferListField.js.map +1 -0
  201. package/dist/hookForm/SubmitButton.js +7 -2
  202. package/dist/hookForm/SubmitButton.js.map +1 -1
  203. package/dist/hookForm/applyServerProblem.js +8 -3
  204. package/dist/hookForm/applyServerProblem.js.map +1 -1
  205. package/dist/inbox/InboxLayout.js +3 -2
  206. package/dist/inbox/InboxLayout.js.map +1 -1
  207. package/dist/inbox/MessageList.js +3 -2
  208. package/dist/inbox/MessageList.js.map +1 -1
  209. package/dist/inbox/MessageView.js +3 -2
  210. package/dist/inbox/MessageView.js.map +1 -1
  211. package/dist/index.js +235 -124
  212. package/dist/index.js.map +1 -1
  213. package/dist/inputs/CatalogInput.js +103 -29
  214. package/dist/inputs/CatalogInput.js.map +1 -1
  215. package/dist/inputs/CheckboxGroupInput.js +103 -0
  216. package/dist/inputs/CheckboxGroupInput.js.map +1 -0
  217. package/dist/inputs/ColorInput.js +7 -4
  218. package/dist/inputs/ColorInput.js.map +1 -1
  219. package/dist/inputs/FieldFrame.js +10 -4
  220. package/dist/inputs/FieldFrame.js.map +1 -1
  221. package/dist/inputs/FormSection.js +1 -1
  222. package/dist/inputs/MaskedInput.js +87 -0
  223. package/dist/inputs/MaskedInput.js.map +1 -0
  224. package/dist/inputs/NumberInput.js +41 -25
  225. package/dist/inputs/NumberInput.js.map +1 -1
  226. package/dist/inputs/RepeatingFields.js +180 -0
  227. package/dist/inputs/RepeatingFields.js.map +1 -0
  228. package/dist/inputs/SelectInput.js +50 -9
  229. package/dist/inputs/SelectInput.js.map +1 -1
  230. package/dist/inputs/TextInput.js +5 -3
  231. package/dist/inputs/TextInput.js.map +1 -1
  232. package/dist/inputs/TransferListInput.js +218 -0
  233. package/dist/inputs/TransferListInput.js.map +1 -0
  234. package/dist/inputs/WizardLayout.js +3 -2
  235. package/dist/inputs/WizardLayout.js.map +1 -1
  236. package/dist/inputs/inputMasks.js +84 -0
  237. package/dist/inputs/inputMasks.js.map +1 -0
  238. package/dist/inputs/selectAllState.js +20 -0
  239. package/dist/inputs/selectAllState.js.map +1 -0
  240. package/dist/inputs/useRowKeys.js +26 -0
  241. package/dist/inputs/useRowKeys.js.map +1 -0
  242. package/dist/kanban/KanbanBoard.js +3 -2
  243. package/dist/kanban/KanbanBoard.js.map +1 -1
  244. package/dist/legal/CookieNotice.js +80 -0
  245. package/dist/legal/CookieNotice.js.map +1 -0
  246. package/dist/legal/TermsAcceptanceDialog.js +130 -0
  247. package/dist/legal/TermsAcceptanceDialog.js.map +1 -0
  248. package/dist/legal/consentStorage.js +18 -0
  249. package/dist/legal/consentStorage.js.map +1 -0
  250. package/dist/legal/useCookieConsent.js +17 -0
  251. package/dist/legal/useCookieConsent.js.map +1 -0
  252. package/dist/locales/en.js +257 -1
  253. package/dist/locales/en.js.map +1 -1
  254. package/dist/locales/es.js +257 -1
  255. package/dist/locales/es.js.map +1 -1
  256. package/dist/maps/MapView.js +3 -2
  257. package/dist/maps/MapView.js.map +1 -1
  258. package/dist/mexico/CfdiSummary.js +82 -0
  259. package/dist/mexico/CfdiSummary.js.map +1 -0
  260. package/dist/mexico/EfirmaFields.js +168 -0
  261. package/dist/mexico/EfirmaFields.js.map +1 -0
  262. package/dist/mexico/certificateReader.js +79 -0
  263. package/dist/mexico/certificateReader.js.map +1 -0
  264. package/dist/mexico/cfdiReader.js +81 -0
  265. package/dist/mexico/cfdiReader.js.map +1 -0
  266. package/dist/mexico/derReader.js +81 -0
  267. package/dist/mexico/derReader.js.map +1 -0
  268. package/dist/mexico/efirmaKey.js +51 -0
  269. package/dist/mexico/efirmaKey.js.map +1 -0
  270. package/dist/mexico/mexicoIdentifiers.js +91 -0
  271. package/dist/mexico/mexicoIdentifiers.js.map +1 -0
  272. package/dist/mexico/mexicoRules.js +21 -0
  273. package/dist/mexico/mexicoRules.js.map +1 -0
  274. package/dist/mexico.js +34 -0
  275. package/dist/mexico.js.map +1 -0
  276. package/dist/pwa/InstallAppSetting.js +3 -2
  277. package/dist/pwa/InstallAppSetting.js.map +1 -1
  278. package/dist/pwa/InstallInstructionsDialog.js +3 -2
  279. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  280. package/dist/pwa/InstallPromptBanner.js +3 -2
  281. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  282. package/dist/react-hook-form.js +43 -17
  283. package/dist/react-hook-form.js.map +1 -1
  284. package/dist/rich-text.js +19 -0
  285. package/dist/rich-text.js.map +1 -0
  286. package/dist/richText/FormRichTextField.js +12 -0
  287. package/dist/richText/FormRichTextField.js.map +1 -0
  288. package/dist/richText/RichTextEditor.js +111 -0
  289. package/dist/richText/RichTextEditor.js.map +1 -0
  290. package/dist/richText/RichTextLinkPopover.js +57 -0
  291. package/dist/richText/RichTextLinkPopover.js.map +1 -0
  292. package/dist/richText/RichTextToolbar.js +189 -0
  293. package/dist/richText/RichTextToolbar.js.map +1 -0
  294. package/dist/richText/RichTextView.js +27 -0
  295. package/dist/richText/RichTextView.js.map +1 -0
  296. package/dist/richText/richTextContentSx.js +42 -0
  297. package/dist/richText/richTextContentSx.js.map +1 -0
  298. package/dist/richText/richTextExtensions.js +44 -0
  299. package/dist/richText/richTextExtensions.js.map +1 -0
  300. package/dist/richText/richTextRules.js +46 -0
  301. package/dist/richText/richTextRules.js.map +1 -0
  302. package/dist/security/ActivityLog.js +3 -2
  303. package/dist/security/ActivityLog.js.map +1 -1
  304. package/dist/security/RecoveryCodesDialog.js +3 -2
  305. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  306. package/dist/security/SessionsCard.js +3 -2
  307. package/dist/security/SessionsCard.js.map +1 -1
  308. package/dist/table.js +23 -2
  309. package/dist/table.js.map +1 -1
  310. package/dist/theme/components/glassComponents.js +27 -0
  311. package/dist/theme/components/glassComponents.js.map +1 -1
  312. package/dist/theme/components/minimalComponents.js +12 -0
  313. package/dist/theme/components/minimalComponents.js.map +1 -1
  314. package/dist/theme/themePieces.js +70 -4
  315. package/dist/theme/themePieces.js.map +1 -1
  316. package/dist/toolkit/ToolkitProvider.js +8 -1
  317. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  318. package/dist/toolkit/browserNavigation.js +19 -4
  319. package/dist/toolkit/browserNavigation.js.map +1 -1
  320. package/dist/toolkit/combineTexts.js +32 -0
  321. package/dist/toolkit/combineTexts.js.map +1 -0
  322. package/dist/toolkit/hashNavigation.js +17 -3
  323. package/dist/toolkit/hashNavigation.js.map +1 -1
  324. package/dist/toolkit/navigationPaths.js +15 -0
  325. package/dist/toolkit/navigationPaths.js.map +1 -1
  326. package/dist/toolkit/pathIndex.js +66 -0
  327. package/dist/toolkit/pathIndex.js.map +1 -0
  328. package/dist/toolkit/toolkitSettings.js +19 -1
  329. package/dist/toolkit/toolkitSettings.js.map +1 -1
  330. package/dist/types/DataTable/AuditTrailDialog.d.ts +6 -17
  331. package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +6 -5
  332. package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +6 -9
  333. package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +6 -14
  334. package/dist/types/DataTable/EmptyState.d.ts +6 -9
  335. package/dist/types/DataTable/FilterChips.d.ts +6 -11
  336. package/dist/types/DataTable/GoToPage.d.ts +6 -9
  337. package/dist/types/DataTable/ServerTable.d.ts +16 -80
  338. package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +14 -0
  339. package/dist/types/DataTable/StackedCell.d.ts +12 -0
  340. package/dist/types/DataTable/TableCardsView.d.ts +27 -0
  341. package/dist/types/DataTable/columnWidths.d.ts +21 -0
  342. package/dist/types/DataTable/columns.d.ts +111 -0
  343. package/dist/types/DataTable/filterContext.d.ts +2 -1
  344. package/dist/types/DataTable/fixedColumns.d.ts +29 -0
  345. package/dist/types/DataTable/goToPageOptions.d.ts +15 -0
  346. package/dist/types/DataTable/index.d.ts +7 -1
  347. package/dist/types/DataTable/useTableView.d.ts +22 -0
  348. package/dist/types/access/PermissionMatrix.d.ts +6 -10
  349. package/dist/types/account/AccountSecuritySection.d.ts +6 -7
  350. package/dist/types/account/DemoTotpCodeButton.d.ts +6 -5
  351. package/dist/types/account/ProfileScreen.d.ts +6 -18
  352. package/dist/types/account/SignInScreen.d.ts +6 -13
  353. package/dist/types/appShell/AccountMenu.d.ts +6 -11
  354. package/dist/types/appShell/AppLayout.d.ts +3 -40
  355. package/dist/types/appShell/AppearanceSettings.d.ts +6 -12
  356. package/dist/types/appShell/AuthLayout.d.ts +3 -11
  357. package/dist/types/appShell/BarActionButton.d.ts +6 -10
  358. package/dist/types/appShell/BarActions.d.ts +6 -15
  359. package/dist/types/appShell/BarCapsule.d.ts +6 -9
  360. package/dist/types/appShell/BarSearch.d.ts +6 -15
  361. package/dist/types/appShell/HeaderTools.d.ts +6 -16
  362. package/dist/types/appShell/MobileTabBar.d.ts +6 -13
  363. package/dist/types/appShell/SettingsMenu.d.ts +6 -10
  364. package/dist/types/appShell/SideMenu.d.ts +6 -20
  365. package/dist/types/appShell/SideMenuEntry.d.ts +6 -11
  366. package/dist/types/appShell/SubmenuActionsMenu.d.ts +6 -12
  367. package/dist/types/appShell/TabBarEditor.d.ts +6 -9
  368. package/dist/types/appShell/TopMenu.d.ts +6 -5
  369. package/dist/types/appShell/Wordmark.d.ts +6 -7
  370. package/dist/types/auth/PasswordChecklist.d.ts +6 -9
  371. package/dist/types/calendar/EventCalendar.d.ts +6 -23
  372. package/dist/types/calendar/EventDetails.d.ts +6 -10
  373. package/dist/types/date-pickers.d.ts +10 -1
  374. package/dist/types/datePickers/DateInput.d.ts +10 -23
  375. package/dist/types/datePickers/DateTimeInput.d.ts +30 -0
  376. package/dist/types/datePickers/FormDateTimeField.d.ts +7 -0
  377. package/dist/types/datePickers/FormMonthField.d.ts +7 -0
  378. package/dist/types/datePickers/FormTimeField.d.ts +7 -0
  379. package/dist/types/datePickers/MonthInput.d.ts +25 -0
  380. package/dist/types/datePickers/PickerFrame.d.ts +22 -0
  381. package/dist/types/datePickers/TimeInput.d.ts +24 -0
  382. package/dist/types/datePickers/dateTimeRules.d.ts +31 -0
  383. package/dist/types/datePickers/pickerValues.d.ts +114 -0
  384. package/dist/types/datePickers/useCurrentMinute.d.ts +5 -0
  385. package/dist/types/datePickers/usePickerField.d.ts +28 -0
  386. package/dist/types/dictionaries/dictionaries.d.ts +6 -3
  387. package/dist/types/downloads/DownloadManager.d.ts +6 -6
  388. package/dist/types/feedback/ConfirmDialog.d.ts +6 -18
  389. package/dist/types/feedback/ModalWaiting.d.ts +6 -9
  390. package/dist/types/feedback/NewBuildNotice.d.ts +6 -10
  391. package/dist/types/filepond/FileDropzone.d.ts +6 -29
  392. package/dist/types/forms/FormAttachmentsField.d.ts +7 -0
  393. package/dist/types/forms/FormCheckboxGroupField.d.ts +7 -0
  394. package/dist/types/forms/FormFileField.d.ts +7 -0
  395. package/dist/types/forms/FormFileUploadField.d.ts +7 -0
  396. package/dist/types/forms/FormImageField.d.ts +7 -0
  397. package/dist/types/forms/FormMaskedField.d.ts +7 -0
  398. package/dist/types/forms/FormProblemAlert.d.ts +6 -5
  399. package/dist/types/forms/FormRepeatingFields.d.ts +25 -0
  400. package/dist/types/forms/FormTransferListField.d.ts +7 -0
  401. package/dist/types/forms/SubmitButton.d.ts +6 -6
  402. package/dist/types/forms/auth/SignInForm.d.ts +6 -11
  403. package/dist/types/forms/fieldRules.d.ts +44 -0
  404. package/dist/types/forms/index.d.ts +11 -1
  405. package/dist/types/forms/listRules.d.ts +48 -0
  406. package/dist/types/forms/security/SecondStepForm.d.ts +6 -14
  407. package/dist/types/forms/security/TwoStepVerificationCard.d.ts +6 -21
  408. package/dist/types/forms/serverProblemFields.d.ts +13 -0
  409. package/dist/types/forms/useValidationText.d.ts +12 -0
  410. package/dist/types/forms/validation.d.ts +117 -174
  411. package/dist/types/forms/validationMessages.d.ts +54 -6
  412. package/dist/types/forms.d.ts +1 -1
  413. package/dist/types/hookForm/FormAttachmentsField.d.ts +8 -0
  414. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +8 -0
  415. package/dist/types/hookForm/FormFileField.d.ts +8 -0
  416. package/dist/types/hookForm/FormFileUploadField.d.ts +8 -0
  417. package/dist/types/hookForm/FormImageField.d.ts +8 -0
  418. package/dist/types/hookForm/FormMaskedField.d.ts +9 -0
  419. package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
  420. package/dist/types/hookForm/FormRepeatingFields.d.ts +27 -0
  421. package/dist/types/hookForm/FormTransferListField.d.ts +8 -0
  422. package/dist/types/hookForm/SubmitButton.d.ts +6 -13
  423. package/dist/types/inbox/InboxLayout.d.ts +6 -17
  424. package/dist/types/inbox/MessageList.d.ts +6 -18
  425. package/dist/types/inbox/MessageView.d.ts +6 -14
  426. package/dist/types/index.d.ts +33 -4
  427. package/dist/types/inputs/CatalogInput.d.ts +10 -21
  428. package/dist/types/inputs/CheckboxGroupInput.d.ts +28 -0
  429. package/dist/types/inputs/ColorInput.d.ts +7 -16
  430. package/dist/types/inputs/FieldFrame.d.ts +6 -10
  431. package/dist/types/inputs/MaskedInput.d.ts +38 -0
  432. package/dist/types/inputs/NumberInput.d.ts +7 -20
  433. package/dist/types/inputs/RepeatingFields.d.ts +31 -0
  434. package/dist/types/inputs/SelectInput.d.ts +10 -18
  435. package/dist/types/inputs/TextInput.d.ts +3 -0
  436. package/dist/types/inputs/TransferListInput.d.ts +30 -0
  437. package/dist/types/inputs/WizardLayout.d.ts +6 -19
  438. package/dist/types/inputs/inputMasks.d.ts +52 -0
  439. package/dist/types/inputs/selectAllState.d.ts +11 -0
  440. package/dist/types/inputs/useRowKeys.d.ts +11 -0
  441. package/dist/types/kanban/KanbanBoard.d.ts +6 -25
  442. package/dist/types/legal/CookieNotice.d.ts +20 -0
  443. package/dist/types/legal/TermsAcceptanceDialog.d.ts +20 -0
  444. package/dist/types/legal/consentStorage.d.ts +16 -0
  445. package/dist/types/legal/useCookieConsent.d.ts +13 -0
  446. package/dist/types/locales/en.d.ts +256 -0
  447. package/dist/types/locales/es.d.ts +256 -0
  448. package/dist/types/maps/MapView.d.ts +6 -17
  449. package/dist/types/mexico/CfdiSummary.d.ts +12 -0
  450. package/dist/types/mexico/EfirmaFields.d.ts +28 -0
  451. package/dist/types/mexico/certificateReader.d.ts +37 -0
  452. package/dist/types/mexico/cfdiReader.d.ts +95 -0
  453. package/dist/types/mexico/derReader.d.ts +54 -0
  454. package/dist/types/mexico/efirmaKey.d.ts +26 -0
  455. package/dist/types/mexico/mexicoIdentifiers.d.ts +68 -0
  456. package/dist/types/mexico/mexicoRules.d.ts +45 -0
  457. package/dist/types/mexico.d.ts +11 -0
  458. package/dist/types/pwa/InstallAppSetting.d.ts +6 -6
  459. package/dist/types/pwa/InstallInstructionsDialog.d.ts +6 -9
  460. package/dist/types/pwa/InstallPromptBanner.d.ts +6 -11
  461. package/dist/types/react-hook-form.d.ts +11 -1
  462. package/dist/types/rich-text.d.ts +5 -0
  463. package/dist/types/richText/FormRichTextField.d.ts +7 -0
  464. package/dist/types/richText/RichTextEditor.d.ts +27 -0
  465. package/dist/types/richText/RichTextLinkPopover.d.ts +17 -0
  466. package/dist/types/richText/RichTextToolbar.d.ts +15 -0
  467. package/dist/types/richText/RichTextView.d.ts +10 -0
  468. package/dist/types/richText/richTextContentSx.d.ts +7 -0
  469. package/dist/types/richText/richTextExtensions.d.ts +15 -0
  470. package/dist/types/richText/richTextRules.d.ts +41 -0
  471. package/dist/types/security/ActivityLog.d.ts +6 -9
  472. package/dist/types/security/RecoveryCodesDialog.d.ts +6 -11
  473. package/dist/types/security/SessionsCard.d.ts +6 -11
  474. package/dist/types/table.d.ts +3 -1
  475. package/dist/types/theme/themePieces.d.ts +55 -3
  476. package/dist/types/toolkit/ToolkitProvider.d.ts +13 -1
  477. package/dist/types/toolkit/browserNavigation.d.ts +7 -3
  478. package/dist/types/toolkit/combineTexts.d.ts +6 -0
  479. package/dist/types/toolkit/hashNavigation.d.ts +4 -2
  480. package/dist/types/toolkit/navigationPaths.d.ts +35 -0
  481. package/dist/types/toolkit/pathIndex.d.ts +27 -0
  482. package/dist/types/toolkit/toolkitSettings.d.ts +7 -4
  483. package/dist/types/ui/actions/ActionsMenu.d.ts +24 -11
  484. package/dist/types/ui/actions/CopyButton.d.ts +8 -11
  485. package/dist/types/ui/actions/DownloadMenu.d.ts +6 -29
  486. package/dist/types/ui/actions/SecretField.d.ts +6 -11
  487. package/dist/types/ui/actions/SelectionActions.d.ts +6 -13
  488. package/dist/types/ui/actions/tableRowText.d.ts +6 -0
  489. package/dist/types/ui/cards/RecordCard.d.ts +40 -0
  490. package/dist/types/ui/cards/RecordCardSkeleton.d.ts +12 -0
  491. package/dist/types/ui/countdown/Countdown.d.ts +22 -0
  492. package/dist/types/ui/countdown/countdownParts.d.ts +39 -0
  493. package/dist/types/ui/countdown/useCountdown.d.ts +14 -0
  494. package/dist/types/ui/documents/PdfViewerDialog.d.ts +14 -0
  495. package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
  496. package/dist/types/ui/filters/FilterDrawer.d.ts +23 -0
  497. package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
  498. package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
  499. package/dist/types/ui/filters/filterDrawerFields.d.ts +139 -0
  500. package/dist/types/ui/help/HelpButton.d.ts +11 -0
  501. package/dist/types/ui/help/HelpCarouselDialog.d.ts +27 -0
  502. package/dist/types/ui/help/useHelpCarousel.d.ts +21 -0
  503. package/dist/types/ui/index.d.ts +10 -0
  504. package/dist/types/ui/labels/IconBadge.d.ts +34 -0
  505. package/dist/types/ui/labels/MarkedText.d.ts +10 -0
  506. package/dist/types/ui/labels/Tag.d.ts +6 -12
  507. package/dist/types/ui/labels/markedTextPieces.d.ts +22 -0
  508. package/dist/types/ui/layout/CollapsibleSection.d.ts +6 -12
  509. package/dist/types/ui/layout/ContentDialog.d.ts +6 -10
  510. package/dist/types/ui/layout/DefinitionList.d.ts +6 -9
  511. package/dist/types/ui/layout/DetailHeader.d.ts +6 -10
  512. package/dist/types/ui/layout/ErrorPage.d.ts +6 -17
  513. package/dist/types/ui/layout/FullPageLoader.d.ts +6 -6
  514. package/dist/types/ui/layout/GlassPanel.d.ts +6 -8
  515. package/dist/types/ui/layout/PageHeader.d.ts +6 -10
  516. package/dist/types/ui/layout/PageHeading.d.ts +6 -11
  517. package/dist/types/ui/layout/ProfileHeader.d.ts +6 -11
  518. package/dist/types/ui/layout/ScrollableRow.d.ts +6 -13
  519. package/dist/types/ui/layout/SectionCard.d.ts +6 -8
  520. package/dist/types/ui/metrics/BarList.d.ts +6 -12
  521. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +6 -19
  522. package/dist/types/ui/metrics/ChartTooltip.d.ts +6 -6
  523. package/dist/types/ui/metrics/ColumnChart.d.ts +6 -25
  524. package/dist/types/ui/metrics/LineChart.d.ts +6 -20
  525. package/dist/types/ui/metrics/MetricCard.d.ts +6 -17
  526. package/dist/types/ui/metrics/TableToggle.d.ts +6 -6
  527. package/dist/types/ui/metrics/chartParts.d.ts +15 -30
  528. package/dist/types/ui/navigation/BackButton.d.ts +6 -9
  529. package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +6 -9
  530. package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -8
  531. package/dist/types/ui/navigation/LanguageToggle.d.ts +6 -8
  532. package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +6 -7
  533. package/dist/types/ui/navigation/RouteTabs.d.ts +39 -0
  534. package/dist/types/ui/navigation/routeTabMatching.d.ts +27 -0
  535. package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
  536. package/dist/types/upload/AttachmentsInput.d.ts +72 -0
  537. package/dist/types/upload/FileChip.d.ts +18 -0
  538. package/dist/types/upload/FileDropzone.d.ts +7 -19
  539. package/dist/types/upload/FileInput.d.ts +31 -0
  540. package/dist/types/upload/FileUploadInput.d.ts +56 -0
  541. package/dist/types/upload/ImageInput.d.ts +35 -0
  542. package/dist/types/upload/TemplateDownloadButton.d.ts +27 -0
  543. package/dist/types/upload/fileAppearance.d.ts +8 -0
  544. package/dist/types/upload/fileRules.d.ts +38 -0
  545. package/dist/types/upload/transferProgress.d.ts +18 -0
  546. package/dist/types/upload/uploadMachine.d.ts +6 -2
  547. package/dist/types/upload/uploadWithProgress.d.ts +6 -2
  548. package/dist/types/upload/useFilePicker.d.ts +39 -0
  549. package/dist/ui/actions/ActionsMenu.js +109 -17
  550. package/dist/ui/actions/ActionsMenu.js.map +1 -1
  551. package/dist/ui/actions/CopyButton.js +37 -12
  552. package/dist/ui/actions/CopyButton.js.map +1 -1
  553. package/dist/ui/actions/DownloadMenu.js +3 -2
  554. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  555. package/dist/ui/actions/SecretField.js +8 -3
  556. package/dist/ui/actions/SecretField.js.map +1 -1
  557. package/dist/ui/actions/SelectionActions.js +3 -2
  558. package/dist/ui/actions/SelectionActions.js.map +1 -1
  559. package/dist/ui/actions/tableRowText.js +7 -0
  560. package/dist/ui/actions/tableRowText.js.map +1 -0
  561. package/dist/ui/cards/RecordCard.js +146 -0
  562. package/dist/ui/cards/RecordCard.js.map +1 -0
  563. package/dist/ui/cards/RecordCardSkeleton.js +38 -0
  564. package/dist/ui/cards/RecordCardSkeleton.js.map +1 -0
  565. package/dist/ui/countdown/Countdown.js +39 -0
  566. package/dist/ui/countdown/Countdown.js.map +1 -0
  567. package/dist/ui/countdown/countdownParts.js +34 -0
  568. package/dist/ui/countdown/countdownParts.js.map +1 -0
  569. package/dist/ui/countdown/useCountdown.js +35 -0
  570. package/dist/ui/countdown/useCountdown.js.map +1 -0
  571. package/dist/ui/documents/PdfViewerDialog.js +82 -0
  572. package/dist/ui/documents/PdfViewerDialog.js.map +1 -0
  573. package/dist/ui/filters/DateRangeFilter.js +3 -2
  574. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  575. package/dist/ui/filters/FilterDrawer.js +212 -0
  576. package/dist/ui/filters/FilterDrawer.js.map +1 -0
  577. package/dist/ui/filters/PeriodRangeSelector.js +3 -2
  578. package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
  579. package/dist/ui/filters/QuickFilters.js +3 -2
  580. package/dist/ui/filters/QuickFilters.js.map +1 -1
  581. package/dist/ui/filters/filterDrawerFields.js +119 -0
  582. package/dist/ui/filters/filterDrawerFields.js.map +1 -0
  583. package/dist/ui/help/HelpButton.js +22 -0
  584. package/dist/ui/help/HelpButton.js.map +1 -0
  585. package/dist/ui/help/HelpCarouselDialog.js +155 -0
  586. package/dist/ui/help/HelpCarouselDialog.js.map +1 -0
  587. package/dist/ui/help/useHelpCarousel.js +25 -0
  588. package/dist/ui/help/useHelpCarousel.js.map +1 -0
  589. package/dist/ui/labels/IconBadge.js +82 -0
  590. package/dist/ui/labels/IconBadge.js.map +1 -0
  591. package/dist/ui/labels/MarkedText.js +31 -0
  592. package/dist/ui/labels/MarkedText.js.map +1 -0
  593. package/dist/ui/labels/Tag.js +3 -2
  594. package/dist/ui/labels/Tag.js.map +1 -1
  595. package/dist/ui/labels/markedTextPieces.js +64 -0
  596. package/dist/ui/labels/markedTextPieces.js.map +1 -0
  597. package/dist/ui/layout/CollapsibleSection.js +3 -2
  598. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  599. package/dist/ui/layout/ContentDialog.js +3 -2
  600. package/dist/ui/layout/ContentDialog.js.map +1 -1
  601. package/dist/ui/layout/DefinitionList.js +3 -2
  602. package/dist/ui/layout/DefinitionList.js.map +1 -1
  603. package/dist/ui/layout/DetailHeader.js +3 -2
  604. package/dist/ui/layout/DetailHeader.js.map +1 -1
  605. package/dist/ui/layout/ErrorPage.js +3 -2
  606. package/dist/ui/layout/ErrorPage.js.map +1 -1
  607. package/dist/ui/layout/FullPageLoader.js +3 -2
  608. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  609. package/dist/ui/layout/GlassPanel.js +3 -2
  610. package/dist/ui/layout/GlassPanel.js.map +1 -1
  611. package/dist/ui/layout/PageHeader.js +3 -2
  612. package/dist/ui/layout/PageHeader.js.map +1 -1
  613. package/dist/ui/layout/PageHeading.js +3 -2
  614. package/dist/ui/layout/PageHeading.js.map +1 -1
  615. package/dist/ui/layout/ProfileHeader.js +3 -2
  616. package/dist/ui/layout/ProfileHeader.js.map +1 -1
  617. package/dist/ui/layout/ScrollableRow.js +3 -2
  618. package/dist/ui/layout/ScrollableRow.js.map +1 -1
  619. package/dist/ui/layout/SectionCard.js +3 -2
  620. package/dist/ui/layout/SectionCard.js.map +1 -1
  621. package/dist/ui/metrics/BarList.js +3 -2
  622. package/dist/ui/metrics/BarList.js.map +1 -1
  623. package/dist/ui/metrics/CalendarHeatmap.js +3 -2
  624. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  625. package/dist/ui/metrics/ChartTooltip.js +3 -2
  626. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  627. package/dist/ui/metrics/ColumnChart.js +3 -2
  628. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  629. package/dist/ui/metrics/LineChart.js +3 -2
  630. package/dist/ui/metrics/LineChart.js.map +1 -1
  631. package/dist/ui/metrics/MetricCard.js +3 -2
  632. package/dist/ui/metrics/MetricCard.js.map +1 -1
  633. package/dist/ui/metrics/TableToggle.js +3 -2
  634. package/dist/ui/metrics/TableToggle.js.map +1 -1
  635. package/dist/ui/metrics/chartParts.js +7 -4
  636. package/dist/ui/metrics/chartParts.js.map +1 -1
  637. package/dist/ui/navigation/BackButton.js +7 -2
  638. package/dist/ui/navigation/BackButton.js.map +1 -1
  639. package/dist/ui/navigation/ColorSchemeToggle.js +3 -2
  640. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  641. package/dist/ui/navigation/FontSizeToggle.js +3 -2
  642. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  643. package/dist/ui/navigation/LanguageToggle.js +3 -2
  644. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  645. package/dist/ui/navigation/PageBreadcrumbs.js +3 -2
  646. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  647. package/dist/ui/navigation/RouteTabs.js +79 -0
  648. package/dist/ui/navigation/RouteTabs.js.map +1 -0
  649. package/dist/ui/navigation/routeTabMatching.js +23 -0
  650. package/dist/ui/navigation/routeTabMatching.js.map +1 -0
  651. package/dist/ui/timeline/GroupedTimeline.js +3 -2
  652. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  653. package/dist/upload/AttachmentsInput.js +130 -0
  654. package/dist/upload/AttachmentsInput.js.map +1 -0
  655. package/dist/upload/FileChip.js +28 -0
  656. package/dist/upload/FileChip.js.map +1 -0
  657. package/dist/upload/FileDropzone.js +23 -87
  658. package/dist/upload/FileDropzone.js.map +1 -1
  659. package/dist/upload/FileInput.js +115 -0
  660. package/dist/upload/FileInput.js.map +1 -0
  661. package/dist/upload/FileUploadInput.js +288 -0
  662. package/dist/upload/FileUploadInput.js.map +1 -0
  663. package/dist/upload/ImageInput.js +128 -0
  664. package/dist/upload/ImageInput.js.map +1 -0
  665. package/dist/upload/TemplateDownloadButton.js +68 -0
  666. package/dist/upload/TemplateDownloadButton.js.map +1 -0
  667. package/dist/upload/fileAppearance.js +21 -0
  668. package/dist/upload/fileAppearance.js.map +1 -0
  669. package/dist/upload/fileRules.js +27 -1
  670. package/dist/upload/fileRules.js.map +1 -1
  671. package/dist/upload/transferProgress.js +23 -0
  672. package/dist/upload/transferProgress.js.map +1 -0
  673. package/dist/upload/uploadMachine.js +8 -8
  674. package/dist/upload/uploadMachine.js.map +1 -1
  675. package/dist/upload/uploadWithProgress.js +1 -1
  676. package/dist/upload/uploadWithProgress.js.map +1 -1
  677. package/dist/upload/useFilePicker.js +101 -0
  678. package/dist/upload/useFilePicker.js.map +1 -0
  679. package/package.json +44 -1
  680. package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
@@ -1 +1 @@
1
- {"version":3,"file":"LanguageToggle.js","sources":["../../../src/ui/navigation/LanguageToggle.jsx"],"sourcesContent":["// El botón de idioma de la barra y de las pantallas de entrada.\n//\n// Los idiomas son los que ofrece la app (ToolkitProvider `languages`; por omisión español e inglés):\n// - con dos, un botón que cambia al otro (dice su código: «EN»);\n// - con tres o más, el botón dice el de ahora y abre un menú con cada uno en su propio idioma («Português»), como lo busca\n// quien lo habla; el elegido con su palomita.\n//\n// Solo avisa el idioma nuevo (onLanguageChange de ToolkitProvider). Lo demás lo hace el proyecto: su i18n, el header\n// Accept-Language de las peticiones y volver a pedir los datos del servidor, que traen textos traducidos. Las traducciones de\n// un idioma que agregue el proyecto van en `texts` de ToolkitProvider: el paquete solo trae español e inglés.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaLanguageToggle con defaultProps y\n// styleOverrides de sus partes: button.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Button from '@mui/material/Button';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\n\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {DEFAULT_LANGUAGES} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaLanguageToggle)\nconst THEME_NAME = 'CalcitaLanguageToggle';\nconst LanguageButton = piecePart(THEME_NAME, 'Button', Button);\n\n/** Los idiomas que traen los textos del paquete. */\nexport const LANGUAGES = Object.freeze(DEFAULT_LANGUAGES.map((entry) => entry.code));\n\n/**\n * @param {object} [props]\n * @param {boolean} [props.inverted] texto blanco para fondos oscuros\n * @param {(language: string) => void} [props.onChange] además de avisar a ToolkitProvider, cada que se elige un idioma\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {language: {switchToNamed, choose}}\n */\nexport default function LanguageToggle(props) {\n // lo que el proyecto puso en su tema (components.CalcitaLanguageToggle.defaultProps) y encima lo que llega aquí\n const {inverted = false, onChange, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t, language, changeLanguage} = useToolkitTranslation('common', texts);\n const {languages = DEFAULT_LANGUAGES} = useToolkitSettings();\n const [menuAnchor, setMenuAnchor] = useState(null);\n const current = languages.find((entry) => entry.code === language) ?? languages[0];\n const buttonSx = {minWidth: 0, fontWeight: 700, letterSpacing: '0.08em', color: inverted ? 'common.white' : 'text.secondary'};\n\n const choose = (code) => {\n setMenuAnchor(null);\n changeLanguage(code);\n onChange?.(code);\n };\n\n // con dos: un botón que cambia al otro\n if (languages.length <= 2) {\n const next = languages.find((entry) => entry.code !== current.code) ?? current;\n // el aviso de siempre para español e inglés; para otro idioma, con su nombre\n const ariaLabel = t(`language.switchTo.${next.code}`, {defaultValue: t('language.switchToNamed', {name: next.label})});\n return (\n <LanguageButton size=\"small\" variant=\"text\" onClick={() => choose(next.code)} lang={next.code} aria-label={ariaLabel} ownSx={buttonSx}>\n {next.code.toUpperCase()}\n </LanguageButton>\n );\n }\n\n // con tres o más: el de ahora y un menú con todos\n return (\n <>\n <LanguageButton size=\"small\" variant=\"text\" onClick={(event) => setMenuAnchor(event.currentTarget)} aria-haspopup=\"menu\"\n aria-expanded={Boolean(menuAnchor)} aria-label={t('language.choose', {name: current.label})} ownSx={buttonSx}>\n {current.code.toUpperCase()}\n </LanguageButton>\n <Menu anchorEl={menuAnchor} open={Boolean(menuAnchor)} onClose={() => setMenuAnchor(null)}>\n {languages.map((entry) => (\n <MenuItem key={entry.code} lang={entry.code} selected={entry.code === current.code} onClick={() => choose(entry.code)}>\n {entry.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,UAAU,MAAM;AAGtD,MAAM,YAAY,OAAO,OAAO,kBAAkB,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AAQnF,SAAwB,eAAe,OAAO;AAE1C,QAAM,EAAC,WAAW,OAAO,UAAU,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AAC5F,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,GAAG,UAAU,mBAAkB,sBAAsB,UAAU,KAAK;AAC3E,QAAM,EAAC,YAAY,kBAAA,IAAqB,mBAAA;AACxC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,UAAU,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,KAAK,UAAU,CAAC;AACjF,QAAM,WAAW,EAAC,UAAU,GAAG,YAAY,KAAK,eAAe,UAAU,OAAO,WAAW,iBAAiB,iBAAA;AAE5G,QAAM,SAAS,CAAC,SAAS;AACrB,kBAAc,IAAI;AAClB,mBAAe,IAAI;AACnB,eAAW,IAAI;AAAA,EACnB;AAGA,MAAI,UAAU,UAAU,GAAG;AACvB,UAAM,OAAO,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,IAAI,KAAK;AAEvE,UAAM,YAAY,EAAE,qBAAqB,KAAK,IAAI,IAAI,EAAC,cAAc,EAAE,0BAA0B,EAAC,MAAM,KAAK,MAAA,CAAM,GAAE;AACrH,WACI,oBAAC,kBAAe,MAAK,SAAQ,SAAQ,QAAO,SAAS,MAAM,OAAO,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,cAAY,WAAW,OAAO,UACxH,UAAA,KAAK,KAAK,YAAA,EAAY,CAC3B;AAAA,EAER;AAGA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAe,MAAK;AAAA,QAAQ,SAAQ;AAAA,QAAO,SAAS,CAAC,UAAU,cAAc,MAAM,aAAa;AAAA,QAAG,iBAAc;AAAA,QAC1G,iBAAe,QAAQ,UAAU;AAAA,QAAG,cAAY,EAAE,mBAAmB,EAAC,MAAM,QAAQ,OAAM;AAAA,QAAG,OAAO;AAAA,QACvG,UAAA,QAAQ,KAAK,YAAA;AAAA,MAAY;AAAA,IAAA;AAAA,wBAE7B,MAAA,EAAK,UAAU,YAAY,MAAM,QAAQ,UAAU,GAAG,SAAS,MAAM,cAAc,IAAI,GACnF,oBAAU,IAAI,CAAC,UACZ,oBAAC,UAAA,EAA0B,MAAM,MAAM,MAAM,UAAU,MAAM,SAAS,QAAQ,MAAM,SAAS,MAAM,OAAO,MAAM,IAAI,GAC/G,UAAA,MAAM,SADI,MAAM,IAErB,CACH,EAAA,CACL;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"LanguageToggle.js","sources":["../../../src/ui/navigation/LanguageToggle.jsx"],"sourcesContent":["// El botón de idioma de la barra y de las pantallas de entrada.\n//\n// Los idiomas son los que ofrece la app (ToolkitProvider `languages`; por omisión español e inglés):\n// - con dos, un botón que cambia al otro (dice su código: «EN»);\n// - con tres o más, el botón dice el de ahora y abre un menú con cada uno en su propio idioma («Português»), como lo busca\n// quien lo habla; el elegido con su palomita.\n//\n// Solo avisa el idioma nuevo (onLanguageChange de ToolkitProvider). Lo demás lo hace el proyecto: su i18n, el header\n// Accept-Language de las peticiones y volver a pedir los datos del servidor, que traen textos traducidos. Las traducciones de\n// un idioma que agregue el proyecto van en `texts` de ToolkitProvider: el paquete solo trae español e inglés.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaLanguageToggle con defaultProps y\n// styleOverrides de sus partes: button.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Button from '@mui/material/Button';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\n\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {DEFAULT_LANGUAGES} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaLanguageToggle)\nconst THEME_NAME = 'CalcitaLanguageToggle';\nconst LanguageButton = piecePart(THEME_NAME, 'Button', Button);\n\n/** Los idiomas que traen los textos del paquete. */\nexport const LANGUAGES = Object.freeze(DEFAULT_LANGUAGES.map((entry) => entry.code));\n\n/**\n * @param {object} [props]\n * @param {boolean} [props.inverted] texto blanco para fondos oscuros\n * @param {(language: string) => void} [props.onChange] además de avisar a ToolkitProvider, cada que se elige un idioma\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {language: {switchToNamed, choose}}\n */\nfunction LanguageToggleContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaLanguageToggle.defaultProps) y encima lo que llega aquí\n const {inverted = false, onChange, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t, language, changeLanguage} = useToolkitTranslation('common', texts);\n const {languages = DEFAULT_LANGUAGES} = useToolkitSettings();\n const [menuAnchor, setMenuAnchor] = useState(null);\n const current = languages.find((entry) => entry.code === language) ?? languages[0];\n const buttonSx = {minWidth: 0, fontWeight: 700, letterSpacing: '0.08em', color: inverted ? 'common.white' : 'text.secondary'};\n\n const choose = (code) => {\n setMenuAnchor(null);\n changeLanguage(code);\n onChange?.(code);\n };\n\n // con dos: un botón que cambia al otro\n if (languages.length <= 2) {\n const next = languages.find((entry) => entry.code !== current.code) ?? current;\n // el aviso de siempre para español e inglés; para otro idioma, con su nombre\n const ariaLabel = t(`language.switchTo.${next.code}`, {defaultValue: t('language.switchToNamed', {name: next.label})});\n return (\n <LanguageButton size=\"small\" variant=\"text\" onClick={() => choose(next.code)} lang={next.code} aria-label={ariaLabel} ownSx={buttonSx}>\n {next.code.toUpperCase()}\n </LanguageButton>\n );\n }\n\n // con tres o más: el de ahora y un menú con todos\n return (\n <>\n <LanguageButton size=\"small\" variant=\"text\" onClick={(event) => setMenuAnchor(event.currentTarget)} aria-haspopup=\"menu\"\n aria-expanded={Boolean(menuAnchor)} aria-label={t('language.choose', {name: current.label})} ownSx={buttonSx}>\n {current.code.toUpperCase()}\n </LanguageButton>\n <Menu anchorEl={menuAnchor} open={Boolean(menuAnchor)} onClose={() => setMenuAnchor(null)}>\n {languages.map((entry) => (\n <MenuItem key={entry.code} lang={entry.code} selected={entry.code === current.code} onClick={() => choose(entry.code)}>\n {entry.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst LanguageToggle = withPieceSlots(LanguageToggleContent, THEME_NAME);\nexport default LanguageToggle;\n"],"names":[],"mappings":";;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,UAAU,MAAM;AAGtD,MAAM,YAAY,OAAO,OAAO,kBAAkB,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AAQnF,SAAS,sBAAsB,OAAO;AAElC,QAAM,EAAC,WAAW,OAAO,UAAU,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AAC5F,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,GAAG,UAAU,mBAAkB,sBAAsB,UAAU,KAAK;AAC3E,QAAM,EAAC,YAAY,kBAAA,IAAqB,mBAAA;AACxC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,UAAU,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,KAAK,UAAU,CAAC;AACjF,QAAM,WAAW,EAAC,UAAU,GAAG,YAAY,KAAK,eAAe,UAAU,OAAO,WAAW,iBAAiB,iBAAA;AAE5G,QAAM,SAAS,CAAC,SAAS;AACrB,kBAAc,IAAI;AAClB,mBAAe,IAAI;AACnB,eAAW,IAAI;AAAA,EACnB;AAGA,MAAI,UAAU,UAAU,GAAG;AACvB,UAAM,OAAO,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,IAAI,KAAK;AAEvE,UAAM,YAAY,EAAE,qBAAqB,KAAK,IAAI,IAAI,EAAC,cAAc,EAAE,0BAA0B,EAAC,MAAM,KAAK,MAAA,CAAM,GAAE;AACrH,WACI,oBAAC,kBAAe,MAAK,SAAQ,SAAQ,QAAO,SAAS,MAAM,OAAO,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,cAAY,WAAW,OAAO,UACxH,UAAA,KAAK,KAAK,YAAA,EAAY,CAC3B;AAAA,EAER;AAGA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAe,MAAK;AAAA,QAAQ,SAAQ;AAAA,QAAO,SAAS,CAAC,UAAU,cAAc,MAAM,aAAa;AAAA,QAAG,iBAAc;AAAA,QAC1G,iBAAe,QAAQ,UAAU;AAAA,QAAG,cAAY,EAAE,mBAAmB,EAAC,MAAM,QAAQ,OAAM;AAAA,QAAG,OAAO;AAAA,QACvG,UAAA,QAAQ,KAAK,YAAA;AAAA,MAAY;AAAA,IAAA;AAAA,wBAE7B,MAAA,EAAK,UAAU,YAAY,MAAM,QAAQ,UAAU,GAAG,SAAS,MAAM,cAAc,IAAI,GACnF,oBAAU,IAAI,CAAC,UACZ,oBAAC,UAAA,EAA0B,MAAM,MAAM,MAAM,UAAU,MAAM,SAAS,QAAQ,MAAM,SAAS,MAAM,OAAO,MAAM,IAAI,GAC/G,UAAA,MAAM,SADI,MAAM,IAErB,CACH,EAAA,CACL;AAAA,EAAA,GACJ;AAER;AAGA,MAAM,iBAAiB,eAAe,uBAAuB,UAAU;"}
@@ -8,13 +8,13 @@ import Typography from "@mui/material/Typography";
8
8
  import { textSizeOf } from "../../theme/appleTracking.js";
9
9
  import ScrollableRow from "../layout/ScrollableRow.js";
10
10
  import NavigateNextIcon from "@mui/icons-material/NavigateNext";
11
- import { piecePart, usePieceProps } from "../../theme/themePieces.js";
11
+ import { piecePart, withPieceSlots, usePieceProps } from "../../theme/themePieces.js";
12
12
  import { warnUnknownProps } from "../../toolkit/unknownProps.js";
13
13
  const THEME_NAME = "CalcitaPageBreadcrumbs";
14
14
  const BreadcrumbsRoot = piecePart(THEME_NAME, "Root", Breadcrumbs);
15
15
  const BreadcrumbsLink = piecePart(THEME_NAME, "Link", Link);
16
16
  const BreadcrumbsCurrent = piecePart(THEME_NAME, "Current", Typography);
17
- function PageBreadcrumbs(props) {
17
+ function PageBreadcrumbsContent(props) {
18
18
  const { items, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
19
19
  warnUnknownProps(THEME_NAME, unknownProps);
20
20
  const { t } = useToolkitTranslation("layout", texts);
@@ -53,6 +53,7 @@ function PageBreadcrumbs(props) {
53
53
  }
54
54
  ) });
55
55
  }
56
+ const PageBreadcrumbs = withPieceSlots(PageBreadcrumbsContent, THEME_NAME);
56
57
  export {
57
58
  PageBreadcrumbs as default
58
59
  };
@@ -1 +1 @@
1
- {"version":3,"file":"PageBreadcrumbs.js","sources":["../../../src/ui/navigation/PageBreadcrumbs.jsx"],"sourcesContent":["// Las migas de una pantalla (Inicio › Catálogos › Productos): cada una lleva a su pantalla menos la última, que es donde\n// ya está la persona. En el teléfono no se parten en dos renglones: se deslizan de lado en la misma tira que los filtros\n// rápidos (ScrollableRow: sin barra de desplazamiento, la orilla donde hay más se desvanece y se arrastra con el mouse).\n// Antes deslizaba la lista de MUI y salía la barra gris del navegador debajo de las migas.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPageBreadcrumbs con defaultProps y\n// styleOverrides de sus partes: root, link, current.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Breadcrumbs from '@mui/material/Breadcrumbs';\nimport Link from '@mui/material/Link';\nimport Typography from '@mui/material/Typography';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport NavigateNextIcon from '@mui/icons-material/NavigateNext';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPageBreadcrumbs)\nconst THEME_NAME = 'CalcitaPageBreadcrumbs';\nconst BreadcrumbsRoot = piecePart(THEME_NAME, 'Root', Breadcrumbs);\nconst BreadcrumbsLink = piecePart(THEME_NAME, 'Link', Link);\nconst BreadcrumbsCurrent = piecePart(THEME_NAME, 'Current', Typography);\n\n/**\n * @param {object} props\n * @param {Array<{label: string, to?: string}>} props.items textos ya traducidos\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function PageBreadcrumbs(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPageBreadcrumbs.defaultProps) y encima lo que llega aquí\n const {items, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('layout', texts);\n const {Link: RouterLink} = useToolkitNavigation();\n if (items.length === 0) {\n return null;\n }\n return (\n <ScrollableRow py={0}>\n <BreadcrumbsRoot aria-label={t('breadcrumbs')} separator={<NavigateNextIcon fontSize=\"small\" />}\n ownSx={{...textSizeOf(13), color: 'text.secondary', flex: '0 0 auto', '& .MuiBreadcrumbs-ol': {flexWrap: 'nowrap'},\n '& .MuiBreadcrumbs-li': {flex: '0 0 auto'}}}>\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n if (!isLast && item.to) {\n return (\n <BreadcrumbsLink key={`${item.to}-${index}`} component={RouterLink} to={item.to} underline=\"hover\" color=\"inherit\" ownSx={{fontSize: 'inherit'}}>\n {item.label}\n </BreadcrumbsLink>\n );\n }\n return (\n <BreadcrumbsCurrent key={`${item.label}-${index}`} component=\"span\" aria-current={isLast ? 'page' : undefined}\n ownSx={{fontSize: 'inherit', fontWeight: isLast ? 500 : 400, color: isLast ? 'text.primary' : 'text.secondary'}}>\n {item.label}\n </BreadcrumbsCurrent>\n );\n })}\n </BreadcrumbsRoot>\n </ScrollableRow>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,QAAQ,WAAW;AACjE,MAAM,kBAAkB,UAAU,YAAY,QAAQ,IAAI;AAC1D,MAAM,qBAAqB,UAAU,YAAY,WAAW,UAAU;AAOtE,SAAwB,gBAAgB,OAAO;AAE3C,QAAM,EAAC,OAAO,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACvE,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AACA,SACI,oBAAC,eAAA,EAAc,IAAI,GACf,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MAAgB,cAAY,EAAE,aAAa;AAAA,MAAG,WAAW,oBAAC,kBAAA,EAAiB,UAAS,QAAA,CAAQ;AAAA,MAChF,OAAO;AAAA,QAAC,GAAG,WAAW,EAAE;AAAA,QAAG,OAAO;AAAA,QAAkB,MAAM;AAAA,QAAY,wBAAwB,EAAC,UAAU,SAAA;AAAA,QACpG,wBAAwB,EAAC,MAAM,WAAA;AAAA,MAAU;AAAA,MACtD,UAAA,MAAM,IAAI,CAAC,MAAM,UAAU;AACxB,cAAM,SAAS,UAAU,MAAM,SAAS;AACxC,YAAI,CAAC,UAAU,KAAK,IAAI;AACpB,iBACI,oBAAC,mBAA4C,WAAW,YAAY,IAAI,KAAK,IAAI,WAAU,SAAQ,OAAM,WAAU,OAAO,EAAC,UAAU,UAAA,GAChI,UAAA,KAAK,MAAA,GADY,GAAG,KAAK,EAAE,IAAI,KAAK,EAEzC;AAAA,QAER;AACA,eACI;AAAA,UAAC;AAAA,UAAA;AAAA,YAAkD,WAAU;AAAA,YAAO,gBAAc,SAAS,SAAS;AAAA,YACxF,OAAO,EAAC,UAAU,WAAW,YAAY,SAAS,MAAM,KAAK,OAAO,SAAS,iBAAiB,iBAAA;AAAA,YACrG,UAAA,KAAK;AAAA,UAAA;AAAA,UAFe,GAAG,KAAK,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,MAKvD,CAAC;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
1
+ {"version":3,"file":"PageBreadcrumbs.js","sources":["../../../src/ui/navigation/PageBreadcrumbs.jsx"],"sourcesContent":["// Las migas de una pantalla (Inicio › Catálogos › Productos): cada una lleva a su pantalla menos la última, que es donde\n// ya está la persona. En el teléfono no se parten en dos renglones: se deslizan de lado en la misma tira que los filtros\n// rápidos (ScrollableRow: sin barra de desplazamiento, la orilla donde hay más se desvanece y se arrastra con el mouse).\n// Antes deslizaba la lista de MUI y salía la barra gris del navegador debajo de las migas.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPageBreadcrumbs con defaultProps y\n// styleOverrides de sus partes: root, link, current.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Breadcrumbs from '@mui/material/Breadcrumbs';\nimport Link from '@mui/material/Link';\nimport Typography from '@mui/material/Typography';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport NavigateNextIcon from '@mui/icons-material/NavigateNext';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPageBreadcrumbs)\nconst THEME_NAME = 'CalcitaPageBreadcrumbs';\nconst BreadcrumbsRoot = piecePart(THEME_NAME, 'Root', Breadcrumbs);\nconst BreadcrumbsLink = piecePart(THEME_NAME, 'Link', Link);\nconst BreadcrumbsCurrent = piecePart(THEME_NAME, 'Current', Typography);\n\n/**\n * @param {object} props\n * @param {Array<{label: string, to?: string}>} props.items textos ya traducidos\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nfunction PageBreadcrumbsContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPageBreadcrumbs.defaultProps) y encima lo que llega aquí\n const {items, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('layout', texts);\n const {Link: RouterLink} = useToolkitNavigation();\n if (items.length === 0) {\n return null;\n }\n return (\n <ScrollableRow py={0}>\n <BreadcrumbsRoot aria-label={t('breadcrumbs')} separator={<NavigateNextIcon fontSize=\"small\" />}\n ownSx={{...textSizeOf(13), color: 'text.secondary', flex: '0 0 auto', '& .MuiBreadcrumbs-ol': {flexWrap: 'nowrap'},\n '& .MuiBreadcrumbs-li': {flex: '0 0 auto'}}}>\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n if (!isLast && item.to) {\n return (\n <BreadcrumbsLink key={`${item.to}-${index}`} component={RouterLink} to={item.to} underline=\"hover\" color=\"inherit\" ownSx={{fontSize: 'inherit'}}>\n {item.label}\n </BreadcrumbsLink>\n );\n }\n return (\n <BreadcrumbsCurrent key={`${item.label}-${index}`} component=\"span\" aria-current={isLast ? 'page' : undefined}\n ownSx={{fontSize: 'inherit', fontWeight: isLast ? 500 : 400, color: isLast ? 'text.primary' : 'text.secondary'}}>\n {item.label}\n </BreadcrumbsCurrent>\n );\n })}\n </BreadcrumbsRoot>\n </ScrollableRow>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst PageBreadcrumbs = withPieceSlots(PageBreadcrumbsContent, THEME_NAME);\nexport default PageBreadcrumbs;\n"],"names":[],"mappings":";;;;;;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,QAAQ,WAAW;AACjE,MAAM,kBAAkB,UAAU,YAAY,QAAQ,IAAI;AAC1D,MAAM,qBAAqB,UAAU,YAAY,WAAW,UAAU;AAOtE,SAAS,uBAAuB,OAAO;AAEnC,QAAM,EAAC,OAAO,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACvE,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AACA,SACI,oBAAC,eAAA,EAAc,IAAI,GACf,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MAAgB,cAAY,EAAE,aAAa;AAAA,MAAG,WAAW,oBAAC,kBAAA,EAAiB,UAAS,QAAA,CAAQ;AAAA,MAChF,OAAO;AAAA,QAAC,GAAG,WAAW,EAAE;AAAA,QAAG,OAAO;AAAA,QAAkB,MAAM;AAAA,QAAY,wBAAwB,EAAC,UAAU,SAAA;AAAA,QACpG,wBAAwB,EAAC,MAAM,WAAA;AAAA,MAAU;AAAA,MACtD,UAAA,MAAM,IAAI,CAAC,MAAM,UAAU;AACxB,cAAM,SAAS,UAAU,MAAM,SAAS;AACxC,YAAI,CAAC,UAAU,KAAK,IAAI;AACpB,iBACI,oBAAC,mBAA4C,WAAW,YAAY,IAAI,KAAK,IAAI,WAAU,SAAQ,OAAM,WAAU,OAAO,EAAC,UAAU,UAAA,GAChI,UAAA,KAAK,MAAA,GADY,GAAG,KAAK,EAAE,IAAI,KAAK,EAEzC;AAAA,QAER;AACA,eACI;AAAA,UAAC;AAAA,UAAA;AAAA,YAAkD,WAAU;AAAA,YAAO,gBAAc,SAAS,SAAS;AAAA,YACxF,OAAO,EAAC,UAAU,WAAW,YAAY,SAAS,MAAM,KAAK,OAAO,SAAS,iBAAiB,iBAAA;AAAA,YACrG,UAAA,KAAK;AAAA,UAAA;AAAA,UAFe,GAAG,KAAK,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,MAKvD,CAAC;AAAA,IAAA;AAAA,EAAA,GAET;AAER;AAGA,MAAM,kBAAkB,eAAe,wBAAwB,UAAU;"}
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Chip from "@mui/material/Chip";
4
+ import Stack from "@mui/material/Stack";
5
+ import Tab from "@mui/material/Tab";
6
+ import Tabs from "@mui/material/Tabs";
7
+ import { useAccessChecker } from "../../access/useCan.js";
8
+ import { piecePart, withPieceSlots, usePieceProps } from "../../theme/themePieces.js";
9
+ import { useToolkitNavigation } from "../../toolkit/toolkitContext.js";
10
+ import { warnUnknownProps } from "../../toolkit/unknownProps.js";
11
+ import { queryTabOf, activeRouteTabOf, searchWithTab } from "./routeTabMatching.js";
12
+ const ROUTE_TABS_MODE = Object.freeze({ path: "path", query: "query" });
13
+ const DEFAULT_PARAM_NAME = "tab";
14
+ const THEME_NAME = "CalcitaRouteTabs";
15
+ const RouteTabsRoot = piecePart(THEME_NAME, "Root", Tabs);
16
+ const RouteTab = piecePart(THEME_NAME, "Tab", Tab);
17
+ const RouteTabBadge = piecePart(THEME_NAME, "Badge", Chip);
18
+ const visibleTabsOf = (tabs, canAccess) => tabs.filter((tab) => !tab.hidden && canAccess(tab.access));
19
+ function useActiveRouteTab(tabs, { mode = ROUTE_TABS_MODE.path, paramName = DEFAULT_PARAM_NAME } = {}) {
20
+ const { useLocation } = useToolkitNavigation();
21
+ const location = useLocation();
22
+ const visibleTabs = visibleTabsOf(tabs, useAccessChecker());
23
+ return mode === ROUTE_TABS_MODE.query ? queryTabOf(visibleTabs, location.search, paramName) : activeRouteTabOf(visibleTabs, location.pathname);
24
+ }
25
+ function RouteTabsContent(props) {
26
+ const { tabs, ariaLabel, mode = ROUTE_TABS_MODE.path, paramName = DEFAULT_PARAM_NAME, replace, onChange, sx, ...unknownProps } = usePieceProps(props, THEME_NAME);
27
+ warnUnknownProps(THEME_NAME, unknownProps);
28
+ const { useLocation, Link } = useToolkitNavigation();
29
+ const location = useLocation();
30
+ const visibleTabs = visibleTabsOf(tabs, useAccessChecker());
31
+ const isQuery = mode === ROUTE_TABS_MODE.query;
32
+ const replacesHistory = replace ?? isQuery;
33
+ const activeKey = isQuery ? queryTabOf(visibleTabs, location.search, paramName) : activeRouteTabOf(visibleTabs, location.pathname);
34
+ return (
35
+ // value false: ninguna pestaña coincide (MUI avisa en la consola si el valor no es de ninguna)
36
+ /* @__PURE__ */ jsx(
37
+ RouteTabsRoot,
38
+ {
39
+ value: activeKey ?? false,
40
+ variant: "scrollable",
41
+ allowScrollButtonsMobile: true,
42
+ "aria-label": ariaLabel,
43
+ ownSx: { minWidth: 0 },
44
+ sx,
45
+ children: visibleTabs.map((tab) => {
46
+ const key = isQuery ? tab.value : tab.to;
47
+ const href = isQuery ? `${location.pathname}${searchWithTab(location.search, paramName, tab.value)}` : tab.to;
48
+ const label = tab.badge === void 0 || tab.badge === null ? tab.label : /* @__PURE__ */ jsxs(Stack, { component: "span", direction: "row", sx: { alignItems: "center", gap: 0.75 }, children: [
49
+ /* @__PURE__ */ jsx("span", { children: tab.label }),
50
+ /* @__PURE__ */ jsx(RouteTabBadge, { size: "small", label: tab.badge, ownSx: { height: 20, fontSize: 12, fontWeight: 600 } })
51
+ ] });
52
+ return /* @__PURE__ */ jsx(
53
+ RouteTab,
54
+ {
55
+ value: key,
56
+ label,
57
+ icon: tab.icon,
58
+ iconPosition: "start",
59
+ disabled: tab.disabled,
60
+ component: Link,
61
+ to: href,
62
+ replace: replacesHistory,
63
+ onClick: () => onChange?.(tab),
64
+ ownSx: { minHeight: 48 }
65
+ },
66
+ key
67
+ );
68
+ })
69
+ }
70
+ )
71
+ );
72
+ }
73
+ const RouteTabs = withPieceSlots(RouteTabsContent, THEME_NAME);
74
+ export {
75
+ ROUTE_TABS_MODE,
76
+ RouteTabs as default,
77
+ useActiveRouteTab
78
+ };
79
+ //# sourceMappingURL=RouteTabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteTabs.js","sources":["../../../src/ui/navigation/RouteTabs.jsx"],"sourcesContent":["// Pestañas por ruta: cada pestaña es una dirección, así se puede compartir la liga a una pestaña y abrirla en otra pestaña\n// del navegador. Dos modos (mode):\n// path cada pestaña es una página de las rutas del proyecto (to); la activa sale de la dirección. Son lugares: cada una\n// deja su paso en el historial y «atrás» regresa a la anterior\n// query pestañas dentro de una misma pantalla, guardadas en la consulta (?tab=pagos); useActiveRouteTab dice cuál pintar.\n// Son la misma ficha vista por otro lado: por omisión REEMPLAZAN la entrada del historial, así «atrás» (y el\n// BackButton) sacan de la ficha en vez de pasear por sus pestañas (trampa pagada: con ocho pestañas, «Volver a la\n// lista» daba un paso por cada pestaña abierta). `replace` lo cambia en cualquiera de los dos modos\n//\n// Cada pestaña puede traer su ícono, un conteo (badge), su permiso (access, con la regla de ToolkitProvider: la que no se\n// puede ver no sale) o estar apagada. Usa la navegación que conectó el proyecto (react-router, Next o la del navegador).\n// En el teléfono se deslizan de lado.\n//\n// <RouteTabs ariaLabel=\"Secciones del cliente\" tabs={[\n// {to: `/customers/${id}`, label: 'Resumen', exact: true},\n// {to: `/customers/${id}/payments`, label: 'Pagos', badge: 3},\n// {to: `/customers/${id}/files`, label: 'Documentos', access: {ability: 'files.view'}},\n// ]} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRouteTabs con defaultProps y\n// styleOverrides de sus partes: root, tab, badge.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Chip from '@mui/material/Chip';\nimport Stack from '@mui/material/Stack';\nimport Tab from '@mui/material/Tab';\nimport Tabs from '@mui/material/Tabs';\n\nimport {useAccessChecker} from '@/access/useCan.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {activeRouteTabOf, queryTabOf, searchWithTab} from '@/ui/navigation/routeTabMatching.js';\n\n/** Dónde vive la pestaña: en la ruta o en la consulta. */\nexport const ROUTE_TABS_MODE = Object.freeze({path: 'path', query: 'query'});\nconst DEFAULT_PARAM_NAME = 'tab';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaRouteTabs)\nconst THEME_NAME = 'CalcitaRouteTabs';\nconst RouteTabsRoot = piecePart(THEME_NAME, 'Root', Tabs);\nconst RouteTab = piecePart(THEME_NAME, 'Tab', Tab);\nconst RouteTabBadge = piecePart(THEME_NAME, 'Badge', Chip);\n\n/**\n * Las pestañas que esta sesión puede ver.\n *\n * @param {object[]} tabs\n * @param {(access: object|undefined) => boolean} canAccess\n * @returns {object[]}\n */\nconst visibleTabsOf = (tabs, canAccess) => tabs.filter((tab) => !tab.hidden && canAccess(tab.access));\n\n/**\n * La pestaña activa, para que la pantalla pinte lo suyo (sobre todo con mode query: la pantalla es una sola).\n *\n * @param {object[]} tabs las mismas de RouteTabs\n * @param {{mode?: string, paramName?: string}} [options]\n * @returns {string|null} el `to` (por ruta) o el `value` (por consulta) de la activa\n */\nexport function useActiveRouteTab(tabs, {mode = ROUTE_TABS_MODE.path, paramName = DEFAULT_PARAM_NAME} = {}) {\n const {useLocation} = useToolkitNavigation();\n const location = useLocation();\n const visibleTabs = visibleTabsOf(tabs, useAccessChecker());\n return mode === ROUTE_TABS_MODE.query\n ? queryTabOf(visibleTabs, location.search, paramName)\n : activeRouteTabOf(visibleTabs, location.pathname);\n}\n\n/**\n * @param {object} props\n * @param {Array<{to?: string, value?: string, label: import('react').ReactNode, icon?: import('react').ReactElement, badge?: import('react').ReactNode, access?: object, exact?: boolean, disabled?: boolean, hidden?: boolean}>} props.tabs\n * to (por ruta) o value (por consulta); label ya traducido; badge: un conteo o un texto corto junto a la etiqueta\n * @param {string} props.ariaLabel ya traducido: de qué son estas pestañas (para el lector de pantalla)\n * @param {'path'|'query'} [props.mode] ROUTE_TABS_MODE\n * @param {string} [props.paramName] con mode query: el nombre en la consulta (por omisión 'tab')\n * @param {boolean} [props.replace] si cambiar de pestaña reemplaza la entrada del historial en lugar de agregar una. Por\n * omisión sí con mode query (vistas de la misma ficha) y no con mode path (páginas distintas)\n * @param {(tab: object) => void} [props.onChange] avisa la pestaña que se eligió (la navegación ya la hace la liga)\n * @param {object} [props.sx]\n */\nfunction RouteTabsContent(props) {\n const {tabs, ariaLabel, mode = ROUTE_TABS_MODE.path, paramName = DEFAULT_PARAM_NAME, replace, onChange, sx, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {useLocation, Link} = useToolkitNavigation();\n const location = useLocation();\n const visibleTabs = visibleTabsOf(tabs, useAccessChecker());\n const isQuery = mode === ROUTE_TABS_MODE.query;\n const replacesHistory = replace ?? isQuery;\n const activeKey = isQuery ? queryTabOf(visibleTabs, location.search, paramName) : activeRouteTabOf(visibleTabs, location.pathname);\n\n return (\n // value false: ninguna pestaña coincide (MUI avisa en la consola si el valor no es de ninguna)\n <RouteTabsRoot value={activeKey ?? false} variant=\"scrollable\" allowScrollButtonsMobile aria-label={ariaLabel}\n ownSx={{minWidth: 0}} sx={sx}>\n {visibleTabs.map((tab) => {\n const key = isQuery ? tab.value : tab.to;\n const href = isQuery ? `${location.pathname}${searchWithTab(location.search, paramName, tab.value)}` : tab.to;\n const label = tab.badge === undefined || tab.badge === null ? tab.label : (\n <Stack component=\"span\" direction=\"row\" sx={{alignItems: 'center', gap: 0.75}}>\n <span>{tab.label}</span>\n <RouteTabBadge size=\"small\" label={tab.badge} ownSx={{height: 20, fontSize: 12, fontWeight: 600}} />\n </Stack>\n );\n return (\n <RouteTab key={key} value={key} label={label} icon={tab.icon} iconPosition=\"start\" disabled={tab.disabled}\n component={Link} to={href} replace={replacesHistory} onClick={() => onChange?.(tab)} ownSx={{minHeight: 48}} />\n );\n })}\n </RouteTabsRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst RouteTabs = withPieceSlots(RouteTabsContent, THEME_NAME);\nexport default RouteTabs;\n"],"names":[],"mappings":";;;;;;;;;;;AAmCO,MAAM,kBAAkB,OAAO,OAAO,EAAC,MAAM,QAAQ,OAAO,SAAQ;AAC3E,MAAM,qBAAqB;AAG3B,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,QAAQ,IAAI;AACxD,MAAM,WAAW,UAAU,YAAY,OAAO,GAAG;AACjD,MAAM,gBAAgB,UAAU,YAAY,SAAS,IAAI;AASzD,MAAM,gBAAgB,CAAC,MAAM,cAAc,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI,UAAU,UAAU,IAAI,MAAM,CAAC;AAS7F,SAAS,kBAAkB,MAAM,EAAC,OAAO,gBAAgB,MAAM,YAAY,mBAAA,IAAsB,IAAI;AACxG,QAAM,EAAC,YAAA,IAAe,qBAAA;AACtB,QAAM,WAAW,YAAA;AACjB,QAAM,cAAc,cAAc,MAAM,iBAAA,CAAkB;AAC1D,SAAO,SAAS,gBAAgB,QAC1B,WAAW,aAAa,SAAS,QAAQ,SAAS,IAClD,iBAAiB,aAAa,SAAS,QAAQ;AACzD;AAcA,SAAS,iBAAiB,OAAO;AAC7B,QAAM,EAAC,MAAM,WAAW,OAAO,gBAAgB,MAAM,YAAY,oBAAoB,SAAS,UAAU,IAAI,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AAC9J,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,aAAa,KAAA,IAAQ,qBAAA;AAC5B,QAAM,WAAW,YAAA;AACjB,QAAM,cAAc,cAAc,MAAM,iBAAA,CAAkB;AAC1D,QAAM,UAAU,SAAS,gBAAgB;AACzC,QAAM,kBAAkB,WAAW;AACnC,QAAM,YAAY,UAAU,WAAW,aAAa,SAAS,QAAQ,SAAS,IAAI,iBAAiB,aAAa,SAAS,QAAQ;AAEjI;AAAA;AAAA,IAEI;AAAA,MAAC;AAAA,MAAA;AAAA,QAAc,OAAO,aAAa;AAAA,QAAO,SAAQ;AAAA,QAAa,0BAAwB;AAAA,QAAC,cAAY;AAAA,QACrF,OAAO,EAAC,UAAU,EAAA;AAAA,QAAI;AAAA,QAChC,UAAA,YAAY,IAAI,CAAC,QAAQ;AACtB,gBAAM,MAAM,UAAU,IAAI,QAAQ,IAAI;AACtC,gBAAM,OAAO,UAAU,GAAG,SAAS,QAAQ,GAAG,cAAc,SAAS,QAAQ,WAAW,IAAI,KAAK,CAAC,KAAK,IAAI;AAC3G,gBAAM,QAAQ,IAAI,UAAU,UAAa,IAAI,UAAU,OAAO,IAAI,QAC9D,qBAAC,SAAM,WAAU,QAAO,WAAU,OAAM,IAAI,EAAC,YAAY,UAAU,KAAK,KAAA,GACpE,UAAA;AAAA,YAAA,oBAAC,QAAA,EAAM,cAAI,MAAA,CAAM;AAAA,YACjB,oBAAC,eAAA,EAAc,MAAK,SAAQ,OAAO,IAAI,OAAO,OAAO,EAAC,QAAQ,IAAI,UAAU,IAAI,YAAY,MAAG,CAAG;AAAA,UAAA,GACtG;AAEJ,iBACI;AAAA,YAAC;AAAA,YAAA;AAAA,cAAmB,OAAO;AAAA,cAAK;AAAA,cAAc,MAAM,IAAI;AAAA,cAAM,cAAa;AAAA,cAAQ,UAAU,IAAI;AAAA,cACvF,WAAW;AAAA,cAAM,IAAI;AAAA,cAAM,SAAS;AAAA,cAAiB,SAAS,MAAM,WAAW,GAAG;AAAA,cAAG,OAAO,EAAC,WAAW,GAAA;AAAA,YAAE;AAAA,YADrG;AAAA,UAAA;AAAA,QAGvB,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA;AAGb;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
@@ -0,0 +1,23 @@
1
+ function activeRouteTabOf(tabs, pathname) {
2
+ const path = pathname.replace(/\/+$/, "") || "/";
3
+ const matching = tabs.filter(({ to, exact }) => {
4
+ const tabPath = to.replace(/\/+$/, "") || "/";
5
+ return path === tabPath || !exact && path.startsWith(`${tabPath === "/" ? "" : tabPath}/`);
6
+ });
7
+ return matching.sort((first, second) => second.to.length - first.to.length)[0]?.to ?? null;
8
+ }
9
+ function queryTabOf(tabs, search, paramName) {
10
+ const wanted = new URLSearchParams(search).get(paramName);
11
+ return tabs.find((tab) => tab.value === wanted)?.value ?? tabs[0]?.value ?? null;
12
+ }
13
+ function searchWithTab(search, paramName, value) {
14
+ const params = new URLSearchParams(search);
15
+ params.set(paramName, value);
16
+ return `?${params.toString()}`;
17
+ }
18
+ export {
19
+ activeRouteTabOf,
20
+ queryTabOf,
21
+ searchWithTab
22
+ };
23
+ //# sourceMappingURL=routeTabMatching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeTabMatching.js","sources":["../../../src/ui/navigation/routeTabMatching.js"],"sourcesContent":["// Qué pestaña va activa según la dirección, para las pestañas por ruta (RouteTabs):\n// por ruta cada pestaña es una página: gana la que coincide con la ruta, o la más larga de la que es parte\n// ('/clientes/123/pagos/abril' activa '/clientes/123/pagos'); exact solo con la ruta idéntica\n// por consulta pestañas dentro de una misma pantalla, en ?tab=pagos: se puede compartir la liga a la pestaña\n//\n// Puro (sin React): se prueba con node (routeTabMatching.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/**\n * @param {Array<{to: string, exact?: boolean}>} tabs\n * @param {string} pathname\n * @returns {string|null} el `to` de la pestaña activa\n */\nexport function activeRouteTabOf(tabs, pathname) {\n const path = pathname.replace(/\\/+$/, '') || '/';\n const matching = tabs.filter(({to, exact}) => {\n const tabPath = to.replace(/\\/+$/, '') || '/';\n return path === tabPath || (!exact && path.startsWith(`${tabPath === '/' ? '' : tabPath}/`));\n });\n // la más específica: la de la ruta más larga\n return matching.sort((first, second) => second.to.length - first.to.length)[0]?.to ?? null;\n}\n\n/**\n * @param {Array<{value: string}>} tabs\n * @param {string} search la consulta de la dirección ('?tab=pagos&page=2')\n * @param {string} paramName 'tab'\n * @returns {string|null} la pestaña de la consulta si existe; si no, la primera\n */\nexport function queryTabOf(tabs, search, paramName) {\n const wanted = new URLSearchParams(search).get(paramName);\n return tabs.find((tab) => tab.value === wanted)?.value ?? tabs[0]?.value ?? null;\n}\n\n/**\n * La consulta con otra pestaña, sin perder lo demás que traía (?page=2 se queda).\n *\n * @param {string} search\n * @param {string} paramName\n * @param {string} value\n * @returns {string} '?tab=pagos&page=2'\n */\nexport function searchWithTab(search, paramName, value) {\n const params = new URLSearchParams(search);\n params.set(paramName, value);\n return `?${params.toString()}`;\n}\n"],"names":[],"mappings":"AAcO,SAAS,iBAAiB,MAAM,UAAU;AAC7C,QAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE,KAAK;AAC7C,QAAM,WAAW,KAAK,OAAO,CAAC,EAAC,IAAI,MAAK,MAAM;AAC1C,UAAM,UAAU,GAAG,QAAQ,QAAQ,EAAE,KAAK;AAC1C,WAAO,SAAS,WAAY,CAAC,SAAS,KAAK,WAAW,GAAG,YAAY,MAAM,KAAK,OAAO,GAAG;AAAA,EAC9F,CAAC;AAED,SAAO,SAAS,KAAK,CAAC,OAAO,WAAW,OAAO,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,MAAM;AAC1F;AAQO,SAAS,WAAW,MAAM,QAAQ,WAAW;AAChD,QAAM,SAAS,IAAI,gBAAgB,MAAM,EAAE,IAAI,SAAS;AACxD,SAAO,KAAK,KAAK,CAAC,QAAQ,IAAI,UAAU,MAAM,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS;AAChF;AAUO,SAAS,cAAc,QAAQ,WAAW,OAAO;AACpD,QAAM,SAAS,IAAI,gBAAgB,MAAM;AACzC,SAAO,IAAI,WAAW,KAAK;AAC3B,SAAO,IAAI,OAAO,SAAQ,CAAE;AAChC;"}
@@ -19,7 +19,7 @@ import { useTheme } from "@mui/material/styles";
19
19
  import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
20
20
  import { paletteOf } from "../../theme/components/paletteOf.js";
21
21
  import { TIMELINE_EMPHASIS, timelineDotOf } from "./timelineDot.js";
22
- import { piecePart, usePieceProps } from "../../theme/themePieces.js";
22
+ import { piecePart, withPieceSlots, usePieceProps } from "../../theme/themePieces.js";
23
23
  import { warnUnknownProps } from "../../toolkit/unknownProps.js";
24
24
  const THEME_NAME = "CalcitaGroupedTimeline";
25
25
  const TimelineGroup = piecePart(THEME_NAME, "Group", Box);
@@ -35,7 +35,7 @@ const TimelineEntryTitle = piecePart(THEME_NAME, "EntryTitle", Typography);
35
35
  const HIGHLIGHT_TINT_PERCENT = 8;
36
36
  const LEFT_ALIGNED_TIMELINE = { m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: { flex: 0, p: 0 } };
37
37
  const ALTERNATE_TIMELINE = { m: 0, px: 0, py: 0.5 };
38
- function GroupedTimeline(props) {
38
+ function GroupedTimelineContent(props) {
39
39
  const { groups, onGroupToggle, ...unknownProps } = usePieceProps(props, THEME_NAME);
40
40
  warnUnknownProps(THEME_NAME, unknownProps);
41
41
  const theme = useTheme();
@@ -115,6 +115,7 @@ function GroupedTimeline(props) {
115
115
  );
116
116
  }) });
117
117
  }
118
+ const GroupedTimeline = withPieceSlots(GroupedTimelineContent, THEME_NAME);
118
119
  export {
119
120
  GroupedTimeline as default
120
121
  };
@@ -1 +1 @@
1
- {"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaGroupedTimeline con defaultProps y\n// styleOverrides de sus partes: group, groupHeader, groupButton, groupTitle, expandIcon, timeline, opposite, connector,\n// content, entryTitle.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaGroupedTimeline)\nconst THEME_NAME = 'CalcitaGroupedTimeline';\nconst TimelineGroup = piecePart(THEME_NAME, 'Group', Box);\nconst TimelineGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst TimelineGroupButton = piecePart(THEME_NAME, 'GroupButton', ButtonBase);\nconst TimelineGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst TimelineExpandIcon = piecePart(THEME_NAME, 'ExpandIcon', ExpandMoreIcon);\nconst TimelineTimeline = piecePart(THEME_NAME, 'Timeline', Timeline);\nconst TimelineOpposite = piecePart(THEME_NAME, 'Opposite', TimelineOppositeContent);\nconst TimelineConnectorPart = piecePart(THEME_NAME, 'Connector', TimelineConnector);\nconst TimelineContentPart = piecePart(THEME_NAME, 'Content', TimelineContent);\nconst TimelineEntryTitle = piecePart(THEME_NAME, 'EntryTitle', Typography);\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nexport default function GroupedTimeline(props) {\n // lo que el proyecto puso en su tema (components.CalcitaGroupedTimeline.defaultProps) y encima lo que llega aquí\n const {groups, onGroupToggle, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <TimelineGroup key={timelineGroup.key} ownSx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <TimelineGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <TimelineGroupButton disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n ownSx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <TimelineGroupTitle variant=\"h6\" component=\"h3\" ownSx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </TimelineGroupTitle>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <TimelineExpandIcon fontSize=\"small\" ownSx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </TimelineGroupButton>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </TimelineGroupHeader>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <TimelineTimeline position={isWide ? 'alternate' : 'right'} ownSx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOpposite variant=\"body2\" color=\"text.secondary\" ownSx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOpposite>\n )}\n <TimelineSeparator>\n <TimelineConnectorPart ownSx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnectorPart ownSx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContentPart ownSx={{py: 1.5, px: 2, minWidth: 0}}>\n <TimelineEntryTitle variant=\"body2\" component=\"p\" ownSx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </TimelineEntryTitle>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContentPart>\n </TimelineItem>\n );\n })}\n </TimelineTimeline>\n </Collapse>\n </TimelineGroup>\n );\n })}\n </Stack>\n );\n}\n"],"names":["theme"],"mappings":"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"}
1
+ {"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaGroupedTimeline con defaultProps y\n// styleOverrides de sus partes: group, groupHeader, groupButton, groupTitle, expandIcon, timeline, opposite, connector,\n// content, entryTitle.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaGroupedTimeline)\nconst THEME_NAME = 'CalcitaGroupedTimeline';\nconst TimelineGroup = piecePart(THEME_NAME, 'Group', Box);\nconst TimelineGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst TimelineGroupButton = piecePart(THEME_NAME, 'GroupButton', ButtonBase);\nconst TimelineGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst TimelineExpandIcon = piecePart(THEME_NAME, 'ExpandIcon', ExpandMoreIcon);\nconst TimelineTimeline = piecePart(THEME_NAME, 'Timeline', Timeline);\nconst TimelineOpposite = piecePart(THEME_NAME, 'Opposite', TimelineOppositeContent);\nconst TimelineConnectorPart = piecePart(THEME_NAME, 'Connector', TimelineConnector);\nconst TimelineContentPart = piecePart(THEME_NAME, 'Content', TimelineContent);\nconst TimelineEntryTitle = piecePart(THEME_NAME, 'EntryTitle', Typography);\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nfunction GroupedTimelineContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaGroupedTimeline.defaultProps) y encima lo que llega aquí\n const {groups, onGroupToggle, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <TimelineGroup key={timelineGroup.key} ownSx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <TimelineGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <TimelineGroupButton disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n ownSx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <TimelineGroupTitle variant=\"h6\" component=\"h3\" ownSx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </TimelineGroupTitle>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <TimelineExpandIcon fontSize=\"small\" ownSx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </TimelineGroupButton>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </TimelineGroupHeader>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <TimelineTimeline position={isWide ? 'alternate' : 'right'} ownSx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOpposite variant=\"body2\" color=\"text.secondary\" ownSx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOpposite>\n )}\n <TimelineSeparator>\n <TimelineConnectorPart ownSx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnectorPart ownSx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContentPart ownSx={{py: 1.5, px: 2, minWidth: 0}}>\n <TimelineEntryTitle variant=\"body2\" component=\"p\" ownSx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </TimelineEntryTitle>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContentPart>\n </TimelineItem>\n );\n })}\n </TimelineTimeline>\n </Collapse>\n </TimelineGroup>\n );\n })}\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst GroupedTimeline = withPieceSlots(GroupedTimelineContent, THEME_NAME);\nexport default GroupedTimeline;\n"],"names":["theme"],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { useId, useImperativeHandle } from "react";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import Chip from "@mui/material/Chip";
7
+ import FormHelperText from "@mui/material/FormHelperText";
8
+ import Stack from "@mui/material/Stack";
9
+ import Typography from "@mui/material/Typography";
10
+ import AttachFileRoundedIcon from "@mui/icons-material/AttachFileRounded";
11
+ import { useValidationText } from "../forms/useValidationText.js";
12
+ import FieldFrame from "../inputs/FieldFrame.js";
13
+ import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
14
+ import { useFormatters } from "../toolkit/useFormatters.js";
15
+ import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
16
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
17
+ import FileChip from "./FileChip.js";
18
+ import TemplateDownloadButton from "./TemplateDownloadButton.js";
19
+ import { useFilePicker } from "./useFilePicker.js";
20
+ const THEME_NAME = "CalcitaAttachmentsInput";
21
+ const AttachmentsRoot = piecePart(THEME_NAME, "Root", Box);
22
+ const AttachmentsActions = piecePart(THEME_NAME, "Actions", Stack);
23
+ const AttachmentsButton = piecePart(THEME_NAME, "Button", Button);
24
+ const AttachmentsList = piecePart(THEME_NAME, "List", Stack);
25
+ const AttachmentsChip = piecePart(THEME_NAME, "FileChip", Chip);
26
+ const AttachmentsLimits = piecePart(THEME_NAME, "Limits", Typography);
27
+ function AttachmentsInputContent(props) {
28
+ const {
29
+ name,
30
+ label,
31
+ value = [],
32
+ onChange,
33
+ onBlur,
34
+ onRemove,
35
+ accept,
36
+ maxSize,
37
+ maxFiles,
38
+ maxTotalSize,
39
+ error,
40
+ helperText,
41
+ buttonLabel,
42
+ showButton = true,
43
+ pickerRef,
44
+ disabled = false,
45
+ busy = false,
46
+ onReject,
47
+ template,
48
+ actions,
49
+ sx,
50
+ messages,
51
+ texts,
52
+ ...unknownProps
53
+ } = usePieceProps(props, THEME_NAME);
54
+ warnUnknownProps(THEME_NAME, unknownProps);
55
+ const { t } = useToolkitTranslation("common", texts);
56
+ const validationTextOf = useValidationText({ name, label, messages });
57
+ const { formatFileSize } = useFormatters();
58
+ const generatedId = useId();
59
+ const buttonId = `${name ?? "attachments"}-${generatedId}`;
60
+ const errorText = validationTextOf(error);
61
+ const isLocked = disabled || busy;
62
+ const change = (files) => {
63
+ onChange(files);
64
+ onBlur?.();
65
+ };
66
+ const picker = useFilePicker({
67
+ accept,
68
+ maxSize,
69
+ multiple: true,
70
+ maxFiles,
71
+ maxTotalSize,
72
+ keptFiles: value,
73
+ disabled: isLocked,
74
+ onAccept: change,
75
+ onReject,
76
+ texts
77
+ });
78
+ useImperativeHandle(pickerRef, () => ({ open: picker.open }), [picker.open]);
79
+ const remove = (file) => {
80
+ onRemove?.(file);
81
+ change(value.filter((item) => item !== file));
82
+ };
83
+ const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);
84
+ const limitsText = maxFiles || picker.maxTotalBytes ? t(picker.maxTotalBytes ? "upload.summaryOf" : "upload.summary", {
85
+ count: value.length,
86
+ total: formatFileSize(totalBytes),
87
+ max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : void 0
88
+ }) : null;
89
+ const bottomText = errorText ?? helperText;
90
+ const content = /* @__PURE__ */ jsxs(
91
+ AttachmentsRoot,
92
+ {
93
+ "aria-busy": busy,
94
+ ...picker.dropHandlers,
95
+ ownSx: {
96
+ borderRadius: 1,
97
+ outline: picker.isDragging ? 2 : 0,
98
+ outlineStyle: "dashed",
99
+ outlineColor: "primary.main",
100
+ bgcolor: picker.isDragging ? "action.hover" : "transparent"
101
+ },
102
+ sx,
103
+ children: [
104
+ /* @__PURE__ */ jsx("input", { ...picker.inputProps, name, "aria-hidden": "true", tabIndex: -1 }),
105
+ (showButton || template || actions) && /* @__PURE__ */ jsxs(AttachmentsActions, { direction: "row", ownSx: { alignItems: "center", flexWrap: "wrap", gap: 1 }, children: [
106
+ showButton && /* @__PURE__ */ jsx(AttachmentsButton, { id: buttonId, size: "small", startIcon: /* @__PURE__ */ jsx(AttachFileRoundedIcon, {}), onClick: picker.open, disabled: isLocked, children: buttonLabel ?? t("upload.attach") }),
107
+ template && /* @__PURE__ */ jsx(TemplateDownloadButton, { size: "small", texts, ...template }),
108
+ actions
109
+ ] }),
110
+ value.length > 0 && /* @__PURE__ */ jsx(AttachmentsList, { direction: "row", role: "list", ownSx: { flexWrap: "wrap", gap: 1, mt: showButton ? 1 : 0 }, children: value.map((file) => /* @__PURE__ */ jsx(Box, { role: "listitem", sx: { maxWidth: "100%" }, children: /* @__PURE__ */ jsx(
111
+ FileChip,
112
+ {
113
+ file,
114
+ ChipComponent: AttachmentsChip,
115
+ removeLabel: t("upload.remove", { name: file.name }),
116
+ onRemove: isLocked ? void 0 : () => remove(file)
117
+ }
118
+ ) }, file.id ?? file.name)) }),
119
+ limitsText && /* @__PURE__ */ jsx(AttachmentsLimits, { variant: "caption", color: "text.secondary", ownSx: { display: "block", mt: 0.5 }, children: limitsText }),
120
+ bottomText && /* @__PURE__ */ jsx(FormHelperText, { error: Boolean(errorText), children: bottomText })
121
+ ]
122
+ }
123
+ );
124
+ return label ? /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, children: content }) : content;
125
+ }
126
+ const AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);
127
+ export {
128
+ AttachmentsInput as default
129
+ };
130
+ //# sourceMappingURL=AttachmentsInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttachmentsInput.js","sources":["../../src/upload/AttachmentsInput.jsx"],"sourcesContent":["// Varios archivos como en un correo: el botón «Adjuntar» con su clip y cada archivo en un chip con su × (Excel en verde,\n// PDF en rojo…). También se pueden soltar encima. Volver a elegir un archivo con el mismo nombre lo reemplaza (se corrigió en\n// disco) y no cuenta doble.\n//\n// Solo ELIGE (controlado: value / onChange recibe la lista completa), con las reglas de los demás cargadores (accept,\n// maxSize por archivo, maxFiles y maxTotalSize; useFilePicker). La lista puede mezclar archivos nuevos (File) con los que ya\n// están guardados ({id?, name, size?}): al editar un registro, los de antes se ven igual y también se quitan.\n//\n// Huecos: showButton={false} + pickerRef para abrir la ventana desde un botón suyo (el clip de la barra de un correo);\n// template («Descargar la plantilla»); actions (botones suyos junto a «Adjuntar»); onRemove (avisar al API que se quitó uno\n// que ya estaba guardado); label, buttonLabel y texts. Con formik: FormAttachmentsField; con react-hook-form, el del mismo\n// nombre.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAttachmentsInput con defaultProps y\n// styleOverrides de sus partes: root, actions, button, list, fileChip, limits.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useImperativeHandle} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAttachmentsInput)\nconst THEME_NAME = 'CalcitaAttachmentsInput';\nconst AttachmentsRoot = piecePart(THEME_NAME, 'Root', Box);\nconst AttachmentsActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst AttachmentsButton = piecePart(THEME_NAME, 'Button', Button);\nconst AttachmentsList = piecePart(THEME_NAME, 'List', Stack);\nconst AttachmentsChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst AttachmentsLimits = piecePart(THEME_NAME, 'Limits', Typography);\n\n/**\n * La lista conserva el tipo que usa la pantalla para lo ya guardado (T): onChange regresa esos mismos y los File nuevos.\n *\n * @template {File|{id?: string, name: string, size?: number}} T\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida; sin ella, va sin etiqueta (dentro de un correo, por ejemplo)\n * @param {T[]} [props.value] los nuevos y los que ya están guardados\n * @param {(files: Array<T|File>) => void} props.onChange la lista completa\n * @param {() => void} [props.onBlur] se llama al agregar o quitar (para que el formulario lo marque como tocado)\n * @param {(file: T) => void} [props.onRemove] al quitar uno (antes de onChange)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] el peso máximo de cada archivo: bytes o '10 MB'\n * @param {number} [props.maxFiles] cuántos como máximo\n * @param {number|string} [props.maxTotalSize] cuánto pueden pesar todos juntos\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Adjuntar»\n * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)\n * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Adjuntar»\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Adjuntar»\n * @param {object} [props.sx]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n * @param {Record<string, import('react').ElementType>} [props.slots] otro componente en lugar de una parte (root, actions, list…)\n * @param {Record<string, object>} [props.slotProps] props de más para una parte\n */\nfunction AttachmentsInputContent(props) {\n const {\n name, label, value = [], onChange, onBlur, onRemove, accept, maxSize, maxFiles, maxTotalSize, error, helperText, buttonLabel,\n showButton = true, pickerRef, disabled = false, busy = false, onReject, template, actions, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const {formatFileSize} = useFormatters();\n const generatedId = useId();\n const buttonId = `${name ?? 'attachments'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (files) => {\n onChange(files);\n onBlur?.();\n };\n const picker = useFilePicker({\n accept, maxSize, multiple: true, maxFiles, maxTotalSize, keptFiles: value, disabled: isLocked, onAccept: change, onReject, texts,\n });\n useImperativeHandle(pickerRef, () => ({open: picker.open}), [picker.open]);\n\n const remove = (file) => {\n onRemove?.(file);\n change(value.filter((item) => item !== file));\n };\n const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);\n // cuántos y cuánto pesan, solo si hay algún tope que cuidar\n const limitsText = maxFiles || picker.maxTotalBytes\n ? t(picker.maxTotalBytes ? 'upload.summaryOf' : 'upload.summary', {\n count: value.length, total: formatFileSize(totalBytes), max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : undefined,\n })\n : null;\n const bottomText = errorText ?? helperText;\n\n const content = (\n <AttachmentsRoot aria-busy={busy} {...picker.dropHandlers}\n ownSx={{\n borderRadius: 1, outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}>\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n {(showButton || template || actions) && (\n <AttachmentsActions direction=\"row\" ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 1}}>\n {showButton && (\n <AttachmentsButton id={buttonId} size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open} disabled={isLocked}>\n {buttonLabel ?? t('upload.attach')}\n </AttachmentsButton>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </AttachmentsActions>\n )}\n {value.length > 0 && (\n <AttachmentsList direction=\"row\" role=\"list\" ownSx={{flexWrap: 'wrap', gap: 1, mt: showButton ? 1 : 0}}>\n {value.map((file) => (\n <Box key={file.id ?? file.name} role=\"listitem\" sx={{maxWidth: '100%'}}>\n <FileChip file={file} ChipComponent={AttachmentsChip} removeLabel={t('upload.remove', {name: file.name})}\n onRemove={isLocked ? undefined : () => remove(file)} />\n </Box>\n ))}\n </AttachmentsList>\n )}\n {limitsText && (\n <AttachmentsLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 0.5}}>{limitsText}</AttachmentsLimits>\n )}\n {bottomText && <FormHelperText error={Boolean(errorText)}>{bottomText}</FormHelperText>}\n </AttachmentsRoot>\n );\n\n return label ? <FieldFrame inputId={buttonId} label={label}>{content}</FieldFrame> : content;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\n// OJO: con su tipo propio, el genérico (T: el tipo de cada archivo) que la envoltura borraría; slots y slotProps van en su\n// documentación\n/** @type {typeof AttachmentsInputContent} */\nconst AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);\nexport default AttachmentsInput;\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import Chip from "@mui/material/Chip";
4
+ import CancelIcon from "@mui/icons-material/Cancel";
5
+ import { sxListOf } from "../theme/themePieces.js";
6
+ import { useFormatters } from "../toolkit/useFormatters.js";
7
+ import { fileAppearanceOf } from "./fileAppearance.js";
8
+ function FileChip({ file, onRemove, removeLabel, ChipComponent = Chip, ownSx }) {
9
+ const { formatFileSize } = useFormatters();
10
+ const { Icon, color } = fileAppearanceOf(file.name);
11
+ const label = typeof file.size === "number" ? `${file.name} · ${formatFileSize(file.size)}` : file.name;
12
+ const chipSx = [{ maxWidth: "100%", height: "auto", py: 0.5, "& .MuiChip-label": { whiteSpace: "normal", overflowWrap: "anywhere" } }, ...sxListOf(ownSx)];
13
+ return /* @__PURE__ */ jsx(
14
+ ChipComponent,
15
+ {
16
+ icon: /* @__PURE__ */ jsx(Icon, { fontSize: "small", color }),
17
+ label,
18
+ variant: "outlined",
19
+ onDelete: onRemove,
20
+ deleteIcon: /* @__PURE__ */ jsx(CancelIcon, { "aria-label": removeLabel }),
21
+ ...ChipComponent === Chip ? { sx: chipSx } : { ownSx: chipSx }
22
+ }
23
+ );
24
+ }
25
+ export {
26
+ FileChip as default
27
+ };
28
+ //# sourceMappingURL=FileChip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileChip.js","sources":["../../src/upload/FileChip.jsx"],"sourcesContent":["// Un archivo elegido como chip: su ícono según el tipo, su nombre y su peso, y la × para quitarlo. Lo comparten los\n// cargadores del paquete; cada uno le pasa su parte del tema (ChipComponent) para que el proyecto la cambie desde ahí.\n//\n// Sirve igual para un archivo nuevo (File) y para uno que ya está guardado en el servidor ({name, size?}): sin peso, solo\n// se ve el nombre. Un nombre largo se parte en renglones en lugar de cortarse: es lo que la persona revisa antes de subir.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Chip from '@mui/material/Chip';\nimport CancelIcon from '@mui/icons-material/Cancel';\n\nimport {sxListOf} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {fileAppearanceOf} from '@/upload/fileAppearance.js';\n\n/**\n * @param {object} props\n * @param {{name: string, size?: number}} props.file\n * @param {(() => void)|undefined} [props.onRemove] sin él no hay × (con el candado puesto, por ejemplo)\n * @param {string} props.removeLabel ya traducido: «Quitar contrato.pdf»\n * @param {import('react').ElementType} [props.ChipComponent] la parte del tema del cargador (por omisión, el Chip de MUI)\n * @param {object} [props.ownSx] el estilo de la casa, debajo del tema del proyecto\n */\nexport default function FileChip({file, onRemove, removeLabel, ChipComponent = Chip, ownSx}) {\n const {formatFileSize} = useFormatters();\n const {Icon, color} = fileAppearanceOf(file.name);\n const label = typeof file.size === 'number' ? `${file.name} · ${formatFileSize(file.size)}` : file.name;\n // en lista: lo del cargador va DESPUÉS y se combina regla por regla (dentro del mismo objeto reemplazaría la llave entera)\n const chipSx = [{maxWidth: '100%', height: 'auto', py: 0.5, '& .MuiChip-label': {whiteSpace: 'normal', overflowWrap: 'anywhere'}}, ...sxListOf(ownSx)];\n\n return (\n <ChipComponent\n icon={<Icon fontSize=\"small\" color={color} />}\n label={label}\n variant=\"outlined\"\n onDelete={onRemove}\n deleteIcon={<CancelIcon aria-label={removeLabel} />}\n {...(ChipComponent === Chip ? {sx: chipSx} : {ownSx: chipSx})}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;AAsBA,SAAwB,SAAS,EAAC,MAAM,UAAU,aAAa,gBAAgB,MAAM,SAAQ;AACzF,QAAM,EAAC,eAAA,IAAkB,cAAA;AACzB,QAAM,EAAC,MAAM,MAAA,IAAS,iBAAiB,KAAK,IAAI;AAChD,QAAM,QAAQ,OAAO,KAAK,SAAS,WAAW,GAAG,KAAK,IAAI,MAAM,eAAe,KAAK,IAAI,CAAC,KAAK,KAAK;AAEnG,QAAM,SAAS,CAAC,EAAC,UAAU,QAAQ,QAAQ,QAAQ,IAAI,KAAK,oBAAoB,EAAC,YAAY,UAAU,cAAc,WAAA,EAAU,GAAI,GAAG,SAAS,KAAK,CAAC;AAErJ,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAAM,oBAAC,MAAA,EAAK,UAAS,SAAQ,OAAc;AAAA,MAC3C;AAAA,MACA,SAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY,oBAAC,YAAA,EAAW,cAAY,YAAA,CAAa;AAAA,MAChD,GAAI,kBAAkB,OAAO,EAAC,IAAI,OAAA,IAAU,EAAC,OAAO,OAAA;AAAA,IAAM;AAAA,EAAA;AAGvE;"}