@stackcraftmx/calcita 0.47.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -13
- package/cli/calcita.js +17 -0
- package/cli/checkTexts.js +266 -0
- package/cli/packageVersions.js +6 -1
- package/cli/projectFiles.js +4 -2
- package/cli/templates/project/README.md.tpl +21 -1
- package/cli/templates/project/eslint.config.js.tpl +27 -0
- package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
- package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
- package/cli/templates/project/vite.config.js.tpl +8 -1
- package/dist/DataTable/AuditTrailDialog.js +3 -2
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -2
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -2
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +3 -2
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/EmptyState.js +3 -2
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +3 -2
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +3 -2
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +95 -28
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/ServerTableFilterDrawer.js +8 -3
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -1
- package/dist/DataTable/StackedCell.js +60 -0
- package/dist/DataTable/StackedCell.js.map +1 -0
- package/dist/DataTable/TableCardsView.js +88 -0
- package/dist/DataTable/TableCardsView.js.map +1 -0
- package/dist/DataTable/columnWidths.js +30 -0
- package/dist/DataTable/columnWidths.js.map +1 -0
- package/dist/DataTable/columns.js +47 -1
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/DataTable/fixedColumns.js +24 -0
- package/dist/DataTable/fixedColumns.js.map +1 -0
- package/dist/DataTable/useTableView.js +29 -0
- package/dist/DataTable/useTableView.js.map +1 -0
- package/dist/access/PermissionMatrix.js +3 -2
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountProvider.js +1 -1
- package/dist/account/AccountSecuritySection.js +3 -2
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +3 -2
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ProfileScreen.js +3 -2
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +3 -2
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +3 -2
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +3 -2
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +3 -2
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +3 -2
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActionButton.js +11 -4
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/appShell/BarActions.js +3 -2
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +3 -2
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +3 -2
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +3 -2
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +3 -2
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +3 -2
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +3 -2
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +3 -2
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +3 -2
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +3 -2
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +3 -2
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +3 -2
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +3 -2
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +3 -2
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +3 -2
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/datePickers/DateInput.js +28 -22
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +33 -27
- package/dist/datePickers/DateTimeInput.js.map +1 -1
- package/dist/datePickers/MonthInput.js +27 -21
- package/dist/datePickers/MonthInput.js.map +1 -1
- package/dist/datePickers/TimeInput.js +26 -20
- package/dist/datePickers/TimeInput.js.map +1 -1
- package/dist/datePickers/usePickerField.js +5 -4
- package/dist/datePickers/usePickerField.js.map +1 -1
- package/dist/downloads/DownloadManager.js +3 -2
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +3 -2
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +3 -2
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +3 -2
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +3 -2
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +3 -2
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/FormWizard.js +21 -6
- package/dist/forms/FormWizard.js.map +1 -1
- package/dist/forms/SubmitButton.js +7 -2
- package/dist/forms/SubmitButton.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +3 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/fieldRules.js +30 -0
- package/dist/forms/fieldRules.js.map +1 -0
- package/dist/forms/security/SecondStepForm.js +3 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +3 -2
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/forms/useValidationText.js +18 -0
- package/dist/forms/useValidationText.js.map +1 -0
- package/dist/forms/validation.js +35 -31
- package/dist/forms/validation.js.map +1 -1
- package/dist/forms/validationMessages.js +64 -7
- package/dist/forms/validationMessages.js.map +1 -1
- package/dist/forms.js +3 -0
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +3 -2
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/hookForm/FormWizard.js +19 -5
- package/dist/hookForm/FormWizard.js.map +1 -1
- package/dist/hookForm/SubmitButton.js +7 -2
- package/dist/hookForm/SubmitButton.js.map +1 -1
- package/dist/inbox/InboxLayout.js +3 -2
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +3 -2
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +3 -2
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +193 -163
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +32 -25
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +8 -5
- package/dist/inputs/CheckboxGroupInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +7 -4
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +10 -4
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +1 -1
- package/dist/inputs/MaskedInput.js +1 -0
- package/dist/inputs/MaskedInput.js.map +1 -1
- package/dist/inputs/NumberInput.js +29 -24
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/RepeatingFields.js +8 -5
- package/dist/inputs/RepeatingFields.js.map +1 -1
- package/dist/inputs/SelectInput.js +8 -5
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +5 -3
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +8 -5
- package/dist/inputs/TransferListInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +48 -19
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/inputs/useWizardSteps.js +36 -0
- package/dist/inputs/useWizardSteps.js.map +1 -0
- package/dist/inputs/wizardSteps.js +32 -0
- package/dist/inputs/wizardSteps.js.map +1 -0
- package/dist/kanban/KanbanBoard.js +3 -2
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/legal/CookieNotice.js +3 -2
- package/dist/legal/CookieNotice.js.map +1 -1
- package/dist/legal/TermsAcceptanceDialog.js +3 -2
- package/dist/legal/TermsAcceptanceDialog.js.map +1 -1
- package/dist/locales/en.js +24 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +24 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/maps/MapView.js +3 -2
- package/dist/maps/MapView.js.map +1 -1
- package/dist/mexico/CfdiSummary.js +3 -2
- package/dist/mexico/CfdiSummary.js.map +1 -1
- package/dist/mexico/EfirmaFields.js +3 -2
- package/dist/mexico/EfirmaFields.js.map +1 -1
- package/dist/mexico/mexicoIdentifiers.js +5 -0
- package/dist/mexico/mexicoIdentifiers.js.map +1 -1
- package/dist/mexico/mexicoRules.js +9 -4
- package/dist/mexico/mexicoRules.js.map +1 -1
- package/dist/mexico.js +4 -2
- package/dist/pwa/InstallAppSetting.js +3 -2
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +3 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +3 -2
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/react-hook-form.js +3 -0
- package/dist/react-hook-form.js.map +1 -1
- package/dist/richText/RichTextEditor.js +7 -4
- package/dist/richText/RichTextEditor.js.map +1 -1
- package/dist/richText/RichTextView.js +3 -2
- package/dist/richText/RichTextView.js.map +1 -1
- package/dist/security/ActivityLog.js +3 -2
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +3 -2
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +3 -2
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/table.js +21 -2
- package/dist/table.js.map +1 -1
- package/dist/theme/themePieces.js +70 -4
- package/dist/theme/themePieces.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +8 -1
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/browserNavigation.js +19 -4
- package/dist/toolkit/browserNavigation.js.map +1 -1
- package/dist/toolkit/combineTexts.js +32 -0
- package/dist/toolkit/combineTexts.js.map +1 -0
- package/dist/toolkit/hashNavigation.js +17 -3
- package/dist/toolkit/hashNavigation.js.map +1 -1
- package/dist/toolkit/navigationPaths.js +15 -0
- package/dist/toolkit/navigationPaths.js.map +1 -1
- package/dist/toolkit/pathIndex.js +66 -0
- package/dist/toolkit/pathIndex.js.map +1 -0
- package/dist/toolkit/toolkitSettings.js +19 -1
- package/dist/toolkit/toolkitSettings.js.map +1 -1
- package/dist/types/DataTable/AuditTrailDialog.d.ts +6 -17
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +6 -5
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +6 -9
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +6 -14
- package/dist/types/DataTable/EmptyState.d.ts +6 -9
- package/dist/types/DataTable/FilterChips.d.ts +6 -11
- package/dist/types/DataTable/GoToPage.d.ts +6 -9
- package/dist/types/DataTable/ServerTable.d.ts +16 -80
- package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +6 -13
- package/dist/types/DataTable/StackedCell.d.ts +12 -0
- package/dist/types/DataTable/TableCardsView.d.ts +27 -0
- package/dist/types/DataTable/columnWidths.d.ts +21 -0
- package/dist/types/DataTable/columns.d.ts +111 -0
- package/dist/types/DataTable/fixedColumns.d.ts +29 -0
- package/dist/types/DataTable/index.d.ts +6 -1
- package/dist/types/DataTable/useTableView.d.ts +22 -0
- package/dist/types/access/PermissionMatrix.d.ts +6 -10
- package/dist/types/account/AccountSecuritySection.d.ts +6 -7
- package/dist/types/account/DemoTotpCodeButton.d.ts +6 -5
- package/dist/types/account/ProfileScreen.d.ts +6 -18
- package/dist/types/account/SignInScreen.d.ts +6 -13
- package/dist/types/appShell/AccountMenu.d.ts +6 -11
- package/dist/types/appShell/AppLayout.d.ts +3 -40
- package/dist/types/appShell/AppearanceSettings.d.ts +6 -12
- package/dist/types/appShell/AuthLayout.d.ts +3 -11
- package/dist/types/appShell/BarActionButton.d.ts +7 -10
- package/dist/types/appShell/BarActions.d.ts +6 -15
- package/dist/types/appShell/BarCapsule.d.ts +6 -9
- package/dist/types/appShell/BarSearch.d.ts +6 -15
- package/dist/types/appShell/HeaderTools.d.ts +6 -16
- package/dist/types/appShell/MobileTabBar.d.ts +6 -13
- package/dist/types/appShell/SettingsMenu.d.ts +6 -10
- package/dist/types/appShell/SideMenu.d.ts +6 -20
- package/dist/types/appShell/SideMenuEntry.d.ts +6 -11
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +6 -12
- package/dist/types/appShell/TabBarEditor.d.ts +6 -9
- package/dist/types/appShell/TopMenu.d.ts +6 -5
- package/dist/types/appShell/Wordmark.d.ts +6 -7
- package/dist/types/auth/PasswordChecklist.d.ts +6 -9
- package/dist/types/calendar/EventCalendar.d.ts +6 -23
- package/dist/types/calendar/EventDetails.d.ts +6 -10
- package/dist/types/datePickers/DateInput.d.ts +7 -22
- package/dist/types/datePickers/DateTimeInput.d.ts +7 -27
- package/dist/types/datePickers/MonthInput.d.ts +7 -21
- package/dist/types/datePickers/TimeInput.d.ts +7 -21
- package/dist/types/datePickers/usePickerField.d.ts +5 -1
- package/dist/types/downloads/DownloadManager.d.ts +6 -6
- package/dist/types/feedback/ConfirmDialog.d.ts +6 -18
- package/dist/types/feedback/ModalWaiting.d.ts +6 -9
- package/dist/types/feedback/NewBuildNotice.d.ts +6 -10
- package/dist/types/filepond/FileDropzone.d.ts +6 -29
- package/dist/types/forms/FormProblemAlert.d.ts +6 -5
- package/dist/types/forms/FormWizard.d.ts +12 -1
- package/dist/types/forms/SubmitButton.d.ts +6 -6
- package/dist/types/forms/auth/SignInForm.d.ts +6 -11
- package/dist/types/forms/fieldRules.d.ts +44 -0
- package/dist/types/forms/index.d.ts +1 -0
- package/dist/types/forms/security/SecondStepForm.d.ts +6 -14
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +6 -21
- package/dist/types/forms/useValidationText.d.ts +12 -0
- package/dist/types/forms/validation.d.ts +117 -174
- package/dist/types/forms/validationMessages.d.ts +54 -6
- package/dist/types/forms.d.ts +1 -1
- package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
- package/dist/types/hookForm/FormWizard.d.ts +12 -1
- package/dist/types/hookForm/SubmitButton.d.ts +6 -13
- package/dist/types/inbox/InboxLayout.d.ts +6 -17
- package/dist/types/inbox/MessageList.d.ts +6 -18
- package/dist/types/inbox/MessageView.d.ts +6 -14
- package/dist/types/index.d.ts +10 -2
- package/dist/types/inputs/CatalogInput.d.ts +7 -24
- package/dist/types/inputs/CheckboxGroupInput.d.ts +7 -18
- package/dist/types/inputs/ColorInput.d.ts +7 -16
- package/dist/types/inputs/FieldFrame.d.ts +6 -10
- package/dist/types/inputs/MaskedInput.d.ts +3 -0
- package/dist/types/inputs/NumberInput.d.ts +7 -20
- package/dist/types/inputs/RepeatingFields.d.ts +7 -28
- package/dist/types/inputs/SelectInput.d.ts +7 -20
- package/dist/types/inputs/TextInput.d.ts +3 -0
- package/dist/types/inputs/TransferListInput.d.ts +7 -20
- package/dist/types/inputs/WizardLayout.d.ts +11 -21
- package/dist/types/inputs/useWizardSteps.d.ts +23 -0
- package/dist/types/inputs/wizardSteps.d.ts +65 -0
- package/dist/types/kanban/KanbanBoard.d.ts +6 -25
- package/dist/types/legal/CookieNotice.d.ts +6 -16
- package/dist/types/legal/TermsAcceptanceDialog.d.ts +6 -18
- package/dist/types/locales/en.d.ts +24 -0
- package/dist/types/locales/es.d.ts +24 -0
- package/dist/types/maps/MapView.d.ts +6 -17
- package/dist/types/mexico/CfdiSummary.d.ts +6 -11
- package/dist/types/mexico/EfirmaFields.d.ts +6 -16
- package/dist/types/mexico/mexicoIdentifiers.d.ts +7 -0
- package/dist/types/mexico/mexicoRules.d.ts +19 -3
- package/dist/types/mexico.d.ts +2 -2
- package/dist/types/pwa/InstallAppSetting.d.ts +6 -6
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +6 -9
- package/dist/types/pwa/InstallPromptBanner.d.ts +6 -11
- package/dist/types/react-hook-form.d.ts +1 -0
- package/dist/types/richText/RichTextEditor.d.ts +7 -22
- package/dist/types/richText/RichTextView.d.ts +6 -9
- package/dist/types/security/ActivityLog.d.ts +6 -9
- package/dist/types/security/RecoveryCodesDialog.d.ts +6 -11
- package/dist/types/security/SessionsCard.d.ts +6 -11
- package/dist/types/table.d.ts +3 -1
- package/dist/types/theme/themePieces.d.ts +55 -3
- package/dist/types/toolkit/ToolkitProvider.d.ts +13 -1
- package/dist/types/toolkit/browserNavigation.d.ts +7 -3
- package/dist/types/toolkit/combineTexts.d.ts +6 -0
- package/dist/types/toolkit/hashNavigation.d.ts +4 -2
- package/dist/types/toolkit/navigationPaths.d.ts +35 -0
- package/dist/types/toolkit/pathIndex.d.ts +27 -0
- package/dist/types/toolkit/toolkitSettings.d.ts +7 -4
- package/dist/types/ui/actions/ActionsMenu.d.ts +9 -11
- package/dist/types/ui/actions/CopyButton.d.ts +6 -12
- package/dist/types/ui/actions/DownloadMenu.d.ts +6 -29
- package/dist/types/ui/actions/SecretField.d.ts +6 -11
- package/dist/types/ui/actions/SelectionActions.d.ts +6 -13
- package/dist/types/ui/cards/RecordCard.d.ts +16 -29
- package/dist/types/ui/cards/RecordCardSkeleton.d.ts +6 -11
- package/dist/types/ui/countdown/Countdown.d.ts +6 -15
- package/dist/types/ui/documents/PdfViewerDialog.d.ts +6 -13
- package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
- package/dist/types/ui/filters/FilterDrawer.d.ts +6 -17
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
- package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
- package/dist/types/ui/help/HelpButton.d.ts +6 -10
- package/dist/types/ui/help/HelpCarouselDialog.d.ts +6 -14
- package/dist/types/ui/index.d.ts +1 -1
- package/dist/types/ui/labels/MarkedText.d.ts +10 -0
- package/dist/types/ui/labels/Tag.d.ts +6 -12
- package/dist/types/ui/labels/markedTextPieces.d.ts +22 -0
- package/dist/types/ui/layout/CollapsibleSection.d.ts +6 -12
- package/dist/types/ui/layout/ContentDialog.d.ts +6 -10
- package/dist/types/ui/layout/DefinitionList.d.ts +6 -9
- package/dist/types/ui/layout/DetailHeader.d.ts +6 -10
- package/dist/types/ui/layout/ErrorPage.d.ts +6 -17
- package/dist/types/ui/layout/FullPageLoader.d.ts +6 -6
- package/dist/types/ui/layout/GlassPanel.d.ts +6 -8
- package/dist/types/ui/layout/PageHeader.d.ts +6 -10
- package/dist/types/ui/layout/PageHeading.d.ts +6 -11
- package/dist/types/ui/layout/ProfileHeader.d.ts +6 -11
- package/dist/types/ui/layout/ScrollableRow.d.ts +6 -13
- package/dist/types/ui/layout/SectionCard.d.ts +6 -8
- package/dist/types/ui/metrics/BarList.d.ts +6 -12
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +6 -19
- package/dist/types/ui/metrics/ChartTooltip.d.ts +6 -6
- package/dist/types/ui/metrics/ColumnChart.d.ts +6 -25
- package/dist/types/ui/metrics/LineChart.d.ts +6 -20
- package/dist/types/ui/metrics/MetricCard.d.ts +6 -17
- package/dist/types/ui/metrics/TableToggle.d.ts +6 -6
- package/dist/types/ui/metrics/chartParts.d.ts +15 -30
- package/dist/types/ui/navigation/BackButton.d.ts +7 -9
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +6 -9
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -8
- package/dist/types/ui/navigation/LanguageToggle.d.ts +6 -8
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +6 -7
- package/dist/types/ui/navigation/RouteTabs.d.ts +7 -12
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
- package/dist/types/upload/AttachmentsInput.d.ts +11 -1
- package/dist/types/upload/FileDropzone.d.ts +6 -21
- package/dist/types/upload/FileInput.d.ts +7 -24
- package/dist/types/upload/FileUploadInput.d.ts +7 -31
- package/dist/types/upload/ImageInput.d.ts +7 -25
- package/dist/types/upload/TemplateDownloadButton.d.ts +6 -18
- package/dist/ui/actions/ActionsMenu.js +14 -5
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +8 -3
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +3 -2
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SecretField.js +8 -3
- package/dist/ui/actions/SecretField.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +3 -2
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/cards/RecordCard.js +10 -5
- package/dist/ui/cards/RecordCard.js.map +1 -1
- package/dist/ui/cards/RecordCardSkeleton.js +8 -3
- package/dist/ui/cards/RecordCardSkeleton.js.map +1 -1
- package/dist/ui/countdown/Countdown.js +3 -2
- package/dist/ui/countdown/Countdown.js.map +1 -1
- package/dist/ui/documents/PdfViewerDialog.js +3 -2
- package/dist/ui/documents/PdfViewerDialog.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +3 -2
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/FilterDrawer.js +3 -2
- package/dist/ui/filters/FilterDrawer.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +3 -2
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +3 -2
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/help/HelpButton.js +8 -3
- package/dist/ui/help/HelpButton.js.map +1 -1
- package/dist/ui/help/HelpCarouselDialog.js +3 -2
- package/dist/ui/help/HelpCarouselDialog.js.map +1 -1
- package/dist/ui/labels/MarkedText.js +31 -0
- package/dist/ui/labels/MarkedText.js.map +1 -0
- package/dist/ui/labels/Tag.js +3 -2
- package/dist/ui/labels/Tag.js.map +1 -1
- package/dist/ui/labels/markedTextPieces.js +64 -0
- package/dist/ui/labels/markedTextPieces.js.map +1 -0
- package/dist/ui/layout/CollapsibleSection.js +3 -2
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +3 -2
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +3 -2
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +3 -2
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +3 -2
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +3 -2
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/GlassPanel.js +3 -2
- package/dist/ui/layout/GlassPanel.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +3 -2
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +3 -2
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +3 -2
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/ScrollableRow.js +3 -2
- package/dist/ui/layout/ScrollableRow.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +3 -2
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +3 -2
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +3 -2
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +3 -2
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +3 -2
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +3 -2
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +3 -2
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +3 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +7 -4
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/navigation/BackButton.js +7 -2
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +3 -2
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +3 -2
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +3 -2
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +3 -2
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/navigation/RouteTabs.js +8 -4
- package/dist/ui/navigation/RouteTabs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +3 -2
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +9 -6
- package/dist/upload/AttachmentsInput.js.map +1 -1
- package/dist/upload/FileChip.js +2 -1
- package/dist/upload/FileChip.js.map +1 -1
- package/dist/upload/FileDropzone.js +5 -4
- package/dist/upload/FileDropzone.js.map +1 -1
- package/dist/upload/FileInput.js +9 -6
- package/dist/upload/FileInput.js.map +1 -1
- package/dist/upload/FileUploadInput.js +9 -6
- package/dist/upload/FileUploadInput.js.map +1 -1
- package/dist/upload/ImageInput.js +8 -5
- package/dist/upload/ImageInput.js.map +1 -1
- package/dist/upload/TemplateDownloadButton.js +3 -2
- package/dist/upload/TemplateDownloadButton.js.map +1 -1
- package/package.json +1 -1
- package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"columnWidths.js","sources":["../../src/DataTable/columnWidths.js"],"sourcesContent":["// Cuánto mide una columna para que su texto más largo no se corte: se mide el texto con la letra de la página (en el\n// navegador, con un canvas) y se le suma lo que lleva alrededor. Sin navegador (al pintar en el servidor) se calcula por\n// letras: un poco de más es mejor que «Registrado pendiente de docum…».\n//\n// Funciones puras, no renderizan: function (regla de la casa). Pruebas en columnWidths.test.js.\n\n// la letra del estatus (StatusChip): 13 px de grosor medio\nconst STATUS_FONT_SIZE_PIXELS = 13;\nconst STATUS_FONT_WEIGHT = 500;\n// el ancho promedio de una letra de 13 px cuando no hay con qué medir\nconst AVERAGE_CHARACTER_WIDTH_PIXELS = 7.4;\n// lo que va alrededor del texto: el punto y su espacio (o el contorno del chip) y el respiro de la celda\nconst STATUS_EXTRA_PIXELS = Object.freeze({dot: 40, chip: 52});\n// el encabezado lleva además sus íconos de orden y de filtro\nconst HEADER_ICONS_PIXELS = 56;\n\nlet measuringContext = null;\n\n/**\n * El ancho de un texto en px con la letra de la página; sin navegador, por número de letras.\n *\n * @param {string} text\n * @param {{fontSize?: number, fontWeight?: number}} [options]\n * @returns {number}\n */\nexport function textWidthOf(text, {fontSize = STATUS_FONT_SIZE_PIXELS, fontWeight = STATUS_FONT_WEIGHT} = {}) {\n const value = String(text ?? '');\n if (typeof document === 'undefined') {\n return value.length * AVERAGE_CHARACTER_WIDTH_PIXELS * (fontSize / STATUS_FONT_SIZE_PIXELS);\n }\n measuringContext ??= document.createElement('canvas').getContext('2d');\n if (!measuringContext) {\n return value.length * AVERAGE_CHARACTER_WIDTH_PIXELS * (fontSize / STATUS_FONT_SIZE_PIXELS);\n }\n const fontFamily = getComputedStyle(document.body).fontFamily || 'system-ui, sans-serif';\n measuringContext.font = `${fontWeight} ${fontSize}px ${fontFamily}`;\n return measuringContext.measureText(value).width;\n}\n\n/**\n * El ancho de una columna de estatus: el de su etiqueta más larga (o el del encabezado, con sus íconos) más lo de alrededor.\n *\n * @param {string[]} labels el encabezado primero y luego las etiquetas, ya traducidos\n * @param {{variant?: 'dot'|'chip'}} [options]\n * @returns {number} px, redondeado hacia arriba\n */\nexport function statusColumnWidthOf([header, ...labels], {variant = 'dot'} = {}) {\n const extra = STATUS_EXTRA_PIXELS[variant] ?? STATUS_EXTRA_PIXELS.dot;\n const widest = Math.max(0, ...labels.map((label) => textWidthOf(label) + extra));\n const headerWidth = textWidthOf(header, {fontWeight: 600}) + HEADER_ICONS_PIXELS;\n return Math.ceil(Math.max(widest, headerWidth));\n}\n"],"names":[],"mappings":"AAOA,MAAM,0BAA0B;AAChC,MAAM,qBAAqB;AAE3B,MAAM,iCAAiC;AAEvC,MAAM,sBAAsB,OAAO,OAAO,EAAC,KAAK,IAAI,MAAM,GAAE,CAAC;AAE7D,MAAM,sBAAsB;AAE5B,IAAI,mBAAmB;AAShB,SAAS,YAAY,MAAM,EAAC,WAAW,yBAAyB,aAAa,mBAAkB,IAAI,IAAI;AAC1G,QAAM,QAAQ,OAAO,QAAQ,EAAE;AAC/B,MAAI,OAAO,aAAa,aAAa;AACjC,WAAO,MAAM,SAAS,kCAAkC,WAAW;AAAA,EACvE;AACA,uBAAqB,SAAS,cAAc,QAAQ,EAAE,WAAW,IAAI;AACrE,MAAI,CAAC,kBAAkB;AACnB,WAAO,MAAM,SAAS,kCAAkC,WAAW;AAAA,EACvE;AACA,QAAM,aAAa,iBAAiB,SAAS,IAAI,EAAE,cAAc;AACjE,mBAAiB,OAAO,GAAG,UAAU,IAAI,QAAQ,MAAM,UAAU;AACjE,SAAO,iBAAiB,YAAY,KAAK,EAAE;AAC/C;AASO,SAAS,oBAAoB,CAAC,QAAQ,GAAG,MAAM,GAAG,EAAC,UAAU,MAAK,IAAI,IAAI;AAC7E,QAAM,QAAQ,oBAAoB,OAAO,KAAK,oBAAoB;AAClE,QAAM,SAAS,KAAK,IAAI,GAAG,GAAG,OAAO,IAAI,CAAC,UAAU,YAAY,KAAK,IAAI,KAAK,CAAC;AAC/E,QAAM,cAAc,YAAY,QAAQ,EAAC,YAAY,IAAG,CAAC,IAAI;AAC7D,SAAO,KAAK,KAAK,KAAK,IAAI,QAAQ,WAAW,CAAC;AAClD;"}
|
|
@@ -8,9 +8,19 @@ import "./ColumnFilters/HeaderFilterPopover.js";
|
|
|
8
8
|
import MultiSelectColumnFilter from "./ColumnFilters/MultiSelectColumnFilter.js";
|
|
9
9
|
import SortableFilterHeader from "./ColumnFilters/SortableFilterHeader.js";
|
|
10
10
|
import TextColumnFilter from "./ColumnFilters/TextColumnFilter.js";
|
|
11
|
+
import { statusColumnWidthOf } from "./columnWidths.js";
|
|
12
|
+
import StackedCell from "./StackedCell.js";
|
|
11
13
|
import { useTableFilter } from "./filterContext.js";
|
|
12
14
|
import { FILTER_KINDS, isFilterActive } from "./useTableState.js";
|
|
15
|
+
import { labelOf, lookup } from "../dictionaries/dictionaries.js";
|
|
16
|
+
import ActionsMenu from "../ui/actions/ActionsMenu.js";
|
|
17
|
+
import DictionaryText from "../ui/labels/DictionaryText.js";
|
|
18
|
+
import StatusChip from "../ui/labels/StatusChip.js";
|
|
13
19
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
20
|
+
const DETAIL_COLUMN_FIELD = "__detail";
|
|
21
|
+
const ACTIONS_COLUMN_FIELD = "__actions";
|
|
22
|
+
const ACTIONS_COLUMN_WIDTH = 56;
|
|
23
|
+
const COLUMN_STACKED_KEY = "calcitaStacked";
|
|
14
24
|
function activeCountOf(kind, value) {
|
|
15
25
|
if (!isFilterActive(kind, value)) {
|
|
16
26
|
return 0;
|
|
@@ -99,7 +109,7 @@ function DetailCell({ row, onOpen, ariaLabelOf }) {
|
|
|
99
109
|
function detailColumn({ onOpen, ariaLabelOf, ...unknownOptions }) {
|
|
100
110
|
warnUnknownProps("detailColumn", unknownOptions);
|
|
101
111
|
return {
|
|
102
|
-
field:
|
|
112
|
+
field: DETAIL_COLUMN_FIELD,
|
|
103
113
|
headerName: "",
|
|
104
114
|
sortable: false,
|
|
105
115
|
filterable: false,
|
|
@@ -109,9 +119,45 @@ function detailColumn({ onOpen, ariaLabelOf, ...unknownOptions }) {
|
|
|
109
119
|
renderCell: ({ row }) => /* @__PURE__ */ jsx(DetailCell, { row, onOpen, ariaLabelOf })
|
|
110
120
|
};
|
|
111
121
|
}
|
|
122
|
+
function stackedColumn({ field, label, entries, sort = true, filter = true, ...gridColumnProps }) {
|
|
123
|
+
return {
|
|
124
|
+
...textColumn({ field, label, sort, filter, ...gridColumnProps }),
|
|
125
|
+
[COLUMN_STACKED_KEY]: true,
|
|
126
|
+
renderCell: (params) => /* @__PURE__ */ jsx(StackedCell, { entries: entries(params.row, params.formattedValue ?? params.value) })
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function statusColumn({ field, label, dictionary, t, short = false, variant = "dot", sort = true, filter = true, facetKey, ...gridColumnProps }) {
|
|
130
|
+
const labels = t ? Object.keys(dictionary).map((value) => labelOf(lookup(dictionary, value), t)) : [];
|
|
131
|
+
const width = gridColumnProps.width ?? (labels.length > 0 ? statusColumnWidthOf([label, ...labels], { variant }) : 160);
|
|
132
|
+
const renderOption = (value) => /* @__PURE__ */ jsx(DictionaryText, { dictionary, value });
|
|
133
|
+
const base = filter ? facetColumn({ field, label, facetKey, renderOption, sort, ...gridColumnProps, width }) : textColumn({ field, label, sort, filter: false, ...gridColumnProps, width });
|
|
134
|
+
return {
|
|
135
|
+
...base,
|
|
136
|
+
renderCell: ({ row }) => /* @__PURE__ */ jsx(StatusChip, { dictionary, value: row[field], short, variant })
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function actionsColumn({ label, actionsOf, labelOf: labelOfRow, width = ACTIONS_COLUMN_WIDTH, ...unknownOptions }) {
|
|
140
|
+
warnUnknownProps("actionsColumn", unknownOptions);
|
|
141
|
+
return {
|
|
142
|
+
field: ACTIONS_COLUMN_FIELD,
|
|
143
|
+
headerName: "",
|
|
144
|
+
sortable: false,
|
|
145
|
+
filterable: false,
|
|
146
|
+
disableColumnMenu: true,
|
|
147
|
+
width,
|
|
148
|
+
align: "center",
|
|
149
|
+
renderCell: ({ row }) => /* @__PURE__ */ jsx(ActionsMenu, { label: labelOfRow?.(row) ?? label, actions: actionsOf(row) })
|
|
150
|
+
};
|
|
151
|
+
}
|
|
112
152
|
export {
|
|
153
|
+
ACTIONS_COLUMN_FIELD,
|
|
154
|
+
COLUMN_STACKED_KEY,
|
|
155
|
+
DETAIL_COLUMN_FIELD,
|
|
156
|
+
actionsColumn,
|
|
113
157
|
detailColumn,
|
|
114
158
|
facetColumn,
|
|
159
|
+
stackedColumn,
|
|
160
|
+
statusColumn,
|
|
115
161
|
textColumn
|
|
116
162
|
};
|
|
117
163
|
//# sourceMappingURL=columns.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"columns.js","sources":["../../src/DataTable/columns.jsx"],"sourcesContent":["// Armadores de columna para ServerTable: la definición del DataGrid + su encabezado con orden y filtro.\n//\n// El filtro vive en el ENCABEZADO de su columna porque ahí está el dato. Aquí solo se COMPONE: el encabezado, el\n// globo y cada tipo de filtro viven en ColumnFilters/, uno por archivo.\n//\n// OJO: sortable y filterable del DataGrid van en false siempre. El orden y el filtro los pide la tabla al SERVIDOR;\n// los nativos del DataGrid ordenarían y filtrarían solo la página que ya llegó.\n//\n// Armadores genéricos: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport {MultiSelectColumnFilter, SortableFilterHeader, TextColumnFilter} from '@/DataTable/ColumnFilters';\nimport {useTableFilter} from '@/DataTable/filterContext.js';\nimport {FILTER_KINDS, isFilterActive} from '@/DataTable/useTableState.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/** Cuántos valores tiene puestos esta columna (lo que muestra el badge). */\nfunction activeCountOf(kind, value) {\n if (!isFilterActive(kind, value)) {\n return 0;\n }\n return kind === FILTER_KINDS.multiSelect ? value.length : 1;\n}\n\n/** Lo que todo encabezado necesita del contexto de la tabla. */\nfunction useColumnHeader(field, kind) {\n const {filters, setFilter, clearFilter, sortOrders, toggleSort, clearSort, facets} = useTableFilter();\n\n return {\n value: filters[field],\n setValue: (value) => setFilter(field, value),\n onClear: () => clearFilter(field),\n sortEntry: sortOrders.find((sortOrder) => sortOrder.column === field),\n onSort: () => toggleSort(field),\n onClearSort: clearSort,\n activeCount: activeCountOf(kind, filters[field]),\n facets,\n };\n}\n\nfunction TextHeader({field, label, sort, filter}) {\n const header = useColumnHeader(field, FILTER_KINDS.text);\n\n return (\n <SortableFilterHeader label={label} sortable={sort} filterable={filter} sortEntry={header.sortEntry}\n onSort={header.onSort} onClearSort={header.onClearSort} activeCount={header.activeCount}\n onClear={header.onClear}>\n <TextColumnFilter value={header.value ?? ''} onChange={header.setValue} />\n </SortableFilterHeader>\n );\n}\n\nfunction FacetHeader({field, label, facetKey, renderOption, sort}) {\n const header = useColumnHeader(field, FILTER_KINDS.multiSelect);\n\n return (\n <SortableFilterHeader label={label} sortable={sort} sortEntry={header.sortEntry} onSort={header.onSort}\n onClearSort={header.onClearSort} activeCount={header.activeCount} onClear={header.onClear}>\n <MultiSelectColumnFilter value={header.value ?? []} onChange={header.setValue}\n options={header.facets[facetKey] ?? []} renderOption={renderOption} />\n </SortableFilterHeader>\n );\n}\n\n/**\n * Las props de columna del DataGrid que más se usan, con su tipo, y cualquier otra suya (Record). OJO: tipo propio y no\n * GridColDef del DataGrid: apuntar a sus tipos hace que un proyecto estricto (skipLibCheck: false) revise los internos del\n * DataGrid, que no compilan con TypeScript 7.\n *\n * @typedef {{width?: number, minWidth?: number, maxWidth?: number, flex?: number, align?: 'left'|'center'|'right', headerAlign?: 'left'|'center'|'right', valueFormatter?: (value: any, row: any) => string, valueGetter?: (value: any, row: any) => any, renderCell?: (params: any) => import('react').ReactNode} & Record<string, any>} GridColumnProps\n */\n\n/**\n * Columna de texto con orden y filtro \"contiene\" en el servidor.\n *\n * @param {{field: string, label: string, sort?: boolean, filter?: boolean} & GridColumnProps} columnDeclaration\n * label ya traducido; lo demás son props de columna del DataGrid (width, flex, valueFormatter, renderCell…)\n */\nexport function textColumn({field, label, sort = true, filter = true, ...gridColumnProps}) {\n return {\n field,\n headerName: label,\n sortable: false,\n filterable: false,\n ...gridColumnProps,\n renderHeader: () => <TextHeader field={field} label={label} sort={sort} filter={filter} />,\n };\n}\n\n/**\n * Columna con filtro de opciones alimentado por las FACETAS del servidor.\n *\n * @param {{field: string, label: string, facetKey?: string, renderOption?: (value: string) => import('react').ReactNode, sort?: boolean} & GridColumnProps} columnDeclaration\n */\nexport function facetColumn({field, label, facetKey, renderOption, sort = true, ...gridColumnProps}) {\n return {\n field,\n headerName: label,\n sortable: false,\n filterable: false,\n ...gridColumnProps,\n renderHeader: () => (\n <FacetHeader field={field} label={label} facetKey={facetKey ?? field} renderOption={renderOption} sort={sort} />\n ),\n };\n}\n\nfunction DetailCell({row, onOpen, ariaLabelOf}) {\n const {t} = useToolkitTranslation('table');\n const ariaLabel = ariaLabelOf?.(row) ?? t('detail.ariaLabel');\n\n return (\n <Tooltip title={t('detail.tooltip')}>\n <IconButton size=\"small\" aria-label={ariaLabel} onClick={() => onOpen(row)}>\n <VisibilityOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n );\n}\n\n/**\n * Columna de acción al final de la fila: un icono que abre el detalle (la ruta propia del renglón). Un icono y no la\n * fila entera a propósito: la fila como botón se dispara al seleccionar texto y no se ve que se puede abrir.\n *\n * El globo del icono se cambia con los textos de la tabla (ServerTable texts={{detail: {tooltip: '…'}}}), no aquí.\n *\n * @param {{onOpen: (row: any) => void, ariaLabelOf?: (row: any) => string}} declaration ariaLabelOf ya traducido\n */\nexport function detailColumn({onOpen, ariaLabelOf, ...unknownOptions}) {\n // un nombre que no existe (label, tooltip) se perdía sin decir nada: se avisa mientras se programa\n warnUnknownProps('detailColumn', unknownOptions);\n return {\n field: '__detail',\n headerName: '',\n sortable: false,\n filterable: false,\n disableColumnMenu: true,\n width: 64,\n align: 'center',\n renderCell: ({row}) => <DetailCell row={row} onOpen={onOpen} ariaLabelOf={ariaLabelOf} />,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"columns.js","sources":["../../src/DataTable/columns.jsx"],"sourcesContent":["// Armadores de columna para ServerTable: la definición del DataGrid + su encabezado con orden y filtro.\n//\n// El filtro vive en el ENCABEZADO de su columna porque ahí está el dato. Aquí solo se COMPONE: el encabezado, el\n// globo y cada tipo de filtro viven en ColumnFilters/, uno por archivo.\n//\n// OJO: sortable y filterable del DataGrid van en false siempre. El orden y el filtro los pide la tabla al SERVIDOR;\n// los nativos del DataGrid ordenarían y filtrarían solo la página que ya llegó.\n//\n// Armadores genéricos: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport {MultiSelectColumnFilter, SortableFilterHeader, TextColumnFilter} from '@/DataTable/ColumnFilters';\nimport {statusColumnWidthOf} from '@/DataTable/columnWidths.js';\nimport StackedCell from '@/DataTable/StackedCell.jsx';\nimport {useTableFilter} from '@/DataTable/filterContext.js';\nimport {FILTER_KINDS, isFilterActive} from '@/DataTable/useTableState.js';\nimport {labelOf, lookup} from '@/dictionaries/dictionaries.js';\nimport ActionsMenu from '@/ui/actions/ActionsMenu.jsx';\nimport DictionaryText from '@/ui/labels/DictionaryText.jsx';\nimport StatusChip from '@/ui/labels/StatusChip.jsx';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/** El campo de las columnas de la casa que no son datos: para nombrarlas en fixedColumn ({fixedColumn: [ACTIONS_COLUMN_FIELD, …]}). */\nexport const DETAIL_COLUMN_FIELD = '__detail';\nexport const ACTIONS_COLUMN_FIELD = '__actions';\n// el ancho de la columna del «⋯»: el botón de 34 px y su respiro\nconst ACTIONS_COLUMN_WIDTH = 56;\n// la marca que deja una columna de datos apilados: con ella la tabla pone los renglones a su alto (ServerTable)\nexport const COLUMN_STACKED_KEY = 'calcitaStacked';\n\n/** Cuántos valores tiene puestos esta columna (lo que muestra el badge). */\nfunction activeCountOf(kind, value) {\n if (!isFilterActive(kind, value)) {\n return 0;\n }\n return kind === FILTER_KINDS.multiSelect ? value.length : 1;\n}\n\n/** Lo que todo encabezado necesita del contexto de la tabla. */\nfunction useColumnHeader(field, kind) {\n const {filters, setFilter, clearFilter, sortOrders, toggleSort, clearSort, facets} = useTableFilter();\n\n return {\n value: filters[field],\n setValue: (value) => setFilter(field, value),\n onClear: () => clearFilter(field),\n sortEntry: sortOrders.find((sortOrder) => sortOrder.column === field),\n onSort: () => toggleSort(field),\n onClearSort: clearSort,\n activeCount: activeCountOf(kind, filters[field]),\n facets,\n };\n}\n\nfunction TextHeader({field, label, sort, filter}) {\n const header = useColumnHeader(field, FILTER_KINDS.text);\n\n return (\n <SortableFilterHeader label={label} sortable={sort} filterable={filter} sortEntry={header.sortEntry}\n onSort={header.onSort} onClearSort={header.onClearSort} activeCount={header.activeCount}\n onClear={header.onClear}>\n <TextColumnFilter value={header.value ?? ''} onChange={header.setValue} />\n </SortableFilterHeader>\n );\n}\n\nfunction FacetHeader({field, label, facetKey, renderOption, sort}) {\n const header = useColumnHeader(field, FILTER_KINDS.multiSelect);\n\n return (\n <SortableFilterHeader label={label} sortable={sort} sortEntry={header.sortEntry} onSort={header.onSort}\n onClearSort={header.onClearSort} activeCount={header.activeCount} onClear={header.onClear}>\n <MultiSelectColumnFilter value={header.value ?? []} onChange={header.setValue}\n options={header.facets[facetKey] ?? []} renderOption={renderOption} />\n </SortableFilterHeader>\n );\n}\n\n/**\n * Las props de columna del DataGrid que más se usan, con su tipo, y cualquier otra suya (Record). OJO: tipo propio y no\n * GridColDef del DataGrid: apuntar a sus tipos hace que un proyecto estricto (skipLibCheck: false) revise los internos del\n * DataGrid, que no compilan con TypeScript 7.\n *\n * @typedef {{width?: number, minWidth?: number, maxWidth?: number, flex?: number, align?: 'left'|'center'|'right', headerAlign?: 'left'|'center'|'right', valueFormatter?: (value: any, row: any) => string, valueGetter?: (value: any, row: any) => any, renderCell?: (params: any) => import('react').ReactNode} & Record<string, any>} GridColumnProps\n */\n\n/**\n * Columna de texto con orden y filtro \"contiene\" en el servidor.\n *\n * @param {{field: string, label: string, sort?: boolean, filter?: boolean} & GridColumnProps} columnDeclaration\n * label ya traducido; lo demás son props de columna del DataGrid (width, flex, valueFormatter, renderCell…)\n */\nexport function textColumn({field, label, sort = true, filter = true, ...gridColumnProps}) {\n return {\n field,\n headerName: label,\n sortable: false,\n filterable: false,\n ...gridColumnProps,\n renderHeader: () => <TextHeader field={field} label={label} sort={sort} filter={filter} />,\n };\n}\n\n/**\n * Columna con filtro de opciones alimentado por las FACETAS del servidor.\n *\n * @param {{field: string, label: string, facetKey?: string, renderOption?: (value: string) => import('react').ReactNode, sort?: boolean} & GridColumnProps} columnDeclaration\n */\nexport function facetColumn({field, label, facetKey, renderOption, sort = true, ...gridColumnProps}) {\n return {\n field,\n headerName: label,\n sortable: false,\n filterable: false,\n ...gridColumnProps,\n renderHeader: () => (\n <FacetHeader field={field} label={label} facetKey={facetKey ?? field} renderOption={renderOption} sort={sort} />\n ),\n };\n}\n\nfunction DetailCell({row, onOpen, ariaLabelOf}) {\n const {t} = useToolkitTranslation('table');\n const ariaLabel = ariaLabelOf?.(row) ?? t('detail.ariaLabel');\n\n return (\n <Tooltip title={t('detail.tooltip')}>\n <IconButton size=\"small\" aria-label={ariaLabel} onClick={() => onOpen(row)}>\n <VisibilityOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n );\n}\n\n/**\n * Columna de acción al final de la fila: un icono que abre el detalle (la ruta propia del renglón). Un icono y no la\n * fila entera a propósito: la fila como botón se dispara al seleccionar texto y no se ve que se puede abrir.\n *\n * El globo del icono se cambia con los textos de la tabla (ServerTable texts={{detail: {tooltip: '…'}}}), no aquí.\n *\n * @param {{onOpen: (row: any) => void, ariaLabelOf?: (row: any) => string}} declaration ariaLabelOf ya traducido\n */\nexport function detailColumn({onOpen, ariaLabelOf, ...unknownOptions}) {\n // un nombre que no existe (label, tooltip) se perdía sin decir nada: se avisa mientras se programa\n warnUnknownProps('detailColumn', unknownOptions);\n return {\n field: DETAIL_COLUMN_FIELD,\n headerName: '',\n sortable: false,\n filterable: false,\n disableColumnMenu: true,\n width: 64,\n align: 'center',\n renderCell: ({row}) => <DetailCell row={row} onOpen={onOpen} ariaLabelOf={ariaLabelOf} />,\n };\n}\n\n/**\n * Columna de datos apilados: los que se leen de corrido en un solo lugar, uno debajo del otro, los que hagan falta (la\n * empresa, su RFC y su régimen; el contacto, su correo y su teléfono). El primero es el principal (en negritas) y los demás\n * van en gris. Cada dato va solo, si el encabezado ya dice qué son, o con su etiqueta arriba ({label, value}) si el\n * encabezado nombra al grupo. Con orden y filtro en el servidor como textColumn, por el campo de la columna. La tabla pone\n * los renglones a su alto sola.\n *\n * stackedColumn({field: 'business_name', label: 'Empresa', entries: (row) => [row.business_name, row.rfc, row.regime]})\n * stackedColumn({field: 'created_at', label: 'Fechas', entries: (row) => [\n * {label: 'Alta', value: formatDate(row.created_at)},\n * {label: 'Último cambio', value: formatDate(row.updated_at)},\n * ]})\n *\n * @param {{field: string, label: string, entries: (row: any, value: any) => import('@/DataTable/StackedCell.jsx').StackedEntry[], sort?: boolean, filter?: boolean} & GridColumnProps} columnDeclaration\n * entries: los datos de cada renglón en orden; `value` es el de la columna ya con su valueFormatter. Un dato vacío\n * no se pinta (salvo el principal o uno con etiqueta: enseñan «—»)\n */\nexport function stackedColumn({field, label, entries, sort = true, filter = true, ...gridColumnProps}) {\n return {\n ...textColumn({field, label, sort, filter, ...gridColumnProps}),\n [COLUMN_STACKED_KEY]: true,\n renderCell: (params) => <StackedCell entries={entries(params.row, params.formattedValue ?? params.value)} />,\n };\n}\n\n/**\n * Columna de estatus desde un diccionario: el estatus con su color y su explicación (StatusChip), filtro con las facetas del\n * servidor (como facetColumn) y un ancho que alcanza para la etiqueta más larga del diccionario. Con `t` el ancho se mide\n * con las etiquetas en el idioma de la pantalla (sin él, 160 px o el `width` que se le dé).\n *\n * statusColumn({field: 'status', label: 'Estado', dictionary: ORDER_STATUS, t})\n *\n * @param {{field: string, label: string, dictionary: Record<string, object>, t?: (key: string) => string, short?: boolean, variant?: 'dot'|'chip', sort?: boolean, filter?: boolean, facetKey?: string} & GridColumnProps} columnDeclaration\n * filter false: sin filtro en el encabezado (cuando el servidor no da facetas de este campo)\n */\nexport function statusColumn({field, label, dictionary, t, short = false, variant = 'dot', sort = true, filter = true, facetKey, ...gridColumnProps}) {\n const labels = t ? Object.keys(dictionary).map((value) => labelOf(lookup(dictionary, value), t)) : [];\n const width = gridColumnProps.width ?? (labels.length > 0 ? statusColumnWidthOf([label, ...labels], {variant}) : 160);\n const renderOption = (value) => <DictionaryText dictionary={dictionary} value={value} />;\n const base = filter\n ? facetColumn({field, label, facetKey, renderOption, sort, ...gridColumnProps, width})\n : textColumn({field, label, sort, filter: false, ...gridColumnProps, width});\n return {\n ...base,\n renderCell: ({row}) => <StatusChip dictionary={dictionary} value={row[field]} short={short} variant={variant} />,\n };\n}\n\n/**\n * Columna con el menú «⋯» de cada renglón (ActionsMenu): las acciones van en su columna con un ícono, no con clic en toda\n * la fila. Va de un ancho fijo, así se puede dejar pegada al deslizar: fixedColumn={[ACTIONS_COLUMN_FIELD, 'company']}.\n *\n * actionsColumn({label: 'Acciones del cliente', actionsOf: (row) => [{key: 'open', label: 'Abrir', to: `/clientes/${row.id}`}]})\n *\n * @param {object} declaration\n * @param {string} declaration.label el nombre del botón (lector de pantalla y su globo), ya traducido\n * @param {(row: any) => Array<object>} declaration.actionsOf las acciones de ese renglón, con la forma de ActionsMenu\n * @param {(row: any) => string} [declaration.labelOf] el nombre del botón por renglón («Acciones de Acme»)\n * @param {number} [declaration.width] por omisión 56\n */\nexport function actionsColumn({label, actionsOf, labelOf: labelOfRow, width = ACTIONS_COLUMN_WIDTH, ...unknownOptions}) {\n warnUnknownProps('actionsColumn', unknownOptions);\n return {\n field: ACTIONS_COLUMN_FIELD,\n headerName: '',\n sortable: false,\n filterable: false,\n disableColumnMenu: true,\n width,\n align: 'center',\n renderCell: ({row}) => <ActionsMenu label={labelOfRow?.(row) ?? label} actions={actionsOf(row)} />,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA0BO,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAEpC,MAAM,uBAAuB;AAEtB,MAAM,qBAAqB;AAGlC,SAAS,cAAc,MAAM,OAAO;AAChC,MAAI,CAAC,eAAe,MAAM,KAAK,GAAG;AAC9B,WAAO;AAAA,EACX;AACA,SAAO,SAAS,aAAa,cAAc,MAAM,SAAS;AAC9D;AAGA,SAAS,gBAAgB,OAAO,MAAM;AAClC,QAAM,EAAC,SAAS,WAAW,aAAa,YAAY,YAAY,WAAW,OAAA,IAAU,eAAA;AAErF,SAAO;AAAA,IACH,OAAO,QAAQ,KAAK;AAAA,IACpB,UAAU,CAAC,UAAU,UAAU,OAAO,KAAK;AAAA,IAC3C,SAAS,MAAM,YAAY,KAAK;AAAA,IAChC,WAAW,WAAW,KAAK,CAAC,cAAc,UAAU,WAAW,KAAK;AAAA,IACpE,QAAQ,MAAM,WAAW,KAAK;AAAA,IAC9B,aAAa;AAAA,IACb,aAAa,cAAc,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC/C;AAAA,EAAA;AAER;AAEA,SAAS,WAAW,EAAC,OAAO,OAAO,MAAM,UAAS;AAC9C,QAAM,SAAS,gBAAgB,OAAO,aAAa,IAAI;AAEvD,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAqB;AAAA,MAAc,UAAU;AAAA,MAAM,YAAY;AAAA,MAAQ,WAAW,OAAO;AAAA,MACpE,QAAQ,OAAO;AAAA,MAAQ,aAAa,OAAO;AAAA,MAAa,aAAa,OAAO;AAAA,MAC5E,SAAS,OAAO;AAAA,MAClC,UAAA,oBAAC,oBAAiB,OAAO,OAAO,SAAS,IAAI,UAAU,OAAO,SAAA,CAAU;AAAA,IAAA;AAAA,EAAA;AAGpF;AAEA,SAAS,YAAY,EAAC,OAAO,OAAO,UAAU,cAAc,QAAO;AAC/D,QAAM,SAAS,gBAAgB,OAAO,aAAa,WAAW;AAE9D,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAqB;AAAA,MAAc,UAAU;AAAA,MAAM,WAAW,OAAO;AAAA,MAAW,QAAQ,OAAO;AAAA,MAC1E,aAAa,OAAO;AAAA,MAAa,aAAa,OAAO;AAAA,MAAa,SAAS,OAAO;AAAA,MACpG,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UAAwB,OAAO,OAAO,SAAS,CAAA;AAAA,UAAI,UAAU,OAAO;AAAA,UAC5C,SAAS,OAAO,OAAO,QAAQ,KAAK,CAAA;AAAA,UAAI;AAAA,QAAA;AAAA,MAAA;AAAA,IAA4B;AAAA,EAAA;AAGzG;AAgBO,SAAS,WAAW,EAAC,OAAO,OAAO,OAAO,MAAM,SAAS,MAAM,GAAG,mBAAkB;AACvF,SAAO;AAAA,IACH;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,GAAG;AAAA,IACH,cAAc,MAAM,oBAAC,cAAW,OAAc,OAAc,MAAY,OAAA,CAAgB;AAAA,EAAA;AAEhG;AAOO,SAAS,YAAY,EAAC,OAAO,OAAO,UAAU,cAAc,OAAO,MAAM,GAAG,mBAAkB;AACjG,SAAO;AAAA,IACH;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,GAAG;AAAA,IACH,cAAc,MACV,oBAAC,aAAA,EAAY,OAAc,OAAc,UAAU,YAAY,OAAO,cAA4B,KAAA,CAAY;AAAA,EAAA;AAG1H;AAEA,SAAS,WAAW,EAAC,KAAK,QAAQ,eAAc;AAC5C,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AACzC,QAAM,YAAY,cAAc,GAAG,KAAK,EAAE,kBAAkB;AAE5D,SACI,oBAAC,WAAQ,OAAO,EAAE,gBAAgB,GAC9B,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,WAAW,SAAS,MAAM,OAAO,GAAG,GACrE,8BAAC,wBAAA,EAAuB,UAAS,SAAQ,EAAA,CAC7C,EAAA,CACJ;AAER;AAUO,SAAS,aAAa,EAAC,QAAQ,aAAa,GAAG,kBAAiB;AAEnE,mBAAiB,gBAAgB,cAAc;AAC/C,SAAO;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,YAAY,CAAC,EAAC,UAAS,oBAAC,YAAA,EAAW,KAAU,QAAgB,YAAA,CAA0B;AAAA,EAAA;AAE/F;AAmBO,SAAS,cAAc,EAAC,OAAO,OAAO,SAAS,OAAO,MAAM,SAAS,MAAM,GAAG,mBAAkB;AACnG,SAAO;AAAA,IACH,GAAG,WAAW,EAAC,OAAO,OAAO,MAAM,QAAQ,GAAG,iBAAgB;AAAA,IAC9D,CAAC,kBAAkB,GAAG;AAAA,IACtB,YAAY,CAAC,WAAW,oBAAC,aAAA,EAAY,SAAS,QAAQ,OAAO,KAAK,OAAO,kBAAkB,OAAO,KAAK,EAAA,CAAG;AAAA,EAAA;AAElH;AAYO,SAAS,aAAa,EAAC,OAAO,OAAO,YAAY,GAAG,QAAQ,OAAO,UAAU,OAAO,OAAO,MAAM,SAAS,MAAM,UAAU,GAAG,mBAAkB;AAClJ,QAAM,SAAS,IAAI,OAAO,KAAK,UAAU,EAAE,IAAI,CAAC,UAAU,QAAQ,OAAO,YAAY,KAAK,GAAG,CAAC,CAAC,IAAI,CAAA;AACnG,QAAM,QAAQ,gBAAgB,UAAU,OAAO,SAAS,IAAI,oBAAoB,CAAC,OAAO,GAAG,MAAM,GAAG,EAAC,QAAA,CAAQ,IAAI;AACjH,QAAM,eAAe,CAAC,UAAU,oBAAC,gBAAA,EAAe,YAAwB,OAAc;AACtF,QAAM,OAAO,SACP,YAAY,EAAC,OAAO,OAAO,UAAU,cAAc,MAAM,GAAG,iBAAiB,MAAA,CAAM,IACnF,WAAW,EAAC,OAAO,OAAO,MAAM,QAAQ,OAAO,GAAG,iBAAiB,OAAM;AAC/E,SAAO;AAAA,IACH,GAAG;AAAA,IACH,YAAY,CAAC,EAAC,UAAS,oBAAC,YAAA,EAAW,YAAwB,OAAO,IAAI,KAAK,GAAG,OAAc,QAAA,CAAkB;AAAA,EAAA;AAEtH;AAcO,SAAS,cAAc,EAAC,OAAO,WAAW,SAAS,YAAY,QAAQ,sBAAsB,GAAG,kBAAiB;AACpH,mBAAiB,iBAAiB,cAAc;AAChD,SAAO;AAAA,IACH,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB;AAAA,IACA,OAAO;AAAA,IACP,YAAY,CAAC,EAAC,IAAA,MAAS,oBAAC,aAAA,EAAY,OAAO,aAAa,GAAG,KAAK,OAAO,SAAS,UAAU,GAAG,EAAA,CAAG;AAAA,EAAA;AAExG;"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const DATA_GRID_DEFAULT_COLUMN_WIDTH = 100;
|
|
2
|
+
function fixedFieldsOf(fixedColumn) {
|
|
3
|
+
if (!fixedColumn) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
return Array.isArray(fixedColumn) ? fixedColumn.filter(Boolean) : [fixedColumn];
|
|
7
|
+
}
|
|
8
|
+
function fixedColumnOffsetsOf(fields, columns, { startLeft = 0 } = {}) {
|
|
9
|
+
const byField = new Map(columns.map((column) => [column.field, column]));
|
|
10
|
+
let left = startLeft;
|
|
11
|
+
return fields.map((field, index) => {
|
|
12
|
+
const column = byField.get(field) ?? {};
|
|
13
|
+
const isLast = index === fields.length - 1;
|
|
14
|
+
const hasFixedWidth = typeof column.width === "number" && !column.flex;
|
|
15
|
+
const placed = { field, left, isLast, hasUnknownWidth: !isLast && !hasFixedWidth };
|
|
16
|
+
left += hasFixedWidth ? column.width : column.minWidth ?? column.width ?? DATA_GRID_DEFAULT_COLUMN_WIDTH;
|
|
17
|
+
return placed;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
fixedColumnOffsetsOf,
|
|
22
|
+
fixedFieldsOf
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=fixedColumns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixedColumns.js","sources":["../../src/DataTable/fixedColumns.js"],"sourcesContent":["// Dónde se pega cada columna fija de la tabla al deslizar de lado: la primera junto a la orilla (o junto a la casilla de\n// elegir renglones) y cada siguiente corrida lo que miden las de antes.\n//\n// OJO: el desplazamiento sale del ancho que la columna declara (`width`). Una columna fija que NO es la última y no trae un\n// ancho fijo (va con `flex` o sin ancho) no se puede sumar: su ancho real lo decide el navegador. Ahí se usa su minWidth\n// (o el de siempre del DataGrid, 100 px) y se avisa: la que sigue podría quedar encima. La ÚLTIMA puede ser flexible\n// (su ancho no mueve a nadie).\n//\n// Funciones puras, no renderizan: function (regla de la casa).\n\n// el ancho de una columna del DataGrid que no dice el suyo\nconst DATA_GRID_DEFAULT_COLUMN_WIDTH = 100;\n\n/**\n * Los campos fijos como lista, con el orden en que se dieron.\n *\n * @param {string|string[]|null|undefined} fixedColumn\n * @returns {string[]}\n */\nexport function fixedFieldsOf(fixedColumn) {\n if (!fixedColumn) {\n return [];\n }\n return Array.isArray(fixedColumn) ? fixedColumn.filter(Boolean) : [fixedColumn];\n}\n\n/**\n * Cada columna fija con su `left` en px.\n *\n * @param {string[]} fields los campos fijos, de izquierda a derecha\n * @param {Array<{field: string, width?: number, minWidth?: number, flex?: number}>} columns las columnas de la tabla\n * @param {{startLeft?: number}} [options] startLeft: lo que ya va pegado antes (la casilla de elegir renglones)\n * @returns {Array<{field: string, left: number, isLast: boolean, hasUnknownWidth: boolean}>} hasUnknownWidth: no es la última\n * y su ancho no es fijo (la siguiente podría encimarse)\n */\nexport function fixedColumnOffsetsOf(fields, columns, {startLeft = 0} = {}) {\n const byField = new Map(columns.map((column) => [column.field, column]));\n let left = startLeft;\n return fields.map((field, index) => {\n const column = byField.get(field) ?? {};\n const isLast = index === fields.length - 1;\n const hasFixedWidth = typeof column.width === 'number' && !column.flex;\n const placed = {field, left, isLast, hasUnknownWidth: !isLast && !hasFixedWidth};\n left += hasFixedWidth ? column.width : (column.minWidth ?? column.width ?? DATA_GRID_DEFAULT_COLUMN_WIDTH);\n return placed;\n });\n}\n"],"names":[],"mappings":"AAWA,MAAM,iCAAiC;AAQhC,SAAS,cAAc,aAAa;AACvC,MAAI,CAAC,aAAa;AACd,WAAO,CAAA;AAAA,EACX;AACA,SAAO,MAAM,QAAQ,WAAW,IAAI,YAAY,OAAO,OAAO,IAAI,CAAC,WAAW;AAClF;AAWO,SAAS,qBAAqB,QAAQ,SAAS,EAAC,YAAY,EAAC,IAAI,IAAI;AACxE,QAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,OAAO,MAAM,CAAC,CAAC;AACvE,MAAI,OAAO;AACX,SAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AAChC,UAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,CAAA;AACrC,UAAM,SAAS,UAAU,OAAO,SAAS;AACzC,UAAM,gBAAgB,OAAO,OAAO,UAAU,YAAY,CAAC,OAAO;AAClE,UAAM,SAAS,EAAC,OAAO,MAAM,QAAQ,iBAAiB,CAAC,UAAU,CAAC,cAAa;AAC/E,YAAQ,gBAAgB,OAAO,QAAS,OAAO,YAAY,OAAO,SAAS;AAC3E,WAAO;AAAA,EACX,CAAC;AACL;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useQueryState, parseAsStringLiteral } from "nuqs";
|
|
4
|
+
const TABLE_VIEW = Object.freeze({ table: "table", cards: "cards" });
|
|
5
|
+
const VIEWS = Object.freeze(Object.values(TABLE_VIEW));
|
|
6
|
+
function useTableView({ prefix, view, defaultView = TABLE_VIEW.table, onViewChange }) {
|
|
7
|
+
const [urlView, setUrlView] = useQueryState(
|
|
8
|
+
`${prefix ?? "table"}_view`,
|
|
9
|
+
parseAsStringLiteral(VIEWS).withDefault(defaultView).withOptions({ history: "replace" })
|
|
10
|
+
);
|
|
11
|
+
const [localView, setLocalView] = useState(defaultView);
|
|
12
|
+
const currentView = view ?? (prefix ? urlView : localView);
|
|
13
|
+
const setView = (nextView) => {
|
|
14
|
+
if (view === void 0) {
|
|
15
|
+
if (prefix) {
|
|
16
|
+
setUrlView(nextView === defaultView ? null : nextView);
|
|
17
|
+
} else {
|
|
18
|
+
setLocalView(nextView);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
onViewChange?.(nextView);
|
|
22
|
+
};
|
|
23
|
+
return { view: currentView, setView };
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
TABLE_VIEW,
|
|
27
|
+
useTableView
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=useTableView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTableView.js","sources":["../../src/DataTable/useTableView.js"],"sourcesContent":["// Cómo se ve la lista de la tabla: en tabla o en tarjetas (ServerTable con renderCard). Con `prefix` vive en la dirección\n// (?orders_view=cards: se comparte la liga y sobrevive a recargar) y cambiarla REEMPLAZA la entrada del historial: es la misma\n// lista vista de otra forma, «atrás» sale de ella. Sin prefijo, en memoria. Con `view` la decide la pantalla.\n//\n// Hook genérico: function (regla de la casa).\nimport {useState} from 'react';\nimport {parseAsStringLiteral, useQueryState} from 'nuqs';\n\n/** Las dos vistas de la lista. */\nexport const TABLE_VIEW = Object.freeze({table: 'table', cards: 'cards'});\nconst VIEWS = Object.freeze(Object.values(TABLE_VIEW));\n\n/**\n * @param {object} options\n * @param {string} [options.prefix] el de la tabla: con él, la vista va en la dirección\n * @param {'table'|'cards'} [options.view] la vista que decide la pantalla (controlada)\n * @param {'table'|'cards'} [options.defaultView] por omisión 'table'\n * @param {(view: 'table'|'cards') => void} [options.onViewChange]\n * @returns {{view: 'table'|'cards', setView: (view: 'table'|'cards') => void}}\n */\nexport function useTableView({prefix, view, defaultView = TABLE_VIEW.table, onViewChange}) {\n // OJO: los dos se piden siempre (un hook no puede ir dentro de un if); se usa el que toca\n const [urlView, setUrlView] = useQueryState(`${prefix ?? 'table'}_view`,\n parseAsStringLiteral(VIEWS).withDefault(defaultView).withOptions({history: 'replace'}));\n const [localView, setLocalView] = useState(defaultView);\n const currentView = view ?? (prefix ? urlView : localView);\n\n const setView = (nextView) => {\n if (view === undefined) {\n if (prefix) {\n setUrlView(nextView === defaultView ? null : nextView);\n } else {\n setLocalView(nextView);\n }\n }\n onViewChange?.(nextView);\n };\n return {view: currentView, setView};\n}\n"],"names":[],"mappings":";;;AASY,MAAC,aAAa,OAAO,OAAO,EAAC,OAAO,SAAS,OAAO,QAAO,CAAC;AACxE,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO,UAAU,CAAC;AAU9C,SAAS,aAAa,EAAC,QAAQ,MAAM,cAAc,WAAW,OAAO,aAAY,GAAG;AAEvF,QAAM,CAAC,SAAS,UAAU,IAAI;AAAA,IAAc,GAAG,UAAU,OAAO;AAAA,IAC5D,qBAAqB,KAAK,EAAE,YAAY,WAAW,EAAE,YAAY,EAAC,SAAS,UAAS,CAAC;AAAA,EAAC;AAC1F,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,WAAW;AACtD,QAAM,cAAc,SAAS,SAAS,UAAU;AAEhD,QAAM,UAAU,CAAC,aAAa;AAC1B,QAAI,SAAS,QAAW;AACpB,UAAI,QAAQ;AACR,mBAAW,aAAa,cAAc,OAAO,QAAQ;AAAA,MACzD,OAAO;AACH,qBAAa,QAAQ;AAAA,MACzB;AAAA,IACJ;AACA,mBAAe,QAAQ;AAAA,EAC3B;AACA,SAAO,EAAC,MAAM,aAAa,QAAO;AACtC;"}
|
|
@@ -11,7 +11,7 @@ import Stack from "@mui/material/Stack";
|
|
|
11
11
|
import Typography from "@mui/material/Typography";
|
|
12
12
|
import { groupStateOf, withPermissionGroup, withPermission } from "./permissionGroups.js";
|
|
13
13
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
14
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
14
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
15
15
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
16
16
|
const THEME_NAME = "CalcitaPermissionMatrix";
|
|
17
17
|
const PermissionsPermission = piecePart(THEME_NAME, "Permission", FormControlLabel);
|
|
@@ -83,12 +83,13 @@ function PermissionGroup({ group, value, onChange, isReadOnly }) {
|
|
|
83
83
|
)) })
|
|
84
84
|
] });
|
|
85
85
|
}
|
|
86
|
-
function
|
|
86
|
+
function PermissionMatrixContent(props) {
|
|
87
87
|
const { groups, value, onChange, readOnly = false, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
88
88
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
89
89
|
const isReadOnly = readOnly || !onChange;
|
|
90
90
|
return /* @__PURE__ */ jsx(ComponentTexts, { namespace: "access", texts, children: /* @__PURE__ */ jsx(Stack, { spacing: 1.5, children: groups.map((group) => /* @__PURE__ */ jsx(PermissionGroup, { group, value, onChange, isReadOnly }, group.key)) }) });
|
|
91
91
|
}
|
|
92
|
+
const PermissionMatrix = withPieceSlots(PermissionMatrixContent, THEME_NAME);
|
|
92
93
|
export {
|
|
93
94
|
PermissionMatrix as default
|
|
94
95
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionMatrix.js","sources":["../../src/access/PermissionMatrix.jsx"],"sourcesContent":["// Los permisos de un perfil, agrupados (un grupo por módulo), para la pantalla de «Perfiles y permisos»: cada grupo con\n// su casilla de «todos» (a medias si tiene algunos) y cuántos tiene, y cada permiso con su nombre y, si hace falta, qué\n// deja hacer en palabras normales.\n//\n// <PermissionMatrix groups={PERMISSION_GROUPS} value={profile.abilities} onChange={setAbilities} />\n//\n// Los permisos son los mismos textos que piden los menu.js y las rutas (access: {ability: 'orders.view'}), así que lo que\n// se palomea aquí es lo que el menú enseña o esconde. La lista de grupos la arma el proyecto (RBAC propio); guardar es\n// cosa de la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPermissionMatrix con defaultProps y\n// styleOverrides de sus partes: permission, checkbox, group, groupHeader, groupTitle, grid.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: todo llega ya traducido.\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Checkbox from '@mui/material/Checkbox';\nimport Chip from '@mui/material/Chip';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {groupStateOf, withPermission, withPermissionGroup} from '@/access/permissionGroups.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.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.CalcitaPermissionMatrix)\nconst THEME_NAME = 'CalcitaPermissionMatrix';\nconst PermissionsPermission = piecePart(THEME_NAME, 'Permission', FormControlLabel);\nconst PermissionsCheckbox = piecePart(THEME_NAME, 'Checkbox', Checkbox);\nconst PermissionsGroup = piecePart(THEME_NAME, 'Group', Paper);\nconst PermissionsGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst PermissionsGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst PermissionsGrid = piecePart(THEME_NAME, 'Grid', Box);\n\n/**\n * Un permiso: su casilla, su nombre y qué deja hacer.\n *\n * @param {{permission: {key: string, label: string, description?: string}, isGranted: boolean, readOnly: boolean, onToggle: (isGranted: boolean) => void}} props\n */\nfunction PermissionOption({permission, isGranted, readOnly, onToggle}) {\n return (\n <PermissionsPermission\n ownSx={{alignItems: 'flex-start', m: 0, gap: 0.5}}\n control={<PermissionsCheckbox size=\"small\" checked={isGranted} disabled={readOnly} onChange={(event) => onToggle(event.target.checked)}\n slotProps={{input: {'data-permission': permission.key}}} ownSx={{mt: -0.5}} />}\n label={(\n <Box>\n <Typography variant=\"body2\">{permission.label}</Typography>\n {permission.description && <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{permission.description}</Typography>}\n </Box>\n )}\n />\n );\n}\n\n/**\n * Un grupo: su encabezado con «todos» y cuántos tiene, y sus permisos.\n *\n * OJO: es un grupo con role=\"group\" y no un <fieldset>: el navegador pinta el <legend> encima del borde y dejaba una franja\n * blanca arriba del encabezado.\n *\n * @param {{group: object, value: string[], onChange: Function, isReadOnly: boolean}} props\n */\nfunction PermissionGroup({group, value, onChange, isReadOnly}) {\n const {t} = useToolkitTranslation('access');\n const titleId = useId();\n const state = groupStateOf(group, value);\n return (\n <PermissionsGroup variant=\"outlined\" role=\"group\" aria-labelledby={titleId} data-group={group.key} ownSx={{overflow: 'hidden'}}>\n <PermissionsGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', px: 1.5, py: 0.75, borderBottom: 1, borderColor: 'divider', bgcolor: 'action.hover'}}>\n <Checkbox size=\"small\" checked={state.isAll} indeterminate={state.isSome} disabled={isReadOnly || state.total === 0}\n onChange={(event) => onChange(withPermissionGroup(value, group, event.target.checked))}\n slotProps={{input: {'aria-label': t('permissions.allOf', {group: group.label})}}} />\n <PermissionsGroupTitle id={titleId} variant=\"subtitle2\" component=\"h3\" ownSx={{flexGrow: 1}}>{group.label}</PermissionsGroupTitle>\n <Chip size=\"small\" label={t('permissions.countOf', {count: state.grantedCount, total: state.total})}\n color={state.grantedCount > 0 ? 'primary' : 'default'} variant={state.isAll ? 'filled' : 'outlined'} />\n </PermissionsGroupHeader>\n <PermissionsGrid ownSx={{display: 'grid', gridTemplateColumns: {xs: '1fr', md: 'repeat(2, minmax(0, 1fr))'}, gap: 1.5, p: 2}}>\n {group.permissions.map((permission) => (\n <PermissionOption key={permission.key} permission={permission} isGranted={value.includes(permission.key)} readOnly={isReadOnly}\n onToggle={(isGranted) => onChange(withPermission(value, permission.key, isGranted))} />\n ))}\n </PermissionsGrid>\n </PermissionsGroup>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{key: string, label: string, permissions: Array<{key: string, label: string, description?: string}>}>} props.groups\n * @param {string[]} props.value los permisos del perfil\n * @param {(value: string[]) => void} [props.onChange] sin ella, o con readOnly, solo se ven\n * @param {boolean} [props.readOnly] un perfil que no se puede cambiar (el de administración)\n * @param {object} [props.texts] los textos que cambian, con la forma de `access` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"PermissionMatrix.js","sources":["../../src/access/PermissionMatrix.jsx"],"sourcesContent":["// Los permisos de un perfil, agrupados (un grupo por módulo), para la pantalla de «Perfiles y permisos»: cada grupo con\n// su casilla de «todos» (a medias si tiene algunos) y cuántos tiene, y cada permiso con su nombre y, si hace falta, qué\n// deja hacer en palabras normales.\n//\n// <PermissionMatrix groups={PERMISSION_GROUPS} value={profile.abilities} onChange={setAbilities} />\n//\n// Los permisos son los mismos textos que piden los menu.js y las rutas (access: {ability: 'orders.view'}), así que lo que\n// se palomea aquí es lo que el menú enseña o esconde. La lista de grupos la arma el proyecto (RBAC propio); guardar es\n// cosa de la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPermissionMatrix con defaultProps y\n// styleOverrides de sus partes: permission, checkbox, group, groupHeader, groupTitle, grid.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: todo llega ya traducido.\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Checkbox from '@mui/material/Checkbox';\nimport Chip from '@mui/material/Chip';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {groupStateOf, withPermission, withPermissionGroup} from '@/access/permissionGroups.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPermissionMatrix)\nconst THEME_NAME = 'CalcitaPermissionMatrix';\nconst PermissionsPermission = piecePart(THEME_NAME, 'Permission', FormControlLabel);\nconst PermissionsCheckbox = piecePart(THEME_NAME, 'Checkbox', Checkbox);\nconst PermissionsGroup = piecePart(THEME_NAME, 'Group', Paper);\nconst PermissionsGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst PermissionsGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst PermissionsGrid = piecePart(THEME_NAME, 'Grid', Box);\n\n/**\n * Un permiso: su casilla, su nombre y qué deja hacer.\n *\n * @param {{permission: {key: string, label: string, description?: string}, isGranted: boolean, readOnly: boolean, onToggle: (isGranted: boolean) => void}} props\n */\nfunction PermissionOption({permission, isGranted, readOnly, onToggle}) {\n return (\n <PermissionsPermission\n ownSx={{alignItems: 'flex-start', m: 0, gap: 0.5}}\n control={<PermissionsCheckbox size=\"small\" checked={isGranted} disabled={readOnly} onChange={(event) => onToggle(event.target.checked)}\n slotProps={{input: {'data-permission': permission.key}}} ownSx={{mt: -0.5}} />}\n label={(\n <Box>\n <Typography variant=\"body2\">{permission.label}</Typography>\n {permission.description && <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{permission.description}</Typography>}\n </Box>\n )}\n />\n );\n}\n\n/**\n * Un grupo: su encabezado con «todos» y cuántos tiene, y sus permisos.\n *\n * OJO: es un grupo con role=\"group\" y no un <fieldset>: el navegador pinta el <legend> encima del borde y dejaba una franja\n * blanca arriba del encabezado.\n *\n * @param {{group: object, value: string[], onChange: Function, isReadOnly: boolean}} props\n */\nfunction PermissionGroup({group, value, onChange, isReadOnly}) {\n const {t} = useToolkitTranslation('access');\n const titleId = useId();\n const state = groupStateOf(group, value);\n return (\n <PermissionsGroup variant=\"outlined\" role=\"group\" aria-labelledby={titleId} data-group={group.key} ownSx={{overflow: 'hidden'}}>\n <PermissionsGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', px: 1.5, py: 0.75, borderBottom: 1, borderColor: 'divider', bgcolor: 'action.hover'}}>\n <Checkbox size=\"small\" checked={state.isAll} indeterminate={state.isSome} disabled={isReadOnly || state.total === 0}\n onChange={(event) => onChange(withPermissionGroup(value, group, event.target.checked))}\n slotProps={{input: {'aria-label': t('permissions.allOf', {group: group.label})}}} />\n <PermissionsGroupTitle id={titleId} variant=\"subtitle2\" component=\"h3\" ownSx={{flexGrow: 1}}>{group.label}</PermissionsGroupTitle>\n <Chip size=\"small\" label={t('permissions.countOf', {count: state.grantedCount, total: state.total})}\n color={state.grantedCount > 0 ? 'primary' : 'default'} variant={state.isAll ? 'filled' : 'outlined'} />\n </PermissionsGroupHeader>\n <PermissionsGrid ownSx={{display: 'grid', gridTemplateColumns: {xs: '1fr', md: 'repeat(2, minmax(0, 1fr))'}, gap: 1.5, p: 2}}>\n {group.permissions.map((permission) => (\n <PermissionOption key={permission.key} permission={permission} isGranted={value.includes(permission.key)} readOnly={isReadOnly}\n onToggle={(isGranted) => onChange(withPermission(value, permission.key, isGranted))} />\n ))}\n </PermissionsGrid>\n </PermissionsGroup>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{key: string, label: string, permissions: Array<{key: string, label: string, description?: string}>}>} props.groups\n * @param {string[]} props.value los permisos del perfil\n * @param {(value: string[]) => void} [props.onChange] sin ella, o con readOnly, solo se ven\n * @param {boolean} [props.readOnly] un perfil que no se puede cambiar (el de administración)\n * @param {object} [props.texts] los textos que cambian, con la forma de `access` en los textos del paquete\n */\nfunction PermissionMatrixContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPermissionMatrix.defaultProps) y encima lo que llega aquí\n const {groups, value, onChange, readOnly = false, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const isReadOnly = readOnly || !onChange;\n return (\n <ComponentTexts namespace=\"access\" texts={texts}>\n <Stack spacing={1.5}>\n {groups.map((group) => <PermissionGroup key={group.key} group={group} value={value} onChange={onChange} isReadOnly={isReadOnly} />)}\n </Stack>\n </ComponentTexts>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst PermissionMatrix = withPieceSlots(PermissionMatrixContent, THEME_NAME);\nexport default PermissionMatrix;\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useState, useCallback, useRef, useEffect, useMemo
|
|
3
|
+
import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from "react";
|
|
4
4
|
import { missingConnectorFunctionsOf } from "./accountConnector.js";
|
|
5
5
|
import { createAccessChecker } from "../access/accessRules.js";
|
|
6
6
|
import ErrorPage, { ERROR_PAGE_KIND } from "../ui/layout/ErrorPage.js";
|
|
@@ -14,14 +14,14 @@ import { createPasskey, passkeysSupported } from "../security/passkeys.js";
|
|
|
14
14
|
import SessionsCard from "../security/SessionsCard.js";
|
|
15
15
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
16
16
|
import SectionCard from "../ui/layout/SectionCard.js";
|
|
17
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
17
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
18
18
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
19
19
|
const THEME_NAME = "CalcitaAccountSecurity";
|
|
20
20
|
const SecurityTitle = piecePart(THEME_NAME, "Title", Typography);
|
|
21
21
|
const SecurityForm = piecePart(THEME_NAME, "Form", Stack);
|
|
22
22
|
const FORM_MAX_WIDTH = 520;
|
|
23
23
|
const LOADING_HEIGHT_PIXELS = 220;
|
|
24
|
-
function
|
|
24
|
+
function AccountSecuritySectionContent(props) {
|
|
25
25
|
const { showPasswordChange = true, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
26
26
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
27
27
|
const { t } = useToolkitTranslation("account", texts);
|
|
@@ -82,6 +82,7 @@ function AccountSecuritySection(props) {
|
|
|
82
82
|
/* @__PURE__ */ jsx(ActivityLog, { events })
|
|
83
83
|
] });
|
|
84
84
|
}
|
|
85
|
+
const AccountSecuritySection = withPieceSlots(AccountSecuritySectionContent, THEME_NAME);
|
|
85
86
|
export {
|
|
86
87
|
AccountSecuritySection as default
|
|
87
88
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountSecuritySection.js","sources":["../../src/account/AccountSecuritySection.jsx"],"sourcesContent":["// La seguridad de la cuenta, completa: cambiar la contraseña (ChangePasswordForm), la verificación en dos pasos\n// (TwoStepVerificationCard: llaves de acceso, app autenticadora y códigos de recuperación), las sesiones abiertas\n// (SessionsCard) y la bitácora (ActivityLog). Todo lo del servidor sale del conector de la cuenta.\n//\n// Las llaves de acceso se crean aquí con el navegador (createPasskey) y la respuesta va al servidor (savePasskey). La\n// bitácora se vuelve a pedir después de cada cambio: así se ve aparecer lo que se acaba de hacer.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAccountSecurity con defaultProps y\n// styleOverrides de sus partes: title, form.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useCallback, useEffect, useMemo, useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {useAccount} from '@/account/AccountProvider.jsx';\nimport DemoTotpCodeButton from '@/account/DemoTotpCodeButton.jsx';\nimport ChangePasswordForm from '@/forms/auth/ChangePasswordForm.jsx';\nimport TwoStepVerificationCard from '@/forms/security/TwoStepVerificationCard.jsx';\nimport ActivityLog from '@/security/ActivityLog.jsx';\nimport {createPasskey, passkeysSupported} from '@/security/passkeys.js';\nimport SessionsCard from '@/security/SessionsCard.jsx';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SectionCard from '@/ui/layout/SectionCard.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.CalcitaAccountSecurity)\nconst THEME_NAME = 'CalcitaAccountSecurity';\nconst SecurityTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst SecurityForm = piecePart(THEME_NAME, 'Form', Stack);\n\n// lo ancho del formulario de la contraseña: un renglón cómodo\nconst FORM_MAX_WIDTH = 520;\nconst LOADING_HEIGHT_PIXELS = 220;\n\n/**\n * @param {object} props\n * @param {boolean} [props.showPasswordChange] por omisión sí\n * @param {object} [props.texts] los textos que cambian, con la forma de `account` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"AccountSecuritySection.js","sources":["../../src/account/AccountSecuritySection.jsx"],"sourcesContent":["// La seguridad de la cuenta, completa: cambiar la contraseña (ChangePasswordForm), la verificación en dos pasos\n// (TwoStepVerificationCard: llaves de acceso, app autenticadora y códigos de recuperación), las sesiones abiertas\n// (SessionsCard) y la bitácora (ActivityLog). Todo lo del servidor sale del conector de la cuenta.\n//\n// Las llaves de acceso se crean aquí con el navegador (createPasskey) y la respuesta va al servidor (savePasskey). La\n// bitácora se vuelve a pedir después de cada cambio: así se ve aparecer lo que se acaba de hacer.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAccountSecurity con defaultProps y\n// styleOverrides de sus partes: title, form.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useCallback, useEffect, useMemo, useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {useAccount} from '@/account/AccountProvider.jsx';\nimport DemoTotpCodeButton from '@/account/DemoTotpCodeButton.jsx';\nimport ChangePasswordForm from '@/forms/auth/ChangePasswordForm.jsx';\nimport TwoStepVerificationCard from '@/forms/security/TwoStepVerificationCard.jsx';\nimport ActivityLog from '@/security/ActivityLog.jsx';\nimport {createPasskey, passkeysSupported} from '@/security/passkeys.js';\nimport SessionsCard from '@/security/SessionsCard.jsx';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAccountSecurity)\nconst THEME_NAME = 'CalcitaAccountSecurity';\nconst SecurityTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst SecurityForm = piecePart(THEME_NAME, 'Form', Stack);\n\n// lo ancho del formulario de la contraseña: un renglón cómodo\nconst FORM_MAX_WIDTH = 520;\nconst LOADING_HEIGHT_PIXELS = 220;\n\n/**\n * @param {object} props\n * @param {boolean} [props.showPasswordChange] por omisión sí\n * @param {object} [props.texts] los textos que cambian, con la forma de `account` en los textos del paquete\n */\nfunction AccountSecuritySectionContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaAccountSecurity.defaultProps) y encima lo que llega aquí\n const {showPasswordChange = true, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('account', texts);\n const {connector, session} = useAccount();\n const [security, setSecurity] = useState(null);\n const [sessions, setSessions] = useState(null);\n const [events, setEvents] = useState(null);\n const [loadError, setLoadError] = useState(null);\n const isDemo = typeof connector.demoTotpCode === 'function';\n\n const refreshActivity = useCallback(() => connector.listActivity().then(setEvents).catch((error) => setLoadError(error.message)), [connector]);\n const refreshSessions = useCallback(() => connector.listSessions().then(setSessions).catch((error) => setLoadError(error.message)), [connector]);\n\n useEffect(() => {\n connector.loadSecurity().then(setSecurity).catch((error) => setLoadError(error.message));\n refreshSessions();\n refreshActivity();\n }, [connector, refreshActivity, refreshSessions]);\n\n // lo que hace la tarjeta de dos pasos en cada caso, con el conector\n const actions = useMemo(() => ({\n addPasskey: async (name) => connector.savePasskey({name, credential: await createPasskey(await connector.passkeyRegistrationOptions())}),\n removePasskey: (passkey, password) => connector.removePasskey({passkeyId: passkey.id, password}),\n startTotp: async () => ({\n ...await connector.startTotp(),\n // en la demostración, el atajo del código abajo del campo\n extra: isDemo ? <DemoTotpCodeButton loadCode={connector.demoTotpCode} /> : undefined,\n }),\n confirmTotp: (code) => connector.confirmTotp(code),\n disableTotp: (password) => connector.disableTotp(password),\n regenerateRecoveryCodes: (password) => connector.regenerateRecoveryCodes(password),\n }), [connector, isDemo]);\n\n const changeSecurity = (nextSecurity) => {\n setSecurity(nextSecurity);\n refreshActivity();\n };\n\n return (\n <Stack spacing={3}>\n {loadError && <Alert severity=\"error\">{loadError}</Alert>}\n {showPasswordChange && (\n <SectionCard title={t('profile.password.title')}>\n <SecurityTitle variant=\"body2\" color=\"text.secondary\" ownSx={{mb: 2}}>{t('profile.password.text')}</SecurityTitle>\n <SecurityForm ownSx={{maxWidth: FORM_MAX_WIDTH}}>\n <ChangePasswordForm onSubmit={async (values) => {\n await connector.changePassword(values);\n refreshActivity();\n }} />\n </SecurityForm>\n </SectionCard>\n )}\n {security\n ? <TwoStepVerificationCard security={security} onSecurityChange={changeSecurity} actions={actions}\n canUsePasskeys={passkeysSupported()} account={session?.user.email}\n // en la demostración la llave es de verdad pero el servidor se olvida al recargar:\n // se avisa antes de crearla para que nadie se quede con una llave huérfana\n passkeyNotice={isDemo ? t('demo.passkeyNote') : undefined} />\n : <Skeleton variant=\"rounded\" height={LOADING_HEIGHT_PIXELS} />}\n {isDemo && <Alert severity=\"info\">{t('demo.securityNote')}</Alert>}\n <SessionsCard sessions={sessions} onCloseOthers={connector.closeOtherSessions} onClosed={() => {\n refreshSessions();\n refreshActivity();\n }} />\n <ActivityLog events={events} />\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst AccountSecuritySection = withPieceSlots(AccountSecuritySectionContent, THEME_NAME);\nexport default AccountSecuritySection;\n"],"names":[],"mappings":"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"}
|
|
@@ -6,12 +6,12 @@ import Stack from "@mui/material/Stack";
|
|
|
6
6
|
import Typography from "@mui/material/Typography";
|
|
7
7
|
import ScienceOutlinedIcon from "@mui/icons-material/ScienceOutlined";
|
|
8
8
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
9
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
9
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
10
10
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
11
11
|
const THEME_NAME = "CalcitaDemoTotpCode";
|
|
12
12
|
const TotpRoot = piecePart(THEME_NAME, "Root", Stack);
|
|
13
13
|
const TotpCode = piecePart(THEME_NAME, "Code", Typography);
|
|
14
|
-
function
|
|
14
|
+
function DemoTotpCodeButtonContent(props) {
|
|
15
15
|
const { loadCode, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
16
16
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
17
17
|
const { t } = useToolkitTranslation("account");
|
|
@@ -27,6 +27,7 @@ function DemoTotpCodeButton(props) {
|
|
|
27
27
|
/* @__PURE__ */ jsx(TotpCode, { variant: "caption", color: "text.secondary", ownSx: { textAlign: "center" }, children: t("demo.totpCodeHelp") })
|
|
28
28
|
] });
|
|
29
29
|
}
|
|
30
|
+
const DemoTotpCodeButton = withPieceSlots(DemoTotpCodeButtonContent, THEME_NAME);
|
|
30
31
|
export {
|
|
31
32
|
DemoTotpCodeButton as default
|
|
32
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DemoTotpCodeButton.js","sources":["../../src/account/DemoTotpCodeButton.jsx"],"sourcesContent":["// El atajo de la DEMOSTRACIÓN para la app autenticadora: llena el campo «code» del formulario con el código que enseñaría\n// la app ahorita. Para quien prueba sin la app en su teléfono (y para las pruebas automáticas, que no escanean QR). Solo\n// sale cuando el conector trae demoTotpCode: con un backend de verdad no existe (el código solo lo conoce la app).\n//\n// Va dentro del formulario de formik (abajo del segundo paso o en el diálogo de activar la app).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDemoTotpCode con defaultProps y\n// styleOverrides de sus partes: root, code.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useFormikContext} from 'formik';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ScienceOutlinedIcon from '@mui/icons-material/ScienceOutlined';\n\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.CalcitaDemoTotpCode)\nconst THEME_NAME = 'CalcitaDemoTotpCode';\nconst TotpRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst TotpCode = piecePart(THEME_NAME, 'Code', Typography);\n\n/**\n * @param {{loadCode: () => Promise<string|null>}} props\n */\
|
|
1
|
+
{"version":3,"file":"DemoTotpCodeButton.js","sources":["../../src/account/DemoTotpCodeButton.jsx"],"sourcesContent":["// El atajo de la DEMOSTRACIÓN para la app autenticadora: llena el campo «code» del formulario con el código que enseñaría\n// la app ahorita. Para quien prueba sin la app en su teléfono (y para las pruebas automáticas, que no escanean QR). Solo\n// sale cuando el conector trae demoTotpCode: con un backend de verdad no existe (el código solo lo conoce la app).\n//\n// Va dentro del formulario de formik (abajo del segundo paso o en el diálogo de activar la app).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDemoTotpCode con defaultProps y\n// styleOverrides de sus partes: root, code.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useFormikContext} from 'formik';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ScienceOutlinedIcon from '@mui/icons-material/ScienceOutlined';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDemoTotpCode)\nconst THEME_NAME = 'CalcitaDemoTotpCode';\nconst TotpRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst TotpCode = piecePart(THEME_NAME, 'Code', Typography);\n\n/**\n * @param {{loadCode: () => Promise<string|null>}} props\n */\nfunction DemoTotpCodeButtonContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaDemoTotpCode.defaultProps) y encima lo que llega aquí\n const {loadCode, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('account');\n const {setFieldValue} = useFormikContext();\n\n const fillCode = async () => {\n const code = await loadCode();\n if (code) {\n setFieldValue('code', code);\n }\n };\n\n return (\n <TotpRoot spacing={0.5} ownSx={{alignItems: 'center'}}>\n <Button size=\"small\" startIcon={<ScienceOutlinedIcon />} onClick={fillCode}>{t('demo.totpCode')}</Button>\n <TotpCode variant=\"caption\" color=\"text.secondary\" ownSx={{textAlign: 'center'}}>{t('demo.totpCodeHelp')}</TotpCode>\n </TotpRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DemoTotpCodeButton = withPieceSlots(DemoTotpCodeButtonContent, THEME_NAME);\nexport default DemoTotpCodeButton;\n"],"names":[],"mappings":";;;;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,WAAW,UAAU,YAAY,QAAQ,KAAK;AACpD,MAAM,WAAW,UAAU,YAAY,QAAQ,UAAU;AAKzD,SAAS,0BAA0B,OAAO;AAEtC,QAAM,EAAC,UAAU,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACnE,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS;AAC3C,QAAM,EAAC,cAAA,IAAiB,iBAAA;AAExB,QAAM,WAAW,YAAY;AACzB,UAAM,OAAO,MAAM,SAAA;AACnB,QAAI,MAAM;AACN,oBAAc,QAAQ,IAAI;AAAA,IAC9B;AAAA,EACJ;AAEA,SACI,qBAAC,YAAS,SAAS,KAAK,OAAO,EAAC,YAAY,YACxC,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAO,MAAK,SAAQ,WAAW,oBAAC,qBAAA,CAAA,CAAoB,GAAI,SAAS,UAAW,UAAA,EAAE,eAAe,GAAE;AAAA,IAChG,oBAAC,UAAA,EAAS,SAAQ,WAAU,OAAM,kBAAiB,OAAO,EAAC,WAAW,SAAA,GAAY,UAAA,EAAE,mBAAmB,EAAA,CAAE;AAAA,EAAA,GAC7G;AAER;AAGA,MAAM,qBAAqB,eAAe,2BAA2B,UAAU;"}
|
|
@@ -20,7 +20,7 @@ import { useToolkitNavigation } from "../toolkit/toolkitContext.js";
|
|
|
20
20
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
21
21
|
import ProfileHeader from "../ui/layout/ProfileHeader.js";
|
|
22
22
|
import SectionCard from "../ui/layout/SectionCard.js";
|
|
23
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
23
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
24
24
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
25
25
|
const THEME_NAME = "CalcitaProfileScreen";
|
|
26
26
|
const ProfileScreenSectionTitle = piecePart(THEME_NAME, "SectionTitle", Typography);
|
|
@@ -74,7 +74,7 @@ function ProfileDataSection({ extraFields, onProfileSaved, texts }) {
|
|
|
74
74
|
] }) }) })
|
|
75
75
|
] });
|
|
76
76
|
}
|
|
77
|
-
function
|
|
77
|
+
function ProfileScreenContent(props) {
|
|
78
78
|
const {
|
|
79
79
|
showColorScheme = true,
|
|
80
80
|
extraFields,
|
|
@@ -136,6 +136,7 @@ function ProfileScreen(props) {
|
|
|
136
136
|
activeExtraTab?.content
|
|
137
137
|
] });
|
|
138
138
|
}
|
|
139
|
+
const ProfileScreen = withPieceSlots(ProfileScreenContent, THEME_NAME);
|
|
139
140
|
export {
|
|
140
141
|
PROFILE_TAB,
|
|
141
142
|
ProfileScreen as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProfileScreen.js","sources":["../../src/account/ProfileScreen.jsx"],"sourcesContent":["// Mi perfil, completo: quién está dentro (ProfileHeader con sus perfiles como chips) y tres pestañas:\n// - Sus datos: nombre y teléfono (el correo se ve pero no se cambia aquí); al guardar se vuelve a leer la sesión, así la\n// barra y el menú de la cuenta ya dicen el nombre nuevo;\n// - Seguridad: AccountSecuritySection (contraseña, dos pasos, sesiones y bitácora);\n// - Preferencias: AppearanceSettings (tamaño de letra, idioma y modo de color).\n// La pestaña abierta va en la dirección (?tab=): se puede compartir o recargar sin perderla. Lo del servidor sale del\n// conector de la cuenta.\n//\n// Se extiende sin tocar el framework (cada proyecto tiene su perfil «especial»):\n// - extraFields: campos propios en «Sus datos» (el área, el número de empleado…), con su valor de inicio sacado del usuario\n// de la sesión, sus reglas de yup y sus campos. Al guardar viajan a connector.updateProfile junto con name y phone: un\n// backend que ya existe recibe lo suyo con sus nombres;\n// - extraTabs: pestañas propias después de las de la casa (facturación, notificaciones…); hiddenTabs esconde las de la casa;\n// - eventos: onTabChange y onProfileSaved (lo guardado y lo que contestó el servidor);\n// - texts: cualquier texto, con la forma de `account` en los textos del paquete.\n//\n// <ProfileScreen />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaProfileScreen con defaultProps y\n// styleOverrides de sus partes: sectionTitle, form, preferences.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo} from 'react';\nimport {Form, Formik} from 'formik';\nimport Stack from '@mui/material/Stack';\nimport Tab from '@mui/material/Tab';\nimport Tabs from '@mui/material/Tabs';\nimport Typography from '@mui/material/Typography';\n\nimport AccountSecuritySection from '@/account/AccountSecuritySection.jsx';\nimport {useAccount} from '@/account/AccountProvider.jsx';\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\nimport {toastSuccess} from '@/feedback/toast.jsx';\nimport FormProblemAlert from '@/forms/FormProblemAlert.jsx';\nimport FormTextField from '@/forms/FormTextField.jsx';\nimport SubmitButton from '@/forms/SubmitButton.jsx';\nimport {applyServerProblem} from '@/forms/applyServerProblem.js';\nimport {Yup} from '@/forms/validation.js';\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ProfileHeader from '@/ui/layout/ProfileHeader.jsx';\nimport SectionCard from '@/ui/layout/SectionCard.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.CalcitaProfileScreen)\nconst THEME_NAME = 'CalcitaProfileScreen';\nconst ProfileScreenSectionTitle = piecePart(THEME_NAME, 'SectionTitle', Typography);\nconst ProfileScreenForm = piecePart(THEME_NAME, 'Form', Stack);\nconst ProfileScreenPreferences = piecePart(THEME_NAME, 'Preferences', Stack);\n\n/** Las pestañas, en el orden en que se pintan. */\nexport const PROFILE_TAB = Object.freeze({data: 'data', security: 'security', preferences: 'preferences'});\nconst TAB_PARAM = 'tab';\n// el teléfono: dígitos, con + opcional al inicio (formato internacional); lo largo que permite el plan de numeración\nconst PHONE_PATTERN = /^\\+?\\d{7,15}$/;\n// lo ancho de los formularios: un renglón cómodo, no toda la pantalla\nconst FORM_MAX_WIDTH = 520;\n\n/**\n * Sus datos: nombre, teléfono y los campos propios del proyecto (extraFields).\n *\n * @param {object} props\n * @param {object} [props.extraFields]\n * @param {Function} [props.onProfileSaved]\n * @param {object} [props.texts]\n */\nfunction ProfileDataSection({extraFields, onProfileSaved, texts}) {\n const {t} = useToolkitTranslation('account', texts);\n const {connector, session, refreshSession} = useAccount();\n const extraRules = extraFields?.rules;\n // el esquema se arma al pintar: yup toma sus mensajes al armarlo. Las reglas propias se suman a las de la casa\n const schema = useMemo(() => Yup.object({\n name: Yup.string().trim().required(),\n phone: Yup.string().trim().matches(PHONE_PATTERN, {message: {key: 'account:profile.data.phoneInvalid'}, excludeEmptyString: true}),\n ...extraRules,\n }), [extraRules]);\n const initialValues = {name: session.user.name ?? '', phone: session.user.phone ?? '', ...extraFields?.initialValues?.(session.user)};\n\n const save = async (values, formikHelpers) => {\n // lo de la casa limpio de espacios; lo propio, tal como lo dejaron sus campos\n const profile = {...values, name: values.name.trim(), phone: values.phone.trim()};\n try {\n const response = await connector.updateProfile(profile);\n await refreshSession();\n toastSuccess(response?.message ?? t('profile.data.saved'));\n onProfileSaved?.({values: profile, response});\n } catch (error) {\n applyServerProblem(error, {...formikHelpers, values});\n }\n };\n\n return (\n <SectionCard title={t('profile.data.title')}>\n <ProfileScreenSectionTitle variant=\"body2\" color=\"text.secondary\" ownSx={{mb: 2}}>{t('profile.data.text')}</ProfileScreenSectionTitle>\n <Formik initialValues={initialValues} validationSchema={schema} onSubmit={save}>\n <Form noValidate>\n <ProfileScreenForm spacing={2} ownSx={{maxWidth: FORM_MAX_WIDTH}}>\n <FormProblemAlert />\n <FormTextField name=\"name\" label={t('profile.data.name')} autoComplete=\"name\" />\n {/* el correo se ve con el mismo estilo de los demás campos, pero no se cambia aquí */}\n <TextInput name=\"email\" label={t('profile.data.email')} value={session.user.email} onChange={() => {}}\n helperText={t('profile.data.emailHelp')} disabled />\n <FormTextField name=\"phone\" label={t('profile.data.phone')} type=\"tel\" autoComplete=\"tel\" inputMode=\"tel\" />\n {extraFields?.fields}\n <SubmitButton sx={{alignSelf: 'flex-start'}}>{t('profile.data.save')}</SubmitButton>\n </ProfileScreenForm>\n </Form>\n </Formik>\n </SectionCard>\n );\n}\n\n/** @typedef {import('@/account/accountConnector.js').AccountUser} AccountUser */\n\n/**\n * @param {object} props\n * @param {boolean} [props.showColorScheme] el ajuste de claro u oscuro en Preferencias; sin modo oscuro, false\n * @param {{initialValues?: (user: AccountUser & Record<string, any>) => Record<string, any>, rules?: Record<string, any>, fields: import('react').ReactNode}} [props.extraFields]\n * campos propios en «Sus datos», debajo del teléfono: sus valores de inicio salen del usuario de la sesión (lo que su\n * conector ponga en session.user), sus reglas de yup y sus campos (FormTextField, FormSelectField… de /forms). Al guardar\n * viajan a connector.updateProfile junto con name y phone\n * @param {Array<{key: string, label: string, content: import('react').ReactNode}>} [props.extraTabs] pestañas propias,\n * después de las de la casa; label ya traducido\n * @param {Array<'data'|'security'|'preferences'>} [props.hiddenTabs] pestañas de la casa que no se ven (PROFILE_TAB)\n * @param {(tab: string) => void} [props.onTabChange] cada que se cambia de pestaña\n * @param {(saved: {values: Record<string, any>, response: unknown}) => void} [props.onProfileSaved] después de guardar «Sus datos»\n * @param {object} [props.texts] los textos que cambian, con la forma de `account` en los textos del paquete:\n * {profile: {tabs: {data: 'Mis datos'}}}\n */\nexport default function ProfileScreen(props) {\n // lo que el proyecto puso en su tema (components.CalcitaProfileScreen.defaultProps) y encima lo que llega aquí\n const {\n showColorScheme = true, extraFields, extraTabs = [], hiddenTabs = [], onTabChange, onProfileSaved, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('account', texts);\n const {session} = useAccount();\n const {useNavigate, useLocation} = useToolkitNavigation();\n const navigate = useNavigate();\n const {pathname, search} = useLocation();\n // las pestañas que se ven: las de la casa que no se escondieron y luego las del proyecto\n const tabs = [\n ...Object.values(PROFILE_TAB).filter((tab) => !hiddenTabs.includes(tab)).map((tab) => ({key: tab, label: t(`profile.tabs.${tab}`)})),\n ...extraTabs.map(({key, label}) => ({key, label})),\n ];\n const requestedTab = new URLSearchParams(search).get(TAB_PARAM);\n const activeTab = tabs.some((tab) => tab.key === requestedTab) ? requestedTab : tabs[0]?.key;\n const activeExtraTab = extraTabs.find((tab) => tab.key === activeTab);\n const changeTab = (tab) => {\n navigate(`${pathname}?${TAB_PARAM}=${tab}`, {replace: true});\n onTabChange?.(tab);\n };\n\n // guard clause: sin sesión no hay perfil que enseñar (el marco de la app ya lleva a entrar)\n if (!session) {\n return null;\n }\n\n return (\n <Stack spacing={2}>\n <ProfileHeader name={session.user.name} email={session.user.email} subtitle={session.user.subtitle} avatarUrl={session.user.avatarUrl}\n chips={session.labels ?? []} />\n <Tabs value={activeTab} onChange={(event, tab) => changeTab(tab)} aria-label={t('profile.tabs.label')}\n variant=\"scrollable\" allowScrollButtonsMobile>\n {tabs.map((tab) => <Tab key={tab.key} value={tab.key} label={tab.label} />)}\n </Tabs>\n {activeTab === PROFILE_TAB.data && <ProfileDataSection extraFields={extraFields} onProfileSaved={onProfileSaved} texts={texts} />}\n {activeTab === PROFILE_TAB.security && <AccountSecuritySection />}\n {activeTab === PROFILE_TAB.preferences && (\n <SectionCard title={t('profile.preferences.title')}>\n <ProfileScreenSectionTitle variant=\"body2\" color=\"text.secondary\" ownSx={{mb: 1}}>{t('profile.preferences.text')}</ProfileScreenSectionTitle>\n <ProfileScreenPreferences ownSx={{maxWidth: FORM_MAX_WIDTH, mx: -1}}>\n <AppearanceSettings showColorScheme={showColorScheme} />\n </ProfileScreenPreferences>\n </SectionCard>\n )}\n {activeExtraTab?.content}\n </Stack>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProfileScreen.js","sources":["../../src/account/ProfileScreen.jsx"],"sourcesContent":["// Mi perfil, completo: quién está dentro (ProfileHeader con sus perfiles como chips) y tres pestañas:\n// - Sus datos: nombre y teléfono (el correo se ve pero no se cambia aquí); al guardar se vuelve a leer la sesión, así la\n// barra y el menú de la cuenta ya dicen el nombre nuevo;\n// - Seguridad: AccountSecuritySection (contraseña, dos pasos, sesiones y bitácora);\n// - Preferencias: AppearanceSettings (tamaño de letra, idioma y modo de color).\n// La pestaña abierta va en la dirección (?tab=): se puede compartir o recargar sin perderla. Lo del servidor sale del\n// conector de la cuenta.\n//\n// Se extiende sin tocar el framework (cada proyecto tiene su perfil «especial»):\n// - extraFields: campos propios en «Sus datos» (el área, el número de empleado…), con su valor de inicio sacado del usuario\n// de la sesión, sus reglas de yup y sus campos. Al guardar viajan a connector.updateProfile junto con name y phone: un\n// backend que ya existe recibe lo suyo con sus nombres;\n// - extraTabs: pestañas propias después de las de la casa (facturación, notificaciones…); hiddenTabs esconde las de la casa;\n// - eventos: onTabChange y onProfileSaved (lo guardado y lo que contestó el servidor);\n// - texts: cualquier texto, con la forma de `account` en los textos del paquete.\n//\n// <ProfileScreen />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaProfileScreen con defaultProps y\n// styleOverrides de sus partes: sectionTitle, form, preferences.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo} from 'react';\nimport {Form, Formik} from 'formik';\nimport Stack from '@mui/material/Stack';\nimport Tab from '@mui/material/Tab';\nimport Tabs from '@mui/material/Tabs';\nimport Typography from '@mui/material/Typography';\n\nimport AccountSecuritySection from '@/account/AccountSecuritySection.jsx';\nimport {useAccount} from '@/account/AccountProvider.jsx';\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\nimport {toastSuccess} from '@/feedback/toast.jsx';\nimport FormProblemAlert from '@/forms/FormProblemAlert.jsx';\nimport FormTextField from '@/forms/FormTextField.jsx';\nimport SubmitButton from '@/forms/SubmitButton.jsx';\nimport {applyServerProblem} from '@/forms/applyServerProblem.js';\nimport {Yup} from '@/forms/validation.js';\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ProfileHeader from '@/ui/layout/ProfileHeader.jsx';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaProfileScreen)\nconst THEME_NAME = 'CalcitaProfileScreen';\nconst ProfileScreenSectionTitle = piecePart(THEME_NAME, 'SectionTitle', Typography);\nconst ProfileScreenForm = piecePart(THEME_NAME, 'Form', Stack);\nconst ProfileScreenPreferences = piecePart(THEME_NAME, 'Preferences', Stack);\n\n/** Las pestañas, en el orden en que se pintan. */\nexport const PROFILE_TAB = Object.freeze({data: 'data', security: 'security', preferences: 'preferences'});\nconst TAB_PARAM = 'tab';\n// el teléfono: dígitos, con + opcional al inicio (formato internacional); lo largo que permite el plan de numeración\nconst PHONE_PATTERN = /^\\+?\\d{7,15}$/;\n// lo ancho de los formularios: un renglón cómodo, no toda la pantalla\nconst FORM_MAX_WIDTH = 520;\n\n/**\n * Sus datos: nombre, teléfono y los campos propios del proyecto (extraFields).\n *\n * @param {object} props\n * @param {object} [props.extraFields]\n * @param {Function} [props.onProfileSaved]\n * @param {object} [props.texts]\n */\nfunction ProfileDataSection({extraFields, onProfileSaved, texts}) {\n const {t} = useToolkitTranslation('account', texts);\n const {connector, session, refreshSession} = useAccount();\n const extraRules = extraFields?.rules;\n // el esquema se arma al pintar: yup toma sus mensajes al armarlo. Las reglas propias se suman a las de la casa\n const schema = useMemo(() => Yup.object({\n name: Yup.string().trim().required(),\n phone: Yup.string().trim().matches(PHONE_PATTERN, {message: {key: 'account:profile.data.phoneInvalid'}, excludeEmptyString: true}),\n ...extraRules,\n }), [extraRules]);\n const initialValues = {name: session.user.name ?? '', phone: session.user.phone ?? '', ...extraFields?.initialValues?.(session.user)};\n\n const save = async (values, formikHelpers) => {\n // lo de la casa limpio de espacios; lo propio, tal como lo dejaron sus campos\n const profile = {...values, name: values.name.trim(), phone: values.phone.trim()};\n try {\n const response = await connector.updateProfile(profile);\n await refreshSession();\n toastSuccess(response?.message ?? t('profile.data.saved'));\n onProfileSaved?.({values: profile, response});\n } catch (error) {\n applyServerProblem(error, {...formikHelpers, values});\n }\n };\n\n return (\n <SectionCard title={t('profile.data.title')}>\n <ProfileScreenSectionTitle variant=\"body2\" color=\"text.secondary\" ownSx={{mb: 2}}>{t('profile.data.text')}</ProfileScreenSectionTitle>\n <Formik initialValues={initialValues} validationSchema={schema} onSubmit={save}>\n <Form noValidate>\n <ProfileScreenForm spacing={2} ownSx={{maxWidth: FORM_MAX_WIDTH}}>\n <FormProblemAlert />\n <FormTextField name=\"name\" label={t('profile.data.name')} autoComplete=\"name\" />\n {/* el correo se ve con el mismo estilo de los demás campos, pero no se cambia aquí */}\n <TextInput name=\"email\" label={t('profile.data.email')} value={session.user.email} onChange={() => {}}\n helperText={t('profile.data.emailHelp')} disabled />\n <FormTextField name=\"phone\" label={t('profile.data.phone')} type=\"tel\" autoComplete=\"tel\" inputMode=\"tel\" />\n {extraFields?.fields}\n <SubmitButton sx={{alignSelf: 'flex-start'}}>{t('profile.data.save')}</SubmitButton>\n </ProfileScreenForm>\n </Form>\n </Formik>\n </SectionCard>\n );\n}\n\n/** @typedef {import('@/account/accountConnector.js').AccountUser} AccountUser */\n\n/**\n * @param {object} props\n * @param {boolean} [props.showColorScheme] el ajuste de claro u oscuro en Preferencias; sin modo oscuro, false\n * @param {{initialValues?: (user: AccountUser & Record<string, any>) => Record<string, any>, rules?: Record<string, any>, fields: import('react').ReactNode}} [props.extraFields]\n * campos propios en «Sus datos», debajo del teléfono: sus valores de inicio salen del usuario de la sesión (lo que su\n * conector ponga en session.user), sus reglas de yup y sus campos (FormTextField, FormSelectField… de /forms). Al guardar\n * viajan a connector.updateProfile junto con name y phone\n * @param {Array<{key: string, label: string, content: import('react').ReactNode}>} [props.extraTabs] pestañas propias,\n * después de las de la casa; label ya traducido\n * @param {Array<'data'|'security'|'preferences'>} [props.hiddenTabs] pestañas de la casa que no se ven (PROFILE_TAB)\n * @param {(tab: string) => void} [props.onTabChange] cada que se cambia de pestaña\n * @param {(saved: {values: Record<string, any>, response: unknown}) => void} [props.onProfileSaved] después de guardar «Sus datos»\n * @param {object} [props.texts] los textos que cambian, con la forma de `account` en los textos del paquete:\n * {profile: {tabs: {data: 'Mis datos'}}}\n */\nfunction ProfileScreenContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaProfileScreen.defaultProps) y encima lo que llega aquí\n const {\n showColorScheme = true, extraFields, extraTabs = [], hiddenTabs = [], onTabChange, onProfileSaved, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('account', texts);\n const {session} = useAccount();\n const {useNavigate, useLocation} = useToolkitNavigation();\n const navigate = useNavigate();\n const {pathname, search} = useLocation();\n // las pestañas que se ven: las de la casa que no se escondieron y luego las del proyecto\n const tabs = [\n ...Object.values(PROFILE_TAB).filter((tab) => !hiddenTabs.includes(tab)).map((tab) => ({key: tab, label: t(`profile.tabs.${tab}`)})),\n ...extraTabs.map(({key, label}) => ({key, label})),\n ];\n const requestedTab = new URLSearchParams(search).get(TAB_PARAM);\n const activeTab = tabs.some((tab) => tab.key === requestedTab) ? requestedTab : tabs[0]?.key;\n const activeExtraTab = extraTabs.find((tab) => tab.key === activeTab);\n const changeTab = (tab) => {\n navigate(`${pathname}?${TAB_PARAM}=${tab}`, {replace: true});\n onTabChange?.(tab);\n };\n\n // guard clause: sin sesión no hay perfil que enseñar (el marco de la app ya lleva a entrar)\n if (!session) {\n return null;\n }\n\n return (\n <Stack spacing={2}>\n <ProfileHeader name={session.user.name} email={session.user.email} subtitle={session.user.subtitle} avatarUrl={session.user.avatarUrl}\n chips={session.labels ?? []} />\n <Tabs value={activeTab} onChange={(event, tab) => changeTab(tab)} aria-label={t('profile.tabs.label')}\n variant=\"scrollable\" allowScrollButtonsMobile>\n {tabs.map((tab) => <Tab key={tab.key} value={tab.key} label={tab.label} />)}\n </Tabs>\n {activeTab === PROFILE_TAB.data && <ProfileDataSection extraFields={extraFields} onProfileSaved={onProfileSaved} texts={texts} />}\n {activeTab === PROFILE_TAB.security && <AccountSecuritySection />}\n {activeTab === PROFILE_TAB.preferences && (\n <SectionCard title={t('profile.preferences.title')}>\n <ProfileScreenSectionTitle variant=\"body2\" color=\"text.secondary\" ownSx={{mb: 1}}>{t('profile.preferences.text')}</ProfileScreenSectionTitle>\n <ProfileScreenPreferences ownSx={{maxWidth: FORM_MAX_WIDTH, mx: -1}}>\n <AppearanceSettings showColorScheme={showColorScheme} />\n </ProfileScreenPreferences>\n </SectionCard>\n )}\n {activeExtraTab?.content}\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ProfileScreen = withPieceSlots(ProfileScreenContent, THEME_NAME);\nexport default ProfileScreen;\n"],"names":[],"mappings":"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"}
|
|
@@ -12,11 +12,11 @@ import { signWithPasskey, passkeysSupported } from "../security/passkeys.js";
|
|
|
12
12
|
import { useToolkitNavigation } from "../toolkit/toolkitContext.js";
|
|
13
13
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
14
14
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
15
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
15
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
16
16
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
17
17
|
const THEME_NAME = "CalcitaSignInScreen";
|
|
18
18
|
const SignInAlert = piecePart(THEME_NAME, "Alert", Alert);
|
|
19
|
-
function
|
|
19
|
+
function SignInScreenContent(props) {
|
|
20
20
|
const { homePath = "/", forgotPasswordPath, logo, brandName, footer, showLanguageToggle = true, showColorSchemeToggle = true, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
21
21
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
22
22
|
const { t } = useToolkitTranslation("auth", texts);
|
|
@@ -102,6 +102,7 @@ function SignInScreen(props) {
|
|
|
102
102
|
}
|
|
103
103
|
) });
|
|
104
104
|
}
|
|
105
|
+
const SignInScreen = withPieceSlots(SignInScreenContent, THEME_NAME);
|
|
105
106
|
export {
|
|
106
107
|
SignInScreen as default
|
|
107
108
|
};
|