@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,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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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { useField, fieldClasses, readOnlyClasses } from "../field.js";
|
|
4
|
+
const TextInput = forwardRef(function TextInput2({ invalid, className = "", id, required, ...props }, ref) {
|
|
5
|
+
const field = useField();
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
"input",
|
|
8
|
+
{
|
|
9
|
+
ref,
|
|
10
|
+
id: id ?? field.id,
|
|
11
|
+
required: required ?? field.required,
|
|
12
|
+
"aria-invalid": invalid || void 0,
|
|
13
|
+
className: `h-control ${fieldClasses} ${readOnlyClasses} ${className}`,
|
|
14
|
+
...props
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
export {
|
|
19
|
+
TextInput as default
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=TextInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextInput.js","sources":["../../../../../../src/v1/primitives/forms/TextInput/TextInput.tsx"],"sourcesContent":["import React, { forwardRef } from 'react'\nimport { fieldClasses, readOnlyClasses, useField } from '../field'\n\nexport interface TextInputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n /** Marca o campo a vermelho (ex: obrigatório por preencher ao submeter). */\n invalid?: boolean\n}\n\n/**\n * Campo de texto de uma linha (também data, número, etc. pelo `type`).\n *\n * Dentro de um [FormField](?path=/docs/v1-components-forms-formfield--docs) fica\n * ligado à etiqueta automaticamente.\n *\n * **Aceita os atributos HTML do [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input)**, com o mesmo comportamento: `value`/`onChange`, `required`, `disabled`, `readOnly`, `name`, `placeholder`, `type`, `min`/`max`, `maxLength`, `pattern`, `ref`, ...\n * Só os próprios do V1 aparecem na tabela abaixo.\n */\nconst TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ invalid, className = '', id, required, ...props }, ref) {\n const field = useField()\n return (\n <input\n ref={ref}\n id={id ?? field.id}\n required={required ?? field.required}\n aria-invalid={invalid || undefined}\n className={`h-control ${fieldClasses} ${readOnlyClasses} ${className}`}\n {...props}\n />\n )\n})\n\nexport default TextInput\n"],"names":["TextInput"],"mappings":";;;AAiBA,MAAM,YAAY,WAA6C,SAASA,WAAU,EAAE,SAAS,YAAY,IAAI,IAAI,UAAU,GAAG,MAAA,GAAS,KAAK;AAC1I,QAAM,QAAQ,SAAA;AACd,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,IAAI,MAAM,MAAM;AAAA,MAChB,UAAU,YAAY,MAAM;AAAA,MAC5B,gBAAc,WAAW;AAAA,MACzB,WAAW,aAAa,YAAY,IAAI,eAAe,IAAI,SAAS;AAAA,MACnE,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useContext, createContext } from "react";
|
|
2
|
+
const fieldBoxClasses = "rounded-lg border border-border bg-card px-2.5 text-control text-foreground outline-none dark:border-slate-600 dark:bg-slate-900 transition-[border-color,box-shadow] duration-150 placeholder:text-muted-foreground/70 focus:border-primary/40 focus:ring-3 focus:ring-primary/10 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/15 disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground";
|
|
3
|
+
const fieldClasses = "block w-full min-w-0 " + fieldBoxClasses;
|
|
4
|
+
const readOnlyClasses = "read-only:cursor-default read-only:bg-muted";
|
|
5
|
+
const FieldContext = createContext({});
|
|
6
|
+
const useField = () => useContext(FieldContext);
|
|
7
|
+
export {
|
|
8
|
+
FieldContext,
|
|
9
|
+
fieldBoxClasses,
|
|
10
|
+
fieldClasses,
|
|
11
|
+
readOnlyClasses,
|
|
12
|
+
useField
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field.js","sources":["../../../../../src/v1/primitives/forms/field.ts"],"sourcesContent":["import { createContext, useContext } from 'react'\r\n\r\n/**\r\n * Aspeto comum dos campos (TextInput, SelectInput, TextArea): altura `--spacing-control`, borda\r\n * clara, anel suave no foco, vermelho quando inválido (`aria-invalid`) e cinzento\r\n * quando desativado.\r\n */\r\n// Em escuro: fundo um pouco mais claro do que a página e borda visível (como a CaixaPesquisa dos portais), senão\r\n// o campo desaparece no fundo.\r\n/** O aspeto do campo sem o `display`/largura (para quem é flex, ex: o botão do SelectInput). */\r\nexport const fieldBoxClasses =\r\n 'rounded-lg border border-border bg-card px-2.5 text-control text-foreground outline-none ' +\r\n 'dark:border-slate-600 dark:bg-slate-900 ' +\r\n 'transition-[border-color,box-shadow] duration-150 placeholder:text-muted-foreground/70 ' +\r\n 'focus:border-primary/40 focus:ring-3 focus:ring-primary/10 ' +\r\n 'aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/15 ' +\r\n 'disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground'\r\n\r\nexport const fieldClasses = 'block w-full min-w-0 ' + fieldBoxClasses\r\n\r\n/** Só para campos de texto: um `<select>` também conta como `:read-only` no browser. */\r\nexport const readOnlyClasses = 'read-only:cursor-default read-only:bg-muted'\r\n\r\n/**\r\n * Dado pelo FormField ao campo lá dentro: o `id` que liga a etiqueta ao campo e\r\n * se é obrigatório. Fora de um FormField, os campos funcionam sozinhos.\r\n */\r\nexport const FieldContext = createContext<{ id?: string; required?: boolean }>({})\r\n\r\nexport const useField = () => useContext(FieldContext)\r\n"],"names":[],"mappings":";AAUO,MAAM,kBACX;AAOK,MAAM,eAAe,0BAA0B;AAG/C,MAAM,kBAAkB;AAMxB,MAAM,eAAe,cAAmD,CAAA,CAAE;AAE1E,MAAM,WAAW,MAAM,WAAW,YAAY;"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment as Fragment$1 } from "react";
|
|
3
|
+
import Checkbox from "../../forms/Checkbox/Checkbox.js";
|
|
4
|
+
const LINHA_ESBATIDA = "bg-[linear-gradient(to_right,transparent,var(--color-slate-200)_15%,var(--color-slate-200)_85%,transparent)] bg-[length:100%_1px] bg-bottom bg-no-repeat last:bg-none";
|
|
5
|
+
const semAcento = (c) => c.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
6
|
+
function HighlightText({ text, terms }) {
|
|
7
|
+
const termos = (terms ?? []).map((t) => [...t].map(semAcento).join("")).filter(Boolean);
|
|
8
|
+
if (!termos.length) return /* @__PURE__ */ jsx(Fragment, { children: text });
|
|
9
|
+
const alvo = [...text].map((c) => semAcento(c).charAt(0) || c).join("");
|
|
10
|
+
const marcado = new Array(alvo.length).fill(false);
|
|
11
|
+
for (const t of termos) {
|
|
12
|
+
for (let i = alvo.indexOf(t); i >= 0; i = alvo.indexOf(t, i + 1)) {
|
|
13
|
+
for (let k = i; k < i + t.length; k++) marcado[k] = true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (!marcado.some(Boolean)) return /* @__PURE__ */ jsx(Fragment, { children: text });
|
|
17
|
+
const chars = [...text];
|
|
18
|
+
const partes = [];
|
|
19
|
+
chars.forEach((c, i) => {
|
|
20
|
+
const ultimo = partes[partes.length - 1];
|
|
21
|
+
if (ultimo && ultimo.hit === marcado[i]) ultimo.txt += c;
|
|
22
|
+
else partes.push({ txt: c, hit: marcado[i] });
|
|
23
|
+
});
|
|
24
|
+
return /* @__PURE__ */ jsx(Fragment, { children: partes.map((p, i) => p.hit ? /* @__PURE__ */ jsx("mark", { className: "rounded-sm bg-amber-200 font-bold text-amber-900", children: p.txt }, i) : /* @__PURE__ */ jsx(Fragment$1, { children: p.txt }, i)) });
|
|
25
|
+
}
|
|
26
|
+
function ListItem({
|
|
27
|
+
title,
|
|
28
|
+
code,
|
|
29
|
+
badges,
|
|
30
|
+
meta,
|
|
31
|
+
tag,
|
|
32
|
+
aside,
|
|
33
|
+
active = false,
|
|
34
|
+
onClick,
|
|
35
|
+
checked = false,
|
|
36
|
+
onCheckedChange,
|
|
37
|
+
checkboxLabel,
|
|
38
|
+
highlight,
|
|
39
|
+
className = ""
|
|
40
|
+
}) {
|
|
41
|
+
const fundo = active ? "bg-blue-50" : checked ? "bg-proef-turquoise/5" : "hover:bg-slate-50";
|
|
42
|
+
const realcar = (n) => typeof n === "string" ? /* @__PURE__ */ jsx(HighlightText, { text: n, terms: highlight }) : n;
|
|
43
|
+
return /* @__PURE__ */ jsxs("li", { className: `v1-reset relative flex list-none items-start gap-1.5 px-3 py-3 transition-colors ${LINHA_ESBATIDA} ${fundo} ${className}`, children: [
|
|
44
|
+
onCheckedChange && /* @__PURE__ */ jsxs("span", { className: "flex w-7 shrink-0 flex-col items-center gap-1", children: [
|
|
45
|
+
/* @__PURE__ */ jsx(Checkbox, { checked, onChange: (e) => onCheckedChange(e.target.checked), "aria-label": checkboxLabel, className: "z-10 mt-0.5" }),
|
|
46
|
+
tag && /* @__PURE__ */ jsx("span", { className: "text-[8px] leading-none font-semibold text-slate-400", children: tag })
|
|
47
|
+
] }),
|
|
48
|
+
/* @__PURE__ */ jsxs(
|
|
49
|
+
"button",
|
|
50
|
+
{
|
|
51
|
+
type: "button",
|
|
52
|
+
onClick,
|
|
53
|
+
"aria-current": active || void 0,
|
|
54
|
+
className: "min-w-0 flex-1 space-y-1 text-left outline-none after:absolute after:inset-0 focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-primary",
|
|
55
|
+
children: [
|
|
56
|
+
(code || badges) && /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 leading-tight", children: [
|
|
57
|
+
code && /* @__PURE__ */ jsx("span", { className: "font-mono text-xs font-semibold text-slate-700", children: realcar(code) }),
|
|
58
|
+
badges
|
|
59
|
+
] }),
|
|
60
|
+
/* @__PURE__ */ jsx("span", { className: code ? "line-clamp-2 block text-[10px] leading-snug break-words text-slate-600" : "block truncate text-xs text-slate-700", children: realcar(title) }),
|
|
61
|
+
meta && /* @__PURE__ */ jsx("span", { className: "block truncate text-[9px] leading-tight text-slate-400", children: meta })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
aside && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 flex-col items-end gap-1", children: aside })
|
|
66
|
+
] });
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
HighlightText,
|
|
70
|
+
LINHA_ESBATIDA,
|
|
71
|
+
ListItem as default
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=ListItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListItem.js","sources":["../../../../../../src/v1/primitives/lists/ListItem/ListItem.tsx"],"sourcesContent":["import React, { Fragment } from 'react'\r\nimport Checkbox from '../../forms/Checkbox/Checkbox'\r\n\r\n/** O que se mostra num ListItem: o resto (ativo, marcado) é estado. */\r\nexport interface ListItemContent {\r\n /** Por baixo do código: o que a linha é (ex: a descrição). Até duas linhas, cortada com \"…\" se não couber. */\r\n title: React.ReactNode\r\n /** O texto mais forte da linha, em letra de máquina (ex: o número ou código). */\r\n code?: React.ReactNode\r\n /** Na linha do código, logo a seguir a ele: indicadores e avisos curtos. */\r\n badges?: React.ReactNode\r\n /** Por baixo do título, pequeno e numa linha só (cortada com \"…\"): o resto do que se quer ver de relance, separado por \" · \" (ex: local · data). */\r\n meta?: React.ReactNode\r\n /**\r\n * Opcional, e só com caixa de marcar (`onCheckedChange`): uma etiqueta curta\r\n * (ex: a categoria) no espaço que a caixa abre por baixo dela. Sem caixa não\r\n * se mostra — não há coluna para ela.\r\n */\r\n tag?: React.ReactNode\r\n /** À direita, ao nível do código: marcas que se querem ver mesmo a correr a lista. */\r\n aside?: React.ReactNode\r\n}\r\n\r\nexport interface ListItemProps extends ListItemContent {\r\n /** O que está aberto ao lado (fica realçado). */\r\n active?: boolean\r\n /** Ao clicar (ou Enter/Espaço com o foco nele): normalmente, abri-lo. */\r\n onClick?: () => void\r\n /** Marcado. Só há caixa de marcar se vier com `onCheckedChange`. */\r\n checked?: boolean\r\n onCheckedChange?: (checked: boolean) => void\r\n /** O nome da caixa de marcar, para leitores de ecrã (ex: \"Marcar P-020000\"). */\r\n checkboxLabel?: string\r\n /**\r\n * O que se procurou: realça-se no código e no título quando são texto (sem\r\n * acentos nem maiúsculas). O [ListFinder](?path=/docs/v1-components-lists-listfinder--docs) passa-o sozinho.\r\n */\r\n highlight?: string[]\r\n className?: string\r\n}\r\n\r\n/**\r\n * A linha entre linhas, esbatida nas pontas: uma risca de ponta a ponta\r\n * fechava cada linha numa caixa. Desenhada como fundo (e não com um ::after,\r\n * que o botão da linha já usa); a última linha não a leva.\r\n */\r\nexport const LINHA_ESBATIDA = 'bg-[linear-gradient(to_right,transparent,var(--color-slate-200)_15%,var(--color-slate-200)_85%,transparent)] bg-[length:100%_1px] bg-bottom bg-no-repeat last:bg-none'\r\n\r\nconst semAcento = (c: string) => c.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()\r\n\r\n/**\r\n * Texto com os pedaços que batem com a pesquisa realçados: numa descrição\r\n * comprida, ou num número de dez dígitos, não se via onde é que o que se\r\n * escreveu bateu.\r\n */\r\nexport function HighlightText({ text, terms }: { text: string; terms?: string[] }) {\r\n const termos = (terms ?? []).map(t => [...t].map(semAcento).join('')).filter(Boolean)\r\n if (!termos.length) return <>{text}</>\r\n // Carácter a carácter, para as posições baterem com o texto original.\r\n const alvo = [...text].map(c => semAcento(c).charAt(0) || c).join('')\r\n const marcado = new Array<boolean>(alvo.length).fill(false)\r\n for (const t of termos) {\r\n for (let i = alvo.indexOf(t); i >= 0; i = alvo.indexOf(t, i + 1)) {\r\n for (let k = i; k < i + t.length; k++) marcado[k] = true\r\n }\r\n }\r\n if (!marcado.some(Boolean)) return <>{text}</>\r\n const chars = [...text]\r\n const partes: { txt: string; hit: boolean }[] = []\r\n chars.forEach((c, i) => {\r\n const ultimo = partes[partes.length - 1]\r\n if (ultimo && ultimo.hit === marcado[i]) ultimo.txt += c\r\n else partes.push({ txt: c, hit: marcado[i] })\r\n })\r\n return (\r\n <>\r\n {partes.map((p, i) => p.hit\r\n ? <mark key={i} className=\"rounded-sm bg-amber-200 font-bold text-amber-900\">{p.txt}</mark>\r\n : <Fragment key={i}>{p.txt}</Fragment>)}\r\n </>\r\n )\r\n}\r\n\r\n/**\r\n * Uma linha de uma lista para escolher: o cartão das listas do\r\n * [ListFinder](?path=/docs/v1-components-lists-listfinder--docs).\r\n *\r\n * Não sabe o que mostra: a app dá as partes e o ListItem arruma-as sempre da\r\n * mesma forma. O código é o texto mais forte da linha, com os indicadores ao\r\n * lado; o título vem por baixo, mais pequeno, e o resto por baixo dele. Com\r\n * `onCheckedChange` leva uma caixa à esquerda, para marcar vários (um\r\n * [Checkbox](?path=/docs/v1-primitives-forms-checkbox--docs)), numa coluna de\r\n * largura fixa — senão os títulos começavam em sítios diferentes de linha para\r\n * linha — e, se se quiser, a `tag` por baixo dela.\r\n *\r\n * É um `<li>`: vai dentro de um `<ul>`.\r\n */\r\nexport default function ListItem({\r\n title, code, badges, meta, tag, aside, active = false, onClick, checked = false, onCheckedChange, checkboxLabel,\r\n highlight, className = '',\r\n}: ListItemProps) {\r\n const fundo = active ? 'bg-blue-50' : checked ? 'bg-proef-turquoise/5' : 'hover:bg-slate-50'\r\n const realcar = (n: React.ReactNode) => (typeof n === 'string' ? <HighlightText text={n} terms={highlight} /> : n)\r\n return (\r\n <li className={`v1-reset relative flex list-none items-start gap-1.5 px-3 py-3 transition-colors ${LINHA_ESBATIDA} ${fundo} ${className}`}>\r\n {onCheckedChange && (\r\n <span className=\"flex w-7 shrink-0 flex-col items-center gap-1\">\r\n <Checkbox checked={checked} onChange={e => onCheckedChange(e.target.checked)} aria-label={checkboxLabel} className=\"z-10 mt-0.5\" />\r\n {tag && <span className=\"text-[8px] leading-none font-semibold text-slate-400\">{tag}</span>}\r\n </span>\r\n )}\r\n {/* O botão cobre a linha toda (o ::after), para se clicar em qualquer\r\n sítio; a caixa de marcar fica por cima dele. */}\r\n <button\r\n type=\"button\"\r\n onClick={onClick}\r\n aria-current={active || undefined}\r\n className=\"min-w-0 flex-1 space-y-1 text-left outline-none after:absolute after:inset-0 focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-primary\"\r\n >\r\n {(code || badges) && (\r\n <span className=\"flex flex-wrap items-center gap-x-2 gap-y-0.5 leading-tight\">\r\n {code && <span className=\"font-mono text-xs font-semibold text-slate-700\">{realcar(code)}</span>}\r\n {badges}\r\n </span>\r\n )}\r\n {/* Sem código, o título é a linha principal. */}\r\n <span className={code\r\n ? 'line-clamp-2 block text-[10px] leading-snug break-words text-slate-600'\r\n : 'block truncate text-xs text-slate-700'}>\r\n {realcar(title)}\r\n </span>\r\n {meta && <span className=\"block truncate text-[9px] leading-tight text-slate-400\">{meta}</span>}\r\n </button>\r\n {aside && <span className=\"flex shrink-0 flex-col items-end gap-1\">{aside}</span>}\r\n </li>\r\n )\r\n}\r\n"],"names":["Fragment"],"mappings":";;;AA8CO,MAAM,iBAAiB;AAE9B,MAAM,YAAY,CAAC,MAAc,EAAE,UAAU,KAAK,EAAE,QAAQ,UAAU,EAAE,EAAE,YAAA;AAOnE,SAAS,cAAc,EAAE,MAAM,SAA6C;AACjF,QAAM,UAAU,SAAS,CAAA,GAAI,IAAI,CAAA,MAAK,CAAC,GAAG,CAAC,EAAE,IAAI,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,OAAO;AACpF,MAAI,CAAC,OAAO,OAAQ,wCAAU,UAAA,MAAK;AAEnC,QAAM,OAAO,CAAC,GAAG,IAAI,EAAE,IAAI,CAAA,MAAK,UAAU,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE;AACpE,QAAM,UAAU,IAAI,MAAe,KAAK,MAAM,EAAE,KAAK,KAAK;AAC1D,aAAW,KAAK,QAAQ;AACtB,aAAS,IAAI,KAAK,QAAQ,CAAC,GAAG,KAAK,GAAG,IAAI,KAAK,QAAQ,GAAG,IAAI,CAAC,GAAG;AAChE,eAAS,IAAI,GAAG,IAAI,IAAI,EAAE,QAAQ,IAAK,SAAQ,CAAC,IAAI;AAAA,IACtD;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,wCAAU,UAAA,MAAK;AAC3C,QAAM,QAAQ,CAAC,GAAG,IAAI;AACtB,QAAM,SAA0C,CAAA;AAChD,QAAM,QAAQ,CAAC,GAAG,MAAM;AACtB,UAAM,SAAS,OAAO,OAAO,SAAS,CAAC;AACvC,QAAI,UAAU,OAAO,QAAQ,QAAQ,CAAC,UAAU,OAAO;AAAA,QAClD,QAAO,KAAK,EAAE,KAAK,GAAG,KAAK,QAAQ,CAAC,GAAG;AAAA,EAC9C,CAAC;AACD,SACE,oBAAA,UAAA,EACG,iBAAO,IAAI,CAAC,GAAG,MAAM,EAAE,MACpB,oBAAC,QAAA,EAAa,WAAU,oDAAoD,UAAA,EAAE,IAAA,GAAnE,CAAuE,IAClF,oBAACA,YAAA,EAAkB,UAAA,EAAE,IAAA,GAAN,CAAU,CAAW,EAAA,CAC1C;AAEJ;AAgBA,SAAwB,SAAS;AAAA,EAC/B;AAAA,EAAO;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAK;AAAA,EAAO,SAAS;AAAA,EAAO;AAAA,EAAS,UAAU;AAAA,EAAO;AAAA,EAAiB;AAAA,EAClG;AAAA,EAAW,YAAY;AACzB,GAAkB;AAChB,QAAM,QAAQ,SAAS,eAAe,UAAU,yBAAyB;AACzE,QAAM,UAAU,CAAC,MAAwB,OAAO,MAAM,WAAW,oBAAC,eAAA,EAAc,MAAM,GAAG,OAAO,UAAA,CAAW,IAAK;AAChH,SACE,qBAAC,QAAG,WAAW,oFAAoF,cAAc,IAAI,KAAK,IAAI,SAAS,IACpI,UAAA;AAAA,IAAA,mBACC,qBAAC,QAAA,EAAK,WAAU,iDACd,UAAA;AAAA,MAAA,oBAAC,UAAA,EAAS,SAAkB,UAAU,CAAA,MAAK,gBAAgB,EAAE,OAAO,OAAO,GAAG,cAAY,eAAe,WAAU,eAAc;AAAA,MAChI,OAAO,oBAAC,QAAA,EAAK,WAAU,wDAAwD,UAAA,IAAA,CAAI;AAAA,IAAA,GACtF;AAAA,IAIF;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA,gBAAc,UAAU;AAAA,QACxB,WAAU;AAAA,QAER,UAAA;AAAA,WAAA,QAAQ,WACR,qBAAC,QAAA,EAAK,WAAU,+DACb,UAAA;AAAA,YAAA,4BAAS,QAAA,EAAK,WAAU,kDAAkD,UAAA,QAAQ,IAAI,GAAE;AAAA,YACxF;AAAA,UAAA,GACH;AAAA,UAGF,oBAAC,UAAK,WAAW,OACb,2EACA,yCACD,UAAA,QAAQ,KAAK,GAChB;AAAA,UACC,QAAQ,oBAAC,QAAA,EAAK,WAAU,0DAA0D,UAAA,KAAA,CAAK;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEzF,SAAS,oBAAC,QAAA,EAAK,WAAU,0CAA0C,UAAA,MAAA,CAAM;AAAA,EAAA,GAC5E;AAEJ;"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { SEGMENTED } from "../../buttons/SegmentedControl/segmentedStyles.js";
|
|
4
|
+
const STYLES = {
|
|
5
|
+
underline: {
|
|
6
|
+
list: "flex flex-wrap border-b border-border",
|
|
7
|
+
tab: "-mb-px h-11 border-0 border-b-[3px] px-4",
|
|
8
|
+
active: "border-primary text-primary dark:border-foreground dark:text-foreground",
|
|
9
|
+
idle: "border-transparent bg-transparent text-muted-foreground hover:text-primary dark:hover:text-foreground",
|
|
10
|
+
countActive: "bg-primary text-primary-foreground dark:bg-proef-blue dark:text-white"
|
|
11
|
+
},
|
|
12
|
+
// O mesmo aspeto do SegmentedControl (definido num só sítio).
|
|
13
|
+
segmented: {
|
|
14
|
+
list: SEGMENTED.list,
|
|
15
|
+
tab: "h-[26px] rounded-md border-0",
|
|
16
|
+
active: SEGMENTED.active,
|
|
17
|
+
idle: SEGMENTED.idle,
|
|
18
|
+
countActive: "bg-primary-foreground/20"
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function Tabs(props) {
|
|
22
|
+
const { items, label, variant = "underline", iconOnly = false, className = "" } = props;
|
|
23
|
+
const refs = useRef([]);
|
|
24
|
+
const s = STYLES[variant];
|
|
25
|
+
const isActive = (id) => {
|
|
26
|
+
if (!props.multiple) return id === props.value;
|
|
27
|
+
if (props.allId !== void 0 && id === props.allId) return props.value.length === 0;
|
|
28
|
+
return props.value.includes(id);
|
|
29
|
+
};
|
|
30
|
+
const select = (id, additive) => {
|
|
31
|
+
if (!props.multiple) return props.onChange(id);
|
|
32
|
+
if (props.allId !== void 0 && id === props.allId) return props.onChange([], id);
|
|
33
|
+
if (!additive) return props.onChange([id], id);
|
|
34
|
+
props.onChange(props.value.includes(id) ? props.value.filter((v) => v !== id) : [...props.value, id], id);
|
|
35
|
+
};
|
|
36
|
+
const move = (from, step) => {
|
|
37
|
+
const n = items.length;
|
|
38
|
+
for (let i = 1; i <= n; i++) {
|
|
39
|
+
const next = ((from + step * i) % n + n) % n;
|
|
40
|
+
if (!items[next].disabled) {
|
|
41
|
+
select(items[next].id, false);
|
|
42
|
+
refs.current[next]?.focus();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const onKeyDown = (e, index) => {
|
|
48
|
+
if (e.key === "ArrowRight") move(index, 1);
|
|
49
|
+
else if (e.key === "ArrowLeft") move(index, -1);
|
|
50
|
+
else if (e.key === "Home") move(-1, 1);
|
|
51
|
+
else if (e.key === "End") move(items.length, -1);
|
|
52
|
+
else return;
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
};
|
|
55
|
+
const focusIndex = Math.max(0, items.findIndex((it) => isActive(it.id)));
|
|
56
|
+
const padding = iconOnly ? "w-[26px] justify-center px-0" : "px-3";
|
|
57
|
+
return /* @__PURE__ */ jsx("div", { role: "tablist", "aria-label": label, "aria-multiselectable": props.multiple || void 0, className: `${s.list} ${className}`, children: items.map((item, i) => {
|
|
58
|
+
const active = isActive(item.id);
|
|
59
|
+
const text = typeof item.label === "string" ? item.label : void 0;
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
"button",
|
|
62
|
+
{
|
|
63
|
+
ref: (el) => {
|
|
64
|
+
refs.current[i] = el;
|
|
65
|
+
},
|
|
66
|
+
type: "button",
|
|
67
|
+
role: "tab",
|
|
68
|
+
"aria-selected": active,
|
|
69
|
+
"aria-label": iconOnly ? text : void 0,
|
|
70
|
+
"data-tip": item.tip ?? (iconOnly ? text : void 0),
|
|
71
|
+
"data-tip-title": item.tip ? text : void 0,
|
|
72
|
+
tabIndex: i === focusIndex ? 0 : -1,
|
|
73
|
+
disabled: item.disabled,
|
|
74
|
+
onClick: (e) => select(item.id, e.ctrlKey || e.metaKey || e.shiftKey),
|
|
75
|
+
onKeyDown: (e) => onKeyDown(e, i),
|
|
76
|
+
className: `inline-flex cursor-pointer items-center gap-2 text-caption font-black tracking-wider whitespace-nowrap uppercase transition-colors ${s.tab} ${variant === "underline" ? "px-4" : padding} focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40 ` + (active ? s.active : s.idle),
|
|
77
|
+
children: [
|
|
78
|
+
item.icon,
|
|
79
|
+
!iconOnly && item.label,
|
|
80
|
+
item.count !== void 0 && /* @__PURE__ */ jsx(
|
|
81
|
+
"span",
|
|
82
|
+
{
|
|
83
|
+
className: `min-w-[18px] rounded-full px-1.5 py-px text-center text-micro font-extrabold tracking-normal tabular-nums ${active ? s.countActive : "bg-muted text-foreground"}`,
|
|
84
|
+
children: item.count
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
item.id
|
|
90
|
+
);
|
|
91
|
+
}) });
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
Tabs as default
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=Tabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tabs.js","sources":["../../../../../../src/v1/primitives/navigation/Tabs/Tabs.tsx"],"sourcesContent":["import React, { useRef } from 'react'\r\nimport { SEGMENTED } from '../../buttons/SegmentedControl/segmentedStyles'\r\n\r\nexport interface TabItem<K extends string = string> {\r\n id: K\r\n /** Normalmente texto; aceita JSX. Com `iconOnly`, tem de ser texto (vai para o nome do botão e para o tooltip). */\r\n label: React.ReactNode\r\n /** Ícone antes do texto (ex: um ícone do lucide com `size={14}`). */\r\n icon?: React.ReactNode\r\n /** Número ao lado do texto (ex: pendentes). Não aparece se for `undefined`. */\r\n count?: number\r\n /** Explicação no tooltip (`data-tip`, com o `TooltipLayer`), com o `label` como título. */\r\n tip?: string\r\n disabled?: boolean\r\n}\r\n\r\ninterface TabsBase<K extends string> {\r\n items: TabItem<K>[]\r\n /** Nome do conjunto, para leitores de ecrã (ex: \"Estado das faturas\"). */\r\n label?: string\r\n /**\r\n * `underline` (por omissão): separadores da página, com a linha por baixo.\r\n * `segmented`: botões juntos numa caixa, para trocar de vista dentro de uma\r\n * secção (ex: Lista · Quadro).\r\n */\r\n variant?: 'underline' | 'segmented'\r\n /** Só os ícones; o `label` fica no tooltip e no nome do botão. */\r\n iconOnly?: boolean\r\n className?: string\r\n}\r\n\r\nexport interface TabsSingleProps<K extends string = string> extends TabsBase<K> {\r\n multiple?: false\r\n /** O separador escolhido. A app guarda este estado. */\r\n value: K\r\n onChange: (id: K) => void\r\n}\r\n\r\nexport interface TabsMultipleProps<K extends string = string> extends TabsBase<K> {\r\n /**\r\n * Vários escolhidos ao mesmo tempo: um clique escolhe só esse separador;\r\n * Ctrl/⌘/Shift + clique junta-o ou tira-o dos escolhidos.\r\n */\r\n multiple: true\r\n value: K[]\r\n /** Os escolhidos depois do clique, e o separador clicado. */\r\n onChange: (ids: K[], clicked: K) => void\r\n /** O separador \"Todos\": escolhê-lo limpa a escolha, e fica ativo quando não há nenhum escolhido. */\r\n allId?: K\r\n}\r\n\r\nexport type TabsProps<K extends string = string> = TabsSingleProps<K> | TabsMultipleProps<K>\r\n\r\nconst STYLES = {\r\n underline: {\r\n list: 'flex flex-wrap border-b border-border',\r\n tab: '-mb-px h-11 border-0 border-b-[3px] px-4',\r\n active: 'border-primary text-primary dark:border-foreground dark:text-foreground',\r\n idle: 'border-transparent bg-transparent text-muted-foreground hover:text-primary dark:hover:text-foreground',\r\n countActive: 'bg-primary text-primary-foreground dark:bg-proef-blue dark:text-white',\r\n },\r\n // O mesmo aspeto do SegmentedControl (definido num só sítio).\r\n segmented: {\r\n list: SEGMENTED.list,\r\n tab: 'h-[26px] rounded-md border-0',\r\n active: SEGMENTED.active,\r\n idle: SEGMENTED.idle,\r\n countActive: 'bg-primary-foreground/20',\r\n },\r\n}\r\n\r\n/**\r\n * Separadores: escolher uma de várias vistas do mesmo ecrã.\r\n *\r\n * Só desenha os botões; o conteúdo de cada vista fica na app, que mostra o\r\n * que corresponde a `value`. Setas ← → (e Home/End) mudam de separador,\r\n * saltando os desativados. O escolhido fica a navy (não turquesa: turquesa\r\n * sobre branco não tem contraste para texto).\r\n *\r\n * Com `multiple`, vários podem estar escolhidos (ex: estados de uma lista).\r\n */\r\nexport default function Tabs<K extends string = string>(props: TabsProps<K>) {\r\n const { items, label, variant = 'underline', iconOnly = false, className = '' } = props\r\n const refs = useRef<(HTMLButtonElement | null)[]>([])\r\n const s = STYLES[variant]\r\n\r\n const isActive = (id: K) => {\r\n if (!props.multiple) return id === props.value\r\n if (props.allId !== undefined && id === props.allId) return props.value.length === 0\r\n return props.value.includes(id)\r\n }\r\n\r\n const select = (id: K, additive: boolean) => {\r\n if (!props.multiple) return props.onChange(id)\r\n if (props.allId !== undefined && id === props.allId) return props.onChange([], id)\r\n if (!additive) return props.onChange([id], id)\r\n props.onChange(props.value.includes(id) ? props.value.filter((v) => v !== id) : [...props.value, id], id)\r\n }\r\n\r\n const move = (from: number, step: number) => {\r\n const n = items.length\r\n for (let i = 1; i <= n; i++) {\r\n const next = (((from + step * i) % n) + n) % n\r\n if (!items[next].disabled) {\r\n select(items[next].id, false)\r\n refs.current[next]?.focus()\r\n return\r\n }\r\n }\r\n }\r\n\r\n const onKeyDown = (e: React.KeyboardEvent, index: number) => {\r\n if (e.key === 'ArrowRight') move(index, 1)\r\n else if (e.key === 'ArrowLeft') move(index, -1)\r\n else if (e.key === 'Home') move(-1, 1)\r\n else if (e.key === 'End') move(items.length, -1)\r\n else return\r\n e.preventDefault()\r\n }\r\n\r\n // O primeiro escolhido entra no Tab do teclado (ou o primeiro, se nenhum).\r\n const focusIndex = Math.max(0, items.findIndex((it) => isActive(it.id)))\r\n const padding = iconOnly ? 'w-[26px] justify-center px-0' : 'px-3'\r\n\r\n return (\r\n <div role=\"tablist\" aria-label={label} aria-multiselectable={props.multiple || undefined} className={`${s.list} ${className}`}>\r\n {items.map((item, i) => {\r\n const active = isActive(item.id)\r\n const text = typeof item.label === 'string' ? item.label : undefined\r\n return (\r\n <button\r\n key={item.id}\r\n ref={(el) => {\r\n refs.current[i] = el\r\n }}\r\n type=\"button\"\r\n role=\"tab\"\r\n aria-selected={active}\r\n aria-label={iconOnly ? text : undefined}\r\n data-tip={item.tip ?? (iconOnly ? text : undefined)}\r\n data-tip-title={item.tip ? text : undefined}\r\n tabIndex={i === focusIndex ? 0 : -1}\r\n disabled={item.disabled}\r\n onClick={(e) => select(item.id, e.ctrlKey || e.metaKey || e.shiftKey)}\r\n onKeyDown={(e) => onKeyDown(e, i)}\r\n className={\r\n `inline-flex cursor-pointer items-center gap-2 text-caption font-black tracking-wider whitespace-nowrap uppercase transition-colors ${s.tab} ${variant === 'underline' ? 'px-4' : padding} ` +\r\n 'focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40 ' +\r\n (active ? s.active : s.idle)\r\n }\r\n >\r\n {item.icon}\r\n {!iconOnly && item.label}\r\n {item.count !== undefined && (\r\n <span\r\n className={`min-w-[18px] rounded-full px-1.5 py-px text-center text-micro font-extrabold tracking-normal tabular-nums ${\r\n active ? s.countActive : 'bg-muted text-foreground'\r\n }`}\r\n >\r\n {item.count}\r\n </span>\r\n )}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
|