@proef/common-ui 0.0.0-stage → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/assets/imagem-6ee08a37d9.png +0 -0
- package/dist/esm/assets/imagem-6fcae1b87b.png +0 -0
- package/dist/esm/index.js +324 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/legacy/assets/svgs/proef-footer-logo.svg.js +5 -0
- package/dist/esm/legacy/assets/svgs/proef-footer-logo.svg.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Acronym/Acronym.css +43 -0
- package/dist/esm/legacy/components/atoms/Acronym/Acronym.js +31 -0
- package/dist/esm/legacy/components/atoms/Acronym/Acronym.js.map +1 -0
- package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.css +36 -0
- package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.js +57 -0
- package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.css +218 -0
- package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.js +50 -0
- package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.css +56 -0
- package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.js +34 -0
- package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Button/variants/IconButton.css +74 -0
- package/dist/esm/legacy/components/atoms/Button/variants/IconButton.js +29 -0
- package/dist/esm/legacy/components/atoms/Button/variants/IconButton.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Button/variants/MainButton.css +99 -0
- package/dist/esm/legacy/components/atoms/Button/variants/MainButton.js +36 -0
- package/dist/esm/legacy/components/atoms/Button/variants/MainButton.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Button/variants/TabButton.css +63 -0
- package/dist/esm/legacy/components/atoms/Button/variants/TabButton.js +21 -0
- package/dist/esm/legacy/components/atoms/Button/variants/TabButton.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Field/Field.css +171 -0
- package/dist/esm/legacy/components/atoms/Field/Field.js +70 -0
- package/dist/esm/legacy/components/atoms/Field/Field.js.map +1 -0
- package/dist/esm/legacy/components/atoms/IconText/IconText.css +10 -0
- package/dist/esm/legacy/components/atoms/IconText/IconText.js +25 -0
- package/dist/esm/legacy/components/atoms/IconText/IconText.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Icone/Icone.js +24 -0
- package/dist/esm/legacy/components/atoms/Icone/Icone.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Input/Input.css +12 -0
- package/dist/esm/legacy/components/atoms/Input/Input.js +28 -0
- package/dist/esm/legacy/components/atoms/Input/Input.js.map +1 -0
- package/dist/esm/legacy/components/atoms/InputData/InputData.js +25 -0
- package/dist/esm/legacy/components/atoms/InputData/InputData.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Label/Label.css +192 -0
- package/dist/esm/legacy/components/atoms/Label/Label.js +13 -0
- package/dist/esm/legacy/components/atoms/Label/Label.js.map +1 -0
- package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.css +40 -0
- package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.js +21 -0
- package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Select/Select.css +16 -0
- package/dist/esm/legacy/components/atoms/Select/Select.js +27 -0
- package/dist/esm/legacy/components/atoms/Select/Select.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Stack/Stack.js +20 -0
- package/dist/esm/legacy/components/atoms/Stack/Stack.js.map +1 -0
- package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.css +36 -0
- package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.js +29 -0
- package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.js.map +1 -0
- package/dist/esm/legacy/components/charts/Charts.js +349 -0
- package/dist/esm/legacy/components/charts/Charts.js.map +1 -0
- package/dist/esm/legacy/components/layout/AppLauncher/AppLauncher.js +100 -0
- package/dist/esm/legacy/components/layout/AppLauncher/AppLauncher.js.map +1 -0
- package/dist/esm/legacy/components/layout/Error/Error.css +20 -0
- package/dist/esm/legacy/components/layout/Error/Error.js +12 -0
- package/dist/esm/legacy/components/layout/Error/Error.js.map +1 -0
- package/dist/esm/legacy/components/layout/Footer/Footer.css +42 -0
- package/dist/esm/legacy/components/layout/Footer/Footer.js +23 -0
- package/dist/esm/legacy/components/layout/Footer/Footer.js.map +1 -0
- package/dist/esm/legacy/components/layout/Header/Header.css +66 -0
- package/dist/esm/legacy/components/layout/Header/Header.js +37 -0
- package/dist/esm/legacy/components/layout/Header/Header.js.map +1 -0
- package/dist/esm/legacy/components/layout/Layout/Layout.js +31 -0
- package/dist/esm/legacy/components/layout/Layout/Layout.js.map +1 -0
- package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js +38 -0
- package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js.map +1 -0
- package/dist/esm/legacy/components/layout/Spinner/Spinner.css +10 -0
- package/dist/esm/legacy/components/layout/Spinner/Spinner.js +19 -0
- package/dist/esm/legacy/components/layout/Spinner/Spinner.js.map +1 -0
- package/dist/esm/legacy/components/layout/TopBar/TopBar.js +34 -0
- package/dist/esm/legacy/components/layout/TopBar/TopBar.js.map +1 -0
- package/dist/esm/legacy/components/layout/TopNavLayout/TopNavLayout.js +82 -0
- package/dist/esm/legacy/components/layout/TopNavLayout/TopNavLayout.js.map +1 -0
- package/dist/esm/legacy/components/molecules/BarraValor/BarraValor.js +25 -0
- package/dist/esm/legacy/components/molecules/BarraValor/BarraValor.js.map +1 -0
- package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js +246 -0
- package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js.map +1 -0
- package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.css +3 -0
- package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.js +33 -0
- package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.js.map +1 -0
- package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +1259 -0
- package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -0
- package/dist/esm/legacy/components/molecules/FileDropzone/FileDropzone.js +68 -0
- package/dist/esm/legacy/components/molecules/FileDropzone/FileDropzone.js.map +1 -0
- package/dist/esm/legacy/components/molecules/LanguageSwitcher/LanguageSwitcher.js +96 -0
- package/dist/esm/legacy/components/molecules/LanguageSwitcher/LanguageSwitcher.js.map +1 -0
- package/dist/esm/legacy/components/molecules/Modal/Modal.css +54 -0
- package/dist/esm/legacy/components/molecules/Modal/Modal.js +64 -0
- package/dist/esm/legacy/components/molecules/Modal/Modal.js.map +1 -0
- package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.css +97 -0
- package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.js +113 -0
- package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.js.map +1 -0
- package/dist/esm/legacy/components/molecules/PhotoAvatar/PhotoAvatar.js +114 -0
- package/dist/esm/legacy/components/molecules/PhotoAvatar/PhotoAvatar.js.map +1 -0
- package/dist/esm/legacy/components/molecules/SeletorPesquisa/SeletorPesquisa.js +116 -0
- package/dist/esm/legacy/components/molecules/SeletorPesquisa/SeletorPesquisa.js.map +1 -0
- package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.css +69 -0
- package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.js +62 -0
- package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.js.map +1 -0
- package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js +23 -0
- package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js.map +1 -0
- package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js +162 -0
- package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js.map +1 -0
- package/dist/esm/legacy/components/organisms/Kanban/Kanban.js +88 -0
- package/dist/esm/legacy/components/organisms/Kanban/Kanban.js.map +1 -0
- package/dist/esm/legacy/components/organisms/LazyLoader/LazyLoader.js +87 -0
- package/dist/esm/legacy/components/organisms/LazyLoader/LazyLoader.js.map +1 -0
- package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js +427 -0
- package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js.map +1 -0
- package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js +218 -0
- package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js.map +1 -0
- package/dist/esm/legacy/components/organisms/PesquisaRapida/PesquisaRapida.js +147 -0
- package/dist/esm/legacy/components/organisms/PesquisaRapida/PesquisaRapida.js.map +1 -0
- package/dist/esm/legacy/components/organisms/Profile/Profile.css +26 -0
- package/dist/esm/legacy/components/organisms/Profile/Profile.js +26 -0
- package/dist/esm/legacy/components/organisms/Profile/Profile.js.map +1 -0
- package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js +79 -0
- package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js.map +1 -0
- package/dist/esm/legacy/components/organisms/Selector/Selector.css +87 -0
- package/dist/esm/legacy/components/organisms/Selector/Selector.js +181 -0
- package/dist/esm/legacy/components/organisms/Selector/Selector.js.map +1 -0
- package/dist/esm/legacy/components/organisms/StatusMatrix/StatusMatrix.js +115 -0
- package/dist/esm/legacy/components/organisms/StatusMatrix/StatusMatrix.js.map +1 -0
- package/dist/esm/legacy/components/ui/Avisos.js +33 -0
- package/dist/esm/legacy/components/ui/Avisos.js.map +1 -0
- package/dist/esm/legacy/components/ui/BarraAcoes.js +19 -0
- package/dist/esm/legacy/components/ui/BarraAcoes.js.map +1 -0
- package/dist/esm/legacy/components/ui/CabecalhoPagina.js +19 -0
- package/dist/esm/legacy/components/ui/CabecalhoPagina.js.map +1 -0
- package/dist/esm/legacy/components/ui/CaixaPesquisa.js +9 -0
- package/dist/esm/legacy/components/ui/CaixaPesquisa.js.map +1 -0
- package/dist/esm/legacy/components/ui/Confirmacao.js +34 -0
- package/dist/esm/legacy/components/ui/Confirmacao.js.map +1 -0
- package/dist/esm/legacy/components/ui/Escolha.js +41 -0
- package/dist/esm/legacy/components/ui/Escolha.js.map +1 -0
- package/dist/esm/legacy/components/ui/FaixaAviso.js +19 -0
- package/dist/esm/legacy/components/ui/FaixaAviso.js.map +1 -0
- package/dist/esm/legacy/components/ui/MenuAcoes.js +116 -0
- package/dist/esm/legacy/components/ui/MenuAcoes.js.map +1 -0
- package/dist/esm/legacy/components/ui/Passos.js +32 -0
- package/dist/esm/legacy/components/ui/Passos.js.map +1 -0
- package/dist/esm/legacy/components/ui/Separadores.js +17 -0
- package/dist/esm/legacy/components/ui/Separadores.js.map +1 -0
- package/dist/esm/legacy/components/ui/TextoAuto.js +33 -0
- package/dist/esm/legacy/components/ui/TextoAuto.js.map +1 -0
- package/dist/esm/legacy/components/ui/Ui.js +263 -0
- package/dist/esm/legacy/components/ui/Ui.js.map +1 -0
- package/dist/esm/legacy/components/ui/painelFlutuante.js +13 -0
- package/dist/esm/legacy/components/ui/painelFlutuante.js.map +1 -0
- package/dist/esm/legacy/constants/Colors.js +33 -0
- package/dist/esm/legacy/constants/Colors.js.map +1 -0
- package/dist/esm/legacy/constants/FontWeight.js +35 -0
- package/dist/esm/legacy/constants/FontWeight.js.map +1 -0
- package/dist/esm/legacy/contexts/apiContext.js +25 -0
- package/dist/esm/legacy/contexts/apiContext.js.map +1 -0
- package/dist/esm/legacy/contexts/authContext.js +85 -0
- package/dist/esm/legacy/contexts/authContext.js.map +1 -0
- package/dist/esm/legacy/contexts/socketContext.js +34 -0
- package/dist/esm/legacy/contexts/socketContext.js.map +1 -0
- package/dist/esm/legacy/hooks/useApi.js +62 -0
- package/dist/esm/legacy/hooks/useApi.js.map +1 -0
- package/dist/esm/legacy/pages/Error/ErrorPage.css +18 -0
- package/dist/esm/legacy/pages/Error/ErrorPage.js +50 -0
- package/dist/esm/legacy/pages/Error/ErrorPage.js.map +1 -0
- package/dist/esm/legacy/pages/Loading/Loading.css +46 -0
- package/dist/esm/legacy/pages/Loading/Loading.js +9 -0
- package/dist/esm/legacy/pages/Loading/Loading.js.map +1 -0
- package/dist/esm/legacy/pages/Login/LoginScreen.js +46 -0
- package/dist/esm/legacy/pages/Login/LoginScreen.js.map +1 -0
- package/dist/esm/legacy/pages/NotFound/NotFound.css +21 -0
- package/dist/esm/legacy/pages/NotFound/NotFound.js +24 -0
- package/dist/esm/legacy/pages/NotFound/NotFound.js.map +1 -0
- package/dist/esm/legacy/styles/themes/ButtonThemes.js +28 -0
- package/dist/esm/legacy/styles/themes/ButtonThemes.js.map +1 -0
- package/dist/esm/legacy/styles/utils/utils.js +7 -0
- package/dist/esm/legacy/styles/utils/utils.js.map +1 -0
- package/dist/esm/legacy/utils/stringUtils.js +35 -0
- package/dist/esm/legacy/utils/stringUtils.js.map +1 -0
- package/dist/esm/v1/capabilities/documents/QRScanning/scanQRCodes.js +157 -0
- package/dist/esm/v1/capabilities/documents/QRScanning/scanQRCodes.js.map +1 -0
- package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js +52 -0
- package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js.map +1 -0
- package/dist/esm/v1/capabilities/theme/useIsDark.js +15 -0
- package/dist/esm/v1/capabilities/theme/useIsDark.js.map +1 -0
- package/dist/esm/v1/capabilities/theme/useTheme.js +66 -0
- package/dist/esm/v1/capabilities/theme/useTheme.js.map +1 -0
- package/dist/esm/v1/components/data/Pagination/Pagination.js +66 -0
- package/dist/esm/v1/components/data/Pagination/Pagination.js.map +1 -0
- package/dist/esm/v1/components/documents/DocumentDropzone/DocumentDropzone.js +307 -0
- package/dist/esm/v1/components/documents/DocumentDropzone/DocumentDropzone.js.map +1 -0
- package/dist/esm/v1/components/feedback/Toast/Toast.js +105 -0
- package/dist/esm/v1/components/feedback/Toast/Toast.js.map +1 -0
- package/dist/esm/v1/components/forms/FormField/FormField.js +59 -0
- package/dist/esm/v1/components/forms/FormField/FormField.js.map +1 -0
- package/dist/esm/v1/components/forms/FormGrid/FormGrid.js +20 -0
- package/dist/esm/v1/components/forms/FormGrid/FormGrid.js.map +1 -0
- package/dist/esm/v1/components/layout/AppShell/AppShell.js +89 -0
- package/dist/esm/v1/components/layout/AppShell/AppShell.js.map +1 -0
- package/dist/esm/v1/components/layout/Page/Page.js +16 -0
- package/dist/esm/v1/components/layout/Page/Page.js.map +1 -0
- package/dist/esm/v1/components/layout/PageHeader/PageHeader.js +26 -0
- package/dist/esm/v1/components/layout/PageHeader/PageHeader.js.map +1 -0
- package/dist/esm/v1/components/layout/SideNav/SideNav.js +287 -0
- package/dist/esm/v1/components/layout/SideNav/SideNav.js.map +1 -0
- package/dist/esm/v1/components/layout/navTrail.js +19 -0
- package/dist/esm/v1/components/layout/navTrail.js.map +1 -0
- package/dist/esm/v1/components/lists/ListFinder/ListFinder.js +271 -0
- package/dist/esm/v1/components/lists/ListFinder/ListFinder.js.map +1 -0
- package/dist/esm/v1/components/lists/ListFinder/labels.js +18 -0
- package/dist/esm/v1/components/lists/ListFinder/labels.js.map +1 -0
- package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js +95 -0
- package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js.map +1 -0
- package/dist/esm/v1/components/overlays/Dialog/Dialog.js +141 -0
- package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -0
- package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js +98 -0
- package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js.map +1 -0
- package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js +84 -0
- package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js.map +1 -0
- package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js +33 -0
- package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js.map +1 -0
- package/dist/esm/v1/components/overlays/topLayer.js +28 -0
- package/dist/esm/v1/components/overlays/topLayer.js.map +1 -0
- package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js +50 -0
- package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js.map +1 -0
- package/dist/esm/v1/components/tables/Table/ColumnFilter.js +292 -0
- package/dist/esm/v1/components/tables/Table/ColumnFilter.js.map +1 -0
- package/dist/esm/v1/components/tables/Table/OverlayScrollbar.js +129 -0
- package/dist/esm/v1/components/tables/Table/OverlayScrollbar.js.map +1 -0
- package/dist/esm/v1/components/tables/Table/Table.js +1410 -0
- package/dist/esm/v1/components/tables/Table/Table.js.map +1 -0
- package/dist/esm/v1/components/tables/Table/labels.js +44 -0
- package/dist/esm/v1/components/tables/Table/labels.js.map +1 -0
- package/dist/esm/v1/foundations/icons/icons.js +227 -0
- package/dist/esm/v1/foundations/icons/icons.js.map +1 -0
- package/dist/esm/v1/primitives/buttons/Button/Button.js +41 -0
- package/dist/esm/v1/primitives/buttons/Button/Button.js.map +1 -0
- package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js +65 -0
- package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js.map +1 -0
- package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js +10 -0
- package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js.map +1 -0
- package/dist/esm/v1/primitives/display/Avatar/Avatar.js +120 -0
- package/dist/esm/v1/primitives/display/Avatar/Avatar.js.map +1 -0
- package/dist/esm/v1/primitives/display/Badge/Badge.js +23 -0
- package/dist/esm/v1/primitives/display/Badge/Badge.js.map +1 -0
- package/dist/esm/v1/primitives/display/Card/Card.js +22 -0
- package/dist/esm/v1/primitives/display/Card/Card.js.map +1 -0
- package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js +45 -0
- package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js.map +1 -0
- package/dist/esm/v1/primitives/display/Kpi/Kpi.js +103 -0
- package/dist/esm/v1/primitives/display/Kpi/Kpi.js.map +1 -0
- package/dist/esm/v1/primitives/display/Notice/Notice.js +71 -0
- package/dist/esm/v1/primitives/display/Notice/Notice.js.map +1 -0
- package/dist/esm/v1/primitives/display/Person/Person.js +14 -0
- package/dist/esm/v1/primitives/display/Person/Person.js.map +1 -0
- package/dist/esm/v1/primitives/display/RichText/RichText.js +12 -0
- package/dist/esm/v1/primitives/display/RichText/RichText.js.map +1 -0
- package/dist/esm/v1/primitives/display/RichText/utils.js +56 -0
- package/dist/esm/v1/primitives/display/RichText/utils.js.map +1 -0
- package/dist/esm/v1/primitives/documents/PdfPreview/PdfPreview.js +137 -0
- package/dist/esm/v1/primitives/documents/PdfPreview/PdfPreview.js.map +1 -0
- package/dist/esm/v1/primitives/documents/PdfPreview/pdfjs.js +26 -0
- package/dist/esm/v1/primitives/documents/PdfPreview/pdfjs.js.map +1 -0
- package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js +37 -0
- package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js.map +1 -0
- package/dist/esm/v1/primitives/forms/DateInput/DateInput.js +150 -0
- package/dist/esm/v1/primitives/forms/DateInput/DateInput.js.map +1 -0
- package/dist/esm/v1/primitives/forms/LookupInput/LookupInput.js +37 -0
- package/dist/esm/v1/primitives/forms/LookupInput/LookupInput.js.map +1 -0
- package/dist/esm/v1/primitives/forms/RichTextEditor/RichTextEditor.js +253 -0
- package/dist/esm/v1/primitives/forms/RichTextEditor/RichTextEditor.js.map +1 -0
- package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js +43 -0
- package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js.map +1 -0
- package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js +207 -0
- package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -0
- package/dist/esm/v1/primitives/forms/TextArea/TextArea.js +75 -0
- package/dist/esm/v1/primitives/forms/TextArea/TextArea.js.map +1 -0
- package/dist/esm/v1/primitives/forms/TextInput/TextInput.js +21 -0
- package/dist/esm/v1/primitives/forms/TextInput/TextInput.js.map +1 -0
- package/dist/esm/v1/primitives/forms/field.js +14 -0
- package/dist/esm/v1/primitives/forms/field.js.map +1 -0
- package/dist/esm/v1/primitives/lists/ListItem/ListItem.js +73 -0
- package/dist/esm/v1/primitives/lists/ListItem/ListItem.js.map +1 -0
- package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js +96 -0
- package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js.map +1 -0
- package/dist/style.components.css +3 -0
- package/dist/style.css +2 -0
- package/dist/style.layered.css +2 -0
- package/dist/tailwind.css +24 -0
- package/dist/theme-boot.js +1 -0
- package/dist/theme.css +265 -0
- package/dist/tokens.css +121 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/legacy/components/atoms/Acronym/Acronym.d.ts +11 -0
- package/dist/types/legacy/components/atoms/BreadCrumb/BreadCrumb.d.ts +9 -0
- package/dist/types/legacy/components/atoms/Button/variants/ActionButton.d.ts +14 -0
- package/dist/types/legacy/components/atoms/Button/variants/FilterButton.d.ts +12 -0
- package/dist/types/legacy/components/atoms/Button/variants/IconButton.d.ts +22 -0
- package/dist/types/legacy/components/atoms/Button/variants/MainButton.d.ts +17 -0
- package/dist/types/legacy/components/atoms/Button/variants/TabButton.d.ts +14 -0
- package/dist/types/legacy/components/atoms/Field/Field.d.ts +15 -0
- package/dist/types/legacy/components/atoms/IconText/IconText.d.ts +12 -0
- package/dist/types/legacy/components/atoms/Icone/Icone.d.ts +31 -0
- package/dist/types/legacy/components/atoms/Input/Input.d.ts +13 -0
- package/dist/types/legacy/components/atoms/InputData/InputData.d.ts +38 -0
- package/dist/types/legacy/components/atoms/Label/Label.d.ts +11 -0
- package/dist/types/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.d.ts +10 -0
- package/dist/types/legacy/components/atoms/SearchSelect/SearchSelect.d.ts +15 -0
- package/dist/types/legacy/components/atoms/Select/Select.d.ts +16 -0
- package/dist/types/legacy/components/atoms/Stack/Stack.d.ts +9 -0
- package/dist/types/legacy/components/atoms/Switch/CustomSwitch.d.ts +12 -0
- package/dist/types/legacy/components/charts/Charts.d.ts +149 -0
- package/dist/types/legacy/components/layout/AppLauncher/AppLauncher.d.ts +35 -0
- package/dist/types/legacy/components/layout/Error/Error.d.ts +8 -0
- package/dist/types/legacy/components/layout/Footer/Footer.d.ts +11 -0
- package/dist/types/legacy/components/layout/Header/Header.d.ts +16 -0
- package/dist/types/legacy/components/layout/Layout/Layout.d.ts +35 -0
- package/dist/types/legacy/components/layout/Sidebar/Sidebar.d.ts +56 -0
- package/dist/types/legacy/components/layout/Spinner/Spinner.d.ts +5 -0
- package/dist/types/legacy/components/layout/TopBar/TopBar.d.ts +10 -0
- package/dist/types/legacy/components/layout/TopNavLayout/TopNavLayout.d.ts +43 -0
- package/dist/types/legacy/components/molecules/BarraValor/BarraValor.d.ts +28 -0
- package/dist/types/legacy/components/molecules/ColumnPicker/ColumnPicker.d.ts +116 -0
- package/dist/types/legacy/components/molecules/ConfirmModal/ConfirmModal.d.ts +13 -0
- package/dist/types/legacy/components/molecules/DataTable/DataTable.d.ts +344 -0
- package/dist/types/legacy/components/molecules/FileDropzone/FileDropzone.d.ts +25 -0
- package/dist/types/legacy/components/molecules/LanguageSwitcher/LanguageSwitcher.d.ts +33 -0
- package/dist/types/legacy/components/molecules/Modal/Modal.d.ts +22 -0
- package/dist/types/legacy/components/molecules/MultiTagPicker/MultiTagPicker.d.ts +25 -0
- package/dist/types/legacy/components/molecules/PhotoAvatar/PhotoAvatar.d.ts +34 -0
- package/dist/types/legacy/components/molecules/SeletorPesquisa/SeletorPesquisa.d.ts +48 -0
- package/dist/types/legacy/components/molecules/Sidebar/Sidebar.d.ts +12 -0
- package/dist/types/legacy/components/molecules/UserAvatar/UserAvatar.d.ts +19 -0
- package/dist/types/legacy/components/organisms/GanttChart/GanttChart.d.ts +62 -0
- package/dist/types/legacy/components/organisms/Kanban/Kanban.d.ts +44 -0
- package/dist/types/legacy/components/organisms/LazyLoader/LazyLoader.d.ts +16 -0
- package/dist/types/legacy/components/organisms/MapaPontos/MapaPontos.d.ts +206 -0
- package/dist/types/legacy/components/organisms/PainelFiltros/PainelFiltros.d.ts +69 -0
- package/dist/types/legacy/components/organisms/PesquisaRapida/PesquisaRapida.d.ts +53 -0
- package/dist/types/legacy/components/organisms/Profile/Profile.d.ts +12 -0
- package/dist/types/legacy/components/organisms/RoleGuard/RoleGuard.d.ts +37 -0
- package/dist/types/legacy/components/organisms/Selector/Selector.d.ts +20 -0
- package/dist/types/legacy/components/organisms/StatusMatrix/StatusMatrix.d.ts +54 -0
- package/dist/types/legacy/components/ui/Avisos.d.ts +33 -0
- package/dist/types/legacy/components/ui/BarraAcoes.d.ts +19 -0
- package/dist/types/legacy/components/ui/CabecalhoPagina.d.ts +23 -0
- package/dist/types/legacy/components/ui/CaixaPesquisa.d.ts +14 -0
- package/dist/types/legacy/components/ui/Confirmacao.d.ts +24 -0
- package/dist/types/legacy/components/ui/Escolha.d.ts +35 -0
- package/dist/types/legacy/components/ui/FaixaAviso.d.ts +19 -0
- package/dist/types/legacy/components/ui/MenuAcoes.d.ts +31 -0
- package/dist/types/legacy/components/ui/Passos.d.ts +31 -0
- package/dist/types/legacy/components/ui/Separadores.d.ts +19 -0
- package/dist/types/legacy/components/ui/TextoAuto.d.ts +23 -0
- package/dist/types/legacy/components/ui/Ui.d.ts +181 -0
- package/dist/types/legacy/components/ui/painelFlutuante.d.ts +16 -0
- package/dist/types/legacy/constants/Colors.d.ts +28 -0
- package/dist/types/legacy/constants/FontWeight.d.ts +9 -0
- package/dist/types/legacy/contexts/apiContext.d.ts +10 -0
- package/dist/types/legacy/contexts/authContext.d.ts +31 -0
- package/dist/types/legacy/contexts/socketContext.d.ts +16 -0
- package/dist/types/legacy/exports/assets.d.ts +0 -0
- package/dist/types/legacy/exports/atoms.d.ts +19 -0
- package/dist/types/legacy/exports/charts.d.ts +2 -0
- package/dist/types/legacy/exports/constants.d.ts +0 -0
- package/dist/types/legacy/exports/contexts.d.ts +3 -0
- package/dist/types/legacy/exports/hooks.d.ts +4 -0
- package/dist/types/legacy/exports/layout.d.ts +14 -0
- package/dist/types/legacy/exports/molecules.d.ts +21 -0
- package/dist/types/legacy/exports/organisms.d.ts +17 -0
- package/dist/types/legacy/exports/pages.d.ts +5 -0
- package/dist/types/legacy/exports/styles.d.ts +2 -0
- package/dist/types/legacy/exports/types.d.ts +7 -0
- package/dist/types/legacy/exports/ui.d.ts +24 -0
- package/dist/types/legacy/exports/utils.d.ts +1 -0
- package/dist/types/legacy/hooks/useApi.d.ts +19 -0
- package/dist/types/legacy/pages/Error/ErrorPage.d.ts +16 -0
- package/dist/types/legacy/pages/Loading/Loading.d.ts +8 -0
- package/dist/types/legacy/pages/Login/LoginScreen.d.ts +42 -0
- package/dist/types/legacy/pages/NotFound/NotFound.d.ts +8 -0
- package/dist/types/legacy/styles/themes/ButtonThemes.d.ts +6 -0
- package/dist/types/legacy/styles/utils/utils.d.ts +2 -0
- package/dist/types/legacy/types/Fetcher.d.ts +5 -0
- package/dist/types/legacy/types/LoggedUser.d.ts +10 -0
- package/dist/types/legacy/types/SelectableElement.d.ts +6 -0
- package/dist/types/legacy/types/SidebarMenu.d.ts +12 -0
- package/dist/types/legacy/types/themes/AcronymThemes.d.ts +4 -0
- package/dist/types/legacy/types/themes/ButtonThemes.d.ts +21 -0
- package/dist/types/legacy/types/themes/CustomSwitchThemes.d.ts +4 -0
- package/dist/types/legacy/types/themes/IconTextThemes.d.ts +6 -0
- package/dist/types/legacy/utils/keycloak.d.ts +0 -0
- package/dist/types/legacy/utils/stringUtils.d.ts +6 -0
- package/dist/types/v1/capabilities/documents/QRScanning/scanQRCodes.d.ts +25 -0
- package/dist/types/v1/capabilities/overlay/useFloatingPanel.d.ts +20 -0
- package/dist/types/v1/capabilities/theme/useIsDark.d.ts +10 -0
- package/dist/types/v1/capabilities/theme/useTheme.d.ts +29 -0
- package/dist/types/v1/components/data/Pagination/Pagination.d.ts +33 -0
- package/dist/types/v1/components/documents/DocumentDropzone/DocumentDropzone.d.ts +60 -0
- package/dist/types/v1/components/feedback/Toast/Toast.d.ts +51 -0
- package/dist/types/v1/components/forms/FormField/FormField.d.ts +44 -0
- package/dist/types/v1/components/forms/FormGrid/FormGrid.d.ts +23 -0
- package/dist/types/v1/components/layout/AppShell/AppShell.d.ts +35 -0
- package/dist/types/v1/components/layout/Page/Page.d.ts +23 -0
- package/dist/types/v1/components/layout/PageHeader/PageHeader.d.ts +35 -0
- package/dist/types/v1/components/layout/SideNav/SideNav.d.ts +92 -0
- package/dist/types/v1/components/layout/navTrail.d.ts +15 -0
- package/dist/types/v1/components/lists/ListFinder/ListFinder.d.ts +86 -0
- package/dist/types/v1/components/lists/ListFinder/labels.d.ts +24 -0
- package/dist/types/v1/components/overlays/ConfirmDialog/ConfirmDialog.d.ts +59 -0
- package/dist/types/v1/components/overlays/Dialog/Dialog.d.ts +66 -0
- package/dist/types/v1/components/overlays/Tooltip/Tooltip.d.ts +34 -0
- package/dist/types/v1/components/overlays/Tooltip/TooltipLayer.d.ts +18 -0
- package/dist/types/v1/components/overlays/Tooltip/TooltipPanel.d.ts +23 -0
- package/dist/types/v1/components/overlays/topLayer.d.ts +23 -0
- package/dist/types/v1/components/tables/PaginatedTable/PaginatedTable.d.ts +41 -0
- package/dist/types/v1/components/tables/Table/ColumnFilter.d.ts +48 -0
- package/dist/types/v1/components/tables/Table/OverlayScrollbar.d.ts +34 -0
- package/dist/types/v1/components/tables/Table/Table.d.ts +250 -0
- package/dist/types/v1/components/tables/Table/labels.d.ts +54 -0
- package/dist/types/v1/foundations/icons/icons.d.ts +81 -0
- package/dist/types/v1/index.d.ts +86 -0
- package/dist/types/v1/primitives/buttons/Button/Button.d.ts +35 -0
- package/dist/types/v1/primitives/buttons/SegmentedControl/SegmentedControl.d.ts +28 -0
- package/dist/types/v1/primitives/buttons/SegmentedControl/segmentedStyles.d.ts +16 -0
- package/dist/types/v1/primitives/display/Avatar/Avatar.d.ts +30 -0
- package/dist/types/v1/primitives/display/Badge/Badge.d.ts +25 -0
- package/dist/types/v1/primitives/display/Card/Card.d.ts +22 -0
- package/dist/types/v1/primitives/display/FilterChips/FilterChips.d.ts +33 -0
- package/dist/types/v1/primitives/display/Kpi/Kpi.d.ts +68 -0
- package/dist/types/v1/primitives/display/Notice/Notice.d.ts +29 -0
- package/dist/types/v1/primitives/display/Person/Person.d.ts +15 -0
- package/dist/types/v1/primitives/display/RichText/RichText.d.ts +17 -0
- package/dist/types/v1/primitives/display/RichText/utils.d.ts +22 -0
- package/dist/types/v1/primitives/documents/PdfPreview/PdfPreview.d.ts +17 -0
- package/dist/types/v1/primitives/documents/PdfPreview/pdfjs.d.ts +11 -0
- package/dist/types/v1/primitives/forms/Checkbox/Checkbox.d.ts +25 -0
- package/dist/types/v1/primitives/forms/DateInput/DateInput.d.ts +43 -0
- package/dist/types/v1/primitives/forms/LookupInput/LookupInput.d.ts +28 -0
- package/dist/types/v1/primitives/forms/RichTextEditor/RichTextEditor.d.ts +64 -0
- package/dist/types/v1/primitives/forms/SearchInput/SearchInput.d.ts +23 -0
- package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +70 -0
- package/dist/types/v1/primitives/forms/TextArea/TextArea.d.ts +40 -0
- package/dist/types/v1/primitives/forms/TextInput/TextInput.d.ts +16 -0
- package/dist/types/v1/primitives/forms/field.d.ts +22 -0
- package/dist/types/v1/primitives/lists/ListItem/ListItem.d.ts +67 -0
- package/dist/types/v1/primitives/navigation/Tabs/Tabs.d.ts +58 -0
- package/package.json +125 -6
- package/README.md +0 -3
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useRef, useState, useEffect } from "react";
|
|
3
|
+
import PdfPreview from "../../../primitives/documents/PdfPreview/PdfPreview.js";
|
|
4
|
+
const EXT_TYPES = {
|
|
5
|
+
".pdf": ["application/pdf"],
|
|
6
|
+
".jpg": ["image/jpeg"],
|
|
7
|
+
".jpeg": ["image/jpeg"],
|
|
8
|
+
".png": ["image/png"],
|
|
9
|
+
".webp": ["image/webp"],
|
|
10
|
+
".gif": ["image/gif"],
|
|
11
|
+
".heic": ["image/heic"],
|
|
12
|
+
".svg": ["image/svg+xml"],
|
|
13
|
+
".doc": ["application/msword"],
|
|
14
|
+
".docx": ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
|
|
15
|
+
".xls": ["application/vnd.ms-excel"],
|
|
16
|
+
".xlsx": ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
|
|
17
|
+
".csv": ["text/csv", "application/vnd.ms-excel"],
|
|
18
|
+
".txt": ["text/plain"],
|
|
19
|
+
".zip": ["application/zip", "application/x-zip-compressed"]
|
|
20
|
+
};
|
|
21
|
+
function acceptsType(type, accept) {
|
|
22
|
+
const rules = (accept ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
23
|
+
if (rules.length === 0) return true;
|
|
24
|
+
const t = type.toLowerCase();
|
|
25
|
+
if (!t) return null;
|
|
26
|
+
let unknown = false;
|
|
27
|
+
for (const rule of rules) {
|
|
28
|
+
if (rule.startsWith(".")) {
|
|
29
|
+
const types = EXT_TYPES[rule];
|
|
30
|
+
if (!types) unknown = true;
|
|
31
|
+
else if (types.includes(t)) return true;
|
|
32
|
+
} else if (rule.endsWith("/*")) {
|
|
33
|
+
if (t.startsWith(rule.slice(0, -1))) return true;
|
|
34
|
+
} else if (t === rule) return true;
|
|
35
|
+
}
|
|
36
|
+
return unknown ? null : false;
|
|
37
|
+
}
|
|
38
|
+
function accepts(file, accept) {
|
|
39
|
+
if (!accept) return true;
|
|
40
|
+
const name = file.name.toLowerCase();
|
|
41
|
+
const type = file.type.toLowerCase();
|
|
42
|
+
const rules = accept.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
43
|
+
if (rules.length === 0) return true;
|
|
44
|
+
return rules.some((rule) => {
|
|
45
|
+
if (rule.startsWith(".")) return name.endsWith(rule);
|
|
46
|
+
if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1));
|
|
47
|
+
return type === rule;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function UploadIcon() {
|
|
51
|
+
return /* @__PURE__ */ jsxs("svg", { width: "36", height: "36", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, children: [
|
|
52
|
+
/* @__PURE__ */ jsx("path", { d: "M12 15V4M7.5 8.5L12 4l4.5 4.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
53
|
+
/* @__PURE__ */ jsx("path", { d: "M4.5 14v3.5a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V14", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
54
|
+
] });
|
|
55
|
+
}
|
|
56
|
+
function DocumentIcon({ size }) {
|
|
57
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", "aria-hidden": true, children: [
|
|
58
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
59
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v5h5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
function ImageIcon() {
|
|
63
|
+
return /* @__PURE__ */ jsxs("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", "aria-hidden": true, children: [
|
|
64
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
65
|
+
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
66
|
+
/* @__PURE__ */ jsx("path", { d: "M21 15l-5-5L4 20", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
67
|
+
] });
|
|
68
|
+
}
|
|
69
|
+
function FileTypeIcon({ label, size = 20 }) {
|
|
70
|
+
const id = React.useId();
|
|
71
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", "aria-hidden": true, children: [
|
|
72
|
+
/* @__PURE__ */ jsxs("mask", { id: `${id}-doc`, stroke: "none", children: [
|
|
73
|
+
/* @__PURE__ */ jsx("rect", { width: "24", height: "24", fill: "#fff" }),
|
|
74
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "11", width: "17", height: "9.5", rx: "2", fill: "#000" })
|
|
75
|
+
] }),
|
|
76
|
+
/* @__PURE__ */ jsxs("mask", { id: `${id}-label`, stroke: "none", children: [
|
|
77
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "12", width: "15", height: "7.5", rx: "1.6", fill: "#fff" }),
|
|
78
|
+
/* @__PURE__ */ jsx("text", { x: "9.5", y: "17.9", textAnchor: "middle", fontFamily: "Arial, Helvetica, sans-serif", fontWeight: "700", fontSize: "6", fill: "#000", children: label })
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsxs("g", { mask: `url(#${id}-doc)`, children: [
|
|
81
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z", strokeLinejoin: "round" }),
|
|
82
|
+
/* @__PURE__ */ jsx("path", { d: "M14 2v5h5", strokeLinejoin: "round" })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "12", width: "15", height: "7.5", rx: "1.6", fill: "currentColor", stroke: "none", mask: `url(#${id}-label)` })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
const spreadsheetLabel = (file) => /.csv$/i.test(file.name) || file.type.toLowerCase() === "text/csv" ? "CSV" : "XLS";
|
|
88
|
+
const isPdf = (file) => file.type === "application/pdf";
|
|
89
|
+
const SPREADSHEET_TYPES = [
|
|
90
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
91
|
+
"application/vnd.ms-excel.sheet.macroenabled.12",
|
|
92
|
+
"application/vnd.ms-excel",
|
|
93
|
+
"text/csv"
|
|
94
|
+
];
|
|
95
|
+
const isSpreadsheet = (file) => /\.(xlsx|xlsm|xls|csv)$/i.test(file.name) || SPREADSHEET_TYPES.includes(file.type.toLowerCase());
|
|
96
|
+
function DefaultPreview({ file, url }) {
|
|
97
|
+
if (file.type.startsWith("image/")) {
|
|
98
|
+
return /* @__PURE__ */ jsx("img", { src: url, alt: file.name, className: "block max-h-full max-w-full object-contain p-3" });
|
|
99
|
+
}
|
|
100
|
+
if (isPdf(file)) {
|
|
101
|
+
return /* @__PURE__ */ jsx(PdfPreview, { file, label: file.name });
|
|
102
|
+
}
|
|
103
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-24 w-24 items-center justify-center rounded-3xl bg-primary text-primary-foreground shadow-tile", children: isSpreadsheet(file) ? /* @__PURE__ */ jsx(FileTypeIcon, { label: spreadsheetLabel(file), size: 42 }) : /* @__PURE__ */ jsx(DocumentIcon, { size: 42 }) });
|
|
104
|
+
}
|
|
105
|
+
function DocumentDropzone({
|
|
106
|
+
file,
|
|
107
|
+
onFileChange,
|
|
108
|
+
accept,
|
|
109
|
+
disabled = false,
|
|
110
|
+
labels,
|
|
111
|
+
renderPreview,
|
|
112
|
+
onQRCodes,
|
|
113
|
+
qrCodeWanted,
|
|
114
|
+
onReject,
|
|
115
|
+
className = ""
|
|
116
|
+
}) {
|
|
117
|
+
const input = useRef(null);
|
|
118
|
+
const [drag, setDrag] = useState("none");
|
|
119
|
+
const [url, setUrl] = useState("");
|
|
120
|
+
const [scanning, setScanning] = useState(false);
|
|
121
|
+
const [rejected, setRejected] = useState(false);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!rejected) return;
|
|
124
|
+
const t = window.setTimeout(() => setRejected(false), 5e3);
|
|
125
|
+
return () => window.clearTimeout(t);
|
|
126
|
+
}, [rejected]);
|
|
127
|
+
const onQRCodesRef = useRef(onQRCodes);
|
|
128
|
+
const qrCodeWantedRef = useRef(qrCodeWanted);
|
|
129
|
+
onQRCodesRef.current = onQRCodes;
|
|
130
|
+
qrCodeWantedRef.current = qrCodeWanted;
|
|
131
|
+
const scanQR = Boolean(onQRCodes);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!file || !scanQR) {
|
|
134
|
+
setScanning(false);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
let alive = true;
|
|
138
|
+
setScanning(true);
|
|
139
|
+
import("../../../capabilities/documents/QRScanning/scanQRCodes.js").then(({ scanQRCodes }) => scanQRCodes(file, { until: (code) => qrCodeWantedRef.current?.(code) ?? true })).catch(() => []).then((codes) => {
|
|
140
|
+
if (!alive) return;
|
|
141
|
+
setScanning(false);
|
|
142
|
+
onQRCodesRef.current?.(codes, file);
|
|
143
|
+
});
|
|
144
|
+
return () => {
|
|
145
|
+
alive = false;
|
|
146
|
+
};
|
|
147
|
+
}, [file, scanQR]);
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (!file) {
|
|
150
|
+
setUrl("");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const created = URL.createObjectURL(file);
|
|
154
|
+
setUrl(created);
|
|
155
|
+
return () => URL.revokeObjectURL(created);
|
|
156
|
+
}, [file]);
|
|
157
|
+
const pick = () => {
|
|
158
|
+
if (!disabled) input.current?.click();
|
|
159
|
+
};
|
|
160
|
+
const receive = (list) => {
|
|
161
|
+
if (disabled || !list || list.length === 0) return;
|
|
162
|
+
const files = Array.from(list);
|
|
163
|
+
const first = files.find((f) => accepts(f, accept));
|
|
164
|
+
if (first) {
|
|
165
|
+
setRejected(false);
|
|
166
|
+
onFileChange(first);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
setRejected(true);
|
|
170
|
+
onReject?.(files);
|
|
171
|
+
};
|
|
172
|
+
const onKeyDown = (e) => {
|
|
173
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
pick();
|
|
176
|
+
};
|
|
177
|
+
let frame = "border-2 border-dashed border-primary/30 bg-card shadow-card hover:border-primary hover:bg-muted hover:shadow-card-hover";
|
|
178
|
+
if (file) frame = "border border-border bg-card shadow-card";
|
|
179
|
+
if (drag === "ok") frame = "border-2 border-dashed border-primary bg-primary/5 shadow-card-active";
|
|
180
|
+
if (drag === "rejected") frame = "border-2 border-dashed border-destructive bg-destructive/5 shadow-card";
|
|
181
|
+
if (disabled) frame += " cursor-not-allowed opacity-60";
|
|
182
|
+
const sizeMb = file ? (file.size / 1024 / 1024).toFixed(2) : "";
|
|
183
|
+
const custom = file && url && renderPreview ? renderPreview(file, url) : null;
|
|
184
|
+
const preview = file && url ? custom ?? /* @__PURE__ */ jsx(DefaultPreview, { file, url }) : null;
|
|
185
|
+
return /* @__PURE__ */ jsxs(
|
|
186
|
+
"div",
|
|
187
|
+
{
|
|
188
|
+
onDragOver: (e) => {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
if (disabled) {
|
|
191
|
+
e.dataTransfer.dropEffect = "none";
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const types = Array.from(e.dataTransfer.items ?? []).filter((item) => item.kind === "file").map((item) => item.type);
|
|
195
|
+
const verdict = types.length && types.every((t) => acceptsType(t, accept) === false) ? "rejected" : "ok";
|
|
196
|
+
e.dataTransfer.dropEffect = verdict === "rejected" ? "none" : "copy";
|
|
197
|
+
setDrag(verdict);
|
|
198
|
+
},
|
|
199
|
+
onDragLeave: (e) => {
|
|
200
|
+
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
201
|
+
if (drag === "rejected") setRejected(true);
|
|
202
|
+
setDrag("none");
|
|
203
|
+
},
|
|
204
|
+
onDrop: (e) => {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
setDrag("none");
|
|
207
|
+
receive(e.dataTransfer.files);
|
|
208
|
+
},
|
|
209
|
+
className: `group relative flex overflow-hidden rounded-2xl transition-[border-color,background-color,box-shadow] duration-200 ${frame} ${className}`,
|
|
210
|
+
children: [
|
|
211
|
+
!file && /* @__PURE__ */ jsxs(
|
|
212
|
+
"div",
|
|
213
|
+
{
|
|
214
|
+
role: "button",
|
|
215
|
+
tabIndex: disabled ? -1 : 0,
|
|
216
|
+
"aria-disabled": disabled,
|
|
217
|
+
onClick: pick,
|
|
218
|
+
onKeyDown,
|
|
219
|
+
className: "flex h-full w-full cursor-pointer flex-col items-center justify-center gap-[18px] px-6 py-7 text-center outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
220
|
+
children: [
|
|
221
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-[78px] w-[78px] items-center justify-center rounded-[22px] bg-primary/5 text-primary transition-transform duration-200 group-hover:-translate-y-0.5", children: /* @__PURE__ */ jsx(UploadIcon, {}) }),
|
|
222
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
223
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1.5 text-title font-semibold text-foreground", children: labels.title }),
|
|
224
|
+
labels.hint && /* @__PURE__ */ jsx("div", { className: "text-control text-muted-foreground", children: labels.hint })
|
|
225
|
+
] })
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
),
|
|
229
|
+
file && /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 w-full flex-col", children: [
|
|
230
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-none items-center gap-[11px] border-b border-border bg-card py-2.5 pr-3 pl-3.5", children: [
|
|
231
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-[30px] w-[30px] flex-none items-center justify-center rounded-lg bg-primary/5 text-primary", children: isPdf(file) ? /* @__PURE__ */ jsx(FileTypeIcon, { label: "PDF" }) : isSpreadsheet(file) ? /* @__PURE__ */ jsx(FileTypeIcon, { label: spreadsheetLabel(file) }) : file.type.startsWith("image/") ? /* @__PURE__ */ jsx(ImageIcon, {}) : /* @__PURE__ */ jsx(DocumentIcon, { size: 17 }) }),
|
|
232
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 text-left", children: [
|
|
233
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-control font-semibold text-primary", children: file.name }),
|
|
234
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-px flex items-center gap-1.5 text-caption text-muted-foreground", children: [
|
|
235
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
236
|
+
sizeMb,
|
|
237
|
+
" MB"
|
|
238
|
+
] }),
|
|
239
|
+
scanning && /* @__PURE__ */ jsxs("span", { role: "status", className: "inline-flex items-center gap-1.5", children: [
|
|
240
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-block h-2.5 w-2.5 animate-spin rounded-full border-[1.5px] border-primary/20 border-t-primary" }),
|
|
241
|
+
labels.scanningQR
|
|
242
|
+
] })
|
|
243
|
+
] })
|
|
244
|
+
] }),
|
|
245
|
+
/* @__PURE__ */ jsxs(
|
|
246
|
+
"button",
|
|
247
|
+
{
|
|
248
|
+
type: "button",
|
|
249
|
+
disabled,
|
|
250
|
+
onClick: pick,
|
|
251
|
+
className: "inline-flex flex-none cursor-pointer items-center gap-1.5 rounded-lg border border-border bg-card px-[11px] py-1.5 text-caption font-bold tracking-[0.02em] text-primary transition-colors duration-150 hover:border-primary/30 hover:bg-primary/5 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", "aria-hidden": true, children: [
|
|
254
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-2.64-6.36", strokeLinecap: "round" }),
|
|
255
|
+
/* @__PURE__ */ jsx("path", { d: "M21 3v5h-5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
256
|
+
] }),
|
|
257
|
+
labels.change
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
/* @__PURE__ */ jsx(
|
|
262
|
+
"button",
|
|
263
|
+
{
|
|
264
|
+
type: "button",
|
|
265
|
+
title: labels.remove,
|
|
266
|
+
"aria-label": labels.remove,
|
|
267
|
+
disabled,
|
|
268
|
+
onClick: () => onFileChange(null),
|
|
269
|
+
className: "inline-flex h-8 w-8 flex-none cursor-pointer items-center justify-center rounded-lg border border-border bg-card p-0 text-muted-foreground transition-colors duration-150 hover:border-destructive/30 hover:bg-destructive/10 hover:text-destructive",
|
|
270
|
+
children: /* @__PURE__ */ jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
] }),
|
|
274
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 basis-0 items-center justify-center overflow-hidden bg-card", children: preview })
|
|
275
|
+
] }),
|
|
276
|
+
(rejected || drag === "rejected") && /* @__PURE__ */ jsx(
|
|
277
|
+
"div",
|
|
278
|
+
{
|
|
279
|
+
role: "alert",
|
|
280
|
+
className: "absolute inset-x-3 bottom-3 rounded-lg border border-destructive/30 bg-card px-3 py-2 text-center text-xs font-medium text-destructive shadow-popover",
|
|
281
|
+
children: labels.rejected
|
|
282
|
+
}
|
|
283
|
+
),
|
|
284
|
+
/* @__PURE__ */ jsx(
|
|
285
|
+
"input",
|
|
286
|
+
{
|
|
287
|
+
ref: input,
|
|
288
|
+
type: "file",
|
|
289
|
+
accept,
|
|
290
|
+
className: "hidden",
|
|
291
|
+
onChange: (e) => {
|
|
292
|
+
receive(e.target.files);
|
|
293
|
+
e.target.value = "";
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
export {
|
|
302
|
+
accepts,
|
|
303
|
+
acceptsType,
|
|
304
|
+
DocumentDropzone as default,
|
|
305
|
+
isSpreadsheet
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=DocumentDropzone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentDropzone.js","sources":["../../../../../../src/v1/components/documents/DocumentDropzone/DocumentDropzone.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\nimport PdfPreview from '../../../primitives/documents/PdfPreview/PdfPreview'\n\nexport interface DocumentDropzoneLabels {\n title: string\n hint?: string\n change: string\n remove: string\n /** Texto enquanto procura QR codes (só com `onQRCodes`), ex: \"A procurar QR codes…\". */\n scanningQR?: string\n /**\n * Mensagem quando um ficheiro não é permitido pelo `accept`, ex: \"Formato não\n * permitido. Aceita PDF, JPG ou PNG.\" Aparece logo ao arrastar e depois de largar.\n */\n rejected: string\n}\n\nexport interface DocumentDropzoneProps {\n file: File | null\n onFileChange: (file: File | null) => void\n /**\n * Tipos permitidos: extensões e/ou tipos MIME, separados por vírgulas, ex:\n * \".pdf,.jpg,.jpeg,.png\". Os restantes são recusados (ver `labels.rejected` e\n * `onReject`). Sem `accept`, aceita qualquer ficheiro.\n *\n * - `image/*` inclui SVG (que pode conter código) e HEIC (sem pré-visualização\n * na maioria dos browsers). Para documentos, prefira uma lista explícita.\n * - Só se verificam o nome e o tipo declarado, não o conteúdo: um ficheiro\n * renomeado passa. O servidor deve validar o que recebe.\n */\n accept?: string\n disabled?: boolean\n labels: DocumentDropzoneLabels\n /** Pré-visualização própria. Se devolver null/undefined, usa a de omissão (imagem, PDF ou ícone). */\n renderPreview?: (file: File, url: string) => React.ReactNode\n /**\n * Procura QR codes em cada ficheiro escolhido (imagem ou 1.ª página do PDF) e\n * entrega o texto de todos os encontrados (vazio se nenhum). Sem esta prop não\n * se procura nada. Usa a capacidade [QR Scanning](?path=/docs/v1-capabilities-documents-qr-scanning--docs).\n */\n onQRCodes?: (codes: string[], file: File) => void\n /** Chamado com os ficheiros recusados pelo `accept` (quando nenhum dos largados/escolhidos serve). */\n onReject?: (files: File[]) => void\n /** Continuar a procurar até um QR code satisfazer isto (ex: ser o QR de uma fatura). Por omissão, pára no primeiro. */\n qrCodeWanted?: (code: string) => boolean\n className?: string\n}\n\n// Tipos MIME das extensões mais comuns. Ao arrastar, o browser só revela o tipo\n// do ficheiro (não o nome), por isso as regras \".ext\" são traduzidas por aqui.\nconst EXT_TYPES: Record<string, string[]> = {\n '.pdf': ['application/pdf'],\n '.jpg': ['image/jpeg'],\n '.jpeg': ['image/jpeg'],\n '.png': ['image/png'],\n '.webp': ['image/webp'],\n '.gif': ['image/gif'],\n '.heic': ['image/heic'],\n '.svg': ['image/svg+xml'],\n '.doc': ['application/msword'],\n '.docx': ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],\n '.xls': ['application/vnd.ms-excel'],\n '.xlsx': ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],\n '.csv': ['text/csv', 'application/vnd.ms-excel'],\n '.txt': ['text/plain'],\n '.zip': ['application/zip', 'application/x-zip-compressed'],\n}\n\n/**\n * Se um ficheiro deste tipo é permitido, sabendo só o tipo (durante o arrastar).\n * `null` quando não dá para saber (tipo desconhecido, ou extensão fora da tabela).\n */\nexport function acceptsType(type: string, accept?: string): boolean | null {\n const rules = (accept ?? '')\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean)\n if (rules.length === 0) return true\n const t = type.toLowerCase()\n if (!t) return null\n let unknown = false\n for (const rule of rules) {\n if (rule.startsWith('.')) {\n const types = EXT_TYPES[rule]\n if (!types) unknown = true\n else if (types.includes(t)) return true\n } else if (rule.endsWith('/*')) {\n if (t.startsWith(rule.slice(0, -1))) return true\n } else if (t === rule) return true\n }\n return unknown ? null : false\n}\n\nexport function accepts(file: File, accept?: string): boolean {\n if (!accept) return true\n const name = file.name.toLowerCase()\n const type = file.type.toLowerCase()\n // Regras vazias (ex: vírgula a mais em \".pdf,\") são ignoradas: senão aceitavam\n // qualquer ficheiro cujo tipo o browser não conhece (type === '').\n const rules = accept\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean)\n if (rules.length === 0) return true\n return rules.some((rule) => {\n if (rule.startsWith('.')) return name.endsWith(rule)\n if (rule.endsWith('/*')) return type.startsWith(rule.slice(0, -1))\n return type === rule\n })\n}\n\nfunction UploadIcon() {\n return (\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" aria-hidden>\n <path d=\"M12 15V4M7.5 8.5L12 4l4.5 4.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M4.5 14v3.5a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V14\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\nfunction DocumentIcon({ size }: { size: number }) {\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden>\n <path d=\"M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M14 2v5h5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\nfunction ImageIcon() {\n return (\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" aria-hidden>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <path d=\"M21 15l-5-5L4 20\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n// Documento com etiqueta do formato (PDF, XLS, CSV). As letras são recortadas\n// da etiqueta (máscara), para lerem bem sobre qualquer fundo, claro ou escuro.\nfunction FileTypeIcon({ label, size = 20 }: { label: string; size?: number }) {\n const id = React.useId()\n return (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" aria-hidden>\n <mask id={`${id}-doc`} stroke=\"none\">\n <rect width=\"24\" height=\"24\" fill=\"#fff\" />\n <rect x=\"1\" y=\"11\" width=\"17\" height=\"9.5\" rx=\"2\" fill=\"#000\" />\n </mask>\n <mask id={`${id}-label`} stroke=\"none\">\n <rect x=\"2\" y=\"12\" width=\"15\" height=\"7.5\" rx=\"1.6\" fill=\"#fff\" />\n <text x=\"9.5\" y=\"17.9\" textAnchor=\"middle\" fontFamily=\"Arial, Helvetica, sans-serif\" fontWeight=\"700\" fontSize=\"6\" fill=\"#000\">\n {label}\n </text>\n </mask>\n <g mask={`url(#${id}-doc)`}>\n <path d=\"M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z\" strokeLinejoin=\"round\" />\n <path d=\"M14 2v5h5\" strokeLinejoin=\"round\" />\n </g>\n <rect x=\"2\" y=\"12\" width=\"15\" height=\"7.5\" rx=\"1.6\" fill=\"currentColor\" stroke=\"none\" mask={`url(#${id}-label)`} />\n </svg>\n )\n}\n\nconst spreadsheetLabel = (file: File) => (/.csv$/i.test(file.name) || file.type.toLowerCase() === 'text/csv' ? 'CSV' : 'XLS')\n\nconst isPdf = (file: File) => file.type === 'application/pdf'\n\nconst SPREADSHEET_TYPES = [\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-excel.sheet.macroenabled.12',\n 'application/vnd.ms-excel',\n 'text/csv',\n]\n/** Folhas de cálculo (Excel, CSV): pela extensão ou pelo tipo, que o browser às vezes não conhece. */\nexport const isSpreadsheet = (file: File) => /\\.(xlsx|xlsm|xls|csv)$/i.test(file.name) || SPREADSHEET_TYPES.includes(file.type.toLowerCase())\n\nfunction DefaultPreview({ file, url }: { file: File; url: string }) {\n if (file.type.startsWith('image/')) {\n return <img src={url} alt={file.name} className=\"block max-h-full max-w-full object-contain p-3\" />\n }\n if (isPdf(file)) {\n return <PdfPreview file={file} label={file.name} />\n }\n return (\n <div className=\"flex h-24 w-24 items-center justify-center rounded-3xl bg-primary text-primary-foreground shadow-tile\">\n {isSpreadsheet(file) ? <FileTypeIcon label={spreadsheetLabel(file)} size={42} /> : <DocumentIcon size={42} />}\n </div>\n )\n}\n\n/**\n * Caixa para anexar um ficheiro a um formulário: arrastar ou clicar para\n * escolher. Mostra o nome, o tamanho e uma pré-visualização do ficheiro.\n *\n * **Usa:** [PdfPreview](?path=/docs/v1-primitives-documents-pdfpreview--docs) para mostrar PDFs; imagens com `<img>`.\n * Com `onQRCodes`, a capacidade [QR Scanning](?path=/docs/v1-capabilities-documents-qr-scanning--docs).\n */\nexport default function DocumentDropzone({\n file,\n onFileChange,\n accept,\n disabled = false,\n labels,\n renderPreview,\n onQRCodes,\n qrCodeWanted,\n onReject,\n className = '',\n}: DocumentDropzoneProps) {\n const input = useRef<HTMLInputElement>(null)\n // Durante o arrastar: 'ok' se o ficheiro serve (ou não dá para saber), 'rejected' se não.\n const [drag, setDrag] = useState<'none' | 'ok' | 'rejected'>('none')\n const [url, setUrl] = useState('')\n const [scanning, setScanning] = useState(false)\n const [rejected, setRejected] = useState(false)\n\n // A mensagem de recusa desaparece sozinha passado uns segundos.\n useEffect(() => {\n if (!rejected) return\n const t = window.setTimeout(() => setRejected(false), 5000)\n return () => window.clearTimeout(t)\n }, [rejected])\n\n // Sempre a versão mais recente das funções da app, sem recomeçar a leitura\n // quando a app passa uma função nova em cada render.\n const onQRCodesRef = useRef(onQRCodes)\n const qrCodeWantedRef = useRef(qrCodeWanted)\n onQRCodesRef.current = onQRCodes\n qrCodeWantedRef.current = qrCodeWanted\n const scanQR = Boolean(onQRCodes)\n\n useEffect(() => {\n if (!file || !scanQR) {\n setScanning(false)\n return\n }\n let alive = true\n setScanning(true)\n import('../../../capabilities/documents/QRScanning/scanQRCodes')\n .then(({ scanQRCodes }) => scanQRCodes(file, { until: (code) => qrCodeWantedRef.current?.(code) ?? true }))\n .catch(() => [] as string[])\n .then((codes) => {\n // Um resultado de um ficheiro que entretanto mudou é descartado.\n if (!alive) return\n setScanning(false)\n onQRCodesRef.current?.(codes, file)\n })\n return () => {\n alive = false\n }\n }, [file, scanQR])\n\n useEffect(() => {\n if (!file) {\n setUrl('')\n return\n }\n const created = URL.createObjectURL(file)\n setUrl(created)\n return () => URL.revokeObjectURL(created)\n }, [file])\n\n const pick = () => {\n if (!disabled) input.current?.click()\n }\n\n const receive = (list: FileList | null) => {\n if (disabled || !list || list.length === 0) return\n const files = Array.from(list)\n const first = files.find((f) => accepts(f, accept))\n if (first) {\n setRejected(false)\n onFileChange(first)\n return\n }\n setRejected(true)\n onReject?.(files)\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key !== 'Enter' && e.key !== ' ') return\n e.preventDefault()\n pick()\n }\n\n let frame = 'border-2 border-dashed border-primary/30 bg-card shadow-card hover:border-primary hover:bg-muted hover:shadow-card-hover'\n if (file) frame = 'border border-border bg-card shadow-card'\n if (drag === 'ok') frame = 'border-2 border-dashed border-primary bg-primary/5 shadow-card-active'\n if (drag === 'rejected') frame = 'border-2 border-dashed border-destructive bg-destructive/5 shadow-card'\n if (disabled) frame += ' cursor-not-allowed opacity-60'\n\n const sizeMb = file ? (file.size / 1024 / 1024).toFixed(2) : ''\n const custom = file && url && renderPreview ? renderPreview(file, url) : null\n const preview = file && url ? custom ?? <DefaultPreview file={file} url={url} /> : null\n\n return (\n <div\n onDragOver={(e) => {\n e.preventDefault()\n if (disabled) {\n e.dataTransfer.dropEffect = 'none'\n return\n }\n const types = Array.from(e.dataTransfer.items ?? [])\n .filter((item) => item.kind === 'file')\n .map((item) => item.type)\n const verdict = types.length && types.every((t) => acceptsType(t, accept) === false) ? 'rejected' : 'ok'\n e.dataTransfer.dropEffect = verdict === 'rejected' ? 'none' : 'copy'\n setDrag(verdict)\n }}\n onDragLeave={(e) => {\n // Passar por cima do ícone ou do texto da caixa não é sair dela.\n if (e.currentTarget.contains(e.relatedTarget as Node | null)) return\n // Ficheiro não permitido: a mensagem fica uns segundos depois de largar/sair.\n if (drag === 'rejected') setRejected(true)\n setDrag('none')\n }}\n onDrop={(e) => {\n e.preventDefault()\n setDrag('none')\n receive(e.dataTransfer.files)\n }}\n className={`group relative flex overflow-hidden rounded-2xl transition-[border-color,background-color,box-shadow] duration-200 ${frame} ${className}`}\n >\n {!file && (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled}\n onClick={pick}\n onKeyDown={onKeyDown}\n className=\"flex h-full w-full cursor-pointer flex-col items-center justify-center gap-[18px] px-6 py-7 text-center outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <div className=\"flex h-[78px] w-[78px] items-center justify-center rounded-[22px] bg-primary/5 text-primary transition-transform duration-200 group-hover:-translate-y-0.5\">\n <UploadIcon />\n </div>\n <div>\n <div className=\"mb-1.5 text-title font-semibold text-foreground\">{labels.title}</div>\n {labels.hint && <div className=\"text-control text-muted-foreground\">{labels.hint}</div>}\n </div>\n </div>\n )}\n\n {file && (\n <div className=\"flex h-full min-h-0 w-full flex-col\">\n <div className=\"flex flex-none items-center gap-[11px] border-b border-border bg-card py-2.5 pr-3 pl-3.5\">\n <span className=\"flex h-[30px] w-[30px] flex-none items-center justify-center rounded-lg bg-primary/5 text-primary\">\n {isPdf(file) ? (\n <FileTypeIcon label=\"PDF\" />\n ) : isSpreadsheet(file) ? (\n <FileTypeIcon label={spreadsheetLabel(file)} />\n ) : file.type.startsWith('image/') ? (\n <ImageIcon />\n ) : (\n <DocumentIcon size={17} />\n )}\n </span>\n <div className=\"min-w-0 flex-1 text-left\">\n <div className=\"truncate text-control font-semibold text-primary\">{file.name}</div>\n <div className=\"mt-px flex items-center gap-1.5 text-caption text-muted-foreground\">\n <span>{sizeMb} MB</span>\n {scanning && (\n <span role=\"status\" className=\"inline-flex items-center gap-1.5\">\n <span aria-hidden className=\"inline-block h-2.5 w-2.5 animate-spin rounded-full border-[1.5px] border-primary/20 border-t-primary\" />\n {labels.scanningQR}\n </span>\n )}\n </div>\n </div>\n <button\n type=\"button\"\n disabled={disabled}\n onClick={pick}\n className=\"inline-flex flex-none cursor-pointer items-center gap-1.5 rounded-lg border border-border bg-card px-[11px] py-1.5 text-caption font-bold tracking-[0.02em] text-primary transition-colors duration-150 hover:border-primary/30 hover:bg-primary/5 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\"\n >\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36\" strokeLinecap=\"round\" />\n <path d=\"M21 3v5h-5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n {labels.change}\n </button>\n <button\n type=\"button\"\n title={labels.remove}\n aria-label={labels.remove}\n disabled={disabled}\n onClick={() => onFileChange(null)}\n className=\"inline-flex h-8 w-8 flex-none cursor-pointer items-center justify-center rounded-lg border border-border bg-card p-0 text-muted-foreground transition-colors duration-150 hover:border-destructive/30 hover:bg-destructive/10 hover:text-destructive\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.9\" aria-hidden>\n <path d=\"M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n </div>\n <div className=\"flex min-h-0 flex-1 basis-0 items-center justify-center overflow-hidden bg-card\">\n {preview}\n </div>\n </div>\n )}\n\n {(rejected || drag === 'rejected') && (\n <div\n role=\"alert\"\n className=\"absolute inset-x-3 bottom-3 rounded-lg border border-destructive/30 bg-card px-3 py-2 text-center text-xs font-medium text-destructive shadow-popover\"\n >\n {labels.rejected}\n </div>\n )}\n\n <input\n ref={input}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n onChange={(e) => {\n receive(e.target.files)\n e.target.value = ''\n }}\n />\n </div>\n )\n}\n"],"names":[],"mappings":";;;AAkDA,MAAM,YAAsC;AAAA,EAC1C,QAAQ,CAAC,iBAAiB;AAAA,EAC1B,QAAQ,CAAC,YAAY;AAAA,EACrB,SAAS,CAAC,YAAY;AAAA,EACtB,QAAQ,CAAC,WAAW;AAAA,EACpB,SAAS,CAAC,YAAY;AAAA,EACtB,QAAQ,CAAC,WAAW;AAAA,EACpB,SAAS,CAAC,YAAY;AAAA,EACtB,QAAQ,CAAC,eAAe;AAAA,EACxB,QAAQ,CAAC,oBAAoB;AAAA,EAC7B,SAAS,CAAC,yEAAyE;AAAA,EACnF,QAAQ,CAAC,0BAA0B;AAAA,EACnC,SAAS,CAAC,mEAAmE;AAAA,EAC7E,QAAQ,CAAC,YAAY,0BAA0B;AAAA,EAC/C,QAAQ,CAAC,YAAY;AAAA,EACrB,QAAQ,CAAC,mBAAmB,8BAA8B;AAC5D;AAMO,SAAS,YAAY,MAAc,QAAiC;AACzE,QAAM,SAAS,UAAU,IACtB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,OAAO,YAAA,CAAa,EACjC,OAAO,OAAO;AACjB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,IAAI,KAAK,YAAA;AACf,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,WAAW,GAAG,GAAG;AACxB,YAAM,QAAQ,UAAU,IAAI;AAC5B,UAAI,CAAC,MAAO,WAAU;AAAA,eACb,MAAM,SAAS,CAAC,EAAG,QAAO;AAAA,IACrC,WAAW,KAAK,SAAS,IAAI,GAAG;AAC9B,UAAI,EAAE,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC,EAAG,QAAO;AAAA,IAC9C,WAAW,MAAM,KAAM,QAAO;AAAA,EAChC;AACA,SAAO,UAAU,OAAO;AAC1B;AAEO,SAAS,QAAQ,MAAY,QAA0B;AAC5D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,KAAK,KAAK,YAAA;AACvB,QAAM,OAAO,KAAK,KAAK,YAAA;AAGvB,QAAM,QAAQ,OACX,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,OAAO,YAAA,CAAa,EACjC,OAAO,OAAO;AACjB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,KAAK,CAAC,SAAS;AAC1B,QAAI,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK,SAAS,IAAI;AACnD,QAAI,KAAK,SAAS,IAAI,EAAG,QAAO,KAAK,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC;AACjE,WAAO,SAAS;AAAA,EAClB,CAAC;AACH;AAEA,SAAS,aAAa;AACpB,SACE,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MAC7G,UAAA;AAAA,IAAA,oBAAC,UAAK,GAAE,iCAAgC,eAAc,SAAQ,gBAAe,SAAQ;AAAA,wBACpF,QAAA,EAAK,GAAE,iDAAgD,eAAc,SAAQ,gBAAe,QAAA,CAAQ;AAAA,EAAA,GACvG;AAEJ;AAEA,SAAS,aAAa,EAAE,QAA0B;AAChD,SACE,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MACjH,UAAA;AAAA,IAAA,oBAAC,UAAK,GAAE,8DAA6D,eAAc,SAAQ,gBAAe,SAAQ;AAAA,wBACjH,QAAA,EAAK,GAAE,aAAY,eAAc,SAAQ,gBAAe,QAAA,CAAQ;AAAA,EAAA,GACnE;AAEJ;AAEA,SAAS,YAAY;AACnB,SACE,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MAC7G,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,wBAC/C,UAAA,EAAO,IAAG,OAAM,IAAG,OAAM,GAAE,OAAM;AAAA,wBACjC,QAAA,EAAK,GAAE,oBAAmB,eAAc,SAAQ,gBAAe,QAAA,CAAQ;AAAA,EAAA,GAC1E;AAEJ;AAIA,SAAS,aAAa,EAAE,OAAO,OAAO,MAAwC;AAC5E,QAAM,KAAK,MAAM,MAAA;AACjB,SACE,qBAAC,OAAA,EAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MACjH,UAAA;AAAA,IAAA,qBAAC,UAAK,IAAI,GAAG,EAAE,QAAQ,QAAO,QAC5B,UAAA;AAAA,MAAA,oBAAC,UAAK,OAAM,MAAK,QAAO,MAAK,MAAK,QAAO;AAAA,MACzC,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,OAAM,IAAG,KAAI,MAAK,OAAA,CAAO;AAAA,IAAA,GAChE;AAAA,yBACC,QAAA,EAAK,IAAI,GAAG,EAAE,UAAU,QAAO,QAC9B,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,OAAM,IAAG,OAAM,MAAK,QAAO;AAAA,0BAC/D,QAAA,EAAK,GAAE,OAAM,GAAE,QAAO,YAAW,UAAS,YAAW,gCAA+B,YAAW,OAAM,UAAS,KAAI,MAAK,QACrH,UAAA,MAAA,CACH;AAAA,IAAA,GACF;AAAA,IACA,qBAAC,KAAA,EAAE,MAAM,QAAQ,EAAE,SACjB,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,GAAE,8DAA6D,gBAAe,SAAQ;AAAA,MAC5F,oBAAC,QAAA,EAAK,GAAE,aAAY,gBAAe,QAAA,CAAQ;AAAA,IAAA,GAC7C;AAAA,wBACC,QAAA,EAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,OAAM,IAAG,OAAM,MAAK,gBAAe,QAAO,QAAO,MAAM,QAAQ,EAAE,UAAA,CAAW;AAAA,EAAA,GACnH;AAEJ;AAEA,MAAM,mBAAmB,CAAC,SAAgB,SAAS,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,YAAA,MAAkB,aAAa,QAAQ;AAEvH,MAAM,QAAQ,CAAC,SAAe,KAAK,SAAS;AAE5C,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,gBAAgB,CAAC,SAAe,0BAA0B,KAAK,KAAK,IAAI,KAAK,kBAAkB,SAAS,KAAK,KAAK,aAAa;AAE5I,SAAS,eAAe,EAAE,MAAM,OAAoC;AAClE,MAAI,KAAK,KAAK,WAAW,QAAQ,GAAG;AAClC,WAAO,oBAAC,SAAI,KAAK,KAAK,KAAK,KAAK,MAAM,WAAU,kDAAiD;AAAA,EACnG;AACA,MAAI,MAAM,IAAI,GAAG;AACf,WAAO,oBAAC,YAAA,EAAW,MAAY,OAAO,KAAK,MAAM;AAAA,EACnD;AACA,SACE,oBAAC,SAAI,WAAU,yGACZ,wBAAc,IAAI,wBAAK,cAAA,EAAa,OAAO,iBAAiB,IAAI,GAAG,MAAM,IAAI,wBAAM,cAAA,EAAa,MAAM,IAAI,EAAA,CAC7G;AAEJ;AASA,SAAwB,iBAAiB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAA0B;AACxB,QAAM,QAAQ,OAAyB,IAAI;AAE3C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAqC,MAAM;AACnE,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,EAAE;AACjC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAG9C,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AACf,UAAM,IAAI,OAAO,WAAW,MAAM,YAAY,KAAK,GAAG,GAAI;AAC1D,WAAO,MAAM,OAAO,aAAa,CAAC;AAAA,EACpC,GAAG,CAAC,QAAQ,CAAC;AAIb,QAAM,eAAe,OAAO,SAAS;AACrC,QAAM,kBAAkB,OAAO,YAAY;AAC3C,eAAa,UAAU;AACvB,kBAAgB,UAAU;AAC1B,QAAM,SAAS,QAAQ,SAAS;AAEhC,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,CAAC,QAAQ;AACpB,kBAAY,KAAK;AACjB;AAAA,IACF;AACA,QAAI,QAAQ;AACZ,gBAAY,IAAI;AAChB,WAAO,2DAAwD,EAC5D,KAAK,CAAC,EAAE,kBAAkB,YAAY,MAAM,EAAE,OAAO,CAAC,SAAS,gBAAgB,UAAU,IAAI,KAAK,KAAA,CAAM,CAAC,EACzG,MAAM,MAAM,EAAc,EAC1B,KAAK,CAAC,UAAU;AAEf,UAAI,CAAC,MAAO;AACZ,kBAAY,KAAK;AACjB,mBAAa,UAAU,OAAO,IAAI;AAAA,IACpC,CAAC;AACH,WAAO,MAAM;AACX,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,YAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT,aAAO,EAAE;AACT;AAAA,IACF;AACA,UAAM,UAAU,IAAI,gBAAgB,IAAI;AACxC,WAAO,OAAO;AACd,WAAO,MAAM,IAAI,gBAAgB,OAAO;AAAA,EAC1C,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,OAAO,MAAM;AACjB,QAAI,CAAC,SAAU,OAAM,SAAS,MAAA;AAAA,EAChC;AAEA,QAAM,UAAU,CAAC,SAA0B;AACzC,QAAI,YAAY,CAAC,QAAQ,KAAK,WAAW,EAAG;AAC5C,UAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAClD,QAAI,OAAO;AACT,kBAAY,KAAK;AACjB,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,gBAAY,IAAI;AAChB,eAAW,KAAK;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,MAA2B;AAC5C,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK;AACxC,MAAE,eAAA;AACF,SAAA;AAAA,EACF;AAEA,MAAI,QAAQ;AACZ,MAAI,KAAM,SAAQ;AAClB,MAAI,SAAS,KAAM,SAAQ;AAC3B,MAAI,SAAS,WAAY,SAAQ;AACjC,MAAI,SAAU,UAAS;AAEvB,QAAM,SAAS,QAAQ,KAAK,OAAO,OAAO,MAAM,QAAQ,CAAC,IAAI;AAC7D,QAAM,SAAS,QAAQ,OAAO,gBAAgB,cAAc,MAAM,GAAG,IAAI;AACzE,QAAM,UAAU,QAAQ,MAAM,8BAAW,gBAAA,EAAe,MAAY,KAAU,IAAK;AAEnF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,YAAY,CAAC,MAAM;AACjB,UAAE,eAAA;AACF,YAAI,UAAU;AACZ,YAAE,aAAa,aAAa;AAC5B;AAAA,QACF;AACA,cAAM,QAAQ,MAAM,KAAK,EAAE,aAAa,SAAS,CAAA,CAAE,EAChD,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,EACrC,IAAI,CAAC,SAAS,KAAK,IAAI;AAC1B,cAAM,UAAU,MAAM,UAAU,MAAM,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,MAAM,KAAK,IAAI,aAAa;AACpG,UAAE,aAAa,aAAa,YAAY,aAAa,SAAS;AAC9D,gBAAQ,OAAO;AAAA,MACjB;AAAA,MACA,aAAa,CAAC,MAAM;AAElB,YAAI,EAAE,cAAc,SAAS,EAAE,aAA4B,EAAG;AAE9D,YAAI,SAAS,WAAY,aAAY,IAAI;AACzC,gBAAQ,MAAM;AAAA,MAChB;AAAA,MACA,QAAQ,CAAC,MAAM;AACb,UAAE,eAAA;AACF,gBAAQ,MAAM;AACd,gBAAQ,EAAE,aAAa,KAAK;AAAA,MAC9B;AAAA,MACA,WAAW,sHAAsH,KAAK,IAAI,SAAS;AAAA,MAElJ,UAAA;AAAA,QAAA,CAAC,QACA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAU,WAAW,KAAK;AAAA,YAC1B,iBAAe;AAAA,YACf,SAAS;AAAA,YACT;AAAA,YACA,WAAU;AAAA,YAEV,UAAA;AAAA,cAAA,oBAAC,OAAA,EAAI,WAAU,8JACb,UAAA,oBAAC,cAAW,GACd;AAAA,mCACC,OAAA,EACC,UAAA;AAAA,gBAAA,oBAAC,OAAA,EAAI,WAAU,mDAAmD,UAAA,OAAO,OAAM;AAAA,gBAC9E,OAAO,QAAQ,oBAAC,SAAI,WAAU,sCAAsC,iBAAO,KAAA,CAAK;AAAA,cAAA,EAAA,CACnF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAIH,QACC,qBAAC,OAAA,EAAI,WAAU,uCACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,4FACb,UAAA;AAAA,YAAA,oBAAC,QAAA,EAAK,WAAU,qGACb,UAAA,MAAM,IAAI,IACT,oBAAC,cAAA,EAAa,OAAM,MAAA,CAAM,IACxB,cAAc,IAAI,IACpB,oBAAC,cAAA,EAAa,OAAO,iBAAiB,IAAI,EAAA,CAAG,IAC3C,KAAK,KAAK,WAAW,QAAQ,IAC/B,oBAAC,aAAU,IAEX,oBAAC,cAAA,EAAa,MAAM,IAAI,GAE5B;AAAA,YACA,qBAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,cAAA,oBAAC,OAAA,EAAI,WAAU,oDAAoD,UAAA,KAAK,MAAK;AAAA,cAC7E,qBAAC,OAAA,EAAI,WAAU,sEACb,UAAA;AAAA,gBAAA,qBAAC,QAAA,EAAM,UAAA;AAAA,kBAAA;AAAA,kBAAO;AAAA,gBAAA,GAAG;AAAA,gBAChB,YACC,qBAAC,QAAA,EAAK,MAAK,UAAS,WAAU,oCAC5B,UAAA;AAAA,kBAAA,oBAAC,QAAA,EAAK,eAAW,MAAC,WAAU,wGAAuG;AAAA,kBAClI,OAAO;AAAA,gBAAA,EAAA,CACV;AAAA,cAAA,EAAA,CAEJ;AAAA,YAAA,GACF;AAAA,YACA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL;AAAA,gBACA,SAAS;AAAA,gBACT,WAAU;AAAA,gBAEV,UAAA;AAAA,kBAAA,qBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MAC7G,UAAA;AAAA,oBAAA,oBAAC,QAAA,EAAK,GAAE,8BAA6B,eAAc,SAAQ;AAAA,wCAC1D,QAAA,EAAK,GAAE,cAAa,eAAc,SAAQ,gBAAe,QAAA,CAAQ;AAAA,kBAAA,GACpE;AAAA,kBACC,OAAO;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAEV;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO,OAAO;AAAA,gBACd,cAAY,OAAO;AAAA,gBACnB;AAAA,gBACA,SAAS,MAAM,aAAa,IAAI;AAAA,gBAChC,WAAU;AAAA,gBAEV,UAAA,oBAAC,OAAA,EAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAW,MAC7G,UAAA,oBAAC,QAAA,EAAK,GAAE,6FAA4F,eAAc,SAAQ,gBAAe,QAAA,CAAQ,EAAA,CACnJ;AAAA,cAAA;AAAA,YAAA;AAAA,UACF,GACF;AAAA,UACA,oBAAC,OAAA,EAAI,WAAU,mFACZ,UAAA,QAAA,CACH;AAAA,QAAA,GACF;AAAA,SAGA,YAAY,SAAS,eACrB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YAET,UAAA,OAAO;AAAA,UAAA;AAAA,QAAA;AAAA,QAIZ;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,MAAK;AAAA,YACL;AAAA,YACA,WAAU;AAAA,YACV,UAAU,CAAC,MAAM;AACf,sBAAQ,EAAE,OAAO,KAAK;AACtB,gBAAE,OAAO,QAAQ;AAAA,YACnB;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useCallback, useMemo, useEffect, createContext, useContext } from "react";
|
|
3
|
+
import { CloseIcon, ErrorIcon, WarningIcon, InfoIcon, CheckIcon } from "../../../foundations/icons/icons.js";
|
|
4
|
+
import { useTopLayer, popoverReset } from "../../overlays/topLayer.js";
|
|
5
|
+
const ToastContext = createContext(null);
|
|
6
|
+
function useToast() {
|
|
7
|
+
const api = useContext(ToastContext);
|
|
8
|
+
if (!api) throw new Error("useToast: falta o <ToastProvider> à volta da app");
|
|
9
|
+
return api;
|
|
10
|
+
}
|
|
11
|
+
const TONES = {
|
|
12
|
+
success: { icon: /* @__PURE__ */ jsx(CheckIcon, { size: 16 }), className: "text-ok", duration: 4e3 },
|
|
13
|
+
info: { icon: /* @__PURE__ */ jsx(InfoIcon, { size: 16 }), className: "text-proef-blue", duration: 5e3 },
|
|
14
|
+
warning: { icon: /* @__PURE__ */ jsx(WarningIcon, { size: 16 }), className: "text-risco", duration: 7e3 },
|
|
15
|
+
error: { icon: /* @__PURE__ */ jsx(ErrorIcon, { size: 16 }), className: "text-incumprido", duration: 9e3 }
|
|
16
|
+
};
|
|
17
|
+
function ToastProvider({ children, closeLabel = "Fechar aviso", regionLabel = "Avisos", max = 4 }) {
|
|
18
|
+
const [toasts, setToasts] = useState([]);
|
|
19
|
+
const nextId = useRef(1);
|
|
20
|
+
const dismiss = useCallback((id) => setToasts((list) => list.filter((t) => t.id !== id)), []);
|
|
21
|
+
const api = useMemo(() => {
|
|
22
|
+
const push = (tone) => (message, options = {}) => {
|
|
23
|
+
const id = nextId.current++;
|
|
24
|
+
setToasts((list) => [...list, { id, tone, message, ...options }].slice(-max));
|
|
25
|
+
return id;
|
|
26
|
+
};
|
|
27
|
+
return { success: push("success"), error: push("error"), warning: push("warning"), info: push("info"), dismiss };
|
|
28
|
+
}, [dismiss, max]);
|
|
29
|
+
const regionRef = useRef(null);
|
|
30
|
+
const raise = useTopLayer(regionRef, toasts.length > 0);
|
|
31
|
+
const lastId = toasts[toasts.length - 1]?.id;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (lastId !== void 0) raise();
|
|
34
|
+
}, [lastId, raise]);
|
|
35
|
+
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: api, children: [
|
|
36
|
+
children,
|
|
37
|
+
/* @__PURE__ */ jsx(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
ref: regionRef,
|
|
41
|
+
popover: "manual",
|
|
42
|
+
role: "region",
|
|
43
|
+
"aria-label": regionLabel,
|
|
44
|
+
className: `${popoverReset} pointer-events-none fixed inset-auto right-4 bottom-4 bg-transparent p-0 z-[300] flex w-[min(400px,calc(100vw-2rem))] flex-col gap-2`,
|
|
45
|
+
children: toasts.map((t) => /* @__PURE__ */ jsx(ToastItem, { toast: t, dismiss, closeLabel }, t.id))
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
function ToastItem({ toast, dismiss, closeLabel }) {
|
|
51
|
+
const tone = TONES[toast.tone];
|
|
52
|
+
const duration = toast.duration ?? tone.duration;
|
|
53
|
+
const [paused, setPaused] = useState(false);
|
|
54
|
+
const { id } = toast;
|
|
55
|
+
const onDismiss = useCallback(() => dismiss(id), [dismiss, id]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (paused || duration <= 0) return;
|
|
58
|
+
const timer = setTimeout(onDismiss, duration);
|
|
59
|
+
return () => clearTimeout(timer);
|
|
60
|
+
}, [paused, duration, onDismiss]);
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
role: toast.tone === "error" ? "alert" : "status",
|
|
65
|
+
onMouseEnter: () => setPaused(true),
|
|
66
|
+
onMouseLeave: () => setPaused(false),
|
|
67
|
+
className: "pointer-events-auto flex items-start gap-3 rounded-xl border border-border bg-card px-4 py-3 text-control text-foreground shadow-popover motion-safe:animate-dialog-in",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx("span", { className: `mt-px flex-none ${tone.className}`, children: tone.icon }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
71
|
+
toast.title && /* @__PURE__ */ jsx("p", { className: "m-0 font-semibold", children: toast.title }),
|
|
72
|
+
/* @__PURE__ */ jsx("div", { className: `m-0 break-words whitespace-pre-line ${toast.title ? "text-muted-foreground" : "font-semibold"}`, children: toast.message }),
|
|
73
|
+
toast.action && /* @__PURE__ */ jsx(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
type: "button",
|
|
77
|
+
onClick: () => {
|
|
78
|
+
toast.action.onClick();
|
|
79
|
+
onDismiss();
|
|
80
|
+
},
|
|
81
|
+
className: "mt-1.5 cursor-pointer border-0 bg-transparent p-0 text-control font-semibold text-proef-blue hover:underline focus-visible:underline focus-visible:outline-none",
|
|
82
|
+
children: toast.action.label
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
"button",
|
|
88
|
+
{
|
|
89
|
+
type: "button",
|
|
90
|
+
"aria-label": closeLabel,
|
|
91
|
+
title: closeLabel,
|
|
92
|
+
onClick: onDismiss,
|
|
93
|
+
className: "flex h-6 w-6 flex-none cursor-pointer items-center justify-center rounded-md border-0 bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none",
|
|
94
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { size: 14 })
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
ToastProvider as default,
|
|
103
|
+
useToast
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=Toast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toast.js","sources":["../../../../../../src/v1/components/feedback/Toast/Toast.tsx"],"sourcesContent":["import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\r\nimport { CheckIcon, CloseIcon, ErrorIcon, InfoIcon, WarningIcon } from '../../../foundations/icons/icons'\r\nimport { popoverReset, useTopLayer } from '../../overlays/topLayer'\r\n\r\nexport type ToastTone = 'success' | 'error' | 'warning' | 'info'\r\n\r\nexport interface ToastOptions {\r\n /** Linha em negrito por cima da mensagem. */\r\n title?: string\r\n /** Botão no aviso (ex: Desfazer, Tentar de novo). Fecha o aviso depois de correr. */\r\n action?: { label: string; onClick: () => void }\r\n /**\r\n * Quanto tempo fica visível, em ms. `0`: fica até ser fechado. Por omissão\r\n * depende do tipo: ler um erro demora mais do que um \"Guardado\".\r\n */\r\n duration?: number\r\n}\r\n\r\nexport interface ToastApi {\r\n success: (message: React.ReactNode, options?: ToastOptions) => number\r\n error: (message: React.ReactNode, options?: ToastOptions) => number\r\n warning: (message: React.ReactNode, options?: ToastOptions) => number\r\n info: (message: React.ReactNode, options?: ToastOptions) => number\r\n /** Fecha um aviso pelo número que as funções acima devolvem. */\r\n dismiss: (id: number) => void\r\n}\r\n\r\nexport interface ToastProviderProps {\r\n children: React.ReactNode\r\n /** Nome do botão × de cada aviso, para leitores de ecrã. Por omissão: \"Fechar aviso\". */\r\n closeLabel?: string\r\n /** Nome da zona dos avisos, para leitores de ecrã. Por omissão: \"Avisos\". */\r\n regionLabel?: string\r\n /** Máximo de avisos visíveis ao mesmo tempo; os mais antigos saem. Por omissão 4. */\r\n max?: number\r\n}\r\n\r\ninterface Toast extends ToastOptions {\r\n id: number\r\n tone: ToastTone\r\n message: React.ReactNode\r\n}\r\n\r\nconst ToastContext = createContext<ToastApi | null>(null)\r\n\r\n/**\r\n * Avisos da app. Fora de um ToastProvider dá erro, para não se perderem\r\n * avisos sem ninguém reparar.\r\n */\r\nexport function useToast(): ToastApi {\r\n const api = useContext(ToastContext)\r\n if (!api) throw new Error('useToast: falta o <ToastProvider> à volta da app')\r\n return api\r\n}\r\n\r\nconst TONES: Record<ToastTone, { icon: React.ReactNode; className: string; duration: number }> = {\r\n success: { icon: <CheckIcon size={16} />, className: 'text-ok', duration: 4000 },\r\n info: { icon: <InfoIcon size={16} />, className: 'text-proef-blue', duration: 5000 },\r\n warning: { icon: <WarningIcon size={16} />, className: 'text-risco', duration: 7000 },\r\n error: { icon: <ErrorIcon size={16} />, className: 'text-incumprido', duration: 9000 },\r\n}\r\n\r\n/**\r\n * Avisos do resultado de uma ação, no canto inferior direito (ex: \"Ticket\r\n * guardado\", \"Não foi possível apagar\").\r\n *\r\n * Monta-se uma vez à volta da app; em qualquer ecrã, `useToast()` dá\r\n * `success`, `error`, `warning` e `info`. Desaparecem sozinhos (os erros\r\n * ficam mais tempo); com o rato por cima ficam até ele sair. Aparecem por cima\r\n * de uma janela (Dialog) aberta.\r\n *\r\n * Substituem o `alert()` do browser. Erros de validação de um formulário não\r\n * vão aqui: ficam junto aos campos.\r\n */\r\nexport default function ToastProvider({ children, closeLabel = 'Fechar aviso', regionLabel = 'Avisos', max = 4 }: ToastProviderProps) {\r\n const [toasts, setToasts] = useState<Toast[]>([])\r\n const nextId = useRef(1)\r\n\r\n const dismiss = useCallback((id: number) => setToasts((list) => list.filter((t) => t.id !== id)), [])\r\n\r\n const api = useMemo<ToastApi>(() => {\r\n const push = (tone: ToastTone) => (message: React.ReactNode, options: ToastOptions = {}) => {\r\n const id = nextId.current++\r\n setToasts((list) => [...list, { id, tone, message, ...options }].slice(-max))\r\n return id\r\n }\r\n return { success: push('success'), error: push('error'), warning: push('warning'), info: push('info'), dismiss }\r\n }, [dismiss, max])\r\n\r\n const regionRef = useRef<HTMLDivElement>(null)\r\n const raise = useTopLayer(regionRef, toasts.length > 0)\r\n // Um aviso novo vem para a frente de uma janela que tenha aberto depois do anterior.\r\n const lastId = toasts[toasts.length - 1]?.id\r\n useEffect(() => {\r\n if (lastId !== undefined) raise()\r\n }, [lastId, raise])\r\n\r\n return (\r\n <ToastContext.Provider value={api}>\r\n {children}\r\n <div\r\n ref={regionRef}\r\n popover=\"manual\"\r\n role=\"region\"\r\n aria-label={regionLabel}\r\n className={`${popoverReset} pointer-events-none fixed inset-auto right-4 bottom-4 bg-transparent p-0 z-[300] flex w-[min(400px,calc(100vw-2rem))] flex-col gap-2`}\r\n >\r\n {toasts.map((t) => (\r\n <ToastItem key={t.id} toast={t} dismiss={dismiss} closeLabel={closeLabel} />\r\n ))}\r\n </div>\r\n </ToastContext.Provider>\r\n )\r\n}\r\n\r\nfunction ToastItem({ toast, dismiss, closeLabel }: { toast: Toast; dismiss: (id: number) => void; closeLabel: string }) {\r\n const tone = TONES[toast.tone]\r\n const duration = toast.duration ?? tone.duration\r\n const [paused, setPaused] = useState(false)\r\n const { id } = toast\r\n const onDismiss = useCallback(() => dismiss(id), [dismiss, id])\r\n\r\n // Estável entre renders: um aviso novo não reinicia o tempo dos outros.\r\n useEffect(() => {\r\n if (paused || duration <= 0) return\r\n const timer = setTimeout(onDismiss, duration)\r\n return () => clearTimeout(timer)\r\n }, [paused, duration, onDismiss])\r\n\r\n return (\r\n <div\r\n role={toast.tone === 'error' ? 'alert' : 'status'}\r\n onMouseEnter={() => setPaused(true)}\r\n onMouseLeave={() => setPaused(false)}\r\n className=\"pointer-events-auto flex items-start gap-3 rounded-xl border border-border bg-card px-4 py-3 text-control text-foreground shadow-popover motion-safe:animate-dialog-in\"\r\n >\r\n <span className={`mt-px flex-none ${tone.className}`}>{tone.icon}</span>\r\n <div className=\"min-w-0 flex-1\">\r\n {toast.title && <p className=\"m-0 font-semibold\">{toast.title}</p>}\r\n <div className={`m-0 break-words whitespace-pre-line ${toast.title ? 'text-muted-foreground' : 'font-semibold'}`}>{toast.message}</div>\r\n {toast.action && (\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n toast.action!.onClick()\r\n onDismiss()\r\n }}\r\n className=\"mt-1.5 cursor-pointer border-0 bg-transparent p-0 text-control font-semibold text-proef-blue hover:underline focus-visible:underline focus-visible:outline-none\"\r\n >\r\n {toast.action.label}\r\n </button>\r\n )}\r\n </div>\r\n <button\r\n type=\"button\"\r\n aria-label={closeLabel}\r\n title={closeLabel}\r\n onClick={onDismiss}\r\n className=\"flex h-6 w-6 flex-none cursor-pointer items-center justify-center rounded-md border-0 bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none\"\r\n >\r\n <CloseIcon size={14} />\r\n </button>\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useId } from "react";
|
|
3
|
+
import { FieldContext } from "../../../primitives/forms/field.js";
|
|
4
|
+
import { useFormGrid } from "../FormGrid/FormGrid.js";
|
|
5
|
+
const MARK = 17;
|
|
6
|
+
const MARK_GAP = 4;
|
|
7
|
+
function FormField({ label, required, hint, marks, markSlots, aside, end, wide, alignTop, children }) {
|
|
8
|
+
const id = useId();
|
|
9
|
+
const { columns } = useFormGrid();
|
|
10
|
+
const marksCount = React.Children.count(marks);
|
|
11
|
+
const slots = Math.max(markSlots ?? 0, marksCount);
|
|
12
|
+
return /* @__PURE__ */ jsxs(FieldContext.Provider, { value: { id, required }, children: [
|
|
13
|
+
/* @__PURE__ */ jsxs(
|
|
14
|
+
"div",
|
|
15
|
+
{
|
|
16
|
+
className: `flex flex-col items-end pr-3 text-right @max-form-sm:items-start @max-form-sm:pr-0 @max-form-sm:pt-0 @max-form-sm:text-left ${alignTop ? "self-start pt-[7px]" : ""}`,
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end @max-form-sm:justify-start", children: [
|
|
19
|
+
slots > 0 && /* @__PURE__ */ jsx("span", { className: "mr-[5px] inline-flex flex-none items-center justify-end gap-1", style: { width: slots * MARK + (slots - 1) * MARK_GAP }, children: marks }),
|
|
20
|
+
aside && /* @__PURE__ */ jsx("span", { className: "mr-1.5 flex-none", children: aside }),
|
|
21
|
+
/* @__PURE__ */ jsxs(
|
|
22
|
+
"label",
|
|
23
|
+
{
|
|
24
|
+
htmlFor: id,
|
|
25
|
+
className: "m-0 text-xs leading-[18px] font-medium whitespace-nowrap text-muted-foreground @max-form-md:whitespace-normal",
|
|
26
|
+
children: [
|
|
27
|
+
label,
|
|
28
|
+
required && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "ml-0.5 text-destructive", children: "*" })
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
] }),
|
|
33
|
+
hint && /* @__PURE__ */ jsx("span", { className: "text-micro text-muted-foreground", children: hint })
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: `min-w-0 ${wide && columns === 2 ? "col-span-3 @max-form-sm:col-span-1" : ""} ${end ? "flex items-center gap-1.5" : ""}`, children: [
|
|
38
|
+
children,
|
|
39
|
+
end
|
|
40
|
+
] })
|
|
41
|
+
] });
|
|
42
|
+
}
|
|
43
|
+
function FieldMark({ title, icon }) {
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
"span",
|
|
46
|
+
{
|
|
47
|
+
role: "img",
|
|
48
|
+
"aria-label": title,
|
|
49
|
+
title,
|
|
50
|
+
className: "inline-flex h-[17px] w-[17px] flex-none cursor-help items-center justify-center rounded-[5px] border border-primary/20 bg-primary/5 text-primary",
|
|
51
|
+
children: icon
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
FieldMark,
|
|
57
|
+
FormField as default
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=FormField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormField.js","sources":["../../../../../../src/v1/components/forms/FormField/FormField.tsx"],"sourcesContent":["import React, { useId } from 'react'\r\nimport { FieldContext } from '../../../primitives/forms/field'\r\nimport { useFormGrid } from '../FormGrid/FormGrid'\r\n\r\nexport interface FormFieldProps {\r\n label: string\r\n /** Asterisco vermelho na etiqueta; o campo lá dentro fica `required`. */\r\n required?: boolean\r\n /** Texto pequeno por baixo da etiqueta. */\r\n hint?: string\r\n /**\r\n * Marcas antes da etiqueta, a dizer de onde veio o valor (ex: lido do QR,\r\n * identificado pela IA). Usar [FieldMark](#fieldmark).\r\n */\r\n marks?: React.ReactNode\r\n /**\r\n * Lugares reservados para marcas, mesmo sem nenhuma. Assim a etiqueta não\r\n * salta quando uma marca aparece depois (ex: ao ler o QR).\r\n */\r\n markSlots?: number\r\n /** Algo junto à etiqueta, depois das marcas (ex: o valor máximo permitido). */\r\n aside?: React.ReactNode\r\n /** Depois do campo, na mesma linha (ex: cadeado, ícone de informação). */\r\n end?: React.ReactNode\r\n /** Ocupa a linha toda (ex: observações, anexo). */\r\n wide?: boolean\r\n /** Etiqueta alinhada ao topo em vez de ao meio (campos altos, como o [TextArea](?path=/docs/v1-primitives-forms-textarea--docs)). */\r\n alignTop?: boolean\r\n /** O campo: [TextInput](?path=/docs/v1-primitives-forms-textinput--docs), [SelectInput](?path=/docs/v1-primitives-forms-selectinput--docs), [TextArea](?path=/docs/v1-primitives-forms-textarea--docs), [LookupInput](?path=/docs/v1-primitives-forms-lookupinput--docs) ou outro. */\r\n children: React.ReactNode\r\n}\r\n\r\nconst MARK = 17\r\nconst MARK_GAP = 4\r\n\r\n/**\r\n * Uma linha do formulário: etiqueta e campo. Vai dentro de um\r\n * [FormGrid](?path=/docs/v1-components-forms-formgrid--docs).\r\n *\r\n * Liga a etiqueta ao campo (clicar na etiqueta foca o campo) e passa-lhe o\r\n * `required`, se o campo for [TextInput](?path=/docs/v1-primitives-forms-textinput--docs), [SelectInput](?path=/docs/v1-primitives-forms-selectinput--docs), [TextArea](?path=/docs/v1-primitives-forms-textarea--docs) ou [LookupInput](?path=/docs/v1-primitives-forms-lookupinput--docs).\r\n */\r\nexport default function FormField({ label, required, hint, marks, markSlots, aside, end, wide, alignTop, children }: FormFieldProps) {\r\n const id = useId()\r\n const { columns } = useFormGrid()\r\n const marksCount = React.Children.count(marks)\r\n const slots = Math.max(markSlots ?? 0, marksCount)\r\n\r\n return (\r\n <FieldContext.Provider value={{ id, required }}>\r\n <div\r\n className={`flex flex-col items-end pr-3 text-right @max-form-sm:items-start @max-form-sm:pr-0 @max-form-sm:pt-0 @max-form-sm:text-left ${alignTop ? 'self-start pt-[7px]' : ''}`}\r\n >\r\n {/* As marcas ficam fora do <label>, para não entrarem no nome do campo lido pelos leitores de ecrã. */}\r\n <div className=\"flex items-center justify-end @max-form-sm:justify-start\">\r\n {slots > 0 && (\r\n <span className=\"mr-[5px] inline-flex flex-none items-center justify-end gap-1\" style={{ width: slots * MARK + (slots - 1) * MARK_GAP }}>\r\n {marks}\r\n </span>\r\n )}\r\n {aside && <span className=\"mr-1.5 flex-none\">{aside}</span>}\r\n <label\r\n htmlFor={id}\r\n className=\"m-0 text-xs leading-[18px] font-medium whitespace-nowrap text-muted-foreground @max-form-md:whitespace-normal\"\r\n >\r\n {label}\r\n {required && (\r\n <span aria-hidden className=\"ml-0.5 text-destructive\">\r\n *\r\n </span>\r\n )}\r\n </label>\r\n </div>\r\n {hint && <span className=\"text-micro text-muted-foreground\">{hint}</span>}\r\n </div>\r\n <div className={`min-w-0 ${wide && columns === 2 ? 'col-span-3 @max-form-sm:col-span-1' : ''} ${end ? 'flex items-center gap-1.5' : ''}`}>\r\n {children}\r\n {end}\r\n </div>\r\n </FieldContext.Provider>\r\n )\r\n}\r\n\r\nexport interface FieldMarkProps {\r\n /** De onde veio o valor (ex: \"Lido do QR Code\"). Mostrado ao passar o rato e lido pelos leitores de ecrã. */\r\n title: string\r\n /** O ícone, a 11 px (ex: `<QrCodeIcon size={11} />`). */\r\n icon: React.ReactNode\r\n}\r\n\r\n/** Marca pequena antes da etiqueta de um FormField, a dizer de onde veio o valor. */\r\nexport function FieldMark({ title, icon }: FieldMarkProps) {\r\n return (\r\n <span\r\n role=\"img\"\r\n aria-label={title}\r\n title={title}\r\n className=\"inline-flex h-[17px] w-[17px] flex-none cursor-help items-center justify-center rounded-[5px] border border-primary/20 bg-primary/5 text-primary\"\r\n >\r\n {icon}\r\n </span>\r\n )\r\n}\r\n"],"names":[],"mappings":";;;;AAgCA,MAAM,OAAO;AACb,MAAM,WAAW;AASjB,SAAwB,UAAU,EAAE,OAAO,UAAU,MAAM,OAAO,WAAW,OAAO,KAAK,MAAM,UAAU,SAAA,GAA4B;AACnI,QAAM,KAAK,MAAA;AACX,QAAM,EAAE,QAAA,IAAY,YAAA;AACpB,QAAM,aAAa,MAAM,SAAS,MAAM,KAAK;AAC7C,QAAM,QAAQ,KAAK,IAAI,aAAa,GAAG,UAAU;AAEjD,SACE,qBAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,IAAI,YAClC,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW,+HAA+H,WAAW,wBAAwB,EAAE;AAAA,QAG/K,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,4DACZ,UAAA;AAAA,YAAA,QAAQ,KACP,oBAAC,QAAA,EAAK,WAAU,iEAAgE,OAAO,EAAE,OAAO,QAAQ,QAAQ,QAAQ,KAAK,YAC1H,UAAA,OACH;AAAA,YAED,SAAS,oBAAC,QAAA,EAAK,WAAU,oBAAoB,UAAA,OAAM;AAAA,YACpD;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAS;AAAA,gBACT,WAAU;AAAA,gBAET,UAAA;AAAA,kBAAA;AAAA,kBACA,YACC,oBAAC,QAAA,EAAK,eAAW,MAAC,WAAU,2BAA0B,UAAA,IAAA,CAEtD;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UAEJ,GACF;AAAA,UACC,QAAQ,oBAAC,QAAA,EAAK,WAAU,oCAAoC,UAAA,KAAA,CAAK;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEpE,qBAAC,OAAA,EAAI,WAAW,WAAW,QAAQ,YAAY,IAAI,uCAAuC,EAAE,IAAI,MAAM,8BAA8B,EAAE,IACnI,UAAA;AAAA,MAAA;AAAA,MACA;AAAA,IAAA,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAUO,SAAS,UAAU,EAAE,OAAO,QAAwB;AACzD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ;AAAA,MACA,WAAU;AAAA,MAET,UAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useContext, createContext } from "react";
|
|
3
|
+
const GridContext = createContext({ columns: 2 });
|
|
4
|
+
const useFormGrid = () => useContext(GridContext);
|
|
5
|
+
const COLUMNS = {
|
|
6
|
+
2: "grid-cols-[var(--spacing-form-label)_minmax(0,1fr)_var(--spacing-form-label)_minmax(0,1fr)] @max-form-md:grid-cols-[var(--spacing-form-label-narrow)_minmax(0,1fr)_var(--spacing-form-label-narrow)_minmax(0,1fr)]",
|
|
7
|
+
1: "grid-cols-[var(--spacing-form-label)_minmax(0,1fr)] @max-form-md:grid-cols-[var(--spacing-form-label-narrow)_minmax(0,1fr)]"
|
|
8
|
+
};
|
|
9
|
+
function FormGrid({ columns = 2, className = "", children }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(GridContext.Provider, { value: { columns }, children: /* @__PURE__ */ jsx("div", { className: `@container ${className}`, children: /* @__PURE__ */ jsx("div", { className: `grid items-center gap-x-3.5 gap-y-[13px] @max-form-sm:grid-cols-1 @max-form-sm:gap-y-1.5 ${COLUMNS[columns]}`, children }) }) });
|
|
11
|
+
}
|
|
12
|
+
function FormDivider() {
|
|
13
|
+
return /* @__PURE__ */ jsx("div", { role: "separator", className: "col-span-full my-0.5 h-px bg-border @max-form-sm:my-2" });
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
FormDivider,
|
|
17
|
+
FormGrid as default,
|
|
18
|
+
useFormGrid
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=FormGrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormGrid.js","sources":["../../../../../../src/v1/components/forms/FormGrid/FormGrid.tsx"],"sourcesContent":["import React, { createContext, useContext } from 'react'\r\n\r\nexport interface FormGridProps {\r\n /** Campos por linha: 2 (etiqueta · campo · etiqueta · campo) ou 1. */\r\n columns?: 1 | 2\r\n className?: string\r\n children: React.ReactNode\r\n}\r\n\r\nconst GridContext = createContext<{ columns: 1 | 2 }>({ columns: 2 })\r\nexport const useFormGrid = () => useContext(GridContext)\r\n\r\nconst COLUMNS = {\r\n 2: 'grid-cols-[var(--spacing-form-label)_minmax(0,1fr)_var(--spacing-form-label)_minmax(0,1fr)] @max-form-md:grid-cols-[var(--spacing-form-label-narrow)_minmax(0,1fr)_var(--spacing-form-label-narrow)_minmax(0,1fr)]',\r\n 1: 'grid-cols-[var(--spacing-form-label)_minmax(0,1fr)] @max-form-md:grid-cols-[var(--spacing-form-label-narrow)_minmax(0,1fr)]',\r\n}\r\n\r\n/**\r\n * Grelha de um formulário: etiquetas à esquerda, alinhadas à direita, e os\r\n * campos ao lado. Os filhos são [FormField](?path=/docs/v1-components-forms-formfield--docs)\r\n * e, entre grupos, `FormDivider`.\r\n *\r\n * Adapta-se à largura do sítio onde está (não à do ecrã): abaixo de 680 px as\r\n * etiquetas estreitam; abaixo de 500 px cada etiqueta fica por cima do campo.\r\n *\r\n * **Exemplo completo:** [Formulário em modal](?path=/docs/v1-patterns-forms-formulário-em-modal--docs).\r\n */\r\nexport default function FormGrid({ columns = 2, className = '', children }: FormGridProps) {\r\n return (\r\n <GridContext.Provider value={{ columns }}>\r\n <div className={`@container ${className}`}>\r\n <div className={`grid items-center gap-x-3.5 gap-y-[13px] @max-form-sm:grid-cols-1 @max-form-sm:gap-y-1.5 ${COLUMNS[columns]}`}>{children}</div>\r\n </div>\r\n </GridContext.Provider>\r\n )\r\n}\r\n\r\n/** Linha de separação entre grupos de campos, a toda a largura. */\r\nexport function FormDivider() {\r\n return <div role=\"separator\" className=\"col-span-full my-0.5 h-px bg-border @max-form-sm:my-2\" />\r\n}\r\n"],"names":[],"mappings":";;AASA,MAAM,cAAc,cAAkC,EAAE,SAAS,GAAG;AAC7D,MAAM,cAAc,MAAM,WAAW,WAAW;AAEvD,MAAM,UAAU;AAAA,EACd,GAAG;AAAA,EACH,GAAG;AACL;AAYA,SAAwB,SAAS,EAAE,UAAU,GAAG,YAAY,IAAI,YAA2B;AACzF,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,EAAE,WAC7B,UAAA,oBAAC,OAAA,EAAI,WAAW,cAAc,SAAS,IACrC,UAAA,oBAAC,OAAA,EAAI,WAAW,4FAA4F,QAAQ,OAAO,CAAC,IAAK,SAAA,CAAS,EAAA,CAC5I,EAAA,CACF;AAEJ;AAGO,SAAS,cAAc;AAC5B,SAAO,oBAAC,OAAA,EAAI,MAAK,aAAY,WAAU,yDAAwD;AACjG;"}
|