@stackcraftmx/calcita 0.32.1 → 0.47.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 (338) hide show
  1. package/README.md +263 -2
  2. package/cli/packageVersions.js +12 -3
  3. package/cli/projectFiles.js +5 -2
  4. package/cli/templates/module/__Name__Page.test.jsx.tpl +24 -0
  5. package/cli/templates/project/README.md.tpl +14 -1
  6. package/cli/templates/project/src/app/appRouter.jsx.tpl +5 -2
  7. package/cli/templates/project/src/core/accountConnector.js.tpl +4 -2
  8. package/cli/templates/project/src/modules/auth/SignInPage.test.jsx.tpl +20 -0
  9. package/cli/templates/project/src/modules/home/HomePage.test.jsx.tpl +16 -0
  10. package/cli/templates/project/src/testing/browserGaps.js.tpl +70 -0
  11. package/cli/templates/project/src/testing/openScreen.jsx.tpl +58 -0
  12. package/cli/templates/project/src/testing/setup.js.tpl +25 -0
  13. package/cli/templates/project/vite.config.js.tpl +12 -0
  14. package/dist/DataTable/GoToPage.js +69 -13
  15. package/dist/DataTable/GoToPage.js.map +1 -1
  16. package/dist/DataTable/ServerTable.js +2 -0
  17. package/dist/DataTable/ServerTable.js.map +1 -1
  18. package/dist/DataTable/ServerTableFilterDrawer.js +52 -0
  19. package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -0
  20. package/dist/DataTable/filterContext.js.map +1 -1
  21. package/dist/DataTable/goToPageOptions.js +17 -0
  22. package/dist/DataTable/goToPageOptions.js.map +1 -0
  23. package/dist/date-pickers.js +28 -2
  24. package/dist/date-pickers.js.map +1 -1
  25. package/dist/datePickers/DateInput.js +15 -34
  26. package/dist/datePickers/DateInput.js.map +1 -1
  27. package/dist/datePickers/DateTimeInput.js +75 -0
  28. package/dist/datePickers/DateTimeInput.js.map +1 -0
  29. package/dist/datePickers/FormDateTimeField.js +12 -0
  30. package/dist/datePickers/FormDateTimeField.js.map +1 -0
  31. package/dist/datePickers/FormMonthField.js +12 -0
  32. package/dist/datePickers/FormMonthField.js.map +1 -0
  33. package/dist/datePickers/FormTimeField.js +12 -0
  34. package/dist/datePickers/FormTimeField.js.map +1 -0
  35. package/dist/datePickers/MonthInput.js +58 -0
  36. package/dist/datePickers/MonthInput.js.map +1 -0
  37. package/dist/datePickers/PickerFrame.js +27 -0
  38. package/dist/datePickers/PickerFrame.js.map +1 -0
  39. package/dist/datePickers/TimeInput.js +55 -0
  40. package/dist/datePickers/TimeInput.js.map +1 -0
  41. package/dist/datePickers/dateTimeRules.js +24 -0
  42. package/dist/datePickers/dateTimeRules.js.map +1 -0
  43. package/dist/datePickers/pickerValues.js +106 -0
  44. package/dist/datePickers/pickerValues.js.map +1 -0
  45. package/dist/datePickers/useCurrentMinute.js +28 -0
  46. package/dist/datePickers/useCurrentMinute.js.map +1 -0
  47. package/dist/datePickers/usePickerField.js +22 -0
  48. package/dist/datePickers/usePickerField.js.map +1 -0
  49. package/dist/dictionaries/dictionaries.js +3 -1
  50. package/dist/dictionaries/dictionaries.js.map +1 -1
  51. package/dist/forms/FormAttachmentsField.js +12 -0
  52. package/dist/forms/FormAttachmentsField.js.map +1 -0
  53. package/dist/forms/FormCheckboxGroupField.js +12 -0
  54. package/dist/forms/FormCheckboxGroupField.js.map +1 -0
  55. package/dist/forms/FormFileField.js +12 -0
  56. package/dist/forms/FormFileField.js.map +1 -0
  57. package/dist/forms/FormFileUploadField.js +12 -0
  58. package/dist/forms/FormFileUploadField.js.map +1 -0
  59. package/dist/forms/FormImageField.js +12 -0
  60. package/dist/forms/FormImageField.js.map +1 -0
  61. package/dist/forms/FormMaskedField.js +12 -0
  62. package/dist/forms/FormMaskedField.js.map +1 -0
  63. package/dist/forms/FormRepeatingFields.js +54 -0
  64. package/dist/forms/FormRepeatingFields.js.map +1 -0
  65. package/dist/forms/FormTransferListField.js +12 -0
  66. package/dist/forms/FormTransferListField.js.map +1 -0
  67. package/dist/forms/applyServerProblem.js +10 -3
  68. package/dist/forms/applyServerProblem.js.map +1 -1
  69. package/dist/forms/listRules.js +111 -0
  70. package/dist/forms/listRules.js.map +1 -0
  71. package/dist/forms/serverProblemFields.js +17 -0
  72. package/dist/forms/serverProblemFields.js.map +1 -1
  73. package/dist/forms.js +51 -29
  74. package/dist/forms.js.map +1 -1
  75. package/dist/hookForm/FormAttachmentsField.js +12 -0
  76. package/dist/hookForm/FormAttachmentsField.js.map +1 -0
  77. package/dist/hookForm/FormCheckboxGroupField.js +12 -0
  78. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -0
  79. package/dist/hookForm/FormFileField.js +12 -0
  80. package/dist/hookForm/FormFileField.js.map +1 -0
  81. package/dist/hookForm/FormFileUploadField.js +12 -0
  82. package/dist/hookForm/FormFileUploadField.js.map +1 -0
  83. package/dist/hookForm/FormImageField.js +12 -0
  84. package/dist/hookForm/FormImageField.js.map +1 -0
  85. package/dist/hookForm/FormMaskedField.js +12 -0
  86. package/dist/hookForm/FormMaskedField.js.map +1 -0
  87. package/dist/hookForm/FormRepeatingFields.js +44 -0
  88. package/dist/hookForm/FormRepeatingFields.js.map +1 -0
  89. package/dist/hookForm/FormTransferListField.js +12 -0
  90. package/dist/hookForm/FormTransferListField.js.map +1 -0
  91. package/dist/hookForm/applyServerProblem.js +8 -3
  92. package/dist/hookForm/applyServerProblem.js.map +1 -1
  93. package/dist/index.js +188 -100
  94. package/dist/index.js.map +1 -1
  95. package/dist/inputs/CatalogInput.js +74 -7
  96. package/dist/inputs/CatalogInput.js.map +1 -1
  97. package/dist/inputs/CheckboxGroupInput.js +100 -0
  98. package/dist/inputs/CheckboxGroupInput.js.map +1 -0
  99. package/dist/inputs/MaskedInput.js +86 -0
  100. package/dist/inputs/MaskedInput.js.map +1 -0
  101. package/dist/inputs/NumberInput.js +12 -1
  102. package/dist/inputs/NumberInput.js.map +1 -1
  103. package/dist/inputs/RepeatingFields.js +177 -0
  104. package/dist/inputs/RepeatingFields.js.map +1 -0
  105. package/dist/inputs/SelectInput.js +43 -5
  106. package/dist/inputs/SelectInput.js.map +1 -1
  107. package/dist/inputs/TransferListInput.js +215 -0
  108. package/dist/inputs/TransferListInput.js.map +1 -0
  109. package/dist/inputs/inputMasks.js +84 -0
  110. package/dist/inputs/inputMasks.js.map +1 -0
  111. package/dist/inputs/selectAllState.js +20 -0
  112. package/dist/inputs/selectAllState.js.map +1 -0
  113. package/dist/inputs/useRowKeys.js +26 -0
  114. package/dist/inputs/useRowKeys.js.map +1 -0
  115. package/dist/legal/CookieNotice.js +79 -0
  116. package/dist/legal/CookieNotice.js.map +1 -0
  117. package/dist/legal/TermsAcceptanceDialog.js +129 -0
  118. package/dist/legal/TermsAcceptanceDialog.js.map +1 -0
  119. package/dist/legal/consentStorage.js +18 -0
  120. package/dist/legal/consentStorage.js.map +1 -0
  121. package/dist/legal/useCookieConsent.js +17 -0
  122. package/dist/legal/useCookieConsent.js.map +1 -0
  123. package/dist/locales/en.js +233 -1
  124. package/dist/locales/en.js.map +1 -1
  125. package/dist/locales/es.js +233 -1
  126. package/dist/locales/es.js.map +1 -1
  127. package/dist/mexico/CfdiSummary.js +81 -0
  128. package/dist/mexico/CfdiSummary.js.map +1 -0
  129. package/dist/mexico/EfirmaFields.js +167 -0
  130. package/dist/mexico/EfirmaFields.js.map +1 -0
  131. package/dist/mexico/certificateReader.js +79 -0
  132. package/dist/mexico/certificateReader.js.map +1 -0
  133. package/dist/mexico/cfdiReader.js +81 -0
  134. package/dist/mexico/cfdiReader.js.map +1 -0
  135. package/dist/mexico/derReader.js +81 -0
  136. package/dist/mexico/derReader.js.map +1 -0
  137. package/dist/mexico/efirmaKey.js +51 -0
  138. package/dist/mexico/efirmaKey.js.map +1 -0
  139. package/dist/mexico/mexicoIdentifiers.js +86 -0
  140. package/dist/mexico/mexicoIdentifiers.js.map +1 -0
  141. package/dist/mexico/mexicoRules.js +16 -0
  142. package/dist/mexico/mexicoRules.js.map +1 -0
  143. package/dist/mexico.js +32 -0
  144. package/dist/mexico.js.map +1 -0
  145. package/dist/react-hook-form.js +40 -17
  146. package/dist/react-hook-form.js.map +1 -1
  147. package/dist/rich-text.js +19 -0
  148. package/dist/rich-text.js.map +1 -0
  149. package/dist/richText/FormRichTextField.js +12 -0
  150. package/dist/richText/FormRichTextField.js.map +1 -0
  151. package/dist/richText/RichTextEditor.js +108 -0
  152. package/dist/richText/RichTextEditor.js.map +1 -0
  153. package/dist/richText/RichTextLinkPopover.js +57 -0
  154. package/dist/richText/RichTextLinkPopover.js.map +1 -0
  155. package/dist/richText/RichTextToolbar.js +189 -0
  156. package/dist/richText/RichTextToolbar.js.map +1 -0
  157. package/dist/richText/RichTextView.js +26 -0
  158. package/dist/richText/RichTextView.js.map +1 -0
  159. package/dist/richText/richTextContentSx.js +42 -0
  160. package/dist/richText/richTextContentSx.js.map +1 -0
  161. package/dist/richText/richTextExtensions.js +44 -0
  162. package/dist/richText/richTextExtensions.js.map +1 -0
  163. package/dist/richText/richTextRules.js +46 -0
  164. package/dist/richText/richTextRules.js.map +1 -0
  165. package/dist/security/SessionsCard.js +11 -7
  166. package/dist/security/SessionsCard.js.map +1 -1
  167. package/dist/table.js +2 -0
  168. package/dist/table.js.map +1 -1
  169. package/dist/theme/components/glassComponents.js +27 -0
  170. package/dist/theme/components/glassComponents.js.map +1 -1
  171. package/dist/theme/components/minimalComponents.js +12 -0
  172. package/dist/theme/components/minimalComponents.js.map +1 -1
  173. package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +21 -0
  174. package/dist/types/DataTable/filterContext.d.ts +2 -1
  175. package/dist/types/DataTable/goToPageOptions.d.ts +15 -0
  176. package/dist/types/DataTable/index.d.ts +1 -0
  177. package/dist/types/date-pickers.d.ts +10 -1
  178. package/dist/types/datePickers/DateInput.d.ts +6 -4
  179. package/dist/types/datePickers/DateTimeInput.d.ts +50 -0
  180. package/dist/types/datePickers/FormDateTimeField.d.ts +7 -0
  181. package/dist/types/datePickers/FormMonthField.d.ts +7 -0
  182. package/dist/types/datePickers/FormTimeField.d.ts +7 -0
  183. package/dist/types/datePickers/MonthInput.d.ts +39 -0
  184. package/dist/types/datePickers/PickerFrame.d.ts +22 -0
  185. package/dist/types/datePickers/TimeInput.d.ts +38 -0
  186. package/dist/types/datePickers/dateTimeRules.d.ts +31 -0
  187. package/dist/types/datePickers/pickerValues.d.ts +114 -0
  188. package/dist/types/datePickers/useCurrentMinute.d.ts +5 -0
  189. package/dist/types/datePickers/usePickerField.d.ts +24 -0
  190. package/dist/types/dictionaries/dictionaries.d.ts +6 -3
  191. package/dist/types/forms/FormAttachmentsField.d.ts +7 -0
  192. package/dist/types/forms/FormCheckboxGroupField.d.ts +7 -0
  193. package/dist/types/forms/FormFileField.d.ts +7 -0
  194. package/dist/types/forms/FormFileUploadField.d.ts +7 -0
  195. package/dist/types/forms/FormImageField.d.ts +7 -0
  196. package/dist/types/forms/FormMaskedField.d.ts +7 -0
  197. package/dist/types/forms/FormRepeatingFields.d.ts +25 -0
  198. package/dist/types/forms/FormTransferListField.d.ts +7 -0
  199. package/dist/types/forms/index.d.ts +10 -1
  200. package/dist/types/forms/listRules.d.ts +48 -0
  201. package/dist/types/forms/serverProblemFields.d.ts +13 -0
  202. package/dist/types/forms.d.ts +1 -1
  203. package/dist/types/hookForm/FormAttachmentsField.d.ts +8 -0
  204. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +8 -0
  205. package/dist/types/hookForm/FormFileField.d.ts +8 -0
  206. package/dist/types/hookForm/FormFileUploadField.d.ts +8 -0
  207. package/dist/types/hookForm/FormImageField.d.ts +8 -0
  208. package/dist/types/hookForm/FormMaskedField.d.ts +9 -0
  209. package/dist/types/hookForm/FormRepeatingFields.d.ts +27 -0
  210. package/dist/types/hookForm/FormTransferListField.d.ts +8 -0
  211. package/dist/types/index.d.ts +25 -3
  212. package/dist/types/inputs/CatalogInput.d.ts +7 -1
  213. package/dist/types/inputs/CheckboxGroupInput.d.ts +39 -0
  214. package/dist/types/inputs/MaskedInput.d.ts +35 -0
  215. package/dist/types/inputs/RepeatingFields.d.ts +52 -0
  216. package/dist/types/inputs/SelectInput.d.ts +6 -1
  217. package/dist/types/inputs/TransferListInput.d.ts +43 -0
  218. package/dist/types/inputs/inputMasks.d.ts +52 -0
  219. package/dist/types/inputs/selectAllState.d.ts +11 -0
  220. package/dist/types/inputs/useRowKeys.d.ts +11 -0
  221. package/dist/types/legal/CookieNotice.d.ts +30 -0
  222. package/dist/types/legal/TermsAcceptanceDialog.d.ts +32 -0
  223. package/dist/types/legal/consentStorage.d.ts +16 -0
  224. package/dist/types/legal/useCookieConsent.d.ts +13 -0
  225. package/dist/types/locales/en.d.ts +232 -0
  226. package/dist/types/locales/es.d.ts +232 -0
  227. package/dist/types/mexico/CfdiSummary.d.ts +17 -0
  228. package/dist/types/mexico/EfirmaFields.d.ts +38 -0
  229. package/dist/types/mexico/certificateReader.d.ts +37 -0
  230. package/dist/types/mexico/cfdiReader.d.ts +95 -0
  231. package/dist/types/mexico/derReader.d.ts +54 -0
  232. package/dist/types/mexico/efirmaKey.d.ts +26 -0
  233. package/dist/types/mexico/mexicoIdentifiers.d.ts +61 -0
  234. package/dist/types/mexico/mexicoRules.d.ts +29 -0
  235. package/dist/types/mexico.d.ts +11 -0
  236. package/dist/types/react-hook-form.d.ts +10 -1
  237. package/dist/types/rich-text.d.ts +5 -0
  238. package/dist/types/richText/FormRichTextField.d.ts +7 -0
  239. package/dist/types/richText/RichTextEditor.d.ts +42 -0
  240. package/dist/types/richText/RichTextLinkPopover.d.ts +17 -0
  241. package/dist/types/richText/RichTextToolbar.d.ts +15 -0
  242. package/dist/types/richText/RichTextView.d.ts +13 -0
  243. package/dist/types/richText/richTextContentSx.d.ts +7 -0
  244. package/dist/types/richText/richTextExtensions.d.ts +15 -0
  245. package/dist/types/richText/richTextRules.d.ts +41 -0
  246. package/dist/types/table.d.ts +1 -1
  247. package/dist/types/ui/actions/ActionsMenu.d.ts +18 -3
  248. package/dist/types/ui/actions/CopyButton.d.ts +7 -4
  249. package/dist/types/ui/actions/tableRowText.d.ts +6 -0
  250. package/dist/types/ui/cards/RecordCard.d.ts +65 -0
  251. package/dist/types/ui/cards/RecordCardSkeleton.d.ts +17 -0
  252. package/dist/types/ui/countdown/Countdown.d.ts +31 -0
  253. package/dist/types/ui/countdown/countdownParts.d.ts +39 -0
  254. package/dist/types/ui/countdown/useCountdown.d.ts +14 -0
  255. package/dist/types/ui/documents/PdfViewerDialog.d.ts +21 -0
  256. package/dist/types/ui/filters/FilterDrawer.d.ts +34 -0
  257. package/dist/types/ui/filters/filterDrawerFields.d.ts +139 -0
  258. package/dist/types/ui/help/HelpButton.d.ts +15 -0
  259. package/dist/types/ui/help/HelpCarouselDialog.d.ts +35 -0
  260. package/dist/types/ui/help/useHelpCarousel.d.ts +21 -0
  261. package/dist/types/ui/index.d.ts +10 -0
  262. package/dist/types/ui/labels/IconBadge.d.ts +34 -0
  263. package/dist/types/ui/navigation/RouteTabs.d.ts +44 -0
  264. package/dist/types/ui/navigation/routeTabMatching.d.ts +27 -0
  265. package/dist/types/upload/AttachmentsInput.d.ts +62 -0
  266. package/dist/types/upload/FileChip.d.ts +18 -0
  267. package/dist/types/upload/FileDropzone.d.ts +3 -0
  268. package/dist/types/upload/FileInput.d.ts +48 -0
  269. package/dist/types/upload/FileUploadInput.d.ts +80 -0
  270. package/dist/types/upload/ImageInput.d.ts +53 -0
  271. package/dist/types/upload/TemplateDownloadButton.d.ts +39 -0
  272. package/dist/types/upload/fileAppearance.d.ts +8 -0
  273. package/dist/types/upload/fileRules.d.ts +38 -0
  274. package/dist/types/upload/transferProgress.d.ts +18 -0
  275. package/dist/types/upload/uploadMachine.d.ts +6 -2
  276. package/dist/types/upload/uploadWithProgress.d.ts +6 -2
  277. package/dist/types/upload/useFilePicker.d.ts +39 -0
  278. package/dist/ui/actions/ActionsMenu.js +98 -15
  279. package/dist/ui/actions/ActionsMenu.js.map +1 -1
  280. package/dist/ui/actions/CopyButton.js +31 -11
  281. package/dist/ui/actions/CopyButton.js.map +1 -1
  282. package/dist/ui/actions/tableRowText.js +7 -0
  283. package/dist/ui/actions/tableRowText.js.map +1 -0
  284. package/dist/ui/cards/RecordCard.js +145 -0
  285. package/dist/ui/cards/RecordCard.js.map +1 -0
  286. package/dist/ui/cards/RecordCardSkeleton.js +33 -0
  287. package/dist/ui/cards/RecordCardSkeleton.js.map +1 -0
  288. package/dist/ui/countdown/Countdown.js +38 -0
  289. package/dist/ui/countdown/Countdown.js.map +1 -0
  290. package/dist/ui/countdown/countdownParts.js +34 -0
  291. package/dist/ui/countdown/countdownParts.js.map +1 -0
  292. package/dist/ui/countdown/useCountdown.js +35 -0
  293. package/dist/ui/countdown/useCountdown.js.map +1 -0
  294. package/dist/ui/documents/PdfViewerDialog.js +81 -0
  295. package/dist/ui/documents/PdfViewerDialog.js.map +1 -0
  296. package/dist/ui/filters/FilterDrawer.js +211 -0
  297. package/dist/ui/filters/FilterDrawer.js.map +1 -0
  298. package/dist/ui/filters/filterDrawerFields.js +119 -0
  299. package/dist/ui/filters/filterDrawerFields.js.map +1 -0
  300. package/dist/ui/help/HelpButton.js +17 -0
  301. package/dist/ui/help/HelpButton.js.map +1 -0
  302. package/dist/ui/help/HelpCarouselDialog.js +154 -0
  303. package/dist/ui/help/HelpCarouselDialog.js.map +1 -0
  304. package/dist/ui/help/useHelpCarousel.js +25 -0
  305. package/dist/ui/help/useHelpCarousel.js.map +1 -0
  306. package/dist/ui/labels/IconBadge.js +82 -0
  307. package/dist/ui/labels/IconBadge.js.map +1 -0
  308. package/dist/ui/navigation/RouteTabs.js +75 -0
  309. package/dist/ui/navigation/RouteTabs.js.map +1 -0
  310. package/dist/ui/navigation/routeTabMatching.js +23 -0
  311. package/dist/ui/navigation/routeTabMatching.js.map +1 -0
  312. package/dist/upload/AttachmentsInput.js +127 -0
  313. package/dist/upload/AttachmentsInput.js.map +1 -0
  314. package/dist/upload/FileChip.js +27 -0
  315. package/dist/upload/FileChip.js.map +1 -0
  316. package/dist/upload/FileDropzone.js +19 -84
  317. package/dist/upload/FileDropzone.js.map +1 -1
  318. package/dist/upload/FileInput.js +112 -0
  319. package/dist/upload/FileInput.js.map +1 -0
  320. package/dist/upload/FileUploadInput.js +285 -0
  321. package/dist/upload/FileUploadInput.js.map +1 -0
  322. package/dist/upload/ImageInput.js +125 -0
  323. package/dist/upload/ImageInput.js.map +1 -0
  324. package/dist/upload/TemplateDownloadButton.js +67 -0
  325. package/dist/upload/TemplateDownloadButton.js.map +1 -0
  326. package/dist/upload/fileAppearance.js +21 -0
  327. package/dist/upload/fileAppearance.js.map +1 -0
  328. package/dist/upload/fileRules.js +27 -1
  329. package/dist/upload/fileRules.js.map +1 -1
  330. package/dist/upload/transferProgress.js +23 -0
  331. package/dist/upload/transferProgress.js.map +1 -0
  332. package/dist/upload/uploadMachine.js +8 -8
  333. package/dist/upload/uploadMachine.js.map +1 -1
  334. package/dist/upload/uploadWithProgress.js +1 -1
  335. package/dist/upload/uploadWithProgress.js.map +1 -1
  336. package/dist/upload/useFilePicker.js +101 -0
  337. package/dist/upload/useFilePicker.js.map +1 -0
  338. package/package.json +44 -1
@@ -0,0 +1,211 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId, useState, useEffect } from "react";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import Drawer from "@mui/material/Drawer";
7
+ import IconButton from "@mui/material/IconButton";
8
+ import Stack from "@mui/material/Stack";
9
+ import Typography from "@mui/material/Typography";
10
+ import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
11
+ import CatalogInput from "../../inputs/CatalogInput.js";
12
+ import CheckboxGroupInput from "../../inputs/CheckboxGroupInput.js";
13
+ import FieldFrame from "../../inputs/FieldFrame.js";
14
+ import NumberInput from "../../inputs/NumberInput.js";
15
+ import SelectInput from "../../inputs/SelectInput.js";
16
+ import TextInput from "../../inputs/TextInput.js";
17
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
18
+ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
19
+ import { warnUnknownProps } from "../../toolkit/unknownProps.js";
20
+ import { drawerValuesOf, invalidRangesOf, emptyDrawerValuesOf, filterKeysOf, FILTER_FIELD_KIND, comparisonOf, COMPARISON_OPERATOR, comparisonTextOf } from "./filterDrawerFields.js";
21
+ const FILTER_DRAWER_ANCHOR = Object.freeze({ left: "left", right: "right" });
22
+ const DEFAULT_WIDTH = 380;
23
+ const ANY_OPTION = "__calcita_any__";
24
+ const THEME_NAME = "CalcitaFilterDrawer";
25
+ const FilterDrawerHeader = piecePart(THEME_NAME, "Header", Stack);
26
+ const FilterDrawerBody = piecePart(THEME_NAME, "Body", Stack);
27
+ const FilterDrawerFooter = piecePart(THEME_NAME, "Footer", Stack);
28
+ function PairedField({ label, inputId, children, template = "1fr 1fr" }) {
29
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: /* @__PURE__ */ jsx(Box, { sx: { display: "grid", gridTemplateColumns: template, gap: 1.5, alignItems: "start" }, children }) });
30
+ }
31
+ function DrawerField({ field, values, setValue, invalidRanges, t }) {
32
+ const fieldId = useId();
33
+ const [key] = filterKeysOf(field);
34
+ switch (field.kind) {
35
+ case FILTER_FIELD_KIND.text:
36
+ return /* @__PURE__ */ jsx(TextInput, { label: field.label, value: values[key], onChange: (text) => setValue(key, text), placeholder: field.placeholder });
37
+ case FILTER_FIELD_KIND.select:
38
+ return /* @__PURE__ */ jsx(
39
+ SelectInput,
40
+ {
41
+ label: field.label,
42
+ value: values[key] || ANY_OPTION,
43
+ onChange: (chosen) => setValue(key, chosen === ANY_OPTION ? "" : chosen),
44
+ options: [{ value: ANY_OPTION, label: t("filterDrawer.any") }, ...field.options]
45
+ }
46
+ );
47
+ case FILTER_FIELD_KIND.multiSelect:
48
+ return /* @__PURE__ */ jsx(SelectInput, { label: field.label, value: values[key], onChange: (chosen) => setValue(key, chosen), options: field.options, multiple: true, selectAll: true, checkboxes: true });
49
+ case FILTER_FIELD_KIND.catalog:
50
+ return /* @__PURE__ */ jsx(
51
+ CatalogInput,
52
+ {
53
+ label: field.label,
54
+ value: values[key],
55
+ onChange: (chosen) => setValue(key, chosen),
56
+ options: field.options,
57
+ multiple: field.multiple,
58
+ selectAll: field.multiple,
59
+ checkboxes: field.multiple,
60
+ formatLabel: field.formatLabel,
61
+ placeholder: field.placeholder
62
+ }
63
+ );
64
+ case FILTER_FIELD_KIND.checkboxes:
65
+ return /* @__PURE__ */ jsx(CheckboxGroupInput, { label: field.label, value: values[key], onChange: (chosen) => setValue(key, chosen), options: field.options, selectAll: field.selectAll });
66
+ case FILTER_FIELD_KIND.numberRange: {
67
+ const [fromKey, toKey] = filterKeysOf(field);
68
+ const numberProps = { decimals: field.decimals ?? 0, prefix: field.prefix, suffix: field.suffix, helperText: "" };
69
+ return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-from`, children: [
70
+ /* @__PURE__ */ jsx(NumberInput, { label: t("filterDrawer.from"), value: values[fromKey], onChange: (number) => setValue(fromKey, number), ...numberProps }),
71
+ /* @__PURE__ */ jsx(
72
+ NumberInput,
73
+ {
74
+ label: t("filterDrawer.to"),
75
+ value: values[toKey],
76
+ onChange: (number) => setValue(toKey, number),
77
+ ...numberProps,
78
+ error: invalidRanges[toKey] ? t("filterDrawer.rangeBackwards") : void 0
79
+ }
80
+ )
81
+ ] });
82
+ }
83
+ case FILTER_FIELD_KIND.dateRange: {
84
+ const [fromKey, toKey] = filterKeysOf(field);
85
+ return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-from`, children: [
86
+ /* @__PURE__ */ jsx(TextInput, { type: "date", label: t("filterDrawer.from"), value: values[fromKey], onChange: (date) => setValue(fromKey, date), clearable: false, helperText: "" }),
87
+ /* @__PURE__ */ jsx(
88
+ TextInput,
89
+ {
90
+ type: "date",
91
+ label: t("filterDrawer.to"),
92
+ value: values[toKey],
93
+ onChange: (date) => setValue(toKey, date),
94
+ clearable: false,
95
+ error: invalidRanges[toKey] ? t("filterDrawer.rangeBackwards") : void 0
96
+ }
97
+ )
98
+ ] });
99
+ }
100
+ case FILTER_FIELD_KIND.comparison: {
101
+ const parts = comparisonOf(values[key]);
102
+ const operators = field.operators ?? Object.values(COMPARISON_OPERATOR);
103
+ const operator = parts?.operator ?? field.defaultOperator ?? operators[0];
104
+ return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-value`, template: "minmax(0, 1fr) minmax(0, 1.2fr)", children: [
105
+ /* @__PURE__ */ jsx(
106
+ SelectInput,
107
+ {
108
+ label: t("filterDrawer.operator"),
109
+ value: operator,
110
+ helperText: "",
111
+ options: operators.map((option) => ({ value: option, label: t(`filterDrawer.operators.${option}`) })),
112
+ onChange: (chosen) => setValue(key, comparisonTextOf(chosen, parts?.value ?? ""))
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsx(
116
+ NumberInput,
117
+ {
118
+ label: t("filterDrawer.value"),
119
+ value: parts?.value ?? "",
120
+ decimals: field.decimals ?? 2,
121
+ prefix: field.prefix,
122
+ suffix: field.suffix,
123
+ helperText: "",
124
+ onChange: (number) => setValue(key, comparisonTextOf(operator, number))
125
+ }
126
+ )
127
+ ] });
128
+ }
129
+ case FILTER_FIELD_KIND.custom:
130
+ return field.render({ value: values[key], onChange: (next) => setValue(key, next), values, setValue });
131
+ default:
132
+ return null;
133
+ }
134
+ }
135
+ function FilterDrawer(props) {
136
+ const {
137
+ open,
138
+ onClose,
139
+ fields,
140
+ values,
141
+ onApply,
142
+ anchor = FILTER_DRAWER_ANCHOR.right,
143
+ width = DEFAULT_WIDTH,
144
+ title,
145
+ applyLabel,
146
+ closeOnApply = true,
147
+ children,
148
+ texts,
149
+ ...unknownProps
150
+ } = usePieceProps(props, THEME_NAME);
151
+ warnUnknownProps(THEME_NAME, unknownProps);
152
+ const { t } = useToolkitTranslation("common", texts);
153
+ const titleId = useId();
154
+ const [draft, setDraft] = useState(() => drawerValuesOf(fields, values));
155
+ useEffect(() => {
156
+ if (open) {
157
+ setDraft(drawerValuesOf(fields, values));
158
+ }
159
+ }, [open]);
160
+ const invalidRanges = invalidRangesOf(fields, draft);
161
+ const hasInvalidRange = Object.keys(invalidRanges).length > 0;
162
+ const setValue = (key, value) => setDraft((current) => ({ ...current, [key]: value }));
163
+ const apply = () => {
164
+ onApply(draft);
165
+ if (closeOnApply) {
166
+ onClose();
167
+ }
168
+ };
169
+ return /* @__PURE__ */ jsx(
170
+ Drawer,
171
+ {
172
+ anchor,
173
+ open,
174
+ onClose,
175
+ slotProps: { paper: { "aria-labelledby": titleId, className: `${THEME_NAME}-paper`, sx: { width: { xs: "100%", sm: width }, maxWidth: "100%" } } },
176
+ children: /* @__PURE__ */ jsxs(
177
+ Box,
178
+ {
179
+ component: "form",
180
+ noValidate: true,
181
+ sx: { display: "flex", flexDirection: "column", height: "100%" },
182
+ onSubmit: (event) => {
183
+ event.preventDefault();
184
+ if (!hasInvalidRange) {
185
+ apply();
186
+ }
187
+ },
188
+ children: [
189
+ /* @__PURE__ */ jsxs(FilterDrawerHeader, { direction: "row", ownSx: { alignItems: "center", gap: 1, px: 2, py: 1.5, borderBottom: 1, borderColor: "divider" }, children: [
190
+ /* @__PURE__ */ jsx(IconButton, { "aria-label": t("filterDrawer.close"), onClick: onClose, edge: "start", children: /* @__PURE__ */ jsx(CloseRoundedIcon, {}) }),
191
+ /* @__PURE__ */ jsx(Typography, { id: titleId, variant: "h6", component: "h2", sx: { flexGrow: 1 }, children: title ?? t("filterDrawer.title") })
192
+ ] }),
193
+ /* @__PURE__ */ jsxs(FilterDrawerBody, { spacing: 1, ownSx: { flex: 1, overflowY: "auto", px: 2.5, py: 2 }, children: [
194
+ fields.map((field) => /* @__PURE__ */ jsx(DrawerField, { field, values: draft, setValue, invalidRanges, t }, field.name)),
195
+ children
196
+ ] }),
197
+ /* @__PURE__ */ jsxs(FilterDrawerFooter, { direction: "row", ownSx: { gap: 1, justifyContent: "flex-end", px: 2.5, py: 1.5, borderTop: 1, borderColor: "divider" }, children: [
198
+ /* @__PURE__ */ jsx(Button, { onClick: () => setDraft(emptyDrawerValuesOf(fields)), children: t("filterDrawer.clear") }),
199
+ /* @__PURE__ */ jsx(Button, { type: "submit", variant: "contained", disabled: hasInvalidRange, children: applyLabel ?? t("filterDrawer.apply") })
200
+ ] })
201
+ ]
202
+ }
203
+ )
204
+ }
205
+ );
206
+ }
207
+ export {
208
+ FILTER_DRAWER_ANCHOR,
209
+ FilterDrawer as default
210
+ };
211
+ //# sourceMappingURL=FilterDrawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterDrawer.js","sources":["../../../src/ui/filters/FilterDrawer.jsx"],"sourcesContent":["// El cajón de búsqueda: un panel que sale de un lado con los campos que filtran una lista (las listas, un rango\n// «desde-hasta», unas fechas, un monto con su operador), para cuando los filtros ya no caben arriba de la tabla.\n//\n// Lo que se cambia adentro es un BORRADOR: la lista no se mueve hasta «Buscar» (no se consulta al servidor con cada tecla).\n// «Limpiar» vacía los campos del cajón; «Buscar» manda todos juntos (onApply) y cierra. Un rango al revés («hasta» menor que\n// «desde») se dice en su campo y no deja buscar.\n//\n// Los campos se declaran (filterDrawerFields.js dice cómo viaja cada uno); con kind 'custom' va uno propio (render). Para\n// conectarlo a ServerTable con su botón y sus chips: ServerTableFilterDrawer de /table.\n//\n// Configurable: de qué lado sale (anchor), su ancho, su título y los textos de sus botones. En el teléfono ocupa todo el\n// ancho.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFilterDrawer con defaultProps y\n// styleOverrides de sus partes: paper, header, body, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Drawer from '@mui/material/Drawer';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport CatalogInput from '@/inputs/CatalogInput.jsx';\nimport CheckboxGroupInput from '@/inputs/CheckboxGroupInput.jsx';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport NumberInput from '@/inputs/NumberInput.jsx';\nimport SelectInput from '@/inputs/SelectInput.jsx';\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {\n COMPARISON_OPERATOR, FILTER_FIELD_KIND, comparisonOf, comparisonTextOf, drawerValuesOf, emptyDrawerValuesOf, filterKeysOf,\n invalidRangesOf,\n} from '@/ui/filters/filterDrawerFields.js';\n\n/** De qué lado sale el cajón. */\nexport const FILTER_DRAWER_ANCHOR = Object.freeze({left: 'left', right: 'right'});\nconst DEFAULT_WIDTH = 380;\n// «Cualquiera» de una lista de una sola opción: no es una opción y viaja como '' (sin filtro)\nconst ANY_OPTION = '__calcita_any__';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFilterDrawer)\nconst THEME_NAME = 'CalcitaFilterDrawer';\nconst FilterDrawerHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst FilterDrawerBody = piecePart(THEME_NAME, 'Body', Stack);\nconst FilterDrawerFooter = piecePart(THEME_NAME, 'Footer', Stack);\n\n/**\n * Dos campos lado a lado (desde y hasta, operador y número); en un cajón angosto se ven bien juntos.\n *\n * @param {{label: import('react').ReactNode, inputId: string, children: import('react').ReactNode, template?: string}} props\n */\nfunction PairedField({label, inputId, children, template = '1fr 1fr'}) {\n return (\n <FieldFrame inputId={inputId} label={label}>\n <Box sx={{display: 'grid', gridTemplateColumns: template, gap: 1.5, alignItems: 'start'}}>{children}</Box>\n </FieldFrame>\n );\n}\n\n/**\n * Un campo del cajón según su tipo, con las piezas de la casa.\n *\n * @param {object} props\n */\nfunction DrawerField({field, values, setValue, invalidRanges, t}) {\n const fieldId = useId();\n const [key] = filterKeysOf(field);\n switch (field.kind) {\n case FILTER_FIELD_KIND.text:\n return <TextInput label={field.label} value={values[key]} onChange={(text) => setValue(key, text)} placeholder={field.placeholder} />;\n case FILTER_FIELD_KIND.select:\n return (\n <SelectInput label={field.label} value={values[key] || ANY_OPTION} onChange={(chosen) => setValue(key, chosen === ANY_OPTION ? '' : chosen)}\n options={[{value: ANY_OPTION, label: t('filterDrawer.any')}, ...field.options]} />\n );\n case FILTER_FIELD_KIND.multiSelect:\n return <SelectInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} multiple selectAll checkboxes />;\n case FILTER_FIELD_KIND.catalog:\n return (\n <CatalogInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options}\n multiple={field.multiple} selectAll={field.multiple} checkboxes={field.multiple} formatLabel={field.formatLabel}\n placeholder={field.placeholder} />\n );\n case FILTER_FIELD_KIND.checkboxes:\n return <CheckboxGroupInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} selectAll={field.selectAll} />;\n case FILTER_FIELD_KIND.numberRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n const numberProps = {decimals: field.decimals ?? 0, prefix: field.prefix, suffix: field.suffix, helperText: ''};\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`}>\n <NumberInput label={t('filterDrawer.from')} value={values[fromKey]} onChange={(number) => setValue(fromKey, number)} {...numberProps} />\n <NumberInput label={t('filterDrawer.to')} value={values[toKey]} onChange={(number) => setValue(toKey, number)} {...numberProps}\n error={invalidRanges[toKey] ? t('filterDrawer.rangeBackwards') : undefined} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.dateRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n // el calendario del navegador (como DateRangeFilter): viaja AAAA-MM-DD, sin pedir MUI X\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`}>\n <TextInput type=\"date\" label={t('filterDrawer.from')} value={values[fromKey]} onChange={(date) => setValue(fromKey, date)} clearable={false} helperText=\"\" />\n <TextInput type=\"date\" label={t('filterDrawer.to')} value={values[toKey]} onChange={(date) => setValue(toKey, date)} clearable={false}\n error={invalidRanges[toKey] ? t('filterDrawer.rangeBackwards') : undefined} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.comparison: {\n const parts = comparisonOf(values[key]);\n const operators = field.operators ?? Object.values(COMPARISON_OPERATOR);\n const operator = parts?.operator ?? field.defaultOperator ?? operators[0];\n return (\n <PairedField label={field.label} inputId={`${fieldId}-value`} template=\"minmax(0, 1fr) minmax(0, 1.2fr)\">\n <SelectInput label={t('filterDrawer.operator')} value={operator} helperText=\"\"\n options={operators.map((option) => ({value: option, label: t(`filterDrawer.operators.${option}`)}))}\n onChange={(chosen) => setValue(key, comparisonTextOf(chosen, parts?.value ?? ''))} />\n <NumberInput label={t('filterDrawer.value')} value={parts?.value ?? ''} decimals={field.decimals ?? 2} prefix={field.prefix} suffix={field.suffix}\n helperText=\"\" onChange={(number) => setValue(key, comparisonTextOf(operator, number))} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.custom:\n return field.render({value: values[key], onChange: (next) => setValue(key, next), values, setValue});\n default:\n return null;\n }\n}\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {object[]} props.fields los campos (filterDrawerFields.js): {name, label, kind, options?, keys?, prefix?, suffix?, decimals?, operators?, render?…}\n * @param {Record<string, unknown>} props.values los filtros puestos hoy (de ahí sale el borrador al abrir)\n * @param {(values: Record<string, unknown>) => void} props.onApply todos los campos del cajón juntos, al picar «Buscar»\n * @param {'left'|'right'} [props.anchor] de qué lado sale (FILTER_DRAWER_ANCHOR); por omisión la derecha\n * @param {number} [props.width] su ancho en escritorio, en px\n * @param {string} [props.title] ya traducido; por omisión «Filtros»\n * @param {string} [props.applyLabel] ya traducido; por omisión «Buscar»\n * @param {boolean} [props.closeOnApply] por omisión sí\n * @param {import('react').ReactNode} [props.children] algo más abajo de los campos (una nota, un enlace)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function FilterDrawer(props) {\n const {\n open, onClose, fields, values, onApply, anchor = FILTER_DRAWER_ANCHOR.right, width = DEFAULT_WIDTH, title, applyLabel,\n closeOnApply = true, children, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const titleId = useId();\n const [draft, setDraft] = useState(() => drawerValuesOf(fields, values));\n // cada vez que se abre, el borrador empieza de lo que hay puesto (lo de la vez pasada sin buscar no se queda)\n useEffect(() => {\n if (open) {\n setDraft(drawerValuesOf(fields, values));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n const invalidRanges = invalidRangesOf(fields, draft);\n const hasInvalidRange = Object.keys(invalidRanges).length > 0;\n const setValue = (key, value) => setDraft((current) => ({...current, [key]: value}));\n const apply = () => {\n onApply(draft);\n if (closeOnApply) {\n onClose();\n }\n };\n\n return (\n <Drawer\n anchor={anchor}\n open={open}\n onClose={onClose}\n slotProps={{paper: {'aria-labelledby': titleId, className: `${THEME_NAME}-paper`, sx: {width: {xs: '100%', sm: width}, maxWidth: '100%'}}}}\n >\n <Box component=\"form\" noValidate sx={{display: 'flex', flexDirection: 'column', height: '100%'}}\n onSubmit={(event) => {\n event.preventDefault();\n if (!hasInvalidRange) {\n apply();\n }\n }}>\n <FilterDrawerHeader direction=\"row\" ownSx={{alignItems: 'center', gap: 1, px: 2, py: 1.5, borderBottom: 1, borderColor: 'divider'}}>\n <IconButton aria-label={t('filterDrawer.close')} onClick={onClose} edge=\"start\"><CloseRoundedIcon /></IconButton>\n <Typography id={titleId} variant=\"h6\" component=\"h2\" sx={{flexGrow: 1}}>{title ?? t('filterDrawer.title')}</Typography>\n </FilterDrawerHeader>\n <FilterDrawerBody spacing={1} ownSx={{flex: 1, overflowY: 'auto', px: 2.5, py: 2}}>\n {fields.map((field) => (\n <DrawerField key={field.name} field={field} values={draft} setValue={setValue} invalidRanges={invalidRanges} t={t} />\n ))}\n {children}\n </FilterDrawerBody>\n <FilterDrawerFooter direction=\"row\" ownSx={{gap: 1, justifyContent: 'flex-end', px: 2.5, py: 1.5, borderTop: 1, borderColor: 'divider'}}>\n <Button onClick={() => setDraft(emptyDrawerValuesOf(fields))}>{t('filterDrawer.clear')}</Button>\n <Button type=\"submit\" variant=\"contained\" disabled={hasInvalidRange}>{applyLabel ?? t('filterDrawer.apply')}</Button>\n </FilterDrawerFooter>\n </Box>\n </Drawer>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ const FILTER_FIELD_KIND = Object.freeze({
2
+ text: "text",
3
+ select: "select",
4
+ multiSelect: "multiSelect",
5
+ catalog: "catalog",
6
+ checkboxes: "checkboxes",
7
+ numberRange: "numberRange",
8
+ dateRange: "dateRange",
9
+ comparison: "comparison",
10
+ custom: "custom"
11
+ });
12
+ const COMPARISON_OPERATOR = Object.freeze({ equals: "eq", greaterThan: "gt", greaterOrEqual: "gte", lessThan: "lt", lessOrEqual: "lte" });
13
+ const OPERATOR_SYMBOLS = Object.freeze({ eq: "=", gt: ">", gte: "≥", lt: "<", lte: "≤" });
14
+ const COMPARISON_SEPARATOR = ":";
15
+ const LIST_KINDS = /* @__PURE__ */ new Set([FILTER_FIELD_KIND.multiSelect, FILTER_FIELD_KIND.checkboxes]);
16
+ function isListField(field) {
17
+ return LIST_KINDS.has(field.kind) || field.kind === FILTER_FIELD_KIND.catalog && Boolean(field.multiple);
18
+ }
19
+ function filterKeysOf(field) {
20
+ if (field.kind === FILTER_FIELD_KIND.numberRange) {
21
+ return [field.keys?.from ?? `${field.name}_min`, field.keys?.to ?? `${field.name}_max`];
22
+ }
23
+ if (field.kind === FILTER_FIELD_KIND.dateRange) {
24
+ return [field.keys?.from ?? `${field.name}_from`, field.keys?.to ?? `${field.name}_to`];
25
+ }
26
+ return [field.name];
27
+ }
28
+ function comparisonOf(text) {
29
+ const [operator, ...rest] = String(text ?? "").split(COMPARISON_SEPARATOR);
30
+ const value = rest.join(COMPARISON_SEPARATOR);
31
+ return OPERATOR_SYMBOLS[operator] && value !== "" ? { operator, value } : null;
32
+ }
33
+ function comparisonTextOf(operator, value) {
34
+ return value === "" || value === null || value === void 0 ? "" : `${operator}${COMPARISON_SEPARATOR}${value}`;
35
+ }
36
+ function isFilterValueSet(value) {
37
+ return Array.isArray(value) ? value.length > 0 : value !== "" && value !== null && value !== void 0;
38
+ }
39
+ function filterDrawerKindsOf(fields) {
40
+ return Object.fromEntries(fields.filter(isListField).map((field) => [field.name, "multiSelect"]));
41
+ }
42
+ function drawerValuesOf(fields, filters) {
43
+ return Object.fromEntries(fields.flatMap((field) => filterKeysOf(field).map((key) => [key, filters?.[key] ?? (isListField(field) ? [] : "")])));
44
+ }
45
+ function emptyDrawerValuesOf(fields) {
46
+ return drawerValuesOf(fields, {});
47
+ }
48
+ function filterDrawerDefaultsOf(fields) {
49
+ return emptyDrawerValuesOf(fields);
50
+ }
51
+ function activeDrawerFilterCountOf(fields, filters) {
52
+ return fields.filter((field) => filterKeysOf(field).some((key) => isFilterValueSet(filters?.[key]))).length;
53
+ }
54
+ function invalidRangesOf(fields, values) {
55
+ const invalid = {};
56
+ for (const field of fields) {
57
+ if (field.kind !== FILTER_FIELD_KIND.numberRange && field.kind !== FILTER_FIELD_KIND.dateRange) {
58
+ continue;
59
+ }
60
+ const [fromKey, toKey] = filterKeysOf(field);
61
+ const from = values?.[fromKey];
62
+ const to = values?.[toKey];
63
+ if (!isFilterValueSet(from) || !isFilterValueSet(to)) {
64
+ continue;
65
+ }
66
+ const isBackwards = field.kind === FILTER_FIELD_KIND.numberRange ? Number(to) < Number(from) : String(to) < String(from);
67
+ if (isBackwards) {
68
+ invalid[toKey] = true;
69
+ }
70
+ }
71
+ return invalid;
72
+ }
73
+ function filterDrawerChipsOf(fields, { t, formatDate = (date) => date }) {
74
+ const text = [];
75
+ const multiSelect = [];
76
+ const optionLabelOf = (field, value) => field.options?.find((option) => (option.value ?? option.code) === value)?.label ?? String(value);
77
+ for (const field of fields) {
78
+ const withUnit = (value) => `${field.prefix ?? ""}${value}${field.suffix ? ` ${field.suffix}` : ""}`;
79
+ if (isListField(field)) {
80
+ multiSelect.push({ key: field.name, label: field.label, itemLabel: (value) => optionLabelOf(field, value) });
81
+ } else if (field.kind === FILTER_FIELD_KIND.numberRange || field.kind === FILTER_FIELD_KIND.dateRange) {
82
+ const [fromKey, toKey] = filterKeysOf(field);
83
+ const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : withUnit;
84
+ text.push({ key: fromKey, label: t("filterDrawer.chipFrom", { label: field.label }), format });
85
+ text.push({ key: toKey, label: t("filterDrawer.chipTo", { label: field.label }), format });
86
+ } else if (field.kind === FILTER_FIELD_KIND.comparison) {
87
+ text.push({
88
+ key: field.name,
89
+ label: field.label,
90
+ format: (value) => {
91
+ const parts = comparisonOf(value);
92
+ return parts ? `${OPERATOR_SYMBOLS[parts.operator]} ${withUnit(parts.value)}` : String(value);
93
+ }
94
+ });
95
+ } else if (field.kind === FILTER_FIELD_KIND.select || field.kind === FILTER_FIELD_KIND.catalog) {
96
+ text.push({ key: field.name, label: field.label, format: (value) => optionLabelOf(field, value) });
97
+ } else {
98
+ text.push({ key: field.name, label: field.label, ...field.chipFormat && { format: field.chipFormat } });
99
+ }
100
+ }
101
+ return { text, multiSelect };
102
+ }
103
+ export {
104
+ COMPARISON_OPERATOR,
105
+ FILTER_FIELD_KIND,
106
+ activeDrawerFilterCountOf,
107
+ comparisonOf,
108
+ comparisonTextOf,
109
+ drawerValuesOf,
110
+ emptyDrawerValuesOf,
111
+ filterDrawerChipsOf,
112
+ filterDrawerDefaultsOf,
113
+ filterDrawerKindsOf,
114
+ filterKeysOf,
115
+ invalidRangesOf,
116
+ isFilterValueSet,
117
+ isListField
118
+ };
119
+ //# sourceMappingURL=filterDrawerFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filterDrawerFields.js","sources":["../../../src/ui/filters/filterDrawerFields.js"],"sourcesContent":["// Los campos del cajón de búsqueda (FilterDrawer): cómo viaja cada uno en los filtros de la lista, cuántos hay puestos y\n// cómo se dicen en sus chips. Un campo se declara una vez y de ahí sale todo:\n//\n// text un texto {name}: 'ana'\n// select una opción {name}: 'card'\n// multiSelect varias opciones (lista) {name}: ['card', 'cash']\n// catalog una o varias de un catálogo largo {name}: 'NL' o ['NL', 'JA'] (multiple)\n// checkboxes varias, a la vista {name}: ['email', 'sms']\n// numberRange desde y hasta {name}_min: '3', {name}_max: '10'\n// dateRange del día al día (AAAA-MM-DD) {name}_from: '2026-11-01', {name}_to: '2026-11-30'\n// comparison un número con su operador {name}: 'gte:500' (mayor o igual que 500)\n// custom uno propio (render) {name}: lo que el proyecto decida\n//\n// Los nombres de un rango se cambian con keys ({from: 'created_from', to: 'created_to'}) para usar los que ya tiene el\n// backend. Los textos llegan ya traducidos (label, options[].label); los de la casa salen de `t` (common.filterDrawer).\n//\n// Puro (sin React): se prueba con node (filterDrawerFields.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/** Los tipos de campo del cajón. */\nexport const FILTER_FIELD_KIND = Object.freeze({\n text: 'text', select: 'select', multiSelect: 'multiSelect', catalog: 'catalog', checkboxes: 'checkboxes',\n numberRange: 'numberRange', dateRange: 'dateRange', comparison: 'comparison', custom: 'custom',\n});\n\n/** Los operadores de un campo de comparación, como viajan ('gte:500'). */\nexport const COMPARISON_OPERATOR = Object.freeze({equals: 'eq', greaterThan: 'gt', greaterOrEqual: 'gte', lessThan: 'lt', lessOrEqual: 'lte'});\n\n// cómo se dice cada operador en un chip, corto\nconst OPERATOR_SYMBOLS = Object.freeze({eq: '=', gt: '>', gte: '≥', lt: '<', lte: '≤'});\nconst COMPARISON_SEPARATOR = ':';\n// los que guardan una lista: así los lee la tabla en la dirección\nconst LIST_KINDS = new Set([FILTER_FIELD_KIND.multiSelect, FILTER_FIELD_KIND.checkboxes]);\n\n/**\n * ¿El campo guarda una lista?\n *\n * @param {{kind: string, multiple?: boolean}} field\n * @returns {boolean}\n */\nexport function isListField(field) {\n return LIST_KINDS.has(field.kind) || (field.kind === FILTER_FIELD_KIND.catalog && Boolean(field.multiple));\n}\n\n/**\n * Los nombres con que viaja un campo en los filtros: uno, o dos para un rango.\n *\n * @param {{name: string, kind: string, keys?: {from?: string, to?: string}}} field\n * @returns {string[]}\n */\nexport function filterKeysOf(field) {\n if (field.kind === FILTER_FIELD_KIND.numberRange) {\n return [field.keys?.from ?? `${field.name}_min`, field.keys?.to ?? `${field.name}_max`];\n }\n if (field.kind === FILTER_FIELD_KIND.dateRange) {\n return [field.keys?.from ?? `${field.name}_from`, field.keys?.to ?? `${field.name}_to`];\n }\n return [field.name];\n}\n\n/**\n * Un valor de comparación ('gte:500') en sus partes; null si viene vacío o no se entiende.\n *\n * @param {unknown} text\n * @returns {{operator: string, value: string}|null}\n */\nexport function comparisonOf(text) {\n const [operator, ...rest] = String(text ?? '').split(COMPARISON_SEPARATOR);\n const value = rest.join(COMPARISON_SEPARATOR);\n return OPERATOR_SYMBOLS[operator] && value !== '' ? {operator, value} : null;\n}\n\n/**\n * Las partes de una comparación como viajan; '' si falta el número.\n *\n * @param {string} operator COMPARISON_OPERATOR\n * @param {string} value\n * @returns {string}\n */\nexport function comparisonTextOf(operator, value) {\n return value === '' || value === null || value === undefined ? '' : `${operator}${COMPARISON_SEPARATOR}${value}`;\n}\n\n/**\n * ¿Ese valor de filtro está puesto? Un texto vacío o una lista vacía no cuentan.\n *\n * @param {unknown} value\n * @returns {boolean}\n */\nexport function isFilterValueSet(value) {\n return Array.isArray(value) ? value.length > 0 : value !== '' && value !== null && value !== undefined;\n}\n\n/**\n * Los tipos de filtro para ServerTable (filterKinds): los de lista se leen como lista en la dirección.\n *\n * @param {object[]} fields\n * @returns {Record<string, string>}\n */\nexport function filterDrawerKindsOf(fields) {\n return Object.fromEntries(fields.filter(isListField).map((field) => [field.name, 'multiSelect']));\n}\n\n/**\n * Lo que hay en los filtros para los campos del cajón (lo demás no se toca).\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {Record<string, unknown>}\n */\nexport function drawerValuesOf(fields, filters) {\n return Object.fromEntries(fields.flatMap((field) => filterKeysOf(field).map((key) => [key, filters?.[key] ?? (isListField(field) ? [] : '')])));\n}\n\n/**\n * Los campos del cajón vacíos: lo que deja «Limpiar».\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function emptyDrawerValuesOf(fields) {\n return drawerValuesOf(fields, {});\n}\n\n/**\n * Los campos del cajón vacíos, para sumarlos a filterDefaults de ServerTable: la tabla solo guarda en la dirección los\n * filtros que declara ahí.\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function filterDrawerDefaultsOf(fields) {\n return emptyDrawerValuesOf(fields);\n}\n\n/**\n * Cuántos campos del cajón tienen algo puesto (un rango con «desde» y «hasta» cuenta uno): el número del botón «Filtros».\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {number}\n */\nexport function activeDrawerFilterCountOf(fields, filters) {\n return fields.filter((field) => filterKeysOf(field).some((key) => isFilterValueSet(filters?.[key]))).length;\n}\n\n/**\n * Los rangos que no tienen sentido («hasta» menor que «desde»): {llave de «hasta»: true}.\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} values\n * @returns {Record<string, boolean>}\n */\nexport function invalidRangesOf(fields, values) {\n const invalid = {};\n for (const field of fields) {\n if (field.kind !== FILTER_FIELD_KIND.numberRange && field.kind !== FILTER_FIELD_KIND.dateRange) {\n continue;\n }\n const [fromKey, toKey] = filterKeysOf(field);\n const from = values?.[fromKey];\n const to = values?.[toKey];\n if (!isFilterValueSet(from) || !isFilterValueSet(to)) {\n continue;\n }\n // los números como números y las fechas AAAA-MM-DD como texto (ya se ordenan solas)\n const isBackwards = field.kind === FILTER_FIELD_KIND.numberRange ? Number(to) < Number(from) : String(to) < String(from);\n if (isBackwards) {\n invalid[toKey] = true;\n }\n }\n return invalid;\n}\n\n/**\n * Los chips de los campos del cajón, con la forma de buildChipConfig: cada valor con su etiqueta, ya dicho en palabras.\n *\n * @param {object[]} fields\n * @param {object} helpers\n * @param {(key: string, values?: object) => string} helpers.t el de common (filterDrawer.chipFrom, chipTo)\n * @param {(date: string) => string} [helpers.formatDate] cómo se ve una fecha\n * @returns {{text: Array<{key: string, label: string, format?: (value: string) => string}>, multiSelect: Array<{key: string, label: string, itemLabel?: (value: string) => string}>}}\n */\nexport function filterDrawerChipsOf(fields, {t, formatDate = (date) => date}) {\n const text = [];\n const multiSelect = [];\n const optionLabelOf = (field, value) => field.options?.find((option) => (option.value ?? option.code) === value)?.label ?? String(value);\n for (const field of fields) {\n const withUnit = (value) => `${field.prefix ?? ''}${value}${field.suffix ? ` ${field.suffix}` : ''}`;\n if (isListField(field)) {\n multiSelect.push({key: field.name, label: field.label, itemLabel: (value) => optionLabelOf(field, value)});\n } else if (field.kind === FILTER_FIELD_KIND.numberRange || field.kind === FILTER_FIELD_KIND.dateRange) {\n const [fromKey, toKey] = filterKeysOf(field);\n const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : withUnit;\n text.push({key: fromKey, label: t('filterDrawer.chipFrom', {label: field.label}), format});\n text.push({key: toKey, label: t('filterDrawer.chipTo', {label: field.label}), format});\n } else if (field.kind === FILTER_FIELD_KIND.comparison) {\n text.push({\n key: field.name, label: field.label,\n format: (value) => {\n const parts = comparisonOf(value);\n return parts ? `${OPERATOR_SYMBOLS[parts.operator]} ${withUnit(parts.value)}` : String(value);\n },\n });\n } else if (field.kind === FILTER_FIELD_KIND.select || field.kind === FILTER_FIELD_KIND.catalog) {\n text.push({key: field.name, label: field.label, format: (value) => optionLabelOf(field, value)});\n } else {\n text.push({key: field.name, label: field.label, ...(field.chipFormat && {format: field.chipFormat})});\n }\n }\n return {text, multiSelect};\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import IconButton from "@mui/material/IconButton";
4
+ import Tooltip from "@mui/material/Tooltip";
5
+ import HelpOutlineRoundedIcon from "@mui/icons-material/HelpOutlineRounded";
6
+ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
7
+ import { warnUnknownProps } from "../../toolkit/unknownProps.js";
8
+ function HelpButton({ onClick, label, size = "medium", sx, texts, ...unknownProps }) {
9
+ warnUnknownProps("HelpButton", unknownProps);
10
+ const { t } = useToolkitTranslation("common", texts);
11
+ const text = label ?? t("help.button");
12
+ return /* @__PURE__ */ jsx(Tooltip, { title: text, children: /* @__PURE__ */ jsx(IconButton, { "aria-label": text, onClick, size, sx, children: /* @__PURE__ */ jsx(HelpOutlineRoundedIcon, { fontSize: size === "small" ? "small" : "medium" }) }) });
13
+ }
14
+ export {
15
+ HelpButton as default
16
+ };
17
+ //# sourceMappingURL=HelpButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HelpButton.js","sources":["../../../src/ui/help/HelpButton.jsx"],"sourcesContent":["// El botón «?» que vuelve a abrir la ayuda de una pantalla (la del carrusel o la que sea): un ícono con su nombre para el\n// lector de pantalla y su globo al pasar el cursor.\n//\n// Genérico del paquete: function (regla de la casa).\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport HelpOutlineRoundedIcon from '@mui/icons-material/HelpOutlineRounded';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {() => void} props.onClick\n * @param {string} [props.label] ya traducido; por omisión «Ayuda de esta pantalla»\n * @param {'small'|'medium'|'large'} [props.size]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function HelpButton({onClick, label, size = 'medium', sx, texts, ...unknownProps}) {\n warnUnknownProps('HelpButton', unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const text = label ?? t('help.button');\n return (\n <Tooltip title={text}>\n <IconButton aria-label={text} onClick={onClick} size={size} sx={sx}>\n <HelpOutlineRoundedIcon fontSize={size === 'small' ? 'small' : 'medium'} />\n </IconButton>\n </Tooltip>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAmBA,SAAwB,WAAW,EAAC,SAAS,OAAO,OAAO,UAAU,IAAI,OAAO,GAAG,gBAAe;AAC9F,mBAAiB,cAAc,YAAY;AAC3C,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,OAAO,SAAS,EAAE,aAAa;AACrC,6BACK,SAAA,EAAQ,OAAO,MACZ,UAAA,oBAAC,YAAA,EAAW,cAAY,MAAM,SAAkB,MAAY,IACxD,UAAA,oBAAC,0BAAuB,UAAU,SAAS,UAAU,UAAU,UAAU,GAC7E,EAAA,CACJ;AAER;"}
@@ -0,0 +1,154 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { useId, useState, useRef, useEffect } from "react";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import ButtonBase from "@mui/material/ButtonBase";
7
+ import Checkbox from "@mui/material/Checkbox";
8
+ import Dialog from "@mui/material/Dialog";
9
+ import DialogActions from "@mui/material/DialogActions";
10
+ import DialogContent from "@mui/material/DialogContent";
11
+ import Fade from "@mui/material/Fade";
12
+ import FormControlLabel from "@mui/material/FormControlLabel";
13
+ import IconButton from "@mui/material/IconButton";
14
+ import Stack from "@mui/material/Stack";
15
+ import Typography from "@mui/material/Typography";
16
+ import useMediaQuery from "@mui/material/useMediaQuery";
17
+ import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
18
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
19
+ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
20
+ import { warnUnknownProps } from "../../toolkit/unknownProps.js";
21
+ const HELP_CLOSE_REASON = Object.freeze({ finished: "finished", closed: "closed" });
22
+ const SWIPE_DISTANCE = 50;
23
+ const FADE_MILLISECONDS = 220;
24
+ const MEDIA_HEIGHT = 200;
25
+ const THEME_NAME = "CalcitaHelpCarouselDialog";
26
+ const HelpMedia = piecePart(THEME_NAME, "Media", Box);
27
+ const HelpContent = piecePart(THEME_NAME, "Content", DialogContent);
28
+ const HelpDots = piecePart(THEME_NAME, "Dots", Stack);
29
+ const HelpActions = piecePart(THEME_NAME, "Actions", DialogActions);
30
+ function HelpCarouselDialog(props) {
31
+ const {
32
+ open,
33
+ onClose,
34
+ steps,
35
+ title,
36
+ showDontShowAgain = true,
37
+ onStepChange,
38
+ initialStep = 0,
39
+ texts,
40
+ ...unknownProps
41
+ } = usePieceProps(props, THEME_NAME);
42
+ warnUnknownProps(THEME_NAME, unknownProps);
43
+ const { t } = useToolkitTranslation("common", texts);
44
+ const titleId = useId();
45
+ const [stepIndex, setStepIndex] = useState(initialStep);
46
+ const [dontShowAgain, setDontShowAgain] = useState(false);
47
+ const reducesMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
48
+ const swipeStartRef = useRef(null);
49
+ const lastIndex = steps.length - 1;
50
+ const step = steps[Math.min(stepIndex, lastIndex)];
51
+ useEffect(() => {
52
+ if (open) {
53
+ setStepIndex(initialStep);
54
+ }
55
+ }, [open]);
56
+ useEffect(() => {
57
+ if (open) {
58
+ onStepChange?.(stepIndex);
59
+ }
60
+ }, [open, stepIndex]);
61
+ const goTo = (index) => setStepIndex(Math.max(0, Math.min(lastIndex, index)));
62
+ const close = (reason) => onClose(reason, { dontShowAgain });
63
+ const next = () => stepIndex >= lastIndex ? close(HELP_CLOSE_REASON.finished) : goTo(stepIndex + 1);
64
+ if (!step) {
65
+ return null;
66
+ }
67
+ return /* @__PURE__ */ jsxs(
68
+ Dialog,
69
+ {
70
+ open,
71
+ onClose: () => close(HELP_CLOSE_REASON.closed),
72
+ maxWidth: "sm",
73
+ fullWidth: true,
74
+ "aria-labelledby": titleId,
75
+ onKeyDown: (event) => {
76
+ if (event.key === "ArrowRight") {
77
+ goTo(stepIndex + 1);
78
+ } else if (event.key === "ArrowLeft") {
79
+ goTo(stepIndex - 1);
80
+ }
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsxs(
84
+ Box,
85
+ {
86
+ onPointerDown: (event) => {
87
+ swipeStartRef.current = event.clientX;
88
+ },
89
+ onPointerUp: (event) => {
90
+ const distance = swipeStartRef.current === null ? 0 : event.clientX - swipeStartRef.current;
91
+ swipeStartRef.current = null;
92
+ if (Math.abs(distance) >= SWIPE_DISTANCE) {
93
+ goTo(stepIndex + (distance < 0 ? 1 : -1));
94
+ }
95
+ },
96
+ sx: { position: "relative", touchAction: "pan-y" },
97
+ children: [
98
+ /* @__PURE__ */ jsx(
99
+ IconButton,
100
+ {
101
+ "aria-label": t("help.close"),
102
+ onClick: () => close(HELP_CLOSE_REASON.closed),
103
+ sx: { position: "absolute", top: 8, right: 8, zIndex: 1 },
104
+ children: /* @__PURE__ */ jsx(CloseRoundedIcon, {})
105
+ }
106
+ ),
107
+ /* @__PURE__ */ jsx(Fade, { in: true, appear: !reducesMotion, timeout: reducesMotion ? 0 : FADE_MILLISECONDS, children: /* @__PURE__ */ jsxs("div", { children: [
108
+ (step.image || step.media) && /* @__PURE__ */ jsx(HelpMedia, { ownSx: { height: MEDIA_HEIGHT, display: "grid", placeItems: "center", overflow: "hidden", bgcolor: "action.hover" }, children: step.media ?? /* @__PURE__ */ jsx(Box, { component: "img", src: step.image, alt: step.imageAlt ?? "", sx: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }) }),
109
+ /* @__PURE__ */ jsxs(HelpContent, { ownSx: { pt: 2.5 }, children: [
110
+ title && /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "text.secondary", component: "p", children: title }),
111
+ /* @__PURE__ */ jsx(Typography, { id: titleId, variant: "h6", component: "h2", sx: { pr: 4 }, children: step.title }),
112
+ /* @__PURE__ */ jsx(Typography, { component: "div", color: "text.secondary", sx: { mt: 1 }, children: step.content })
113
+ ] })
114
+ ] }) }, stepIndex)
115
+ ]
116
+ }
117
+ ),
118
+ /* @__PURE__ */ jsx(HelpDots, { direction: "row", ownSx: { justifyContent: "center", gap: 1, pb: 1 }, role: "group", "aria-label": t("help.stepOf", { current: stepIndex + 1, total: steps.length }), children: steps.map((unused, index) => /* @__PURE__ */ jsx(
119
+ ButtonBase,
120
+ {
121
+ onClick: () => goTo(index),
122
+ "aria-label": t("help.goToStep", { number: index + 1 }),
123
+ "aria-current": index === stepIndex ? "step" : void 0,
124
+ sx: {
125
+ width: index === stepIndex ? 22 : 8,
126
+ height: 8,
127
+ borderRadius: 4,
128
+ transition: reducesMotion ? "none" : "width 200ms",
129
+ bgcolor: index === stepIndex ? "primary.main" : "action.disabled"
130
+ }
131
+ },
132
+ index
133
+ )) }),
134
+ /* @__PURE__ */ jsxs(HelpActions, { ownSx: { px: 3, pb: 2, gap: 1, flexWrap: "wrap" }, children: [
135
+ showDontShowAgain && /* @__PURE__ */ jsx(
136
+ FormControlLabel,
137
+ {
138
+ sx: { mr: "auto" },
139
+ label: t("help.dontShowAgain"),
140
+ control: /* @__PURE__ */ jsx(Checkbox, { size: "small", checked: dontShowAgain, onChange: (event) => setDontShowAgain(event.target.checked) })
141
+ }
142
+ ),
143
+ stepIndex > 0 && /* @__PURE__ */ jsx(Button, { onClick: () => goTo(stepIndex - 1), children: t("help.back") }),
144
+ /* @__PURE__ */ jsx(Button, { variant: "contained", onClick: next, autoFocus: true, children: stepIndex >= lastIndex ? t("help.done") : t("help.next") })
145
+ ] })
146
+ ]
147
+ }
148
+ );
149
+ }
150
+ export {
151
+ HELP_CLOSE_REASON,
152
+ HelpCarouselDialog as default
153
+ };
154
+ //# sourceMappingURL=HelpCarouselDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HelpCarouselDialog.js","sources":["../../../src/ui/help/HelpCarouselDialog.jsx"],"sourcesContent":["// La ayuda en carrusel: unos cuantos pasos, cada uno con su imagen o ilustración, su título y su texto, para explicar una\n// pantalla la primera vez que se entra (o cuando cambia). Se avanza con «Siguiente», con los puntos de abajo, con las\n// flechas del teclado o deslizando con el dedo; el último paso dice «Listo». Con «reducir movimiento» del sistema no hay\n// animación.\n//\n// Cuándo sale y que se recuerde lo hace useHelpCarousel; esta ventana solo enseña y avisa:\n// onClose(reason, {dontShowAgain}) reason 'finished' (terminó) o 'closed' (la cerró antes); el proyecto puede guardarlo\n// también en su servidor\n// onStepChange(index) cada paso que se ve\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaHelpCarouselDialog con defaultProps y\n// styleOverrides de sus partes: media, content, dots, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useRef, useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Checkbox from '@mui/material/Checkbox';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport Fade from '@mui/material/Fade';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/** Por qué se cerró la ayuda. */\nexport const HELP_CLOSE_REASON = Object.freeze({finished: 'finished', closed: 'closed'});\n\n// lo que hay que deslizar con el dedo para cambiar de paso (px)\nconst SWIPE_DISTANCE = 50;\nconst FADE_MILLISECONDS = 220;\nconst MEDIA_HEIGHT = 200;\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaHelpCarouselDialog)\nconst THEME_NAME = 'CalcitaHelpCarouselDialog';\nconst HelpMedia = piecePart(THEME_NAME, 'Media', Box);\nconst HelpContent = piecePart(THEME_NAME, 'Content', DialogContent);\nconst HelpDots = piecePart(THEME_NAME, 'Dots', Stack);\nconst HelpActions = piecePart(THEME_NAME, 'Actions', DialogActions);\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {(reason: string, details: {dontShowAgain: boolean}) => void} props.onClose HELP_CLOSE_REASON y si marcó «No volver a mostrar»\n * @param {Array<{title: string, content: import('react').ReactNode, image?: string, imageAlt?: string, media?: import('react').ReactNode}>} props.steps\n * title y content ya traducidos; image (una dirección) o media (una ilustración propia) arriba\n * @param {string} [props.title] ya traducido: de qué es la ayuda («Cómo usar el tablero»), arriba de los pasos\n * @param {boolean} [props.showDontShowAgain] la casilla «No volver a mostrar al entrar» (por omisión sí)\n * @param {(index: number) => void} [props.onStepChange]\n * @param {number} [props.initialStep]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function HelpCarouselDialog(props) {\n const {\n open, onClose, steps, title, showDontShowAgain = true, onStepChange, initialStep = 0, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const titleId = useId();\n const [stepIndex, setStepIndex] = useState(initialStep);\n const [dontShowAgain, setDontShowAgain] = useState(false);\n const reducesMotion = useMediaQuery('(prefers-reduced-motion: reduce)');\n const swipeStartRef = useRef(null);\n const lastIndex = steps.length - 1;\n const step = steps[Math.min(stepIndex, lastIndex)];\n\n // cada vez que se abre empieza en su primer paso\n useEffect(() => {\n if (open) {\n setStepIndex(initialStep);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n useEffect(() => {\n if (open) {\n onStepChange?.(stepIndex);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, stepIndex]);\n\n const goTo = (index) => setStepIndex(Math.max(0, Math.min(lastIndex, index)));\n const close = (reason) => onClose(reason, {dontShowAgain});\n const next = () => (stepIndex >= lastIndex ? close(HELP_CLOSE_REASON.finished) : goTo(stepIndex + 1));\n\n if (!step) {\n return null;\n }\n return (\n <Dialog\n open={open}\n onClose={() => close(HELP_CLOSE_REASON.closed)}\n maxWidth=\"sm\"\n fullWidth\n aria-labelledby={titleId}\n onKeyDown={(event) => {\n // las flechas cambian de paso (dentro de la casilla o de un botón también: no escriben nada)\n if (event.key === 'ArrowRight') {\n goTo(stepIndex + 1);\n } else if (event.key === 'ArrowLeft') {\n goTo(stepIndex - 1);\n }\n }}\n >\n <Box\n onPointerDown={(event) => {\n swipeStartRef.current = event.clientX;\n }}\n onPointerUp={(event) => {\n const distance = swipeStartRef.current === null ? 0 : event.clientX - swipeStartRef.current;\n swipeStartRef.current = null;\n if (Math.abs(distance) >= SWIPE_DISTANCE) {\n goTo(stepIndex + (distance < 0 ? 1 : -1));\n }\n }}\n sx={{position: 'relative', touchAction: 'pan-y'}}\n >\n <IconButton aria-label={t('help.close')} onClick={() => close(HELP_CLOSE_REASON.closed)}\n sx={{position: 'absolute', top: 8, right: 8, zIndex: 1}}>\n <CloseRoundedIcon />\n </IconButton>\n <Fade key={stepIndex} in appear={!reducesMotion} timeout={reducesMotion ? 0 : FADE_MILLISECONDS}>\n <div>\n {(step.image || step.media) && (\n <HelpMedia ownSx={{height: MEDIA_HEIGHT, display: 'grid', placeItems: 'center', overflow: 'hidden', bgcolor: 'action.hover'}}>\n {step.media ?? <Box component=\"img\" src={step.image} alt={step.imageAlt ?? ''} sx={{maxWidth: '100%', maxHeight: '100%', objectFit: 'contain'}} />}\n </HelpMedia>\n )}\n <HelpContent ownSx={{pt: 2.5}}>\n {title && <Typography variant=\"overline\" color=\"text.secondary\" component=\"p\">{title}</Typography>}\n <Typography id={titleId} variant=\"h6\" component=\"h2\" sx={{pr: 4}}>{step.title}</Typography>\n <Typography component=\"div\" color=\"text.secondary\" sx={{mt: 1}}>{step.content}</Typography>\n </HelpContent>\n </div>\n </Fade>\n </Box>\n {/* los puntos: dónde va y para saltar a un paso */}\n <HelpDots direction=\"row\" ownSx={{justifyContent: 'center', gap: 1, pb: 1}} role=\"group\" aria-label={t('help.stepOf', {current: stepIndex + 1, total: steps.length})}>\n {steps.map((unused, index) => (\n <ButtonBase key={index} onClick={() => goTo(index)} aria-label={t('help.goToStep', {number: index + 1})}\n aria-current={index === stepIndex ? 'step' : undefined}\n sx={{width: index === stepIndex ? 22 : 8, height: 8, borderRadius: 4, transition: reducesMotion ? 'none' : 'width 200ms',\n bgcolor: index === stepIndex ? 'primary.main' : 'action.disabled'}} />\n ))}\n </HelpDots>\n <HelpActions ownSx={{px: 3, pb: 2, gap: 1, flexWrap: 'wrap'}}>\n {showDontShowAgain && (\n <FormControlLabel sx={{mr: 'auto'}} label={t('help.dontShowAgain')}\n control={<Checkbox size=\"small\" checked={dontShowAgain} onChange={(event) => setDontShowAgain(event.target.checked)} />} />\n )}\n {stepIndex > 0 && <Button onClick={() => goTo(stepIndex - 1)}>{t('help.back')}</Button>}\n <Button variant=\"contained\" onClick={next} autoFocus>\n {stepIndex >= lastIndex ? t('help.done') : t('help.next')}\n </Button>\n </HelpActions>\n </Dialog>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAmCO,MAAM,oBAAoB,OAAO,OAAO,EAAC,UAAU,YAAY,QAAQ,UAAS;AAGvF,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AAGrB,MAAM,aAAa;AACnB,MAAM,YAAY,UAAU,YAAY,SAAS,GAAG;AACpD,MAAM,cAAc,UAAU,YAAY,WAAW,aAAa;AAClE,MAAM,WAAW,UAAU,YAAY,QAAQ,KAAK;AACpD,MAAM,cAAc,UAAU,YAAY,WAAW,aAAa;AAclE,SAAwB,mBAAmB,OAAO;AAC9C,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAS;AAAA,IAAO;AAAA,IAAO,oBAAoB;AAAA,IAAM;AAAA,IAAc,cAAc;AAAA,IAAG;AAAA,IACtF,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,UAAU,MAAA;AAChB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,WAAW;AACtD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,QAAM,gBAAgB,cAAc,kCAAkC;AACtE,QAAM,gBAAgB,OAAO,IAAI;AACjC,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,OAAO,MAAM,KAAK,IAAI,WAAW,SAAS,CAAC;AAGjD,YAAU,MAAM;AACZ,QAAI,MAAM;AACN,mBAAa,WAAW;AAAA,IAC5B;AAAA,EAEJ,GAAG,CAAC,IAAI,CAAC;AACT,YAAU,MAAM;AACZ,QAAI,MAAM;AACN,qBAAe,SAAS;AAAA,IAC5B;AAAA,EAEJ,GAAG,CAAC,MAAM,SAAS,CAAC;AAEpB,QAAM,OAAO,CAAC,UAAU,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,KAAK,CAAC,CAAC;AAC5E,QAAM,QAAQ,CAAC,WAAW,QAAQ,QAAQ,EAAC,eAAc;AACzD,QAAM,OAAO,MAAO,aAAa,YAAY,MAAM,kBAAkB,QAAQ,IAAI,KAAK,YAAY,CAAC;AAEnG,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,EACX;AACA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,SAAS,MAAM,MAAM,kBAAkB,MAAM;AAAA,MAC7C,UAAS;AAAA,MACT,WAAS;AAAA,MACT,mBAAiB;AAAA,MACjB,WAAW,CAAC,UAAU;AAElB,YAAI,MAAM,QAAQ,cAAc;AAC5B,eAAK,YAAY,CAAC;AAAA,QACtB,WAAW,MAAM,QAAQ,aAAa;AAClC,eAAK,YAAY,CAAC;AAAA,QACtB;AAAA,MACJ;AAAA,MAEA,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACG,eAAe,CAAC,UAAU;AACtB,4BAAc,UAAU,MAAM;AAAA,YAClC;AAAA,YACA,aAAa,CAAC,UAAU;AACpB,oBAAM,WAAW,cAAc,YAAY,OAAO,IAAI,MAAM,UAAU,cAAc;AACpF,4BAAc,UAAU;AACxB,kBAAI,KAAK,IAAI,QAAQ,KAAK,gBAAgB;AACtC,qBAAK,aAAa,WAAW,IAAI,IAAI,GAAG;AAAA,cAC5C;AAAA,YACJ;AAAA,YACA,IAAI,EAAC,UAAU,YAAY,aAAa,QAAA;AAAA,YAExC,UAAA;AAAA,cAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAAW,cAAY,EAAE,YAAY;AAAA,kBAAG,SAAS,MAAM,MAAM,kBAAkB,MAAM;AAAA,kBAC1E,IAAI,EAAC,UAAU,YAAY,KAAK,GAAG,OAAO,GAAG,QAAQ,EAAA;AAAA,kBAC7D,8BAAC,kBAAA,CAAA,CAAiB;AAAA,gBAAA;AAAA,cAAA;AAAA,cAEtB,oBAAC,MAAA,EAAqB,IAAE,MAAC,QAAQ,CAAC,eAAe,SAAS,gBAAgB,IAAI,mBAC1E,UAAA,qBAAC,OAAA,EACK,UAAA;AAAA,iBAAA,KAAK,SAAS,KAAK,8BAChB,WAAA,EAAU,OAAO,EAAC,QAAQ,cAAc,SAAS,QAAQ,YAAY,UAAU,UAAU,UAAU,SAAS,kBACxG,UAAA,KAAK,SAAS,oBAAC,KAAA,EAAI,WAAU,OAAM,KAAK,KAAK,OAAO,KAAK,KAAK,YAAY,IAAI,IAAI,EAAC,UAAU,QAAQ,WAAW,QAAQ,WAAW,aAAY,GACpJ;AAAA,qCAEH,aAAA,EAAY,OAAO,EAAC,IAAI,OACpB,UAAA;AAAA,kBAAA,SAAS,oBAAC,cAAW,SAAQ,YAAW,OAAM,kBAAiB,WAAU,KAAK,UAAA,MAAA,CAAM;AAAA,kBACrF,oBAAC,YAAA,EAAW,IAAI,SAAS,SAAQ,MAAK,WAAU,MAAK,IAAI,EAAC,IAAI,EAAA,GAAK,eAAK,OAAM;AAAA,kBAC9E,oBAAC,YAAA,EAAW,WAAU,OAAM,OAAM,kBAAiB,IAAI,EAAC,IAAI,EAAA,GAAK,UAAA,KAAK,QAAA,CAAQ;AAAA,gBAAA,EAAA,CAClF;AAAA,cAAA,EAAA,CACJ,KAZO,SAaX;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGJ,oBAAC,UAAA,EAAS,WAAU,OAAM,OAAO,EAAC,gBAAgB,UAAU,KAAK,GAAG,IAAI,EAAA,GAAI,MAAK,SAAQ,cAAY,EAAE,eAAe,EAAC,SAAS,YAAY,GAAG,OAAO,MAAM,OAAA,CAAO,GAC9J,UAAA,MAAM,IAAI,CAAC,QAAQ,UAChB;AAAA,UAAC;AAAA,UAAA;AAAA,YAAuB,SAAS,MAAM,KAAK,KAAK;AAAA,YAAG,cAAY,EAAE,iBAAiB,EAAC,QAAQ,QAAQ,GAAE;AAAA,YAC1F,gBAAc,UAAU,YAAY,SAAS;AAAA,YAC7C,IAAI;AAAA,cAAC,OAAO,UAAU,YAAY,KAAK;AAAA,cAAG,QAAQ;AAAA,cAAG,cAAc;AAAA,cAAG,YAAY,gBAAgB,SAAS;AAAA,cACvG,SAAS,UAAU,YAAY,iBAAiB;AAAA,YAAA;AAAA,UAAiB;AAAA,UAHhE;AAAA,QAAA,CAIpB,GACL;AAAA,QACA,qBAAC,aAAA,EAAY,OAAO,EAAC,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,UAAU,OAAA,GAChD,UAAA;AAAA,UAAA,qBACG;AAAA,YAAC;AAAA,YAAA;AAAA,cAAiB,IAAI,EAAC,IAAI,OAAA;AAAA,cAAS,OAAO,EAAE,oBAAoB;AAAA,cAC/C,SAAS,oBAAC,UAAA,EAAS,MAAK,SAAQ,SAAS,eAAe,UAAU,CAAC,UAAU,iBAAiB,MAAM,OAAO,OAAO,EAAA,CAAG;AAAA,YAAA;AAAA,UAAA;AAAA,UAE1I,YAAY,KAAK,oBAAC,QAAA,EAAO,SAAS,MAAM,KAAK,YAAY,CAAC,GAAI,UAAA,EAAE,WAAW,EAAA,CAAE;AAAA,UAC9E,oBAAC,QAAA,EAAO,SAAQ,aAAY,SAAS,MAAM,WAAS,MAC/C,UAAA,aAAa,YAAY,EAAE,WAAW,IAAI,EAAE,WAAW,EAAA,CAC5D;AAAA,QAAA,EAAA,CACJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGZ;"}
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { useState } from "react";
3
+ import { acceptedVersionOf, rememberAcceptedVersion } from "../../legal/consentStorage.js";
4
+ const STORAGE_PREFIX = "calcita:help:";
5
+ function useHelpCarousel({ helpKey, version = "1", autoOpen = true, seen = false }) {
6
+ const storageKey = `${STORAGE_PREFIX}${helpKey}`;
7
+ const wasSeen = seen || acceptedVersionOf(storageKey) === version;
8
+ const [open, setOpen] = useState(() => autoOpen && !wasSeen);
9
+ return {
10
+ open,
11
+ wasSeen,
12
+ openHelp: () => setOpen(true),
13
+ // terminarla la recuerda; cerrarla antes, solo con «No volver a mostrar»
14
+ closeHelp: (reason, { dontShowAgain = false } = {}) => {
15
+ setOpen(false);
16
+ if (reason === "finished" || dontShowAgain) {
17
+ rememberAcceptedVersion(storageKey, version);
18
+ }
19
+ }
20
+ };
21
+ }
22
+ export {
23
+ useHelpCarousel
24
+ };
25
+ //# sourceMappingURL=useHelpCarousel.js.map