@stackcraftmx/calcita 0.47.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -12
- package/cli/calcita.js +15 -0
- package/cli/checkTexts.js +209 -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/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/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 +186 -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 +3 -2
- package/dist/inputs/WizardLayout.js.map +1 -1
- 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 +6 -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/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/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 +8 -1
- 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 +6 -19
- 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 +6 -31
- 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/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 +6 -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 +4 -3
- 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
|
@@ -19,7 +19,7 @@ import { useTheme } from "@mui/material/styles";
|
|
|
19
19
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
20
20
|
import { paletteOf } from "../../theme/components/paletteOf.js";
|
|
21
21
|
import { TIMELINE_EMPHASIS, timelineDotOf } from "./timelineDot.js";
|
|
22
|
-
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
22
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../../theme/themePieces.js";
|
|
23
23
|
import { warnUnknownProps } from "../../toolkit/unknownProps.js";
|
|
24
24
|
const THEME_NAME = "CalcitaGroupedTimeline";
|
|
25
25
|
const TimelineGroup = piecePart(THEME_NAME, "Group", Box);
|
|
@@ -35,7 +35,7 @@ const TimelineEntryTitle = piecePart(THEME_NAME, "EntryTitle", Typography);
|
|
|
35
35
|
const HIGHLIGHT_TINT_PERCENT = 8;
|
|
36
36
|
const LEFT_ALIGNED_TIMELINE = { m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: { flex: 0, p: 0 } };
|
|
37
37
|
const ALTERNATE_TIMELINE = { m: 0, px: 0, py: 0.5 };
|
|
38
|
-
function
|
|
38
|
+
function GroupedTimelineContent(props) {
|
|
39
39
|
const { groups, onGroupToggle, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
40
40
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
41
41
|
const theme = useTheme();
|
|
@@ -115,6 +115,7 @@ function GroupedTimeline(props) {
|
|
|
115
115
|
);
|
|
116
116
|
}) });
|
|
117
117
|
}
|
|
118
|
+
const GroupedTimeline = withPieceSlots(GroupedTimelineContent, THEME_NAME);
|
|
118
119
|
export {
|
|
119
120
|
GroupedTimeline as default
|
|
120
121
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaGroupedTimeline con defaultProps y\n// styleOverrides de sus partes: group, groupHeader, groupButton, groupTitle, expandIcon, timeline, opposite, connector,\n// content, entryTitle.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaGroupedTimeline)\nconst THEME_NAME = 'CalcitaGroupedTimeline';\nconst TimelineGroup = piecePart(THEME_NAME, 'Group', Box);\nconst TimelineGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst TimelineGroupButton = piecePart(THEME_NAME, 'GroupButton', ButtonBase);\nconst TimelineGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst TimelineExpandIcon = piecePart(THEME_NAME, 'ExpandIcon', ExpandMoreIcon);\nconst TimelineTimeline = piecePart(THEME_NAME, 'Timeline', Timeline);\nconst TimelineOpposite = piecePart(THEME_NAME, 'Opposite', TimelineOppositeContent);\nconst TimelineConnectorPart = piecePart(THEME_NAME, 'Connector', TimelineConnector);\nconst TimelineContentPart = piecePart(THEME_NAME, 'Content', TimelineContent);\nconst TimelineEntryTitle = piecePart(THEME_NAME, 'EntryTitle', Typography);\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nexport default function GroupedTimeline(props) {\n // lo que el proyecto puso en su tema (components.CalcitaGroupedTimeline.defaultProps) y encima lo que llega aquí\n const {groups, onGroupToggle, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <TimelineGroup key={timelineGroup.key} ownSx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <TimelineGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <TimelineGroupButton disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n ownSx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <TimelineGroupTitle variant=\"h6\" component=\"h3\" ownSx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </TimelineGroupTitle>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <TimelineExpandIcon fontSize=\"small\" ownSx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </TimelineGroupButton>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </TimelineGroupHeader>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <TimelineTimeline position={isWide ? 'alternate' : 'right'} ownSx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOpposite variant=\"body2\" color=\"text.secondary\" ownSx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOpposite>\n )}\n <TimelineSeparator>\n <TimelineConnectorPart ownSx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnectorPart ownSx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContentPart ownSx={{py: 1.5, px: 2, minWidth: 0}}>\n <TimelineEntryTitle variant=\"body2\" component=\"p\" ownSx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </TimelineEntryTitle>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContentPart>\n </TimelineItem>\n );\n })}\n </TimelineTimeline>\n </Collapse>\n </TimelineGroup>\n );\n })}\n </Stack>\n );\n}\n"],"names":["theme"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaGroupedTimeline con defaultProps y\n// styleOverrides de sus partes: group, groupHeader, groupButton, groupTitle, expandIcon, timeline, opposite, connector,\n// content, entryTitle.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaGroupedTimeline)\nconst THEME_NAME = 'CalcitaGroupedTimeline';\nconst TimelineGroup = piecePart(THEME_NAME, 'Group', Box);\nconst TimelineGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst TimelineGroupButton = piecePart(THEME_NAME, 'GroupButton', ButtonBase);\nconst TimelineGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst TimelineExpandIcon = piecePart(THEME_NAME, 'ExpandIcon', ExpandMoreIcon);\nconst TimelineTimeline = piecePart(THEME_NAME, 'Timeline', Timeline);\nconst TimelineOpposite = piecePart(THEME_NAME, 'Opposite', TimelineOppositeContent);\nconst TimelineConnectorPart = piecePart(THEME_NAME, 'Connector', TimelineConnector);\nconst TimelineContentPart = piecePart(THEME_NAME, 'Content', TimelineContent);\nconst TimelineEntryTitle = piecePart(THEME_NAME, 'EntryTitle', Typography);\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nfunction GroupedTimelineContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaGroupedTimeline.defaultProps) y encima lo que llega aquí\n const {groups, onGroupToggle, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <TimelineGroup key={timelineGroup.key} ownSx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <TimelineGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <TimelineGroupButton disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n ownSx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <TimelineGroupTitle variant=\"h6\" component=\"h3\" ownSx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </TimelineGroupTitle>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <TimelineExpandIcon fontSize=\"small\" ownSx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </TimelineGroupButton>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </TimelineGroupHeader>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <TimelineTimeline position={isWide ? 'alternate' : 'right'} ownSx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOpposite variant=\"body2\" color=\"text.secondary\" ownSx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOpposite>\n )}\n <TimelineSeparator>\n <TimelineConnectorPart ownSx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnectorPart ownSx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContentPart ownSx={{py: 1.5, px: 2, minWidth: 0}}>\n <TimelineEntryTitle variant=\"body2\" component=\"p\" ownSx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </TimelineEntryTitle>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContentPart>\n </TimelineItem>\n );\n })}\n </TimelineTimeline>\n </Collapse>\n </TimelineGroup>\n );\n })}\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst GroupedTimeline = withPieceSlots(GroupedTimelineContent, THEME_NAME);\nexport default GroupedTimeline;\n"],"names":["theme"],"mappings":"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"}
|
|
@@ -8,9 +8,9 @@ import FormHelperText from "@mui/material/FormHelperText";
|
|
|
8
8
|
import Stack from "@mui/material/Stack";
|
|
9
9
|
import Typography from "@mui/material/Typography";
|
|
10
10
|
import AttachFileRoundedIcon from "@mui/icons-material/AttachFileRounded";
|
|
11
|
-
import {
|
|
11
|
+
import { useValidationText } from "../forms/useValidationText.js";
|
|
12
12
|
import FieldFrame from "../inputs/FieldFrame.js";
|
|
13
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
13
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
14
14
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
15
15
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
16
16
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
@@ -24,7 +24,7 @@ const AttachmentsButton = piecePart(THEME_NAME, "Button", Button);
|
|
|
24
24
|
const AttachmentsList = piecePart(THEME_NAME, "List", Stack);
|
|
25
25
|
const AttachmentsChip = piecePart(THEME_NAME, "FileChip", Chip);
|
|
26
26
|
const AttachmentsLimits = piecePart(THEME_NAME, "Limits", Typography);
|
|
27
|
-
function
|
|
27
|
+
function AttachmentsInputContent(props) {
|
|
28
28
|
const {
|
|
29
29
|
name,
|
|
30
30
|
label,
|
|
@@ -47,15 +47,17 @@ function AttachmentsInput(props) {
|
|
|
47
47
|
template,
|
|
48
48
|
actions,
|
|
49
49
|
sx,
|
|
50
|
+
messages,
|
|
50
51
|
texts,
|
|
51
52
|
...unknownProps
|
|
52
53
|
} = usePieceProps(props, THEME_NAME);
|
|
53
54
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
54
55
|
const { t } = useToolkitTranslation("common", texts);
|
|
56
|
+
const validationTextOf = useValidationText({ name, label, messages });
|
|
55
57
|
const { formatFileSize } = useFormatters();
|
|
56
58
|
const generatedId = useId();
|
|
57
59
|
const buttonId = `${name ?? "attachments"}-${generatedId}`;
|
|
58
|
-
const errorText =
|
|
60
|
+
const errorText = validationTextOf(error);
|
|
59
61
|
const isLocked = disabled || busy;
|
|
60
62
|
const change = (files) => {
|
|
61
63
|
onChange(files);
|
|
@@ -95,9 +97,9 @@ function AttachmentsInput(props) {
|
|
|
95
97
|
outline: picker.isDragging ? 2 : 0,
|
|
96
98
|
outlineStyle: "dashed",
|
|
97
99
|
outlineColor: "primary.main",
|
|
98
|
-
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
99
|
-
...sx
|
|
100
|
+
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
100
101
|
},
|
|
102
|
+
sx,
|
|
101
103
|
children: [
|
|
102
104
|
/* @__PURE__ */ jsx("input", { ...picker.inputProps, name, "aria-hidden": "true", tabIndex: -1 }),
|
|
103
105
|
(showButton || template || actions) && /* @__PURE__ */ jsxs(AttachmentsActions, { direction: "row", ownSx: { alignItems: "center", flexWrap: "wrap", gap: 1 }, children: [
|
|
@@ -121,6 +123,7 @@ function AttachmentsInput(props) {
|
|
|
121
123
|
);
|
|
122
124
|
return label ? /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, children: content }) : content;
|
|
123
125
|
}
|
|
126
|
+
const AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);
|
|
124
127
|
export {
|
|
125
128
|
AttachmentsInput as default
|
|
126
129
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttachmentsInput.js","sources":["../../src/upload/AttachmentsInput.jsx"],"sourcesContent":["// Varios archivos como en un correo: el botón «Adjuntar» con su clip y cada archivo en un chip con su × (Excel en verde,\n// PDF en rojo…). También se pueden soltar encima. Volver a elegir un archivo con el mismo nombre lo reemplaza (se corrigió en\n// disco) y no cuenta doble.\n//\n// Solo ELIGE (controlado: value / onChange recibe la lista completa), con las reglas de los demás cargadores (accept,\n// maxSize por archivo, maxFiles y maxTotalSize; useFilePicker). La lista puede mezclar archivos nuevos (File) con los que ya\n// están guardados ({id?, name, size?}): al editar un registro, los de antes se ven igual y también se quitan.\n//\n// Huecos: showButton={false} + pickerRef para abrir la ventana desde un botón suyo (el clip de la barra de un correo);\n// template («Descargar la plantilla»); actions (botones suyos junto a «Adjuntar»); onRemove (avisar al API que se quitó uno\n// que ya estaba guardado); label, buttonLabel y texts. Con formik: FormAttachmentsField; con react-hook-form, el del mismo\n// nombre.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAttachmentsInput con defaultProps y\n// styleOverrides de sus partes: root, actions, button, list, fileChip, limits.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useImperativeHandle} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAttachmentsInput)\nconst THEME_NAME = 'CalcitaAttachmentsInput';\nconst AttachmentsRoot = piecePart(THEME_NAME, 'Root', Box);\nconst AttachmentsActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst AttachmentsButton = piecePart(THEME_NAME, 'Button', Button);\nconst AttachmentsList = piecePart(THEME_NAME, 'List', Stack);\nconst AttachmentsChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst AttachmentsLimits = piecePart(THEME_NAME, 'Limits', Typography);\n\n/**\n * La lista conserva el tipo que usa la pantalla para lo ya guardado (T): onChange regresa esos mismos y los File nuevos.\n *\n * @template {File|{id?: string, name: string, size?: number}} T\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida; sin ella, va sin etiqueta (dentro de un correo, por ejemplo)\n * @param {T[]} [props.value] los nuevos y los que ya están guardados\n * @param {(files: Array<T|File>) => void} props.onChange la lista completa\n * @param {() => void} [props.onBlur] se llama al agregar o quitar (para que el formulario lo marque como tocado)\n * @param {(file: T) => void} [props.onRemove] al quitar uno (antes de onChange)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] el peso máximo de cada archivo: bytes o '10 MB'\n * @param {number} [props.maxFiles] cuántos como máximo\n * @param {number|string} [props.maxTotalSize] cuánto pueden pesar todos juntos\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Adjuntar»\n * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)\n * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Adjuntar»\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Adjuntar»\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function AttachmentsInput(props) {\n const {\n name, label, value = [], onChange, onBlur, onRemove, accept, maxSize, maxFiles, maxTotalSize, error, helperText, buttonLabel,\n showButton = true, pickerRef, disabled = false, busy = false, onReject, template, actions, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {formatFileSize} = useFormatters();\n const generatedId = useId();\n const buttonId = `${name ?? 'attachments'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const isLocked = disabled || busy;\n const change = (files) => {\n onChange(files);\n onBlur?.();\n };\n const picker = useFilePicker({\n accept, maxSize, multiple: true, maxFiles, maxTotalSize, keptFiles: value, disabled: isLocked, onAccept: change, onReject, texts,\n });\n useImperativeHandle(pickerRef, () => ({open: picker.open}), [picker.open]);\n\n const remove = (file) => {\n onRemove?.(file);\n change(value.filter((item) => item !== file));\n };\n const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);\n // cuántos y cuánto pesan, solo si hay algún tope que cuidar\n const limitsText = maxFiles || picker.maxTotalBytes\n ? t(picker.maxTotalBytes ? 'upload.summaryOf' : 'upload.summary', {\n count: value.length, total: formatFileSize(totalBytes), max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : undefined,\n })\n : null;\n const bottomText = errorText ?? helperText;\n\n const content = (\n <AttachmentsRoot aria-busy={busy} {...picker.dropHandlers}\n ownSx={{\n borderRadius: 1, outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent', ...sx,\n }}>\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n {(showButton || template || actions) && (\n <AttachmentsActions direction=\"row\" ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 1}}>\n {showButton && (\n <AttachmentsButton id={buttonId} size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open} disabled={isLocked}>\n {buttonLabel ?? t('upload.attach')}\n </AttachmentsButton>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </AttachmentsActions>\n )}\n {value.length > 0 && (\n <AttachmentsList direction=\"row\" role=\"list\" ownSx={{flexWrap: 'wrap', gap: 1, mt: showButton ? 1 : 0}}>\n {value.map((file) => (\n <Box key={file.id ?? file.name} role=\"listitem\" sx={{maxWidth: '100%'}}>\n <FileChip file={file} ChipComponent={AttachmentsChip} removeLabel={t('upload.remove', {name: file.name})}\n onRemove={isLocked ? undefined : () => remove(file)} />\n </Box>\n ))}\n </AttachmentsList>\n )}\n {limitsText && (\n <AttachmentsLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 0.5}}>{limitsText}</AttachmentsLimits>\n )}\n {bottomText && <FormHelperText error={Boolean(errorText)}>{bottomText}</FormHelperText>}\n </AttachmentsRoot>\n );\n\n return label ? <FieldFrame inputId={buttonId} label={label}>{content}</FieldFrame> : content;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AttachmentsInput.js","sources":["../../src/upload/AttachmentsInput.jsx"],"sourcesContent":["// Varios archivos como en un correo: el botón «Adjuntar» con su clip y cada archivo en un chip con su × (Excel en verde,\n// PDF en rojo…). También se pueden soltar encima. Volver a elegir un archivo con el mismo nombre lo reemplaza (se corrigió en\n// disco) y no cuenta doble.\n//\n// Solo ELIGE (controlado: value / onChange recibe la lista completa), con las reglas de los demás cargadores (accept,\n// maxSize por archivo, maxFiles y maxTotalSize; useFilePicker). La lista puede mezclar archivos nuevos (File) con los que ya\n// están guardados ({id?, name, size?}): al editar un registro, los de antes se ven igual y también se quitan.\n//\n// Huecos: showButton={false} + pickerRef para abrir la ventana desde un botón suyo (el clip de la barra de un correo);\n// template («Descargar la plantilla»); actions (botones suyos junto a «Adjuntar»); onRemove (avisar al API que se quitó uno\n// que ya estaba guardado); label, buttonLabel y texts. Con formik: FormAttachmentsField; con react-hook-form, el del mismo\n// nombre.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAttachmentsInput con defaultProps y\n// styleOverrides de sus partes: root, actions, button, list, fileChip, limits.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useImperativeHandle} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAttachmentsInput)\nconst THEME_NAME = 'CalcitaAttachmentsInput';\nconst AttachmentsRoot = piecePart(THEME_NAME, 'Root', Box);\nconst AttachmentsActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst AttachmentsButton = piecePart(THEME_NAME, 'Button', Button);\nconst AttachmentsList = piecePart(THEME_NAME, 'List', Stack);\nconst AttachmentsChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst AttachmentsLimits = piecePart(THEME_NAME, 'Limits', Typography);\n\n/**\n * La lista conserva el tipo que usa la pantalla para lo ya guardado (T): onChange regresa esos mismos y los File nuevos.\n *\n * @template {File|{id?: string, name: string, size?: number}} T\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida; sin ella, va sin etiqueta (dentro de un correo, por ejemplo)\n * @param {T[]} [props.value] los nuevos y los que ya están guardados\n * @param {(files: Array<T|File>) => void} props.onChange la lista completa\n * @param {() => void} [props.onBlur] se llama al agregar o quitar (para que el formulario lo marque como tocado)\n * @param {(file: T) => void} [props.onRemove] al quitar uno (antes de onChange)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] el peso máximo de cada archivo: bytes o '10 MB'\n * @param {number} [props.maxFiles] cuántos como máximo\n * @param {number|string} [props.maxTotalSize] cuánto pueden pesar todos juntos\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Adjuntar»\n * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)\n * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Adjuntar»\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Adjuntar»\n * @param {object} [props.sx]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n * @param {Record<string, import('react').ElementType>} [props.slots] otro componente en lugar de una parte (root, actions, list…)\n * @param {Record<string, object>} [props.slotProps] props de más para una parte\n */\nfunction AttachmentsInputContent(props) {\n const {\n name, label, value = [], onChange, onBlur, onRemove, accept, maxSize, maxFiles, maxTotalSize, error, helperText, buttonLabel,\n showButton = true, pickerRef, disabled = false, busy = false, onReject, template, actions, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const {formatFileSize} = useFormatters();\n const generatedId = useId();\n const buttonId = `${name ?? 'attachments'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (files) => {\n onChange(files);\n onBlur?.();\n };\n const picker = useFilePicker({\n accept, maxSize, multiple: true, maxFiles, maxTotalSize, keptFiles: value, disabled: isLocked, onAccept: change, onReject, texts,\n });\n useImperativeHandle(pickerRef, () => ({open: picker.open}), [picker.open]);\n\n const remove = (file) => {\n onRemove?.(file);\n change(value.filter((item) => item !== file));\n };\n const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);\n // cuántos y cuánto pesan, solo si hay algún tope que cuidar\n const limitsText = maxFiles || picker.maxTotalBytes\n ? t(picker.maxTotalBytes ? 'upload.summaryOf' : 'upload.summary', {\n count: value.length, total: formatFileSize(totalBytes), max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : undefined,\n })\n : null;\n const bottomText = errorText ?? helperText;\n\n const content = (\n <AttachmentsRoot aria-busy={busy} {...picker.dropHandlers}\n ownSx={{\n borderRadius: 1, outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}>\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n {(showButton || template || actions) && (\n <AttachmentsActions direction=\"row\" ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 1}}>\n {showButton && (\n <AttachmentsButton id={buttonId} size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open} disabled={isLocked}>\n {buttonLabel ?? t('upload.attach')}\n </AttachmentsButton>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </AttachmentsActions>\n )}\n {value.length > 0 && (\n <AttachmentsList direction=\"row\" role=\"list\" ownSx={{flexWrap: 'wrap', gap: 1, mt: showButton ? 1 : 0}}>\n {value.map((file) => (\n <Box key={file.id ?? file.name} role=\"listitem\" sx={{maxWidth: '100%'}}>\n <FileChip file={file} ChipComponent={AttachmentsChip} removeLabel={t('upload.remove', {name: file.name})}\n onRemove={isLocked ? undefined : () => remove(file)} />\n </Box>\n ))}\n </AttachmentsList>\n )}\n {limitsText && (\n <AttachmentsLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 0.5}}>{limitsText}</AttachmentsLimits>\n )}\n {bottomText && <FormHelperText error={Boolean(errorText)}>{bottomText}</FormHelperText>}\n </AttachmentsRoot>\n );\n\n return label ? <FieldFrame inputId={buttonId} label={label}>{content}</FieldFrame> : content;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\n// OJO: con su tipo propio, el genérico (T: el tipo de cada archivo) que la envoltura borraría; slots y slotProps van en su\n// documentación\n/** @type {typeof AttachmentsInputContent} */\nconst AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);\nexport default AttachmentsInput;\n"],"names":[],"mappings":"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"}
|
package/dist/upload/FileChip.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import Chip from "@mui/material/Chip";
|
|
4
4
|
import CancelIcon from "@mui/icons-material/Cancel";
|
|
5
|
+
import { sxListOf } from "../theme/themePieces.js";
|
|
5
6
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
6
7
|
import { fileAppearanceOf } from "./fileAppearance.js";
|
|
7
8
|
function FileChip({ file, onRemove, removeLabel, ChipComponent = Chip, ownSx }) {
|
|
8
9
|
const { formatFileSize } = useFormatters();
|
|
9
10
|
const { Icon, color } = fileAppearanceOf(file.name);
|
|
10
11
|
const label = typeof file.size === "number" ? `${file.name} · ${formatFileSize(file.size)}` : file.name;
|
|
11
|
-
const chipSx = { maxWidth: "100%", height: "auto", py: 0.5, "& .MuiChip-label": { whiteSpace: "normal", overflowWrap: "anywhere" }, ...ownSx
|
|
12
|
+
const chipSx = [{ maxWidth: "100%", height: "auto", py: 0.5, "& .MuiChip-label": { whiteSpace: "normal", overflowWrap: "anywhere" } }, ...sxListOf(ownSx)];
|
|
12
13
|
return /* @__PURE__ */ jsx(
|
|
13
14
|
ChipComponent,
|
|
14
15
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileChip.js","sources":["../../src/upload/FileChip.jsx"],"sourcesContent":["// Un archivo elegido como chip: su ícono según el tipo, su nombre y su peso, y la × para quitarlo. Lo comparten los\n// cargadores del paquete; cada uno le pasa su parte del tema (ChipComponent) para que el proyecto la cambie desde ahí.\n//\n// Sirve igual para un archivo nuevo (File) y para uno que ya está guardado en el servidor ({name, size?}): sin peso, solo\n// se ve el nombre. Un nombre largo se parte en renglones en lugar de cortarse: es lo que la persona revisa antes de subir.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Chip from '@mui/material/Chip';\nimport CancelIcon from '@mui/icons-material/Cancel';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {fileAppearanceOf} from '@/upload/fileAppearance.js';\n\n/**\n * @param {object} props\n * @param {{name: string, size?: number}} props.file\n * @param {(() => void)|undefined} [props.onRemove] sin él no hay × (con el candado puesto, por ejemplo)\n * @param {string} props.removeLabel ya traducido: «Quitar contrato.pdf»\n * @param {import('react').ElementType} [props.ChipComponent] la parte del tema del cargador (por omisión, el Chip de MUI)\n * @param {object} [props.ownSx] el estilo de la casa, debajo del tema del proyecto\n */\nexport default function FileChip({file, onRemove, removeLabel, ChipComponent = Chip, ownSx}) {\n const {formatFileSize} = useFormatters();\n const {Icon, color} = fileAppearanceOf(file.name);\n const label = typeof file.size === 'number' ? `${file.name} · ${formatFileSize(file.size)}` : file.name;\n const chipSx = {maxWidth: '100%', height: 'auto', py: 0.5, '& .MuiChip-label': {whiteSpace: 'normal', overflowWrap: 'anywhere'}, ...ownSx
|
|
1
|
+
{"version":3,"file":"FileChip.js","sources":["../../src/upload/FileChip.jsx"],"sourcesContent":["// Un archivo elegido como chip: su ícono según el tipo, su nombre y su peso, y la × para quitarlo. Lo comparten los\n// cargadores del paquete; cada uno le pasa su parte del tema (ChipComponent) para que el proyecto la cambie desde ahí.\n//\n// Sirve igual para un archivo nuevo (File) y para uno que ya está guardado en el servidor ({name, size?}): sin peso, solo\n// se ve el nombre. Un nombre largo se parte en renglones en lugar de cortarse: es lo que la persona revisa antes de subir.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Chip from '@mui/material/Chip';\nimport CancelIcon from '@mui/icons-material/Cancel';\n\nimport {sxListOf} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {fileAppearanceOf} from '@/upload/fileAppearance.js';\n\n/**\n * @param {object} props\n * @param {{name: string, size?: number}} props.file\n * @param {(() => void)|undefined} [props.onRemove] sin él no hay × (con el candado puesto, por ejemplo)\n * @param {string} props.removeLabel ya traducido: «Quitar contrato.pdf»\n * @param {import('react').ElementType} [props.ChipComponent] la parte del tema del cargador (por omisión, el Chip de MUI)\n * @param {object} [props.ownSx] el estilo de la casa, debajo del tema del proyecto\n */\nexport default function FileChip({file, onRemove, removeLabel, ChipComponent = Chip, ownSx}) {\n const {formatFileSize} = useFormatters();\n const {Icon, color} = fileAppearanceOf(file.name);\n const label = typeof file.size === 'number' ? `${file.name} · ${formatFileSize(file.size)}` : file.name;\n // en lista: lo del cargador va DESPUÉS y se combina regla por regla (dentro del mismo objeto reemplazaría la llave entera)\n const chipSx = [{maxWidth: '100%', height: 'auto', py: 0.5, '& .MuiChip-label': {whiteSpace: 'normal', overflowWrap: 'anywhere'}}, ...sxListOf(ownSx)];\n\n return (\n <ChipComponent\n icon={<Icon fontSize=\"small\" color={color} />}\n label={label}\n variant=\"outlined\"\n onDelete={onRemove}\n deleteIcon={<CancelIcon aria-label={removeLabel} />}\n {...(ChipComponent === Chip ? {sx: chipSx} : {ownSx: chipSx})}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;AAsBA,SAAwB,SAAS,EAAC,MAAM,UAAU,aAAa,gBAAgB,MAAM,SAAQ;AACzF,QAAM,EAAC,eAAA,IAAkB,cAAA;AACzB,QAAM,EAAC,MAAM,MAAA,IAAS,iBAAiB,KAAK,IAAI;AAChD,QAAM,QAAQ,OAAO,KAAK,SAAS,WAAW,GAAG,KAAK,IAAI,MAAM,eAAe,KAAK,IAAI,CAAC,KAAK,KAAK;AAEnG,QAAM,SAAS,CAAC,EAAC,UAAU,QAAQ,QAAQ,QAAQ,IAAI,KAAK,oBAAoB,EAAC,YAAY,UAAU,cAAc,WAAA,EAAU,GAAI,GAAG,SAAS,KAAK,CAAC;AAErJ,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAAM,oBAAC,MAAA,EAAK,UAAS,SAAQ,OAAc;AAAA,MAC3C;AAAA,MACA,SAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY,oBAAC,YAAA,EAAW,cAAY,YAAA,CAAa;AAAA,MAChD,GAAI,kBAAkB,OAAO,EAAC,IAAI,OAAA,IAAU,EAAC,OAAO,OAAA;AAAA,IAAM;AAAA,EAAA;AAGvE;"}
|
|
@@ -7,7 +7,7 @@ import Paper from "@mui/material/Paper";
|
|
|
7
7
|
import Stack from "@mui/material/Stack";
|
|
8
8
|
import Typography from "@mui/material/Typography";
|
|
9
9
|
import ModalWaiting from "../feedback/ModalWaiting.js";
|
|
10
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
10
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
11
11
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
12
12
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
13
13
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
@@ -22,7 +22,7 @@ const DropzoneHint = piecePart(THEME_NAME, "Hint", Typography);
|
|
|
22
22
|
const DropzoneActions = piecePart(THEME_NAME, "Actions", Stack);
|
|
23
23
|
const DropzoneLimits = piecePart(THEME_NAME, "Limits", Typography);
|
|
24
24
|
const DropzoneExtra = piecePart(THEME_NAME, "Extra", Box);
|
|
25
|
-
function
|
|
25
|
+
function FileDropzoneContent(props) {
|
|
26
26
|
const {
|
|
27
27
|
value = null,
|
|
28
28
|
onChange,
|
|
@@ -61,9 +61,9 @@ function FileDropzone(props) {
|
|
|
61
61
|
borderColor: picker.isDragging ? "primary.main" : "divider",
|
|
62
62
|
bgcolor: picker.isDragging ? "action.hover" : "background.paper",
|
|
63
63
|
transition: "border-color .15s ease, background-color .15s ease",
|
|
64
|
-
opacity: disabled ? 0.6 : 1
|
|
65
|
-
...sx
|
|
64
|
+
opacity: disabled ? 0.6 : 1
|
|
66
65
|
},
|
|
66
|
+
sx,
|
|
67
67
|
children: [
|
|
68
68
|
/* @__PURE__ */ jsx("input", { ...picker.inputProps, "aria-label": hintText }),
|
|
69
69
|
value ? /* @__PURE__ */ jsx(DropzoneFileArea, { ownSx: { mb: 2 }, children: /* @__PURE__ */ jsx(
|
|
@@ -87,6 +87,7 @@ function FileDropzone(props) {
|
|
|
87
87
|
}
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
+
const FileDropzone = withPieceSlots(FileDropzoneContent, THEME_NAME);
|
|
90
91
|
export {
|
|
91
92
|
FileDropzone as default
|
|
92
93
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileDropzone.js","sources":["../../src/upload/FileDropzone.jsx"],"sourcesContent":["// La zona para soltar UN archivo, con puro MUI (sin librerías): se suelta o se elige, se revisa su tipo y su peso, y el\n// elegido se ve con su ícono, su nombre y su peso, con la × para quitarlo sin volver a abrir la ventana de archivos.\n//\n// Hay otra con el mismo uso y FilePond por dentro (@stackcraftmx/calcita/filepond): animaciones, varios archivos y\n// avance por archivo. Cambiar de una a otra es cambiar el import. Para un formulario, del tamaño de un campo: FileInput;\n// para varios como en un correo: AttachmentsInput; para una foto: ImageInput.\n//\n// Solo ELIGE el archivo (controlado: value / onChange). Enviarlo es de la pantalla: arma su FormData con los campos que\n// pida su backend y lo sube (uploadWithProgress + uploadMachine). Para eso están los huecos:\n// actions botones junto a «Elegir archivo» (p. ej. el de enviar)\n// template «Descargar la plantilla» junto a «Elegir archivo» (las props de TemplateDownloadButton)\n// children lo que va abajo (campos del formulario, el avance de la subida)\n//\n// Configuración: accept y maxSize (upload/fileRules.js); busy pone el candado mientras se sube (no se puede quitar ni\n// cambiar el archivo a media subida) y busyModal además bloquea toda la pantalla con ModalWaiting.\n//\n// Lo que se rechaza se avisa con toastError y los textos del paquete; con onReject la pantalla decide qué hacer\n// (useFilePicker, el mismo de los demás cargadores).\n//\n// Mobile primero: en el teléfono no hay arrastrar; el botón es el camino y los botones se apilan a lo ancho.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileDropzone con defaultProps y\n// styleOverrides de sus partes: root, fileArea, fileChip, hint, actions, limits, extra.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport ModalWaiting from '@/feedback/ModalWaiting.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileDropzone)\nconst THEME_NAME = 'CalcitaFileDropzone';\nconst DropzoneRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst DropzoneFileArea = piecePart(THEME_NAME, 'FileArea', Box);\nconst DropzoneFileChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst DropzoneHint = piecePart(THEME_NAME, 'Hint', Typography);\nconst DropzoneActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst DropzoneLimits = piecePart(THEME_NAME, 'Limits', Typography);\nconst DropzoneExtra = piecePart(THEME_NAME, 'Extra', Box);\n\n/**\n * @param {object} props\n * @param {File|null} [props.value] el archivo elegido\n * @param {(file: File|null) => void} props.onChange null al quitarlo\n * @param {string|string[]} [props.accept] 'excel' | ['excel', 'pdf'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string} [props.hint] ya traducido; por omisión «Arrastre aquí su archivo»\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Elegir archivo»\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede soltar, elegir ni quitar\n * @param {boolean|{title?: string, text?: string}} [props.busyModal] además bloquea la pantalla mientras busy\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Elegir archivo»: {href} de un archivo fijo o {request} al API\n * @param {import('react').ReactNode} [props.actions]\n * @param {import('react').ReactNode} [props.children]\n * @param {object} [props.sx] ajustes del recuadro\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"FileDropzone.js","sources":["../../src/upload/FileDropzone.jsx"],"sourcesContent":["// La zona para soltar UN archivo, con puro MUI (sin librerías): se suelta o se elige, se revisa su tipo y su peso, y el\n// elegido se ve con su ícono, su nombre y su peso, con la × para quitarlo sin volver a abrir la ventana de archivos.\n//\n// Hay otra con el mismo uso y FilePond por dentro (@stackcraftmx/calcita/filepond): animaciones, varios archivos y\n// avance por archivo. Cambiar de una a otra es cambiar el import. Para un formulario, del tamaño de un campo: FileInput;\n// para varios como en un correo: AttachmentsInput; para una foto: ImageInput.\n//\n// Solo ELIGE el archivo (controlado: value / onChange). Enviarlo es de la pantalla: arma su FormData con los campos que\n// pida su backend y lo sube (uploadWithProgress + uploadMachine). Para eso están los huecos:\n// actions botones junto a «Elegir archivo» (p. ej. el de enviar)\n// template «Descargar la plantilla» junto a «Elegir archivo» (las props de TemplateDownloadButton)\n// children lo que va abajo (campos del formulario, el avance de la subida)\n//\n// Configuración: accept y maxSize (upload/fileRules.js); busy pone el candado mientras se sube (no se puede quitar ni\n// cambiar el archivo a media subida) y busyModal además bloquea toda la pantalla con ModalWaiting.\n//\n// Lo que se rechaza se avisa con toastError y los textos del paquete; con onReject la pantalla decide qué hacer\n// (useFilePicker, el mismo de los demás cargadores).\n//\n// Mobile primero: en el teléfono no hay arrastrar; el botón es el camino y los botones se apilan a lo ancho.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileDropzone con defaultProps y\n// styleOverrides de sus partes: root, fileArea, fileChip, hint, actions, limits, extra.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport ModalWaiting from '@/feedback/ModalWaiting.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileDropzone)\nconst THEME_NAME = 'CalcitaFileDropzone';\nconst DropzoneRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst DropzoneFileArea = piecePart(THEME_NAME, 'FileArea', Box);\nconst DropzoneFileChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst DropzoneHint = piecePart(THEME_NAME, 'Hint', Typography);\nconst DropzoneActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst DropzoneLimits = piecePart(THEME_NAME, 'Limits', Typography);\nconst DropzoneExtra = piecePart(THEME_NAME, 'Extra', Box);\n\n/**\n * @param {object} props\n * @param {File|null} [props.value] el archivo elegido\n * @param {(file: File|null) => void} props.onChange null al quitarlo\n * @param {string|string[]} [props.accept] 'excel' | ['excel', 'pdf'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string} [props.hint] ya traducido; por omisión «Arrastre aquí su archivo»\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Elegir archivo»\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede soltar, elegir ni quitar\n * @param {boolean|{title?: string, text?: string}} [props.busyModal] además bloquea la pantalla mientras busy\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Elegir archivo»: {href} de un archivo fijo o {request} al API\n * @param {import('react').ReactNode} [props.actions]\n * @param {import('react').ReactNode} [props.children]\n * @param {object} [props.sx] ajustes del recuadro\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction FileDropzoneContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFileDropzone.defaultProps) y encima lo que llega aquí\n const {\n value = null, onChange, accept, maxSize, hint, buttonLabel, disabled = false, busy = false, busyModal = false, onReject, template,\n actions, children, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {formatFileSize} = useFormatters();\n const isLocked = disabled || busy;\n const hintText = hint ?? t('upload.hint');\n const picker = useFilePicker({accept, maxSize, disabled: isLocked, onAccept: onChange, onReject, texts});\n\n return (\n <DropzoneRoot\n variant=\"outlined\"\n aria-busy={busy}\n {...picker.dropHandlers}\n ownSx={{\n p: {xs: 3, sm: 5},\n textAlign: 'center',\n borderStyle: 'dashed',\n borderWidth: 2,\n borderColor: picker.isDragging ? 'primary.main' : 'divider',\n bgcolor: picker.isDragging ? 'action.hover' : 'background.paper',\n transition: 'border-color .15s ease, background-color .15s ease',\n opacity: disabled ? 0.6 : 1,\n }}\n sx={sx}\n >\n <input {...picker.inputProps} aria-label={hintText} />\n {value ? (\n <DropzoneFileArea ownSx={{mb: 2}}>\n <FileChip file={value} ChipComponent={DropzoneFileChip} removeLabel={t('upload.remove', {name: value.name})}\n onRemove={isLocked ? undefined : () => onChange(null)} />\n </DropzoneFileArea>\n ) : (\n <DropzoneHint variant=\"body1\" ownSx={{mb: 2, overflowWrap: 'anywhere'}}>{hintText}</DropzoneHint>\n )}\n <DropzoneActions direction={{xs: 'column', sm: 'row'}} spacing={2} ownSx={{justifyContent: 'center', alignItems: {xs: 'stretch', sm: 'center'}}}>\n <Button variant=\"outlined\" onClick={picker.open} disabled={isLocked}>{buttonLabel ?? t('upload.choose')}</Button>\n {template && <TemplateDownloadButton texts={texts} {...template} />}\n {actions}\n </DropzoneActions>\n {/* el límite, discreto, solo si hay límite */}\n {picker.maxBytes && (\n <DropzoneLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 1.5}}>\n {t('upload.maxSizeHint', {max: formatFileSize(picker.maxBytes)})}\n </DropzoneLimits>\n )}\n {children && <DropzoneExtra ownSx={{mt: 3, textAlign: 'left'}}>{children}</DropzoneExtra>}\n {busyModal && <ModalWaiting open={busy} title={busyModal.title} text={busyModal.text} />}\n </DropzoneRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FileDropzone = withPieceSlots(FileDropzoneContent, THEME_NAME);\nexport default FileDropzone;\n"],"names":[],"mappings":"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"}
|
package/dist/upload/FileInput.js
CHANGED
|
@@ -7,9 +7,9 @@ import FormHelperText from "@mui/material/FormHelperText";
|
|
|
7
7
|
import Stack from "@mui/material/Stack";
|
|
8
8
|
import Typography from "@mui/material/Typography";
|
|
9
9
|
import AttachFileRoundedIcon from "@mui/icons-material/AttachFileRounded";
|
|
10
|
-
import {
|
|
10
|
+
import { useValidationText } from "../forms/useValidationText.js";
|
|
11
11
|
import FieldFrame from "../inputs/FieldFrame.js";
|
|
12
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
12
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
13
13
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
14
14
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
15
15
|
import FileChip from "./FileChip.js";
|
|
@@ -20,7 +20,7 @@ const FileInputRoot = piecePart(THEME_NAME, "Root", Stack);
|
|
|
20
20
|
const FileInputButton = piecePart(THEME_NAME, "Button", Button);
|
|
21
21
|
const FileInputChip = piecePart(THEME_NAME, "FileChip", Chip);
|
|
22
22
|
const FileInputEmpty = piecePart(THEME_NAME, "Empty", Typography);
|
|
23
|
-
function
|
|
23
|
+
function FileInputContent(props) {
|
|
24
24
|
const {
|
|
25
25
|
name,
|
|
26
26
|
label,
|
|
@@ -39,14 +39,16 @@ function FileInput(props) {
|
|
|
39
39
|
template,
|
|
40
40
|
actions,
|
|
41
41
|
sx,
|
|
42
|
+
messages,
|
|
42
43
|
texts,
|
|
43
44
|
...unknownProps
|
|
44
45
|
} = usePieceProps(props, THEME_NAME);
|
|
45
46
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
46
47
|
const { t } = useToolkitTranslation("common", texts);
|
|
48
|
+
const validationTextOf = useValidationText({ name, label, messages });
|
|
47
49
|
const generatedId = useId();
|
|
48
50
|
const buttonId = `${name ?? "file"}-${generatedId}`;
|
|
49
|
-
const errorText =
|
|
51
|
+
const errorText = validationTextOf(error);
|
|
50
52
|
const isLocked = disabled || busy;
|
|
51
53
|
const change = (file) => {
|
|
52
54
|
onChange(file);
|
|
@@ -71,9 +73,9 @@ function FileInput(props) {
|
|
|
71
73
|
outline: picker.isDragging ? 2 : 0,
|
|
72
74
|
outlineStyle: "dashed",
|
|
73
75
|
outlineColor: "primary.main",
|
|
74
|
-
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
75
|
-
...sx
|
|
76
|
+
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
76
77
|
},
|
|
78
|
+
sx,
|
|
77
79
|
children: [
|
|
78
80
|
/* @__PURE__ */ jsx("input", { ...picker.inputProps, name, "aria-hidden": "true", tabIndex: -1 }),
|
|
79
81
|
/* @__PURE__ */ jsx(
|
|
@@ -106,6 +108,7 @@ function FileInput(props) {
|
|
|
106
108
|
/* @__PURE__ */ jsx(FormHelperText, { error: Boolean(errorText), children: errorText ?? helperText })
|
|
107
109
|
] }) });
|
|
108
110
|
}
|
|
111
|
+
const FileInput = withPieceSlots(FileInputContent, THEME_NAME);
|
|
109
112
|
export {
|
|
110
113
|
FileInput as default
|
|
111
114
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileInput.js","sources":["../../src/upload/FileInput.jsx"],"sourcesContent":["// Un archivo del tamaño de un campo, para un formulario (la constancia fiscal de un cliente, el contrato firmado): la\n// etiqueta como los demás campos, el botón «Elegir archivo», el elegido en su chip con la × y abajo su ayuda o su error.\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Solo ELIGE (controlado: value / onChange), con las mismas reglas que la zona grande (accept, maxSize; useFilePicker):\n// enviarlo es de la pantalla. value puede ser un archivo nuevo (File) o uno que ya está guardado ({name, size?}), para\n// editar un registro que ya lo tiene.\n//\n// Huecos: template («Descargar la plantilla» junto al botón), actions (botones suyos en el mismo renglón), emptyText,\n// buttonLabel y texts. Con formik: FormFileField (/forms); con react-hook-form, el del mismo nombre (/react-hook-form).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileInput con defaultProps y\n// styleOverrides de sus partes: root, button, fileChip, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {
|
|
1
|
+
{"version":3,"file":"FileInput.js","sources":["../../src/upload/FileInput.jsx"],"sourcesContent":["// Un archivo del tamaño de un campo, para un formulario (la constancia fiscal de un cliente, el contrato firmado): la\n// etiqueta como los demás campos, el botón «Elegir archivo», el elegido en su chip con la × y abajo su ayuda o su error.\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Solo ELIGE (controlado: value / onChange), con las mismas reglas que la zona grande (accept, maxSize; useFilePicker):\n// enviarlo es de la pantalla. value puede ser un archivo nuevo (File) o uno que ya está guardado ({name, size?}), para\n// editar un registro que ya lo tiene.\n//\n// Huecos: template («Descargar la plantilla» junto al botón), actions (botones suyos en el mismo renglón), emptyText,\n// buttonLabel y texts. Con formik: FormFileField (/forms); con react-hook-form, el del mismo nombre (/react-hook-form).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileInput con defaultProps y\n// styleOverrides de sus partes: root, button, fileChip, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileInput)\nconst THEME_NAME = 'CalcitaFileInput';\nconst FileInputRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FileInputButton = piecePart(THEME_NAME, 'Button', Button);\nconst FileInputChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst FileInputEmpty = piecePart(THEME_NAME, 'Empty', Typography);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida\n * @param {File|{name: string, size?: number}|null} [props.value] el archivo nuevo o el que ya está guardado\n * @param {(file: File|null) => void} props.onChange null al quitarlo\n * @param {() => void} [props.onBlur] se llama al elegir o quitar (para que el formulario lo marque como tocado)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Elegir archivo»\n * @param {string} [props.emptyText] ya traducido; por omisión «Ningún archivo elegido»\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede elegir, soltar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto al botón\n * @param {import('react').ReactNode} [props.actions] botones suyos en el mismo renglón\n * @param {object} [props.sx] ajustes del renglón\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction FileInputContent(props) {\n const {\n name, label, value = null, onChange, onBlur, accept, maxSize, error, helperText = ' ', buttonLabel, emptyText, disabled = false,\n busy = false, onReject, template, actions, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const buttonId = `${name ?? 'file'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (file) => {\n onChange(file);\n onBlur?.();\n };\n const picker = useFilePicker({accept, maxSize, disabled: isLocked, onAccept: change, onReject, texts});\n\n return (\n <FieldFrame inputId={buttonId} label={label}>\n <div>\n <FileInputRoot\n direction=\"row\"\n aria-busy={busy}\n {...picker.dropHandlers}\n ownSx={{\n alignItems: 'center', flexWrap: 'wrap', gap: 1, minHeight: 40, borderRadius: 1, px: 0.5, mx: -0.5,\n outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}\n >\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n <FileInputButton id={buttonId} variant=\"outlined\" size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open}\n disabled={isLocked} ownSx={{flexShrink: 0}}>\n {buttonLabel ?? t('upload.choose')}\n </FileInputButton>\n {value ? (\n <FileChip file={value} ChipComponent={FileInputChip} removeLabel={t('upload.remove', {name: value.name})}\n onRemove={isLocked ? undefined : () => change(null)} />\n ) : (\n <FileInputEmpty variant=\"body2\" color=\"text.secondary\">{emptyText ?? t('upload.empty')}</FileInputEmpty>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </FileInputRoot>\n <FormHelperText error={Boolean(errorText)}>{errorText ?? helperText}</FormHelperText>\n </div>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FileInput = withPieceSlots(FileInputContent, THEME_NAME);\nexport default FileInput;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAiCA,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,QAAQ,KAAK;AACzD,MAAM,kBAAkB,UAAU,YAAY,UAAU,MAAM;AAC9D,MAAM,gBAAgB,UAAU,YAAY,YAAY,IAAI;AAC5D,MAAM,iBAAiB,UAAU,YAAY,SAAS,UAAU;AA0BhE,SAAS,iBAAiB,OAAO;AAC7B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO,QAAQ;AAAA,IAAM;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAa;AAAA,IAAW,WAAW;AAAA,IAC1H,OAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAU;AAAA,IAAS;AAAA,IAAI;AAAA,IAAU;AAAA,IACzD,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,mBAAmB,kBAAkB,EAAC,MAAM,OAAO,UAAS;AAClE,QAAM,cAAc,MAAA;AACpB,QAAM,WAAW,GAAG,QAAQ,MAAM,IAAI,WAAW;AACjD,QAAM,YAAY,iBAAiB,KAAK;AACxC,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,CAAC,SAAS;AACrB,aAAS,IAAI;AACb,aAAA;AAAA,EACJ;AACA,QAAM,SAAS,cAAc,EAAC,QAAQ,SAAS,UAAU,UAAU,UAAU,QAAQ,UAAU,MAAA,CAAM;AAErG,6BACK,YAAA,EAAW,SAAS,UAAU,OAC3B,+BAAC,OAAA,EACG,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,WAAU;AAAA,QACV,aAAW;AAAA,QACV,GAAG,OAAO;AAAA,QACX,OAAO;AAAA,UACH,YAAY;AAAA,UAAU,UAAU;AAAA,UAAQ,KAAK;AAAA,UAAG,WAAW;AAAA,UAAI,cAAc;AAAA,UAAG,IAAI;AAAA,UAAK,IAAI;AAAA,UAC7F,SAAS,OAAO,aAAa,IAAI;AAAA,UAAG,cAAc;AAAA,UAAU,cAAc;AAAA,UAC1E,SAAS,OAAO,aAAa,iBAAiB;AAAA,QAAA;AAAA,QAElD;AAAA,QAEA,UAAA;AAAA,UAAA,oBAAC,SAAA,EAAO,GAAG,OAAO,YAAY,MAAY,eAAY,QAAO,UAAU,IAAI;AAAA,UAC3E;AAAA,YAAC;AAAA,YAAA;AAAA,cAAgB,IAAI;AAAA,cAAU,SAAQ;AAAA,cAAW,MAAK;AAAA,cAAQ,+BAAY,uBAAA,EAAsB;AAAA,cAAI,SAAS,OAAO;AAAA,cACpG,UAAU;AAAA,cAAU,OAAO,EAAC,YAAY,EAAA;AAAA,cACpD,UAAA,eAAe,EAAE,eAAe;AAAA,YAAA;AAAA,UAAA;AAAA,UAEpC,QACG;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,MAAM;AAAA,cAAO,eAAe;AAAA,cAAe,aAAa,EAAE,iBAAiB,EAAC,MAAM,MAAM,MAAK;AAAA,cAC7F,UAAU,WAAW,SAAY,MAAM,OAAO,IAAI;AAAA,YAAA;AAAA,UAAA,IAE5D,oBAAC,gBAAA,EAAe,SAAQ,SAAQ,OAAM,kBAAkB,UAAA,aAAa,EAAE,cAAc,EAAA,CAAE;AAAA,UAE1F,YAAY,oBAAC,wBAAA,EAAuB,MAAK,SAAQ,OAAe,GAAG,UAAU;AAAA,UAC7E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,wBAEJ,gBAAA,EAAe,OAAO,QAAQ,SAAS,GAAI,uBAAa,WAAA,CAAW;AAAA,EAAA,EAAA,CACxE,EAAA,CACJ;AAER;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
|
|
@@ -15,11 +15,11 @@ import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
|
|
|
15
15
|
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
|
|
16
16
|
import FileUploadOutlinedIcon from "@mui/icons-material/FileUploadOutlined";
|
|
17
17
|
import { startDownload } from "../downloads/downloadQueue.js";
|
|
18
|
-
import {
|
|
18
|
+
import { useValidationText } from "../forms/useValidationText.js";
|
|
19
19
|
import { newIdentifier } from "../identifiers.js";
|
|
20
20
|
import FieldFrame from "../inputs/FieldFrame.js";
|
|
21
21
|
import { fieldErrorsOf } from "../problems.js";
|
|
22
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
22
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
23
23
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
24
24
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
25
25
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
@@ -56,7 +56,7 @@ function uploadErrorTextOf(error) {
|
|
|
56
56
|
const firstFieldMessage = Object.values(fieldErrorsOf(problem ?? null)).flat()[0];
|
|
57
57
|
return firstFieldMessage ?? problem?.detail ?? null;
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function FileUploadInputContent(props) {
|
|
60
60
|
const {
|
|
61
61
|
name,
|
|
62
62
|
label,
|
|
@@ -80,11 +80,13 @@ function FileUploadInput(props) {
|
|
|
80
80
|
onRemove,
|
|
81
81
|
onStatusChange,
|
|
82
82
|
sx,
|
|
83
|
+
messages,
|
|
83
84
|
texts,
|
|
84
85
|
...unknownProps
|
|
85
86
|
} = usePieceProps(props, THEME_NAME);
|
|
86
87
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
87
88
|
const { t } = useToolkitTranslation("common", texts);
|
|
89
|
+
const validationTextOf = useValidationText({ name, label, messages });
|
|
88
90
|
const { formatFileSize } = useFormatters();
|
|
89
91
|
const generatedId = useId();
|
|
90
92
|
const inputId = `${name ?? "file-upload"}-${generatedId}`;
|
|
@@ -173,7 +175,7 @@ function FileUploadInput(props) {
|
|
|
173
175
|
};
|
|
174
176
|
const shownFile = chosenFile ?? (value ? uploadedFile : null);
|
|
175
177
|
const statusLabel = status === FILE_UPLOAD_STATUS.uploading ? t("upload.status.uploading", { percent: progress ?? 0 }) : t(`upload.status.${status}`);
|
|
176
|
-
const validationText =
|
|
178
|
+
const validationText = validationTextOf(error);
|
|
177
179
|
const errorText = rejectedText ?? (uploadSnapshot.matches("failed") ? uploadErrorTextOf(problem) ?? t("upload.failed") : null) ?? (isUploading ? null : validationText);
|
|
178
180
|
let progressText = null;
|
|
179
181
|
if (isProcessing) {
|
|
@@ -214,9 +216,9 @@ function FileUploadInput(props) {
|
|
|
214
216
|
outline: picker.isDragging ? 2 : 0,
|
|
215
217
|
outlineStyle: "dashed",
|
|
216
218
|
outlineColor: "primary.main",
|
|
217
|
-
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
218
|
-
...sx
|
|
219
|
+
bgcolor: picker.isDragging ? "action.hover" : "transparent"
|
|
219
220
|
},
|
|
221
|
+
sx,
|
|
220
222
|
children: [
|
|
221
223
|
/* @__PURE__ */ jsx("input", { ...picker.inputProps, id: inputId, name, "aria-labelledby": label ? labelId : void 0 }),
|
|
222
224
|
shownFile && // el nombre cortado si es largo y completo al pasar el cursor; en el teléfono baja a su renglón
|
|
@@ -278,6 +280,7 @@ function FileUploadInput(props) {
|
|
|
278
280
|
}
|
|
279
281
|
return /* @__PURE__ */ jsx(FieldFrame, { inputId, labelId, label, children: row });
|
|
280
282
|
}
|
|
283
|
+
const FileUploadInput = withPieceSlots(FileUploadInputContent, THEME_NAME);
|
|
281
284
|
export {
|
|
282
285
|
FILE_UPLOAD_STATUS,
|
|
283
286
|
FileUploadInput as default
|