@stackcraftmx/calcita 0.31.0 → 0.32.1

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 (281) hide show
  1. package/README.md +7 -0
  2. package/dist/DataTable/AuditTrailDialog.js +4 -1
  3. package/dist/DataTable/AuditTrailDialog.js.map +1 -1
  4. package/dist/DataTable/AuditTrailTable.js +4 -1
  5. package/dist/DataTable/AuditTrailTable.js.map +1 -1
  6. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -1
  7. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
  8. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -1
  9. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
  10. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +4 -1
  11. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
  12. package/dist/DataTable/ColumnFilters/TextColumnFilter.js +3 -1
  13. package/dist/DataTable/ColumnFilters/TextColumnFilter.js.map +1 -1
  14. package/dist/DataTable/EmptyState.js +3 -1
  15. package/dist/DataTable/EmptyState.js.map +1 -1
  16. package/dist/DataTable/FilterChips.js +3 -1
  17. package/dist/DataTable/FilterChips.js.map +1 -1
  18. package/dist/DataTable/GoToPage.js +3 -1
  19. package/dist/DataTable/GoToPage.js.map +1 -1
  20. package/dist/DataTable/ServerTable.js +4 -1
  21. package/dist/DataTable/ServerTable.js.map +1 -1
  22. package/dist/DataTable/columns.js +3 -1
  23. package/dist/DataTable/columns.js.map +1 -1
  24. package/dist/access/Can.js +3 -1
  25. package/dist/access/Can.js.map +1 -1
  26. package/dist/access/PermissionMatrix.js +3 -1
  27. package/dist/access/PermissionMatrix.js.map +1 -1
  28. package/dist/access/RequireAccess.js +3 -1
  29. package/dist/access/RequireAccess.js.map +1 -1
  30. package/dist/account/AccountProvider.js +3 -1
  31. package/dist/account/AccountProvider.js.map +1 -1
  32. package/dist/account/AccountSecuritySection.js +3 -1
  33. package/dist/account/AccountSecuritySection.js.map +1 -1
  34. package/dist/account/DemoTotpCodeButton.js +3 -1
  35. package/dist/account/DemoTotpCodeButton.js.map +1 -1
  36. package/dist/account/ForgotPasswordScreen.js +3 -1
  37. package/dist/account/ForgotPasswordScreen.js.map +1 -1
  38. package/dist/account/ProfileScreen.js +4 -1
  39. package/dist/account/ProfileScreen.js.map +1 -1
  40. package/dist/account/ResetPasswordScreen.js +4 -1
  41. package/dist/account/ResetPasswordScreen.js.map +1 -1
  42. package/dist/account/SignInScreen.js +3 -1
  43. package/dist/account/SignInScreen.js.map +1 -1
  44. package/dist/appShell/AccountMenu.js +3 -1
  45. package/dist/appShell/AccountMenu.js.map +1 -1
  46. package/dist/appShell/AppLayout.js +4 -1
  47. package/dist/appShell/AppLayout.js.map +1 -1
  48. package/dist/appShell/AppearanceSettings.js +4 -1
  49. package/dist/appShell/AppearanceSettings.js.map +1 -1
  50. package/dist/appShell/AuthLayout.js +3 -1
  51. package/dist/appShell/AuthLayout.js.map +1 -1
  52. package/dist/appShell/BarActions.js +3 -1
  53. package/dist/appShell/BarActions.js.map +1 -1
  54. package/dist/appShell/BarSearch.js +3 -1
  55. package/dist/appShell/BarSearch.js.map +1 -1
  56. package/dist/appShell/HeaderTools.js +4 -1
  57. package/dist/appShell/HeaderTools.js.map +1 -1
  58. package/dist/appShell/MenuLinks.js +3 -1
  59. package/dist/appShell/MenuLinks.js.map +1 -1
  60. package/dist/appShell/MobileTabBar.js +3 -1
  61. package/dist/appShell/MobileTabBar.js.map +1 -1
  62. package/dist/appShell/SettingsMenu.js +3 -1
  63. package/dist/appShell/SettingsMenu.js.map +1 -1
  64. package/dist/appShell/SideMenu.js +4 -1
  65. package/dist/appShell/SideMenu.js.map +1 -1
  66. package/dist/appShell/SideMenuEntry.js +3 -1
  67. package/dist/appShell/SideMenuEntry.js.map +1 -1
  68. package/dist/appShell/SubmenuActionsMenu.js +3 -1
  69. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  70. package/dist/appShell/TabBarEditor.js +3 -1
  71. package/dist/appShell/TabBarEditor.js.map +1 -1
  72. package/dist/appShell/TopMenu.js +3 -1
  73. package/dist/appShell/TopMenu.js.map +1 -1
  74. package/dist/appShell/Wordmark.js +3 -1
  75. package/dist/appShell/Wordmark.js.map +1 -1
  76. package/dist/appShell/pageTitle.js +3 -1
  77. package/dist/appShell/pageTitle.js.map +1 -1
  78. package/dist/auth/PasswordChecklist.js +3 -1
  79. package/dist/auth/PasswordChecklist.js.map +1 -1
  80. package/dist/calendar/EventCalendar.js +4 -1
  81. package/dist/calendar/EventCalendar.js.map +1 -1
  82. package/dist/calendar/EventDetails.js +3 -1
  83. package/dist/calendar/EventDetails.js.map +1 -1
  84. package/dist/datePickers/DateInput.js +4 -1
  85. package/dist/datePickers/DateInput.js.map +1 -1
  86. package/dist/downloads/DownloadManager.js +3 -1
  87. package/dist/downloads/DownloadManager.js.map +1 -1
  88. package/dist/feedback/ConfirmDialog.js +4 -1
  89. package/dist/feedback/ConfirmDialog.js.map +1 -1
  90. package/dist/feedback/ModalWaiting.js +3 -1
  91. package/dist/feedback/ModalWaiting.js.map +1 -1
  92. package/dist/feedback/NewBuildNotice.js +3 -1
  93. package/dist/feedback/NewBuildNotice.js.map +1 -1
  94. package/dist/filepond/FileDropzone.js +4 -1
  95. package/dist/filepond/FileDropzone.js.map +1 -1
  96. package/dist/formatting.js +8 -5
  97. package/dist/formatting.js.map +1 -1
  98. package/dist/forms/FormProblemAlert.js +3 -1
  99. package/dist/forms/FormProblemAlert.js.map +1 -1
  100. package/dist/forms/FormServerProblem.js +3 -1
  101. package/dist/forms/FormServerProblem.js.map +1 -1
  102. package/dist/forms/FormWizard.js +3 -1
  103. package/dist/forms/FormWizard.js.map +1 -1
  104. package/dist/forms/auth/ChangePasswordForm.js +3 -1
  105. package/dist/forms/auth/ChangePasswordForm.js.map +1 -1
  106. package/dist/forms/auth/ForgotPasswordForm.js +3 -1
  107. package/dist/forms/auth/ForgotPasswordForm.js.map +1 -1
  108. package/dist/forms/auth/SignInForm.js +3 -1
  109. package/dist/forms/auth/SignInForm.js.map +1 -1
  110. package/dist/forms/security/SecondStepForm.js +3 -1
  111. package/dist/forms/security/SecondStepForm.js.map +1 -1
  112. package/dist/forms/security/TwoStepVerificationCard.js +3 -1
  113. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  114. package/dist/hookForm/FormProblemAlert.js +3 -1
  115. package/dist/hookForm/FormProblemAlert.js.map +1 -1
  116. package/dist/hookForm/FormWizard.js +3 -1
  117. package/dist/hookForm/FormWizard.js.map +1 -1
  118. package/dist/inbox/InboxLayout.js +4 -1
  119. package/dist/inbox/InboxLayout.js.map +1 -1
  120. package/dist/inbox/MessageList.js +4 -1
  121. package/dist/inbox/MessageList.js.map +1 -1
  122. package/dist/inbox/MessageView.js +3 -1
  123. package/dist/inbox/MessageView.js.map +1 -1
  124. package/dist/index.js +3 -1
  125. package/dist/inputs/CatalogInput.js +4 -1
  126. package/dist/inputs/CatalogInput.js.map +1 -1
  127. package/dist/inputs/ColorInput.js +4 -1
  128. package/dist/inputs/ColorInput.js.map +1 -1
  129. package/dist/inputs/FieldFrame.js +3 -1
  130. package/dist/inputs/FieldFrame.js.map +1 -1
  131. package/dist/inputs/FormSection.js +5 -2
  132. package/dist/inputs/FormSection.js.map +1 -1
  133. package/dist/inputs/NumberInput.js +4 -1
  134. package/dist/inputs/NumberInput.js.map +1 -1
  135. package/dist/inputs/SelectInput.js +4 -1
  136. package/dist/inputs/SelectInput.js.map +1 -1
  137. package/dist/inputs/TextInput.js +2 -0
  138. package/dist/inputs/TextInput.js.map +1 -1
  139. package/dist/inputs/WizardLayout.js +4 -1
  140. package/dist/inputs/WizardLayout.js.map +1 -1
  141. package/dist/kanban/KanbanBoard.js +4 -1
  142. package/dist/kanban/KanbanBoard.js.map +1 -1
  143. package/dist/maps/MapView.js +4 -1
  144. package/dist/maps/MapView.js.map +1 -1
  145. package/dist/pwa/InstallAppSetting.js +3 -1
  146. package/dist/pwa/InstallAppSetting.js.map +1 -1
  147. package/dist/pwa/InstallInstructionsDialog.js +3 -1
  148. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  149. package/dist/pwa/InstallPromptBanner.js +3 -1
  150. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  151. package/dist/security/ActivityLog.js +3 -1
  152. package/dist/security/ActivityLog.js.map +1 -1
  153. package/dist/security/RecoveryCodesDialog.js +3 -1
  154. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  155. package/dist/security/SessionsCard.js +3 -1
  156. package/dist/security/SessionsCard.js.map +1 -1
  157. package/dist/theme/createToolkitTheme.js +4 -1
  158. package/dist/theme/createToolkitTheme.js.map +1 -1
  159. package/dist/toolkit/ComponentTexts.js +3 -1
  160. package/dist/toolkit/ComponentTexts.js.map +1 -1
  161. package/dist/toolkit/ToolkitProvider.js +24 -3
  162. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  163. package/dist/toolkit/toolkitContext.js.map +1 -1
  164. package/dist/toolkit/toolkitSettings.js +9 -0
  165. package/dist/toolkit/toolkitSettings.js.map +1 -1
  166. package/dist/toolkit/unknownProps.js +33 -0
  167. package/dist/toolkit/unknownProps.js.map +1 -0
  168. package/dist/types/DataTable/AuditTrailTable.d.ts +1 -1
  169. package/dist/types/DataTable/ColumnFilters/TextColumnFilter.d.ts +1 -1
  170. package/dist/types/DataTable/columns.d.ts +3 -1
  171. package/dist/types/access/Can.d.ts +1 -1
  172. package/dist/types/access/RequireAccess.d.ts +1 -1
  173. package/dist/types/account/AccountProvider.d.ts +1 -1
  174. package/dist/types/account/ForgotPasswordScreen.d.ts +1 -1
  175. package/dist/types/account/ResetPasswordScreen.d.ts +1 -1
  176. package/dist/types/appShell/MenuLinks.d.ts +1 -1
  177. package/dist/types/appShell/pageTitle.d.ts +1 -1
  178. package/dist/types/datePickers/DateInput.d.ts +1 -1
  179. package/dist/types/downloads/DownloadManager.d.ts +1 -1
  180. package/dist/types/formatting.d.ts +2 -1
  181. package/dist/types/forms/FormProblemAlert.d.ts +1 -1
  182. package/dist/types/forms/FormServerProblem.d.ts +1 -1
  183. package/dist/types/forms/FormWizard.d.ts +1 -1
  184. package/dist/types/forms/auth/ChangePasswordForm.d.ts +1 -1
  185. package/dist/types/forms/auth/ForgotPasswordForm.d.ts +1 -1
  186. package/dist/types/hookForm/FormProblemAlert.d.ts +1 -1
  187. package/dist/types/hookForm/FormWizard.d.ts +1 -1
  188. package/dist/types/index.d.ts +1 -1
  189. package/dist/types/inputs/CatalogInput.d.ts +1 -1
  190. package/dist/types/inputs/FieldFrame.d.ts +1 -1
  191. package/dist/types/inputs/NumberInput.d.ts +1 -1
  192. package/dist/types/inputs/TextInput.d.ts +2 -1
  193. package/dist/types/pwa/InstallAppSetting.d.ts +1 -1
  194. package/dist/types/theme/createToolkitTheme.d.ts +1 -1
  195. package/dist/types/toolkit/ComponentTexts.d.ts +1 -1
  196. package/dist/types/toolkit/ToolkitProvider.d.ts +4 -1
  197. package/dist/types/toolkit/toolkitContext.d.ts +2 -1
  198. package/dist/types/toolkit/toolkitSettings.d.ts +13 -2
  199. package/dist/types/toolkit/unknownProps.d.ts +23 -0
  200. package/dist/types/ui/actions/CopyButton.d.ts +1 -1
  201. package/dist/types/ui/actions/SecretField.d.ts +1 -1
  202. package/dist/types/ui/labels/DictionaryText.d.ts +1 -1
  203. package/dist/types/ui/layout/FullPageLoader.d.ts +1 -1
  204. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +9 -8
  205. package/dist/types/ui/metrics/chartParts.d.ts +3 -3
  206. package/dist/types/ui/metrics/heatmapScale.d.ts +23 -0
  207. package/dist/types/ui/navigation/DocumentTitle.d.ts +1 -1
  208. package/dist/ui/actions/ActionsMenu.js +3 -1
  209. package/dist/ui/actions/ActionsMenu.js.map +1 -1
  210. package/dist/ui/actions/CopyButton.js +3 -1
  211. package/dist/ui/actions/CopyButton.js.map +1 -1
  212. package/dist/ui/actions/DownloadMenu.js +4 -1
  213. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  214. package/dist/ui/actions/SecretField.js +3 -1
  215. package/dist/ui/actions/SecretField.js.map +1 -1
  216. package/dist/ui/actions/SelectionActions.js +3 -1
  217. package/dist/ui/actions/SelectionActions.js.map +1 -1
  218. package/dist/ui/filters/DateRangeFilter.js +3 -1
  219. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  220. package/dist/ui/filters/PeriodRangeSelector.js +3 -1
  221. package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
  222. package/dist/ui/filters/QuickFilters.js +3 -1
  223. package/dist/ui/filters/QuickFilters.js.map +1 -1
  224. package/dist/ui/filters/SegmentedControl.js +3 -1
  225. package/dist/ui/filters/SegmentedControl.js.map +1 -1
  226. package/dist/ui/labels/DictionaryText.js +3 -1
  227. package/dist/ui/labels/DictionaryText.js.map +1 -1
  228. package/dist/ui/labels/StatusChip.js +3 -1
  229. package/dist/ui/labels/StatusChip.js.map +1 -1
  230. package/dist/ui/layout/CollapsibleSection.js +3 -1
  231. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  232. package/dist/ui/layout/ContentDialog.js +3 -1
  233. package/dist/ui/layout/ContentDialog.js.map +1 -1
  234. package/dist/ui/layout/DefinitionList.js +3 -1
  235. package/dist/ui/layout/DefinitionList.js.map +1 -1
  236. package/dist/ui/layout/DetailHeader.js +3 -1
  237. package/dist/ui/layout/DetailHeader.js.map +1 -1
  238. package/dist/ui/layout/ErrorPage.js +4 -1
  239. package/dist/ui/layout/ErrorPage.js.map +1 -1
  240. package/dist/ui/layout/FullPageLoader.js +3 -1
  241. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  242. package/dist/ui/layout/PageHeader.js +3 -1
  243. package/dist/ui/layout/PageHeader.js.map +1 -1
  244. package/dist/ui/layout/PageHeading.js +3 -1
  245. package/dist/ui/layout/PageHeading.js.map +1 -1
  246. package/dist/ui/layout/ProfileHeader.js +3 -1
  247. package/dist/ui/layout/ProfileHeader.js.map +1 -1
  248. package/dist/ui/layout/SectionCard.js +3 -1
  249. package/dist/ui/layout/SectionCard.js.map +1 -1
  250. package/dist/ui/metrics/BarList.js +3 -1
  251. package/dist/ui/metrics/BarList.js.map +1 -1
  252. package/dist/ui/metrics/CalendarHeatmap.js +13 -4
  253. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  254. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  255. package/dist/ui/metrics/ColumnChart.js +4 -1
  256. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  257. package/dist/ui/metrics/LineChart.js +4 -1
  258. package/dist/ui/metrics/LineChart.js.map +1 -1
  259. package/dist/ui/metrics/MetricCard.js +4 -1
  260. package/dist/ui/metrics/MetricCard.js.map +1 -1
  261. package/dist/ui/metrics/TableToggle.js +3 -1
  262. package/dist/ui/metrics/TableToggle.js.map +1 -1
  263. package/dist/ui/metrics/chartParts.js +7 -3
  264. package/dist/ui/metrics/chartParts.js.map +1 -1
  265. package/dist/ui/metrics/heatmapScale.js +15 -1
  266. package/dist/ui/metrics/heatmapScale.js.map +1 -1
  267. package/dist/ui/navigation/ColorSchemeToggle.js +3 -1
  268. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  269. package/dist/ui/navigation/DocumentTitle.js +3 -1
  270. package/dist/ui/navigation/DocumentTitle.js.map +1 -1
  271. package/dist/ui/navigation/FontSizeToggle.js +3 -1
  272. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  273. package/dist/ui/navigation/LanguageToggle.js +3 -1
  274. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  275. package/dist/ui/navigation/PageBreadcrumbs.js +3 -1
  276. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  277. package/dist/ui/timeline/GroupedTimeline.js +3 -1
  278. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  279. package/dist/upload/FileDropzone.js +4 -1
  280. package/dist/upload/FileDropzone.js.map +1 -1
  281. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TabBarEditor.js","sources":["../../src/appShell/TabBarEditor.jsx"],"sourcesContent":["// Elegir qué pantallas van en la barra de pestañas del teléfono: una lista de todo el menú con un interruptor por renglón.\n// Sale dentro de la hoja de «Más» (MobileTabBar) al picar «Editar la barra».\n//\n// - Hasta PINNABLE_TAB_LIMIT (las que caben junto a «Más»); llenas, las demás se apagan hasta quitar una.\n// - Al menos una: el interruptor de la última no se puede apagar (sin ninguna, la barra volvería sola a las del proyecto\n// y parecería que no hizo caso).\n// - Lo elegido se guarda al picar «Listo»; «Como venía» regresa a las que eligió el proyecto.\n// - Van en el orden del menú: acomodarlas arrastrando sería otra decisión para la persona y otra cosa que probar.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTabBarEditor con defaultProps y\n// styleOverrides de sus partes: root, header, title, list, itemIcon, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Switch from '@mui/material/Switch';\nimport Typography from '@mui/material/Typography';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {PINNABLE_TAB_LIMIT, menuItemKeyOf} from '@/navigation/appMenu.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaTabBarEditor)\nconst THEME_NAME = 'CalcitaTabBarEditor';\nconst EditorRoot = piecePart(THEME_NAME, 'Root', Box);\nconst EditorHeader = piecePart(THEME_NAME, 'Header', Box);\nconst EditorTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst EditorList = piecePart(THEME_NAME, 'List', List);\nconst EditorItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\nconst EditorFooter = piecePart(THEME_NAME, 'Footer', Box);\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems todo el menú (lo que regresa composeMenu)\n * @param {string[]} props.pinnedKeys las que van hoy en la barra (menuItemKeyOf)\n * @param {(keys: string[]) => void} props.onDone con las elegidas, en el orden del menú\n * @param {() => void} props.onReset regresar a las del proyecto\n */\nexport default function TabBarEditor(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTabBarEditor.defaultProps) y encima lo que llega aquí\n const {menuItems, pinnedKeys, onDone, onReset} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('layout');\n const [chosenKeys, setChosenKeys] = useState(pinnedKeys);\n const isFull = chosenKeys.length >= PINNABLE_TAB_LIMIT;\n\n const toggle = (key, isOn) => setChosenKeys((current) => (isOn ? [...current, key] : current.filter((chosenKey) => chosenKey !== key)));\n // en el orden del menú, no en el que se fueron picando\n const chosenInMenuOrder = () => menuItems.map(menuItemKeyOf).filter((key) => chosenKeys.includes(key));\n\n return (\n <EditorRoot ownSx={{display: 'flex', flexDirection: 'column', flex: '1 1 auto', minHeight: 0}}>\n <EditorHeader ownSx={{px: 2, pt: 2, pb: 1, flexShrink: 0}}>\n <EditorTitle variant=\"subtitle1\" component=\"h2\" ownSx={{fontWeight: 600}}>{t('drawer.editTabs')}</EditorTitle>\n <Typography variant=\"body2\" color=\"text.secondary\">{t('drawer.editTabsHint', {count: PINNABLE_TAB_LIMIT})}</Typography>\n </EditorHeader>\n <EditorList dense ownSx={{overflowY: 'auto', flex: '1 1 auto', minHeight: 0, px: 1}}>\n {menuItems.map((menuItem) => {\n const key = menuItemKeyOf(menuItem);\n const label = menuItemLabelOf(menuItem, t);\n const isOn = chosenKeys.includes(key);\n const isLastOne = isOn && chosenKeys.length === 1;\n const switchId = `tab-bar-editor-${key}`;\n return (\n <ListItem key={key} secondaryAction={(\n <Switch edge=\"end\" checked={isOn} disabled={(!isOn && isFull) || isLastOne}\n onChange={(event) => toggle(key, event.target.checked)}\n slotProps={{input: {id: switchId, 'aria-label': t('drawer.tabInBar', {label})}}} />\n )}>\n {menuItem.Icon && <EditorItemIcon ownSx={{minWidth: 40}}><menuItem.Icon /></EditorItemIcon>}\n <ListItemText primary={<label htmlFor={switchId}>{label}</label>} />\n </ListItem>\n );\n })}\n </EditorList>\n <EditorFooter ownSx={{display: 'flex', justifyContent: 'space-between', gap: 1, px: 2, py: 1.5, flexShrink: 0}}>\n <Button onClick={onReset}>{t('drawer.resetTabs')}</Button>\n <Button variant=\"contained\" onClick={() => onDone(chosenInMenuOrder())}>{t('drawer.doneEditing')}</Button>\n </EditorFooter>\n </EditorRoot>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TabBarEditor.js","sources":["../../src/appShell/TabBarEditor.jsx"],"sourcesContent":["// Elegir qué pantallas van en la barra de pestañas del teléfono: una lista de todo el menú con un interruptor por renglón.\n// Sale dentro de la hoja de «Más» (MobileTabBar) al picar «Editar la barra».\n//\n// - Hasta PINNABLE_TAB_LIMIT (las que caben junto a «Más»); llenas, las demás se apagan hasta quitar una.\n// - Al menos una: el interruptor de la última no se puede apagar (sin ninguna, la barra volvería sola a las del proyecto\n// y parecería que no hizo caso).\n// - Lo elegido se guarda al picar «Listo»; «Como venía» regresa a las que eligió el proyecto.\n// - Van en el orden del menú: acomodarlas arrastrando sería otra decisión para la persona y otra cosa que probar.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTabBarEditor con defaultProps y\n// styleOverrides de sus partes: root, header, title, list, itemIcon, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Switch from '@mui/material/Switch';\nimport Typography from '@mui/material/Typography';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {PINNABLE_TAB_LIMIT, menuItemKeyOf} from '@/navigation/appMenu.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.CalcitaTabBarEditor)\nconst THEME_NAME = 'CalcitaTabBarEditor';\nconst EditorRoot = piecePart(THEME_NAME, 'Root', Box);\nconst EditorHeader = piecePart(THEME_NAME, 'Header', Box);\nconst EditorTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst EditorList = piecePart(THEME_NAME, 'List', List);\nconst EditorItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\nconst EditorFooter = piecePart(THEME_NAME, 'Footer', Box);\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems todo el menú (lo que regresa composeMenu)\n * @param {string[]} props.pinnedKeys las que van hoy en la barra (menuItemKeyOf)\n * @param {(keys: string[]) => void} props.onDone con las elegidas, en el orden del menú\n * @param {() => void} props.onReset regresar a las del proyecto\n */\nexport default function TabBarEditor(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTabBarEditor.defaultProps) y encima lo que llega aquí\n const {menuItems, pinnedKeys, onDone, onReset, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('layout');\n const [chosenKeys, setChosenKeys] = useState(pinnedKeys);\n const isFull = chosenKeys.length >= PINNABLE_TAB_LIMIT;\n\n const toggle = (key, isOn) => setChosenKeys((current) => (isOn ? [...current, key] : current.filter((chosenKey) => chosenKey !== key)));\n // en el orden del menú, no en el que se fueron picando\n const chosenInMenuOrder = () => menuItems.map(menuItemKeyOf).filter((key) => chosenKeys.includes(key));\n\n return (\n <EditorRoot ownSx={{display: 'flex', flexDirection: 'column', flex: '1 1 auto', minHeight: 0}}>\n <EditorHeader ownSx={{px: 2, pt: 2, pb: 1, flexShrink: 0}}>\n <EditorTitle variant=\"subtitle1\" component=\"h2\" ownSx={{fontWeight: 600}}>{t('drawer.editTabs')}</EditorTitle>\n <Typography variant=\"body2\" color=\"text.secondary\">{t('drawer.editTabsHint', {count: PINNABLE_TAB_LIMIT})}</Typography>\n </EditorHeader>\n <EditorList dense ownSx={{overflowY: 'auto', flex: '1 1 auto', minHeight: 0, px: 1}}>\n {menuItems.map((menuItem) => {\n const key = menuItemKeyOf(menuItem);\n const label = menuItemLabelOf(menuItem, t);\n const isOn = chosenKeys.includes(key);\n const isLastOne = isOn && chosenKeys.length === 1;\n const switchId = `tab-bar-editor-${key}`;\n return (\n <ListItem key={key} secondaryAction={(\n <Switch edge=\"end\" checked={isOn} disabled={(!isOn && isFull) || isLastOne}\n onChange={(event) => toggle(key, event.target.checked)}\n slotProps={{input: {id: switchId, 'aria-label': t('drawer.tabInBar', {label})}}} />\n )}>\n {menuItem.Icon && <EditorItemIcon ownSx={{minWidth: 40}}><menuItem.Icon /></EditorItemIcon>}\n <ListItemText primary={<label htmlFor={switchId}>{label}</label>} />\n </ListItem>\n );\n })}\n </EditorList>\n <EditorFooter ownSx={{display: 'flex', justifyContent: 'space-between', gap: 1, px: 2, py: 1.5, flexShrink: 0}}>\n <Button onClick={onReset}>{t('drawer.resetTabs')}</Button>\n <Button variant=\"contained\" onClick={() => onDone(chosenInMenuOrder())}>{t('drawer.doneEditing')}</Button>\n </EditorFooter>\n </EditorRoot>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -12,6 +12,7 @@ import MenuItem from "@mui/material/MenuItem";
12
12
  import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
13
13
  import { menuItemLabelOf, menuLinkPropsOf } from "./menuItemText.js";
14
14
  import { piecePart, usePieceProps } from "../theme/themePieces.js";
15
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
15
16
  const THEME_NAME = "CalcitaTopMenu";
16
17
  const TopMenuLink = piecePart(THEME_NAME, "Link", Button);
17
18
  const TopMenuRoot = piecePart(THEME_NAME, "Root", Box);
@@ -66,7 +67,8 @@ function TopMenuEntry({ menuItem, activeItem }) {
66
67
  ] });
67
68
  }
68
69
  function TopMenu(props) {
69
- const { menuItems, activeItem } = usePieceProps(props, THEME_NAME);
70
+ const { menuItems, activeItem, ...unknownProps } = usePieceProps(props, THEME_NAME);
71
+ warnUnknownProps(THEME_NAME, unknownProps);
70
72
  const { t } = useToolkitTranslation("layout");
71
73
  return /* @__PURE__ */ jsx(
72
74
  TopMenuRoot,
@@ -1 +1 @@
1
- {"version":3,"file":"TopMenu.js","sources":["../../src/appShell/TopMenu.jsx"],"sourcesContent":["// El menú dentro de la barra (menuPosition=\"top\"), para apps de pocos módulos: cada entrada es un botón; la que tiene\n// submenú abre una lista debajo. Las secciones no se pintan aquí (en una fila no hay dónde); en el teléfono el menú es\n// el mismo cajón del estilo lateral, con sus secciones.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTopMenu con defaultProps y\n// styleOverrides de sus partes: link, root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {menuItemLabelOf, menuLinkPropsOf} from '@/appShell/menuItemText.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaTopMenu)\nconst THEME_NAME = 'CalcitaTopMenu';\nconst TopMenuLink = piecePart(THEME_NAME, 'Link', Button);\nconst TopMenuRoot = piecePart(THEME_NAME, 'Root', Box);\n\n// la entrada abierta se marca con una raya abajo, del color principal (en la barra naranja, del color del texto)\nconst ACTIVE_UNDERLINE_PIXELS = 2;\n\n/**\n * Un botón del menú de arriba, con su lista si tiene submenú.\n *\n * @param {{menuItem: object, activeItem: object|null}} props\n */\nfunction TopMenuEntry({menuItem, activeItem}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const [anchorElement, setAnchorElement] = useState(null);\n const hasChildren = Array.isArray(menuItem.children) && menuItem.children.length > 0;\n const isActive = menuItem === activeItem || (hasChildren && menuItem.children.includes(activeItem));\n const Icon = menuItem.Icon;\n const buttonSx = {\n color: 'inherit', borderRadius: 1, px: 1.5, whiteSpace: 'nowrap', fontWeight: isActive ? 600 : 500,\n boxShadow: isActive ? `inset 0 -${ACTIVE_UNDERLINE_PIXELS}px 0 currentColor` : 'none',\n };\n\n if (!hasChildren) {\n return (\n <TopMenuLink {...menuLinkPropsOf(menuItem, RouterLink)} startIcon={Icon ? <Icon fontSize=\"small\" /> : undefined}\n aria-current={isActive ? 'page' : undefined} ownSx={buttonSx}>\n {menuItemLabelOf(menuItem, t)}\n </TopMenuLink>\n );\n }\n return (\n <>\n <TopMenuLink onClick={(event) => setAnchorElement(event.currentTarget)} aria-haspopup=\"menu\" aria-expanded={Boolean(anchorElement)}\n startIcon={Icon ? <Icon fontSize=\"small\" /> : undefined} endIcon={<ExpandMoreIcon fontSize=\"small\" />} ownSx={buttonSx}>\n {menuItemLabelOf(menuItem, t)}\n </TopMenuLink>\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={() => setAnchorElement(null)} slotProps={{paper: {sx: {minWidth: 220, mt: 0.5}}}}>\n {menuItem.children.map((child) => {\n const ChildIcon = child.Icon;\n return (\n <MenuItem key={child.to ?? child.href} {...menuLinkPropsOf(child, RouterLink)} selected={child === activeItem} onClick={() => setAnchorElement(null)}>\n {ChildIcon && <ListItemIcon><ChildIcon fontSize=\"small\" /></ListItemIcon>}\n <ListItemText primary={menuItemLabelOf(child, t)} />\n </MenuItem>\n );\n })}\n </Menu>\n </>\n );\n}\n\n/**\n * @param {{menuItems: object[], activeItem: object|null}} props\n */\nexport default function TopMenu(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTopMenu.defaultProps) y encima lo que llega aquí\n const {menuItems, activeItem} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('layout');\n return (\n <TopMenuRoot component=\"nav\" aria-label={t('drawer.ariaLabel')}\n ownSx={{display: {xs: 'none', md: 'flex'}, alignItems: 'stretch', alignSelf: 'stretch', gap: 0.5, minWidth: 0, overflowX: 'auto'}}>\n {menuItems.map((menuItem) => (\n <TopMenuEntry key={menuItem.to ?? menuItem.href ?? menuItemLabelOf(menuItem, t)} menuItem={menuItem} activeItem={activeItem} />\n ))}\n </TopMenuRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAuBA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,QAAQ,MAAM;AACxD,MAAM,cAAc,UAAU,YAAY,QAAQ,GAAG;AAGrD,MAAM,0BAA0B;AAOhC,SAAS,aAAa,EAAC,UAAU,cAAa;AAC1C,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AACvD,QAAM,cAAc,MAAM,QAAQ,SAAS,QAAQ,KAAK,SAAS,SAAS,SAAS;AACnF,QAAM,WAAW,aAAa,cAAe,eAAe,SAAS,SAAS,SAAS,UAAU;AACjG,QAAM,OAAO,SAAS;AACtB,QAAM,WAAW;AAAA,IACb,OAAO;AAAA,IAAW,cAAc;AAAA,IAAG,IAAI;AAAA,IAAK,YAAY;AAAA,IAAU,YAAY,WAAW,MAAM;AAAA,IAC/F,WAAW,WAAW,YAAY,uBAAuB,sBAAsB;AAAA,EAAA;AAGnF,MAAI,CAAC,aAAa;AACd,WACI;AAAA,MAAC;AAAA,MAAA;AAAA,QAAa,GAAG,gBAAgB,UAAU,UAAU;AAAA,QAAG,WAAW,OAAO,oBAAC,MAAA,EAAK,UAAS,SAAQ,IAAK;AAAA,QAC9F,gBAAc,WAAW,SAAS;AAAA,QAAW,OAAO;AAAA,QACvD,UAAA,gBAAgB,UAAU,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAGxC;AACA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAY,SAAS,CAAC,UAAU,iBAAiB,MAAM,aAAa;AAAA,QAAG,iBAAc;AAAA,QAAO,iBAAe,QAAQ,aAAa;AAAA,QACzH,WAAW,OAAO,oBAAC,MAAA,EAAK,UAAS,SAAQ,IAAK;AAAA,QAAW,SAAS,oBAAC,gBAAA,EAAe,UAAS,QAAA,CAAQ;AAAA,QAAI,OAAO;AAAA,QACjH,UAAA,gBAAgB,UAAU,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,IAEhC,oBAAC,MAAA,EAAK,UAAU,eAAe,MAAM,QAAQ,aAAa,GAAG,SAAS,MAAM,iBAAiB,IAAI,GAAG,WAAW,EAAC,OAAO,EAAC,IAAI,EAAC,UAAU,KAAK,IAAI,IAAA,IAAI,GAC/I,UAAA,SAAS,SAAS,IAAI,CAAC,UAAU;AAC9B,YAAM,YAAY,MAAM;AACxB,aACI,qBAAC,UAAA,EAAuC,GAAG,gBAAgB,OAAO,UAAU,GAAG,UAAU,UAAU,YAAY,SAAS,MAAM,iBAAiB,IAAI,GAC9I,UAAA;AAAA,QAAA,iCAAc,cAAA,EAAa,UAAA,oBAAC,WAAA,EAAU,UAAS,SAAQ,GAAE;AAAA,4BACzD,cAAA,EAAa,SAAS,gBAAgB,OAAO,CAAC,EAAA,CAAG;AAAA,MAAA,EAAA,GAFvC,MAAM,MAAM,MAAM,IAGjC;AAAA,IAER,CAAC,EAAA,CACL;AAAA,EAAA,GACJ;AAER;AAKA,SAAwB,QAAQ,OAAO;AAEnC,QAAM,EAAC,WAAW,WAAA,IAAc,cAAc,OAAO,UAAU;AAC/D,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAY,WAAU;AAAA,MAAM,cAAY,EAAE,kBAAkB;AAAA,MACxD,OAAO,EAAC,SAAS,EAAC,IAAI,QAAQ,IAAI,OAAA,GAAS,YAAY,WAAW,WAAW,WAAW,KAAK,KAAK,UAAU,GAAG,WAAW,OAAA;AAAA,MAC1H,oBAAU,IAAI,CAAC,aACZ,oBAAC,gBAAgF,UAAoB,WAAA,GAAlF,SAAS,MAAM,SAAS,QAAQ,gBAAgB,UAAU,CAAC,CAA+C,CAChI;AAAA,IAAA;AAAA,EAAA;AAGb;"}
1
+ {"version":3,"file":"TopMenu.js","sources":["../../src/appShell/TopMenu.jsx"],"sourcesContent":["// El menú dentro de la barra (menuPosition=\"top\"), para apps de pocos módulos: cada entrada es un botón; la que tiene\n// submenú abre una lista debajo. Las secciones no se pintan aquí (en una fila no hay dónde); en el teléfono el menú es\n// el mismo cajón del estilo lateral, con sus secciones.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTopMenu con defaultProps y\n// styleOverrides de sus partes: link, root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {menuItemLabelOf, menuLinkPropsOf} from '@/appShell/menuItemText.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.CalcitaTopMenu)\nconst THEME_NAME = 'CalcitaTopMenu';\nconst TopMenuLink = piecePart(THEME_NAME, 'Link', Button);\nconst TopMenuRoot = piecePart(THEME_NAME, 'Root', Box);\n\n// la entrada abierta se marca con una raya abajo, del color principal (en la barra naranja, del color del texto)\nconst ACTIVE_UNDERLINE_PIXELS = 2;\n\n/**\n * Un botón del menú de arriba, con su lista si tiene submenú.\n *\n * @param {{menuItem: object, activeItem: object|null}} props\n */\nfunction TopMenuEntry({menuItem, activeItem}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const [anchorElement, setAnchorElement] = useState(null);\n const hasChildren = Array.isArray(menuItem.children) && menuItem.children.length > 0;\n const isActive = menuItem === activeItem || (hasChildren && menuItem.children.includes(activeItem));\n const Icon = menuItem.Icon;\n const buttonSx = {\n color: 'inherit', borderRadius: 1, px: 1.5, whiteSpace: 'nowrap', fontWeight: isActive ? 600 : 500,\n boxShadow: isActive ? `inset 0 -${ACTIVE_UNDERLINE_PIXELS}px 0 currentColor` : 'none',\n };\n\n if (!hasChildren) {\n return (\n <TopMenuLink {...menuLinkPropsOf(menuItem, RouterLink)} startIcon={Icon ? <Icon fontSize=\"small\" /> : undefined}\n aria-current={isActive ? 'page' : undefined} ownSx={buttonSx}>\n {menuItemLabelOf(menuItem, t)}\n </TopMenuLink>\n );\n }\n return (\n <>\n <TopMenuLink onClick={(event) => setAnchorElement(event.currentTarget)} aria-haspopup=\"menu\" aria-expanded={Boolean(anchorElement)}\n startIcon={Icon ? <Icon fontSize=\"small\" /> : undefined} endIcon={<ExpandMoreIcon fontSize=\"small\" />} ownSx={buttonSx}>\n {menuItemLabelOf(menuItem, t)}\n </TopMenuLink>\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={() => setAnchorElement(null)} slotProps={{paper: {sx: {minWidth: 220, mt: 0.5}}}}>\n {menuItem.children.map((child) => {\n const ChildIcon = child.Icon;\n return (\n <MenuItem key={child.to ?? child.href} {...menuLinkPropsOf(child, RouterLink)} selected={child === activeItem} onClick={() => setAnchorElement(null)}>\n {ChildIcon && <ListItemIcon><ChildIcon fontSize=\"small\" /></ListItemIcon>}\n <ListItemText primary={menuItemLabelOf(child, t)} />\n </MenuItem>\n );\n })}\n </Menu>\n </>\n );\n}\n\n/**\n * @param {{menuItems: object[], activeItem: object|null}} props\n */\nexport default function TopMenu(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTopMenu.defaultProps) y encima lo que llega aquí\n const {menuItems, activeItem, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('layout');\n return (\n <TopMenuRoot component=\"nav\" aria-label={t('drawer.ariaLabel')}\n ownSx={{display: {xs: 'none', md: 'flex'}, alignItems: 'stretch', alignSelf: 'stretch', gap: 0.5, minWidth: 0, overflowX: 'auto'}}>\n {menuItems.map((menuItem) => (\n <TopMenuEntry key={menuItem.to ?? menuItem.href ?? menuItemLabelOf(menuItem, t)} menuItem={menuItem} activeItem={activeItem} />\n ))}\n </TopMenuRoot>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { isValidElement } from "react";
4
4
  import Box from "@mui/material/Box";
5
5
  import { piecePart, usePieceProps } from "../theme/themePieces.js";
6
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
6
7
  const THEME_NAME = "CalcitaWordmark";
7
8
  const WordmarkRoot = piecePart(THEME_NAME, "Root", Box);
8
9
  const WordmarkMode = piecePart(THEME_NAME, "Mode", "span");
@@ -11,7 +12,8 @@ function isWordmarkPair(wordmark) {
11
12
  return wordmark !== null && typeof wordmark === "object" && !isValidElement(wordmark) && !Array.isArray(wordmark) && ("light" in wordmark || "dark" in wordmark);
12
13
  }
13
14
  function Wordmark(props) {
14
- const { children } = usePieceProps(props, THEME_NAME);
15
+ const { children, ...unknownProps } = usePieceProps(props, THEME_NAME);
16
+ warnUnknownProps(THEME_NAME, unknownProps);
15
17
  return /* @__PURE__ */ jsx(WordmarkRoot, { ownSx: {
16
18
  height: WORDMARK_HEIGHT_PIXELS,
17
19
  display: "flex",
@@ -1 +1 @@
1
- {"version":3,"file":"Wordmark.js","sources":["../../src/appShell/Wordmark.jsx"],"sourcesContent":["// El logo ancho de la app (la marca con su nombre en una sola imagen, como el de muchas empresas) en la barra y arriba del\n// menú del teléfono: a 24 px de alto y lo ancho que pida, sin deformarse. Una imagen o un svg toman ese alto solos.\n//\n// Con una imagen para cada modo ({light: <img …/>, dark: <img …/>}: el de color en claro y el blanco en oscuro) se pintan\n// las dos y el modo decide cuál se ve, sin parpadeo al cambiar ni al cargar.\n//\n// OJO, trampa pagada (la prueba en un sistema que ya existía): la regla de la imagen («height 100 %, display block») iba\n// como «& > img» y le ganaba al sx de la imagen del proyecto; quien escondía una de sus dos imágenes con CSS no podía. Ahora\n// va con :where(), que no suma peso: lo que el proyecto le ponga a su imagen gana.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWordmark con defaultProps y\n// styleOverrides de sus partes: root y mode (la imagen de cada modo).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {isValidElement} from 'react';\nimport Box from '@mui/material/Box';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWordmark)\nconst THEME_NAME = 'CalcitaWordmark';\nconst WordmarkRoot = piecePart(THEME_NAME, 'Root', Box);\nconst WordmarkMode = piecePart(THEME_NAME, 'Mode', 'span');\n\n// el alto del logo ancho: el de la letra del nombre con su renglón, para que la barra no crezca\nconst WORDMARK_HEIGHT_PIXELS = 24;\n\n/**\n * ¿Llegó una imagen para cada modo ({light, dark}) y no un logo solo?\n *\n * @param {unknown} wordmark\n * @returns {boolean}\n */\nexport function isWordmarkPair(wordmark) {\n return wordmark !== null && typeof wordmark === 'object' && !isValidElement(wordmark) && !Array.isArray(wordmark)\n && ('light' in wordmark || 'dark' in wordmark);\n}\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode | {light?: import('react').ReactNode, dark?: import('react').ReactNode}} props.children el\n * logo (una <img>, un <svg> o lo que pinte el proyecto) o uno para cada modo: {light, dark}\n */\nexport default function Wordmark(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWordmark.defaultProps) y encima lo que llega aquí\n const {children} = usePieceProps(props, THEME_NAME);\n return (\n <WordmarkRoot ownSx={{\n height: WORDMARK_HEIGHT_PIXELS, display: 'flex', alignItems: 'center', flex: '0 0 auto',\n '& :where(img, svg)': {height: '100%', width: 'auto', display: 'block'},\n }}>\n {isWordmarkPair(children) ? (\n <>\n {/* las dos se pintan y el modo decide cuál se ve: display contents deja a la imagen tomar el alto */}\n <WordmarkMode ownSx={(theme) => ({display: 'contents', ...theme.applyStyles('dark', {display: 'none'})})}>{children.light ?? children.dark}</WordmarkMode>\n <WordmarkMode ownSx={(theme) => ({display: 'none', ...theme.applyStyles('dark', {display: 'contents'})})}>{children.dark ?? children.light}</WordmarkMode>\n </>\n ) : children}\n </WordmarkRoot>\n );\n}\n"],"names":[],"mappings":";;;;;AAmBA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,QAAQ,GAAG;AACtD,MAAM,eAAe,UAAU,YAAY,QAAQ,MAAM;AAGzD,MAAM,yBAAyB;AAQxB,SAAS,eAAe,UAAU;AACrC,SAAO,aAAa,QAAQ,OAAO,aAAa,YAAY,CAAC,eAAe,QAAQ,KAAK,CAAC,MAAM,QAAQ,QAAQ,MACxG,WAAW,YAAY,UAAU;AAC7C;AAOA,SAAwB,SAAS,OAAO;AAEpC,QAAM,EAAC,SAAA,IAAY,cAAc,OAAO,UAAU;AAClD,SACI,oBAAC,gBAAa,OAAO;AAAA,IACjB,QAAQ;AAAA,IAAwB,SAAS;AAAA,IAAQ,YAAY;AAAA,IAAU,MAAM;AAAA,IAC7E,sBAAsB,EAAC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,QAAA;AAAA,EAAO,GAErE,UAAA,eAAe,QAAQ,IACpB,qBAAA,UAAA,EAEI,UAAA;AAAA,IAAA,oBAAC,cAAA,EAAa,OAAO,CAAC,WAAW,EAAC,SAAS,YAAY,GAAG,MAAM,YAAY,QAAQ,EAAC,SAAS,QAAO,MAAM,UAAA,SAAS,SAAS,SAAS,MAAK;AAAA,IAC3I,oBAAC,gBAAa,OAAO,CAAC,WAAW,EAAC,SAAS,QAAQ,GAAG,MAAM,YAAY,QAAQ,EAAC,SAAS,WAAA,CAAW,MAAM,UAAA,SAAS,QAAQ,SAAS,MAAA,CAAM;AAAA,EAAA,EAAA,CAC/I,IACA,UACR;AAER;"}
1
+ {"version":3,"file":"Wordmark.js","sources":["../../src/appShell/Wordmark.jsx"],"sourcesContent":["// El logo ancho de la app (la marca con su nombre en una sola imagen, como el de muchas empresas) en la barra y arriba del\n// menú del teléfono: a 24 px de alto y lo ancho que pida, sin deformarse. Una imagen o un svg toman ese alto solos.\n//\n// Con una imagen para cada modo ({light: <img …/>, dark: <img …/>}: el de color en claro y el blanco en oscuro) se pintan\n// las dos y el modo decide cuál se ve, sin parpadeo al cambiar ni al cargar.\n//\n// OJO, trampa pagada (la prueba en un sistema que ya existía): la regla de la imagen («height 100 %, display block») iba\n// como «& > img» y le ganaba al sx de la imagen del proyecto; quien escondía una de sus dos imágenes con CSS no podía. Ahora\n// va con :where(), que no suma peso: lo que el proyecto le ponga a su imagen gana.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWordmark con defaultProps y\n// styleOverrides de sus partes: root y mode (la imagen de cada modo).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {isValidElement} from 'react';\nimport Box from '@mui/material/Box';\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.CalcitaWordmark)\nconst THEME_NAME = 'CalcitaWordmark';\nconst WordmarkRoot = piecePart(THEME_NAME, 'Root', Box);\nconst WordmarkMode = piecePart(THEME_NAME, 'Mode', 'span');\n\n// el alto del logo ancho: el de la letra del nombre con su renglón, para que la barra no crezca\nconst WORDMARK_HEIGHT_PIXELS = 24;\n\n/**\n * ¿Llegó una imagen para cada modo ({light, dark}) y no un logo solo?\n *\n * @param {unknown} wordmark\n * @returns {boolean}\n */\nexport function isWordmarkPair(wordmark) {\n return wordmark !== null && typeof wordmark === 'object' && !isValidElement(wordmark) && !Array.isArray(wordmark)\n && ('light' in wordmark || 'dark' in wordmark);\n}\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode | {light?: import('react').ReactNode, dark?: import('react').ReactNode}} props.children el\n * logo (una <img>, un <svg> o lo que pinte el proyecto) o uno para cada modo: {light, dark}\n */\nexport default function Wordmark(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWordmark.defaultProps) y encima lo que llega aquí\n const {children, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n return (\n <WordmarkRoot ownSx={{\n height: WORDMARK_HEIGHT_PIXELS, display: 'flex', alignItems: 'center', flex: '0 0 auto',\n '& :where(img, svg)': {height: '100%', width: 'auto', display: 'block'},\n }}>\n {isWordmarkPair(children) ? (\n <>\n {/* las dos se pintan y el modo decide cuál se ve: display contents deja a la imagen tomar el alto */}\n <WordmarkMode ownSx={(theme) => ({display: 'contents', ...theme.applyStyles('dark', {display: 'none'})})}>{children.light ?? children.dark}</WordmarkMode>\n <WordmarkMode ownSx={(theme) => ({display: 'none', ...theme.applyStyles('dark', {display: 'contents'})})}>{children.dark ?? children.light}</WordmarkMode>\n </>\n ) : children}\n </WordmarkRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,QAAQ,GAAG;AACtD,MAAM,eAAe,UAAU,YAAY,QAAQ,MAAM;AAGzD,MAAM,yBAAyB;AAQxB,SAAS,eAAe,UAAU;AACrC,SAAO,aAAa,QAAQ,OAAO,aAAa,YAAY,CAAC,eAAe,QAAQ,KAAK,CAAC,MAAM,QAAQ,QAAQ,MACxG,WAAW,YAAY,UAAU;AAC7C;AAOA,SAAwB,SAAS,OAAO;AAEpC,QAAM,EAAC,UAAU,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACnE,mBAAiB,YAAY,YAAY;AACzC,SACI,oBAAC,gBAAa,OAAO;AAAA,IACjB,QAAQ;AAAA,IAAwB,SAAS;AAAA,IAAQ,YAAY;AAAA,IAAU,MAAM;AAAA,IAC7E,sBAAsB,EAAC,QAAQ,QAAQ,OAAO,QAAQ,SAAS,QAAA;AAAA,EAAO,GAErE,UAAA,eAAe,QAAQ,IACpB,qBAAA,UAAA,EAEI,UAAA;AAAA,IAAA,oBAAC,cAAA,EAAa,OAAO,CAAC,WAAW,EAAC,SAAS,YAAY,GAAG,MAAM,YAAY,QAAQ,EAAC,SAAS,QAAO,MAAM,UAAA,SAAS,SAAS,SAAS,MAAK;AAAA,IAC3I,oBAAC,gBAAa,OAAO,CAAC,WAAW,EAAC,SAAS,QAAQ,GAAG,MAAM,YAAY,QAAQ,EAAC,SAAS,WAAA,CAAW,MAAM,UAAA,SAAS,QAAQ,SAAS,MAAA,CAAM;AAAA,EAAA,EAAA,CAC/I,IACA,UACR;AAER;"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { createContext, useEffect, useContext, useState, useMemo } from "react";
4
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
4
5
  const PageTitleContext = createContext({
5
6
  title: null,
6
7
  setTitle: () => {
@@ -9,7 +10,8 @@ const PageTitleContext = createContext({
9
10
  setTitleVisible: () => {
10
11
  }
11
12
  });
12
- function PageTitleProvider({ children }) {
13
+ function PageTitleProvider({ children, ...unknownProps }) {
14
+ warnUnknownProps("PageTitleProvider", unknownProps);
13
15
  const [title, setTitle] = useState(null);
14
16
  const [isTitleVisible, setTitleVisible] = useState(true);
15
17
  const value = useMemo(() => ({ title, setTitle, isTitleVisible, setTitleVisible }), [title, isTitleVisible]);
@@ -1 +1 @@
1
- {"version":3,"file":"pageTitle.js","sources":["../../src/appShell/pageTitle.jsx"],"sourcesContent":["// El título de la pantalla abierta, para la barra de arriba (del marco de walk-ot, como Linear):\n// - en escritorio, la barra enseña el título SOLO cuando el título grande de la página ya salió de la vista al deslizar;\n// - en el teléfono, la barra siempre enseña el título (no hay lugar para un encabezado grande y una barra vacía).\n// PageHeader lo registra solo; AppLayout trae el proveedor. Fuera de AppLayout no pasa nada (el contexto por omisión no\n// hace nada).\n//\n// Proveedor y hooks genéricos del paquete: function (regla de la casa).\nimport {createContext, useContext, useEffect, useMemo, useState} from 'react';\n\nconst PageTitleContext = createContext({\n title: null,\n setTitle: () => {},\n isTitleVisible: true,\n setTitleVisible: () => {},\n});\n\n/**\n * @param {{children: import('react').ReactNode}} props\n */\nexport function PageTitleProvider({children}) {\n const [title, setTitle] = useState(null);\n const [isTitleVisible, setTitleVisible] = useState(true);\n const value = useMemo(() => ({title, setTitle, isTitleVisible, setTitleVisible}), [title, isTitleVisible]);\n return <PageTitleContext.Provider value={value}>{children}</PageTitleContext.Provider>;\n}\n\n/**\n * El título registrado y si se está viendo.\n *\n * @returns {{title: string|null, setTitle: Function, isTitleVisible: boolean, setTitleVisible: Function}}\n */\nexport function usePageTitle() {\n return useContext(PageTitleContext);\n}\n\n/**\n * Registra el título de la página y avisa si su elemento se está viendo (para la barra).\n *\n * @param {string|null|undefined} title\n * @param {{current: Element|null}} titleRef el elemento del título grande\n */\nexport function useRegisterPageTitle(title, titleRef) {\n const {setTitle, setTitleVisible} = usePageTitle();\n\n useEffect(() => {\n setTitle(title ?? null);\n return () => setTitle(null);\n }, [title, setTitle]);\n\n useEffect(() => {\n const titleElement = titleRef.current;\n // guard clause: sin elemento o sin IntersectionObserver (pintando en el servidor) no hay nada que observar\n if (!titleElement || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) => setTitleVisible(entry.isIntersecting), {threshold: 0});\n observer.observe(titleElement);\n return () => {\n observer.disconnect();\n setTitleVisible(true);\n };\n }, [titleRef, setTitleVisible]);\n}\n"],"names":[],"mappings":";;;AASA,MAAM,mBAAmB,cAAc;AAAA,EACnC,OAAO;AAAA,EACP,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,gBAAgB;AAAA,EAChB,iBAAiB,MAAM;AAAA,EAAC;AAC5B,CAAC;AAKM,SAAS,kBAAkB,EAAC,YAAW;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,IAAI;AACvC,QAAM,CAAC,gBAAgB,eAAe,IAAI,SAAS,IAAI;AACvD,QAAM,QAAQ,QAAQ,OAAO,EAAC,OAAO,UAAU,gBAAgB,oBAAmB,CAAC,OAAO,cAAc,CAAC;AACzG,SAAO,oBAAC,iBAAiB,UAAjB,EAA0B,OAAe,SAAA,CAAS;AAC9D;AAOO,SAAS,eAAe;AAC3B,SAAO,WAAW,gBAAgB;AACtC;AAQO,SAAS,qBAAqB,OAAO,UAAU;AAClD,QAAM,EAAC,UAAU,gBAAA,IAAmB,aAAA;AAEpC,YAAU,MAAM;AACZ,aAAS,SAAS,IAAI;AACtB,WAAO,MAAM,SAAS,IAAI;AAAA,EAC9B,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,YAAU,MAAM;AACZ,UAAM,eAAe,SAAS;AAE9B,QAAI,CAAC,gBAAgB,OAAO,yBAAyB,aAAa;AAC9D,aAAO;AAAA,IACX;AACA,UAAM,WAAW,IAAI,qBAAqB,CAAC,CAAC,KAAK,MAAM,gBAAgB,MAAM,cAAc,GAAG,EAAC,WAAW,GAAE;AAC5G,aAAS,QAAQ,YAAY;AAC7B,WAAO,MAAM;AACT,eAAS,WAAA;AACT,sBAAgB,IAAI;AAAA,IACxB;AAAA,EACJ,GAAG,CAAC,UAAU,eAAe,CAAC;AAClC;"}
1
+ {"version":3,"file":"pageTitle.js","sources":["../../src/appShell/pageTitle.jsx"],"sourcesContent":["// El título de la pantalla abierta, para la barra de arriba (del marco de walk-ot, como Linear):\n// - en escritorio, la barra enseña el título SOLO cuando el título grande de la página ya salió de la vista al deslizar;\n// - en el teléfono, la barra siempre enseña el título (no hay lugar para un encabezado grande y una barra vacía).\n// PageHeader lo registra solo; AppLayout trae el proveedor. Fuera de AppLayout no pasa nada (el contexto por omisión no\n// hace nada).\n//\n// Proveedor y hooks genéricos del paquete: function (regla de la casa).\nimport {createContext, useContext, useEffect, useMemo, useState} from 'react';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\nconst PageTitleContext = createContext({\n title: null,\n setTitle: () => {},\n isTitleVisible: true,\n setTitleVisible: () => {},\n});\n\n/**\n * @param {{children: import('react').ReactNode}} props\n */\nexport function PageTitleProvider({children, ...unknownProps}) {\n warnUnknownProps('PageTitleProvider', unknownProps);\n const [title, setTitle] = useState(null);\n const [isTitleVisible, setTitleVisible] = useState(true);\n const value = useMemo(() => ({title, setTitle, isTitleVisible, setTitleVisible}), [title, isTitleVisible]);\n return <PageTitleContext.Provider value={value}>{children}</PageTitleContext.Provider>;\n}\n\n/**\n * El título registrado y si se está viendo.\n *\n * @returns {{title: string|null, setTitle: Function, isTitleVisible: boolean, setTitleVisible: Function}}\n */\nexport function usePageTitle() {\n return useContext(PageTitleContext);\n}\n\n/**\n * Registra el título de la página y avisa si su elemento se está viendo (para la barra).\n *\n * @param {string|null|undefined} title\n * @param {{current: Element|null}} titleRef el elemento del título grande\n */\nexport function useRegisterPageTitle(title, titleRef) {\n const {setTitle, setTitleVisible} = usePageTitle();\n\n useEffect(() => {\n setTitle(title ?? null);\n return () => setTitle(null);\n }, [title, setTitle]);\n\n useEffect(() => {\n const titleElement = titleRef.current;\n // guard clause: sin elemento o sin IntersectionObserver (pintando en el servidor) no hay nada que observar\n if (!titleElement || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) => setTitleVisible(entry.isIntersecting), {threshold: 0});\n observer.observe(titleElement);\n return () => {\n observer.disconnect();\n setTitleVisible(true);\n };\n }, [titleRef, setTitleVisible]);\n}\n"],"names":[],"mappings":";;;;AAUA,MAAM,mBAAmB,cAAc;AAAA,EACnC,OAAO;AAAA,EACP,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,gBAAgB;AAAA,EAChB,iBAAiB,MAAM;AAAA,EAAC;AAC5B,CAAC;AAKM,SAAS,kBAAkB,EAAC,UAAU,GAAG,gBAAe;AAC3D,mBAAiB,qBAAqB,YAAY;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,IAAI;AACvC,QAAM,CAAC,gBAAgB,eAAe,IAAI,SAAS,IAAI;AACvD,QAAM,QAAQ,QAAQ,OAAO,EAAC,OAAO,UAAU,gBAAgB,oBAAmB,CAAC,OAAO,cAAc,CAAC;AACzG,SAAO,oBAAC,iBAAiB,UAAjB,EAA0B,OAAe,SAAA,CAAS;AAC9D;AAOO,SAAS,eAAe;AAC3B,SAAO,WAAW,gBAAgB;AACtC;AAQO,SAAS,qBAAqB,OAAO,UAAU;AAClD,QAAM,EAAC,UAAU,gBAAA,IAAmB,aAAA;AAEpC,YAAU,MAAM;AACZ,aAAS,SAAS,IAAI;AACtB,WAAO,MAAM,SAAS,IAAI;AAAA,EAC9B,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,YAAU,MAAM;AACZ,UAAM,eAAe,SAAS;AAE9B,QAAI,CAAC,gBAAgB,OAAO,yBAAyB,aAAa;AAC9D,aAAO;AAAA,IACX;AACA,UAAM,WAAW,IAAI,qBAAqB,CAAC,CAAC,KAAK,MAAM,gBAAgB,MAAM,cAAc,GAAG,EAAC,WAAW,GAAE;AAC5G,aAAS,QAAQ,YAAY;AAC7B,WAAO,MAAM;AACT,eAAS,WAAA;AACT,sBAAgB,IAAI;AAAA,IACxB;AAAA,EACJ,GAAG,CAAC,UAAU,eAAe,CAAC;AAClC;"}
@@ -7,6 +7,7 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle";
7
7
  import RadioButtonUncheckedIcon from "@mui/icons-material/RadioButtonUnchecked";
8
8
  import { DEFAULT_PASSWORD_POLICY, passwordChecksOf } from "./passwordRules.js";
9
9
  import { piecePart, usePieceProps } from "../theme/themePieces.js";
10
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
10
11
  const THEME_NAME = "CalcitaPasswordChecklist";
11
12
  const ChecklistTitle = piecePart(THEME_NAME, "Title", Typography);
12
13
  const ChecklistList = piecePart(THEME_NAME, "List", Box);
@@ -24,7 +25,8 @@ const SCREEN_READER_ONLY_SX = {
24
25
  border: 0
25
26
  };
26
27
  function PasswordChecklist(props) {
27
- const { password, policy = DEFAULT_PASSWORD_POLICY, id, texts } = usePieceProps(props, THEME_NAME);
28
+ const { password, policy = DEFAULT_PASSWORD_POLICY, id, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
29
+ warnUnknownProps(THEME_NAME, unknownProps);
28
30
  const { t } = useToolkitTranslation("auth", texts);
29
31
  return /* @__PURE__ */ jsxs(Box, { id, children: [
30
32
  /* @__PURE__ */ jsx(ChecklistTitle, { variant: "caption", color: "text.secondary", component: "p", ownSx: { mb: 0.5 }, children: t("passwordRules.title") }),
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordChecklist.js","sources":["../../src/auth/PasswordChecklist.jsx"],"sourcesContent":["// La lista de lo que debe tener una contraseña nueva, que se va palomeando mientras la persona escribe: dice qué falta\n// ANTES de enviar, en lugar de un «contraseña inválida» después.\n//\n// Para el lector de pantalla cada renglón dice si ya cumple o falta (con el ícono solo, se oía la regla sin saber cómo\n// iba). Va justo debajo del campo de la contraseña nueva.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPasswordChecklist con defaultProps y\n// styleOverrides de sus partes: title, list, item, hidden.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';\n\nimport {DEFAULT_PASSWORD_POLICY, passwordChecksOf} from '@/auth/passwordRules.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPasswordChecklist)\nconst THEME_NAME = 'CalcitaPasswordChecklist';\nconst ChecklistTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst ChecklistList = piecePart(THEME_NAME, 'List', Box);\nconst ChecklistItem = piecePart(THEME_NAME, 'Item', Box);\nconst ChecklistHidden = piecePart(THEME_NAME, 'Hidden', Box);\n\n// texto que solo oye el lector de pantalla (el mismo truco que usa MUI)\nconst SCREEN_READER_ONLY_SX = {\n position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap', border: 0,\n};\n\n/**\n * @param {object} props\n * @param {string} props.password lo que lleva escrito la persona\n * @param {object} [props.policy] las reglas; por omisión DEFAULT_PASSWORD_POLICY\n * @param {string} [props.id]\n * @param {object} [props.texts] los textos que cambian, con la forma de `auth` en los textos del paquete\n */\nexport default function PasswordChecklist(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPasswordChecklist.defaultProps) y encima lo que llega aquí\n const {password, policy = DEFAULT_PASSWORD_POLICY, id, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('auth', texts);\n return (\n <Box id={id}>\n <ChecklistTitle variant=\"caption\" color=\"text.secondary\" component=\"p\" ownSx={{mb: 0.5}}>{t('passwordRules.title')}</ChecklistTitle>\n <ChecklistList component=\"ul\" ownSx={{listStyle: 'none', m: 0, p: 0, display: 'grid', gap: 0.5}}>\n {passwordChecksOf(password, policy).map((check) => (\n <ChecklistItem component=\"li\" key={check.rule} data-rule={check.rule} data-met={check.isMet}\n ownSx={{display: 'flex', alignItems: 'center', gap: 1, color: check.isMet ? 'text.primary' : 'text.secondary'}}>\n {check.isMet\n ? <CheckCircleIcon fontSize=\"small\" color=\"success\" aria-hidden />\n : <RadioButtonUncheckedIcon fontSize=\"small\" color=\"disabled\" aria-hidden />}\n <Typography variant=\"body2\" component=\"span\">{t(`passwordRules.${check.rule}`, check.values)}</Typography>\n <ChecklistHidden component=\"span\" ownSx={SCREEN_READER_ONLY_SX}>{check.isMet ? t('passwordRules.met') : t('passwordRules.missing')}</ChecklistHidden>\n </ChecklistItem>\n ))}\n </ChecklistList>\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,SAAS,UAAU;AAChE,MAAM,gBAAgB,UAAU,YAAY,QAAQ,GAAG;AACvD,MAAM,gBAAgB,UAAU,YAAY,QAAQ,GAAG;AACvD,MAAM,kBAAkB,UAAU,YAAY,UAAU,GAAG;AAG3D,MAAM,wBAAwB;AAAA,EAC1B,UAAU;AAAA,EAAY,OAAO;AAAA,EAAO,QAAQ;AAAA,EAAO,SAAS;AAAA,EAAG,QAAQ;AAAA,EAAQ,UAAU;AAAA,EAAU,MAAM;AAAA,EAAiB,YAAY;AAAA,EAAU,QAAQ;AAC5J;AASA,SAAwB,kBAAkB,OAAO;AAE7C,QAAM,EAAC,UAAU,SAAS,yBAAyB,IAAI,UAAS,cAAc,OAAO,UAAU;AAC/F,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ,KAAK;AAC/C,SACI,qBAAC,OAAI,IACD,UAAA;AAAA,IAAA,oBAAC,gBAAA,EAAe,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAI,OAAO,EAAC,IAAI,IAAA,GAAO,UAAA,EAAE,qBAAqB,GAAE;AAAA,IACnH,oBAAC,iBAAc,WAAU,MAAK,OAAO,EAAC,WAAW,QAAQ,GAAG,GAAG,GAAG,GAAG,SAAS,QAAQ,KAAK,IAAA,GACtF,UAAA,iBAAiB,UAAU,MAAM,EAAE,IAAI,CAAC,UACrC;AAAA,MAAC;AAAA,MAAA;AAAA,QAAc,WAAU;AAAA,QAAsB,aAAW,MAAM;AAAA,QAAM,YAAU,MAAM;AAAA,QACjF,OAAO,EAAC,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,OAAO,MAAM,QAAQ,iBAAiB,iBAAA;AAAA,QAC7F,UAAA;AAAA,UAAA,MAAM,QACD,oBAAC,iBAAA,EAAgB,UAAS,SAAQ,OAAM,WAAU,eAAW,KAAA,CAAC,wBAC7D,0BAAA,EAAyB,UAAS,SAAQ,OAAM,YAAW,eAAW,MAAC;AAAA,UAC9E,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,QAAQ,UAAA,EAAE,iBAAiB,MAAM,IAAI,IAAI,MAAM,MAAM,GAAE;AAAA,UAC7F,oBAAC,iBAAA,EAAgB,WAAU,QAAO,OAAO,uBAAwB,UAAA,MAAM,QAAQ,EAAE,mBAAmB,IAAI,EAAE,uBAAuB,EAAA,CAAE;AAAA,QAAA;AAAA,MAAA;AAAA,MANpG,MAAM;AAAA,IAAA,CAQ5C,EAAA,CACL;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"PasswordChecklist.js","sources":["../../src/auth/PasswordChecklist.jsx"],"sourcesContent":["// La lista de lo que debe tener una contraseña nueva, que se va palomeando mientras la persona escribe: dice qué falta\n// ANTES de enviar, en lugar de un «contraseña inválida» después.\n//\n// Para el lector de pantalla cada renglón dice si ya cumple o falta (con el ícono solo, se oía la regla sin saber cómo\n// iba). Va justo debajo del campo de la contraseña nueva.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPasswordChecklist con defaultProps y\n// styleOverrides de sus partes: title, list, item, hidden.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';\n\nimport {DEFAULT_PASSWORD_POLICY, passwordChecksOf} from '@/auth/passwordRules.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.CalcitaPasswordChecklist)\nconst THEME_NAME = 'CalcitaPasswordChecklist';\nconst ChecklistTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst ChecklistList = piecePart(THEME_NAME, 'List', Box);\nconst ChecklistItem = piecePart(THEME_NAME, 'Item', Box);\nconst ChecklistHidden = piecePart(THEME_NAME, 'Hidden', Box);\n\n// texto que solo oye el lector de pantalla (el mismo truco que usa MUI)\nconst SCREEN_READER_ONLY_SX = {\n position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap', border: 0,\n};\n\n/**\n * @param {object} props\n * @param {string} props.password lo que lleva escrito la persona\n * @param {object} [props.policy] las reglas; por omisión DEFAULT_PASSWORD_POLICY\n * @param {string} [props.id]\n * @param {object} [props.texts] los textos que cambian, con la forma de `auth` en los textos del paquete\n */\nexport default function PasswordChecklist(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPasswordChecklist.defaultProps) y encima lo que llega aquí\n const {password, policy = DEFAULT_PASSWORD_POLICY, id, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('auth', texts);\n return (\n <Box id={id}>\n <ChecklistTitle variant=\"caption\" color=\"text.secondary\" component=\"p\" ownSx={{mb: 0.5}}>{t('passwordRules.title')}</ChecklistTitle>\n <ChecklistList component=\"ul\" ownSx={{listStyle: 'none', m: 0, p: 0, display: 'grid', gap: 0.5}}>\n {passwordChecksOf(password, policy).map((check) => (\n <ChecklistItem component=\"li\" key={check.rule} data-rule={check.rule} data-met={check.isMet}\n ownSx={{display: 'flex', alignItems: 'center', gap: 1, color: check.isMet ? 'text.primary' : 'text.secondary'}}>\n {check.isMet\n ? <CheckCircleIcon fontSize=\"small\" color=\"success\" aria-hidden />\n : <RadioButtonUncheckedIcon fontSize=\"small\" color=\"disabled\" aria-hidden />}\n <Typography variant=\"body2\" component=\"span\">{t(`passwordRules.${check.rule}`, check.values)}</Typography>\n <ChecklistHidden component=\"span\" ownSx={SCREEN_READER_ONLY_SX}>{check.isMet ? t('passwordRules.met') : t('passwordRules.missing')}</ChecklistHidden>\n </ChecklistItem>\n ))}\n </ChecklistList>\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,SAAS,UAAU;AAChE,MAAM,gBAAgB,UAAU,YAAY,QAAQ,GAAG;AACvD,MAAM,gBAAgB,UAAU,YAAY,QAAQ,GAAG;AACvD,MAAM,kBAAkB,UAAU,YAAY,UAAU,GAAG;AAG3D,MAAM,wBAAwB;AAAA,EAC1B,UAAU;AAAA,EAAY,OAAO;AAAA,EAAO,QAAQ;AAAA,EAAO,SAAS;AAAA,EAAG,QAAQ;AAAA,EAAQ,UAAU;AAAA,EAAU,MAAM;AAAA,EAAiB,YAAY;AAAA,EAAU,QAAQ;AAC5J;AASA,SAAwB,kBAAkB,OAAO;AAE7C,QAAM,EAAC,UAAU,SAAS,yBAAyB,IAAI,OAAO,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AAChH,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ,KAAK;AAC/C,SACI,qBAAC,OAAI,IACD,UAAA;AAAA,IAAA,oBAAC,gBAAA,EAAe,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAI,OAAO,EAAC,IAAI,IAAA,GAAO,UAAA,EAAE,qBAAqB,GAAE;AAAA,IACnH,oBAAC,iBAAc,WAAU,MAAK,OAAO,EAAC,WAAW,QAAQ,GAAG,GAAG,GAAG,GAAG,SAAS,QAAQ,KAAK,IAAA,GACtF,UAAA,iBAAiB,UAAU,MAAM,EAAE,IAAI,CAAC,UACrC;AAAA,MAAC;AAAA,MAAA;AAAA,QAAc,WAAU;AAAA,QAAsB,aAAW,MAAM;AAAA,QAAM,YAAU,MAAM;AAAA,QACjF,OAAO,EAAC,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,OAAO,MAAM,QAAQ,iBAAiB,iBAAA;AAAA,QAC7F,UAAA;AAAA,UAAA,MAAM,QACD,oBAAC,iBAAA,EAAgB,UAAS,SAAQ,OAAM,WAAU,eAAW,KAAA,CAAC,wBAC7D,0BAAA,EAAyB,UAAS,SAAQ,OAAM,YAAW,eAAW,MAAC;AAAA,UAC9E,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,QAAQ,UAAA,EAAE,iBAAiB,MAAM,IAAI,IAAI,MAAM,MAAM,GAAE;AAAA,UAC7F,oBAAC,iBAAA,EAAgB,WAAU,QAAO,OAAO,uBAAwB,UAAA,MAAM,QAAQ,EAAE,mBAAmB,IAAI,EAAE,uBAAuB,EAAA,CAAE;AAAA,QAAA;AAAA,MAAA;AAAA,MANpG,MAAM;AAAA,IAAA,CAQ5C,EAAA,CACL;AAAA,EAAA,GACJ;AAER;"}
@@ -21,6 +21,7 @@ import { useToolkitSettings } from "../toolkit/toolkitContext.js";
21
21
  import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
22
22
  import SegmentedControl from "../ui/filters/SegmentedControl.js";
23
23
  import { piecePart, usePieceProps } from "../theme/themePieces.js";
24
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
24
25
  const THEME_NAME = "CalcitaEventCalendar";
25
26
  const CalendarToolbar = piecePart(THEME_NAME, "Toolbar", Stack);
26
27
  const CalendarNavigation = piecePart(THEME_NAME, "Navigation", Stack);
@@ -73,8 +74,10 @@ function EventCalendar(props) {
73
74
  onViewChange,
74
75
  renderEvent,
75
76
  fullCalendarProps,
76
- texts
77
+ texts,
78
+ ...unknownProps
77
79
  } = usePieceProps(props, THEME_NAME);
80
+ warnUnknownProps(THEME_NAME, unknownProps);
78
81
  const theme = useTheme();
79
82
  const palette = paletteOf(theme);
80
83
  const { t } = useToolkitTranslation("calendar", texts);
@@ -1 +1 @@
1
- {"version":3,"file":"EventCalendar.js","sources":["../../src/calendar/EventCalendar.jsx"],"sourcesContent":["// Un calendario de eventos (@stackcraftmx/calcita/calendar): FullCalendar por dentro, con la barra del framework (anterior y\n// siguiente, «Hoy», el título y la vista con el control segmentado) y los colores del tema. En el idioma de\n// ToolkitProvider, la semana empieza en lunes y marca la hora actual. Las citas van en bloques de su color (el color es\n// información: el tipo de cita), no en puntitos.\n//\n// <EventCalendar events={[{id: '1', title: 'Entrega P-2375', start: '2026-10-20T10:00', end: '2026-10-20T11:00', tone: 'info'}]}\n// onEventClick={openDetail} onDateSelect={openNewEvent} onEventChange={(change) => api.patch(...)} />\n//\n// Arrastrar un evento lo cambia de día u hora: onEventChange recibe lo nuevo y, si truena (el servidor dijo que no), el\n// evento regresa a donde estaba. Sin onEventChange no se puede arrastrar; sin onDateSelect no se pueden elegir días.\n//\n// Se extiende sin tocar el framework: onDatesChange avisa el rango que se ve (para que el backend mande solo esas citas),\n// onViewChange la vista, renderEvent pinta cada cita a su manera (recibe la cita tal cual, con sus campos propios),\n// fullCalendarProps pasa lo demás de FullCalendar (horario laboral, días ocultos, horas visibles…) y texts los textos. El\n// detalle de una cita para su ventana: EventDetails.\n//\n// OJO: las horas se pintan en la zona del navegador (FullCalendar pide un complemento aparte para zonas con nombre).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEventCalendar con defaultProps y\n// styleOverrides de sus partes: toolbar, navigation, title, calendar.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useRef, useState} from 'react';\nimport FullCalendar from '@fullcalendar/react';\nimport dayGridPlugin from '@fullcalendar/daygrid';\nimport interactionPlugin from '@fullcalendar/interaction';\nimport listPlugin from '@fullcalendar/list';\nimport timeGridPlugin from '@fullcalendar/timegrid';\nimport spanishLocale from '@fullcalendar/core/locales/es';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport ChevronLeftRoundedIcon from '@mui/icons-material/ChevronLeftRounded';\nimport ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SegmentedControl from '@/ui/filters/SegmentedControl.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaEventCalendar)\nconst THEME_NAME = 'CalcitaEventCalendar';\nconst CalendarToolbar = piecePart(THEME_NAME, 'Toolbar', Stack);\nconst CalendarNavigation = piecePart(THEME_NAME, 'Navigation', Stack);\nconst CalendarTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst CalendarCalendar = piecePart(THEME_NAME, 'Calendar', Box);\n\n/** Las vistas del calendario (los nombres de FullCalendar). */\nexport const CALENDAR_VIEW = Object.freeze({month: 'dayGridMonth', week: 'timeGridWeek', day: 'timeGridDay', list: 'listWeek'});\n\n// el texto de cada vista en los textos del paquete (calendar.views.*)\nconst VIEW_TEXT_KEY = Object.freeze({\n [CALENDAR_VIEW.month]: 'month', [CALENDAR_VIEW.week]: 'week', [CALENDAR_VIEW.day]: 'day', [CALENDAR_VIEW.list]: 'list',\n});\n// cuántos eventos caben en un día del mes antes del «+2 más»\nconst DAY_MAX_EVENTS = 3;\nconst MONDAY = 1;\n\n/**\n * La primera letra en mayúscula y lo demás como viene: «octubre de 2026» → «Octubre de 2026». OJO: con CSS\n * (text-transform: capitalize) salía «Octubre De 2026»: en español solo va la primera.\n *\n * @param {string} text\n * @returns {string}\n */\nfunction capitalizeFirst(text) {\n return text ? text.charAt(0).toLocaleUpperCase() + text.slice(1) : text;\n}\n\n/**\n * Los colores del calendario sobre el tema (las variables CSS de FullCalendar), en claro y en oscuro.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction calendarStylesOf(theme) {\n const palette = paletteOf(theme);\n return {\n '--fc-border-color': palette.divider,\n '--fc-page-bg-color': 'transparent',\n '--fc-neutral-bg-color': palette.action.hover,\n '--fc-today-bg-color': `color-mix(in srgb, ${palette.primary.main} 8%, transparent)`,\n '--fc-list-event-hover-bg-color': palette.action.hover,\n '--fc-now-indicator-color': palette.error.main,\n '--fc-highlight-color': `color-mix(in srgb, ${palette.primary.main} 14%, transparent)`,\n '& .fc': {fontFamily: 'inherit', fontSize: theme.typography.body2.fontSize},\n // los números de los días y los encabezados son ligas en FullCalendar: sin subrayado y del color del texto\n '& .fc a': {color: 'inherit', textDecoration: 'none'},\n '& .fc-col-header-cell-cushion': {fontWeight: 600, py: 1},\n '& .fc-event': {borderRadius: '6px', px: 0.5},\n // la hora no se encoge: en un día angosto se recortaba («1C» en lugar de «10»); lo que se corta es el nombre\n '& .fc-event-time': {flexShrink: 0, overflow: 'visible'},\n '& .fc-scrollgrid': {borderRadius: '12px', overflow: 'hidden'},\n '& .fc-list': {borderRadius: '12px'},\n };\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, title: string, start: string, end?: string, allDay?: boolean, tone?: string}>} props.events\n * start y end como texto ISO; tone: un color del tema (por omisión primary)\n * @param {string} [props.initialView] una de CALENDAR_VIEW\n * @param {string[]} [props.views] las que se ofrecen en el control segmentado\n * @param {(eventId: string) => void} [props.onEventClick]\n * @param {(range: {start: string, end: string, allDay: boolean}) => void} [props.onDateSelect] al elegir días u horas\n * @param {(change: {id: string, start: string, end: string|null, allDay: boolean}) => Promise<unknown>|void} [props.onEventChange]\n * al arrastrar un evento; si truena, el evento regresa\n * @param {number|'auto'} [props.height]\n * @param {string} props.ariaLabel ya traducido\n * @param {(range: {start: string, end: string, view: string}) => void} [props.onDatesChange] cada que cambia lo que se ve (al\n * abrir, al ir al anterior o siguiente, al cambiar de vista): para pedir al backend solo las citas de ese rango\n * @param {(view: string) => void} [props.onViewChange] al cambiar de vista (CALENDAR_VIEW)\n * @param {(event: any) => import('react').ReactNode} [props.renderEvent] lo de adentro de cada cita (recibe la cita de events\n * tal cual, con sus campos propios)\n * @param {object} [props.fullCalendarProps] lo demás de FullCalendar (businessHours, hiddenDays, slotMinTime, locale…); lo\n * que el calendario controla (las citas, la barra, los eventos de arriba) no se reemplaza desde aquí\n * @param {object} [props.texts] los textos que cambian, con la forma de `calendar` en los textos del paquete\n */\nexport default function EventCalendar(props) {\n // lo que el proyecto puso en su tema (components.CalcitaEventCalendar.defaultProps) y encima lo que llega aquí\n const {\n events, initialView = CALENDAR_VIEW.month, views = Object.values(CALENDAR_VIEW), onEventClick, onDateSelect, onEventChange, height = 'auto', ariaLabel,\n onDatesChange, onViewChange, renderEvent, fullCalendarProps, texts,\n} = usePieceProps(props, THEME_NAME);\n const theme = useTheme();\n const palette = paletteOf(theme);\n const {t} = useToolkitTranslation('calendar', texts);\n const {language} = useToolkitSettings();\n const calendarRef = useRef(null);\n const [title, setTitle] = useState('');\n const [view, setView] = useState(initialView);\n const calendarApi = () => calendarRef.current?.getApi();\n\n const coloredEvents = events.map((event) => {\n const color = palette[event.tone] ?? palette.primary;\n return {...event, backgroundColor: color.main, borderColor: color.main, textColor: color.contrastText};\n });\n\n const changeEvent = async ({event, revert}) => {\n try {\n await onEventChange({id: event.id, start: event.startStr, end: event.endStr || null, allDay: event.allDay});\n } catch {\n // el servidor dijo que no: el evento regresa a donde estaba (quien llama ya avisó el error)\n revert();\n }\n };\n\n return (\n <Stack spacing={2} role=\"region\" aria-label={ariaLabel}>\n <CalendarToolbar direction={{xs: 'column', md: 'row'}} spacing={1.5} ownSx={{alignItems: {md: 'center'}, justifyContent: 'space-between'}}>\n <CalendarNavigation direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n <Tooltip title={t('previous')}>\n <IconButton aria-label={t('previous')} onClick={() => calendarApi()?.prev()}><ChevronLeftRoundedIcon /></IconButton>\n </Tooltip>\n <Tooltip title={t('next')}>\n <IconButton aria-label={t('next')} onClick={() => calendarApi()?.next()}><ChevronRightRoundedIcon /></IconButton>\n </Tooltip>\n <Button variant=\"outlined\" size=\"small\" onClick={() => calendarApi()?.today()}>{t('today')}</Button>\n <CalendarTitle variant=\"h6\" component=\"h2\" ownSx={{pl: 1}} aria-live=\"polite\">{capitalizeFirst(title)}</CalendarTitle>\n </CalendarNavigation>\n {views.length > 1 && (\n <SegmentedControl ariaLabel={t('view')} value={view} onChange={(nextView) => calendarApi()?.changeView(nextView)}\n options={views.map((value) => ({value, label: t(`views.${VIEW_TEXT_KEY[value]}`)}))} />\n )}\n </CalendarToolbar>\n <CalendarCalendar ownSx={calendarStylesOf}>\n {/* primero lo del proyecto (fullCalendarProps); lo de abajo es lo que el calendario controla y le gana */}\n <FullCalendar locale={language === 'es' ? spanishLocale : 'en'} firstDay={MONDAY} {...fullCalendarProps}\n ref={calendarRef} plugins={[dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin, ...(fullCalendarProps?.plugins ?? [])]}\n initialView={initialView} headerToolbar={false} height={height}\n nowIndicator dayMaxEvents={DAY_MAX_EVENTS} events={coloredEvents} eventDisplay=\"block\"\n eventContent={renderEvent ? (contentInfo) => renderEvent(events.find((event) => event.id === contentInfo.event.id) ?? contentInfo.event) : undefined}\n datesSet={(dateInfo) => {\n setTitle(dateInfo.view.title);\n if (dateInfo.view.type !== view) {\n onViewChange?.(dateInfo.view.type);\n }\n setView(dateInfo.view.type);\n onDatesChange?.({start: dateInfo.startStr, end: dateInfo.endStr, view: dateInfo.view.type});\n }}\n eventClick={onEventClick ? (clickInfo) => onEventClick(clickInfo.event.id) : undefined}\n selectable={Boolean(onDateSelect)} selectMirror\n select={onDateSelect ? (selectInfo) => {\n onDateSelect({start: selectInfo.startStr, end: selectInfo.endStr, allDay: selectInfo.allDay});\n calendarApi()?.unselect();\n } : undefined}\n editable={Boolean(onEventChange)} eventChange={onEventChange ? changeEvent : undefined} />\n </CalendarCalendar>\n </Stack>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EventCalendar.js","sources":["../../src/calendar/EventCalendar.jsx"],"sourcesContent":["// Un calendario de eventos (@stackcraftmx/calcita/calendar): FullCalendar por dentro, con la barra del framework (anterior y\n// siguiente, «Hoy», el título y la vista con el control segmentado) y los colores del tema. En el idioma de\n// ToolkitProvider, la semana empieza en lunes y marca la hora actual. Las citas van en bloques de su color (el color es\n// información: el tipo de cita), no en puntitos.\n//\n// <EventCalendar events={[{id: '1', title: 'Entrega P-2375', start: '2026-10-20T10:00', end: '2026-10-20T11:00', tone: 'info'}]}\n// onEventClick={openDetail} onDateSelect={openNewEvent} onEventChange={(change) => api.patch(...)} />\n//\n// Arrastrar un evento lo cambia de día u hora: onEventChange recibe lo nuevo y, si truena (el servidor dijo que no), el\n// evento regresa a donde estaba. Sin onEventChange no se puede arrastrar; sin onDateSelect no se pueden elegir días.\n//\n// Se extiende sin tocar el framework: onDatesChange avisa el rango que se ve (para que el backend mande solo esas citas),\n// onViewChange la vista, renderEvent pinta cada cita a su manera (recibe la cita tal cual, con sus campos propios),\n// fullCalendarProps pasa lo demás de FullCalendar (horario laboral, días ocultos, horas visibles…) y texts los textos. El\n// detalle de una cita para su ventana: EventDetails.\n//\n// OJO: las horas se pintan en la zona del navegador (FullCalendar pide un complemento aparte para zonas con nombre).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEventCalendar con defaultProps y\n// styleOverrides de sus partes: toolbar, navigation, title, calendar.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useRef, useState} from 'react';\nimport FullCalendar from '@fullcalendar/react';\nimport dayGridPlugin from '@fullcalendar/daygrid';\nimport interactionPlugin from '@fullcalendar/interaction';\nimport listPlugin from '@fullcalendar/list';\nimport timeGridPlugin from '@fullcalendar/timegrid';\nimport spanishLocale from '@fullcalendar/core/locales/es';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport ChevronLeftRoundedIcon from '@mui/icons-material/ChevronLeftRounded';\nimport ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SegmentedControl from '@/ui/filters/SegmentedControl.jsx';\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.CalcitaEventCalendar)\nconst THEME_NAME = 'CalcitaEventCalendar';\nconst CalendarToolbar = piecePart(THEME_NAME, 'Toolbar', Stack);\nconst CalendarNavigation = piecePart(THEME_NAME, 'Navigation', Stack);\nconst CalendarTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst CalendarCalendar = piecePart(THEME_NAME, 'Calendar', Box);\n\n/** Las vistas del calendario (los nombres de FullCalendar). */\nexport const CALENDAR_VIEW = Object.freeze({month: 'dayGridMonth', week: 'timeGridWeek', day: 'timeGridDay', list: 'listWeek'});\n\n// el texto de cada vista en los textos del paquete (calendar.views.*)\nconst VIEW_TEXT_KEY = Object.freeze({\n [CALENDAR_VIEW.month]: 'month', [CALENDAR_VIEW.week]: 'week', [CALENDAR_VIEW.day]: 'day', [CALENDAR_VIEW.list]: 'list',\n});\n// cuántos eventos caben en un día del mes antes del «+2 más»\nconst DAY_MAX_EVENTS = 3;\nconst MONDAY = 1;\n\n/**\n * La primera letra en mayúscula y lo demás como viene: «octubre de 2026» → «Octubre de 2026». OJO: con CSS\n * (text-transform: capitalize) salía «Octubre De 2026»: en español solo va la primera.\n *\n * @param {string} text\n * @returns {string}\n */\nfunction capitalizeFirst(text) {\n return text ? text.charAt(0).toLocaleUpperCase() + text.slice(1) : text;\n}\n\n/**\n * Los colores del calendario sobre el tema (las variables CSS de FullCalendar), en claro y en oscuro.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction calendarStylesOf(theme) {\n const palette = paletteOf(theme);\n return {\n '--fc-border-color': palette.divider,\n '--fc-page-bg-color': 'transparent',\n '--fc-neutral-bg-color': palette.action.hover,\n '--fc-today-bg-color': `color-mix(in srgb, ${palette.primary.main} 8%, transparent)`,\n '--fc-list-event-hover-bg-color': palette.action.hover,\n '--fc-now-indicator-color': palette.error.main,\n '--fc-highlight-color': `color-mix(in srgb, ${palette.primary.main} 14%, transparent)`,\n '& .fc': {fontFamily: 'inherit', fontSize: theme.typography.body2.fontSize},\n // los números de los días y los encabezados son ligas en FullCalendar: sin subrayado y del color del texto\n '& .fc a': {color: 'inherit', textDecoration: 'none'},\n '& .fc-col-header-cell-cushion': {fontWeight: 600, py: 1},\n '& .fc-event': {borderRadius: '6px', px: 0.5},\n // la hora no se encoge: en un día angosto se recortaba («1C» en lugar de «10»); lo que se corta es el nombre\n '& .fc-event-time': {flexShrink: 0, overflow: 'visible'},\n '& .fc-scrollgrid': {borderRadius: '12px', overflow: 'hidden'},\n '& .fc-list': {borderRadius: '12px'},\n };\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, title: string, start: string, end?: string, allDay?: boolean, tone?: string}>} props.events\n * start y end como texto ISO; tone: un color del tema (por omisión primary)\n * @param {string} [props.initialView] una de CALENDAR_VIEW\n * @param {string[]} [props.views] las que se ofrecen en el control segmentado\n * @param {(eventId: string) => void} [props.onEventClick]\n * @param {(range: {start: string, end: string, allDay: boolean}) => void} [props.onDateSelect] al elegir días u horas\n * @param {(change: {id: string, start: string, end: string|null, allDay: boolean}) => Promise<unknown>|void} [props.onEventChange]\n * al arrastrar un evento; si truena, el evento regresa\n * @param {number|'auto'} [props.height]\n * @param {string} props.ariaLabel ya traducido\n * @param {(range: {start: string, end: string, view: string}) => void} [props.onDatesChange] cada que cambia lo que se ve (al\n * abrir, al ir al anterior o siguiente, al cambiar de vista): para pedir al backend solo las citas de ese rango\n * @param {(view: string) => void} [props.onViewChange] al cambiar de vista (CALENDAR_VIEW)\n * @param {(event: any) => import('react').ReactNode} [props.renderEvent] lo de adentro de cada cita (recibe la cita de events\n * tal cual, con sus campos propios)\n * @param {object} [props.fullCalendarProps] lo demás de FullCalendar (businessHours, hiddenDays, slotMinTime, locale…); lo\n * que el calendario controla (las citas, la barra, los eventos de arriba) no se reemplaza desde aquí\n * @param {object} [props.texts] los textos que cambian, con la forma de `calendar` en los textos del paquete\n */\nexport default function EventCalendar(props) {\n // lo que el proyecto puso en su tema (components.CalcitaEventCalendar.defaultProps) y encima lo que llega aquí\n const {\n events, initialView = CALENDAR_VIEW.month, views = Object.values(CALENDAR_VIEW), onEventClick, onDateSelect, onEventChange, height = 'auto', ariaLabel,\n onDatesChange, onViewChange, renderEvent, fullCalendarProps, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const palette = paletteOf(theme);\n const {t} = useToolkitTranslation('calendar', texts);\n const {language} = useToolkitSettings();\n const calendarRef = useRef(null);\n const [title, setTitle] = useState('');\n const [view, setView] = useState(initialView);\n const calendarApi = () => calendarRef.current?.getApi();\n\n const coloredEvents = events.map((event) => {\n const color = palette[event.tone] ?? palette.primary;\n return {...event, backgroundColor: color.main, borderColor: color.main, textColor: color.contrastText};\n });\n\n const changeEvent = async ({event, revert}) => {\n try {\n await onEventChange({id: event.id, start: event.startStr, end: event.endStr || null, allDay: event.allDay});\n } catch {\n // el servidor dijo que no: el evento regresa a donde estaba (quien llama ya avisó el error)\n revert();\n }\n };\n\n return (\n <Stack spacing={2} role=\"region\" aria-label={ariaLabel}>\n <CalendarToolbar direction={{xs: 'column', md: 'row'}} spacing={1.5} ownSx={{alignItems: {md: 'center'}, justifyContent: 'space-between'}}>\n <CalendarNavigation direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n <Tooltip title={t('previous')}>\n <IconButton aria-label={t('previous')} onClick={() => calendarApi()?.prev()}><ChevronLeftRoundedIcon /></IconButton>\n </Tooltip>\n <Tooltip title={t('next')}>\n <IconButton aria-label={t('next')} onClick={() => calendarApi()?.next()}><ChevronRightRoundedIcon /></IconButton>\n </Tooltip>\n <Button variant=\"outlined\" size=\"small\" onClick={() => calendarApi()?.today()}>{t('today')}</Button>\n <CalendarTitle variant=\"h6\" component=\"h2\" ownSx={{pl: 1}} aria-live=\"polite\">{capitalizeFirst(title)}</CalendarTitle>\n </CalendarNavigation>\n {views.length > 1 && (\n <SegmentedControl ariaLabel={t('view')} value={view} onChange={(nextView) => calendarApi()?.changeView(nextView)}\n options={views.map((value) => ({value, label: t(`views.${VIEW_TEXT_KEY[value]}`)}))} />\n )}\n </CalendarToolbar>\n <CalendarCalendar ownSx={calendarStylesOf}>\n {/* primero lo del proyecto (fullCalendarProps); lo de abajo es lo que el calendario controla y le gana */}\n <FullCalendar locale={language === 'es' ? spanishLocale : 'en'} firstDay={MONDAY} {...fullCalendarProps}\n ref={calendarRef} plugins={[dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin, ...(fullCalendarProps?.plugins ?? [])]}\n initialView={initialView} headerToolbar={false} height={height}\n nowIndicator dayMaxEvents={DAY_MAX_EVENTS} events={coloredEvents} eventDisplay=\"block\"\n eventContent={renderEvent ? (contentInfo) => renderEvent(events.find((event) => event.id === contentInfo.event.id) ?? contentInfo.event) : undefined}\n datesSet={(dateInfo) => {\n setTitle(dateInfo.view.title);\n if (dateInfo.view.type !== view) {\n onViewChange?.(dateInfo.view.type);\n }\n setView(dateInfo.view.type);\n onDatesChange?.({start: dateInfo.startStr, end: dateInfo.endStr, view: dateInfo.view.type});\n }}\n eventClick={onEventClick ? (clickInfo) => onEventClick(clickInfo.event.id) : undefined}\n selectable={Boolean(onDateSelect)} selectMirror\n select={onDateSelect ? (selectInfo) => {\n onDateSelect({start: selectInfo.startStr, end: selectInfo.endStr, allDay: selectInfo.allDay});\n calendarApi()?.unselect();\n } : undefined}\n editable={Boolean(onEventChange)} eventChange={onEventChange ? changeEvent : undefined} />\n </CalendarCalendar>\n </Stack>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -17,6 +17,7 @@ import { reminderOf, isOpenableMeetingUrl, REMINDER_UNIT } from "./calendarEvent
17
17
  import { useFormatters } from "../toolkit/useFormatters.js";
18
18
  import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
19
19
  import { piecePart, usePieceProps } from "../theme/themePieces.js";
20
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
20
21
  const THEME_NAME = "CalcitaEventDetails";
21
22
  const EventRow = piecePart(THEME_NAME, "Row", Stack);
22
23
  const EventRowContent = piecePart(THEME_NAME, "RowContent", Box);
@@ -45,7 +46,8 @@ function DetailRow({ Icon, label, children }) {
45
46
  ] });
46
47
  }
47
48
  function EventDetails(props) {
48
- const { event, actions, titleId, texts } = usePieceProps(props, THEME_NAME);
49
+ const { event, actions, titleId, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
50
+ warnUnknownProps(THEME_NAME, unknownProps);
49
51
  const { t } = useToolkitTranslation("calendar", texts);
50
52
  const { formatDate, formatTime } = useFormatters();
51
53
  const isWholeDay = event.allDay || WHOLE_DAY_PATTERN.test(event.start);
@@ -1 +1 @@
1
- {"version":3,"file":"EventDetails.js","sources":["../../src/calendar/EventDetails.jsx"],"sourcesContent":["// El detalle de una cita, para la ventana que se abre al picarla en el calendario: su título con el color de su tipo,\n// cuándo (el día y las horas), dónde, la liga de la reunión con «Unirse», los invitados con su respuesta, el recordatorio y\n// las notas. Pinta lo que la cita traiga; lo que no trae no ocupa lugar.\n//\n// <EventDetails event={{title: 'Junta de cierre', start: '2026-10-20T10:00', end: '2026-10-20T11:00', location: 'Sala 2',\n// meetingUrl: 'https://meet.example.com/abc', attendees: [{id: 'ana', name: 'Ana', response: 'accepted'}],\n// reminderMinutes: 15}}\n// actions={<Button onClick={edit}>Editar</Button>} />\n//\n// No llama a ningún servidor ni abre la reunión por su cuenta: «Unirse» es una liga (solo http o https) y lo demás lo pone la\n// pantalla en actions. Sus textos se cambian con texts (la forma de `calendar` en los textos del paquete).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEventDetails con defaultProps y\n// styleOverrides de sus partes: row, rowContent, titleRow, toneDot, linkRow, link, guestsTitle, guest, guestAvatar,\n// guestName, notes, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AccessTimeRoundedIcon from '@mui/icons-material/AccessTimeRounded';\nimport NotesRoundedIcon from '@mui/icons-material/NotesRounded';\nimport NotificationsNoneRoundedIcon from '@mui/icons-material/NotificationsNoneRounded';\nimport PeopleOutlineRoundedIcon from '@mui/icons-material/PeopleOutlineRounded';\nimport PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';\nimport VideocamOutlinedIcon from '@mui/icons-material/VideocamOutlined';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {REMINDER_UNIT, isOpenableMeetingUrl, reminderOf} from '@/calendar/calendarEvents.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaEventDetails)\nconst THEME_NAME = 'CalcitaEventDetails';\nconst EventRow = piecePart(THEME_NAME, 'Row', Stack);\nconst EventRowContent = piecePart(THEME_NAME, 'RowContent', Box);\nconst EventTitleRow = piecePart(THEME_NAME, 'TitleRow', Stack);\nconst EventToneDot = piecePart(THEME_NAME, 'ToneDot', Box);\nconst EventLinkRow = piecePart(THEME_NAME, 'LinkRow', Stack);\nconst EventLink = piecePart(THEME_NAME, 'Link', Typography);\nconst EventGuestsTitle = piecePart(THEME_NAME, 'GuestsTitle', Typography);\nconst EventGuest = piecePart(THEME_NAME, 'Guest', Stack);\nconst EventGuestAvatar = piecePart(THEME_NAME, 'GuestAvatar', Avatar);\nconst EventGuestName = piecePart(THEME_NAME, 'GuestName', Typography);\nconst EventNotes = piecePart(THEME_NAME, 'Notes', Typography);\nconst EventActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/** Las respuestas de un invitado. */\nexport const ATTENDEE_RESPONSE = Object.freeze({accepted: 'accepted', declined: 'declined', pending: 'pending'});\n// el color de cada respuesta (el color es información: verde va, rojo no va, gris no ha contestado)\nconst RESPONSE_COLOR = Object.freeze({\n [ATTENDEE_RESPONSE.accepted]: 'success', [ATTENDEE_RESPONSE.declined]: 'error', [ATTENDEE_RESPONSE.pending]: 'default',\n});\n// un día completo viene sin hora («2026-10-20»)\nconst WHOLE_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst ICON_SIZE = 20;\n\n/** Un renglón del detalle: su ícono a la izquierda y lo de adentro. */\nfunction DetailRow({Icon, label, children}) {\n return (\n <EventRow direction=\"row\" spacing={1.5} ownSx={{alignItems: 'flex-start'}} aria-label={label} role=\"group\">\n <Icon sx={{fontSize: ICON_SIZE, color: 'text.secondary', mt: 0.25}} aria-hidden=\"true\" />\n <EventRowContent ownSx={{minWidth: 0, flexGrow: 1}}>{children}</EventRowContent>\n </EventRow>\n );\n}\n\n/**\n * @param {object} props\n * @param {{title: string, start: string, end?: string|null, allDay?: boolean, tone?: string, location?: string, meetingUrl?: string, attendees?: Array<{id: string, name: string, email?: string, response?: 'accepted'|'declined'|'pending'}>, reminderMinutes?: number|null, description?: string}} props.event\n * start y end como texto ISO (o AAAA-MM-DD un día completo); tone: el color de su tipo\n * @param {import('react').ReactNode} [props.actions] botones de la pantalla (editar, borrar, responder…)\n * @param {string} [props.titleId] el id del título: la ventana que lo contiene se nombra con él (aria-labelledby)\n * @param {object} [props.texts] los textos que cambian, con la forma de `calendar` en los textos del paquete\n */\nexport default function EventDetails(props) {\n // lo que el proyecto puso en su tema (components.CalcitaEventDetails.defaultProps) y encima lo que llega aquí\n const {event, actions, titleId, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('calendar', texts);\n const {formatDate, formatTime} = useFormatters();\n const isWholeDay = event.allDay || WHOLE_DAY_PATTERN.test(event.start);\n let when = formatDate(event.start);\n if (!isWholeDay) {\n when = t('details.timeRange', {date: formatDate(event.start), start: formatTime(event.start), end: event.end ? formatTime(event.end) : ''});\n }\n const reminder = reminderOf(event.reminderMinutes);\n const canJoin = isOpenableMeetingUrl(event.meetingUrl);\n\n return (\n <Stack spacing={2}>\n <EventTitleRow direction=\"row\" spacing={1.5} ownSx={{alignItems: 'center'}}>\n <EventToneDot aria-hidden=\"true\" ownSx={{width: 12, height: 12, borderRadius: '4px', flexShrink: 0, bgcolor: `${event.tone ?? 'primary'}.main`}} />\n <Typography id={titleId} variant=\"h6\" component=\"h2\">{event.title}</Typography>\n </EventTitleRow>\n <DetailRow Icon={AccessTimeRoundedIcon} label={t('details.when')}>\n <Typography variant=\"body2\">{isWholeDay ? t('details.allDay', {date: when}) : when}</Typography>\n </DetailRow>\n {event.location && (\n <DetailRow Icon={PlaceOutlinedIcon} label={t('details.location')}>\n <Typography variant=\"body2\">{event.location}</Typography>\n </DetailRow>\n )}\n {event.meetingUrl && (\n <DetailRow Icon={VideocamOutlinedIcon} label={t('details.meeting')}>\n <EventLinkRow direction=\"row\" spacing={1} ownSx={{alignItems: 'center', flexWrap: 'wrap'}} useFlexGap>\n {canJoin && (\n <Button variant=\"contained\" size=\"small\" href={event.meetingUrl} target=\"_blank\" rel=\"noreferrer\">{t('details.join')}</Button>\n )}\n <EventLink variant=\"caption\" color=\"text.secondary\" ownSx={{wordBreak: 'break-all'}}>{event.meetingUrl}</EventLink>\n </EventLinkRow>\n </DetailRow>\n )}\n {event.attendees?.length > 0 && (\n <DetailRow Icon={PeopleOutlineRoundedIcon} label={t('details.attendees')}>\n <EventGuestsTitle variant=\"body2\" ownSx={{mb: 0.75}}>{t('details.attendeesCount', {count: event.attendees.length})}</EventGuestsTitle>\n <Stack spacing={0.75}>\n {event.attendees.map((attendee) => {\n const response = attendee.response ?? ATTENDEE_RESPONSE.pending;\n return (\n <EventGuest key={attendee.id} direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n <EventGuestAvatar ownSx={{width: 24, height: 24, fontSize: 11}}>{initialsOf(attendee.name)}</EventGuestAvatar>\n <EventGuestName variant=\"body2\" ownSx={{flexGrow: 1, minWidth: 0}} noWrap>{attendee.name}</EventGuestName>\n <Chip size=\"small\" variant=\"outlined\" color={RESPONSE_COLOR[response] ?? 'default'} label={t(`details.response.${response}`)} />\n </EventGuest>\n );\n })}\n </Stack>\n </DetailRow>\n )}\n {reminder.unit !== REMINDER_UNIT.none && (\n <DetailRow Icon={NotificationsNoneRoundedIcon} label={t('details.reminder')}>\n <Typography variant=\"body2\">{t(`details.reminderUnit.${reminder.unit}`, {count: reminder.count})}</Typography>\n </DetailRow>\n )}\n {event.description && (\n <DetailRow Icon={NotesRoundedIcon} label={t('details.notes')}>\n <EventNotes variant=\"body2\" ownSx={{whiteSpace: 'pre-wrap'}}>{event.description}</EventNotes>\n </DetailRow>\n )}\n {actions && <EventActions direction=\"row\" spacing={1} useFlexGap ownSx={{justifyContent: 'flex-end', flexWrap: 'wrap'}}>{actions}</EventActions>}\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,MAAM,aAAa;AACnB,MAAM,WAAW,UAAU,YAAY,OAAO,KAAK;AACnD,MAAM,kBAAkB,UAAU,YAAY,cAAc,GAAG;AAC/D,MAAM,gBAAgB,UAAU,YAAY,YAAY,KAAK;AAC7D,MAAM,eAAe,UAAU,YAAY,WAAW,GAAG;AACzD,MAAM,eAAe,UAAU,YAAY,WAAW,KAAK;AAC3D,MAAM,YAAY,UAAU,YAAY,QAAQ,UAAU;AAC1D,MAAM,mBAAmB,UAAU,YAAY,eAAe,UAAU;AACxE,MAAM,aAAa,UAAU,YAAY,SAAS,KAAK;AACvD,MAAM,mBAAmB,UAAU,YAAY,eAAe,MAAM;AACpE,MAAM,iBAAiB,UAAU,YAAY,aAAa,UAAU;AACpE,MAAM,aAAa,UAAU,YAAY,SAAS,UAAU;AAC5D,MAAM,eAAe,UAAU,YAAY,WAAW,KAAK;AAGpD,MAAM,oBAAoB,OAAO,OAAO,EAAC,UAAU,YAAY,UAAU,YAAY,SAAS,UAAA,CAAU;AAE/G,MAAM,iBAAiB,OAAO,OAAO;AAAA,EACjC,CAAC,kBAAkB,QAAQ,GAAG;AAAA,EAAW,CAAC,kBAAkB,QAAQ,GAAG;AAAA,EAAS,CAAC,kBAAkB,OAAO,GAAG;AACjH,CAAC;AAED,MAAM,oBAAoB;AAC1B,MAAM,YAAY;AAGlB,SAAS,UAAU,EAAC,MAAM,OAAO,YAAW;AACxC,SACI,qBAAC,UAAA,EAAS,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,aAAA,GAAe,cAAY,OAAO,MAAK,SAC/F,UAAA;AAAA,IAAA,oBAAC,MAAA,EAAK,IAAI,EAAC,UAAU,WAAW,OAAO,kBAAkB,IAAI,KAAA,GAAO,eAAY,OAAA,CAAO;AAAA,IACvF,oBAAC,mBAAgB,OAAO,EAAC,UAAU,GAAG,UAAU,EAAA,GAAK,SAAA,CAAS;AAAA,EAAA,GAClE;AAER;AAUA,SAAwB,aAAa,OAAO;AAExC,QAAM,EAAC,OAAO,SAAS,SAAS,UAAS,cAAc,OAAO,UAAU;AACxE,QAAM,EAAC,EAAA,IAAK,sBAAsB,YAAY,KAAK;AACnD,QAAM,EAAC,YAAY,WAAA,IAAc,cAAA;AACjC,QAAM,aAAa,MAAM,UAAU,kBAAkB,KAAK,MAAM,KAAK;AACrE,MAAI,OAAO,WAAW,MAAM,KAAK;AACjC,MAAI,CAAC,YAAY;AACb,WAAO,EAAE,qBAAqB,EAAC,MAAM,WAAW,MAAM,KAAK,GAAG,OAAO,WAAW,MAAM,KAAK,GAAG,KAAK,MAAM,MAAM,WAAW,MAAM,GAAG,IAAI,IAAG;AAAA,EAC9I;AACA,QAAM,WAAW,WAAW,MAAM,eAAe;AACjD,QAAM,UAAU,qBAAqB,MAAM,UAAU;AAErD,SACI,qBAAC,OAAA,EAAM,SAAS,GACZ,UAAA;AAAA,IAAA,qBAAC,eAAA,EAAc,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,SAAA,GAC7D,UAAA;AAAA,MAAA,oBAAC,gBAAa,eAAY,QAAO,OAAO,EAAC,OAAO,IAAI,QAAQ,IAAI,cAAc,OAAO,YAAY,GAAG,SAAS,GAAG,MAAM,QAAQ,SAAS,WAAU;AAAA,MACjJ,oBAAC,cAAW,IAAI,SAAS,SAAQ,MAAK,WAAU,MAAM,UAAA,MAAM,MAAA,CAAM;AAAA,IAAA,GACtE;AAAA,IACA,oBAAC,aAAU,MAAM,uBAAuB,OAAO,EAAE,cAAc,GAC3D,UAAA,oBAAC,YAAA,EAAW,SAAQ,SAAS,UAAA,aAAa,EAAE,kBAAkB,EAAC,MAAM,KAAA,CAAK,IAAI,KAAA,CAAK,EAAA,CACvF;AAAA,IACC,MAAM,YACH,oBAAC,WAAA,EAAU,MAAM,mBAAmB,OAAO,EAAE,kBAAkB,GAC3D,8BAAC,YAAA,EAAW,SAAQ,SAAS,UAAA,MAAM,UAAS,GAChD;AAAA,IAEH,MAAM,cACH,oBAAC,WAAA,EAAU,MAAM,sBAAsB,OAAO,EAAE,iBAAiB,GAC7D,UAAA,qBAAC,cAAA,EAAa,WAAU,OAAM,SAAS,GAAG,OAAO,EAAC,YAAY,UAAU,UAAU,OAAA,GAAS,YAAU,MAChG,UAAA;AAAA,MAAA,WACG,oBAAC,QAAA,EAAO,SAAQ,aAAY,MAAK,SAAQ,MAAM,MAAM,YAAY,QAAO,UAAS,KAAI,cAAc,UAAA,EAAE,cAAc,GAAE;AAAA,MAEzH,oBAAC,WAAA,EAAU,SAAQ,WAAU,OAAM,kBAAiB,OAAO,EAAC,WAAW,YAAA,GAAe,UAAA,MAAM,WAAA,CAAW;AAAA,IAAA,EAAA,CAC3G,EAAA,CACJ;AAAA,IAEH,MAAM,WAAW,SAAS,KACvB,qBAAC,WAAA,EAAU,MAAM,0BAA0B,OAAO,EAAE,mBAAmB,GACnE,UAAA;AAAA,MAAA,oBAAC,oBAAiB,SAAQ,SAAQ,OAAO,EAAC,IAAI,KAAA,GAAQ,UAAA,EAAE,0BAA0B,EAAC,OAAO,MAAM,UAAU,OAAA,CAAO,GAAE;AAAA,MACnH,oBAAC,SAAM,SAAS,MACX,gBAAM,UAAU,IAAI,CAAC,aAAa;AAC/B,cAAM,WAAW,SAAS,YAAY,kBAAkB;AACxD,eACI,qBAAC,YAAA,EAA6B,WAAU,OAAM,SAAS,GAAG,OAAO,EAAC,YAAY,SAAA,GAC1E,UAAA;AAAA,UAAA,oBAAC,kBAAA,EAAiB,OAAO,EAAC,OAAO,IAAI,QAAQ,IAAI,UAAU,GAAA,GAAM,UAAA,WAAW,SAAS,IAAI,GAAE;AAAA,UAC3F,oBAAC,gBAAA,EAAe,SAAQ,SAAQ,OAAO,EAAC,UAAU,GAAG,UAAU,EAAA,GAAI,QAAM,MAAE,mBAAS,MAAK;AAAA,8BACxF,MAAA,EAAK,MAAK,SAAQ,SAAQ,YAAW,OAAO,eAAe,QAAQ,KAAK,WAAW,OAAO,EAAE,oBAAoB,QAAQ,EAAE,EAAA,CAAG;AAAA,QAAA,EAAA,GAHjH,SAAS,EAI1B;AAAA,MAER,CAAC,EAAA,CACL;AAAA,IAAA,GACJ;AAAA,IAEH,SAAS,SAAS,cAAc,QAC7B,oBAAC,WAAA,EAAU,MAAM,8BAA8B,OAAO,EAAE,kBAAkB,GACtE,UAAA,oBAAC,YAAA,EAAW,SAAQ,SAAS,UAAA,EAAE,wBAAwB,SAAS,IAAI,IAAI,EAAC,OAAO,SAAS,MAAA,CAAM,EAAA,CAAE,EAAA,CACrG;AAAA,IAEH,MAAM,eACH,oBAAC,WAAA,EAAU,MAAM,kBAAkB,OAAO,EAAE,eAAe,GACvD,8BAAC,YAAA,EAAW,SAAQ,SAAQ,OAAO,EAAC,YAAY,WAAA,GAAc,UAAA,MAAM,YAAA,CAAY,EAAA,CACpF;AAAA,IAEH,WAAW,oBAAC,cAAA,EAAa,WAAU,OAAM,SAAS,GAAG,YAAU,MAAC,OAAO,EAAC,gBAAgB,YAAY,UAAU,OAAA,GAAU,UAAA,QAAA,CAAQ;AAAA,EAAA,GACrI;AAER;"}
1
+ {"version":3,"file":"EventDetails.js","sources":["../../src/calendar/EventDetails.jsx"],"sourcesContent":["// El detalle de una cita, para la ventana que se abre al picarla en el calendario: su título con el color de su tipo,\n// cuándo (el día y las horas), dónde, la liga de la reunión con «Unirse», los invitados con su respuesta, el recordatorio y\n// las notas. Pinta lo que la cita traiga; lo que no trae no ocupa lugar.\n//\n// <EventDetails event={{title: 'Junta de cierre', start: '2026-10-20T10:00', end: '2026-10-20T11:00', location: 'Sala 2',\n// meetingUrl: 'https://meet.example.com/abc', attendees: [{id: 'ana', name: 'Ana', response: 'accepted'}],\n// reminderMinutes: 15}}\n// actions={<Button onClick={edit}>Editar</Button>} />\n//\n// No llama a ningún servidor ni abre la reunión por su cuenta: «Unirse» es una liga (solo http o https) y lo demás lo pone la\n// pantalla en actions. Sus textos se cambian con texts (la forma de `calendar` en los textos del paquete).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEventDetails con defaultProps y\n// styleOverrides de sus partes: row, rowContent, titleRow, toneDot, linkRow, link, guestsTitle, guest, guestAvatar,\n// guestName, notes, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AccessTimeRoundedIcon from '@mui/icons-material/AccessTimeRounded';\nimport NotesRoundedIcon from '@mui/icons-material/NotesRounded';\nimport NotificationsNoneRoundedIcon from '@mui/icons-material/NotificationsNoneRounded';\nimport PeopleOutlineRoundedIcon from '@mui/icons-material/PeopleOutlineRounded';\nimport PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';\nimport VideocamOutlinedIcon from '@mui/icons-material/VideocamOutlined';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {REMINDER_UNIT, isOpenableMeetingUrl, reminderOf} from '@/calendar/calendarEvents.js';\nimport {useFormatters} from '@/toolkit/useFormatters.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.CalcitaEventDetails)\nconst THEME_NAME = 'CalcitaEventDetails';\nconst EventRow = piecePart(THEME_NAME, 'Row', Stack);\nconst EventRowContent = piecePart(THEME_NAME, 'RowContent', Box);\nconst EventTitleRow = piecePart(THEME_NAME, 'TitleRow', Stack);\nconst EventToneDot = piecePart(THEME_NAME, 'ToneDot', Box);\nconst EventLinkRow = piecePart(THEME_NAME, 'LinkRow', Stack);\nconst EventLink = piecePart(THEME_NAME, 'Link', Typography);\nconst EventGuestsTitle = piecePart(THEME_NAME, 'GuestsTitle', Typography);\nconst EventGuest = piecePart(THEME_NAME, 'Guest', Stack);\nconst EventGuestAvatar = piecePart(THEME_NAME, 'GuestAvatar', Avatar);\nconst EventGuestName = piecePart(THEME_NAME, 'GuestName', Typography);\nconst EventNotes = piecePart(THEME_NAME, 'Notes', Typography);\nconst EventActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/** Las respuestas de un invitado. */\nexport const ATTENDEE_RESPONSE = Object.freeze({accepted: 'accepted', declined: 'declined', pending: 'pending'});\n// el color de cada respuesta (el color es información: verde va, rojo no va, gris no ha contestado)\nconst RESPONSE_COLOR = Object.freeze({\n [ATTENDEE_RESPONSE.accepted]: 'success', [ATTENDEE_RESPONSE.declined]: 'error', [ATTENDEE_RESPONSE.pending]: 'default',\n});\n// un día completo viene sin hora («2026-10-20»)\nconst WHOLE_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst ICON_SIZE = 20;\n\n/** Un renglón del detalle: su ícono a la izquierda y lo de adentro. */\nfunction DetailRow({Icon, label, children}) {\n return (\n <EventRow direction=\"row\" spacing={1.5} ownSx={{alignItems: 'flex-start'}} aria-label={label} role=\"group\">\n <Icon sx={{fontSize: ICON_SIZE, color: 'text.secondary', mt: 0.25}} aria-hidden=\"true\" />\n <EventRowContent ownSx={{minWidth: 0, flexGrow: 1}}>{children}</EventRowContent>\n </EventRow>\n );\n}\n\n/**\n * @param {object} props\n * @param {{title: string, start: string, end?: string|null, allDay?: boolean, tone?: string, location?: string, meetingUrl?: string, attendees?: Array<{id: string, name: string, email?: string, response?: 'accepted'|'declined'|'pending'}>, reminderMinutes?: number|null, description?: string}} props.event\n * start y end como texto ISO (o AAAA-MM-DD un día completo); tone: el color de su tipo\n * @param {import('react').ReactNode} [props.actions] botones de la pantalla (editar, borrar, responder…)\n * @param {string} [props.titleId] el id del título: la ventana que lo contiene se nombra con él (aria-labelledby)\n * @param {object} [props.texts] los textos que cambian, con la forma de `calendar` en los textos del paquete\n */\nexport default function EventDetails(props) {\n // lo que el proyecto puso en su tema (components.CalcitaEventDetails.defaultProps) y encima lo que llega aquí\n const {event, actions, titleId, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('calendar', texts);\n const {formatDate, formatTime} = useFormatters();\n const isWholeDay = event.allDay || WHOLE_DAY_PATTERN.test(event.start);\n let when = formatDate(event.start);\n if (!isWholeDay) {\n when = t('details.timeRange', {date: formatDate(event.start), start: formatTime(event.start), end: event.end ? formatTime(event.end) : ''});\n }\n const reminder = reminderOf(event.reminderMinutes);\n const canJoin = isOpenableMeetingUrl(event.meetingUrl);\n\n return (\n <Stack spacing={2}>\n <EventTitleRow direction=\"row\" spacing={1.5} ownSx={{alignItems: 'center'}}>\n <EventToneDot aria-hidden=\"true\" ownSx={{width: 12, height: 12, borderRadius: '4px', flexShrink: 0, bgcolor: `${event.tone ?? 'primary'}.main`}} />\n <Typography id={titleId} variant=\"h6\" component=\"h2\">{event.title}</Typography>\n </EventTitleRow>\n <DetailRow Icon={AccessTimeRoundedIcon} label={t('details.when')}>\n <Typography variant=\"body2\">{isWholeDay ? t('details.allDay', {date: when}) : when}</Typography>\n </DetailRow>\n {event.location && (\n <DetailRow Icon={PlaceOutlinedIcon} label={t('details.location')}>\n <Typography variant=\"body2\">{event.location}</Typography>\n </DetailRow>\n )}\n {event.meetingUrl && (\n <DetailRow Icon={VideocamOutlinedIcon} label={t('details.meeting')}>\n <EventLinkRow direction=\"row\" spacing={1} ownSx={{alignItems: 'center', flexWrap: 'wrap'}} useFlexGap>\n {canJoin && (\n <Button variant=\"contained\" size=\"small\" href={event.meetingUrl} target=\"_blank\" rel=\"noreferrer\">{t('details.join')}</Button>\n )}\n <EventLink variant=\"caption\" color=\"text.secondary\" ownSx={{wordBreak: 'break-all'}}>{event.meetingUrl}</EventLink>\n </EventLinkRow>\n </DetailRow>\n )}\n {event.attendees?.length > 0 && (\n <DetailRow Icon={PeopleOutlineRoundedIcon} label={t('details.attendees')}>\n <EventGuestsTitle variant=\"body2\" ownSx={{mb: 0.75}}>{t('details.attendeesCount', {count: event.attendees.length})}</EventGuestsTitle>\n <Stack spacing={0.75}>\n {event.attendees.map((attendee) => {\n const response = attendee.response ?? ATTENDEE_RESPONSE.pending;\n return (\n <EventGuest key={attendee.id} direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n <EventGuestAvatar ownSx={{width: 24, height: 24, fontSize: 11}}>{initialsOf(attendee.name)}</EventGuestAvatar>\n <EventGuestName variant=\"body2\" ownSx={{flexGrow: 1, minWidth: 0}} noWrap>{attendee.name}</EventGuestName>\n <Chip size=\"small\" variant=\"outlined\" color={RESPONSE_COLOR[response] ?? 'default'} label={t(`details.response.${response}`)} />\n </EventGuest>\n );\n })}\n </Stack>\n </DetailRow>\n )}\n {reminder.unit !== REMINDER_UNIT.none && (\n <DetailRow Icon={NotificationsNoneRoundedIcon} label={t('details.reminder')}>\n <Typography variant=\"body2\">{t(`details.reminderUnit.${reminder.unit}`, {count: reminder.count})}</Typography>\n </DetailRow>\n )}\n {event.description && (\n <DetailRow Icon={NotesRoundedIcon} label={t('details.notes')}>\n <EventNotes variant=\"body2\" ownSx={{whiteSpace: 'pre-wrap'}}>{event.description}</EventNotes>\n </DetailRow>\n )}\n {actions && <EventActions direction=\"row\" spacing={1} useFlexGap ownSx={{justifyContent: 'flex-end', flexWrap: 'wrap'}}>{actions}</EventActions>}\n </Stack>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
10
10
  import { enUS, esES } from "@mui/x-date-pickers/locales";
11
11
  import { translateValidationMessage } from "../forms/validationMessages.js";
12
12
  import FieldFrame from "../inputs/FieldFrame.js";
13
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
13
14
  const CALENDAR_DATE_FORMAT = "YYYY-MM-DD";
14
15
  const DATE_LOOK_BY_LANGUAGE = Object.freeze({
15
16
  es: { adapterLocale: "es", displayFormat: "DD/MM/YYYY", localeText: esES.components.MuiLocalizationProvider.defaultProps.localeText },
@@ -33,8 +34,10 @@ function DateInput({
33
34
  openTo = "day",
34
35
  disabled = false,
35
36
  inputRef,
36
- texts
37
+ texts,
38
+ ...unknownProps
37
39
  }) {
40
+ warnUnknownProps("DateInput", unknownProps);
38
41
  const { t, language } = useToolkitTranslation("common", texts);
39
42
  const generatedId = useId();
40
43
  const inputId = `${name ?? "date"}-${generatedId}`;
@@ -1 +1 @@
1
- {"version":3,"file":"DateInput.js","sources":["../../src/datePickers/DateInput.jsx"],"sourcesContent":["// Campo de FECHA suelto con el DatePicker de MUI X (@stackcraftmx/calcita/date-pickers), con la etiqueta arriba como los\n// demás campos del paquete.\n//\n// Al formulario (y al servidor) viaja la fecha de calendario 'AAAA-MM-DD' en texto; en pantalla se ve DD/MM/AAAA en español\n// y MM/DD/AAAA en inglés, con el calendario y sus textos en el idioma activo. '' = sin fecha. Una fecha de calendario no\n// tiene hora ni zona: nunca se pasa por Date (que la movería de día según la zona).\n//\n// Cada campo trae su LocalizationProvider con dayjs: la pantalla no tiene que poner nada alrededor.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport dayjs from 'dayjs';\nimport 'dayjs/locale/es';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {AdapterDayjs} from '@mui/x-date-pickers/AdapterDayjs';\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\nimport {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider';\nimport {enUS, esES} from '@mui/x-date-pickers/locales';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\n/** Cómo viaja una fecha de calendario. */\nexport const CALENDAR_DATE_FORMAT = 'YYYY-MM-DD';\n\n// cómo se ve y con qué textos sale el calendario en cada idioma\nconst DATE_LOOK_BY_LANGUAGE = Object.freeze({\n es: {adapterLocale: 'es', displayFormat: 'DD/MM/YYYY', localeText: esES.components.MuiLocalizationProvider.defaultProps.localeText},\n en: {adapterLocale: 'en', displayFormat: 'MM/DD/YYYY', localeText: enUS.components.MuiLocalizationProvider.defaultProps.localeText},\n});\n\n/**\n * Una fecha de calendario 'AAAA-MM-DD' como la quiere el DatePicker; null si está vacía.\n *\n * @param {string|null|undefined} calendarDate\n * @returns {import('dayjs').Dayjs|null}\n */\nfunction pickerValueOf(calendarDate) {\n // dayjs lee 'AAAA-MM-DD' como día local (a diferencia de new Date, que lo toma en UTC y lo mueve de día)\n return calendarDate ? dayjs(calendarDate) : null;\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'AAAA-MM-DD' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM-DD' ('' al borrar o con una fecha a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minDate] 'AAAA-MM-DD'\n * @param {string} [props.maxDate] 'AAAA-MM-DD'\n * @param {boolean} [props.disableFuture] p. ej. una fecha de nacimiento\n * @param {boolean} [props.disablePast] p. ej. una cita\n * @param {'day'|'month'|'year'} [props.openTo] por dónde abre el calendario (year para un nacimiento)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function DateInput({\n name, label, value, onChange, onBlur, error, helperText = ' ', minDate, maxDate, disableFuture = false, disablePast = false,\n openTo = 'day', disabled = false, inputRef, texts,\n}) {\n const {t, language} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'date'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const look = DATE_LOOK_BY_LANGUAGE[language] ?? DATE_LOOK_BY_LANGUAGE.es;\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={look.adapterLocale} localeText={look.localeText}>\n <DatePicker\n format={look.displayFormat}\n value={pickerValueOf(value)}\n minDate={pickerValueOf(minDate) ?? undefined}\n maxDate={pickerValueOf(maxDate) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n openTo={openTo}\n views={openTo === 'year' ? ['year', 'month', 'day'] : undefined}\n inputRef={inputRef}\n // una fecha a medias (25/1_/____) todavía no es fecha: viaja como vacía hasta que se complete\n onChange={(pickedDate) => onChange(pickedDate?.isValid() ? pickedDate.format(CALENDAR_DATE_FORMAT) : '')}\n onClose={onBlur}\n slotProps={{\n textField: {\n id: inputId, name, size: 'small', fullWidth: true, onBlur, error: Boolean(errorText), helperText: errorText ?? helperText,\n },\n }}\n />\n </LocalizationProvider>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAuBO,MAAM,uBAAuB;AAGpC,MAAM,wBAAwB,OAAO,OAAO;AAAA,EACxC,IAAI,EAAC,eAAe,MAAM,eAAe,cAAc,YAAY,KAAK,WAAW,wBAAwB,aAAa,WAAA;AAAA,EACxH,IAAI,EAAC,eAAe,MAAM,eAAe,cAAc,YAAY,KAAK,WAAW,wBAAwB,aAAa,WAAA;AAC5H,CAAC;AAQD,SAAS,cAAc,cAAc;AAEjC,SAAO,eAAe,MAAM,YAAY,IAAI;AAChD;AAoBA,SAAwB,UAAU;AAAA,EAC9B;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAO,aAAa;AAAA,EAAK;AAAA,EAAS;AAAA,EAAS,gBAAgB;AAAA,EAAO,cAAc;AAAA,EACtH,SAAS;AAAA,EAAO,WAAW;AAAA,EAAO;AAAA,EAAU;AAChD,GAAG;AACC,QAAM,EAAC,GAAG,SAAA,IAAY,sBAAsB,UAAU,KAAK;AAC3D,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,MAAM,IAAI,WAAW;AAChD,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,OAAO,sBAAsB,QAAQ,KAAK,sBAAsB;AAEtE,SACI,oBAAC,YAAA,EAAW,SAAkB,OAC1B,UAAA,oBAAC,sBAAA,EAAqB,aAAa,cAAc,eAAe,KAAK,eAAe,YAAY,KAAK,YACjG,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,QAAQ,KAAK;AAAA,MACb,OAAO,cAAc,KAAK;AAAA,MAC1B,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,WAAW,SAAS,CAAC,QAAQ,SAAS,KAAK,IAAI;AAAA,MACtD;AAAA,MAEA,UAAU,CAAC,eAAe,SAAS,YAAY,YAAY,WAAW,OAAO,oBAAoB,IAAI,EAAE;AAAA,MACvG,SAAS;AAAA,MACT,WAAW;AAAA,QACP,WAAW;AAAA,UACP,IAAI;AAAA,UAAS;AAAA,UAAM,MAAM;AAAA,UAAS,WAAW;AAAA,UAAM;AAAA,UAAQ,OAAO,QAAQ,SAAS;AAAA,UAAG,YAAY,aAAa;AAAA,QAAA;AAAA,MACnH;AAAA,IACJ;AAAA,EAAA,GAER,EAAA,CACJ;AAER;"}
1
+ {"version":3,"file":"DateInput.js","sources":["../../src/datePickers/DateInput.jsx"],"sourcesContent":["// Campo de FECHA suelto con el DatePicker de MUI X (@stackcraftmx/calcita/date-pickers), con la etiqueta arriba como los\n// demás campos del paquete.\n//\n// Al formulario (y al servidor) viaja la fecha de calendario 'AAAA-MM-DD' en texto; en pantalla se ve DD/MM/AAAA en español\n// y MM/DD/AAAA en inglés, con el calendario y sus textos en el idioma activo. '' = sin fecha. Una fecha de calendario no\n// tiene hora ni zona: nunca se pasa por Date (que la movería de día según la zona).\n//\n// Cada campo trae su LocalizationProvider con dayjs: la pantalla no tiene que poner nada alrededor.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport dayjs from 'dayjs';\nimport 'dayjs/locale/es';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {AdapterDayjs} from '@mui/x-date-pickers/AdapterDayjs';\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\nimport {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider';\nimport {enUS, esES} from '@mui/x-date-pickers/locales';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/** Cómo viaja una fecha de calendario. */\nexport const CALENDAR_DATE_FORMAT = 'YYYY-MM-DD';\n\n// cómo se ve y con qué textos sale el calendario en cada idioma\nconst DATE_LOOK_BY_LANGUAGE = Object.freeze({\n es: {adapterLocale: 'es', displayFormat: 'DD/MM/YYYY', localeText: esES.components.MuiLocalizationProvider.defaultProps.localeText},\n en: {adapterLocale: 'en', displayFormat: 'MM/DD/YYYY', localeText: enUS.components.MuiLocalizationProvider.defaultProps.localeText},\n});\n\n/**\n * Una fecha de calendario 'AAAA-MM-DD' como la quiere el DatePicker; null si está vacía.\n *\n * @param {string|null|undefined} calendarDate\n * @returns {import('dayjs').Dayjs|null}\n */\nfunction pickerValueOf(calendarDate) {\n // dayjs lee 'AAAA-MM-DD' como día local (a diferencia de new Date, que lo toma en UTC y lo mueve de día)\n return calendarDate ? dayjs(calendarDate) : null;\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'AAAA-MM-DD' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM-DD' ('' al borrar o con una fecha a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minDate] 'AAAA-MM-DD'\n * @param {string} [props.maxDate] 'AAAA-MM-DD'\n * @param {boolean} [props.disableFuture] p. ej. una fecha de nacimiento\n * @param {boolean} [props.disablePast] p. ej. una cita\n * @param {'day'|'month'|'year'} [props.openTo] por dónde abre el calendario (year para un nacimiento)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function DateInput({\n name, label, value, onChange, onBlur, error, helperText = ' ', minDate, maxDate, disableFuture = false, disablePast = false,\n openTo = 'day', disabled = false, inputRef, texts,\n ...unknownProps\n}) {\n warnUnknownProps('DateInput', unknownProps);\n const {t, language} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'date'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const look = DATE_LOOK_BY_LANGUAGE[language] ?? DATE_LOOK_BY_LANGUAGE.es;\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={look.adapterLocale} localeText={look.localeText}>\n <DatePicker\n format={look.displayFormat}\n value={pickerValueOf(value)}\n minDate={pickerValueOf(minDate) ?? undefined}\n maxDate={pickerValueOf(maxDate) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n openTo={openTo}\n views={openTo === 'year' ? ['year', 'month', 'day'] : undefined}\n inputRef={inputRef}\n // una fecha a medias (25/1_/____) todavía no es fecha: viaja como vacía hasta que se complete\n onChange={(pickedDate) => onChange(pickedDate?.isValid() ? pickedDate.format(CALENDAR_DATE_FORMAT) : '')}\n onClose={onBlur}\n slotProps={{\n textField: {\n id: inputId, name, size: 'small', fullWidth: true, onBlur, error: Boolean(errorText), helperText: errorText ?? helperText,\n },\n }}\n />\n </LocalizationProvider>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAwBO,MAAM,uBAAuB;AAGpC,MAAM,wBAAwB,OAAO,OAAO;AAAA,EACxC,IAAI,EAAC,eAAe,MAAM,eAAe,cAAc,YAAY,KAAK,WAAW,wBAAwB,aAAa,WAAA;AAAA,EACxH,IAAI,EAAC,eAAe,MAAM,eAAe,cAAc,YAAY,KAAK,WAAW,wBAAwB,aAAa,WAAA;AAC5H,CAAC;AAQD,SAAS,cAAc,cAAc;AAEjC,SAAO,eAAe,MAAM,YAAY,IAAI;AAChD;AAoBA,SAAwB,UAAU;AAAA,EAC9B;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAO,aAAa;AAAA,EAAK;AAAA,EAAS;AAAA,EAAS,gBAAgB;AAAA,EAAO,cAAc;AAAA,EACtH,SAAS;AAAA,EAAO,WAAW;AAAA,EAAO;AAAA,EAAU;AAAA,EAC5C,GAAG;AACP,GAAG;AACC,mBAAiB,aAAa,YAAY;AAC1C,QAAM,EAAC,GAAG,SAAA,IAAY,sBAAsB,UAAU,KAAK;AAC3D,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,MAAM,IAAI,WAAW;AAChD,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,OAAO,sBAAsB,QAAQ,KAAK,sBAAsB;AAEtE,SACI,oBAAC,YAAA,EAAW,SAAkB,OAC1B,UAAA,oBAAC,sBAAA,EAAqB,aAAa,cAAc,eAAe,KAAK,eAAe,YAAY,KAAK,YACjG,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,QAAQ,KAAK;AAAA,MACb,OAAO,cAAc,KAAK;AAAA,MAC1B,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,WAAW,SAAS,CAAC,QAAQ,SAAS,KAAK,IAAI;AAAA,MACtD;AAAA,MAEA,UAAU,CAAC,eAAe,SAAS,YAAY,YAAY,WAAW,OAAO,oBAAoB,IAAI,EAAE;AAAA,MACvG,SAAS;AAAA,MACT,WAAW;AAAA,QACP,WAAW;AAAA,UACP,IAAI;AAAA,UAAS;AAAA,UAAM,MAAM;AAAA,UAAS,WAAW;AAAA,UAAM;AAAA,UAAQ,OAAO,QAAQ,SAAS;AAAA,UAAG,YAAY,aAAa;AAAA,QAAA;AAAA,MACnH;AAAA,IACJ;AAAA,EAAA,GAER,EAAA,CACJ;AAER;"}
@@ -28,6 +28,7 @@ import { useFormatters } from "../toolkit/useFormatters.js";
28
28
  import { isGlassTheme, glassStylesOf } from "../theme/glassSurface.js";
29
29
  import ComponentTexts from "../toolkit/ComponentTexts.js";
30
30
  import { piecePart } from "../theme/themePieces.js";
31
+ import { warnUnknownProps } from "../toolkit/unknownProps.js";
31
32
  const THEME_NAME = "CalcitaDownloadManager";
32
33
  const DownloadsDoneIcon = piecePart(THEME_NAME, "DoneIcon", CheckCircleIcon);
33
34
  const DownloadsFailedIcon = piecePart(THEME_NAME, "FailedIcon", ErrorOutlineIcon);
@@ -142,7 +143,8 @@ function DownloadRow({ download }) {
142
143
  ] })
143
144
  );
144
145
  }
145
- function DownloadManager({ texts } = {}) {
146
+ function DownloadManager({ texts, ...unknownProps } = {}) {
147
+ warnUnknownProps("DownloadManager", unknownProps);
146
148
  const { t } = useToolkitTranslation("common", texts);
147
149
  const downloads = useSelector(downloadsActor, (snapshot) => snapshot.context.downloads);
148
150
  const [isExpanded, setIsExpanded] = useState(true);