@stackcraftmx/calcita 0.0.0-stage → 0.23.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/LICENSE +110 -0
- package/README.md +611 -2
- package/cli/askQuestions.js +63 -0
- package/cli/calcita.js +150 -0
- package/cli/makeComponent.js +50 -0
- package/cli/makeForm.js +68 -0
- package/cli/makeModule.js +78 -0
- package/cli/makePage.js +62 -0
- package/cli/names.js +64 -0
- package/cli/newProject.js +103 -0
- package/cli/packageVersions.js +38 -0
- package/cli/projectAnswers.js +145 -0
- package/cli/projectConfig.js +38 -0
- package/cli/projectFiles.js +126 -0
- package/cli/screenLabels.js +41 -0
- package/cli/templateFiles.js +186 -0
- package/cli/templates/component/core/__ComponentName__.jsx.tpl +23 -0
- package/cli/templates/component/module/__ComponentName__.jsx.tpl +21 -0
- package/cli/templates/form/formik/__FormName__.jsx.tpl +50 -0
- package/cli/templates/form/hookForm/__FormName__.jsx.tpl +53 -0
- package/cli/templates/form/shared/__formName__Schema.js.tpl +20 -0
- package/cli/templates/form/shared/__formName__Texts.js.tpl +6 -0
- package/cli/templates/module/__Name__Page.jsx.tpl +75 -0
- package/cli/templates/module/__name__Api.js.tpl +39 -0
- package/cli/templates/module/__name__Texts.js.tpl +27 -0
- package/cli/templates/module/menu.js.tpl +21 -0
- package/cli/templates/module/routes.js.tpl +7 -0
- package/cli/templates/moduleServer/__name__Server.js.tpl +68 -0
- package/cli/templates/page/__PageName__Page.jsx.tpl +22 -0
- package/cli/templates/page/__pageName__PageTexts.js.tpl +23 -0
- package/cli/templates/project/README.md.tpl +55 -0
- package/cli/templates/project/dot-gitignore.tpl +11 -0
- package/cli/templates/project/index.html.tpl +20 -0
- package/cli/templates/project/jsconfig.json.tpl +16 -0
- package/cli/templates/project/pnpm-workspace.yaml.tpl +5 -0
- package/cli/templates/project/public/favicon.svg +1 -0
- package/cli/templates/project/public/icons/apple-touch-icon.png +0 -0
- package/cli/templates/project/public/icons/icon-192.png +0 -0
- package/cli/templates/project/public/icons/icon-512-maskable.png +0 -0
- package/cli/templates/project/public/icons/icon-512.png +0 -0
- package/cli/templates/project/public/manifest.webmanifest.tpl +17 -0
- package/cli/templates/project/src/app/AppProviders.jsx.tpl +102 -0
- package/cli/templates/project/src/app/AppShell.jsx.tpl +87 -0
- package/cli/templates/project/src/app/appRouter.jsx.tpl +49 -0
- package/cli/templates/project/src/app/appSettings.js.tpl +13 -0
- package/cli/templates/project/src/app/appTexts.js.tpl +15 -0
- package/cli/templates/project/src/app/combineTexts.js.tpl +31 -0
- package/cli/templates/project/src/app/i18n.js.tpl +19 -0
- package/cli/templates/project/src/app/shellTexts.js.tpl +16 -0
- package/cli/templates/project/src/core/AppLogo.jsx.tpl +12 -0
- package/cli/templates/project/src/core/accountConnector.js.tpl +36 -0
- package/cli/templates/project/src/core/apiClient.js.tpl +15 -0
- package/cli/templates/project/src/core/appPaths.js.tpl +11 -0
- package/cli/templates/project/src/core/index.js.tpl +8 -0
- package/cli/templates/project/src/fakeServer/networkDelay.js.tpl +40 -0
- package/cli/templates/project/src/fakeServer/serverText.js.tpl +20 -0
- package/cli/templates/project/src/main.jsx.tpl +24 -0
- package/cli/templates/project/src/modules/auth/ForgotPasswordPage.jsx.tpl +14 -0
- package/cli/templates/project/src/modules/auth/ResetPasswordPage.jsx.tpl +15 -0
- package/cli/templates/project/src/modules/auth/SignInPage.jsx.tpl +29 -0
- package/cli/templates/project/src/modules/auth/authTexts.js.tpl +6 -0
- package/cli/templates/project/src/modules/auth/routes.js.tpl +11 -0
- package/cli/templates/project/src/modules/home/HomePage.jsx.tpl +38 -0
- package/cli/templates/project/src/modules/home/homeTexts.js.tpl +29 -0
- package/cli/templates/project/src/modules/home/menu.js.tpl +17 -0
- package/cli/templates/project/src/modules/home/routes.js.tpl +4 -0
- package/cli/templates/project/src/modules/profile/ProfilePage.jsx.tpl +13 -0
- package/cli/templates/project/src/modules/profile/routes.js.tpl +6 -0
- package/cli/templates/project/vite.config.js.tpl +27 -0
- package/dist/DataTable/AuditTrailDialog.js +40 -0
- package/dist/DataTable/AuditTrailDialog.js.map +1 -0
- package/dist/DataTable/AuditTrailTable.js +87 -0
- package/dist/DataTable/AuditTrailTable.js.map +1 -0
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +71 -0
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -0
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +50 -0
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -0
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +54 -0
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -0
- package/dist/DataTable/ColumnFilters/TextColumnFilter.js +24 -0
- package/dist/DataTable/ColumnFilters/TextColumnFilter.js.map +1 -0
- package/dist/DataTable/EmptyState.js +22 -0
- package/dist/DataTable/EmptyState.js.map +1 -0
- package/dist/DataTable/FilterChips.js +39 -0
- package/dist/DataTable/FilterChips.js.map +1 -0
- package/dist/DataTable/GoToPage.js +62 -0
- package/dist/DataTable/GoToPage.js.map +1 -0
- package/dist/DataTable/ServerTable.js +407 -0
- package/dist/DataTable/ServerTable.js.map +1 -0
- package/dist/DataTable/buildChipConfig.js +27 -0
- package/dist/DataTable/buildChipConfig.js.map +1 -0
- package/dist/DataTable/columns.js +122 -0
- package/dist/DataTable/columns.js.map +1 -0
- package/dist/DataTable/filterContext.js +15 -0
- package/dist/DataTable/filterContext.js.map +1 -0
- package/dist/DataTable/serverTableParams.js +19 -0
- package/dist/DataTable/serverTableParams.js.map +1 -0
- package/dist/DataTable/tableMachine.js +87 -0
- package/dist/DataTable/tableMachine.js.map +1 -0
- package/dist/DataTable/useDebouncedFilter.js +29 -0
- package/dist/DataTable/useDebouncedFilter.js.map +1 -0
- package/dist/DataTable/useTableState.js +132 -0
- package/dist/DataTable/useTableState.js.map +1 -0
- package/dist/access/Can.js +9 -0
- package/dist/access/Can.js.map +1 -0
- package/dist/access/PermissionMatrix.js +83 -0
- package/dist/access/PermissionMatrix.js.map +1 -0
- package/dist/access/RequireAccess.js +14 -0
- package/dist/access/RequireAccess.js.map +1 -0
- package/dist/access/accessRules.js +49 -0
- package/dist/access/accessRules.js.map +1 -0
- package/dist/access/permissionGroups.js +43 -0
- package/dist/access/permissionGroups.js.map +1 -0
- package/dist/access/useCan.js +12 -0
- package/dist/access/useCan.js.map +1 -0
- package/dist/account/AccountProvider.js +58 -0
- package/dist/account/AccountProvider.js.map +1 -0
- package/dist/account/AccountSecuritySection.js +81 -0
- package/dist/account/AccountSecuritySection.js.map +1 -0
- package/dist/account/DemoTotpCodeButton.js +26 -0
- package/dist/account/DemoTotpCodeButton.js.map +1 -0
- package/dist/account/ForgotPasswordScreen.js +26 -0
- package/dist/account/ForgotPasswordScreen.js.map +1 -0
- package/dist/account/ProfileScreen.js +108 -0
- package/dist/account/ProfileScreen.js.map +1 -0
- package/dist/account/ResetPasswordScreen.js +57 -0
- package/dist/account/ResetPasswordScreen.js.map +1 -0
- package/dist/account/SignInScreen.js +101 -0
- package/dist/account/SignInScreen.js.map +1 -0
- package/dist/account/accountConnector.js +31 -0
- package/dist/account/accountConnector.js.map +1 -0
- package/dist/account/demo/createDemoAccountConnector.js +368 -0
- package/dist/account/demo/createDemoAccountConnector.js.map +1 -0
- package/dist/account/demo/totp.js +61 -0
- package/dist/account/demo/totp.js.map +1 -0
- package/dist/account/restAccountConnector.js +117 -0
- package/dist/account/restAccountConnector.js.map +1 -0
- package/dist/account-demo.js +12 -0
- package/dist/account-demo.js.map +1 -0
- package/dist/account.js +25 -0
- package/dist/account.js.map +1 -0
- package/dist/adapters/i18next.js +7 -0
- package/dist/adapters/i18next.js.map +1 -0
- package/dist/adapters/nextNavigation.js +36 -0
- package/dist/adapters/nextNavigation.js.map +1 -0
- package/dist/adapters/reactRouter.js +7 -0
- package/dist/adapters/reactRouter.js.map +1 -0
- package/dist/adapters/reactRouterRoutes.js +48 -0
- package/dist/adapters/reactRouterRoutes.js.map +1 -0
- package/dist/api/ApiError.js +58 -0
- package/dist/api/ApiError.js.map +1 -0
- package/dist/api/createApiClient.js +226 -0
- package/dist/api/createApiClient.js.map +1 -0
- package/dist/api/staleChunks.js +47 -0
- package/dist/api/staleChunks.js.map +1 -0
- package/dist/appShell/AccountMenu.js +86 -0
- package/dist/appShell/AccountMenu.js.map +1 -0
- package/dist/appShell/AppLayout.js +493 -0
- package/dist/appShell/AppLayout.js.map +1 -0
- package/dist/appShell/AppearanceSettings.js +93 -0
- package/dist/appShell/AppearanceSettings.js.map +1 -0
- package/dist/appShell/AuthLayout.js +44 -0
- package/dist/appShell/AuthLayout.js.map +1 -0
- package/dist/appShell/BarActionButton.js +31 -0
- package/dist/appShell/BarActionButton.js.map +1 -0
- package/dist/appShell/BarActions.js +94 -0
- package/dist/appShell/BarActions.js.map +1 -0
- package/dist/appShell/BarCapsule.js +35 -0
- package/dist/appShell/BarCapsule.js.map +1 -0
- package/dist/appShell/HeaderTools.js +53 -0
- package/dist/appShell/HeaderTools.js.map +1 -0
- package/dist/appShell/MobileTabBar.js +346 -0
- package/dist/appShell/MobileTabBar.js.map +1 -0
- package/dist/appShell/SettingsMenu.js +41 -0
- package/dist/appShell/SettingsMenu.js.map +1 -0
- package/dist/appShell/SideMenu.js +217 -0
- package/dist/appShell/SideMenu.js.map +1 -0
- package/dist/appShell/SideMenuEntry.js +140 -0
- package/dist/appShell/SideMenuEntry.js.map +1 -0
- package/dist/appShell/SubmenuActionsMenu.js +71 -0
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -0
- package/dist/appShell/TabBarEditor.js +55 -0
- package/dist/appShell/TabBarEditor.js.map +1 -0
- package/dist/appShell/TopMenu.js +80 -0
- package/dist/appShell/TopMenu.js.map +1 -0
- package/dist/appShell/appMenuContext.js +11 -0
- package/dist/appShell/appMenuContext.js.map +1 -0
- package/dist/appShell/barActionsSlot.js +26 -0
- package/dist/appShell/barActionsSlot.js.map +1 -0
- package/dist/appShell/barMenuPlacement.js +33 -0
- package/dist/appShell/barMenuPlacement.js.map +1 -0
- package/dist/appShell/menuItemText.js +33 -0
- package/dist/appShell/menuItemText.js.map +1 -0
- package/dist/appShell/pageTitle.js +45 -0
- package/dist/appShell/pageTitle.js.map +1 -0
- package/dist/appShell/shellSizes.js +38 -0
- package/dist/appShell/shellSizes.js.map +1 -0
- package/dist/appShell/tabBarRule.js +12 -0
- package/dist/appShell/tabBarRule.js.map +1 -0
- package/dist/appShell/tabBarScroll.js +23 -0
- package/dist/appShell/tabBarScroll.js.map +1 -0
- package/dist/appShell/useDrawerCollapsed.js +29 -0
- package/dist/appShell/useDrawerCollapsed.js.map +1 -0
- package/dist/appShell/useFittingActionCount.js +45 -0
- package/dist/appShell/useFittingActionCount.js.map +1 -0
- package/dist/appShell/usePinnedMobileTabs.js +32 -0
- package/dist/appShell/usePinnedMobileTabs.js.map +1 -0
- package/dist/appShell/useTabBarMinimized.js +33 -0
- package/dist/appShell/useTabBarMinimized.js.map +1 -0
- package/dist/auth/PasswordChecklist.js +44 -0
- package/dist/auth/PasswordChecklist.js.map +1 -0
- package/dist/auth/passwordRules.js +43 -0
- package/dist/auth/passwordRules.js.map +1 -0
- package/dist/calendar/EventCalendar.js +138 -0
- package/dist/calendar/EventCalendar.js.map +1 -0
- package/dist/calendar.js +6 -0
- package/dist/calendar.js.map +1 -0
- package/dist/charts.js +7 -0
- package/dist/charts.js.map +1 -0
- package/dist/date-pickers.js +8 -0
- package/dist/date-pickers.js.map +1 -0
- package/dist/datePickers/DateInput.js +75 -0
- package/dist/datePickers/DateInput.js.map +1 -0
- package/dist/datePickers/FormDateField.js +12 -0
- package/dist/datePickers/FormDateField.js.map +1 -0
- package/dist/dates.js +25 -0
- package/dist/dates.js.map +1 -0
- package/dist/dictionaries/dictionaries.js +48 -0
- package/dist/dictionaries/dictionaries.js.map +1 -0
- package/dist/downloads/DownloadManager.js +201 -0
- package/dist/downloads/DownloadManager.js.map +1 -0
- package/dist/downloads/downloadQueue.js +54 -0
- package/dist/downloads/downloadQueue.js.map +1 -0
- package/dist/downloads/downloadsMachine.js +77 -0
- package/dist/downloads/downloadsMachine.js.map +1 -0
- package/dist/excel/reportWorkbook.js +299 -0
- package/dist/excel/reportWorkbook.js.map +1 -0
- package/dist/excel.js +13 -0
- package/dist/excel.js.map +1 -0
- package/dist/feedback/ConfirmDialog.js +72 -0
- package/dist/feedback/ConfirmDialog.js.map +1 -0
- package/dist/feedback/ModalWaiting.js +23 -0
- package/dist/feedback/ModalWaiting.js.map +1 -0
- package/dist/feedback/NewBuildNotice.js +79 -0
- package/dist/feedback/NewBuildNotice.js.map +1 -0
- package/dist/feedback/StaleBuildBoundary.js +36 -0
- package/dist/feedback/StaleBuildBoundary.js.map +1 -0
- package/dist/feedback/Toaster.js +20 -0
- package/dist/feedback/Toaster.js.map +1 -0
- package/dist/feedback/confirmActionMachine.js +67 -0
- package/dist/feedback/confirmActionMachine.js.map +1 -0
- package/dist/feedback/toast.js +88 -0
- package/dist/feedback/toast.js.map +1 -0
- package/dist/feedback/useConfirmAction.js +40 -0
- package/dist/feedback/useConfirmAction.js.map +1 -0
- package/dist/filepond/FileDropzone.js +254 -0
- package/dist/filepond/FileDropzone.js.map +1 -0
- package/dist/filepond/pondServer.js +48 -0
- package/dist/filepond/pondServer.js.map +1 -0
- package/dist/filepond.js +10 -0
- package/dist/filepond.js.map +1 -0
- package/dist/files.js +43 -0
- package/dist/files.js.map +1 -0
- package/dist/formatting.js +177 -0
- package/dist/formatting.js.map +1 -0
- package/dist/forms/FormCatalogField.js +12 -0
- package/dist/forms/FormCatalogField.js.map +1 -0
- package/dist/forms/FormColorField.js +12 -0
- package/dist/forms/FormColorField.js.map +1 -0
- package/dist/forms/FormNumberField.js +12 -0
- package/dist/forms/FormNumberField.js.map +1 -0
- package/dist/forms/FormProblemAlert.js +15 -0
- package/dist/forms/FormProblemAlert.js.map +1 -0
- package/dist/forms/FormSelectField.js +12 -0
- package/dist/forms/FormSelectField.js.map +1 -0
- package/dist/forms/FormServerProblem.js +23 -0
- package/dist/forms/FormServerProblem.js.map +1 -0
- package/dist/forms/FormTextField.js +12 -0
- package/dist/forms/FormTextField.js.map +1 -0
- package/dist/forms/FormWizard.js +90 -0
- package/dist/forms/FormWizard.js.map +1 -0
- package/dist/forms/SubmitButton.js +14 -0
- package/dist/forms/SubmitButton.js.map +1 -0
- package/dist/forms/applyServerProblem.js +15 -0
- package/dist/forms/applyServerProblem.js.map +1 -0
- package/dist/forms/auth/ChangePasswordForm.js +46 -0
- package/dist/forms/auth/ChangePasswordForm.js.map +1 -0
- package/dist/forms/auth/ForgotPasswordForm.js +50 -0
- package/dist/forms/auth/ForgotPasswordForm.js.map +1 -0
- package/dist/forms/auth/SignInForm.js +48 -0
- package/dist/forms/auth/SignInForm.js.map +1 -0
- package/dist/forms/security/SecondStepForm.js +122 -0
- package/dist/forms/security/SecondStepForm.js.map +1 -0
- package/dist/forms/security/TwoStepVerificationCard.js +336 -0
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -0
- package/dist/forms/security/securityRules.js +28 -0
- package/dist/forms/security/securityRules.js.map +1 -0
- package/dist/forms/serverProblemFields.js +25 -0
- package/dist/forms/serverProblemFields.js.map +1 -0
- package/dist/forms/useFormikField.js +16 -0
- package/dist/forms/useFormikField.js.map +1 -0
- package/dist/forms/validation.js +55 -0
- package/dist/forms/validation.js.map +1 -0
- package/dist/forms/validationMessages.js +16 -0
- package/dist/forms/validationMessages.js.map +1 -0
- package/dist/forms.js +57 -0
- package/dist/forms.js.map +1 -0
- package/dist/hookForm/FormCatalogField.js +12 -0
- package/dist/hookForm/FormCatalogField.js.map +1 -0
- package/dist/hookForm/FormColorField.js +12 -0
- package/dist/hookForm/FormColorField.js.map +1 -0
- package/dist/hookForm/FormNumberField.js +12 -0
- package/dist/hookForm/FormNumberField.js.map +1 -0
- package/dist/hookForm/FormProblemAlert.js +17 -0
- package/dist/hookForm/FormProblemAlert.js.map +1 -0
- package/dist/hookForm/FormSelectField.js +12 -0
- package/dist/hookForm/FormSelectField.js.map +1 -0
- package/dist/hookForm/FormTextField.js +12 -0
- package/dist/hookForm/FormTextField.js.map +1 -0
- package/dist/hookForm/SubmitButton.js +15 -0
- package/dist/hookForm/SubmitButton.js.map +1 -0
- package/dist/hookForm/applyServerProblem.js +21 -0
- package/dist/hookForm/applyServerProblem.js.map +1 -0
- package/dist/hookForm/useHookFormField.js +19 -0
- package/dist/hookForm/useHookFormField.js.map +1 -0
- package/dist/i18next.js +5 -0
- package/dist/i18next.js.map +1 -0
- package/dist/identifiers.js +8 -0
- package/dist/identifiers.js.map +1 -0
- package/dist/inbox/InboxLayout.js +68 -0
- package/dist/inbox/InboxLayout.js.map +1 -0
- package/dist/inbox/MessageList.js +64 -0
- package/dist/inbox/MessageList.js.map +1 -0
- package/dist/inbox/MessageView.js +53 -0
- package/dist/inbox/MessageView.js.map +1 -0
- package/dist/inbox/inboxDates.js +8 -0
- package/dist/inbox/inboxDates.js.map +1 -0
- package/dist/index.js +383 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs/CatalogInput.js +62 -0
- package/dist/inputs/CatalogInput.js.map +1 -0
- package/dist/inputs/ColorInput.js +108 -0
- package/dist/inputs/ColorInput.js.map +1 -0
- package/dist/inputs/FieldFrame.js +20 -0
- package/dist/inputs/FieldFrame.js.map +1 -0
- package/dist/inputs/FormSection.js +105 -0
- package/dist/inputs/FormSection.js.map +1 -0
- package/dist/inputs/NumberInput.js +69 -0
- package/dist/inputs/NumberInput.js.map +1 -0
- package/dist/inputs/SelectInput.js +66 -0
- package/dist/inputs/SelectInput.js.map +1 -0
- package/dist/inputs/TextInput.js +97 -0
- package/dist/inputs/TextInput.js.map +1 -0
- package/dist/inputs/colorContrast.js +46 -0
- package/dist/inputs/colorContrast.js.map +1 -0
- package/dist/inputs/formLayoutContext.js +9 -0
- package/dist/inputs/formLayoutContext.js.map +1 -0
- package/dist/inputs/numberText.js +48 -0
- package/dist/inputs/numberText.js.map +1 -0
- package/dist/kanban/KanbanBoard.js +113 -0
- package/dist/kanban/KanbanBoard.js.map +1 -0
- package/dist/kanban/kanbanMoves.js +29 -0
- package/dist/kanban/kanbanMoves.js.map +1 -0
- package/dist/kanban.js +9 -0
- package/dist/kanban.js.map +1 -0
- package/dist/laravel/csrfRetry.js +18 -0
- package/dist/laravel/csrfRetry.js.map +1 -0
- package/dist/laravel.js +6 -0
- package/dist/laravel.js.map +1 -0
- package/dist/lastListUrl.js +35 -0
- package/dist/lastListUrl.js.map +1 -0
- package/dist/locales/en.js +714 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es.js +714 -0
- package/dist/locales/es.js.map +1 -0
- package/dist/maps/MapView.js +96 -0
- package/dist/maps/MapView.js.map +1 -0
- package/dist/maps/mapMarkers.js +19 -0
- package/dist/maps/mapMarkers.js.map +1 -0
- package/dist/maps.js +10 -0
- package/dist/maps.js.map +1 -0
- package/dist/money/money.js +380 -0
- package/dist/money/money.js.map +1 -0
- package/dist/money.js +6 -0
- package/dist/money.js.map +1 -0
- package/dist/navigation/appMenu.js +93 -0
- package/dist/navigation/appMenu.js.map +1 -0
- package/dist/navigation/useMenuBadges.js +45 -0
- package/dist/navigation/useMenuBadges.js.map +1 -0
- package/dist/navigation.js +30 -0
- package/dist/navigation.js.map +1 -0
- package/dist/navigationTrail.js +82 -0
- package/dist/navigationTrail.js.map +1 -0
- package/dist/next.js +5 -0
- package/dist/next.js.map +1 -0
- package/dist/observability/buildWatch.js +19 -0
- package/dist/observability/buildWatch.js.map +1 -0
- package/dist/observability/logger.js +43 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/publishedBuild.js +39 -0
- package/dist/observability/publishedBuild.js.map +1 -0
- package/dist/observability/usePublishedBuildWatch.js +76 -0
- package/dist/observability/usePublishedBuildWatch.js.map +1 -0
- package/dist/passkeys.js +7 -0
- package/dist/passkeys.js.map +1 -0
- package/dist/periodRanges.js +33 -0
- package/dist/periodRanges.js.map +1 -0
- package/dist/problems.js +51 -0
- package/dist/problems.js.map +1 -0
- package/dist/pwa/InstallAppSetting.js +33 -0
- package/dist/pwa/InstallAppSetting.js.map +1 -0
- package/dist/pwa/InstallInstructionsDialog.js +35 -0
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -0
- package/dist/pwa/InstallPromptBanner.js +64 -0
- package/dist/pwa/InstallPromptBanner.js.map +1 -0
- package/dist/pwa/installInstructions.js +60 -0
- package/dist/pwa/installInstructions.js.map +1 -0
- package/dist/pwa/useInstallPrompt.js +70 -0
- package/dist/pwa/useInstallPrompt.js.map +1 -0
- package/dist/react-hook-form.js +30 -0
- package/dist/react-hook-form.js.map +1 -0
- package/dist/react-router.js +10 -0
- package/dist/react-router.js.map +1 -0
- package/dist/security/ActivityLog.js +79 -0
- package/dist/security/ActivityLog.js.map +1 -0
- package/dist/security/RecoveryCodesDialog.js +84 -0
- package/dist/security/RecoveryCodesDialog.js.map +1 -0
- package/dist/security/SessionsCard.js +81 -0
- package/dist/security/SessionsCard.js.map +1 -0
- package/dist/security/passkeys.js +39 -0
- package/dist/security/passkeys.js.map +1 -0
- package/dist/table.js +35 -0
- package/dist/table.js.map +1 -0
- package/dist/theme/androidDevice.js +16 -0
- package/dist/theme/androidDevice.js.map +1 -0
- package/dist/theme/appleTracking.js +34 -0
- package/dist/theme/appleTracking.js.map +1 -0
- package/dist/theme/components/MuiAppBar.js +27 -0
- package/dist/theme/components/MuiAppBar.js.map +1 -0
- package/dist/theme/components/MuiBackdrop.js +13 -0
- package/dist/theme/components/MuiBackdrop.js.map +1 -0
- package/dist/theme/components/MuiButton.js +25 -0
- package/dist/theme/components/MuiButton.js.map +1 -0
- package/dist/theme/components/MuiButtonBase.js +16 -0
- package/dist/theme/components/MuiButtonBase.js.map +1 -0
- package/dist/theme/components/MuiChip.js +30 -0
- package/dist/theme/components/MuiChip.js.map +1 -0
- package/dist/theme/components/MuiDataGrid.js +30 -0
- package/dist/theme/components/MuiDataGrid.js.map +1 -0
- package/dist/theme/components/MuiDrawer.js +18 -0
- package/dist/theme/components/MuiDrawer.js.map +1 -0
- package/dist/theme/components/MuiMenuItem.js +17 -0
- package/dist/theme/components/MuiMenuItem.js.map +1 -0
- package/dist/theme/components/MuiTypography.js +22 -0
- package/dist/theme/components/MuiTypography.js.map +1 -0
- package/dist/theme/components/glassComponents.js +885 -0
- package/dist/theme/components/glassComponents.js.map +1 -0
- package/dist/theme/components/minimalComponents.js +146 -0
- package/dist/theme/components/minimalComponents.js.map +1 -0
- package/dist/theme/components/paletteOf.js +7 -0
- package/dist/theme/components/paletteOf.js.map +1 -0
- package/dist/theme/createToolkitTheme.js +100 -0
- package/dist/theme/createToolkitTheme.js.map +1 -0
- package/dist/theme/glassSurface.js +328 -0
- package/dist/theme/glassSurface.js.map +1 -0
- package/dist/theme/glassToasterStyles.js +50 -0
- package/dist/theme/glassToasterStyles.js.map +1 -0
- package/dist/theme/mergeComponents.js +42 -0
- package/dist/theme/mergeComponents.js.map +1 -0
- package/dist/theme/stackcraftPalette.js +59 -0
- package/dist/theme/stackcraftPalette.js.map +1 -0
- package/dist/theme/toolkitComponents.js +26 -0
- package/dist/theme/toolkitComponents.js.map +1 -0
- package/dist/timeline.js +8 -0
- package/dist/timeline.js.map +1 -0
- package/dist/toolkit/ToolkitProvider.js +49 -0
- package/dist/toolkit/ToolkitProvider.js.map +1 -0
- package/dist/toolkit/browserNavigation.js +34 -0
- package/dist/toolkit/browserNavigation.js.map +1 -0
- package/dist/toolkit/toolkitContext.js +19 -0
- package/dist/toolkit/toolkitContext.js.map +1 -0
- package/dist/toolkit/toolkitSettings.js +87 -0
- package/dist/toolkit/toolkitSettings.js.map +1 -0
- package/dist/toolkit/useToolkitTranslation.js +18 -0
- package/dist/toolkit/useToolkitTranslation.js.map +1 -0
- package/dist/types/DataTable/AuditTrailDialog.d.ts +32 -0
- package/dist/types/DataTable/AuditTrailTable.d.ts +28 -0
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +8 -0
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +16 -0
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +26 -0
- package/dist/types/DataTable/ColumnFilters/TextColumnFilter.d.ts +8 -0
- package/dist/types/DataTable/ColumnFilters/index.d.ts +4 -0
- package/dist/types/DataTable/EmptyState.d.ts +8 -0
- package/dist/types/DataTable/FilterChips.d.ts +15 -0
- package/dist/types/DataTable/GoToPage.d.ts +8 -0
- package/dist/types/DataTable/ServerTable.d.ts +110 -0
- package/dist/types/DataTable/buildChipConfig.d.ts +22 -0
- package/dist/types/DataTable/columns.d.ts +93 -0
- package/dist/types/DataTable/filterContext.d.ts +13 -0
- package/dist/types/DataTable/index.d.ts +13 -0
- package/dist/types/DataTable/serverTableParams.d.ts +16 -0
- package/dist/types/DataTable/tableMachine.d.ts +41 -0
- package/dist/types/DataTable/useDebouncedFilter.d.ts +8 -0
- package/dist/types/DataTable/useTableState.d.ts +93 -0
- package/dist/types/access/Can.d.ts +11 -0
- package/dist/types/access/PermissionMatrix.d.ts +21 -0
- package/dist/types/access/RequireAccess.d.ts +11 -0
- package/dist/types/access/accessRules.d.ts +24 -0
- package/dist/types/access/permissionGroups.d.ts +56 -0
- package/dist/types/access/useCan.d.ts +11 -0
- package/dist/types/account/AccountProvider.d.ts +27 -0
- package/dist/types/account/AccountSecuritySection.d.ts +7 -0
- package/dist/types/account/DemoTotpCodeButton.d.ts +6 -0
- package/dist/types/account/ForgotPasswordScreen.d.ts +15 -0
- package/dist/types/account/ProfileScreen.d.ts +13 -0
- package/dist/types/account/ResetPasswordScreen.d.ts +19 -0
- package/dist/types/account/SignInScreen.d.ts +25 -0
- package/dist/types/account/accountConnector.d.ts +263 -0
- package/dist/types/account/demo/createDemoAccountConnector.d.ts +30 -0
- package/dist/types/account/demo/totp.d.ts +45 -0
- package/dist/types/account/restAccountConnector.d.ts +49 -0
- package/dist/types/account-demo.d.ts +2 -0
- package/dist/types/account.d.ts +16 -0
- package/dist/types/adapters/i18next.d.ts +11 -0
- package/dist/types/adapters/nextNavigation.d.ts +33 -0
- package/dist/types/adapters/reactRouter.d.ts +6 -0
- package/dist/types/adapters/reactRouterRoutes.d.ts +30 -0
- package/dist/types/api/ApiError.d.ts +42 -0
- package/dist/types/api/createApiClient.d.ts +146 -0
- package/dist/types/api/staleChunks.d.ts +15 -0
- package/dist/types/appShell/AccountMenu.d.ts +28 -0
- package/dist/types/appShell/AppLayout.d.ts +66 -0
- package/dist/types/appShell/AppearanceSettings.d.ts +34 -0
- package/dist/types/appShell/AuthLayout.d.ts +21 -0
- package/dist/types/appShell/BarActionButton.d.ts +15 -0
- package/dist/types/appShell/BarActions.d.ts +18 -0
- package/dist/types/appShell/BarCapsule.d.ts +12 -0
- package/dist/types/appShell/HeaderTools.d.ts +27 -0
- package/dist/types/appShell/MobileTabBar.d.ts +17 -0
- package/dist/types/appShell/SettingsMenu.d.ts +13 -0
- package/dist/types/appShell/SideMenu.d.ts +31 -0
- package/dist/types/appShell/SideMenuEntry.d.ts +17 -0
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +24 -0
- package/dist/types/appShell/TabBarEditor.d.ts +13 -0
- package/dist/types/appShell/TopMenu.d.ts +7 -0
- package/dist/types/appShell/appMenuContext.d.ts +9 -0
- package/dist/types/appShell/barActionsSlot.d.ts +35 -0
- package/dist/types/appShell/barMenuPlacement.d.ts +9 -0
- package/dist/types/appShell/menuItemText.d.ts +55 -0
- package/dist/types/appShell/pageTitle.d.ts +26 -0
- package/dist/types/appShell/shellSizes.d.ts +45 -0
- package/dist/types/appShell/tabBarRule.d.ts +5 -0
- package/dist/types/appShell/tabBarScroll.d.ts +26 -0
- package/dist/types/appShell/useDrawerCollapsed.d.ts +8 -0
- package/dist/types/appShell/useFittingActionCount.d.ts +7 -0
- package/dist/types/appShell/usePinnedMobileTabs.d.ts +7 -0
- package/dist/types/appShell/useTabBarMinimized.d.ts +8 -0
- package/dist/types/auth/PasswordChecklist.d.ts +11 -0
- package/dist/types/auth/passwordRules.d.ts +34 -0
- package/dist/types/calendar/EventCalendar.d.ts +46 -0
- package/dist/types/calendar.d.ts +1 -0
- package/dist/types/charts.d.ts +2 -0
- package/dist/types/date-pickers.d.ts +2 -0
- package/dist/types/datePickers/DateInput.d.ts +39 -0
- package/dist/types/datePickers/FormDateField.d.ts +7 -0
- package/dist/types/dates.d.ts +18 -0
- package/dist/types/dictionaries/dictionaries.d.ts +54 -0
- package/dist/types/downloads/DownloadManager.d.ts +1 -0
- package/dist/types/downloads/downloadQueue.d.ts +45 -0
- package/dist/types/downloads/downloadsMachine.d.ts +10 -0
- package/dist/types/downloads/index.d.ts +3 -0
- package/dist/types/excel/reportWorkbook.d.ts +157 -0
- package/dist/types/excel.d.ts +1 -0
- package/dist/types/feedback/ConfirmDialog.d.ts +29 -0
- package/dist/types/feedback/ModalWaiting.d.ts +8 -0
- package/dist/types/feedback/NewBuildNotice.d.ts +18 -0
- package/dist/types/feedback/StaleBuildBoundary.d.ts +21 -0
- package/dist/types/feedback/Toaster.d.ts +1 -0
- package/dist/types/feedback/confirmActionMachine.d.ts +19 -0
- package/dist/types/feedback/index.d.ts +8 -0
- package/dist/types/feedback/toast.d.ts +100 -0
- package/dist/types/feedback/useConfirmAction.d.ts +33 -0
- package/dist/types/filepond/FileDropzone.d.ts +59 -0
- package/dist/types/filepond/pondServer.d.ts +17 -0
- package/dist/types/filepond.d.ts +3 -0
- package/dist/types/files.d.ts +29 -0
- package/dist/types/formatting.d.ts +106 -0
- package/dist/types/forms/FormCatalogField.d.ts +6 -0
- package/dist/types/forms/FormColorField.d.ts +6 -0
- package/dist/types/forms/FormNumberField.d.ts +6 -0
- package/dist/types/forms/FormProblemAlert.d.ts +6 -0
- package/dist/types/forms/FormSelectField.d.ts +6 -0
- package/dist/types/forms/FormServerProblem.d.ts +8 -0
- package/dist/types/forms/FormTextField.d.ts +6 -0
- package/dist/types/forms/FormWizard.d.ts +20 -0
- package/dist/types/forms/SubmitButton.d.ts +9 -0
- package/dist/types/forms/applyServerProblem.d.ts +9 -0
- package/dist/types/forms/auth/ChangePasswordForm.d.ts +15 -0
- package/dist/types/forms/auth/ForgotPasswordForm.d.ts +12 -0
- package/dist/types/forms/auth/SignInForm.d.ts +18 -0
- package/dist/types/forms/index.d.ts +19 -0
- package/dist/types/forms/security/SecondStepForm.d.ts +28 -0
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +81 -0
- package/dist/types/forms/security/securityRules.d.ts +24 -0
- package/dist/types/forms/serverProblemFields.d.ts +19 -0
- package/dist/types/forms/useFormikField.d.ts +11 -0
- package/dist/types/forms/validation.d.ts +251 -0
- package/dist/types/forms/validationMessages.d.ts +12 -0
- package/dist/types/forms.d.ts +2 -0
- package/dist/types/hookForm/FormCatalogField.d.ts +8 -0
- package/dist/types/hookForm/FormColorField.d.ts +8 -0
- package/dist/types/hookForm/FormNumberField.d.ts +8 -0
- package/dist/types/hookForm/FormProblemAlert.d.ts +7 -0
- package/dist/types/hookForm/FormSelectField.d.ts +8 -0
- package/dist/types/hookForm/FormTextField.d.ts +8 -0
- package/dist/types/hookForm/SubmitButton.d.ts +21 -0
- package/dist/types/hookForm/applyServerProblem.d.ts +10 -0
- package/dist/types/hookForm/useHookFormField.d.ts +13 -0
- package/dist/types/i18next.d.ts +1 -0
- package/dist/types/identifiers.d.ts +2 -0
- package/dist/types/inbox/InboxLayout.d.ts +26 -0
- package/dist/types/inbox/MessageList.d.ts +27 -0
- package/dist/types/inbox/MessageView.d.ts +23 -0
- package/dist/types/inbox/inboxDates.d.ts +6 -0
- package/dist/types/index.d.ts +84 -0
- package/dist/types/inputs/CatalogInput.d.ts +41 -0
- package/dist/types/inputs/ColorInput.d.ts +28 -0
- package/dist/types/inputs/FieldFrame.d.ts +14 -0
- package/dist/types/inputs/FormSection.d.ts +29 -0
- package/dist/types/inputs/NumberInput.d.ts +36 -0
- package/dist/types/inputs/SelectInput.d.ts +34 -0
- package/dist/types/inputs/TextInput.d.ts +60 -0
- package/dist/types/inputs/colorContrast.d.ts +18 -0
- package/dist/types/inputs/formLayoutContext.d.ts +3 -0
- package/dist/types/inputs/numberText.d.ts +25 -0
- package/dist/types/kanban/KanbanBoard.d.ts +33 -0
- package/dist/types/kanban/kanbanMoves.d.ts +38 -0
- package/dist/types/kanban.d.ts +2 -0
- package/dist/types/laravel/csrfRetry.d.ts +21 -0
- package/dist/types/laravel.d.ts +1 -0
- package/dist/types/lastListUrl.d.ts +20 -0
- package/dist/types/locales/en.d.ts +711 -0
- package/dist/types/locales/es.d.ts +711 -0
- package/dist/types/maps/MapView.d.ts +32 -0
- package/dist/types/maps/mapMarkers.d.ts +23 -0
- package/dist/types/maps.d.ts +2 -0
- package/dist/types/money/money.d.ts +203 -0
- package/dist/types/money.d.ts +1 -0
- package/dist/types/navigation/appMenu.d.ts +92 -0
- package/dist/types/navigation/useMenuBadges.d.ts +16 -0
- package/dist/types/navigation.d.ts +22 -0
- package/dist/types/navigationTrail.d.ts +49 -0
- package/dist/types/next.d.ts +1 -0
- package/dist/types/observability/buildWatch.d.ts +28 -0
- package/dist/types/observability/logger.d.ts +19 -0
- package/dist/types/observability/publishedBuild.d.ts +35 -0
- package/dist/types/observability/usePublishedBuildWatch.d.ts +16 -0
- package/dist/types/passkeys.d.ts +1 -0
- package/dist/types/periodRanges.d.ts +32 -0
- package/dist/types/problems.d.ts +31 -0
- package/dist/types/pwa/InstallAppSetting.d.ts +1 -0
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +18 -0
- package/dist/types/pwa/InstallPromptBanner.d.ts +7 -0
- package/dist/types/pwa/installInstructions.d.ts +52 -0
- package/dist/types/pwa/useInstallPrompt.d.ts +12 -0
- package/dist/types/react-hook-form.d.ts +11 -0
- package/dist/types/react-router.d.ts +2 -0
- package/dist/types/security/ActivityLog.d.ts +31 -0
- package/dist/types/security/RecoveryCodesDialog.d.ts +11 -0
- package/dist/types/security/SessionsCard.d.ts +22 -0
- package/dist/types/security/passkeys.d.ts +18 -0
- package/dist/types/table.d.ts +1 -0
- package/dist/types/theme/androidDevice.d.ts +11 -0
- package/dist/types/theme/appleTracking.d.ts +17 -0
- package/dist/types/theme/components/MuiAppBar.d.ts +13 -0
- package/dist/types/theme/components/MuiBackdrop.d.ts +11 -0
- package/dist/types/theme/components/MuiButton.d.ts +27 -0
- package/dist/types/theme/components/MuiButtonBase.d.ts +14 -0
- package/dist/types/theme/components/MuiChip.d.ts +29 -0
- package/dist/types/theme/components/MuiDataGrid.d.ts +45 -0
- package/dist/types/theme/components/MuiDrawer.d.ts +13 -0
- package/dist/types/theme/components/MuiMenuItem.d.ts +17 -0
- package/dist/types/theme/components/MuiTypography.d.ts +20 -0
- package/dist/types/theme/components/glassComponents.d.ts +8 -0
- package/dist/types/theme/components/minimalComponents.d.ts +22 -0
- package/dist/types/theme/components/paletteOf.d.ts +5 -0
- package/dist/types/theme/createToolkitTheme.d.ts +48 -0
- package/dist/types/theme/glassSurface.d.ts +247 -0
- package/dist/types/theme/glassToasterStyles.d.ts +7 -0
- package/dist/types/theme/mergeComponents.d.ts +15 -0
- package/dist/types/theme/stackcraftPalette.d.ts +142 -0
- package/dist/types/theme/toolkitComponents.d.ts +4 -0
- package/dist/types/timeline.d.ts +2 -0
- package/dist/types/toolkit/ToolkitProvider.d.ts +31 -0
- package/dist/types/toolkit/browserNavigation.d.ts +32 -0
- package/dist/types/toolkit/toolkitContext.d.ts +28 -0
- package/dist/types/toolkit/toolkitSettings.d.ts +68 -0
- package/dist/types/toolkit/useToolkitTranslation.d.ts +10 -0
- package/dist/types/ui/actions/CopyButton.d.ts +13 -0
- package/dist/types/ui/actions/DownloadMenu.d.ts +50 -0
- package/dist/types/ui/actions/SecretField.d.ts +13 -0
- package/dist/types/ui/filters/DateRangeFilter.d.ts +16 -0
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +28 -0
- package/dist/types/ui/filters/QuickFilters.d.ts +21 -0
- package/dist/types/ui/filters/SegmentedControl.d.ts +44 -0
- package/dist/types/ui/index.d.ts +33 -0
- package/dist/types/ui/labels/DictionaryText.d.ts +7 -0
- package/dist/types/ui/labels/StatusChip.d.ts +21 -0
- package/dist/types/ui/labels/Tag.d.ts +19 -0
- package/dist/types/ui/layout/CollapsibleSection.d.ts +15 -0
- package/dist/types/ui/layout/ContentDialog.d.ts +13 -0
- package/dist/types/ui/layout/DefinitionList.d.ts +15 -0
- package/dist/types/ui/layout/DetailHeader.d.ts +15 -0
- package/dist/types/ui/layout/ErrorPage.d.ts +22 -0
- package/dist/types/ui/layout/FullPageLoader.d.ts +1 -0
- package/dist/types/ui/layout/GlassLensFilter.d.ts +1 -0
- package/dist/types/ui/layout/GlassPanel.d.ts +11 -0
- package/dist/types/ui/layout/PageHeader.d.ts +15 -0
- package/dist/types/ui/layout/PageHeading.d.ts +18 -0
- package/dist/types/ui/layout/ProfileHeader.d.ts +17 -0
- package/dist/types/ui/layout/ScrollableRow.d.ts +33 -0
- package/dist/types/ui/layout/SectionCard.d.ts +11 -0
- package/dist/types/ui/layout/appBarHeight.d.ts +13 -0
- package/dist/types/ui/metrics/BarList.d.ts +19 -0
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +28 -0
- package/dist/types/ui/metrics/ChartTooltip.d.ts +5 -0
- package/dist/types/ui/metrics/ColumnChart.d.ts +35 -0
- package/dist/types/ui/metrics/MetricCard.d.ts +31 -0
- package/dist/types/ui/metrics/TableToggle.d.ts +7 -0
- package/dist/types/ui/metrics/barListLabels.d.ts +17 -0
- package/dist/types/ui/metrics/heatmapScale.d.ts +45 -0
- package/dist/types/ui/navigation/BackButton.d.ts +11 -0
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +7 -0
- package/dist/types/ui/navigation/DocumentTitle.d.ts +6 -0
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -0
- package/dist/types/ui/navigation/LanguageToggle.d.ts +8 -0
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +9 -0
- package/dist/types/ui/navigation/colorMode.d.ts +18 -0
- package/dist/types/ui/navigation/fontSizeLevels.d.ts +25 -0
- package/dist/types/ui/navigation/useFontSize.d.ts +10 -0
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +38 -0
- package/dist/types/ui/timeline/timelineDot.d.ts +19 -0
- package/dist/types/upload/FileDropzone.d.ts +34 -0
- package/dist/types/upload/fileRules.d.ts +51 -0
- package/dist/types/upload/uploadMachine.d.ts +23 -0
- package/dist/types/upload/uploadWithProgress.d.ts +16 -0
- package/dist/ui/actions/CopyButton.js +28 -0
- package/dist/ui/actions/CopyButton.js.map +1 -0
- package/dist/ui/actions/DownloadMenu.js +163 -0
- package/dist/ui/actions/DownloadMenu.js.map +1 -0
- package/dist/ui/actions/SecretField.js +57 -0
- package/dist/ui/actions/SecretField.js.map +1 -0
- package/dist/ui/filters/DateRangeFilter.js +86 -0
- package/dist/ui/filters/DateRangeFilter.js.map +1 -0
- package/dist/ui/filters/PeriodRangeSelector.js +56 -0
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -0
- package/dist/ui/filters/QuickFilters.js +91 -0
- package/dist/ui/filters/QuickFilters.js.map +1 -0
- package/dist/ui/filters/SegmentedControl.js +80 -0
- package/dist/ui/filters/SegmentedControl.js.map +1 -0
- package/dist/ui/labels/DictionaryText.js +11 -0
- package/dist/ui/labels/DictionaryText.js.map +1 -0
- package/dist/ui/labels/StatusChip.js +52 -0
- package/dist/ui/labels/StatusChip.js.map +1 -0
- package/dist/ui/labels/Tag.js +26 -0
- package/dist/ui/labels/Tag.js.map +1 -0
- package/dist/ui/layout/CollapsibleSection.js +48 -0
- package/dist/ui/layout/CollapsibleSection.js.map +1 -0
- package/dist/ui/layout/ContentDialog.js +97 -0
- package/dist/ui/layout/ContentDialog.js.map +1 -0
- package/dist/ui/layout/DefinitionList.js +30 -0
- package/dist/ui/layout/DefinitionList.js.map +1 -0
- package/dist/ui/layout/DetailHeader.js +27 -0
- package/dist/ui/layout/DetailHeader.js.map +1 -0
- package/dist/ui/layout/ErrorPage.js +40 -0
- package/dist/ui/layout/ErrorPage.js.map +1 -0
- package/dist/ui/layout/FullPageLoader.js +13 -0
- package/dist/ui/layout/FullPageLoader.js.map +1 -0
- package/dist/ui/layout/GlassLensFilter.js +53 -0
- package/dist/ui/layout/GlassLensFilter.js.map +1 -0
- package/dist/ui/layout/GlassPanel.js +18 -0
- package/dist/ui/layout/GlassPanel.js.map +1 -0
- package/dist/ui/layout/PageHeader.js +23 -0
- package/dist/ui/layout/PageHeader.js.map +1 -0
- package/dist/ui/layout/PageHeading.js +51 -0
- package/dist/ui/layout/PageHeading.js.map +1 -0
- package/dist/ui/layout/ProfileHeader.js +64 -0
- package/dist/ui/layout/ProfileHeader.js.map +1 -0
- package/dist/ui/layout/ScrollableRow.js +156 -0
- package/dist/ui/layout/ScrollableRow.js.map +1 -0
- package/dist/ui/layout/SectionCard.js +18 -0
- package/dist/ui/layout/SectionCard.js.map +1 -0
- package/dist/ui/layout/appBarHeight.js +42 -0
- package/dist/ui/layout/appBarHeight.js.map +1 -0
- package/dist/ui/metrics/BarList.js +89 -0
- package/dist/ui/metrics/BarList.js.map +1 -0
- package/dist/ui/metrics/CalendarHeatmap.js +131 -0
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -0
- package/dist/ui/metrics/ChartTooltip.js +16 -0
- package/dist/ui/metrics/ChartTooltip.js.map +1 -0
- package/dist/ui/metrics/ColumnChart.js +110 -0
- package/dist/ui/metrics/ColumnChart.js.map +1 -0
- package/dist/ui/metrics/MetricCard.js +64 -0
- package/dist/ui/metrics/MetricCard.js.map +1 -0
- package/dist/ui/metrics/TableToggle.js +13 -0
- package/dist/ui/metrics/TableToggle.js.map +1 -0
- package/dist/ui/metrics/barListLabels.js +9 -0
- package/dist/ui/metrics/barListLabels.js.map +1 -0
- package/dist/ui/metrics/heatmapScale.js +67 -0
- package/dist/ui/metrics/heatmapScale.js.map +1 -0
- package/dist/ui/navigation/BackButton.js +28 -0
- package/dist/ui/navigation/BackButton.js.map +1 -0
- package/dist/ui/navigation/ColorSchemeToggle.js +37 -0
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -0
- package/dist/ui/navigation/DocumentTitle.js +11 -0
- package/dist/ui/navigation/DocumentTitle.js.map +1 -0
- package/dist/ui/navigation/FontSizeToggle.js +40 -0
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -0
- package/dist/ui/navigation/LanguageToggle.js +27 -0
- package/dist/ui/navigation/LanguageToggle.js.map +1 -0
- package/dist/ui/navigation/PageBreadcrumbs.js +51 -0
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -0
- package/dist/ui/navigation/colorMode.js +13 -0
- package/dist/ui/navigation/colorMode.js.map +1 -0
- package/dist/ui/navigation/fontSizeLevels.js +24 -0
- package/dist/ui/navigation/fontSizeLevels.js.map +1 -0
- package/dist/ui/navigation/useFontSize.js +44 -0
- package/dist/ui/navigation/useFontSize.js.map +1 -0
- package/dist/ui/timeline/GroupedTimeline.js +104 -0
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -0
- package/dist/ui/timeline/timelineDot.js +20 -0
- package/dist/ui/timeline/timelineDot.js.map +1 -0
- package/dist/upload/FileDropzone.js +143 -0
- package/dist/upload/FileDropzone.js.map +1 -0
- package/dist/upload/fileRules.js +62 -0
- package/dist/upload/fileRules.js.map +1 -0
- package/dist/upload/uploadMachine.js +73 -0
- package/dist/upload/uploadMachine.js.map +1 -0
- package/dist/upload/uploadWithProgress.js +45 -0
- package/dist/upload/uploadWithProgress.js.map +1 -0
- package/package.json +303 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colorMode.js","sources":["../../../src/ui/navigation/colorMode.js"],"sourcesContent":["// A qué modo lleva el interruptor de claro / oscuro (ui/navigation/ColorSchemeToggle.jsx) y en qué modo pintar lo\n// que no es de MUI (el Toaster de sonner).\n//\n// Aparte del componente para probarlo sin navegador (colorMode.test.js).\n//\n// Funciones puras, no renderizan: function (regla de la casa).\n\nconst COLOR_MODES = ['light', 'dark'];\n\n/**\n * El modo que SE ESTÁ viendo. Con mode 'system' manda lo que diga el sistema operativo; antes de montar en el cliente\n * MUI todavía no sabe el modo y regresa undefined.\n *\n * @param {string|undefined} mode 'light' | 'dark' | 'system' (useColorScheme de MUI)\n * @param {string|undefined} systemMode 'light' | 'dark' (lo que dice el sistema operativo)\n * @returns {'light'|'dark'} el modo visible; 'light' si todavía no se sabe\n */\nexport function resolvedColorModeOf(mode, systemMode) {\n const visibleMode = mode === 'system' ? systemMode : mode;\n return COLOR_MODES.includes(visibleMode) ? visibleMode : 'light';\n}\n\n/**\n * El modo al que lleva el interruptor: siempre el contrario del que se ve. Dos estados y no tres: un «Sistema» en la\n * barra le agrega una decisión al usuario sin resolverle nada.\n *\n * @param {string|undefined} mode 'light' | 'dark' | 'system'\n * @param {string|undefined} systemMode 'light' | 'dark'\n * @returns {'light'|'dark'}\n */\nexport function nextColorModeOf(mode, systemMode) {\n return resolvedColorModeOf(mode, systemMode) === 'dark' ? 'light' : 'dark';\n}\n"],"names":[],"mappings":"AAOA,MAAM,cAAc,CAAC,SAAS,MAAM;AAU7B,SAAS,oBAAoB,MAAM,YAAY;AAClD,QAAM,cAAc,SAAS,WAAW,aAAa;AACrD,SAAO,YAAY,SAAS,WAAW,IAAI,cAAc;AAC7D;AAUO,SAAS,gBAAgB,MAAM,YAAY;AAC9C,SAAO,oBAAoB,MAAM,UAAU,MAAM,SAAS,UAAU;AACxE;"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const ROOT_FONT_SIZE_PIXELS = 16;
|
|
2
|
+
const FONT_SIZE_STORAGE_KEY = "calcita:fontSize";
|
|
3
|
+
const FONT_SIZE_LEVELS = Object.freeze([
|
|
4
|
+
{ key: "normal", multiplier: 1, labelKey: "layout:fontSize.normal" },
|
|
5
|
+
{ key: "large", multiplier: 1.15, labelKey: "layout:fontSize.large" },
|
|
6
|
+
{ key: "xLarge", multiplier: 1.3, labelKey: "layout:fontSize.xLarge" }
|
|
7
|
+
]);
|
|
8
|
+
const DEFAULT_FONT_SIZE_LEVEL = "normal";
|
|
9
|
+
function rootFontSizeOf(levelKey) {
|
|
10
|
+
const level = FONT_SIZE_LEVELS.find((candidate) => candidate.key === levelKey);
|
|
11
|
+
return `${ROOT_FONT_SIZE_PIXELS * (level?.multiplier ?? 1)}px`;
|
|
12
|
+
}
|
|
13
|
+
function isFontSizeLevel(levelKey) {
|
|
14
|
+
return FONT_SIZE_LEVELS.some((level) => level.key === levelKey);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
DEFAULT_FONT_SIZE_LEVEL,
|
|
18
|
+
FONT_SIZE_LEVELS,
|
|
19
|
+
FONT_SIZE_STORAGE_KEY,
|
|
20
|
+
ROOT_FONT_SIZE_PIXELS,
|
|
21
|
+
isFontSizeLevel,
|
|
22
|
+
rootFontSizeOf
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=fontSizeLevels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontSizeLevels.js","sources":["../../../src/ui/navigation/fontSizeLevels.js"],"sourcesContent":["// Los tamaños de letra que la persona puede elegir (del tema de walk-ot). Todo el tema está en rem, así que basta con\n// cambiar la letra de la raíz (<html>) para que crezca toda la pantalla, también las tablas y los diálogos.\n//\n// Funciones puras, sin React: function (regla de la casa).\n\n/** La letra de la raíz del navegador: la base de los rem. Con menos de 16 px el iPhone hace zoom al entrar a un campo. */\nexport const ROOT_FONT_SIZE_PIXELS = 16;\n\n/** Dónde se recuerda la elección en este navegador. */\nexport const FONT_SIZE_STORAGE_KEY = 'calcita:fontSize';\n\n/** Los niveles: su llave, cuánto crece la letra y su texto. */\nexport const FONT_SIZE_LEVELS = Object.freeze([\n {key: 'normal', multiplier: 1, labelKey: 'layout:fontSize.normal'},\n {key: 'large', multiplier: 1.15, labelKey: 'layout:fontSize.large'},\n {key: 'xLarge', multiplier: 1.3, labelKey: 'layout:fontSize.xLarge'},\n]);\n\nexport const DEFAULT_FONT_SIZE_LEVEL = 'normal';\n\n/**\n * La letra de la raíz para un nivel; con una llave que no existe, la normal.\n *\n * @param {string} levelKey\n * @returns {string} p. ej. '18.4px'\n */\nexport function rootFontSizeOf(levelKey) {\n const level = FONT_SIZE_LEVELS.find((candidate) => candidate.key === levelKey);\n return `${ROOT_FONT_SIZE_PIXELS * (level?.multiplier ?? 1)}px`;\n}\n\n/**\n * ¿Es un nivel que existe? (lo guardado en el navegador puede venir de otra versión).\n *\n * @param {unknown} levelKey\n * @returns {boolean}\n */\nexport function isFontSizeLevel(levelKey) {\n return FONT_SIZE_LEVELS.some((level) => level.key === levelKey);\n}\n"],"names":[],"mappings":"AAMY,MAAC,wBAAwB;AAGzB,MAAC,wBAAwB;AAGzB,MAAC,mBAAmB,OAAO,OAAO;AAAA,EAC1C,EAAC,KAAK,UAAU,YAAY,GAAG,UAAU,yBAAwB;AAAA,EACjE,EAAC,KAAK,SAAS,YAAY,MAAM,UAAU,wBAAuB;AAAA,EAClE,EAAC,KAAK,UAAU,YAAY,KAAK,UAAU,yBAAwB;AACvE,CAAC;AAEW,MAAC,0BAA0B;AAQhC,SAAS,eAAe,UAAU;AACrC,QAAM,QAAQ,iBAAiB,KAAK,CAAC,cAAc,UAAU,QAAQ,QAAQ;AAC7E,SAAO,GAAG,yBAAyB,OAAO,cAAc,EAAE;AAC9D;AAQO,SAAS,gBAAgB,UAAU;AACtC,SAAO,iBAAiB,KAAK,CAAC,UAAU,MAAM,QAAQ,QAAQ;AAClE;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
|
3
|
+
import { DEFAULT_FONT_SIZE_LEVEL, FONT_SIZE_STORAGE_KEY, isFontSizeLevel, rootFontSizeOf, FONT_SIZE_LEVELS } from "./fontSizeLevels.js";
|
|
4
|
+
const FONT_SIZE_CHANGE_EVENT = "calcita:font-size";
|
|
5
|
+
function useFontSize() {
|
|
6
|
+
const [level, setLevelState] = useState(DEFAULT_FONT_SIZE_LEVEL);
|
|
7
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
try {
|
|
10
|
+
const storedLevel = window.localStorage.getItem(FONT_SIZE_STORAGE_KEY);
|
|
11
|
+
if (isFontSizeLevel(storedLevel)) {
|
|
12
|
+
setLevelState(storedLevel);
|
|
13
|
+
}
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
setIsMounted(true);
|
|
17
|
+
}, []);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const followOtherCopies = (event) => setLevelState(event.detail);
|
|
20
|
+
window.addEventListener(FONT_SIZE_CHANGE_EVENT, followOtherCopies);
|
|
21
|
+
return () => window.removeEventListener(FONT_SIZE_CHANGE_EVENT, followOtherCopies);
|
|
22
|
+
}, []);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (isMounted) {
|
|
25
|
+
document.documentElement.style.fontSize = rootFontSizeOf(level);
|
|
26
|
+
}
|
|
27
|
+
}, [level, isMounted]);
|
|
28
|
+
const setLevel = useCallback((levelKey) => {
|
|
29
|
+
if (!isFontSizeLevel(levelKey)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
setLevelState(levelKey);
|
|
33
|
+
try {
|
|
34
|
+
window.localStorage.setItem(FONT_SIZE_STORAGE_KEY, levelKey);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
window.dispatchEvent(new CustomEvent(FONT_SIZE_CHANGE_EVENT, { detail: levelKey }));
|
|
38
|
+
}, []);
|
|
39
|
+
return { level, setLevel, isMounted, levels: FONT_SIZE_LEVELS };
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
useFontSize
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=useFontSize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFontSize.js","sources":["../../../src/ui/navigation/useFontSize.js"],"sourcesContent":["// El tamaño de letra elegido, recordado en este navegador (del tema de walk-ot).\n//\n// - Se lee al montar en el cliente: en el servidor no hay almacenamiento y la primera pintura es la normal.\n// - Sin almacenamiento (ventana privada estricta) funciona igual; solo no se recuerda.\n//\n// Hook genérico del paquete: function (regla de la casa).\nimport {useCallback, useEffect, useState} from 'react';\n\nimport {\n DEFAULT_FONT_SIZE_LEVEL, FONT_SIZE_LEVELS, FONT_SIZE_STORAGE_KEY, isFontSizeLevel, rootFontSizeOf,\n} from '@/ui/navigation/fontSizeLevels.js';\n\n// el aviso entre copias del gancho cuando la persona cambia el tamaño\nconst FONT_SIZE_CHANGE_EVENT = 'calcita:font-size';\n\n/**\n * @returns {{level: string, setLevel: (levelKey: string) => void, isMounted: boolean, levels: typeof FONT_SIZE_LEVELS}}\n */\nexport function useFontSize() {\n const [level, setLevelState] = useState(DEFAULT_FONT_SIZE_LEVEL);\n const [isMounted, setIsMounted] = useState(false);\n\n useEffect(() => {\n try {\n const storedLevel = window.localStorage.getItem(FONT_SIZE_STORAGE_KEY);\n if (isFontSizeLevel(storedLevel)) {\n setLevelState(storedLevel);\n }\n } catch {\n // almacenamiento bloqueado: se queda la normal\n }\n setIsMounted(true);\n }, []);\n\n // otra copia del gancho (el menú de la cuenta, «Mi perfil») cambió el tamaño: esta también se entera\n useEffect(() => {\n const followOtherCopies = (event) => setLevelState(event.detail);\n window.addEventListener(FONT_SIZE_CHANGE_EVENT, followOtherCopies);\n return () => window.removeEventListener(FONT_SIZE_CHANGE_EVENT, followOtherCopies);\n }, []);\n\n useEffect(() => {\n if (isMounted) {\n document.documentElement.style.fontSize = rootFontSizeOf(level);\n }\n }, [level, isMounted]);\n\n const setLevel = useCallback((levelKey) => {\n if (!isFontSizeLevel(levelKey)) {\n return;\n }\n setLevelState(levelKey);\n try {\n window.localStorage.setItem(FONT_SIZE_STORAGE_KEY, levelKey);\n } catch {\n // almacenamiento bloqueado: cambia igual, solo no se recuerda\n }\n window.dispatchEvent(new CustomEvent(FONT_SIZE_CHANGE_EVENT, {detail: levelKey}));\n }, []);\n\n return {level, setLevel, isMounted, levels: FONT_SIZE_LEVELS};\n}\n"],"names":[],"mappings":";;;AAaA,MAAM,yBAAyB;AAKxB,SAAS,cAAc;AAC1B,QAAM,CAAC,OAAO,aAAa,IAAI,SAAS,uBAAuB;AAC/D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,YAAU,MAAM;AACZ,QAAI;AACA,YAAM,cAAc,OAAO,aAAa,QAAQ,qBAAqB;AACrE,UAAI,gBAAgB,WAAW,GAAG;AAC9B,sBAAc,WAAW;AAAA,MAC7B;AAAA,IACJ,QAAQ;AAAA,IAER;AACA,iBAAa,IAAI;AAAA,EACrB,GAAG,CAAA,CAAE;AAGL,YAAU,MAAM;AACZ,UAAM,oBAAoB,CAAC,UAAU,cAAc,MAAM,MAAM;AAC/D,WAAO,iBAAiB,wBAAwB,iBAAiB;AACjE,WAAO,MAAM,OAAO,oBAAoB,wBAAwB,iBAAiB;AAAA,EACrF,GAAG,CAAA,CAAE;AAEL,YAAU,MAAM;AACZ,QAAI,WAAW;AACX,eAAS,gBAAgB,MAAM,WAAW,eAAe,KAAK;AAAA,IAClE;AAAA,EACJ,GAAG,CAAC,OAAO,SAAS,CAAC;AAErB,QAAM,WAAW,YAAY,CAAC,aAAa;AACvC,QAAI,CAAC,gBAAgB,QAAQ,GAAG;AAC5B;AAAA,IACJ;AACA,kBAAc,QAAQ;AACtB,QAAI;AACA,aAAO,aAAa,QAAQ,uBAAuB,QAAQ;AAAA,IAC/D,QAAQ;AAAA,IAER;AACA,WAAO,cAAc,IAAI,YAAY,wBAAwB,EAAC,QAAQ,SAAQ,CAAC,CAAC;AAAA,EACpF,GAAG,CAAA,CAAE;AAEL,SAAO,EAAC,OAAO,UAAU,WAAW,QAAQ,iBAAgB;AAChE;"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Timeline from "@mui/lab/Timeline";
|
|
5
|
+
import TimelineConnector from "@mui/lab/TimelineConnector";
|
|
6
|
+
import TimelineContent from "@mui/lab/TimelineContent";
|
|
7
|
+
import TimelineDot from "@mui/lab/TimelineDot";
|
|
8
|
+
import TimelineItem, { timelineItemClasses } from "@mui/lab/TimelineItem";
|
|
9
|
+
import TimelineOppositeContent from "@mui/lab/TimelineOppositeContent";
|
|
10
|
+
import TimelineSeparator from "@mui/lab/TimelineSeparator";
|
|
11
|
+
import Box from "@mui/material/Box";
|
|
12
|
+
import ButtonBase from "@mui/material/ButtonBase";
|
|
13
|
+
import Chip from "@mui/material/Chip";
|
|
14
|
+
import Collapse from "@mui/material/Collapse";
|
|
15
|
+
import Stack from "@mui/material/Stack";
|
|
16
|
+
import Typography from "@mui/material/Typography";
|
|
17
|
+
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
18
|
+
import { useTheme } from "@mui/material/styles";
|
|
19
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
20
|
+
import { paletteOf } from "../../theme/components/paletteOf.js";
|
|
21
|
+
import { TIMELINE_EMPHASIS, timelineDotOf } from "./timelineDot.js";
|
|
22
|
+
const HIGHLIGHT_TINT_PERCENT = 8;
|
|
23
|
+
const LEFT_ALIGNED_TIMELINE = { m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: { flex: 0, p: 0 } };
|
|
24
|
+
const ALTERNATE_TIMELINE = { m: 0, px: 0, py: 0.5 };
|
|
25
|
+
function GroupedTimeline({ groups }) {
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
const isWide = useMediaQuery(theme.breakpoints.up("md"));
|
|
28
|
+
const [expandedKeys, setExpandedKeys] = useState(() => new Set(
|
|
29
|
+
groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key)
|
|
30
|
+
));
|
|
31
|
+
const toggleGroup = (groupKey) => setExpandedKeys((currentKeys) => {
|
|
32
|
+
const nextKeys = new Set(currentKeys);
|
|
33
|
+
if (nextKeys.has(groupKey)) {
|
|
34
|
+
nextKeys.delete(groupKey);
|
|
35
|
+
} else {
|
|
36
|
+
nextKeys.add(groupKey);
|
|
37
|
+
}
|
|
38
|
+
return nextKeys;
|
|
39
|
+
});
|
|
40
|
+
return /* @__PURE__ */ jsx(Stack, { spacing: 1.5, children: groups.map((timelineGroup) => {
|
|
41
|
+
const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;
|
|
42
|
+
const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;
|
|
43
|
+
const isMuted = emphasis === TIMELINE_EMPHASIS.muted;
|
|
44
|
+
const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);
|
|
45
|
+
const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);
|
|
46
|
+
return (
|
|
47
|
+
// el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado
|
|
48
|
+
/* @__PURE__ */ jsxs(Box, { sx: {
|
|
49
|
+
border: 1,
|
|
50
|
+
borderRadius: 1.5,
|
|
51
|
+
px: 1.5,
|
|
52
|
+
pt: 1.25,
|
|
53
|
+
pb: isExpanded ? 0 : 1.25,
|
|
54
|
+
borderColor: isHighlighted ? "primary.main" : "transparent",
|
|
55
|
+
// un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo
|
|
56
|
+
bgcolor: (theme2) => isHighlighted ? `color-mix(in srgb, ${paletteOf(theme2).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : "transparent"
|
|
57
|
+
}, children: [
|
|
58
|
+
timelineGroup.title && /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center", justifyContent: isWide ? "center" : "flex-start" }, children: [
|
|
59
|
+
/* @__PURE__ */ jsxs(
|
|
60
|
+
ButtonBase,
|
|
61
|
+
{
|
|
62
|
+
disabled: !isCollapsible,
|
|
63
|
+
onClick: () => toggleGroup(timelineGroup.key),
|
|
64
|
+
"aria-expanded": isCollapsible ? isExpanded : void 0,
|
|
65
|
+
sx: { gap: 1, px: 1, py: 0.25, borderRadius: 1, "&.Mui-disabled": { pointerEvents: "none" } },
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h6", component: "h3", sx: { fontWeight: 700, color: isMuted ? "text.secondary" : "text.primary" }, children: timelineGroup.title }),
|
|
68
|
+
isCollapsible && timelineGroup.summary && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", component: "span", children: timelineGroup.summary }),
|
|
69
|
+
isCollapsible && /* @__PURE__ */ jsx(ExpandMoreIcon, { fontSize: "small", sx: {
|
|
70
|
+
color: "text.secondary",
|
|
71
|
+
transition: theme.transitions.create("transform"),
|
|
72
|
+
transform: isExpanded ? "rotate(180deg)" : "none"
|
|
73
|
+
} })
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
timelineGroup.badge && /* @__PURE__ */ jsx(Chip, { size: "small", color: "primary", label: timelineGroup.badge })
|
|
78
|
+
] }),
|
|
79
|
+
/* @__PURE__ */ jsx(Collapse, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx(Timeline, { position: isWide ? "alternate" : "right", sx: isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE, children: timelineGroup.items.map((timelineEntry, entryPosition) => {
|
|
80
|
+
const EntryIcon = timelineEntry.icon;
|
|
81
|
+
const isFirstEntry = entryPosition === 0;
|
|
82
|
+
const isLastEntry = entryPosition === timelineGroup.items.length - 1;
|
|
83
|
+
return /* @__PURE__ */ jsxs(TimelineItem, { children: [
|
|
84
|
+
isWide && /* @__PURE__ */ jsx(TimelineOppositeContent, { variant: "body2", color: "text.secondary", sx: { m: "auto 0" }, children: timelineEntry.timestamp }),
|
|
85
|
+
/* @__PURE__ */ jsxs(TimelineSeparator, { children: [
|
|
86
|
+
/* @__PURE__ */ jsx(TimelineConnector, { sx: { visibility: isFirstEntry ? "hidden" : "visible" } }),
|
|
87
|
+
/* @__PURE__ */ jsx(TimelineDot, { ...timelineDotOf({ emphasis, tone: timelineEntry.tone }), children: EntryIcon && /* @__PURE__ */ jsx(EntryIcon, { fontSize: "small" }) }),
|
|
88
|
+
/* @__PURE__ */ jsx(TimelineConnector, { sx: { visibility: isLastEntry ? "hidden" : "visible" } })
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsxs(TimelineContent, { sx: { py: 1.5, px: 2, minWidth: 0 }, children: [
|
|
91
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", component: "p", sx: { fontWeight: 600, color: isMuted ? "text.secondary" : "text.primary" }, children: timelineEntry.title }),
|
|
92
|
+
!isWide && timelineEntry.timestamp && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", children: timelineEntry.timestamp }),
|
|
93
|
+
timelineEntry.details
|
|
94
|
+
] })
|
|
95
|
+
] }, timelineEntry.key);
|
|
96
|
+
}) }) })
|
|
97
|
+
] }, timelineGroup.key)
|
|
98
|
+
);
|
|
99
|
+
}) });
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
GroupedTimeline as default
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=GroupedTimeline.js.map
|
|
@@ -0,0 +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// 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';\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 */\nexport default function GroupedTimeline({groups}) {\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) => setExpandedKeys((currentKeys) => {\n const nextKeys = new Set(currentKeys);\n if (nextKeys.has(groupKey)) {\n nextKeys.delete(groupKey);\n } else {\n nextKeys.add(groupKey);\n }\n return nextKeys;\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 <Box key={timelineGroup.key} sx={{\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 <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <ButtonBase disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n sx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <Typography variant=\"h6\" component=\"h3\" sx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </Typography>\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 <ExpandMoreIcon fontSize=\"small\" sx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </ButtonBase>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </Stack>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <Timeline position={isWide ? 'alternate' : 'right'} sx={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 <TimelineOppositeContent variant=\"body2\" color=\"text.secondary\" sx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOppositeContent>\n )}\n <TimelineSeparator>\n <TimelineConnector sx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnector sx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContent sx={{py: 1.5, px: 2, minWidth: 0}}>\n <Typography variant=\"body2\" component=\"p\" sx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </Typography>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContent>\n </TimelineItem>\n );\n })}\n </Timeline>\n </Collapse>\n </Box>\n );\n })}\n </Stack>\n );\n}\n"],"names":["theme"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const TIMELINE_EMPHASIS = Object.freeze({
|
|
2
|
+
// el grupo que importa ahora (p. ej. el intento vigente): recuadro, puntos llenos con el color de su tono
|
|
3
|
+
highlighted: "highlighted",
|
|
4
|
+
// un grupo sin resaltar: puntos llenos con el color de su tono
|
|
5
|
+
normal: "normal",
|
|
6
|
+
// un grupo que ya pasó: puntos con contorno gris, para que el ojo se vaya al resaltado
|
|
7
|
+
muted: "muted"
|
|
8
|
+
});
|
|
9
|
+
const DOT_COLORS = ["primary", "secondary", "success", "warning", "error", "info", "grey"];
|
|
10
|
+
function timelineDotOf({ emphasis, tone } = {}) {
|
|
11
|
+
if (emphasis === TIMELINE_EMPHASIS.muted) {
|
|
12
|
+
return { variant: "outlined", color: "grey" };
|
|
13
|
+
}
|
|
14
|
+
return { variant: "filled", color: DOT_COLORS.includes(tone) ? tone : "grey" };
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
TIMELINE_EMPHASIS,
|
|
18
|
+
timelineDotOf
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=timelineDot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timelineDot.js","sources":["../../../src/ui/timeline/timelineDot.js"],"sourcesContent":["// Cómo se pinta el punto de un evento en GroupedTimeline, según el énfasis de su grupo y el tono del evento.\n//\n// Funciones puras (no renderizan): function (regla de la casa). Se prueban sin navegador (timelineDot.test.js).\n\n/** Énfasis de un grupo de la línea de tiempo. */\nexport const TIMELINE_EMPHASIS = Object.freeze({\n // el grupo que importa ahora (p. ej. el intento vigente): recuadro, puntos llenos con el color de su tono\n highlighted: 'highlighted',\n // un grupo sin resaltar: puntos llenos con el color de su tono\n normal: 'normal',\n // un grupo que ya pasó: puntos con contorno gris, para que el ojo se vaya al resaltado\n muted: 'muted',\n});\n\n// los colores que acepta TimelineDot de @mui/lab\nconst DOT_COLORS = ['primary', 'secondary', 'success', 'warning', 'error', 'info', 'grey'];\n\n/**\n * Variante y color del punto de un evento.\n *\n * @param {{emphasis?: string, tone?: string}} dotOptions énfasis del grupo y tono del evento (success, warning, error...)\n * @returns {{variant: 'filled'|'outlined', color: string}} props de TimelineDot; un tono desconocido sale en gris\n */\nexport function timelineDotOf({emphasis, tone} = {}) {\n if (emphasis === TIMELINE_EMPHASIS.muted) {\n return {variant: 'outlined', color: 'grey'};\n }\n\n return {variant: 'filled', color: DOT_COLORS.includes(tone) ? tone : 'grey'};\n}\n"],"names":[],"mappings":"AAKY,MAAC,oBAAoB,OAAO,OAAO;AAAA;AAAA,EAE3C,aAAa;AAAA;AAAA,EAEb,QAAQ;AAAA;AAAA,EAER,OAAO;AACX,CAAC;AAGD,MAAM,aAAa,CAAC,WAAW,aAAa,WAAW,WAAW,SAAS,QAAQ,MAAM;AAQlF,SAAS,cAAc,EAAC,UAAU,KAAI,IAAI,CAAA,GAAI;AACjD,MAAI,aAAa,kBAAkB,OAAO;AACtC,WAAO,EAAC,SAAS,YAAY,OAAO,OAAM;AAAA,EAC9C;AAEA,SAAO,EAAC,SAAS,UAAU,OAAO,WAAW,SAAS,IAAI,IAAI,OAAO,OAAM;AAC/E;"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
5
|
+
import Box from "@mui/material/Box";
|
|
6
|
+
import Button from "@mui/material/Button";
|
|
7
|
+
import Chip from "@mui/material/Chip";
|
|
8
|
+
import Paper from "@mui/material/Paper";
|
|
9
|
+
import Stack from "@mui/material/Stack";
|
|
10
|
+
import Typography from "@mui/material/Typography";
|
|
11
|
+
import CancelIcon from "@mui/icons-material/Cancel";
|
|
12
|
+
import DescriptionOutlinedIcon from "@mui/icons-material/DescriptionOutlined";
|
|
13
|
+
import ImageOutlinedIcon from "@mui/icons-material/ImageOutlined";
|
|
14
|
+
import InsertDriveFileOutlinedIcon from "@mui/icons-material/InsertDriveFileOutlined";
|
|
15
|
+
import PictureAsPdfOutlinedIcon from "@mui/icons-material/PictureAsPdfOutlined";
|
|
16
|
+
import TableViewOutlinedIcon from "@mui/icons-material/TableViewOutlined";
|
|
17
|
+
import ModalWaiting from "../feedback/ModalWaiting.js";
|
|
18
|
+
import { toastError } from "../feedback/toast.js";
|
|
19
|
+
import { formatFileSize } from "../formatting.js";
|
|
20
|
+
import { resolveAccept, parseFileSize, matchesAccept, FILE_REJECTION } from "./fileRules.js";
|
|
21
|
+
const FILE_APPEARANCES = [
|
|
22
|
+
{ pattern: /\.(xlsx?|csv)$/, Icon: TableViewOutlinedIcon, color: "success" },
|
|
23
|
+
{ pattern: /\.pdf$/, Icon: PictureAsPdfOutlinedIcon, color: "error" },
|
|
24
|
+
{ pattern: /\.(png|jpe?g|gif|webp|svg|avif)$/, Icon: ImageOutlinedIcon, color: "info" },
|
|
25
|
+
{ pattern: /\.(docx?|txt|md)$/, Icon: DescriptionOutlinedIcon, color: "primary" }
|
|
26
|
+
];
|
|
27
|
+
const DEFAULT_FILE_APPEARANCE = { Icon: InsertDriveFileOutlinedIcon, color: "action" };
|
|
28
|
+
function FileDropzone({
|
|
29
|
+
value = null,
|
|
30
|
+
onChange,
|
|
31
|
+
accept,
|
|
32
|
+
maxSize,
|
|
33
|
+
hint,
|
|
34
|
+
buttonLabel,
|
|
35
|
+
disabled = false,
|
|
36
|
+
busy = false,
|
|
37
|
+
busyModal = false,
|
|
38
|
+
onReject,
|
|
39
|
+
actions,
|
|
40
|
+
children,
|
|
41
|
+
sx
|
|
42
|
+
}) {
|
|
43
|
+
const { t } = useToolkitTranslation("common");
|
|
44
|
+
const inputRef = useRef(null);
|
|
45
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
46
|
+
const isLocked = disabled || busy;
|
|
47
|
+
const resolvedAccept = resolveAccept(accept);
|
|
48
|
+
const maxBytes = parseFileSize(maxSize);
|
|
49
|
+
const hintText = hint ?? t("upload.hint");
|
|
50
|
+
const reject = (file, reason) => {
|
|
51
|
+
if (onReject) {
|
|
52
|
+
onReject(file, reason);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const message = reason === FILE_REJECTION.size ? t("upload.tooLarge", { name: file.name, max: formatFileSize(maxBytes) }) : t("upload.invalidType", { name: file.name, accept: resolvedAccept });
|
|
56
|
+
toastError(message);
|
|
57
|
+
};
|
|
58
|
+
const pick = (file) => {
|
|
59
|
+
if (!file || isLocked) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!matchesAccept(file, resolvedAccept)) {
|
|
63
|
+
reject(file, FILE_REJECTION.type);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (maxBytes && file.size > maxBytes) {
|
|
67
|
+
reject(file, FILE_REJECTION.size);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
onChange(file);
|
|
71
|
+
};
|
|
72
|
+
const openPicker = () => {
|
|
73
|
+
if (inputRef.current) {
|
|
74
|
+
inputRef.current.value = "";
|
|
75
|
+
}
|
|
76
|
+
inputRef.current?.click();
|
|
77
|
+
};
|
|
78
|
+
const appearance = value ? FILE_APPEARANCES.find(({ pattern }) => pattern.test(value.name.toLowerCase())) ?? DEFAULT_FILE_APPEARANCE : null;
|
|
79
|
+
return /* @__PURE__ */ jsxs(
|
|
80
|
+
Paper,
|
|
81
|
+
{
|
|
82
|
+
variant: "outlined",
|
|
83
|
+
"aria-busy": busy,
|
|
84
|
+
onDragOver: (event) => {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
setIsDragging(!isLocked);
|
|
87
|
+
},
|
|
88
|
+
onDragLeave: () => setIsDragging(false),
|
|
89
|
+
onDrop: (event) => {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
setIsDragging(false);
|
|
92
|
+
pick(event.dataTransfer.files?.[0]);
|
|
93
|
+
},
|
|
94
|
+
sx: {
|
|
95
|
+
p: { xs: 3, sm: 5 },
|
|
96
|
+
textAlign: "center",
|
|
97
|
+
borderStyle: "dashed",
|
|
98
|
+
borderWidth: 2,
|
|
99
|
+
borderColor: isDragging ? "primary.main" : "divider",
|
|
100
|
+
bgcolor: isDragging ? "action.hover" : "background.paper",
|
|
101
|
+
transition: "border-color .15s ease, background-color .15s ease",
|
|
102
|
+
opacity: disabled ? 0.6 : 1,
|
|
103
|
+
...sx
|
|
104
|
+
},
|
|
105
|
+
children: [
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
"input",
|
|
108
|
+
{
|
|
109
|
+
ref: inputRef,
|
|
110
|
+
type: "file",
|
|
111
|
+
accept: resolvedAccept,
|
|
112
|
+
hidden: true,
|
|
113
|
+
disabled: isLocked,
|
|
114
|
+
"aria-label": hintText,
|
|
115
|
+
onChange: (event) => pick(event.target.files?.[0])
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
value ? /* @__PURE__ */ jsx(Box, { sx: { mb: 2 }, children: /* @__PURE__ */ jsx(
|
|
119
|
+
Chip,
|
|
120
|
+
{
|
|
121
|
+
icon: /* @__PURE__ */ jsx(appearance.Icon, { fontSize: "small", color: appearance.color }),
|
|
122
|
+
label: `${value.name} · ${formatFileSize(value.size)}`,
|
|
123
|
+
variant: "outlined",
|
|
124
|
+
onDelete: isLocked ? void 0 : () => onChange(null),
|
|
125
|
+
deleteIcon: /* @__PURE__ */ jsx(CancelIcon, { "aria-label": t("upload.remove", { name: value.name }) }),
|
|
126
|
+
sx: { maxWidth: "100%", height: "auto", py: 0.5, "& .MuiChip-label": { whiteSpace: "normal", overflowWrap: "anywhere" } }
|
|
127
|
+
}
|
|
128
|
+
) }) : /* @__PURE__ */ jsx(Typography, { variant: "body1", sx: { mb: 2, overflowWrap: "anywhere" }, children: hintText }),
|
|
129
|
+
/* @__PURE__ */ jsxs(Stack, { direction: { xs: "column", sm: "row" }, spacing: 2, sx: { justifyContent: "center", alignItems: { xs: "stretch", sm: "center" } }, children: [
|
|
130
|
+
/* @__PURE__ */ jsx(Button, { variant: "outlined", onClick: openPicker, disabled: isLocked, children: buttonLabel ?? t("upload.choose") }),
|
|
131
|
+
actions
|
|
132
|
+
] }),
|
|
133
|
+
maxBytes && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { display: "block", mt: 1.5 }, children: t("upload.maxSizeHint", { max: formatFileSize(maxBytes) }) }),
|
|
134
|
+
children && /* @__PURE__ */ jsx(Box, { sx: { mt: 3, textAlign: "left" }, children }),
|
|
135
|
+
busyModal && /* @__PURE__ */ jsx(ModalWaiting, { open: busy, title: busyModal.title, text: busyModal.text })
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
FileDropzone as default
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=FileDropzone.js.map
|
|
@@ -0,0 +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.\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// 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//\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// Genérico del paquete: function (regla de la casa).\nimport {useRef, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\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';\nimport CancelIcon from '@mui/icons-material/Cancel';\nimport DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';\nimport ImageOutlinedIcon from '@mui/icons-material/ImageOutlined';\nimport InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined';\nimport PictureAsPdfOutlinedIcon from '@mui/icons-material/PictureAsPdfOutlined';\nimport TableViewOutlinedIcon from '@mui/icons-material/TableViewOutlined';\n\nimport ModalWaiting from '@/feedback/ModalWaiting.jsx';\nimport {toastError} from '@/feedback/toast.jsx';\nimport {formatFileSize} from '@/formatting.js';\nimport {FILE_REJECTION, matchesAccept, parseFileSize, resolveAccept} from '@/upload/fileRules.js';\n\n// el ícono según el tipo del archivo elegido, con el mismo color que el panel de descargas\nconst FILE_APPEARANCES = [\n {pattern: /\\.(xlsx?|csv)$/, Icon: TableViewOutlinedIcon, color: 'success'},\n {pattern: /\\.pdf$/, Icon: PictureAsPdfOutlinedIcon, color: 'error'},\n {pattern: /\\.(png|jpe?g|gif|webp|svg|avif)$/, Icon: ImageOutlinedIcon, color: 'info'},\n {pattern: /\\.(docx?|txt|md)$/, Icon: DescriptionOutlinedIcon, color: 'primary'},\n];\nconst DEFAULT_FILE_APPEARANCE = {Icon: InsertDriveFileOutlinedIcon, color: 'action'};\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 {import('react').ReactNode} [props.actions]\n * @param {import('react').ReactNode} [props.children]\n * @param {object} [props.sx] ajustes del recuadro\n */\nexport default function FileDropzone({\n value = null, onChange, accept, maxSize, hint, buttonLabel, disabled = false, busy = false, busyModal = false, onReject, actions,\n children, sx,\n}) {\n const {t} = useToolkitTranslation('common');\n const inputRef = useRef(null);\n const [isDragging, setIsDragging] = useState(false);\n const isLocked = disabled || busy;\n const resolvedAccept = resolveAccept(accept);\n const maxBytes = parseFileSize(maxSize);\n const hintText = hint ?? t('upload.hint');\n\n const reject = (file, reason) => {\n if (onReject) {\n onReject(file, reason);\n return;\n }\n const message = reason === FILE_REJECTION.size\n ? t('upload.tooLarge', {name: file.name, max: formatFileSize(maxBytes)})\n : t('upload.invalidType', {name: file.name, accept: resolvedAccept});\n toastError(message);\n };\n\n const pick = (file) => {\n // guard clause: se cerró la ventana sin elegir, o el candado está puesto\n if (!file || isLocked) {\n return;\n }\n if (!matchesAccept(file, resolvedAccept)) {\n reject(file, FILE_REJECTION.type);\n return;\n }\n if (maxBytes && file.size > maxBytes) {\n reject(file, FILE_REJECTION.size);\n return;\n }\n onChange(file);\n };\n\n const openPicker = () => {\n // OJO: se limpia el valor del input para poder volver a elegir EL MISMO archivo (corregido en disco); sin esto\n // el navegador no avisa el cambio\n if (inputRef.current) {\n inputRef.current.value = '';\n }\n inputRef.current?.click();\n };\n\n const appearance = value\n ? FILE_APPEARANCES.find(({pattern}) => pattern.test(value.name.toLowerCase())) ?? DEFAULT_FILE_APPEARANCE\n : null;\n\n return (\n <Paper\n variant=\"outlined\"\n aria-busy={busy}\n onDragOver={(event) => {\n event.preventDefault();\n setIsDragging(!isLocked);\n }}\n onDragLeave={() => setIsDragging(false)}\n onDrop={(event) => {\n event.preventDefault();\n setIsDragging(false);\n pick(event.dataTransfer.files?.[0]);\n }}\n sx={{\n p: {xs: 3, sm: 5},\n textAlign: 'center',\n borderStyle: 'dashed',\n borderWidth: 2,\n borderColor: isDragging ? 'primary.main' : 'divider',\n bgcolor: isDragging ? 'action.hover' : 'background.paper',\n transition: 'border-color .15s ease, background-color .15s ease',\n opacity: disabled ? 0.6 : 1,\n ...sx,\n }}\n >\n <input ref={inputRef} type=\"file\" accept={resolvedAccept} hidden disabled={isLocked} aria-label={hintText}\n onChange={(event) => pick(event.target.files?.[0])} />\n {value ? (\n <Box sx={{mb: 2}}>\n <Chip\n icon={<appearance.Icon fontSize=\"small\" color={appearance.color} />}\n label={`${value.name} · ${formatFileSize(value.size)}`}\n variant=\"outlined\"\n onDelete={isLocked ? undefined : () => onChange(null)}\n deleteIcon={<CancelIcon aria-label={t('upload.remove', {name: value.name})} />}\n sx={{maxWidth: '100%', height: 'auto', py: 0.5, '& .MuiChip-label': {whiteSpace: 'normal', overflowWrap: 'anywhere'}}}\n />\n </Box>\n ) : (\n <Typography variant=\"body1\" sx={{mb: 2, overflowWrap: 'anywhere'}}>{hintText}</Typography>\n )}\n <Stack direction={{xs: 'column', sm: 'row'}} spacing={2} sx={{justifyContent: 'center', alignItems: {xs: 'stretch', sm: 'center'}}}>\n <Button variant=\"outlined\" onClick={openPicker} disabled={isLocked}>{buttonLabel ?? t('upload.choose')}</Button>\n {actions}\n </Stack>\n {/* el límite, discreto, solo si hay límite */}\n {maxBytes && (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{display: 'block', mt: 1.5}}>\n {t('upload.maxSizeHint', {max: formatFileSize(maxBytes)})}\n </Typography>\n )}\n {children && <Box sx={{mt: 3, textAlign: 'left'}}>{children}</Box>}\n {busyModal && <ModalWaiting open={busy} title={busyModal.title} text={busyModal.text} />}\n </Paper>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAwCA,MAAM,mBAAmB;AAAA,EACrB,EAAC,SAAS,kBAAkB,MAAM,uBAAuB,OAAO,UAAA;AAAA,EAChE,EAAC,SAAS,UAAU,MAAM,0BAA0B,OAAO,QAAA;AAAA,EAC3D,EAAC,SAAS,oCAAoC,MAAM,mBAAmB,OAAO,OAAA;AAAA,EAC9E,EAAC,SAAS,qBAAqB,MAAM,yBAAyB,OAAO,UAAA;AACzE;AACA,MAAM,0BAA0B,EAAC,MAAM,6BAA6B,OAAO,SAAA;AAkB3E,SAAwB,aAAa;AAAA,EACjC,QAAQ;AAAA,EAAM;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAM;AAAA,EAAa,WAAW;AAAA,EAAO,OAAO;AAAA,EAAO,YAAY;AAAA,EAAO;AAAA,EAAU;AAAA,EACzH;AAAA,EAAU;AACd,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,WAAW,OAAO,IAAI;AAC5B,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,WAAW,YAAY;AAC7B,QAAM,iBAAiB,cAAc,MAAM;AAC3C,QAAM,WAAW,cAAc,OAAO;AACtC,QAAM,WAAW,QAAQ,EAAE,aAAa;AAExC,QAAM,SAAS,CAAC,MAAM,WAAW;AAC7B,QAAI,UAAU;AACV,eAAS,MAAM,MAAM;AACrB;AAAA,IACJ;AACA,UAAM,UAAU,WAAW,eAAe,OACpC,EAAE,mBAAmB,EAAC,MAAM,KAAK,MAAM,KAAK,eAAe,QAAQ,EAAA,CAAE,IACrE,EAAE,sBAAsB,EAAC,MAAM,KAAK,MAAM,QAAQ,gBAAe;AACvE,eAAW,OAAO;AAAA,EACtB;AAEA,QAAM,OAAO,CAAC,SAAS;AAEnB,QAAI,CAAC,QAAQ,UAAU;AACnB;AAAA,IACJ;AACA,QAAI,CAAC,cAAc,MAAM,cAAc,GAAG;AACtC,aAAO,MAAM,eAAe,IAAI;AAChC;AAAA,IACJ;AACA,QAAI,YAAY,KAAK,OAAO,UAAU;AAClC,aAAO,MAAM,eAAe,IAAI;AAChC;AAAA,IACJ;AACA,aAAS,IAAI;AAAA,EACjB;AAEA,QAAM,aAAa,MAAM;AAGrB,QAAI,SAAS,SAAS;AAClB,eAAS,QAAQ,QAAQ;AAAA,IAC7B;AACA,aAAS,SAAS,MAAA;AAAA,EACtB;AAEA,QAAM,aAAa,QACb,iBAAiB,KAAK,CAAC,EAAC,QAAA,MAAa,QAAQ,KAAK,MAAM,KAAK,YAAA,CAAa,CAAC,KAAK,0BAChF;AAEN,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,SAAQ;AAAA,MACR,aAAW;AAAA,MACX,YAAY,CAAC,UAAU;AACnB,cAAM,eAAA;AACN,sBAAc,CAAC,QAAQ;AAAA,MAC3B;AAAA,MACA,aAAa,MAAM,cAAc,KAAK;AAAA,MACtC,QAAQ,CAAC,UAAU;AACf,cAAM,eAAA;AACN,sBAAc,KAAK;AACnB,aAAK,MAAM,aAAa,QAAQ,CAAC,CAAC;AAAA,MACtC;AAAA,MACA,IAAI;AAAA,QACA,GAAG,EAAC,IAAI,GAAG,IAAI,EAAA;AAAA,QACf,WAAW;AAAA,QACX,aAAa;AAAA,QACb,aAAa;AAAA,QACb,aAAa,aAAa,iBAAiB;AAAA,QAC3C,SAAS,aAAa,iBAAiB;AAAA,QACvC,YAAY;AAAA,QACZ,SAAS,WAAW,MAAM;AAAA,QAC1B,GAAG;AAAA,MAAA;AAAA,MAGP,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YAAM,KAAK;AAAA,YAAU,MAAK;AAAA,YAAO,QAAQ;AAAA,YAAgB,QAAM;AAAA,YAAC,UAAU;AAAA,YAAU,cAAY;AAAA,YAC1F,UAAU,CAAC,UAAU,KAAK,MAAM,OAAO,QAAQ,CAAC,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,QACvD,QACG,oBAAC,KAAA,EAAI,IAAI,EAAC,IAAI,KACV,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACG,0BAAO,WAAW,MAAX,EAAgB,UAAS,SAAQ,OAAO,WAAW,OAAO;AAAA,YACjE,OAAO,GAAG,MAAM,IAAI,MAAM,eAAe,MAAM,IAAI,CAAC;AAAA,YACpD,SAAQ;AAAA,YACR,UAAU,WAAW,SAAY,MAAM,SAAS,IAAI;AAAA,YACpD,YAAY,oBAAC,YAAA,EAAW,cAAY,EAAE,iBAAiB,EAAC,MAAM,MAAM,KAAA,CAAK,EAAA,CAAG;AAAA,YAC5E,IAAI,EAAC,UAAU,QAAQ,QAAQ,QAAQ,IAAI,KAAK,oBAAoB,EAAC,YAAY,UAAU,cAAc,aAAU;AAAA,UAAC;AAAA,QAAA,EACxH,CACJ,IAEA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,IAAI,EAAC,IAAI,GAAG,cAAc,WAAA,GAAc,UAAA,SAAA,CAAS;AAAA,QAEjF,qBAAC,SAAM,WAAW,EAAC,IAAI,UAAU,IAAI,SAAQ,SAAS,GAAG,IAAI,EAAC,gBAAgB,UAAU,YAAY,EAAC,IAAI,WAAW,IAAI,WAAQ,GAC5H,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAO,SAAQ,YAAW,SAAS,YAAY,UAAU,UAAW,UAAA,eAAe,EAAE,eAAe,EAAA,CAAE;AAAA,UACtG;AAAA,QAAA,GACL;AAAA,QAEC,gCACI,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,IAAI,EAAC,SAAS,SAAS,IAAI,IAAA,GAC3E,YAAE,sBAAsB,EAAC,KAAK,eAAe,QAAQ,EAAA,CAAE,GAC5D;AAAA,QAEH,YAAY,oBAAC,KAAA,EAAI,IAAI,EAAC,IAAI,GAAG,WAAW,OAAA,GAAU,SAAA,CAAS;AAAA,QAC3D,aAAa,oBAAC,cAAA,EAAa,MAAM,MAAM,OAAO,UAAU,OAAO,MAAM,UAAU,KAAA,CAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGlG;"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const FILE_REJECTION = Object.freeze({ type: "type", size: "size", total: "total", count: "count", content: "content" });
|
|
2
|
+
const FILE_ACCEPT_PRESETS = Object.freeze({
|
|
3
|
+
excel: ".xlsx,.xls",
|
|
4
|
+
csv: ".csv",
|
|
5
|
+
pdf: ".pdf",
|
|
6
|
+
word: ".doc,.docx",
|
|
7
|
+
text: ".txt",
|
|
8
|
+
json: ".json",
|
|
9
|
+
xml: ".xml",
|
|
10
|
+
zip: ".zip,.rar,.7z",
|
|
11
|
+
image: "image/*",
|
|
12
|
+
audio: "audio/*",
|
|
13
|
+
video: "video/*"
|
|
14
|
+
});
|
|
15
|
+
const BYTES_BY_UNIT = Object.freeze({ b: 1, kb: 1e3, mb: 1e6, gb: 1e9 });
|
|
16
|
+
const SIZE_TEXT = /^\s*(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?\s*$/i;
|
|
17
|
+
const ACCEPT_SEPARATOR = ",";
|
|
18
|
+
const WILDCARD_SUFFIX = "/*";
|
|
19
|
+
function resolveAccept(accept) {
|
|
20
|
+
if (!accept) {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
const parts = Array.isArray(accept) ? accept : [accept];
|
|
24
|
+
return parts.map((part) => FILE_ACCEPT_PRESETS[String(part).toLowerCase()] ?? part).join(ACCEPT_SEPARATOR);
|
|
25
|
+
}
|
|
26
|
+
function matchesAccept(file, resolvedAccept) {
|
|
27
|
+
if (!resolvedAccept) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const fileName = file.name.toLowerCase();
|
|
31
|
+
const fileType = (file.type ?? "").toLowerCase();
|
|
32
|
+
return resolvedAccept.split(ACCEPT_SEPARATOR).map((rule) => rule.trim().toLowerCase()).filter(Boolean).some((rule) => {
|
|
33
|
+
if (rule.startsWith(".")) {
|
|
34
|
+
return fileName.endsWith(rule);
|
|
35
|
+
}
|
|
36
|
+
if (rule.endsWith(WILDCARD_SUFFIX)) {
|
|
37
|
+
return fileType.startsWith(rule.slice(0, -1));
|
|
38
|
+
}
|
|
39
|
+
return fileType === rule;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function parseFileSize(size) {
|
|
43
|
+
if (size === void 0 || size === null || size === "") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (typeof size === "number") {
|
|
47
|
+
return size;
|
|
48
|
+
}
|
|
49
|
+
const match = SIZE_TEXT.exec(size);
|
|
50
|
+
if (!match) {
|
|
51
|
+
throw new Error(`«${size}» no es un tamaño: se espera un número de bytes o algo como '10 MB'`);
|
|
52
|
+
}
|
|
53
|
+
return Math.round(Number(match[1]) * BYTES_BY_UNIT[(match[2] ?? "b").toLowerCase()]);
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
FILE_ACCEPT_PRESETS,
|
|
57
|
+
FILE_REJECTION,
|
|
58
|
+
matchesAccept,
|
|
59
|
+
parseFileSize,
|
|
60
|
+
resolveAccept
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=fileRules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileRules.js","sources":["../../src/upload/fileRules.js"],"sourcesContent":["// Las reglas de qué archivo se acepta: su tipo (accept) y su peso (maxSize). Las comparten las dos zonas para soltar\n// archivos del paquete, la de MUI (upload/FileDropzone.jsx) y la de FilePond (filepond/FileDropzone.jsx), para que se\n// configuren igual.\n//\n// accept un nombre del menú ('excel'), una lista (['excel', 'pdf']) o lo de siempre del input ('.xlsx,.xls',\n// 'image/*'); se pueden mezclar: ['excel', '.dat'].\n// maxSize bytes (número) o texto con unidades: '500 KB', '10mb', '1 GB'.\n//\n// OJO: los tamaños van en base 1000 (1 MB = 1,000,000 bytes), igual que formatFileSize y que el navegador en sus\n// descargas. En base 1024 un límite de «10 MB» se pintaría como «10.5 MB».\n//\n// Funciones puras, sin React: function (regla de la casa). Se prueban con node --test (fileRules.test.js).\n\n/**\n * Por qué se rechazó un archivo (lo que recibe onReject de las dos zonas). La de MUI solo rechaza por tipo y por peso;\n * la de FilePond además por el peso de todos juntos, por cantidad y por contenido (validate).\n */\nexport const FILE_REJECTION = Object.freeze({type: 'type', size: 'size', total: 'total', count: 'count', content: 'content'});\n\n/** El menú de tipos comunes, para no memorizar extensiones. */\nexport const FILE_ACCEPT_PRESETS = Object.freeze({\n excel: '.xlsx,.xls',\n csv: '.csv',\n pdf: '.pdf',\n word: '.doc,.docx',\n text: '.txt',\n json: '.json',\n xml: '.xml',\n zip: '.zip,.rar,.7z',\n image: 'image/*',\n audio: 'audio/*',\n video: 'video/*',\n});\n\n// bytes de cada unidad que se puede escribir en maxSize\nconst BYTES_BY_UNIT = Object.freeze({b: 1, kb: 1e3, mb: 1e6, gb: 1e9});\nconst SIZE_TEXT = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb)?\\s*$/i;\nconst ACCEPT_SEPARATOR = ',';\nconst WILDCARD_SUFFIX = '/*';\n\n/**\n * El accept del paquete como lo entiende el input del navegador.\n *\n * @param {string|string[]|undefined} accept 'excel' | ['excel', 'pdf'] | '.xlsx,.xls' | 'image/*'\n * @returns {string|undefined} '.xlsx,.xls,.pdf'; undefined si se acepta todo\n */\nexport function resolveAccept(accept) {\n if (!accept) {\n return undefined;\n }\n const parts = Array.isArray(accept) ? accept : [accept];\n return parts.map((part) => FILE_ACCEPT_PRESETS[String(part).toLowerCase()] ?? part).join(ACCEPT_SEPARATOR);\n}\n\n/**\n * ¿El archivo es de un tipo permitido? Revisa la extensión (.xlsx), el tipo exacto (application/pdf) o la familia\n * (image/*). El input del navegador ya filtra al elegir, pero al soltar un archivo no filtra nada.\n *\n * @param {{name: string, type?: string}} file\n * @param {string|undefined} resolvedAccept lo que regresa resolveAccept\n * @returns {boolean}\n */\nexport function matchesAccept(file, resolvedAccept) {\n if (!resolvedAccept) {\n return true;\n }\n const fileName = file.name.toLowerCase();\n const fileType = (file.type ?? '').toLowerCase();\n return resolvedAccept\n .split(ACCEPT_SEPARATOR)\n .map((rule) => rule.trim().toLowerCase())\n .filter(Boolean)\n .some((rule) => {\n if (rule.startsWith('.')) {\n return fileName.endsWith(rule);\n }\n if (rule.endsWith(WILDCARD_SUFFIX)) {\n return fileType.startsWith(rule.slice(0, -1));\n }\n return fileType === rule;\n });\n}\n\n/**\n * Un tamaño escrito por una persona en bytes: '10 MB' → 10000000.\n *\n * @param {number|string|undefined|null} size\n * @returns {number|null} null si no hay límite\n */\nexport function parseFileSize(size) {\n if (size === undefined || size === null || size === '') {\n return null;\n }\n if (typeof size === 'number') {\n return size;\n }\n const match = SIZE_TEXT.exec(size);\n // guard clause: un límite mal escrito es error de quien configura, no algo que se ignore callado\n if (!match) {\n throw new Error(`«${size}» no es un tamaño: se espera un número de bytes o algo como '10 MB'`);\n }\n return Math.round(Number(match[1]) * BYTES_BY_UNIT[(match[2] ?? 'b').toLowerCase()]);\n}\n"],"names":[],"mappings":"AAiBY,MAAC,iBAAiB,OAAO,OAAO,EAAC,MAAM,QAAQ,MAAM,QAAQ,OAAO,SAAS,OAAO,SAAS,SAAS,UAAS,CAAC;AAGhH,MAAC,sBAAsB,OAAO,OAAO;AAAA,EAC7C,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACX,CAAC;AAGD,MAAM,gBAAgB,OAAO,OAAO,EAAC,GAAG,GAAG,IAAI,KAAK,IAAI,KAAK,IAAI,IAAG,CAAC;AACrE,MAAM,YAAY;AAClB,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AAQjB,SAAS,cAAc,QAAQ;AAClC,MAAI,CAAC,QAAQ;AACT,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACtD,SAAO,MAAM,IAAI,CAAC,SAAS,oBAAoB,OAAO,IAAI,EAAE,YAAW,CAAE,KAAK,IAAI,EAAE,KAAK,gBAAgB;AAC7G;AAUO,SAAS,cAAc,MAAM,gBAAgB;AAChD,MAAI,CAAC,gBAAgB;AACjB,WAAO;AAAA,EACX;AACA,QAAM,WAAW,KAAK,KAAK,YAAW;AACtC,QAAM,YAAY,KAAK,QAAQ,IAAI,YAAW;AAC9C,SAAO,eACF,MAAM,gBAAgB,EACtB,IAAI,CAAC,SAAS,KAAK,KAAI,EAAG,YAAW,CAAE,EACvC,OAAO,OAAO,EACd,KAAK,CAAC,SAAS;AACZ,QAAI,KAAK,WAAW,GAAG,GAAG;AACtB,aAAO,SAAS,SAAS,IAAI;AAAA,IACjC;AACA,QAAI,KAAK,SAAS,eAAe,GAAG;AAChC,aAAO,SAAS,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,IAChD;AACA,WAAO,aAAa;AAAA,EACxB,CAAC;AACT;AAQO,SAAS,cAAc,MAAM;AAChC,MAAI,SAAS,UAAa,SAAS,QAAQ,SAAS,IAAI;AACpD,WAAO;AAAA,EACX;AACA,MAAI,OAAO,SAAS,UAAU;AAC1B,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,UAAU,KAAK,IAAI;AAEjC,MAAI,CAAC,OAAO;AACR,UAAM,IAAI,MAAM,IAAI,IAAI,qEAAqE;AAAA,EACjG;AACA,SAAO,KAAK,MAAM,OAAO,MAAM,CAAC,CAAC,IAAI,eAAe,MAAM,CAAC,KAAK,KAAK,YAAW,CAAE,CAAC;AACvF;"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { assign, setup, fromCallback } from "xstate";
|
|
2
|
+
const resetContext = assign({ progress: null, result: null, problem: null });
|
|
3
|
+
const uploadMachine = setup({
|
|
4
|
+
actors: {
|
|
5
|
+
upload: fromCallback(() => {
|
|
6
|
+
throw new Error("uploadMachine: falta proveer upload con buildUploadActors().");
|
|
7
|
+
})
|
|
8
|
+
},
|
|
9
|
+
guards: {
|
|
10
|
+
// 100% enviado no es terminado: el servidor sigue trabajando
|
|
11
|
+
isFullySent: ({ event }) => event.percent >= 100
|
|
12
|
+
}
|
|
13
|
+
}).createMachine({
|
|
14
|
+
id: "upload",
|
|
15
|
+
initial: "idle",
|
|
16
|
+
context: { progress: null, result: null, problem: null },
|
|
17
|
+
states: {
|
|
18
|
+
idle: {
|
|
19
|
+
on: { UPLOAD: { target: "uploading", actions: resetContext } }
|
|
20
|
+
},
|
|
21
|
+
uploading: {
|
|
22
|
+
invoke: { src: "upload", input: ({ event }) => event.input },
|
|
23
|
+
initial: "sending",
|
|
24
|
+
states: {
|
|
25
|
+
sending: {
|
|
26
|
+
on: {
|
|
27
|
+
PROGRESS: [
|
|
28
|
+
{ guard: "isFullySent", target: "processing", actions: assign({ progress: ({ event }) => event.percent }) },
|
|
29
|
+
{ actions: assign({ progress: ({ event }) => event.percent }) }
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
// cuerpo entregado; esperando la respuesta del servidor (progreso indeterminado)
|
|
34
|
+
processing: {}
|
|
35
|
+
},
|
|
36
|
+
on: {
|
|
37
|
+
DONE: { target: "done", actions: assign({ result: ({ event }) => event.output, progress: null }) },
|
|
38
|
+
FAIL: { target: "failed", actions: assign({ problem: ({ event }) => event.problem, progress: null }) },
|
|
39
|
+
// salir de uploading DETIENE el actor y su cleanup aborta la petición de verdad (no solo la UI)
|
|
40
|
+
CANCEL: { target: "cancelled", actions: assign({ progress: null }) }
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
done: {
|
|
44
|
+
on: { UPLOAD: { target: "uploading", actions: resetContext }, RESET: { target: "idle", actions: resetContext } }
|
|
45
|
+
},
|
|
46
|
+
failed: {
|
|
47
|
+
on: { UPLOAD: { target: "uploading", actions: resetContext }, RESET: { target: "idle", actions: resetContext } }
|
|
48
|
+
},
|
|
49
|
+
// cancelado por la persona: el archivo sigue elegido y se puede reintentar o limpiar
|
|
50
|
+
cancelled: {
|
|
51
|
+
on: { UPLOAD: { target: "uploading", actions: resetContext }, RESET: { target: "idle", actions: resetContext } }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
function buildUploadActors(uploadFile) {
|
|
56
|
+
return {
|
|
57
|
+
upload: fromCallback(({ input, sendBack }) => {
|
|
58
|
+
const abortController = new AbortController();
|
|
59
|
+
uploadFile(input, (percent) => sendBack({ type: "PROGRESS", percent }), abortController.signal).then((output) => sendBack({ type: "DONE", output })).catch((problem) => {
|
|
60
|
+
if (problem?.name === "AbortError" || problem?.name === "CanceledError") {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
sendBack({ type: "FAIL", problem });
|
|
64
|
+
});
|
|
65
|
+
return () => abortController.abort();
|
|
66
|
+
})
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
buildUploadActors,
|
|
71
|
+
uploadMachine
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=uploadMachine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadMachine.js","sources":["../../src/upload/uploadMachine.js"],"sourcesContent":["// Máquina del flujo de SUBIDA de un archivo (compañera de una zona para soltar archivos).\n//\n// idle ──UPLOAD──▶ uploading.sending ──(PROGRESS 100%)──▶ uploading.processing\n// │ │\n// ├────────────── DONE ────────────────────┼──▶ done\n// ├────────────── FAIL ────────────────────┴──▶ failed\n// └─── CANCEL (el cleanup aborta la petición) ──▶ cancelled\n//\n// Por qué máquina y no cuatro useState: los estados imposibles dejan de existir (no hay \"resultado y error a la vez\"\n// ni \"progreso sin carga\") y el flujo sending → processing → done queda DECLARADO, no regado en callbacks.\n//\n// Contexto: progress (% de bytes enviados; null fuera de sending) · result (lo que resuelva la subida) · problem (el\n// error RFC 9457 del rechazo, normalmente un ApiError).\n//\n// Uso:\n// const [uploadSnapshot, sendToUpload] = useMachine(uploadMachine.provide({actors: buildUploadActors(uploadFile)}));\n// sendToUpload({type: 'UPLOAD', input: {file}});\n//\n// Pura: solo importa xstate.\nimport {assign, fromCallback, setup} from 'xstate';\n\nconst resetContext = assign({progress: null, result: null, problem: null});\n\nexport const uploadMachine = setup({\n actors: {\n upload: fromCallback(() => {\n throw new Error('uploadMachine: falta proveer upload con buildUploadActors().');\n }),\n },\n guards: {\n // 100% enviado no es terminado: el servidor sigue trabajando\n isFullySent: ({event}) => event.percent >= 100,\n },\n}).createMachine({\n id: 'upload',\n initial: 'idle',\n context: {progress: null, result: null, problem: null},\n states: {\n idle: {\n on: {UPLOAD: {target: 'uploading', actions: resetContext}},\n },\n uploading: {\n invoke: {src: 'upload', input: ({event}) => event.input},\n initial: 'sending',\n states: {\n sending: {\n on: {\n PROGRESS: [\n {guard: 'isFullySent', target: 'processing', actions: assign({progress: ({event}) => event.percent})},\n {actions: assign({progress: ({event}) => event.percent})},\n ],\n },\n },\n // cuerpo entregado; esperando la respuesta del servidor (progreso indeterminado)\n processing: {},\n },\n on: {\n DONE: {target: 'done', actions: assign({result: ({event}) => event.output, progress: null})},\n FAIL: {target: 'failed', actions: assign({problem: ({event}) => event.problem, progress: null})},\n // salir de uploading DETIENE el actor y su cleanup aborta la petición de verdad (no solo la UI)\n CANCEL: {target: 'cancelled', actions: assign({progress: null})},\n },\n },\n done: {\n on: {UPLOAD: {target: 'uploading', actions: resetContext}, RESET: {target: 'idle', actions: resetContext}},\n },\n failed: {\n on: {UPLOAD: {target: 'uploading', actions: resetContext}, RESET: {target: 'idle', actions: resetContext}},\n },\n // cancelado por la persona: el archivo sigue elegido y se puede reintentar o limpiar\n cancelled: {\n on: {UPLOAD: {target: 'uploading', actions: resetContext}, RESET: {target: 'idle', actions: resetContext}},\n },\n },\n});\n\n/**\n * El actor upload a partir de la función de la pantalla.\n *\n * fromCallback y no fromPromise: hacen falta EVENTOS a medio camino (el avance) y un CLEANUP que aborte la petición.\n *\n * @param {(input: object, onProgress: (percent: number) => void, signal: AbortSignal) => Promise<unknown>} uploadFile\n */\nexport function buildUploadActors(uploadFile) {\n return {\n upload: fromCallback(({input, sendBack}) => {\n const abortController = new AbortController();\n uploadFile(input, (percent) => sendBack({type: 'PROGRESS', percent}), abortController.signal)\n .then((output) => sendBack({type: 'DONE', output}))\n .catch((problem) => {\n // aborto esperado (CANCEL): la máquina ya cambió de estado sola\n if (problem?.name === 'AbortError' || problem?.name === 'CanceledError') {\n return;\n }\n sendBack({type: 'FAIL', problem});\n });\n return () => abortController.abort();\n }),\n };\n}\n"],"names":[],"mappings":";AAqBA,MAAM,eAAe,OAAO,EAAC,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAI,CAAC;AAE7D,MAAC,gBAAgB,MAAM;AAAA,EAC/B,QAAQ;AAAA,IACJ,QAAQ,aAAa,MAAM;AACvB,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAClF,CAAC;AAAA,EACT;AAAA,EACI,QAAQ;AAAA;AAAA,IAEJ,aAAa,CAAC,EAAC,MAAK,MAAM,MAAM,WAAW;AAAA,EACnD;AACA,CAAC,EAAE,cAAc;AAAA,EACb,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,SAAS,EAAC,UAAU,MAAM,QAAQ,MAAM,SAAS,KAAI;AAAA,EACrD,QAAQ;AAAA,IACJ,MAAM;AAAA,MACF,IAAI,EAAC,QAAQ,EAAC,QAAQ,aAAa,SAAS,aAAY,EAAC;AAAA,IACrE;AAAA,IACQ,WAAW;AAAA,MACP,QAAQ,EAAC,KAAK,UAAU,OAAO,CAAC,EAAC,MAAK,MAAM,MAAM,MAAK;AAAA,MACvD,SAAS;AAAA,MACT,QAAQ;AAAA,QACJ,SAAS;AAAA,UACL,IAAI;AAAA,YACA,UAAU;AAAA,cACN,EAAC,OAAO,eAAe,QAAQ,cAAc,SAAS,OAAO,EAAC,UAAU,CAAC,EAAC,MAAK,MAAM,MAAM,QAAO,CAAC,EAAC;AAAA,cACpG,EAAC,SAAS,OAAO,EAAC,UAAU,CAAC,EAAC,MAAK,MAAM,MAAM,QAAO,CAAC,EAAC;AAAA,YACpF;AAAA,UACA;AAAA,QACA;AAAA;AAAA,QAEgB,YAAY,CAAA;AAAA,MAC5B;AAAA,MACY,IAAI;AAAA,QACA,MAAM,EAAC,QAAQ,QAAQ,SAAS,OAAO,EAAC,QAAQ,CAAC,EAAC,MAAK,MAAM,MAAM,QAAQ,UAAU,KAAI,CAAC,EAAC;AAAA,QAC3F,MAAM,EAAC,QAAQ,UAAU,SAAS,OAAO,EAAC,SAAS,CAAC,EAAC,MAAK,MAAM,MAAM,SAAS,UAAU,KAAI,CAAC,EAAC;AAAA;AAAA,QAE/F,QAAQ,EAAC,QAAQ,aAAa,SAAS,OAAO,EAAC,UAAU,KAAI,CAAC,EAAC;AAAA,MAC/E;AAAA,IACA;AAAA,IACQ,MAAM;AAAA,MACF,IAAI,EAAC,QAAQ,EAAC,QAAQ,aAAa,SAAS,aAAY,GAAG,OAAO,EAAC,QAAQ,QAAQ,SAAS,aAAY,EAAC;AAAA,IACrH;AAAA,IACQ,QAAQ;AAAA,MACJ,IAAI,EAAC,QAAQ,EAAC,QAAQ,aAAa,SAAS,aAAY,GAAG,OAAO,EAAC,QAAQ,QAAQ,SAAS,aAAY,EAAC;AAAA,IACrH;AAAA;AAAA,IAEQ,WAAW;AAAA,MACP,IAAI,EAAC,QAAQ,EAAC,QAAQ,aAAa,SAAS,aAAY,GAAG,OAAO,EAAC,QAAQ,QAAQ,SAAS,aAAY,EAAC;AAAA,IACrH;AAAA,EACA;AACA,CAAC;AASM,SAAS,kBAAkB,YAAY;AAC1C,SAAO;AAAA,IACH,QAAQ,aAAa,CAAC,EAAC,OAAO,SAAQ,MAAM;AACxC,YAAM,kBAAkB,IAAI,gBAAe;AAC3C,iBAAW,OAAO,CAAC,YAAY,SAAS,EAAC,MAAM,YAAY,QAAO,CAAC,GAAG,gBAAgB,MAAM,EACvF,KAAK,CAAC,WAAW,SAAS,EAAC,MAAM,QAAQ,OAAM,CAAC,CAAC,EACjD,MAAM,CAAC,YAAY;AAEhB,YAAI,SAAS,SAAS,gBAAgB,SAAS,SAAS,iBAAiB;AACrE;AAAA,QACJ;AACA,iBAAS,EAAC,MAAM,QAAQ,QAAO,CAAC;AAAA,MACpC,CAAC;AACL,aAAO,MAAM,gBAAgB,MAAK;AAAA,IACtC,CAAC;AAAA,EACT;AACA;"}
|