@proef/common-ui 0.0.0-stage → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/assets/imagem-6ee08a37d9.png +0 -0
- package/dist/esm/assets/imagem-6fcae1b87b.png +0 -0
- package/dist/esm/index.js +301 -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 +214 -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 +178 -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/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/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 +39 -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 +60 -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 +61 -0
- package/dist/types/v1/index.d.ts +81 -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/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/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 +36 -0
- package/dist/types/v1/primitives/navigation/Tabs/Tabs.d.ts +58 -0
- package/package.json +120 -6
- package/README.md +0 -3
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
import { useField, fieldClasses, readOnlyClasses } from "../field.js";
|
|
4
|
+
const isoToBr = (iso) => {
|
|
5
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso ?? "");
|
|
6
|
+
return m ? `${m[3]}/${m[2]}/${m[1]}` : "";
|
|
7
|
+
};
|
|
8
|
+
const brToIso = (br) => {
|
|
9
|
+
const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(br.trim());
|
|
10
|
+
if (!m) return "";
|
|
11
|
+
const [, dd, mm, yyyy] = m;
|
|
12
|
+
const d = /* @__PURE__ */ new Date(`${yyyy}-${mm}-${dd}T00:00:00`);
|
|
13
|
+
if (Number.isNaN(d.getTime()) || d.getDate() !== Number(dd) || d.getMonth() + 1 !== Number(mm)) return "";
|
|
14
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
15
|
+
};
|
|
16
|
+
const mask = (raw) => {
|
|
17
|
+
const d = raw.replace(/\D/g, "").slice(0, 8);
|
|
18
|
+
if (d.length <= 2) return d;
|
|
19
|
+
if (d.length <= 4) return `${d.slice(0, 2)}/${d.slice(2)}`;
|
|
20
|
+
return `${d.slice(0, 2)}/${d.slice(2, 4)}/${d.slice(4)}`;
|
|
21
|
+
};
|
|
22
|
+
function CalendarIcon() {
|
|
23
|
+
return /* @__PURE__ */ jsxs("svg", { "aria-hidden": true, viewBox: "0 0 24 24", width: 13, height: 13, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
24
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }),
|
|
25
|
+
/* @__PURE__ */ jsx("path", { d: "M16 2v4M8 2v4M3 10h18" })
|
|
26
|
+
] });
|
|
27
|
+
}
|
|
28
|
+
function DateInput({
|
|
29
|
+
value,
|
|
30
|
+
defaultValue,
|
|
31
|
+
onChange,
|
|
32
|
+
onBlur,
|
|
33
|
+
onKeyDown,
|
|
34
|
+
min,
|
|
35
|
+
max,
|
|
36
|
+
disabled,
|
|
37
|
+
readOnly,
|
|
38
|
+
required,
|
|
39
|
+
autoFocus,
|
|
40
|
+
id,
|
|
41
|
+
invalid,
|
|
42
|
+
placeholder = "dd/mm/aaaa",
|
|
43
|
+
"aria-label": ariaLabel,
|
|
44
|
+
calendarLabel = "Abrir calendário",
|
|
45
|
+
className = ""
|
|
46
|
+
}) {
|
|
47
|
+
const field = useField();
|
|
48
|
+
const controlled = value !== void 0;
|
|
49
|
+
const [iso, setIso] = useState(value ?? defaultValue ?? "");
|
|
50
|
+
const [text, setText] = useState(isoToBr(value ?? defaultValue ?? ""));
|
|
51
|
+
const nativeRef = useRef(null);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!controlled) return;
|
|
54
|
+
const outside = value ?? "";
|
|
55
|
+
if (outside === brToIso(text)) return;
|
|
56
|
+
setIso(outside);
|
|
57
|
+
setText(isoToBr(outside));
|
|
58
|
+
}, [value, controlled]);
|
|
59
|
+
const onBlurRef = useRef(onBlur);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
onBlurRef.current = onBlur;
|
|
62
|
+
});
|
|
63
|
+
const pendingCommit = useRef(false);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (!pendingCommit.current) return;
|
|
66
|
+
pendingCommit.current = false;
|
|
67
|
+
onBlurRef.current?.();
|
|
68
|
+
}, [value]);
|
|
69
|
+
const emit = (next) => {
|
|
70
|
+
setIso(next);
|
|
71
|
+
onChange?.(next);
|
|
72
|
+
};
|
|
73
|
+
const type = (raw) => {
|
|
74
|
+
const masked = mask(raw);
|
|
75
|
+
setText(masked);
|
|
76
|
+
if (masked === "") return emit("");
|
|
77
|
+
const next = brToIso(masked);
|
|
78
|
+
if (next) emit(next);
|
|
79
|
+
};
|
|
80
|
+
const leave = () => {
|
|
81
|
+
if (text !== "" && !brToIso(text)) setText(isoToBr(iso));
|
|
82
|
+
onBlur?.();
|
|
83
|
+
};
|
|
84
|
+
const openCalendar = () => {
|
|
85
|
+
const el = nativeRef.current;
|
|
86
|
+
if (!el || disabled || readOnly) return;
|
|
87
|
+
if (typeof el.showPicker === "function") el.showPicker();
|
|
88
|
+
else el.click();
|
|
89
|
+
};
|
|
90
|
+
return /* @__PURE__ */ jsxs("span", { className: `relative inline-flex w-full items-center ${className}`, children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
"input",
|
|
93
|
+
{
|
|
94
|
+
id: id ?? field.id,
|
|
95
|
+
type: "text",
|
|
96
|
+
inputMode: "numeric",
|
|
97
|
+
autoComplete: "off",
|
|
98
|
+
"aria-label": ariaLabel,
|
|
99
|
+
"aria-invalid": invalid || void 0,
|
|
100
|
+
placeholder,
|
|
101
|
+
value: text,
|
|
102
|
+
onChange: (e) => type(e.target.value),
|
|
103
|
+
onBlur: leave,
|
|
104
|
+
onKeyDown,
|
|
105
|
+
autoFocus,
|
|
106
|
+
disabled,
|
|
107
|
+
readOnly,
|
|
108
|
+
required: required ?? field.required,
|
|
109
|
+
className: `h-control ${fieldClasses} ${readOnlyClasses} pr-8 tabular-nums`
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
!disabled && !readOnly && /* @__PURE__ */ jsx(
|
|
113
|
+
"button",
|
|
114
|
+
{
|
|
115
|
+
type: "button",
|
|
116
|
+
tabIndex: -1,
|
|
117
|
+
onClick: openCalendar,
|
|
118
|
+
"aria-label": calendarLabel,
|
|
119
|
+
"data-tip": calendarLabel,
|
|
120
|
+
className: "absolute right-1.5 flex h-6 w-6 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-muted-foreground hover:text-foreground",
|
|
121
|
+
children: /* @__PURE__ */ jsx(CalendarIcon, {})
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
"input",
|
|
126
|
+
{
|
|
127
|
+
ref: nativeRef,
|
|
128
|
+
type: "date",
|
|
129
|
+
tabIndex: -1,
|
|
130
|
+
"aria-hidden": true,
|
|
131
|
+
value: iso,
|
|
132
|
+
min,
|
|
133
|
+
max,
|
|
134
|
+
onChange: (e) => {
|
|
135
|
+
const v = e.target.value;
|
|
136
|
+
setText(isoToBr(v));
|
|
137
|
+
emit(v);
|
|
138
|
+
if (controlled) pendingCommit.current = true;
|
|
139
|
+
else onBlurRef.current?.();
|
|
140
|
+
},
|
|
141
|
+
className: "sr-only absolute right-1.5 bottom-0 h-0 w-0 border-0 p-0"
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
] });
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
DateInput,
|
|
148
|
+
DateInput as default
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=DateInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateInput.js","sources":["../../../../../../src/v1/primitives/forms/DateInput/DateInput.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\r\nimport { fieldClasses, readOnlyClasses, useField } from '../field'\r\n\r\nexport interface DateInputProps {\r\n /** Data em ISO (`aaaa-mm-dd`) ou `''` (vazio). Controlado. */\r\n value?: string\r\n /** Valor inicial, sem controlar (ISO). */\r\n defaultValue?: string\r\n /** Recebe só datas completas e válidas, em ISO; `''` quando se apaga. Enquanto se escreve, não é chamado. */\r\n onChange?: (value: string) => void\r\n /** Ao terminar a edição: sair do campo ou escolher no calendário. */\r\n onBlur?: () => void\r\n onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void\r\n /** Limites, em ISO (só no calendário). */\r\n min?: string\r\n max?: string\r\n disabled?: boolean\r\n readOnly?: boolean\r\n required?: boolean\r\n autoFocus?: boolean\r\n id?: string\r\n /** Marca o campo a vermelho (ex: obrigatório por preencher ao submeter). */\r\n invalid?: boolean\r\n placeholder?: string\r\n /** Nome do campo para leitores de ecrã, quando não há etiqueta. */\r\n 'aria-label'?: string\r\n /** Texto do botão do calendário. */\r\n calendarLabel?: string\r\n /** Classes da caixa à volta (ex: largura). */\r\n className?: string\r\n}\r\n\r\nconst isoToBr = (iso: string): string => {\r\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(iso ?? '')\r\n return m ? `${m[3]}/${m[2]}/${m[1]}` : ''\r\n}\r\n\r\nconst brToIso = (br: string): string => {\r\n const m = /^(\\d{2})\\/(\\d{2})\\/(\\d{4})$/.exec(br.trim())\r\n if (!m) return ''\r\n const [, dd, mm, yyyy] = m\r\n const d = new Date(`${yyyy}-${mm}-${dd}T00:00:00`)\r\n // Rejeita 31/02 e afins: o Date normaliza para 03/03 em vez de falhar.\r\n if (Number.isNaN(d.getTime()) || d.getDate() !== Number(dd) || d.getMonth() + 1 !== Number(mm)) return ''\r\n return `${yyyy}-${mm}-${dd}`\r\n}\r\n\r\n/** Mascara o que se escreve, deixando só dígitos e pondo as barras no sítio. */\r\nconst mask = (raw: string): string => {\r\n const d = raw.replace(/\\D/g, '').slice(0, 8)\r\n if (d.length <= 2) return d\r\n if (d.length <= 4) return `${d.slice(0, 2)}/${d.slice(2)}`\r\n return `${d.slice(0, 2)}/${d.slice(2, 4)}/${d.slice(4)}`\r\n}\r\n\r\nfunction CalendarIcon() {\r\n return (\r\n <svg aria-hidden viewBox=\"0 0 24 24\" width={13} height={13} fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" />\r\n <path d=\"M16 2v4M8 2v4M3 10h18\" />\r\n </svg>\r\n )\r\n}\r\n\r\n/**\r\n * Campo de data, sempre em **dd/mm/aaaa**.\r\n *\r\n * O `<input type=\"date\">` do browser mostra a data no idioma do BROWSER, não no\r\n * da página (num Chrome em inglês aparece mm/dd/aaaa), e não há atributo que o\r\n * force. Aqui o texto é escrito pela biblioteca, com máscara; o calendário\r\n * continua a ser o nativo (acessível e traduzido), aberto pelo ícone.\r\n *\r\n * O valor entra e sai em ISO (`aaaa-mm-dd`). Dentro de um\r\n * [FormField](?path=/docs/v1-components-forms-formfield--docs) fica ligado à\r\n * etiqueta automaticamente.\r\n */\r\nexport default function DateInput({\r\n value,\r\n defaultValue,\r\n onChange,\r\n onBlur,\r\n onKeyDown,\r\n min,\r\n max,\r\n disabled,\r\n readOnly,\r\n required,\r\n autoFocus,\r\n id,\r\n invalid,\r\n placeholder = 'dd/mm/aaaa',\r\n 'aria-label': ariaLabel,\r\n calendarLabel = 'Abrir calendário',\r\n className = '',\r\n}: DateInputProps) {\r\n const field = useField()\r\n const controlled = value !== undefined\r\n const [iso, setIso] = useState(value ?? defaultValue ?? '')\r\n const [text, setText] = useState(isoToBr(value ?? defaultValue ?? ''))\r\n const nativeRef = useRef<HTMLInputElement>(null)\r\n\r\n // Segue o valor de fora, exceto enquanto o campo tem uma data a meio: senão\r\n // um re-render durante a escrita apagava os dígitos já introduzidos.\r\n useEffect(() => {\r\n if (!controlled) return\r\n const outside = value ?? ''\r\n if (outside === brToIso(text)) return\r\n setIso(outside)\r\n setText(isoToBr(outside))\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [value, controlled])\r\n\r\n // Escolher no calendário conta como terminar a edição (quem grava está quase\r\n // sempre no `onBlur`). O aviso espera que o valor novo volte em `value`:\r\n // chamado logo, o `onBlur` de fora ainda lia o valor anterior.\r\n const onBlurRef = useRef(onBlur)\r\n useEffect(() => {\r\n onBlurRef.current = onBlur\r\n })\r\n const pendingCommit = useRef(false)\r\n useEffect(() => {\r\n if (!pendingCommit.current) return\r\n pendingCommit.current = false\r\n onBlurRef.current?.()\r\n }, [value])\r\n\r\n const emit = (next: string) => {\r\n setIso(next)\r\n onChange?.(next)\r\n }\r\n\r\n const type = (raw: string) => {\r\n const masked = mask(raw)\r\n setText(masked)\r\n if (masked === '') return emit('')\r\n const next = brToIso(masked)\r\n if (next) emit(next)\r\n }\r\n\r\n // Ao sair, uma data incompleta ou impossível volta ao último valor bom.\r\n const leave = () => {\r\n if (text !== '' && !brToIso(text)) setText(isoToBr(iso))\r\n onBlur?.()\r\n }\r\n\r\n const openCalendar = () => {\r\n const el = nativeRef.current\r\n if (!el || disabled || readOnly) return\r\n // showPicker() é o caminho normal; sem ele (browsers antigos) resta o clique.\r\n if (typeof (el as HTMLInputElement & { showPicker?: () => void }).showPicker === 'function') el.showPicker()\r\n else el.click()\r\n }\r\n\r\n return (\r\n <span className={`relative inline-flex w-full items-center ${className}`}>\r\n <input\r\n id={id ?? field.id}\r\n type=\"text\"\r\n inputMode=\"numeric\"\r\n autoComplete=\"off\"\r\n aria-label={ariaLabel}\r\n aria-invalid={invalid || undefined}\r\n placeholder={placeholder}\r\n value={text}\r\n onChange={(e) => type(e.target.value)}\r\n onBlur={leave}\r\n onKeyDown={onKeyDown}\r\n autoFocus={autoFocus}\r\n disabled={disabled}\r\n readOnly={readOnly}\r\n required={required ?? field.required}\r\n className={`h-control ${fieldClasses} ${readOnlyClasses} pr-8 tabular-nums`}\r\n />\r\n {!disabled && !readOnly && (\r\n <button\r\n type=\"button\"\r\n tabIndex={-1}\r\n onClick={openCalendar}\r\n aria-label={calendarLabel}\r\n data-tip={calendarLabel}\r\n className=\"absolute right-1.5 flex h-6 w-6 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-muted-foreground hover:text-foreground\"\r\n >\r\n <CalendarIcon />\r\n </button>\r\n )}\r\n {/* O nativo só serve de calendário: nunca se vê, por isso o idioma dele não\r\n chega ao ecrã. `sr-only` em vez de display:none — o showPicker() recusa\r\n abrir num campo escondido. */}\r\n <input\r\n ref={nativeRef}\r\n type=\"date\"\r\n tabIndex={-1}\r\n aria-hidden\r\n value={iso}\r\n min={min}\r\n max={max}\r\n onChange={(e) => {\r\n const v = e.target.value\r\n setText(isoToBr(v))\r\n emit(v)\r\n // Sem `value` de fora não há nada por que esperar.\r\n if (controlled) pendingCommit.current = true\r\n else onBlurRef.current?.()\r\n }}\r\n className=\"sr-only absolute right-1.5 bottom-0 h-0 w-0 border-0 p-0\"\r\n />\r\n </span>\r\n )\r\n}\r\n\r\nexport { DateInput }\r\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SearchIcon } from "../../../foundations/icons/icons.js";
|
|
3
|
+
import { useField, fieldClasses, readOnlyClasses } from "../field.js";
|
|
4
|
+
function LookupInput({ value, onLookup, lookupLabel, placeholder, invalid, disabled, id, className = "" }) {
|
|
5
|
+
const field = useField();
|
|
6
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex min-w-0 items-center gap-1.5 ${className}`, children: [
|
|
7
|
+
/* @__PURE__ */ jsx(
|
|
8
|
+
"input",
|
|
9
|
+
{
|
|
10
|
+
id: id ?? field.id,
|
|
11
|
+
readOnly: true,
|
|
12
|
+
required: field.required,
|
|
13
|
+
disabled,
|
|
14
|
+
value,
|
|
15
|
+
placeholder,
|
|
16
|
+
"aria-invalid": invalid || void 0,
|
|
17
|
+
className: `h-control ${fieldClasses} ${readOnlyClasses}`
|
|
18
|
+
}
|
|
19
|
+
),
|
|
20
|
+
/* @__PURE__ */ jsx(
|
|
21
|
+
"button",
|
|
22
|
+
{
|
|
23
|
+
type: "button",
|
|
24
|
+
onClick: onLookup,
|
|
25
|
+
disabled,
|
|
26
|
+
"aria-label": lookupLabel,
|
|
27
|
+
title: lookupLabel,
|
|
28
|
+
className: "flex h-control w-control flex-none cursor-pointer items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground transition-colors duration-150 hover:border-primary/25 hover:bg-primary/5 hover:text-primary focus-visible:border-primary/40 focus-visible:ring-3 focus-visible:ring-primary/10 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-border disabled:hover:bg-muted disabled:hover:text-muted-foreground",
|
|
29
|
+
children: /* @__PURE__ */ jsx(SearchIcon, { size: 14 })
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
] });
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
LookupInput as default
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=LookupInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookupInput.js","sources":["../../../../../../src/v1/primitives/forms/LookupInput/LookupInput.tsx"],"sourcesContent":["import React from 'react'\r\nimport { SearchIcon } from '../../../foundations/icons/icons'\r\nimport { fieldClasses, readOnlyClasses, useField } from '../field'\r\n\r\nexport interface LookupInputProps {\r\n /** O que foi escolhido, como a app o quer mostrar (ex: \"PRJ-0001 - Projeto de exemplo\"). Vazio se nada. */\r\n value: string\r\n /** Chamado ao carregar na lupa: a app abre a sua pesquisa e, quando se escolhe algo, atualiza o `value`. */\r\n onLookup: () => void\r\n /** O que a lupa faz (ex: \"Pesquisar projeto\"). Lido pelos leitores de ecrã e mostrado ao passar o rato. */\r\n lookupLabel: string\r\n /** Texto quando não há nada escolhido (ex: \"Nenhum projeto\"). */\r\n placeholder?: string\r\n /** Marca o campo a vermelho (ex: obrigatório por escolher ao submeter). */\r\n invalid?: boolean\r\n disabled?: boolean\r\n id?: string\r\n /** Aplica-se à caixa à volta (para a largura, por exemplo). */\r\n className?: string\r\n}\r\n\r\n/**\r\n * Campo que **não se escreve**: o valor escolhe-se numa pesquisa, aberta pela\r\n * lupa ao lado (ex: escolher um projeto). A pesquisa é da app; este campo só\r\n * mostra o que foi escolhido.\r\n *\r\n * Dentro de um [FormField](?path=/docs/v1-components-forms-formfield--docs) fica\r\n * ligado à etiqueta automaticamente.\r\n *\r\n * Não é como um `<input>` HTML: só aceita as props da tabela abaixo.\r\n */\r\nexport default function LookupInput({ value, onLookup, lookupLabel, placeholder, invalid, disabled, id, className = '' }: LookupInputProps) {\r\n const field = useField()\r\n return (\r\n <div className={`flex min-w-0 items-center gap-1.5 ${className}`}>\r\n <input\r\n id={id ?? field.id}\r\n readOnly\r\n required={field.required}\r\n disabled={disabled}\r\n value={value}\r\n placeholder={placeholder}\r\n aria-invalid={invalid || undefined}\r\n className={`h-control ${fieldClasses} ${readOnlyClasses}`}\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={onLookup}\r\n disabled={disabled}\r\n aria-label={lookupLabel}\r\n title={lookupLabel}\r\n className={\r\n 'flex h-control w-control flex-none cursor-pointer items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground ' +\r\n 'transition-colors duration-150 hover:border-primary/25 hover:bg-primary/5 hover:text-primary ' +\r\n 'focus-visible:border-primary/40 focus-visible:ring-3 focus-visible:ring-primary/10 focus-visible:outline-none ' +\r\n 'disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-border disabled:hover:bg-muted disabled:hover:text-muted-foreground'\r\n }\r\n >\r\n <SearchIcon size={14} />\r\n </button>\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":";;;AA+BA,SAAwB,YAAY,EAAE,OAAO,UAAU,aAAa,aAAa,SAAS,UAAU,IAAI,YAAY,GAAA,GAAwB;AAC1I,QAAM,QAAQ,SAAA;AACd,SACE,qBAAC,OAAA,EAAI,WAAW,qCAAqC,SAAS,IAC5D,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAI,MAAM,MAAM;AAAA,QAChB,UAAQ;AAAA,QACR,UAAU,MAAM;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,QACA,gBAAc,WAAW;AAAA,QACzB,WAAW,aAAa,YAAY,IAAI,eAAe;AAAA,MAAA;AAAA,IAAA;AAAA,IAEzD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT;AAAA,QACA,cAAY;AAAA,QACZ,OAAO;AAAA,QACP,WACE;AAAA,QAMF,UAAA,oBAAC,YAAA,EAAW,MAAM,GAAA,CAAI;AAAA,MAAA;AAAA,IAAA;AAAA,EACxB,GACF;AAEJ;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { fieldClasses } from "../field.js";
|
|
4
|
+
import { SearchIcon, CloseIcon } from "../../../foundations/icons/icons.js";
|
|
5
|
+
const SearchInput = forwardRef(function SearchInput2({ value, onChange, placeholder = "Pesquisar…", label, clearLabel = "Limpar pesquisa", className = "w-56", icon, onKeyDown, ...props }, ref) {
|
|
6
|
+
return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, children: [
|
|
7
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute top-1/2 left-2.5 flex -translate-y-1/2 text-muted-foreground", children: icon ?? /* @__PURE__ */ jsx(SearchIcon, { size: 14 }) }),
|
|
8
|
+
/* @__PURE__ */ jsx(
|
|
9
|
+
"input",
|
|
10
|
+
{
|
|
11
|
+
ref,
|
|
12
|
+
type: "search",
|
|
13
|
+
value,
|
|
14
|
+
onChange: (e) => onChange(e.target.value),
|
|
15
|
+
onKeyDown: (e) => {
|
|
16
|
+
if (e.key === "Escape" && value) {
|
|
17
|
+
e.stopPropagation();
|
|
18
|
+
onChange("");
|
|
19
|
+
}
|
|
20
|
+
onKeyDown?.(e);
|
|
21
|
+
},
|
|
22
|
+
placeholder,
|
|
23
|
+
"aria-label": label ?? placeholder,
|
|
24
|
+
className: `h-control ${fieldClasses} pr-7 pl-8 [&::-webkit-search-cancel-button]:hidden`,
|
|
25
|
+
...props
|
|
26
|
+
}
|
|
27
|
+
),
|
|
28
|
+
value && /* @__PURE__ */ jsx(
|
|
29
|
+
"button",
|
|
30
|
+
{
|
|
31
|
+
type: "button",
|
|
32
|
+
onClick: () => onChange(""),
|
|
33
|
+
"aria-label": clearLabel,
|
|
34
|
+
className: "absolute top-1/2 right-1.5 flex h-5 w-5 -translate-y-1/2 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-muted-foreground hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none",
|
|
35
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { size: 12 })
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
] });
|
|
39
|
+
});
|
|
40
|
+
export {
|
|
41
|
+
SearchInput as default
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=SearchInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchInput.js","sources":["../../../../../../src/v1/primitives/forms/SearchInput/SearchInput.tsx"],"sourcesContent":["import React, { forwardRef } from 'react'\r\nimport { fieldClasses } from '../field'\r\nimport { CloseIcon, SearchIcon } from '../../../foundations/icons/icons'\r\n\r\nexport interface SearchInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type' | 'className'> {\r\n value: string\r\n /** O texto novo, a cada tecla (e `''` ao limpar). */\r\n onChange: (value: string) => void\r\n /** Nome do campo para leitores de ecrã. Por omissão, o placeholder. */\r\n label?: string\r\n /** Nome do botão ×, para leitores de ecrã. Por omissão: \"Limpar pesquisa\". */\r\n clearLabel?: string\r\n /** Largura e posição (classes do invólucro). Por omissão `w-56`. */\r\n className?: string\r\n /** Ícone à esquerda, no lugar da lupa, quando o filtro é de algo concreto (ex: empresa, colaborador). Tamanho 14. */\r\n icon?: React.ReactNode\r\n}\r\n\r\n/**\r\n * Caixa de pesquisa das barras de páginas, tabelas e listas: a mesma altura\r\n * dos botões ao lado. Lupa à esquerda; com texto, um × para limpar (Esc também\r\n * limpa).\r\n *\r\n * Aceita os outros atributos do `<input>` (`autoFocus`, `name`, `onKeyDown`, …).\r\n */\r\nconst SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(function SearchInput(\r\n { value, onChange, placeholder = 'Pesquisar…', label, clearLabel = 'Limpar pesquisa', className = 'w-56', icon, onKeyDown, ...props },\r\n ref,\r\n) {\r\n return (\r\n <div className={`relative ${className}`}>\r\n <span aria-hidden className=\"pointer-events-none absolute top-1/2 left-2.5 flex -translate-y-1/2 text-muted-foreground\">\r\n {icon ?? <SearchIcon size={14} />}\r\n </span>\r\n <input\r\n ref={ref}\r\n type=\"search\"\r\n value={value}\r\n onChange={(e) => onChange(e.target.value)}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Escape' && value) {\r\n e.stopPropagation()\r\n onChange('')\r\n }\r\n onKeyDown?.(e)\r\n }}\r\n placeholder={placeholder}\r\n aria-label={label ?? placeholder}\r\n className={`h-control ${fieldClasses} pr-7 pl-8 [&::-webkit-search-cancel-button]:hidden`}\r\n {...props}\r\n />\r\n {value && (\r\n <button\r\n type=\"button\"\r\n onClick={() => onChange('')}\r\n aria-label={clearLabel}\r\n className=\"absolute top-1/2 right-1.5 flex h-5 w-5 -translate-y-1/2 cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-muted-foreground hover:text-foreground focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none\"\r\n >\r\n <CloseIcon size={12} />\r\n </button>\r\n )}\r\n </div>\r\n )\r\n})\r\n\r\nexport default SearchInput\r\n"],"names":["SearchInput"],"mappings":";;;;AAyBA,MAAM,cAAc,WAA+C,SAASA,aAC1E,EAAE,OAAO,UAAU,cAAc,cAAc,OAAO,aAAa,mBAAmB,YAAY,QAAQ,MAAM,WAAW,GAAG,MAAA,GAC9H,KACA;AACA,SACE,qBAAC,OAAA,EAAI,WAAW,YAAY,SAAS,IACnC,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,eAAW,MAAC,WAAU,6FACzB,kBAAQ,oBAAC,YAAA,EAAW,MAAM,GAAA,CAAI,EAAA,CACjC;AAAA,IACA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,QACxC,WAAW,CAAC,MAAM;AAChB,cAAI,EAAE,QAAQ,YAAY,OAAO;AAC/B,cAAE,gBAAA;AACF,qBAAS,EAAE;AAAA,UACb;AACA,sBAAY,CAAC;AAAA,QACf;AAAA,QACA;AAAA,QACA,cAAY,SAAS;AAAA,QACrB,WAAW,aAAa,YAAY;AAAA,QACnC,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,IAEL,SACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,SAAS,EAAE;AAAA,QAC1B,cAAY;AAAA,QACZ,WAAU;AAAA,QAEV,UAAA,oBAAC,WAAA,EAAU,MAAM,GAAA,CAAI;AAAA,MAAA;AAAA,IAAA;AAAA,EACvB,GAEJ;AAEJ,CAAC;"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { forwardRef, useImperativeHandle, useState, useMemo, useRef, useEffect } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { ChevronDownIcon } from "../../../foundations/icons/icons.js";
|
|
5
|
+
import { useField, fieldBoxClasses } from "../field.js";
|
|
6
|
+
import { useFloatingPanel, PANEL_CLASS, itemClass } from "../../../capabilities/overlay/useFloatingPanel.js";
|
|
7
|
+
const DEFAULT_SELECT_LABELS = {
|
|
8
|
+
search: "Search…",
|
|
9
|
+
searchIn: "Search in {label}",
|
|
10
|
+
noResults: "No results"
|
|
11
|
+
};
|
|
12
|
+
const Check = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", className: "h-4 w-4 shrink-0 text-proef-blue dark:text-sky-300", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
|
|
13
|
+
function optionsFromChildren(children) {
|
|
14
|
+
const out = [];
|
|
15
|
+
React.Children.forEach(children, (child) => {
|
|
16
|
+
if (!React.isValidElement(child)) return;
|
|
17
|
+
const props = child.props;
|
|
18
|
+
if (child.type === "optgroup") return out.push(...optionsFromChildren(props.children));
|
|
19
|
+
if (child.type !== "option") return;
|
|
20
|
+
const text = React.Children.toArray(props.children).join("");
|
|
21
|
+
out.push({ value: props.value !== void 0 ? String(props.value) : text, label: text, disabled: props.disabled });
|
|
22
|
+
});
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
const SelectInput = forwardRef(function SelectInput2({
|
|
26
|
+
value: controlled,
|
|
27
|
+
defaultValue = "",
|
|
28
|
+
options,
|
|
29
|
+
children,
|
|
30
|
+
onValueChange,
|
|
31
|
+
onChange,
|
|
32
|
+
placeholder,
|
|
33
|
+
placeholderOption = true,
|
|
34
|
+
emptyOption,
|
|
35
|
+
searchable,
|
|
36
|
+
disabled = false,
|
|
37
|
+
required,
|
|
38
|
+
invalid,
|
|
39
|
+
id,
|
|
40
|
+
name,
|
|
41
|
+
"aria-label": ariaLabel,
|
|
42
|
+
"aria-labelledby": ariaLabelledBy,
|
|
43
|
+
variant = "field",
|
|
44
|
+
pillClassName = "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200",
|
|
45
|
+
tall = false,
|
|
46
|
+
labels,
|
|
47
|
+
className = ""
|
|
48
|
+
}, ref) {
|
|
49
|
+
const L = { ...DEFAULT_SELECT_LABELS, ...labels };
|
|
50
|
+
const field = useField();
|
|
51
|
+
const p = useFloatingPanel("left");
|
|
52
|
+
useImperativeHandle(ref, () => p.triggerRef.current);
|
|
53
|
+
const [own, setOwn] = useState(defaultValue);
|
|
54
|
+
const value = controlled ?? own;
|
|
55
|
+
const list = useMemo(() => {
|
|
56
|
+
const base = options ? options.map((o) => typeof o === "string" ? { value: o, label: o } : o) : optionsFromChildren(children);
|
|
57
|
+
const empty = emptyOption ?? (placeholder !== void 0 && placeholderOption ? placeholder : void 0);
|
|
58
|
+
return empty !== void 0 && !base.some((o) => o.value === "") ? [{ value: "", label: empty }, ...base] : base;
|
|
59
|
+
}, [options, children, emptyOption, placeholder, placeholderOption]);
|
|
60
|
+
const [query, setQuery] = useState("");
|
|
61
|
+
const withSearch = searchable ?? list.length > 10;
|
|
62
|
+
const visible = useMemo(() => {
|
|
63
|
+
const q = query.trim().toLowerCase();
|
|
64
|
+
return q ? list.filter((o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)) : list;
|
|
65
|
+
}, [list, query]);
|
|
66
|
+
const [active, setActive] = useState(0);
|
|
67
|
+
const searchRef = useRef(null);
|
|
68
|
+
const chosen = list.find((o) => o.value === value);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!p.open) return;
|
|
71
|
+
setQuery("");
|
|
72
|
+
setActive(Math.max(0, list.findIndex((o) => o.value === value)));
|
|
73
|
+
if (withSearch) setTimeout(() => searchRef.current?.focus(), 0);
|
|
74
|
+
}, [p.open]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (p.open) p.panelRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView?.({ block: "nearest" });
|
|
77
|
+
}, [active, p.open, p.panelRef]);
|
|
78
|
+
const choose = (o) => {
|
|
79
|
+
if (o.disabled) return;
|
|
80
|
+
p.close();
|
|
81
|
+
p.triggerRef.current?.focus();
|
|
82
|
+
if (o.value === value) return;
|
|
83
|
+
if (controlled === void 0) setOwn(o.value);
|
|
84
|
+
onValueChange?.(o.value);
|
|
85
|
+
if (onChange) {
|
|
86
|
+
const target = { value: o.value, name };
|
|
87
|
+
onChange({ target, currentTarget: target });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const keys = (e) => {
|
|
91
|
+
if (!p.open) {
|
|
92
|
+
if (["ArrowDown", "ArrowUp", "Enter", " "].includes(e.key)) {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
p.setOpen(true);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (e.key === "Escape") {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
p.close();
|
|
102
|
+
p.triggerRef.current?.focus();
|
|
103
|
+
} else if (e.key === "ArrowDown") {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
setActive((i) => Math.min(visible.length - 1, i + 1));
|
|
106
|
+
} else if (e.key === "ArrowUp") {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
setActive((i) => Math.max(0, i - 1));
|
|
109
|
+
} else if (e.key === "Home") {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
setActive(0);
|
|
112
|
+
} else if (e.key === "End") {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
setActive(visible.length - 1);
|
|
115
|
+
} else if (e.key === "Enter") {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
const o = visible[active];
|
|
118
|
+
if (o) choose(o);
|
|
119
|
+
} else if (e.key === "Tab") {
|
|
120
|
+
p.close();
|
|
121
|
+
} else if (!withSearch && e.key.length === 1) {
|
|
122
|
+
const i = visible.findIndex((o) => o.label.toLowerCase().startsWith(e.key.toLowerCase()));
|
|
123
|
+
if (i >= 0) setActive(i);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const pill = variant === "pill";
|
|
127
|
+
const showsPlaceholder = !chosen || chosen.value === "" && placeholder !== void 0 && emptyOption === void 0;
|
|
128
|
+
return /* @__PURE__ */ jsxs("div", { className: `relative min-w-0 ${className}`, onKeyDown: keys, children: [
|
|
129
|
+
/* @__PURE__ */ jsxs(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
ref: p.triggerRef,
|
|
133
|
+
id: id ?? field.id,
|
|
134
|
+
type: "button",
|
|
135
|
+
disabled,
|
|
136
|
+
"aria-haspopup": "listbox",
|
|
137
|
+
"aria-expanded": p.open,
|
|
138
|
+
"aria-label": ariaLabel,
|
|
139
|
+
"aria-labelledby": ariaLabelledBy,
|
|
140
|
+
"aria-invalid": invalid || void 0,
|
|
141
|
+
"aria-required": (required ?? field.required) || void 0,
|
|
142
|
+
onClick: (e) => {
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
p.setOpen((o) => !o);
|
|
145
|
+
},
|
|
146
|
+
className: pill ? `inline-flex cursor-pointer appearance-none items-center gap-1 rounded-full px-2.5 py-1 text-xs font-bold whitespace-nowrap transition hover:brightness-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-proef-blue disabled:cursor-not-allowed disabled:opacity-50 ${pillClassName}` : `flex w-full min-w-0 cursor-pointer appearance-none items-center justify-between gap-2 text-left ${tall ? "h-[38px]" : "h-control"} ${fieldBoxClasses}`,
|
|
147
|
+
children: [
|
|
148
|
+
/* @__PURE__ */ jsx("span", { className: `truncate ${showsPlaceholder && !pill ? "text-muted-foreground/70" : ""}`, children: showsPlaceholder ? placeholder ?? chosen?.label ?? "" : chosen?.label }),
|
|
149
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, { size: 14, className: `shrink-0 ${pill ? "opacity-60" : "text-muted-foreground"}` })
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value }),
|
|
154
|
+
p.open && createPortal(
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { ref: p.panelRef, style: p.style, className: PANEL_CLASS, onClick: (e) => e.stopPropagation(), onKeyDown: keys, children: [
|
|
156
|
+
withSearch && /* @__PURE__ */ jsx("div", { className: "px-2 pt-0.5 pb-1", children: /* @__PURE__ */ jsx(
|
|
157
|
+
"input",
|
|
158
|
+
{
|
|
159
|
+
ref: searchRef,
|
|
160
|
+
type: "search",
|
|
161
|
+
value: query,
|
|
162
|
+
onChange: (e) => {
|
|
163
|
+
setQuery(e.target.value);
|
|
164
|
+
setActive(0);
|
|
165
|
+
},
|
|
166
|
+
placeholder: L.search,
|
|
167
|
+
"aria-label": L.searchIn.replace("{label}", ariaLabel ?? ""),
|
|
168
|
+
className: "h-8 w-full rounded-lg border border-slate-300 bg-white px-2.5 text-sm outline-none focus:border-proef-blue dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
|
|
169
|
+
}
|
|
170
|
+
) }),
|
|
171
|
+
/* @__PURE__ */ jsxs("ul", { role: "listbox", "aria-label": ariaLabel, children: [
|
|
172
|
+
visible.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-slate-400", children: L.noResults }),
|
|
173
|
+
visible.map((o, i) => {
|
|
174
|
+
const selected = o.value === value;
|
|
175
|
+
return /* @__PURE__ */ jsxs(
|
|
176
|
+
"li",
|
|
177
|
+
{
|
|
178
|
+
role: "option",
|
|
179
|
+
"aria-selected": selected,
|
|
180
|
+
"aria-disabled": o.disabled || void 0,
|
|
181
|
+
"data-index": i,
|
|
182
|
+
onMouseEnter: () => setActive(i),
|
|
183
|
+
onClick: () => choose(o),
|
|
184
|
+
className: `${itemClass(i === active)} ${o.disabled ? "cursor-not-allowed opacity-50" : ""}`,
|
|
185
|
+
children: [
|
|
186
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
187
|
+
/* @__PURE__ */ jsx("span", { className: `block truncate ${selected ? "font-semibold" : ""} ${o.value === "" ? "text-slate-500 dark:text-slate-400" : ""}`, children: o.label }),
|
|
188
|
+
o.description && /* @__PURE__ */ jsx("span", { className: "block truncate text-[11px] text-slate-500 dark:text-slate-400", children: o.description })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsx("span", { className: "w-4 shrink-0", children: selected && /* @__PURE__ */ jsx(Check, {}) })
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
o.value || "__empty"
|
|
194
|
+
);
|
|
195
|
+
})
|
|
196
|
+
] })
|
|
197
|
+
] }),
|
|
198
|
+
document.body
|
|
199
|
+
)
|
|
200
|
+
] });
|
|
201
|
+
});
|
|
202
|
+
export {
|
|
203
|
+
DEFAULT_SELECT_LABELS,
|
|
204
|
+
SelectInput,
|
|
205
|
+
SelectInput as default
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=SelectInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectInput.js","sources":["../../../../../../src/v1/primitives/forms/SelectInput/SelectInput.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { ChevronDownIcon } from '../../../foundations/icons/icons'\r\nimport { fieldBoxClasses, useField } from '../field'\r\nimport { PANEL_CLASS, itemClass, useFloatingPanel } from '../../../capabilities/overlay/useFloatingPanel'\r\n\r\nexport interface SelectOption {\r\n value: string\r\n label: string\r\n /** Texto pequeno por baixo do rótulo, na lista. */\r\n description?: string\r\n disabled?: boolean\r\n}\r\n\r\nexport interface SelectInputLabels {\r\n /** Texto da caixa de pesquisa. */\r\n search: string\r\n /** Nome da caixa de pesquisa para leitores de ecrã; \"{label}\" é o nome do campo. */\r\n searchIn: string\r\n noResults: string\r\n}\r\n\r\nexport const DEFAULT_SELECT_LABELS: SelectInputLabels = {\r\n search: 'Search…',\r\n searchIn: 'Search in {label}',\r\n noResults: 'No results',\r\n}\r\n\r\nexport interface SelectInputProps {\r\n /** A opção escolhida (controlado). `''` = nenhuma. */\r\n value?: string\r\n defaultValue?: string\r\n /** As opções: texto simples (valor = rótulo) ou `{ value, label, description }`. */\r\n options?: readonly (string | SelectOption)[]\r\n /** Em alternativa a `options`: `<option>` como num `<select>` (compatível com o uso antigo). */\r\n children?: React.ReactNode\r\n /** Recebe o valor escolhido. */\r\n onValueChange?: (value: string) => void\r\n /** Como no `<select>`: `e.target.value` é o valor escolhido. */\r\n onChange?: (e: React.ChangeEvent<HTMLSelectElement>) => void\r\n /** Texto do botão sem nada escolhido; com `placeholderOption`, também a primeira opção (vazia). */\r\n placeholder?: string\r\n /** O `placeholder` é também uma opção (vazia) na lista. Por omissão sim, como no `<select>`. */\r\n placeholderOption?: boolean\r\n /** Primeira opção vazia para desfazer a escolha (ex: \"Todas\"). */\r\n emptyOption?: string\r\n /** Caixa de pesquisa no topo da lista. Por omissão, só com mais de 10 opções. */\r\n searchable?: boolean\r\n disabled?: boolean\r\n required?: boolean\r\n /** Marca o campo a vermelho (ex: obrigatório por escolher ao submeter). */\r\n invalid?: boolean\r\n id?: string\r\n /** Nome no formulário (vai num campo escondido). */\r\n name?: string\r\n 'aria-label'?: string\r\n 'aria-labelledby'?: string\r\n /** `field` (por omissão): campo. `pill`: etiqueta arredondada (ex: o estado numa linha de tabela). A lista é a mesma. */\r\n variant?: 'field' | 'pill'\r\n /** Cores da etiqueta (`pill`), normalmente conforme o valor escolhido; pode trazer contorno (ex: `border border-dashed`). */\r\n pillClassName?: string\r\n /** 38px em vez da altura dos controlos (para alinhar com campos de formulários mais altos). */\r\n tall?: boolean\r\n labels?: Partial<SelectInputLabels>\r\n /** Classes da caixa à volta (ex: a largura). */\r\n className?: string\r\n}\r\n\r\nconst Check = () => (\r\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4 shrink-0 text-proef-blue dark:text-sky-300\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M20 6 9 17l-5-5\" />\r\n </svg>\r\n)\r\n\r\n/** As `<option>` dos filhos como opções (aceita também `<optgroup>`, achatado). */\r\nfunction optionsFromChildren(children: React.ReactNode): SelectOption[] {\r\n const out: SelectOption[] = []\r\n React.Children.forEach(children, (child) => {\r\n if (!React.isValidElement(child)) return\r\n const props = child.props as { value?: unknown; children?: React.ReactNode; disabled?: boolean }\r\n if (child.type === 'optgroup') return out.push(...optionsFromChildren(props.children))\r\n if (child.type !== 'option') return\r\n const text = React.Children.toArray(props.children).join('')\r\n out.push({ value: props.value !== undefined ? String(props.value) : text, label: text, disabled: props.disabled })\r\n })\r\n return out\r\n}\r\n\r\n/**\r\n * Lista de escolha: um campo que abre uma lista desenhada pela biblioteca (não a do sistema, que não segue o\r\n * tema nem o aspeto dos portais). A opção escolhida tem ✓; com muitas opções há pesquisa.\r\n *\r\n * Teclado: ↑/↓, Home/End, Enter escolhe, Esc fecha; sem pesquisa, uma letra salta para a opção.\r\n *\r\n * Dentro de um [FormField](?path=/docs/v1-components-forms-formfield--docs) fica ligado à etiqueta\r\n * automaticamente. Clicar não chega a uma linha clicável por baixo (ex: numa tabela).\r\n *\r\n * Usa-se como um `<select>`: `<option>` como filhos e `onChange` com `e.target.value`; `placeholder`\r\n * acrescenta uma primeira opção vazia.\r\n */\r\nconst SelectInput = forwardRef<HTMLButtonElement, SelectInputProps>(function SelectInput(\r\n {\r\n value: controlled,\r\n defaultValue = '',\r\n options,\r\n children,\r\n onValueChange,\r\n onChange,\r\n placeholder,\r\n placeholderOption = true,\r\n emptyOption,\r\n searchable,\r\n disabled = false,\r\n required,\r\n invalid,\r\n id,\r\n name,\r\n 'aria-label': ariaLabel,\r\n 'aria-labelledby': ariaLabelledBy,\r\n variant = 'field',\r\n pillClassName = 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200',\r\n tall = false,\r\n labels,\r\n className = '',\r\n },\r\n ref,\r\n) {\r\n const L = { ...DEFAULT_SELECT_LABELS, ...labels }\r\n const field = useField()\r\n const p = useFloatingPanel('left')\r\n useImperativeHandle(ref, () => p.triggerRef.current as HTMLButtonElement)\r\n\r\n const [own, setOwn] = useState(defaultValue)\r\n const value = controlled ?? own\r\n\r\n const list = useMemo<SelectOption[]>(() => {\r\n const base = options ? options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o)) : optionsFromChildren(children)\r\n const empty = emptyOption ?? (placeholder !== undefined && placeholderOption ? placeholder : undefined)\r\n return empty !== undefined && !base.some((o) => o.value === '') ? [{ value: '', label: empty }, ...base] : base\r\n }, [options, children, emptyOption, placeholder, placeholderOption])\r\n\r\n const [query, setQuery] = useState('')\r\n const withSearch = searchable ?? list.length > 10\r\n const visible = useMemo(() => {\r\n const q = query.trim().toLowerCase()\r\n return q ? list.filter((o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)) : list\r\n }, [list, query])\r\n const [active, setActive] = useState(0)\r\n const searchRef = useRef<HTMLInputElement>(null)\r\n const chosen = list.find((o) => o.value === value)\r\n\r\n // Ao abrir: limpa a pesquisa e põe o teclado na opção escolhida.\r\n useEffect(() => {\r\n if (!p.open) return\r\n setQuery('')\r\n setActive(Math.max(0, list.findIndex((o) => o.value === value)))\r\n if (withSearch) setTimeout(() => searchRef.current?.focus(), 0)\r\n }, [p.open]) // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n // O item ativo fica sempre à vista ao navegar com o teclado.\r\n useEffect(() => {\r\n if (p.open) p.panelRef.current?.querySelector<HTMLElement>(`[data-index=\"${active}\"]`)?.scrollIntoView?.({ block: 'nearest' })\r\n }, [active, p.open, p.panelRef])\r\n\r\n const choose = (o: SelectOption) => {\r\n if (o.disabled) return\r\n p.close()\r\n p.triggerRef.current?.focus()\r\n if (o.value === value) return\r\n if (controlled === undefined) setOwn(o.value)\r\n onValueChange?.(o.value)\r\n if (onChange) {\r\n const target = { value: o.value, name } as HTMLSelectElement\r\n onChange({ target, currentTarget: target } as React.ChangeEvent<HTMLSelectElement>)\r\n }\r\n }\r\n\r\n const keys = (e: React.KeyboardEvent) => {\r\n if (!p.open) {\r\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {\r\n e.preventDefault()\r\n p.setOpen(true)\r\n }\r\n return\r\n }\r\n if (e.key === 'Escape') {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n p.close()\r\n p.triggerRef.current?.focus()\r\n } else if (e.key === 'ArrowDown') {\r\n e.preventDefault()\r\n setActive((i) => Math.min(visible.length - 1, i + 1))\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault()\r\n setActive((i) => Math.max(0, i - 1))\r\n } else if (e.key === 'Home') {\r\n e.preventDefault()\r\n setActive(0)\r\n } else if (e.key === 'End') {\r\n e.preventDefault()\r\n setActive(visible.length - 1)\r\n } else if (e.key === 'Enter') {\r\n e.preventDefault()\r\n const o = visible[active]\r\n if (o) choose(o)\r\n } else if (e.key === 'Tab') {\r\n p.close()\r\n } else if (!withSearch && e.key.length === 1) {\r\n // Sem pesquisa: a letra salta para a primeira opção que começa por ela.\r\n const i = visible.findIndex((o) => o.label.toLowerCase().startsWith(e.key.toLowerCase()))\r\n if (i >= 0) setActive(i)\r\n }\r\n }\r\n\r\n const pill = variant === 'pill'\r\n const showsPlaceholder = !chosen || (chosen.value === '' && placeholder !== undefined && emptyOption === undefined)\r\n\r\n return (\r\n <div className={`relative min-w-0 ${className}`} onKeyDown={keys}>\r\n <button\r\n ref={p.triggerRef}\r\n id={id ?? field.id}\r\n type=\"button\"\r\n disabled={disabled}\r\n aria-haspopup=\"listbox\"\r\n aria-expanded={p.open}\r\n aria-label={ariaLabel}\r\n aria-labelledby={ariaLabelledBy}\r\n aria-invalid={invalid || undefined}\r\n aria-required={(required ?? field.required) || undefined}\r\n onClick={(e) => {\r\n // Dentro de uma linha clicável (tabelas), abrir a lista não abre a linha.\r\n e.stopPropagation()\r\n p.setOpen((o) => !o)\r\n }}\r\n className={\r\n pill\r\n ? `inline-flex cursor-pointer appearance-none items-center gap-1 rounded-full px-2.5 py-1 text-xs font-bold whitespace-nowrap transition hover:brightness-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-proef-blue disabled:cursor-not-allowed disabled:opacity-50 ${pillClassName}`\r\n : `flex w-full min-w-0 cursor-pointer appearance-none items-center justify-between gap-2 text-left ${tall ? 'h-[38px]' : 'h-control'} ${fieldBoxClasses}`\r\n }\r\n >\r\n <span className={`truncate ${showsPlaceholder && !pill ? 'text-muted-foreground/70' : ''}`}>\r\n {showsPlaceholder ? (placeholder ?? chosen?.label ?? '') : chosen?.label}\r\n </span>\r\n <ChevronDownIcon size={14} className={`shrink-0 ${pill ? 'opacity-60' : 'text-muted-foreground'}`} />\r\n </button>\r\n {name && <input type=\"hidden\" name={name} value={value} />}\r\n {p.open &&\r\n createPortal(\r\n <div ref={p.panelRef} style={p.style} className={PANEL_CLASS} onClick={(e) => e.stopPropagation()} onKeyDown={keys}>\r\n {withSearch && (\r\n <div className=\"px-2 pt-0.5 pb-1\">\r\n <input\r\n ref={searchRef}\r\n type=\"search\"\r\n value={query}\r\n onChange={(e) => {\r\n setQuery(e.target.value)\r\n setActive(0)\r\n }}\r\n placeholder={L.search}\r\n aria-label={L.searchIn.replace('{label}', ariaLabel ?? '')}\r\n className=\"h-8 w-full rounded-lg border border-slate-300 bg-white px-2.5 text-sm outline-none focus:border-proef-blue dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100\"\r\n />\r\n </div>\r\n )}\r\n <ul role=\"listbox\" aria-label={ariaLabel}>\r\n {visible.length === 0 && <li className=\"px-3 py-2 text-slate-400\">{L.noResults}</li>}\r\n {visible.map((o, i) => {\r\n const selected = o.value === value\r\n return (\r\n <li\r\n key={o.value || '__empty'}\r\n role=\"option\"\r\n aria-selected={selected}\r\n aria-disabled={o.disabled || undefined}\r\n data-index={i}\r\n onMouseEnter={() => setActive(i)}\r\n onClick={() => choose(o)}\r\n className={`${itemClass(i === active)} ${o.disabled ? 'cursor-not-allowed opacity-50' : ''}`}\r\n >\r\n <span className=\"min-w-0 flex-1\">\r\n <span className={`block truncate ${selected ? 'font-semibold' : ''} ${o.value === '' ? 'text-slate-500 dark:text-slate-400' : ''}`}>{o.label}</span>\r\n {o.description && <span className=\"block truncate text-[11px] text-slate-500 dark:text-slate-400\">{o.description}</span>}\r\n </span>\r\n <span className=\"w-4 shrink-0\">{selected && <Check />}</span>\r\n </li>\r\n )\r\n })}\r\n </ul>\r\n </div>,\r\n document.body,\r\n )}\r\n </div>\r\n )\r\n})\r\n\r\nexport default SelectInput\r\nexport { SelectInput }\r\n"],"names":["SelectInput"],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState, useRef } from "react";
|
|
3
|
+
import { useField, fieldClasses, readOnlyClasses } from "../field.js";
|
|
4
|
+
const TextArea = forwardRef(function TextArea2({
|
|
5
|
+
invalid,
|
|
6
|
+
counter,
|
|
7
|
+
overflow,
|
|
8
|
+
limitReachedLabel,
|
|
9
|
+
truncatedLabel,
|
|
10
|
+
overflowLabel,
|
|
11
|
+
maxLength: max,
|
|
12
|
+
className = "",
|
|
13
|
+
id,
|
|
14
|
+
required,
|
|
15
|
+
rows = 3,
|
|
16
|
+
onChange,
|
|
17
|
+
onPaste,
|
|
18
|
+
onKeyDown,
|
|
19
|
+
...props
|
|
20
|
+
}, ref) {
|
|
21
|
+
const field = useField();
|
|
22
|
+
const [typed, setTyped] = useState(String(props.defaultValue ?? ""));
|
|
23
|
+
const [blocked, setBlocked] = useState(null);
|
|
24
|
+
const pasting = useRef(false);
|
|
25
|
+
const length = props.value !== void 0 ? String(props.value).length : typed.length;
|
|
26
|
+
const limited = max !== void 0 && !overflow;
|
|
27
|
+
const excess = max !== void 0 && overflow ? Math.max(0, length - max) : 0;
|
|
28
|
+
const isError = excess > 0 && overflow === "error";
|
|
29
|
+
const room = (el) => max - (el.value.length - (el.selectionEnd - el.selectionStart));
|
|
30
|
+
const counterColor = isError ? "text-destructive" : excess > 0 || blocked || max !== void 0 && length >= max * 0.87 ? "text-risco" : length > 0 ? "text-muted-foreground" : "text-muted-foreground/70";
|
|
31
|
+
const message = blocked === "pasted" ? truncatedLabel ?? `Texto cortado: só cabem ${max} caracteres.` : blocked === "typed" ? limitReachedLabel ?? `Chegou ao limite de ${max} caracteres.` : excess > 0 ? overflowLabel?.(excess, max) ?? `${excess} ${excess === 1 ? "carácter" : "caracteres"} a mais (máximo ${max}).` : null;
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { className: `min-w-0 ${className}`, children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
34
|
+
/* @__PURE__ */ jsx(
|
|
35
|
+
"textarea",
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
id: id ?? field.id,
|
|
39
|
+
required: required ?? field.required,
|
|
40
|
+
rows,
|
|
41
|
+
maxLength: limited ? max : void 0,
|
|
42
|
+
"aria-invalid": invalid || isError || void 0,
|
|
43
|
+
onKeyDown: (e) => {
|
|
44
|
+
const writes = (e.key.length === 1 || e.key === "Enter") && !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
45
|
+
if (limited && writes && room(e.currentTarget) <= 0) setBlocked("typed");
|
|
46
|
+
onKeyDown?.(e);
|
|
47
|
+
},
|
|
48
|
+
onPaste: (e) => {
|
|
49
|
+
if (limited) {
|
|
50
|
+
const free = room(e.currentTarget);
|
|
51
|
+
const cut = e.clipboardData.getData("text").length > free;
|
|
52
|
+
pasting.current = cut && free > 0;
|
|
53
|
+
setBlocked(cut ? "pasted" : null);
|
|
54
|
+
}
|
|
55
|
+
onPaste?.(e);
|
|
56
|
+
},
|
|
57
|
+
onChange: (e) => {
|
|
58
|
+
setTyped(e.target.value);
|
|
59
|
+
if (pasting.current) pasting.current = false;
|
|
60
|
+
else setBlocked(null);
|
|
61
|
+
onChange?.(e);
|
|
62
|
+
},
|
|
63
|
+
className: `min-h-[62px] resize-y py-1.5 leading-normal ${counter ? "pb-6" : ""} ${fieldClasses} ${readOnlyClasses}`,
|
|
64
|
+
...props
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
counter && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `pointer-events-none absolute right-2.5 bottom-2 rounded-full bg-muted px-1.5 py-px text-caption font-medium ${counterColor}`, children: max !== void 0 ? `${length}/${max}` : length })
|
|
68
|
+
] }),
|
|
69
|
+
message && /* @__PURE__ */ jsx("p", { role: "status", className: `m-0 mt-1 text-caption font-medium ${isError ? "text-destructive" : "text-risco"}`, children: message })
|
|
70
|
+
] });
|
|
71
|
+
});
|
|
72
|
+
export {
|
|
73
|
+
TextArea as default
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=TextArea.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextArea.js","sources":["../../../../../../src/v1/primitives/forms/TextArea/TextArea.tsx"],"sourcesContent":["import React, { forwardRef, useRef, useState } from 'react'\r\nimport { fieldClasses, readOnlyClasses, useField } from '../field'\r\n\r\nexport interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\r\n /** Marca o campo a vermelho (ex: obrigatório por preencher ao submeter). */\r\n invalid?: boolean\r\n /** Mostra no canto quantos caracteres tem. Com `maxLength`, \"usados/máximo\", e fica âmbar perto do limite. */\r\n counter?: boolean\r\n /**\r\n * Deixar o texto passar o `maxLength`, e como o mostrar:\r\n * - sem `overflow` (por omissão): não passa. Tentar escrever ou colar a mais\r\n * avisa (`limitReachedLabel`, `truncatedLabel`).\r\n * - `warning`: passa, com aviso a âmbar. O campo continua válido.\r\n * - `error`: passa, com aviso a vermelho, e o campo fica inválido.\r\n *\r\n * Com `overflow`, o texto não é cortado: a app tem de verificar o tamanho\r\n * antes de guardar.\r\n */\r\n overflow?: 'warning' | 'error'\r\n /** Sem `overflow`: aviso ao tentar escrever com o campo cheio. Por omissão: \"Chegou ao limite de {maxLength} caracteres.\" */\r\n limitReachedLabel?: string\r\n /** Sem `overflow`: aviso quando um texto colado é cortado. Por omissão: \"Texto cortado: só cabem {maxLength} caracteres.\" */\r\n truncatedLabel?: string\r\n /** Com `overflow`: aviso enquanto o texto passa o limite. Por omissão: \"{a mais} caracteres a mais (máximo {maxLength}).\" */\r\n overflowLabel?: (excess: number, max: number) => string\r\n}\r\n\r\n/**\r\n * Texto em várias linhas. Pode ser esticado na vertical.\r\n *\r\n * Com `counter`, mostra quantos caracteres tem (e o máximo, se houver `maxLength`).\r\n * Com `maxLength`, o texto não passa do limite e o campo avisa quando se tenta\r\n * escrever ou colar a mais. Com `overflow`, pode passar, como aviso ou erro.\r\n *\r\n * Dentro de um [FormField](?path=/docs/v1-components-forms-formfield--docs) fica\r\n * ligado à etiqueta automaticamente.\r\n *\r\n * **Aceita os atributos HTML do [<textarea>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/textarea)**, com o mesmo comportamento: `value`/`onChange`, `required`, `disabled`, `readOnly`, `name`, `placeholder`, `maxLength`, `minLength`, `ref`, ...\r\n * Só os próprios do V1 aparecem na tabela abaixo.\r\n * Diferenças: o `className` aplica-se à caixa à volta; com `overflow`, o `maxLength` deixa de ser imposto.\r\n */\r\nconst TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function TextArea(\r\n {\r\n invalid,\r\n counter,\r\n overflow,\r\n limitReachedLabel,\r\n truncatedLabel,\r\n overflowLabel,\r\n maxLength: max,\r\n className = '',\r\n id,\r\n required,\r\n rows = 3,\r\n onChange,\r\n onPaste,\r\n onKeyDown,\r\n ...props\r\n },\r\n ref,\r\n) {\r\n const field = useField()\r\n // Para o contador sem `value` controlado pela app.\r\n const [typed, setTyped] = useState(String(props.defaultValue ?? ''))\r\n // Sem overflow: a última tentativa de passar o limite, até à edição seguinte.\r\n const [blocked, setBlocked] = useState<null | 'typed' | 'pasted'>(null)\r\n // O onChange que vem logo a seguir a um paste cortado não apaga o aviso.\r\n const pasting = useRef(false)\r\n const length = props.value !== undefined ? String(props.value).length : typed.length\r\n const limited = max !== undefined && !overflow\r\n const excess = max !== undefined && overflow ? Math.max(0, length - max) : 0\r\n const isError = excess > 0 && overflow === 'error'\r\n\r\n // Espaço livre se o texto selecionado for substituído.\r\n const room = (el: HTMLTextAreaElement) => max! - (el.value.length - (el.selectionEnd - el.selectionStart))\r\n\r\n const counterColor = isError\r\n ? 'text-destructive'\r\n : excess > 0 || blocked || (max !== undefined && length >= max * 0.87)\r\n ? 'text-risco'\r\n : length > 0\r\n ? 'text-muted-foreground'\r\n : 'text-muted-foreground/70'\r\n\r\n const message =\r\n blocked === 'pasted'\r\n ? (truncatedLabel ?? `Texto cortado: só cabem ${max} caracteres.`)\r\n : blocked === 'typed'\r\n ? (limitReachedLabel ?? `Chegou ao limite de ${max} caracteres.`)\r\n : excess > 0\r\n ? (overflowLabel?.(excess, max!) ?? `${excess} ${excess === 1 ? 'carácter' : 'caracteres'} a mais (máximo ${max}).`)\r\n : null\r\n\r\n return (\r\n <div className={`min-w-0 ${className}`}>\r\n <div className=\"relative\">\r\n <textarea\r\n ref={ref}\r\n id={id ?? field.id}\r\n required={required ?? field.required}\r\n rows={rows}\r\n // Só sem overflow o browser impõe o limite.\r\n maxLength={limited ? max : undefined}\r\n aria-invalid={invalid || isError || undefined}\r\n onKeyDown={(e) => {\r\n // Tecla que escreveria algo, com o campo cheio: o browser ignora-a em silêncio.\r\n const writes = (e.key.length === 1 || e.key === 'Enter') && !e.ctrlKey && !e.metaKey && !e.altKey\r\n if (limited && writes && room(e.currentTarget) <= 0) setBlocked('typed')\r\n onKeyDown?.(e)\r\n }}\r\n onPaste={(e) => {\r\n if (limited) {\r\n const free = room(e.currentTarget)\r\n const cut = e.clipboardData.getData('text').length > free\r\n // Campo já cheio: o browser não insere nada e não há onChange a seguir.\r\n pasting.current = cut && free > 0\r\n setBlocked(cut ? 'pasted' : null)\r\n }\r\n onPaste?.(e)\r\n }}\r\n onChange={(e) => {\r\n setTyped(e.target.value)\r\n if (pasting.current) pasting.current = false\r\n else setBlocked(null)\r\n onChange?.(e)\r\n }}\r\n className={`min-h-[62px] resize-y py-1.5 leading-normal ${counter ? 'pb-6' : ''} ${fieldClasses} ${readOnlyClasses}`}\r\n {...props}\r\n />\r\n {counter && (\r\n <span aria-hidden className={`pointer-events-none absolute right-2.5 bottom-2 rounded-full bg-muted px-1.5 py-px text-caption font-medium ${counterColor}`}>\r\n {max !== undefined ? `${length}/${max}` : length}\r\n </span>\r\n )}\r\n </div>\r\n {message && (\r\n <p role=\"status\" className={`m-0 mt-1 text-caption font-medium ${isError ? 'text-destructive' : 'text-risco'}`}>\r\n {message}\r\n </p>\r\n )}\r\n </div>\r\n )\r\n})\r\n\r\nexport default TextArea\r\n"],"names":["TextArea"],"mappings":"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"}
|