@12-apps/ui 6.6.0 → 6.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QLBYAYUG.js → chunk-KR2BP7NK.js} +4 -4
- package/dist/chunk-KR2BP7NK.js.map +1 -0
- package/dist/{chunk-DFVRZ3UK.js → chunk-KXARS46L.js} +3 -3
- package/dist/chunk-KXARS46L.js.map +1 -0
- package/dist/{chunk-JP55QWVR.js → chunk-L7L6SX64.js} +5 -2
- package/dist/chunk-L7L6SX64.js.map +1 -0
- package/dist/{chunk-QZZVHY6U.js → chunk-PVLDKXKR.js} +7 -3
- package/dist/chunk-PVLDKXKR.js.map +1 -0
- package/dist/{chunk-3KWD5Y5O.js → chunk-RXE5CUUE.js} +8 -4
- package/dist/chunk-RXE5CUUE.js.map +1 -0
- package/dist/{chunk-H7UAGTVW.js → chunk-TSG5C7MP.js} +4 -3
- package/dist/chunk-TSG5C7MP.js.map +1 -0
- package/dist/{chunk-NUCLEOHF.js → chunk-UNM7KFDX.js} +44 -14
- package/dist/chunk-UNM7KFDX.js.map +1 -0
- package/dist/{chunk-CEKPLUQV.js → chunk-UY7UF7IN.js} +19 -13
- package/dist/chunk-UY7UF7IN.js.map +1 -0
- package/dist/data-display/Alert.js +1 -1
- package/dist/data-display/AsyncStateContainer.js +2 -1
- package/dist/data-display/AsyncStateContainer.js.map +1 -1
- package/dist/data-display/Banner.js +4 -2
- package/dist/data-display/Banner.js.map +1 -1
- package/dist/data-display/CardKit.js +3 -1
- package/dist/data-display/CardKit.js.map +1 -1
- package/dist/data-display/Carousel.js +5 -2
- package/dist/data-display/Carousel.js.map +1 -1
- package/dist/data-display/DataGrid.js +1 -1
- package/dist/data-display/DataViews.js +90 -66
- package/dist/data-display/DataViews.js.map +1 -1
- package/dist/data-display/HoverCard.js +4 -1
- package/dist/data-display/HoverCard.js.map +1 -1
- package/dist/data-display/Lightbox.js +147 -117
- package/dist/data-display/Lightbox.js.map +1 -1
- package/dist/data-display/MapPreview.js +14 -10
- package/dist/data-display/MapPreview.js.map +1 -1
- package/dist/data-display/Table.js +4 -2
- package/dist/data-display/Table.js.map +1 -1
- package/dist/data-display/TimingDiagram.js +3 -2
- package/dist/data-display/TimingDiagram.js.map +1 -1
- package/dist/feedback/Sonner.js +5 -4
- package/dist/feedback/Sonner.js.map +1 -1
- package/dist/feedback/StackedModal.js +1 -1
- package/dist/feedback/TutorialOverlay.js +19 -5
- package/dist/feedback/TutorialOverlay.js.map +1 -1
- package/dist/form/AddressAutocomplete.js +16 -8
- package/dist/form/AddressAutocomplete.js.map +1 -1
- package/dist/form/Autocomplete.js +9 -7
- package/dist/form/Autocomplete.js.map +1 -1
- package/dist/form/Calendar.js +1 -1
- package/dist/form/CodeEditor.js +14 -9
- package/dist/form/CodeEditor.js.map +1 -1
- package/dist/form/DateRangePicker.js +10 -3
- package/dist/form/DateRangePicker.js.map +1 -1
- package/dist/form/Menubar.js +4 -1
- package/dist/form/Menubar.js.map +1 -1
- package/dist/form/PasswordStrength.js +48 -28
- package/dist/form/PasswordStrength.js.map +1 -1
- package/dist/form/PhoneInput.js +11 -5
- package/dist/form/PhoneInput.js.map +1 -1
- package/dist/form/SearchPalette.js +4 -1
- package/dist/form/SearchPalette.js.map +1 -1
- package/dist/form/Textarea.js +21 -19
- package/dist/form/Textarea.js.map +1 -1
- package/dist/form/UploadButton.js +16 -11
- package/dist/form/UploadButton.js.map +1 -1
- package/dist/layout/ContentToolbar.js +1 -1
- package/dist/layout/Dashboard.js +2 -1
- package/dist/layout/Dashboard.js.map +1 -1
- package/dist/layout/ScrollArea.js +9 -3
- package/dist/layout/ScrollArea.js.map +1 -1
- package/dist/layout/TableFilter.js +1 -1
- package/dist/navigation/Breadcrumbs.js +9 -3
- package/dist/navigation/Breadcrumbs.js.map +1 -1
- package/dist/navigation/CommandPalette.js +20 -7
- package/dist/navigation/CommandPalette.js.map +1 -1
- package/dist/navigation/Pagination.js +1 -1
- package/dist/navigation/Tabs.js +7 -3
- package/dist/navigation/Tabs.js.map +1 -1
- package/dist/pt-BR.js +192 -5
- package/dist/pt-BR.js.map +1 -1
- package/dist/types/components/data-display/Alert/Alert.d.ts +9 -2
- package/dist/types/components/data-display/Alert/Alert.d.ts.map +1 -1
- package/dist/types/components/data-display/Alert/Alert.types.d.ts +25 -9
- package/dist/types/components/data-display/Alert/Alert.types.d.ts.map +1 -1
- package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.d.ts.map +1 -1
- package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts +6 -0
- package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Banner/Banner.d.ts.map +1 -1
- package/dist/types/components/data-display/Banner/Banner.types.d.ts +2 -0
- package/dist/types/components/data-display/Banner/Banner.types.d.ts.map +1 -1
- package/dist/types/components/data-display/CardKit/card-actions-context.d.ts +3 -1
- package/dist/types/components/data-display/CardKit/card-actions-context.d.ts.map +1 -1
- package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts +3 -0
- package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts.map +1 -1
- package/dist/types/components/data-display/Carousel/Carousel.parts.d.ts.map +1 -1
- package/dist/types/components/data-display/Carousel/Carousel.types.d.ts +7 -0
- package/dist/types/components/data-display/Carousel/Carousel.types.d.ts.map +1 -1
- package/dist/types/components/data-display/DataGrid/DataGrid.states.d.ts +1 -0
- package/dist/types/components/data-display/DataGrid/DataGrid.states.d.ts.map +1 -1
- package/dist/types/components/data-display/DataGrid/DataGrid.types.d.ts +5 -0
- package/dist/types/components/data-display/DataGrid/DataGrid.types.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-category-pill.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +29 -1
- package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts +12 -1
- package/dist/types/components/data-display/DataViews/data-views-day-input.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-filter-panel.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-grid-bodies.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-grid-parts.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-more-fields.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-pagination.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-range-pill.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-table-parts.d.ts.map +1 -1
- package/dist/types/components/data-display/DataViews/data-views-toolbar.d.ts.map +1 -1
- package/dist/types/components/data-display/HoverCard/HoverCard.content.d.ts +1 -1
- package/dist/types/components/data-display/HoverCard/HoverCard.content.d.ts.map +1 -1
- package/dist/types/components/data-display/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/types/components/data-display/HoverCard/HoverCard.types.d.ts +2 -0
- package/dist/types/components/data-display/HoverCard/HoverCard.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts +9 -0
- package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Lightbox/LightboxControls.d.ts.map +1 -1
- package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts +17 -0
- package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts.map +1 -0
- package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts +7 -0
- package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts.map +1 -1
- package/dist/types/components/data-display/MapPreview/MapLayers.d.ts.map +1 -1
- package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts +9 -0
- package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts.map +1 -1
- package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts +2 -0
- package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/Table.types.d.ts +5 -0
- package/dist/types/components/data-display/Table/Table.types.d.ts.map +1 -1
- package/dist/types/components/data-display/Table/TableStates.d.ts +3 -1
- package/dist/types/components/data-display/Table/TableStates.d.ts.map +1 -1
- package/dist/types/components/data-display/TimingDiagram/TimingDiagram.d.ts.map +1 -1
- package/dist/types/components/data-display/TimingDiagram/TimingDiagram.types.d.ts +3 -0
- package/dist/types/components/data-display/TimingDiagram/TimingDiagram.types.d.ts.map +1 -1
- package/dist/types/components/feedback/Sonner/Sonner.d.ts +8 -0
- package/dist/types/components/feedback/Sonner/Sonner.d.ts.map +1 -1
- package/dist/types/components/feedback/Sonner/Sonner.types.d.ts +2 -0
- package/dist/types/components/feedback/Sonner/Sonner.types.d.ts.map +1 -1
- package/dist/types/components/feedback/Sonner/SonnerToast.d.ts.map +1 -1
- package/dist/types/components/feedback/StackedModal/StackedModal.d.ts.map +1 -1
- package/dist/types/components/feedback/StackedModal/StackedModal.parts.d.ts +2 -0
- package/dist/types/components/feedback/StackedModal/StackedModal.parts.d.ts.map +1 -1
- package/dist/types/components/feedback/StackedModal/StackedModal.types.d.ts +2 -0
- package/dist/types/components/feedback/StackedModal/StackedModal.types.d.ts.map +1 -1
- package/dist/types/components/feedback/TutorialOverlay/TutorialOverlay.d.ts.map +1 -1
- package/dist/types/components/feedback/TutorialOverlay/TutorialOverlay.types.d.ts +3 -0
- package/dist/types/components/feedback/TutorialOverlay/TutorialOverlay.types.d.ts.map +1 -1
- package/dist/types/components/feedback/TutorialOverlay/TutorialStepBody.d.ts +2 -0
- package/dist/types/components/feedback/TutorialOverlay/TutorialStepBody.d.ts.map +1 -1
- package/dist/types/components/form/AddressAutocomplete/AddressAutocomplete.types.d.ts +7 -0
- package/dist/types/components/form/AddressAutocomplete/AddressAutocomplete.types.d.ts.map +1 -1
- package/dist/types/components/form/AddressAutocomplete/AddressAutocompleteField.d.ts.map +1 -1
- package/dist/types/components/form/AddressAutocomplete/AddressFallbackField.d.ts +1 -0
- package/dist/types/components/form/AddressAutocomplete/AddressFallbackField.d.ts.map +1 -1
- package/dist/types/components/form/AddressAutocomplete/AddressInputField.d.ts +1 -0
- package/dist/types/components/form/AddressAutocomplete/AddressInputField.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/Autocomplete.parts.d.ts +9 -3
- package/dist/types/components/form/Autocomplete/Autocomplete.parts.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/Autocomplete.types.d.ts +7 -0
- package/dist/types/components/form/Autocomplete/Autocomplete.types.d.ts.map +1 -1
- package/dist/types/components/form/Calendar/Calendar.types.d.ts +6 -0
- package/dist/types/components/form/Calendar/Calendar.types.d.ts.map +1 -1
- package/dist/types/components/form/CodeEditor/CodeEditor.d.ts.map +1 -1
- package/dist/types/components/form/CodeEditor/CodeEditor.types.d.ts +7 -0
- package/dist/types/components/form/CodeEditor/CodeEditor.types.d.ts.map +1 -1
- package/dist/types/components/form/CodeEditor/CodeEditorToolbar.d.ts +2 -0
- package/dist/types/components/form/CodeEditor/CodeEditorToolbar.d.ts.map +1 -1
- package/dist/types/components/form/DateRangePicker/DateRangePicker.d.ts.map +1 -1
- package/dist/types/components/form/DateRangePicker/DateRangePicker.range.d.ts.map +1 -1
- package/dist/types/components/form/DateRangePicker/DateRangePicker.types.d.ts +11 -0
- package/dist/types/components/form/DateRangePicker/DateRangePicker.types.d.ts.map +1 -1
- package/dist/types/components/form/DateRangePicker/DateRangePickerFields.d.ts.map +1 -1
- package/dist/types/components/form/Menubar/Menubar.d.ts.map +1 -1
- package/dist/types/components/form/Menubar/Menubar.toolbar.d.ts +1 -0
- package/dist/types/components/form/Menubar/Menubar.toolbar.d.ts.map +1 -1
- package/dist/types/components/form/Menubar/Menubar.types.d.ts +5 -0
- package/dist/types/components/form/Menubar/Menubar.types.d.ts.map +1 -1
- package/dist/types/components/form/PasswordStrength/PasswordStrength.d.ts.map +1 -1
- package/dist/types/components/form/PasswordStrength/PasswordStrength.helpers.d.ts +4 -2
- package/dist/types/components/form/PasswordStrength/PasswordStrength.helpers.d.ts.map +1 -1
- package/dist/types/components/form/PasswordStrength/PasswordStrength.types.d.ts +8 -0
- package/dist/types/components/form/PasswordStrength/PasswordStrength.types.d.ts.map +1 -1
- package/dist/types/components/form/PhoneInput/PhoneInput.d.ts.map +1 -1
- package/dist/types/components/form/PhoneInput/PhoneInput.types.d.ts +7 -0
- package/dist/types/components/form/PhoneInput/PhoneInput.types.d.ts.map +1 -1
- package/dist/types/components/form/SearchPalette/SearchPalette.d.ts.map +1 -1
- package/dist/types/components/form/SearchPalette/SearchPalette.parts.d.ts +2 -0
- package/dist/types/components/form/SearchPalette/SearchPalette.parts.d.ts.map +1 -1
- package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts +6 -0
- package/dist/types/components/form/SearchPalette/SearchPalette.types.d.ts.map +1 -1
- package/dist/types/components/form/Textarea/Textarea.d.ts.map +1 -1
- package/dist/types/components/form/Textarea/Textarea.types.d.ts +16 -1
- package/dist/types/components/form/Textarea/Textarea.types.d.ts.map +1 -1
- package/dist/types/components/form/Textarea/TextareaRichEditor.d.ts +7 -0
- package/dist/types/components/form/Textarea/TextareaRichEditor.d.ts.map +1 -1
- package/dist/types/components/form/UploadButton/UploadButton.d.ts.map +1 -1
- package/dist/types/components/form/UploadButton/UploadButton.parts.d.ts +6 -3
- package/dist/types/components/form/UploadButton/UploadButton.parts.d.ts.map +1 -1
- package/dist/types/components/form/UploadButton/UploadButton.types.d.ts +7 -0
- package/dist/types/components/form/UploadButton/UploadButton.types.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts +12 -0
- package/dist/types/components/layout/ContentToolbar/ContentToolbar.types.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/MultiSelectDropdown.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/SortByDropdown.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/SortByDropdown.d.ts.map +1 -1
- package/dist/types/components/layout/ContentToolbar/ViewSelector.d.ts +1 -1
- package/dist/types/components/layout/ContentToolbar/ViewSelector.d.ts.map +1 -1
- package/dist/types/components/layout/Dashboard/Dashboard.d.ts +1 -1
- package/dist/types/components/layout/Dashboard/Dashboard.types.d.ts +4 -0
- package/dist/types/components/layout/Dashboard/Dashboard.types.d.ts.map +1 -1
- package/dist/types/components/layout/Dashboard/DashboardParts.d.ts +1 -1
- package/dist/types/components/layout/Dashboard/DashboardParts.d.ts.map +1 -1
- package/dist/types/components/layout/ScrollArea/ScrollArea.d.ts.map +1 -1
- package/dist/types/components/layout/ScrollArea/ScrollArea.types.d.ts +21 -3
- package/dist/types/components/layout/ScrollArea/ScrollArea.types.d.ts.map +1 -1
- package/dist/types/components/layout/ScrollArea/ScrollViewport.d.ts +2 -0
- package/dist/types/components/layout/ScrollArea/ScrollViewport.d.ts.map +1 -1
- package/dist/types/components/layout/TableFilter/TableFilter.d.ts +1 -1
- package/dist/types/components/layout/TableFilter/TableFilter.d.ts.map +1 -1
- package/dist/types/components/layout/TableFilter/TableFilter.types.d.ts +7 -0
- package/dist/types/components/layout/TableFilter/TableFilter.types.d.ts.map +1 -1
- package/dist/types/components/layout/TableFilter/TableFilterContext.d.ts +9 -0
- package/dist/types/components/layout/TableFilter/TableFilterContext.d.ts.map +1 -1
- package/dist/types/components/navigation/Breadcrumbs/BreadcrumbEntry.d.ts +2 -0
- package/dist/types/components/navigation/Breadcrumbs/BreadcrumbEntry.d.ts.map +1 -1
- package/dist/types/components/navigation/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/types/components/navigation/Breadcrumbs/Breadcrumbs.types.d.ts +3 -0
- package/dist/types/components/navigation/Breadcrumbs/Breadcrumbs.types.d.ts.map +1 -1
- package/dist/types/components/navigation/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/types/components/navigation/CommandPalette/CommandPalette.types.d.ts +3 -0
- package/dist/types/components/navigation/CommandPalette/CommandPalette.types.d.ts.map +1 -1
- package/dist/types/components/navigation/CommandPalette/CommandPaletteResults.d.ts +2 -0
- package/dist/types/components/navigation/CommandPalette/CommandPaletteResults.d.ts.map +1 -1
- package/dist/types/components/navigation/Pagination/Pagination.d.ts.map +1 -1
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +2 -0
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts.map +1 -1
- package/dist/types/components/navigation/Pagination/PaginationParts.d.ts +2 -0
- package/dist/types/components/navigation/Pagination/PaginationParts.d.ts.map +1 -1
- package/dist/types/components/navigation/Tabs/Tabs.d.ts.map +1 -1
- package/dist/types/components/navigation/Tabs/Tabs.types.d.ts +2 -0
- package/dist/types/components/navigation/Tabs/Tabs.types.d.ts.map +1 -1
- package/dist/types/components/utility/InfiniteScroll/InfiniteScroll.d.ts.map +1 -1
- package/dist/types/components/utility/InfiniteScroll/InfiniteScroll.parts.d.ts +6 -2
- package/dist/types/components/utility/InfiniteScroll/InfiniteScroll.parts.d.ts.map +1 -1
- package/dist/types/components/utility/InfiniteScroll/InfiniteScroll.types.d.ts +6 -0
- package/dist/types/components/utility/InfiniteScroll/InfiniteScroll.types.d.ts.map +1 -1
- package/dist/types/components/utility/InstallPrompt/InstallPrompt.d.ts +4 -2
- package/dist/types/components/utility/InstallPrompt/InstallPrompt.d.ts.map +1 -1
- package/dist/types/components/utility/InstallPrompt/InstallPrompt.types.d.ts +8 -7
- package/dist/types/components/utility/InstallPrompt/InstallPrompt.types.d.ts.map +1 -1
- package/dist/types/copy/data-display.d.ts +56 -0
- package/dist/types/copy/data-display.d.ts.map +1 -0
- package/dist/types/copy/feedback.d.ts +38 -0
- package/dist/types/copy/feedback.d.ts.map +1 -0
- package/dist/types/copy/form.d.ts +146 -0
- package/dist/types/copy/form.d.ts.map +1 -0
- package/dist/types/copy/layout.d.ts +18 -0
- package/dist/types/copy/layout.d.ts.map +1 -0
- package/dist/types/copy/navigation.d.ts +24 -0
- package/dist/types/copy/navigation.d.ts.map +1 -0
- package/dist/types/copy/shared.d.ts +119 -0
- package/dist/types/copy/shared.d.ts.map +1 -0
- package/dist/types/copy/utility.d.ts +35 -0
- package/dist/types/copy/utility.d.ts.map +1 -0
- package/dist/types/copy.d.ts +13 -113
- package/dist/types/copy.d.ts.map +1 -1
- package/dist/types/pt-BR.d.ts +15 -18
- package/dist/types/pt-BR.d.ts.map +1 -1
- package/dist/types/pt-BR.data-display.d.ts +13 -0
- package/dist/types/pt-BR.data-display.d.ts.map +1 -0
- package/dist/types/pt-BR.feedback.d.ts +8 -0
- package/dist/types/pt-BR.feedback.d.ts.map +1 -0
- package/dist/types/pt-BR.form.d.ts +13 -0
- package/dist/types/pt-BR.form.d.ts.map +1 -0
- package/dist/types/pt-BR.layout.d.ts +7 -0
- package/dist/types/pt-BR.layout.d.ts.map +1 -0
- package/dist/types/pt-BR.navigation.d.ts +8 -0
- package/dist/types/pt-BR.navigation.d.ts.map +1 -0
- package/dist/types/pt-BR.shared.d.ts +21 -0
- package/dist/types/pt-BR.shared.d.ts.map +1 -0
- package/dist/types/pt-BR.utility.d.ts +8 -0
- package/dist/types/pt-BR.utility.d.ts.map +1 -0
- package/dist/types/user-avatar/user-avatar.d.ts +7 -1
- package/dist/types/user-avatar/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +2 -2
- package/dist/user-avatar.js.map +1 -1
- package/dist/utility/InfiniteScroll.js +7 -5
- package/dist/utility/InfiniteScroll.js.map +1 -1
- package/dist/utility/InstallPrompt.js +14 -13
- package/dist/utility/InstallPrompt.js.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/Alert/Alert.tsx +22 -5
- package/src/components/data-display/Alert/Alert.types.ts +20 -10
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +2 -1
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.types.ts +6 -0
- package/src/components/data-display/Banner/Banner.tsx +5 -2
- package/src/components/data-display/Banner/Banner.types.ts +2 -0
- package/src/components/data-display/CardKit/card-actions-context.tsx +4 -0
- package/src/components/data-display/Carousel/Carousel.chrome.tsx +5 -0
- package/src/components/data-display/Carousel/Carousel.parts.tsx +4 -3
- package/src/components/data-display/Carousel/Carousel.types.ts +7 -0
- package/src/components/data-display/DataGrid/DataGrid.states.tsx +3 -3
- package/src/components/data-display/DataGrid/DataGrid.tsx +1 -1
- package/src/components/data-display/DataGrid/DataGrid.types.ts +5 -0
- package/src/components/data-display/DataViews/data-views-category-pill.tsx +2 -0
- package/src/components/data-display/DataViews/data-views-copy.ts +29 -1
- package/src/components/data-display/DataViews/data-views-day-input.tsx +14 -3
- package/src/components/data-display/DataViews/data-views-filter-panel.tsx +2 -1
- package/src/components/data-display/DataViews/data-views-grid-bodies.tsx +3 -0
- package/src/components/data-display/DataViews/data-views-grid-parts.tsx +9 -10
- package/src/components/data-display/DataViews/data-views-more-fields.tsx +3 -1
- package/src/components/data-display/DataViews/data-views-pagination.tsx +3 -0
- package/src/components/data-display/DataViews/data-views-range-pill.tsx +3 -1
- package/src/components/data-display/DataViews/data-views-table-parts.tsx +1 -0
- package/src/components/data-display/DataViews/data-views-toolbar.tsx +2 -0
- package/src/components/data-display/HoverCard/HoverCard.content.tsx +10 -2
- package/src/components/data-display/HoverCard/HoverCard.tsx +2 -3
- package/src/components/data-display/HoverCard/HoverCard.types.ts +2 -0
- package/src/components/data-display/Lightbox/Lightbox.tsx +3 -0
- package/src/components/data-display/Lightbox/Lightbox.types.ts +9 -0
- package/src/components/data-display/Lightbox/LightboxControls.tsx +28 -15
- package/src/components/data-display/Lightbox/lightbox-copy-context.tsx +42 -0
- package/src/components/data-display/MapPreview/MapControlBar.tsx +17 -9
- package/src/components/data-display/MapPreview/MapLayers.tsx +2 -0
- package/src/components/data-display/MapPreview/MapPreview.types.ts +10 -0
- package/src/components/data-display/MapPreview/MapSearchBar.tsx +4 -1
- package/src/components/data-display/Table/Table.tsx +7 -2
- package/src/components/data-display/Table/Table.types.ts +5 -0
- package/src/components/data-display/Table/TableStates.tsx +2 -2
- package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +3 -2
- package/src/components/data-display/TimingDiagram/TimingDiagram.types.ts +3 -0
- package/src/components/feedback/Sonner/Sonner.tsx +15 -3
- package/src/components/feedback/Sonner/Sonner.types.ts +2 -0
- package/src/components/feedback/Sonner/SonnerToast.tsx +2 -1
- package/src/components/feedback/StackedModal/StackedModal.parts.tsx +10 -3
- package/src/components/feedback/StackedModal/StackedModal.tsx +2 -1
- package/src/components/feedback/StackedModal/StackedModal.types.ts +2 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +13 -1
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.types.ts +3 -0
- package/src/components/feedback/TutorialOverlay/TutorialStepBody.tsx +10 -4
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +7 -0
- package/src/components/form/AddressAutocomplete/AddressAutocompleteField.tsx +2 -0
- package/src/components/form/AddressAutocomplete/AddressFallbackField.tsx +4 -2
- package/src/components/form/AddressAutocomplete/AddressInputField.tsx +13 -6
- package/src/components/form/Autocomplete/Autocomplete.parts.tsx +9 -6
- package/src/components/form/Autocomplete/Autocomplete.tsx +2 -1
- package/src/components/form/Autocomplete/Autocomplete.types.ts +7 -0
- package/src/components/form/Calendar/Calendar.tsx +2 -2
- package/src/components/form/Calendar/Calendar.types.ts +7 -0
- package/src/components/form/CodeEditor/CodeEditor.tsx +6 -4
- package/src/components/form/CodeEditor/CodeEditor.types.ts +7 -0
- package/src/components/form/CodeEditor/CodeEditorToolbar.tsx +12 -5
- package/src/components/form/DateRangePicker/DateRangePicker.range.ts +2 -0
- package/src/components/form/DateRangePicker/DateRangePicker.tsx +4 -0
- package/src/components/form/DateRangePicker/DateRangePicker.types.ts +11 -0
- package/src/components/form/DateRangePicker/DateRangePickerFields.tsx +2 -0
- package/src/components/form/Menubar/Menubar.toolbar.tsx +3 -1
- package/src/components/form/Menubar/Menubar.tsx +2 -1
- package/src/components/form/Menubar/Menubar.types.ts +5 -0
- package/src/components/form/PasswordStrength/PasswordStrength.helpers.tsx +33 -17
- package/src/components/form/PasswordStrength/PasswordStrength.tsx +29 -13
- package/src/components/form/PasswordStrength/PasswordStrength.types.ts +9 -0
- package/src/components/form/PhoneInput/PhoneInput.tsx +15 -5
- package/src/components/form/PhoneInput/PhoneInput.types.ts +7 -0
- package/src/components/form/SearchPalette/SearchPalette.parts.tsx +4 -1
- package/src/components/form/SearchPalette/SearchPalette.tsx +4 -1
- package/src/components/form/SearchPalette/SearchPalette.types.ts +6 -0
- package/src/components/form/Textarea/Textarea.tsx +6 -1
- package/src/components/form/Textarea/Textarea.types.ts +14 -1
- package/src/components/form/Textarea/TextareaRichEditor.tsx +28 -18
- package/src/components/form/UploadButton/UploadButton.parts.tsx +16 -7
- package/src/components/form/UploadButton/UploadButton.tsx +4 -1
- package/src/components/form/UploadButton/UploadButton.types.ts +8 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.tsx +10 -2
- package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +12 -0
- package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +15 -4
- package/src/components/layout/ContentToolbar/SortByDropdown.tsx +12 -3
- package/src/components/layout/ContentToolbar/ViewSelector.tsx +2 -1
- package/src/components/layout/Dashboard/Dashboard.types.ts +4 -0
- package/src/components/layout/Dashboard/DashboardParts.tsx +2 -1
- package/src/components/layout/ScrollArea/ScrollArea.tsx +16 -3
- package/src/components/layout/ScrollArea/ScrollArea.types.ts +19 -4
- package/src/components/layout/ScrollArea/ScrollViewport.tsx +4 -1
- package/src/components/layout/TableFilter/TableFilter.tsx +19 -11
- package/src/components/layout/TableFilter/TableFilter.types.ts +8 -0
- package/src/components/layout/TableFilter/TableFilterContext.tsx +10 -0
- package/src/components/navigation/Breadcrumbs/BreadcrumbEntry.tsx +11 -4
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +2 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.types.ts +3 -0
- package/src/components/navigation/CommandPalette/CommandPalette.tsx +15 -5
- package/src/components/navigation/CommandPalette/CommandPalette.types.ts +3 -0
- package/src/components/navigation/CommandPalette/CommandPaletteResults.tsx +5 -2
- package/src/components/navigation/Pagination/Pagination.tsx +2 -0
- package/src/components/navigation/Pagination/Pagination.types.ts +2 -0
- package/src/components/navigation/Pagination/PaginationParts.tsx +4 -1
- package/src/components/navigation/Tabs/Tabs.tsx +8 -3
- package/src/components/navigation/Tabs/Tabs.types.ts +2 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.parts.tsx +4 -4
- package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +4 -2
- package/src/components/utility/InfiniteScroll/InfiniteScroll.types.ts +6 -0
- package/src/components/utility/InstallPrompt/InstallPrompt.tsx +18 -13
- package/src/components/utility/InstallPrompt/InstallPrompt.types.ts +9 -7
- package/src/copy/data-display.ts +60 -0
- package/src/copy/feedback.ts +39 -0
- package/src/copy/form.ts +154 -0
- package/src/copy/layout.ts +18 -0
- package/src/copy/navigation.ts +25 -0
- package/src/copy/shared.ts +122 -0
- package/src/copy/utility.ts +36 -0
- package/src/copy.ts +13 -117
- package/src/pt-BR.data-display.ts +236 -0
- package/src/pt-BR.feedback.ts +25 -0
- package/src/pt-BR.form.ts +96 -0
- package/src/pt-BR.layout.ts +15 -0
- package/src/pt-BR.navigation.ts +21 -0
- package/src/pt-BR.shared.ts +94 -0
- package/src/pt-BR.ts +15 -267
- package/src/pt-BR.utility.ts +21 -0
- package/src/user-avatar/user-avatar.tsx +8 -2
- package/dist/chunk-3KWD5Y5O.js.map +0 -1
- package/dist/chunk-CEKPLUQV.js.map +0 -1
- package/dist/chunk-DFVRZ3UK.js.map +0 -1
- package/dist/chunk-H7UAGTVW.js.map +0 -1
- package/dist/chunk-JP55QWVR.js.map +0 -1
- package/dist/chunk-NUCLEOHF.js.map +0 -1
- package/dist/chunk-QLBYAYUG.js.map +0 -1
- package/dist/chunk-QZZVHY6U.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/form/PasswordStrength/PasswordStrength.tsx","../../src/components/form/PasswordStrength/PasswordStrength.helpers.tsx"],"sourcesContent":["import CheckIcon from '@mui/icons-material/Check';\nimport CloseIcon from '@mui/icons-material/Close';\n\nimport {\n alpha,\n Box,\n Chip,\n Fade,\n keyframes,\n LinearProgress,\n Stack,\n styled,\n Typography,\n useTheme } from '@mui/material';\nimport type { FC} from 'react';\nimport React, { useMemo } from 'react';\n\nimport type { RequirementChecks } from './PasswordStrength.helpers';\nimport {\n buildSuggestions,\n calculatePasswordStrength,\n defaultRequirements,\n evaluateRequirements,\n getStrengthIcon,\n getStrengthLabel,\n SuggestionsList } from './PasswordStrength.helpers';\nimport type { PasswordRequirements, PasswordStrengthProps } from './PasswordStrength.types';\n\ninterface RequirementIconProps {\n met: boolean;\n}\n const RequirementIcon: FC<RequirementIconProps> = ({ met }) => {\n return met ? (\n <CheckIcon data-testid=\"check-icon\" />\n ) : (\n <CloseIcon data-testid=\"close-icon\" />\n );\n};\n\n// Default requirements\n// Animation keyframes\nconst pulseAnimation = keyframes`\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.7;\n }\n`;\n\nconst slideInAnimation = keyframes`\n from {\n transform: translateX(-10px);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n`;\n\n// Styled components\nconst StrengthContainer = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'animated' })<{ animated?: boolean }>(({ theme, animated }) => ({\n width: '100%',\n padding: theme.spacing(2),\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.8)} 0%, ${alpha(theme.palette.background.paper, 0.6)} 100%)`,\n backdropFilter: 'blur(10px)',\n WebkitBackdropFilter: 'blur(10px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n borderRadius: theme.shape.borderRadius * 2,\n transition: animated ? 'all 0.3s ease' : 'none' }));\n\nconst StrengthBar = styled(LinearProgress, {\n shouldForwardProp: (prop) => prop !== 'strength' && prop !== 'animated' })<{ strength: number; animated?: boolean }>(({ theme, strength, animated }) => {\n const getColor = () => {\n if (strength <= 20) return theme.palette.error.main;\n if (strength <= 40) return theme.palette.warning.main;\n if (strength <= 60) return theme.palette.info.main;\n if (strength <= 80) return theme.palette.success.light;\n return theme.palette.success.main;\n };\n\n const getGradient = () => {\n const color = getColor();\n return `linear-gradient(90deg, ${color} 0%, ${alpha(color, 0.8)} 100%)`;\n };\n\n return {\n height: 8,\n borderRadius: 4,\n backgroundColor: alpha(theme.palette.action.disabled, 0.1),\n '& .MuiLinearProgress-bar': {\n borderRadius: 4,\n background: getGradient(),\n transition: animated ? 'all 0.3s ease' : 'none',\n boxShadow: `0 2px 8px ${alpha(getColor(), 0.3)}` } };\n});\n\nconst RequirementItem = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'met' && prop !== 'animated' })<{ met: boolean; animated?: boolean }>(({ theme, met, animated }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n padding: theme.spacing(0.75, 1),\n borderRadius: theme.shape.borderRadius,\n background: met\n ? alpha(theme.palette.success.main, 0.08)\n : alpha(theme.palette.action.disabled, 0.04),\n transition: animated ? 'all 0.3s ease' : 'none',\n animation: animated ? `${slideInAnimation} 0.3s ease` : 'none',\n '& svg': {\n fontSize: '1rem',\n color: met ? theme.palette.success.main : theme.palette.text.disabled } }));\n\nconst StrengthLabel = styled(Chip, {\n shouldForwardProp: (prop) => prop !== 'strength' && prop !== 'animated' })<{ strength: number; animated?: boolean }>(({ theme, strength, animated }) => {\n const getColor = () => {\n if (strength <= 20) return theme.palette.error;\n if (strength <= 40) return theme.palette.warning;\n if (strength <= 60) return theme.palette.info;\n if (strength <= 80) return theme.palette.success;\n return theme.palette.success;\n };\n\n const palette = getColor();\n\n return {\n background: alpha(palette.main, 0.1),\n color: palette.main,\n border: `1px solid ${alpha(palette.main, 0.3)}`,\n fontWeight: 600,\n animation: animated ? `${pulseAnimation} 2s ease infinite` : 'none' };\n});\n\nconst StepsContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n gap: theme.spacing(1),\n marginTop: theme.spacing(2) }));\n\nconst Step = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'active' && prop !== 'completed' && prop !== 'animated' })<{ active: boolean; completed: boolean; animated?: boolean }>(\n ({ theme, active, completed, animated }) => ({\n flex: 1,\n height: 6,\n borderRadius: 3,\n background: completed\n ? `linear-gradient(90deg, ${theme.palette.success.main} 0%, ${theme.palette.success.light} 100%)`\n : active\n ? `linear-gradient(90deg, ${theme.palette.warning.main} 0%, ${theme.palette.warning.light} 100%)`\n : alpha(theme.palette.action.disabled, 0.2),\n transition: animated ? 'all 0.3s ease' : 'none',\n boxShadow: completed ? `0 2px 8px ${alpha(theme.palette.success.main, 0.3)}` : 'none' }),\n);\n\n// The three indicator variants, out of the component body so the render keeps\n// only its own layout branches.\nconst StrengthIndicator: React.FC<{\n variant: PasswordStrengthProps['variant'];\n strength: number;\n animated: boolean;\n}> = ({ variant, strength, animated }) => {\n const theme = useTheme();\n\n switch (variant) {\n case 'circular':\n return (\n <Box sx={{ position: 'relative', display: 'inline-flex' }}>\n <Box\n sx={{\n width: 80,\n height: 80,\n borderRadius: '50%',\n background: `conic-gradient(\n ${theme.palette.success.main} 0deg ${strength * 3.6}deg,\n ${alpha(theme.palette.action.disabled, 0.1)} ${strength * 3.6}deg 360deg\n )`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxShadow: `0 4px 12px ${alpha(theme.palette.success.main, strength / 200)}` }}\n >\n <Box\n sx={{\n width: 64,\n height: 64,\n borderRadius: '50%',\n background: theme.palette.background.paper,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexDirection: 'column' }}\n >\n <Typography variant=\"h6\" fontWeight=\"bold\">\n {strength}%\n </Typography>\n </Box>\n </Box>\n </Box>\n );\n\n case 'steps': {\n const steps = 5;\n const activeStep = Math.ceil((strength / 100) * steps);\n return (\n <StepsContainer>\n {Array.from({ length: steps }, (_, i) => (\n <Step key={i} active={i === activeStep - 1} completed={i < activeStep - 1} animated={animated} />\n ))}\n </StepsContainer>\n );\n }\n\n default:\n return <StrengthBar variant=\"determinate\" value={strength} strength={strength} animated={animated} />;\n }\n};\n\nconst StrengthHeading: React.FC<{\n strength: number;\n hasValue: boolean;\n animated: boolean;\n}> = ({ strength, hasValue, animated }) => (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Password Strength\n </Typography>\n {hasValue && (\n <Fade in={true}>\n <StrengthLabel\n label={getStrengthLabel(strength)}\n icon={getStrengthIcon(strength)}\n size=\"small\"\n strength={strength}\n animated={animated}\n />\n </Fade>\n )}\n </Box>\n);\n\n// One row per enabled requirement. Previously five near-identical JSX blocks,\n// each with its own `requirements.x &&` guard.\nconst RequirementsList: React.FC<{\n requirements: PasswordRequirements;\n checks: RequirementChecks;\n animated: boolean;\n}> = ({ requirements, checks, animated }) => {\n const rows: ReadonlyArray<[unknown, keyof RequirementChecks, React.ReactNode]> = [\n [requirements.minLength, 'length', `At least ${requirements.minLength} characters`],\n [requirements.uppercase, 'uppercase', 'One uppercase letter'],\n [requirements.lowercase, 'lowercase', 'One lowercase letter'],\n [requirements.numbers, 'numbers', 'One number'],\n [requirements.special, 'special', 'One special character'],\n ];\n\n return (\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\" fontWeight=\"medium\">\n Requirements:\n </Typography>\n {rows\n .filter(([enabled]) => Boolean(enabled))\n .map(([, key, label]) => (\n <RequirementItem key={key} met={checks[key]} animated={animated}>\n <RequirementIcon met={checks[key]} />\n <Typography variant=\"caption\">{label}</Typography>\n </RequirementItem>\n ))}\n </Stack>\n );\n};\n\n// The visible sections. Kept apart from PasswordStrength itself so the exported\n// component is just prop defaults plus the three memos, and every display guard\n// lives in one place.\nconst PasswordStrengthBody: React.FC<{\n value: string;\n strength: number;\n requirements: PasswordRequirements;\n requirementChecks: RequirementChecks;\n suggestions: string[];\n showStrengthLabel: boolean;\n showRequirements: boolean;\n showSuggestions: boolean;\n variant: PasswordStrengthProps['variant'];\n animated: boolean;\n}> = ({\n value,\n strength,\n requirements,\n requirementChecks,\n suggestions,\n showStrengthLabel,\n showRequirements,\n showSuggestions,\n variant,\n animated }) => (\n <Stack spacing={2}>\n {showStrengthLabel && (\n <StrengthHeading strength={strength} hasValue={value.length > 0} animated={animated} />\n )}\n\n <StrengthIndicator variant={variant} strength={strength} animated={animated} />\n\n {showRequirements && (\n <Fade in={value.length > 0}>\n <RequirementsList\n requirements={requirements}\n checks={requirementChecks}\n animated={animated}\n />\n </Fade>\n )}\n\n {showSuggestions && suggestions.length > 0 && value.length > 0 && (\n <Fade in={true}>\n <SuggestionsList suggestions={suggestions} />\n </Fade>\n )}\n </Stack>\n);\n\n// Main component\nexport const PasswordStrength: FC<PasswordStrengthProps> = ({\n value = '',\n showRequirements = true,\n requirements = defaultRequirements,\n showStrengthLabel = true,\n showSuggestions = false,\n variant = 'linear',\n animated = true,\n 'data-testid': dataTestId }) => {\n\n const strength = useMemo(\n () => calculatePasswordStrength(value, requirements),\n [value, requirements],\n );\n\n const requirementChecks = useMemo(\n () => evaluateRequirements(value, requirements),\n [value, requirements],\n );\n\n const suggestions = useMemo(() => buildSuggestions(requirementChecks), [requirementChecks]);\n\n return (\n <StrengthContainer animated={animated} data-testid={dataTestId}>\n <PasswordStrengthBody\n value={value}\n strength={strength}\n requirements={requirements}\n requirementChecks={requirementChecks}\n suggestions={suggestions}\n showStrengthLabel={showStrengthLabel}\n showRequirements={showRequirements}\n showSuggestions={showSuggestions}\n variant={variant}\n animated={animated}\n />\n </StrengthContainer>\n );\n};\n\n// Export default\nexport default PasswordStrength;\n","import SecurityIcon from '@mui/icons-material/Security';\nimport SuccessIcon from '@mui/icons-material/CheckCircle';\nimport WarningIcon from '@mui/icons-material/Warning';\nimport { Box, Typography } from '@mui/material';\nimport React from 'react';\n\nimport type { PasswordRequirements } from './PasswordStrength.types';\n\nexport const defaultRequirements: PasswordRequirements = {\n minLength: 8,\n uppercase: true,\n lowercase: true,\n numbers: true,\n special: true,\n};\n\n\nconst SPECIAL_CHARACTERS = /[!@#$%^&*(),.?\":{}|<>]/;\nconst DEFAULT_MIN_LENGTH = 8;\n\n// Points awarded per satisfied requirement.\nconst REQUIREMENT_POINTS = {\n length: 20,\n uppercase: 10,\n lowercase: 10,\n numbers: 15,\n special: 15,\n} as const;\n\nexport type RequirementChecks = Record<keyof typeof REQUIREMENT_POINTS, boolean>;\n\n// A requirement that is switched off counts as satisfied. This was previously\n// written out twice — once to score the password and once to render the\n// checklist — so the two could drift.\nexport const evaluateRequirements = (\n password: string,\n requirements: PasswordRequirements,\n): RequirementChecks => {\n if (!password) {\n return { length: false, uppercase: false, lowercase: false, numbers: false, special: false };\n }\n\n return {\n length: password.length >= (requirements.minLength || DEFAULT_MIN_LENGTH),\n uppercase: !requirements.uppercase || /[A-Z]/.test(password),\n lowercase: !requirements.lowercase || /[a-z]/.test(password),\n numbers: !requirements.numbers || /\\d/.test(password),\n special: !requirements.special || SPECIAL_CHARACTERS.test(password),\n };\n};\n\nconst lengthBonus = (length: number): number => {\n const base = Math.min(length * 3, 30);\n const over12 = length > 12 ? 10 : 0;\n const over16 = length > 16 ? 10 : 0;\n\n return base + over12 + over16;\n};\n\nexport const calculatePasswordStrength = (\n password: string,\n requirements: PasswordRequirements,\n): number => {\n if (!password) return 0;\n\n const checks = evaluateRequirements(password, requirements);\n const earned = Object.entries(REQUIREMENT_POINTS).reduce(\n (total, [key, points]) => total + (checks[key as keyof RequirementChecks] ? points : 0),\n 0,\n );\n\n return Math.min(earned + lengthBonus(password.length), 100);\n};\n\nconst STRENGTH_LABELS: ReadonlyArray<[number, string]> = [\n [20, 'Very Weak'],\n [40, 'Weak'],\n [60, 'Fair'],\n [80, 'Good'],\n];\n\nexport const getStrengthLabel = (strength: number): string =>\n STRENGTH_LABELS.find(([ceiling]) => strength <= ceiling)?.[1] ?? 'Strong';\n\nexport const getStrengthIcon = (strength: number) => {\n if (strength <= 40) return <WarningIcon />;\n if (strength <= 80) return <SecurityIcon />;\n return <SuccessIcon />;\n};\n\nconst SUGGESTIONS: ReadonlyArray<[keyof RequirementChecks, string]> = [\n ['length', 'Use at least 8 characters'],\n ['uppercase', 'Add uppercase letters'],\n ['lowercase', 'Add lowercase letters'],\n ['numbers', 'Include numbers'],\n ['special', 'Add special characters'],\n];\n\nexport const buildSuggestions = (checks: RequirementChecks): string[] =>\n SUGGESTIONS.filter(([key]) => !checks[key]).map(([, tip]) => tip);\n\n// Rendered by PasswordStrength; depends on nothing styled, so it lives beside\n// the pure helpers rather than in the component file.\nexport const SuggestionsList: React.FC<{ suggestions: string[] }> = ({ suggestions }) => (\n <Box>\n <Typography variant=\"caption\" color=\"text.secondary\" fontWeight=\"medium\">\n Suggestions:\n </Typography>\n <Box sx={{ mt: 1 }}>\n {suggestions.map((tip) => (\n <Typography key={tip} variant=\"caption\" display=\"block\" color=\"warning.main\" sx={{ ml: 2 }}>\n • {tip}\n </Typography>\n ))}\n </Box>\n </Box>\n);\n"],"mappings":"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","names":["Box","Typography","jsx","jsxs","Box","Typography"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/form/PasswordStrength/PasswordStrength.tsx","../../src/components/form/PasswordStrength/PasswordStrength.helpers.tsx"],"sourcesContent":["import CheckIcon from '@mui/icons-material/Check';\nimport CloseIcon from '@mui/icons-material/Close';\n\nimport {\n alpha,\n Box,\n Chip,\n Fade,\n keyframes,\n LinearProgress,\n Stack,\n styled,\n Typography,\n useTheme } from '@mui/material';\nimport type { FC} from 'react';\nimport React, { useMemo } from 'react';\n\nimport type { RequirementChecks } from './PasswordStrength.helpers';\nimport {\n buildSuggestions,\n calculatePasswordStrength,\n defaultRequirements,\n evaluateRequirements,\n getStrengthIcon,\n getStrengthLabel,\n SuggestionsList } from './PasswordStrength.helpers';\nimport type { PasswordRequirements, PasswordStrengthProps } from './PasswordStrength.types';\nimport type { PasswordStrengthCopy } from '../../../copy';\n\ninterface RequirementIconProps {\n met: boolean;\n}\n const RequirementIcon: FC<RequirementIconProps> = ({ met }) => {\n return met ? (\n <CheckIcon data-testid=\"check-icon\" />\n ) : (\n <CloseIcon data-testid=\"close-icon\" />\n );\n};\n\n// Default requirements\n// Animation keyframes\nconst pulseAnimation = keyframes`\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.7;\n }\n`;\n\nconst slideInAnimation = keyframes`\n from {\n transform: translateX(-10px);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n`;\n\n// Styled components\nconst StrengthContainer = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'animated' })<{ animated?: boolean }>(({ theme, animated }) => ({\n width: '100%',\n padding: theme.spacing(2),\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.8)} 0%, ${alpha(theme.palette.background.paper, 0.6)} 100%)`,\n backdropFilter: 'blur(10px)',\n WebkitBackdropFilter: 'blur(10px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n borderRadius: theme.shape.borderRadius * 2,\n transition: animated ? 'all 0.3s ease' : 'none' }));\n\nconst StrengthBar = styled(LinearProgress, {\n shouldForwardProp: (prop) => prop !== 'strength' && prop !== 'animated' })<{ strength: number; animated?: boolean }>(({ theme, strength, animated }) => {\n const getColor = () => {\n if (strength <= 20) return theme.palette.error.main;\n if (strength <= 40) return theme.palette.warning.main;\n if (strength <= 60) return theme.palette.info.main;\n if (strength <= 80) return theme.palette.success.light;\n return theme.palette.success.main;\n };\n\n const getGradient = () => {\n const color = getColor();\n return `linear-gradient(90deg, ${color} 0%, ${alpha(color, 0.8)} 100%)`;\n };\n\n return {\n height: 8,\n borderRadius: 4,\n backgroundColor: alpha(theme.palette.action.disabled, 0.1),\n '& .MuiLinearProgress-bar': {\n borderRadius: 4,\n background: getGradient(),\n transition: animated ? 'all 0.3s ease' : 'none',\n boxShadow: `0 2px 8px ${alpha(getColor(), 0.3)}` } };\n});\n\nconst RequirementItem = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'met' && prop !== 'animated' })<{ met: boolean; animated?: boolean }>(({ theme, met, animated }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n padding: theme.spacing(0.75, 1),\n borderRadius: theme.shape.borderRadius,\n background: met\n ? alpha(theme.palette.success.main, 0.08)\n : alpha(theme.palette.action.disabled, 0.04),\n transition: animated ? 'all 0.3s ease' : 'none',\n animation: animated ? `${slideInAnimation} 0.3s ease` : 'none',\n '& svg': {\n fontSize: '1rem',\n color: met ? theme.palette.success.main : theme.palette.text.disabled } }));\n\nconst StrengthLabel = styled(Chip, {\n shouldForwardProp: (prop) => prop !== 'strength' && prop !== 'animated' })<{ strength: number; animated?: boolean }>(({ theme, strength, animated }) => {\n const getColor = () => {\n if (strength <= 20) return theme.palette.error;\n if (strength <= 40) return theme.palette.warning;\n if (strength <= 60) return theme.palette.info;\n if (strength <= 80) return theme.palette.success;\n return theme.palette.success;\n };\n\n const palette = getColor();\n\n return {\n background: alpha(palette.main, 0.1),\n color: palette.main,\n border: `1px solid ${alpha(palette.main, 0.3)}`,\n fontWeight: 600,\n animation: animated ? `${pulseAnimation} 2s ease infinite` : 'none' };\n});\n\nconst StepsContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n gap: theme.spacing(1),\n marginTop: theme.spacing(2) }));\n\nconst Step = styled(Box, {\n shouldForwardProp: (prop) => prop !== 'active' && prop !== 'completed' && prop !== 'animated' })<{ active: boolean; completed: boolean; animated?: boolean }>(\n ({ theme, active, completed, animated }) => ({\n flex: 1,\n height: 6,\n borderRadius: 3,\n background: completed\n ? `linear-gradient(90deg, ${theme.palette.success.main} 0%, ${theme.palette.success.light} 100%)`\n : active\n ? `linear-gradient(90deg, ${theme.palette.warning.main} 0%, ${theme.palette.warning.light} 100%)`\n : alpha(theme.palette.action.disabled, 0.2),\n transition: animated ? 'all 0.3s ease' : 'none',\n boxShadow: completed ? `0 2px 8px ${alpha(theme.palette.success.main, 0.3)}` : 'none' }),\n);\n\n// The three indicator variants, out of the component body so the render keeps\n// only its own layout branches.\nconst StrengthIndicator: React.FC<{\n variant: PasswordStrengthProps['variant'];\n strength: number;\n animated: boolean;\n}> = ({ variant, strength, animated }) => {\n const theme = useTheme();\n\n switch (variant) {\n case 'circular':\n return (\n <Box sx={{ position: 'relative', display: 'inline-flex' }}>\n <Box\n sx={{\n width: 80,\n height: 80,\n borderRadius: '50%',\n background: `conic-gradient(\n ${theme.palette.success.main} 0deg ${strength * 3.6}deg,\n ${alpha(theme.palette.action.disabled, 0.1)} ${strength * 3.6}deg 360deg\n )`,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n boxShadow: `0 4px 12px ${alpha(theme.palette.success.main, strength / 200)}` }}\n >\n <Box\n sx={{\n width: 64,\n height: 64,\n borderRadius: '50%',\n background: theme.palette.background.paper,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexDirection: 'column' }}\n >\n <Typography variant=\"h6\" fontWeight=\"bold\">\n {strength}%\n </Typography>\n </Box>\n </Box>\n </Box>\n );\n\n case 'steps': {\n const steps = 5;\n const activeStep = Math.ceil((strength / 100) * steps);\n return (\n <StepsContainer>\n {Array.from({ length: steps }, (_, i) => (\n <Step key={i} active={i === activeStep - 1} completed={i < activeStep - 1} animated={animated} />\n ))}\n </StepsContainer>\n );\n }\n\n default:\n return <StrengthBar variant=\"determinate\" value={strength} strength={strength} animated={animated} />;\n }\n};\n\nconst StrengthHeading: React.FC<{\n copy: PasswordStrengthCopy;\n strength: number;\n hasValue: boolean;\n animated: boolean;\n}> = ({ copy, strength, hasValue, animated }) => (\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {copy.strengthHeading}\n </Typography>\n {hasValue && (\n <Fade in={true}>\n <StrengthLabel\n label={getStrengthLabel(strength, copy.bands)}\n icon={getStrengthIcon(strength)}\n size=\"small\"\n strength={strength}\n animated={animated}\n />\n </Fade>\n )}\n </Box>\n);\n\n// One row per enabled requirement. Previously five near-identical JSX blocks,\n// each with its own `requirements.x &&` guard.\nconst RequirementsList: React.FC<{\n copy: PasswordStrengthCopy;\n requirements: PasswordRequirements;\n checks: RequirementChecks;\n animated: boolean;\n}> = ({ copy, requirements, checks, animated }) => {\n const rows: ReadonlyArray<[unknown, keyof RequirementChecks, React.ReactNode]> = [\n [requirements.minLength, 'length', copy.requirements.minLength(requirements.minLength ?? 0)],\n [requirements.uppercase, 'uppercase', copy.requirements.uppercase],\n [requirements.lowercase, 'lowercase', copy.requirements.lowercase],\n [requirements.numbers, 'numbers', copy.requirements.numbers],\n [requirements.special, 'special', copy.requirements.special],\n ];\n\n return (\n <Stack spacing={1}>\n <Typography variant=\"caption\" color=\"text.secondary\" fontWeight=\"medium\">\n {copy.requirementsHeading}\n </Typography>\n {rows\n .filter(([enabled]) => Boolean(enabled))\n .map(([, key, label]) => (\n <RequirementItem key={key} met={checks[key]} animated={animated}>\n <RequirementIcon met={checks[key]} />\n <Typography variant=\"caption\">{label}</Typography>\n </RequirementItem>\n ))}\n </Stack>\n );\n};\n\n// The visible sections. Kept apart from PasswordStrength itself so the exported\n// component is just prop defaults plus the three memos, and every display guard\n// lives in one place.\nconst PasswordStrengthBody: React.FC<{\n value: string;\n strength: number;\n copy: PasswordStrengthCopy;\n requirements: PasswordRequirements;\n requirementChecks: RequirementChecks;\n suggestions: string[];\n showStrengthLabel: boolean;\n showRequirements: boolean;\n showSuggestions: boolean;\n variant: PasswordStrengthProps['variant'];\n animated: boolean;\n}> = ({\n copy,\n value,\n strength,\n requirements,\n requirementChecks,\n suggestions,\n showStrengthLabel,\n showRequirements,\n showSuggestions,\n variant,\n animated }) => (\n <Stack spacing={2}>\n {showStrengthLabel && (\n <StrengthHeading\n copy={copy}\n strength={strength}\n hasValue={value.length > 0}\n animated={animated}\n />\n )}\n\n <StrengthIndicator variant={variant} strength={strength} animated={animated} />\n\n {showRequirements && (\n <Fade in={value.length > 0}>\n <RequirementsList\n copy={copy}\n requirements={requirements}\n checks={requirementChecks}\n animated={animated}\n />\n </Fade>\n )}\n\n {showSuggestions && suggestions.length > 0 && value.length > 0 && (\n <Fade in={true}>\n <SuggestionsList suggestions={suggestions} heading={copy.suggestionsHeading} />\n </Fade>\n )}\n </Stack>\n);\n\n// Main component\nexport const PasswordStrength: FC<PasswordStrengthProps> = ({\n copy,\n value = '',\n showRequirements = true,\n requirements = defaultRequirements,\n showStrengthLabel = true,\n showSuggestions = false,\n variant = 'linear',\n animated = true,\n 'data-testid': dataTestId }) => {\n\n const strength = useMemo(\n () => calculatePasswordStrength(value, requirements),\n [value, requirements],\n );\n\n const requirementChecks = useMemo(\n () => evaluateRequirements(value, requirements),\n [value, requirements],\n );\n\n const suggestions = useMemo(\n () => buildSuggestions(requirementChecks, copy.suggestions, requirements.minLength ?? 0),\n [requirementChecks, copy.suggestions, requirements.minLength],\n );\n\n return (\n <StrengthContainer animated={animated} data-testid={dataTestId}>\n <PasswordStrengthBody\n copy={copy}\n value={value}\n strength={strength}\n requirements={requirements}\n requirementChecks={requirementChecks}\n suggestions={suggestions}\n showStrengthLabel={showStrengthLabel}\n showRequirements={showRequirements}\n showSuggestions={showSuggestions}\n variant={variant}\n animated={animated}\n />\n </StrengthContainer>\n );\n};\n\n// Export default\nexport default PasswordStrength;\n","import SecurityIcon from '@mui/icons-material/Security';\nimport SuccessIcon from '@mui/icons-material/CheckCircle';\nimport WarningIcon from '@mui/icons-material/Warning';\nimport { Box, Typography } from '@mui/material';\nimport React from 'react';\n\nimport type { PasswordRequirementCopy, PasswordStrengthCopy } from '../../../copy';\nimport type { PasswordRequirements } from './PasswordStrength.types';\n\nexport const defaultRequirements: PasswordRequirements = {\n minLength: 8,\n uppercase: true,\n lowercase: true,\n numbers: true,\n special: true,\n};\n\n\nconst SPECIAL_CHARACTERS = /[!@#$%^&*(),.?\":{}|<>]/;\nconst DEFAULT_MIN_LENGTH = 8;\n\n// Points awarded per satisfied requirement.\nconst REQUIREMENT_POINTS = {\n length: 20,\n uppercase: 10,\n lowercase: 10,\n numbers: 15,\n special: 15,\n} as const;\n\nexport type RequirementChecks = Record<keyof typeof REQUIREMENT_POINTS, boolean>;\n\n// A requirement that is switched off counts as satisfied. This was previously\n// written out twice — once to score the password and once to render the\n// checklist — so the two could drift.\nexport const evaluateRequirements = (\n password: string,\n requirements: PasswordRequirements,\n): RequirementChecks => {\n if (!password) {\n return { length: false, uppercase: false, lowercase: false, numbers: false, special: false };\n }\n\n return {\n length: password.length >= (requirements.minLength || DEFAULT_MIN_LENGTH),\n uppercase: !requirements.uppercase || /[A-Z]/.test(password),\n lowercase: !requirements.lowercase || /[a-z]/.test(password),\n numbers: !requirements.numbers || /\\d/.test(password),\n special: !requirements.special || SPECIAL_CHARACTERS.test(password),\n };\n};\n\nconst lengthBonus = (length: number): number => {\n const base = Math.min(length * 3, 30);\n const over12 = length > 12 ? 10 : 0;\n const over16 = length > 16 ? 10 : 0;\n\n return base + over12 + over16;\n};\n\nexport const calculatePasswordStrength = (\n password: string,\n requirements: PasswordRequirements,\n): number => {\n if (!password) return 0;\n\n const checks = evaluateRequirements(password, requirements);\n const earned = Object.entries(REQUIREMENT_POINTS).reduce(\n (total, [key, points]) => total + (checks[key as keyof RequirementChecks] ? points : 0),\n 0,\n );\n\n return Math.min(earned + lengthBonus(password.length), 100);\n};\n\n// The four ceilings, and which band each one names. The band NAMES come from\n// the host's table — only the thresholds are ours.\nconst STRENGTH_BANDS: ReadonlyArray<[number, keyof PasswordStrengthCopy['bands']]> = [\n [20, 'veryWeak'],\n [40, 'weak'],\n [60, 'fair'],\n [80, 'good'],\n];\n\nexport const getStrengthLabel = (\n strength: number,\n bands: PasswordStrengthCopy['bands'],\n): string => bands[STRENGTH_BANDS.find(([ceiling]) => strength <= ceiling)?.[1] ?? 'strong'];\n\nexport const getStrengthIcon = (strength: number) => {\n if (strength <= 40) return <WarningIcon />;\n if (strength <= 80) return <SecurityIcon />;\n return <SuccessIcon />;\n};\n\n// One tip per unmet requirement, in checklist order. The `length` tip carries\n// the configured minimum, so the number in the sentence is the one enforced.\nconst SUGGESTION_KEYS: ReadonlyArray<[keyof RequirementChecks, keyof PasswordRequirementCopy]> = [\n ['length', 'minLength'],\n ['uppercase', 'uppercase'],\n ['lowercase', 'lowercase'],\n ['numbers', 'numbers'],\n ['special', 'special'],\n];\n\nexport const buildSuggestions = (\n checks: RequirementChecks,\n copy: PasswordRequirementCopy,\n minLength: number,\n): string[] =>\n SUGGESTION_KEYS.filter(([check]) => !checks[check]).map(([, key]) =>\n key === 'minLength' ? copy.minLength(minLength) : copy[key],\n );\n\n// Rendered by PasswordStrength; depends on nothing styled, so it lives beside\n// the pure helpers rather than in the component file.\nexport const SuggestionsList: React.FC<{ suggestions: string[]; heading: string }> = ({\n suggestions,\n heading,\n}) => (\n <Box>\n <Typography variant=\"caption\" color=\"text.secondary\" fontWeight=\"medium\">\n {heading}\n </Typography>\n <Box sx={{ mt: 1 }}>\n {suggestions.map((tip) => (\n <Typography key={tip} variant=\"caption\" display=\"block\" color=\"warning.main\" sx={{ ml: 2 }}>\n • {tip}\n </Typography>\n ))}\n </Box>\n </Box>\n);\n"],"mappings":"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","names":["Box","Typography","jsx","jsxs","Box","Typography"]}
|
package/dist/form/PhoneInput.js
CHANGED
|
@@ -246,6 +246,7 @@ var CountryMenu = styled(Menu)(({ theme }) => ({
|
|
|
246
246
|
}));
|
|
247
247
|
var PhoneField = ({
|
|
248
248
|
TextFieldComponent,
|
|
249
|
+
copy,
|
|
249
250
|
variant,
|
|
250
251
|
label,
|
|
251
252
|
placeholder,
|
|
@@ -284,6 +285,7 @@ var PhoneField = ({
|
|
|
284
285
|
startAdornment: /* @__PURE__ */ jsx(
|
|
285
286
|
CountryAdornment,
|
|
286
287
|
{
|
|
288
|
+
selectCountryLabel: copy.selectCountry,
|
|
287
289
|
country: selectedCountry,
|
|
288
290
|
menuOpen: Boolean(anchorEl),
|
|
289
291
|
onOpen: onCountryOpen,
|
|
@@ -315,12 +317,12 @@ var PHONE_INPUT_DEFAULTS = {
|
|
|
315
317
|
var definedProps = (props) => Object.fromEntries(
|
|
316
318
|
Object.entries(props).filter(([, value]) => value !== void 0)
|
|
317
319
|
);
|
|
318
|
-
var CountryAdornment = ({ country, menuOpen, onOpen, onOpenViaKeyboard }) => /* @__PURE__ */ jsx(InputAdornment, { position: "start", "data-testid": "phone-input-start-adornment", children: /* @__PURE__ */ jsxs(
|
|
320
|
+
var CountryAdornment = ({ country, menuOpen, onOpen, onOpenViaKeyboard, selectCountryLabel }) => /* @__PURE__ */ jsx(InputAdornment, { position: "start", "data-testid": "phone-input-start-adornment", children: /* @__PURE__ */ jsxs(
|
|
319
321
|
CountrySelector,
|
|
320
322
|
{
|
|
321
323
|
onClick: onOpen,
|
|
322
324
|
role: "button",
|
|
323
|
-
"aria-label":
|
|
325
|
+
"aria-label": selectCountryLabel,
|
|
324
326
|
"aria-expanded": menuOpen,
|
|
325
327
|
tabIndex: 0,
|
|
326
328
|
"data-testid": "country-selector",
|
|
@@ -416,15 +418,17 @@ var resolveValidation = ({
|
|
|
416
418
|
value,
|
|
417
419
|
isFocused,
|
|
418
420
|
helper,
|
|
419
|
-
countryName
|
|
421
|
+
countryName,
|
|
422
|
+
copy
|
|
420
423
|
}) => {
|
|
421
424
|
const looksInvalid = !isValid && value !== "" && !isFocused;
|
|
422
425
|
return {
|
|
423
426
|
hasError: error || looksInvalid,
|
|
424
|
-
helperText: errorMessage || (looksInvalid ?
|
|
427
|
+
helperText: errorMessage || (looksInvalid ? copy.invalidNumber(countryName || copy.unknownCountry) : helper)
|
|
425
428
|
};
|
|
426
429
|
};
|
|
427
430
|
var PhoneInput = (props) => {
|
|
431
|
+
const { copy } = props;
|
|
428
432
|
const {
|
|
429
433
|
variant,
|
|
430
434
|
label,
|
|
@@ -462,13 +466,15 @@ var PhoneInput = (props) => {
|
|
|
462
466
|
value,
|
|
463
467
|
isFocused,
|
|
464
468
|
helper,
|
|
465
|
-
countryName: selectedCountry?.name
|
|
469
|
+
countryName: selectedCountry?.name,
|
|
470
|
+
copy
|
|
466
471
|
});
|
|
467
472
|
const TextFieldComponent = variant === "glass" ? GlassTextField : TextField;
|
|
468
473
|
return /* @__PURE__ */ jsxs(Box, { "data-testid": "phone-input-container", children: [
|
|
469
474
|
/* @__PURE__ */ jsx(
|
|
470
475
|
PhoneField,
|
|
471
476
|
{
|
|
477
|
+
copy,
|
|
472
478
|
TextFieldComponent,
|
|
473
479
|
variant,
|
|
474
480
|
label: floating ? void 0 : label,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/form/PhoneInput/PhoneInput.tsx","../../src/components/form/PhoneInput/countries.ts","../../src/components/form/PhoneInput/PhoneInput.hooks.ts","../../src/components/form/PhoneInput/phone.ts"],"sourcesContent":["import ArrowDropDown from '@mui/icons-material/ArrowDropDown';\nimport PhoneIcon from '@mui/icons-material/Phone';\nimport {\n alpha,\n Box,\n InputAdornment,\n ListItemIcon,\n ListItemText,\n Menu,\n MenuItem,\n styled,\n TextField,\n Typography,\n useTheme } from '@mui/material';\nimport type { FC} from 'react';\nimport React, { } from 'react';\n\nimport { countries } from './countries';\nimport { usePhoneInput } from './PhoneInput.hooks';\nimport type { CountryData,PhoneInputProps } from './PhoneInput.types';\n\n// Country data with expanded support\n\n// Styled components\nconst GlassTextField = styled(TextField)(({ theme }) => ({\n '& .MuiOutlinedInput-root': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.8)} 0%, ${alpha(theme.palette.background.paper, 0.6)} 100%)`,\n backdropFilter: 'blur(10px)',\n WebkitBackdropFilter: 'blur(10px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n transition: theme.transitions.create(['border-color', 'box-shadow', 'background']),\n '&:hover': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.9)} 0%, ${alpha(theme.palette.background.paper, 0.7)} 100%)`,\n borderColor: theme.palette.primary.main },\n '&.Mui-focused': {\n background: theme.palette.background.paper,\n boxShadow: `0 0 0 2px ${alpha(theme.palette.primary.main, 0.25)}` },\n '& fieldset': {\n border: 'none' } },\n '& .MuiInputLabel-root': {\n '&.Mui-focused': {\n color: theme.palette.primary.main } } }));\n\nconst CountrySelector = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n padding: theme.spacing(0.5, 1),\n borderRadius: theme.shape.borderRadius,\n cursor: 'pointer',\n transition: theme.transitions.create(['background-color']),\n '&:hover': {\n backgroundColor: alpha(theme.palette.primary.main, 0.08) } }));\n\nconst CountryMenu = styled(Menu)(({ theme }) => ({\n '& .MuiPaper-root': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.98)} 0%, ${alpha(theme.palette.background.paper, 0.95)} 100%)`,\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n maxHeight: 400 } }));\n\n// Helper functions with enhanced validation\n// The text field itself with its two adornments. Split out so PhoneInput is\n// prop defaults, the hook call and composition.\nconst PhoneField: React.FC<{\n TextFieldComponent: React.ElementType;\n variant: PhoneInputProps['variant'];\n label?: React.ReactNode;\n placeholder?: string;\n value: string;\n validation: { hasError: boolean; helperText?: React.ReactNode };\n disabled: boolean;\n required: boolean;\n fullWidth: boolean;\n icon?: React.ReactNode;\n isValid: boolean;\n selectedCountry?: CountryData;\n anchorEl: HTMLElement | null;\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;\n onFocus: () => void;\n onBlur: () => void;\n onCountryOpen: (event: React.MouseEvent<HTMLElement>) => void;\n onCountryOpenViaKeyboard: (element: HTMLElement) => void;\n}> = ({\n TextFieldComponent,\n variant,\n label,\n placeholder,\n value,\n validation,\n disabled,\n required,\n fullWidth,\n icon,\n isValid,\n selectedCountry,\n anchorEl,\n onChange,\n onFocus,\n onBlur,\n onCountryOpen,\n onCountryOpenViaKeyboard }) => (\n <TextFieldComponent\n variant={variant === 'glass' ? 'outlined' : variant}\n label={label}\n placeholder={placeholder}\n value={value}\n onChange={onChange}\n onFocus={onFocus}\n onBlur={onBlur}\n error={validation.hasError}\n helperText={validation.helperText}\n disabled={disabled}\n required={required}\n fullWidth={fullWidth}\n type=\"tel\"\n data-testid=\"phone-input-field\"\n InputProps={{\n startAdornment: (\n <CountryAdornment\n country={selectedCountry}\n menuOpen={Boolean(anchorEl)}\n onOpen={onCountryOpen}\n onOpenViaKeyboard={onCountryOpenViaKeyboard}\n />\n ),\n endAdornment: <ValidityAdornment icon={icon} isValid={isValid} /> }}\n sx={{\n '& input': {\n letterSpacing: '0.5px' } }}\n />\n);\n\nconst PHONE_INPUT_DEFAULTS = {\n variant: 'outlined',\n label: 'Phone Number',\n placeholder: 'Enter phone number',\n icon: <PhoneIcon />,\n defaultValue: '',\n countryCode: 'US',\n floating: false,\n error: false,\n disabled: false,\n required: false,\n fullWidth: true } satisfies Partial<PhoneInputProps>;\n\n// Drops explicitly-undefined props before the merge, so `prop={undefined}` falls\n// back to the default exactly as a destructuring default would. Eleven separate\n// destructuring defaults were most of this component's branch count.\nconst definedProps = (props: PhoneInputProps): Partial<PhoneInputProps> =>\n Object.fromEntries(\n Object.entries(props).filter(([, value]) => value !== undefined),\n ) as Partial<PhoneInputProps>;\n\nconst CountryAdornment: React.FC<{\n country?: CountryData;\n menuOpen: boolean;\n onOpen: (event: React.MouseEvent<HTMLElement>) => void;\n onOpenViaKeyboard: (element: HTMLElement) => void;\n}> = ({ country, menuOpen, onOpen, onOpenViaKeyboard }) => (\n <InputAdornment position=\"start\" data-testid=\"phone-input-start-adornment\">\n <CountrySelector\n onClick={onOpen}\n role=\"button\"\n aria-label=\"Select country\"\n aria-expanded={menuOpen}\n tabIndex={0}\n data-testid=\"country-selector\"\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return;\n e.preventDefault();\n onOpenViaKeyboard(e.currentTarget as HTMLElement);\n }}\n >\n <Typography variant=\"h6\" component=\"span\" sx={{ mr: 0.5 }} data-testid=\"country-flag\">\n {country?.flag}\n </Typography>\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ mr: 0.5 }}\n data-testid=\"country-dial-code\"\n >\n {country?.dial}\n </Typography>\n <ArrowDropDown fontSize=\"small\" data-testid=\"country-dropdown-icon\" />\n </CountrySelector>\n </InputAdornment>\n);\n\n// The trailing icon turns green once the number validates.\nconst ValidityAdornment: React.FC<{ icon?: React.ReactNode; isValid: boolean }> = ({\n icon,\n isValid }) => {\n const theme = useTheme();\n\n if (!icon) return null;\n\n return (\n <InputAdornment position=\"end\" data-testid=\"phone-input-end-adornment\">\n <Box\n sx={{\n color: isValid ? theme.palette.success.main : theme.palette.text.secondary,\n transition: 'color 0.3s ease' }}\n data-testid=\"phone-input-icon\"\n >\n {icon}\n </Box>\n </InputAdornment>\n );\n};\n\nconst CountryPicker: React.FC<{\n anchorEl: HTMLElement | null;\n selectedCode?: CountryData['code'];\n onClose: () => void;\n onSelect: (country: CountryData) => void;\n}> = ({ anchorEl, selectedCode, onClose, onSelect }) => {\n const theme = useTheme();\n\n return (\n <CountryMenu\n anchorEl={anchorEl}\n open={Boolean(anchorEl)}\n onClose={onClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left' }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left' }}\n data-testid=\"country-menu\"\n >\n {countries.map((country) => (\n <MenuItem\n key={country.code}\n onClick={() => onSelect(country)}\n selected={country.code === selectedCode}\n data-testid={`country-option-${country.code}`}\n sx={{\n '&:hover': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.1)} 0%, ${alpha(theme.palette.primary.main, 0.05)} 100%)` } }}\n >\n <ListItemIcon sx={{ minWidth: 36 }}>\n <Typography variant=\"h5\" component=\"span\">\n {country.flag}\n </Typography>\n </ListItemIcon>\n <ListItemText\n primary={country.name}\n secondary={country.dial}\n primaryTypographyProps={{ fontSize: '0.9rem' }}\n secondaryTypographyProps={{ fontSize: '0.8rem' }}\n />\n </MenuItem>\n ))}\n </CountryMenu>\n );\n};\n\n// The field shows an error either because the caller says so, or because what\n// has been typed does not validate and the user has moved on.\nconst resolveValidation = ({\n error,\n errorMessage,\n isValid,\n value,\n isFocused,\n helper,\n countryName }: {\n error: boolean;\n errorMessage?: string;\n isValid: boolean;\n value: string;\n isFocused: boolean;\n helper?: React.ReactNode;\n countryName?: string;\n}) => {\n const looksInvalid = !isValid && value !== '' && !isFocused;\n\n return {\n hasError: error || looksInvalid,\n helperText:\n errorMessage ||\n (looksInvalid ? `Invalid phone number for ${countryName || 'selected country'}` : helper) };\n};\n\nexport const PhoneInput: FC<PhoneInputProps> = (props) => {\n const {\n variant,\n label,\n placeholder,\n icon,\n defaultValue,\n countryCode: initialCountryCode,\n floating,\n onChange,\n helper,\n error,\n errorMessage,\n disabled,\n required,\n fullWidth } = { ...PHONE_INPUT_DEFAULTS, ...definedProps(props) };\n const {\n value,\n selectedCountry,\n anchorEl,\n isValid,\n isFocused,\n setIsFocused,\n setAnchorEl,\n handleCountryClick,\n handleCountryClose,\n handleCountrySelect,\n handleChange,\n handleBlur } = usePhoneInput({ defaultValue, initialCountryCode, onChange });\n\n const validation = resolveValidation({\n error,\n errorMessage,\n isValid,\n value,\n isFocused,\n helper,\n countryName: selectedCountry?.name });\n\n const TextFieldComponent = variant === 'glass' ? GlassTextField : TextField;\n\n return (\n <Box data-testid=\"phone-input-container\">\n <PhoneField\n TextFieldComponent={TextFieldComponent}\n variant={variant}\n label={floating ? undefined : label}\n placeholder={placeholder}\n value={value}\n validation={validation}\n disabled={disabled}\n required={required}\n fullWidth={fullWidth}\n icon={icon}\n isValid={isValid}\n selectedCountry={selectedCountry}\n anchorEl={anchorEl}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={handleBlur}\n onCountryOpen={handleCountryClick}\n onCountryOpenViaKeyboard={setAnchorEl}\n />\n\n <CountryPicker\n anchorEl={anchorEl}\n selectedCode={selectedCountry?.code}\n onClose={handleCountryClose}\n onSelect={handleCountrySelect}\n />\n </Box>\n );\n};\n\n// Export default\nexport default PhoneInput;\n","import type { CountryCode } from 'libphonenumber-js';\n\nimport type { CountryData } from './PhoneInput.types';\n\n// Dial codes and flags for the countries the input offers, sorted by name.\nexport const countries: CountryData[] = [\n { code: 'US' as CountryCode, name: 'United States', dial: '+1', flag: '🇺🇸' },\n { code: 'GB' as CountryCode, name: 'United Kingdom', dial: '+44', flag: '🇬🇧' },\n { code: 'CA' as CountryCode, name: 'Canada', dial: '+1', flag: '🇨🇦' },\n { code: 'AU' as CountryCode, name: 'Australia', dial: '+61', flag: '🇦🇺' },\n { code: 'DE' as CountryCode, name: 'Germany', dial: '+49', flag: '🇩🇪' },\n { code: 'FR' as CountryCode, name: 'France', dial: '+33', flag: '🇫🇷' },\n { code: 'IT' as CountryCode, name: 'Italy', dial: '+39', flag: '🇮🇹' },\n { code: 'ES' as CountryCode, name: 'Spain', dial: '+34', flag: '🇪🇸' },\n { code: 'NL' as CountryCode, name: 'Netherlands', dial: '+31', flag: '🇳🇱' },\n { code: 'JP' as CountryCode, name: 'Japan', dial: '+81', flag: '🇯🇵' },\n { code: 'CN' as CountryCode, name: 'China', dial: '+86', flag: '🇨🇳' },\n { code: 'IN' as CountryCode, name: 'India', dial: '+91', flag: '🇮🇳' },\n { code: 'BR' as CountryCode, name: 'Brazil', dial: '+55', flag: '🇧🇷' },\n { code: 'MX' as CountryCode, name: 'Mexico', dial: '+52', flag: '🇲🇽' },\n { code: 'KR' as CountryCode, name: 'South Korea', dial: '+82', flag: '🇰🇷' },\n { code: 'SE' as CountryCode, name: 'Sweden', dial: '+46', flag: '🇸🇪' },\n { code: 'NO' as CountryCode, name: 'Norway', dial: '+47', flag: '🇳🇴' },\n { code: 'DK' as CountryCode, name: 'Denmark', dial: '+45', flag: '🇩🇰' },\n { code: 'CH' as CountryCode, name: 'Switzerland', dial: '+41', flag: '🇨🇭' },\n { code: 'AT' as CountryCode, name: 'Austria', dial: '+43', flag: '🇦🇹' },\n { code: 'BE' as CountryCode, name: 'Belgium', dial: '+32', flag: '🇧🇪' },\n { code: 'FI' as CountryCode, name: 'Finland', dial: '+358', flag: '🇫🇮' },\n { code: 'IE' as CountryCode, name: 'Ireland', dial: '+353', flag: '🇮🇪' },\n { code: 'PT' as CountryCode, name: 'Portugal', dial: '+351', flag: '🇵🇹' },\n { code: 'GR' as CountryCode, name: 'Greece', dial: '+30', flag: '🇬🇷' },\n { code: 'PL' as CountryCode, name: 'Poland', dial: '+48', flag: '🇵🇱' },\n { code: 'CZ' as CountryCode, name: 'Czech Republic', dial: '+420', flag: '🇨🇿' },\n { code: 'HU' as CountryCode, name: 'Hungary', dial: '+36', flag: '🇭🇺' },\n { code: 'RU' as CountryCode, name: 'Russia', dial: '+7', flag: '🇷🇺' },\n { code: 'ZA' as CountryCode, name: 'South Africa', dial: '+27', flag: '🇿🇦' },\n { code: 'EG' as CountryCode, name: 'Egypt', dial: '+20', flag: '🇪🇬' },\n { code: 'NG' as CountryCode, name: 'Nigeria', dial: '+234', flag: '🇳🇬' },\n { code: 'KE' as CountryCode, name: 'Kenya', dial: '+254', flag: '🇰🇪' },\n { code: 'AR' as CountryCode, name: 'Argentina', dial: '+54', flag: '🇦🇷' },\n { code: 'CL' as CountryCode, name: 'Chile', dial: '+56', flag: '🇨🇱' },\n { code: 'CO' as CountryCode, name: 'Colombia', dial: '+57', flag: '🇨🇴' },\n { code: 'PE' as CountryCode, name: 'Peru', dial: '+51', flag: '🇵🇪' },\n { code: 'MY' as CountryCode, name: 'Malaysia', dial: '+60', flag: '🇲🇾' },\n { code: 'SG' as CountryCode, name: 'Singapore', dial: '+65', flag: '🇸🇬' },\n { code: 'TH' as CountryCode, name: 'Thailand', dial: '+66', flag: '🇹🇭' },\n { code: 'VN' as CountryCode, name: 'Vietnam', dial: '+84', flag: '🇻🇳' },\n { code: 'PH' as CountryCode, name: 'Philippines', dial: '+63', flag: '🇵🇭' },\n { code: 'ID' as CountryCode, name: 'Indonesia', dial: '+62', flag: '🇮🇩' },\n { code: 'TR' as CountryCode, name: 'Turkey', dial: '+90', flag: '🇹🇷' },\n { code: 'IL' as CountryCode, name: 'Israel', dial: '+972', flag: '🇮🇱' },\n { code: 'AE' as CountryCode, name: 'United Arab Emirates', dial: '+971', flag: '🇦🇪' },\n { code: 'SA' as CountryCode, name: 'Saudi Arabia', dial: '+966', flag: '🇸🇦' },\n].sort((a, b) => a.name.localeCompare(b.name));\n","import type { CountryCode } from 'libphonenumber-js';\nimport React, { useEffect, useRef, useState } from 'react';\n\nimport { countries } from './countries';\nimport { detectCountryFromNumber, formatPhoneNumber, validatePhoneNumber } from './phone';\nimport type { CountryData } from './PhoneInput.types';\n\n// Value, selected country, menu anchor and validity, plus the handlers that move\n// between them. Kept out of the component so its body is markup.\nexport const usePhoneInput = ({\n defaultValue,\n initialCountryCode,\n onChange,\n}: {\n defaultValue: string;\n initialCountryCode: CountryCode;\n onChange?: (value: string, isValid: boolean, country: CountryCode) => void;\n}) => {\nconst [value, setValue] = useState(defaultValue);\nconst [selectedCountry, setSelectedCountry] = useState(\n countries.find((c) => c.code === initialCountryCode) || countries[0],\n);\nconst [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\nconst [isValid, setIsValid] = useState(false);\nconst [isFocused, setIsFocused] = useState(false);\nconst onChangeRef = useRef(onChange);\nconst isInitialMountRef = useRef(true);\n\n// Update ref when onChange changes\nuseEffect(() => {\n onChangeRef.current = onChange;\n}, [onChange]);\n\nuseEffect(() => {\n if (!selectedCountry) return;\n\n const valid = validatePhoneNumber(value, selectedCountry.code);\n setIsValid(valid);\n\n // Skip onChange call on initial mount\n if (isInitialMountRef.current) {\n isInitialMountRef.current = false;\n return;\n }\n\n // Call onChange for all value/country changes after initial mount\n if (onChangeRef.current) {\n onChangeRef.current(value, valid, selectedCountry.code);\n }\n}, [value, selectedCountry]);\n\n const handlers = usePhoneHandlers({\n value,\n setValue,\n selectedCountry,\n setSelectedCountry,\n setAnchorEl,\n setIsFocused,\n });\n\n return {\n value,\n selectedCountry,\n anchorEl,\n isValid,\n isFocused,\n setIsFocused,\n setAnchorEl,\n ...handlers,\n };\n};\n\n// Opening and closing the country menu, picking a country, typing, and\n// reformatting on blur.\nconst usePhoneHandlers = ({\n value,\n setValue,\n selectedCountry,\n setSelectedCountry,\n setAnchorEl,\n setIsFocused,\n}: {\n value: string;\n setValue: React.Dispatch<React.SetStateAction<string>>;\n selectedCountry?: CountryData;\n setSelectedCountry: React.Dispatch<React.SetStateAction<CountryData | undefined>>;\n setAnchorEl: React.Dispatch<React.SetStateAction<null | HTMLElement>>;\n setIsFocused: React.Dispatch<React.SetStateAction<boolean>>;\n}) => {\n const handleCountryClick = (event: React.MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleCountryClose = () => {\n setAnchorEl(null);\n };\n\n const handleCountrySelect = (country: CountryData) => {\n setSelectedCountry(country);\n handleCountryClose();\n\n // Reformat number with new country code\n if (value) {\n const formatted = formatPhoneNumber(value, country.code);\n setValue(formatted);\n }\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = event.target.value;\n setValue(newValue);\n\n // Auto-detect country if user types international format\n if (newValue.startsWith('+')) {\n const detectedCountry = detectCountryFromNumber(newValue);\n if (detectedCountry) {\n const detectedCountryData = countries.find((c) => c.code === detectedCountry);\n if (detectedCountryData && detectedCountryData.code !== selectedCountry?.code) {\n setSelectedCountry(detectedCountryData);\n }\n }\n }\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n if (value && selectedCountry) {\n const formatted = formatPhoneNumber(value, selectedCountry.code);\n setValue(formatted);\n }\n };\n\n return { handleCountryClick, handleCountryClose, handleCountrySelect, handleChange, handleBlur };\n};\n","import type { CountryCode } from 'libphonenumber-js';\nimport { parsePhoneNumber } from 'libphonenumber-js';\n\n\nexport const formatPhoneNumber = (value: string, country: CountryCode): string => {\n if (!value || value.trim() === '') return value;\n\n try {\n const phoneNumber = parsePhoneNumber(value, country);\n // Format if we have a phone number object, regardless of strict validity\n // This allows formatting of test numbers (like 555 prefix) and partially valid numbers\n if (phoneNumber) {\n return phoneNumber.formatInternational();\n }\n } catch {\n // Silently handle formatting errors\n }\n return value;\n};\n\nexport const validatePhoneNumber = (value: string, country: CountryCode): boolean => {\n if (!value || value.trim() === '') return false;\n\n try {\n const phoneNumber = parsePhoneNumber(value, country);\n // Consider a number valid if it can be parsed and has the right format\n // This allows test numbers (like 555 prefix) which isPossible but not isValid\n return phoneNumber ? phoneNumber.isPossible() : false;\n } catch {\n // Silently handle validation errors\n return false;\n }\n};\n\n// Enhanced helper to detect country from number\nexport const detectCountryFromNumber = (value: string): CountryCode | undefined => {\n if (!value || !value.startsWith('+')) return undefined;\n\n try {\n const phoneNumber = parsePhoneNumber(value);\n return phoneNumber?.country;\n } catch {\n return undefined;\n }\n};\n\n// Main component\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/form/PhoneInput/PhoneInput.tsx","../../src/components/form/PhoneInput/countries.ts","../../src/components/form/PhoneInput/PhoneInput.hooks.ts","../../src/components/form/PhoneInput/phone.ts"],"sourcesContent":["import ArrowDropDown from '@mui/icons-material/ArrowDropDown';\nimport PhoneIcon from '@mui/icons-material/Phone';\nimport {\n alpha,\n Box,\n InputAdornment,\n ListItemIcon,\n ListItemText,\n Menu,\n MenuItem,\n styled,\n TextField,\n Typography,\n useTheme } from '@mui/material';\nimport type { FC} from 'react';\nimport React, { } from 'react';\n\nimport { countries } from './countries';\nimport { usePhoneInput } from './PhoneInput.hooks';\nimport type { CountryData,PhoneInputProps } from './PhoneInput.types';\nimport type { PhoneInputCopy } from '../../../copy';\n\n// Country data with expanded support\n\n// Styled components\nconst GlassTextField = styled(TextField)(({ theme }) => ({\n '& .MuiOutlinedInput-root': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.8)} 0%, ${alpha(theme.palette.background.paper, 0.6)} 100%)`,\n backdropFilter: 'blur(10px)',\n WebkitBackdropFilter: 'blur(10px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n transition: theme.transitions.create(['border-color', 'box-shadow', 'background']),\n '&:hover': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.9)} 0%, ${alpha(theme.palette.background.paper, 0.7)} 100%)`,\n borderColor: theme.palette.primary.main },\n '&.Mui-focused': {\n background: theme.palette.background.paper,\n boxShadow: `0 0 0 2px ${alpha(theme.palette.primary.main, 0.25)}` },\n '& fieldset': {\n border: 'none' } },\n '& .MuiInputLabel-root': {\n '&.Mui-focused': {\n color: theme.palette.primary.main } } }));\n\nconst CountrySelector = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n padding: theme.spacing(0.5, 1),\n borderRadius: theme.shape.borderRadius,\n cursor: 'pointer',\n transition: theme.transitions.create(['background-color']),\n '&:hover': {\n backgroundColor: alpha(theme.palette.primary.main, 0.08) } }));\n\nconst CountryMenu = styled(Menu)(({ theme }) => ({\n '& .MuiPaper-root': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.98)} 0%, ${alpha(theme.palette.background.paper, 0.95)} 100%)`,\n backdropFilter: 'blur(20px)',\n WebkitBackdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.18)}`,\n maxHeight: 400 } }));\n\n// Helper functions with enhanced validation\n// The text field itself with its two adornments. Split out so PhoneInput is\n// prop defaults, the hook call and composition.\nconst PhoneField: React.FC<{\n TextFieldComponent: React.ElementType;\n variant: PhoneInputProps['variant'];\n label?: React.ReactNode;\n placeholder?: string;\n value: string;\n validation: { hasError: boolean; helperText?: React.ReactNode };\n disabled: boolean;\n required: boolean;\n fullWidth: boolean;\n icon?: React.ReactNode;\n isValid: boolean;\n selectedCountry?: CountryData;\n anchorEl: HTMLElement | null;\n onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;\n onFocus: () => void;\n onBlur: () => void;\n onCountryOpen: (event: React.MouseEvent<HTMLElement>) => void;\n onCountryOpenViaKeyboard: (element: HTMLElement) => void;\n copy: PhoneInputCopy;\n}> = ({\n TextFieldComponent,\n copy,\n variant,\n label,\n placeholder,\n value,\n validation,\n disabled,\n required,\n fullWidth,\n icon,\n isValid,\n selectedCountry,\n anchorEl,\n onChange,\n onFocus,\n onBlur,\n onCountryOpen,\n onCountryOpenViaKeyboard }) => (\n <TextFieldComponent\n variant={variant === 'glass' ? 'outlined' : variant}\n label={label}\n placeholder={placeholder}\n value={value}\n onChange={onChange}\n onFocus={onFocus}\n onBlur={onBlur}\n error={validation.hasError}\n helperText={validation.helperText}\n disabled={disabled}\n required={required}\n fullWidth={fullWidth}\n type=\"tel\"\n data-testid=\"phone-input-field\"\n InputProps={{\n startAdornment: (\n <CountryAdornment\n selectCountryLabel={copy.selectCountry}\n country={selectedCountry}\n menuOpen={Boolean(anchorEl)}\n onOpen={onCountryOpen}\n onOpenViaKeyboard={onCountryOpenViaKeyboard}\n />\n ),\n endAdornment: <ValidityAdornment icon={icon} isValid={isValid} /> }}\n sx={{\n '& input': {\n letterSpacing: '0.5px' } }}\n />\n);\n\nconst PHONE_INPUT_DEFAULTS = {\n variant: 'outlined',\n label: 'Phone Number',\n placeholder: 'Enter phone number',\n icon: <PhoneIcon />,\n defaultValue: '',\n countryCode: 'US',\n floating: false,\n error: false,\n disabled: false,\n required: false,\n fullWidth: true } satisfies Partial<PhoneInputProps>;\n\n// Drops explicitly-undefined props before the merge, so `prop={undefined}` falls\n// back to the default exactly as a destructuring default would. Eleven separate\n// destructuring defaults were most of this component's branch count.\nconst definedProps = (props: PhoneInputProps): Partial<PhoneInputProps> =>\n Object.fromEntries(\n Object.entries(props).filter(([, value]) => value !== undefined),\n ) as Partial<PhoneInputProps>;\n\nconst CountryAdornment: React.FC<{\n country?: CountryData;\n menuOpen: boolean;\n onOpen: (event: React.MouseEvent<HTMLElement>) => void;\n onOpenViaKeyboard: (element: HTMLElement) => void;\n selectCountryLabel: string;\n}> = ({ country, menuOpen, onOpen, onOpenViaKeyboard, selectCountryLabel }) => (\n <InputAdornment position=\"start\" data-testid=\"phone-input-start-adornment\">\n <CountrySelector\n onClick={onOpen}\n role=\"button\"\n aria-label={selectCountryLabel}\n aria-expanded={menuOpen}\n tabIndex={0}\n data-testid=\"country-selector\"\n onKeyDown={(e) => {\n if (e.key !== 'Enter' && e.key !== ' ') return;\n e.preventDefault();\n onOpenViaKeyboard(e.currentTarget as HTMLElement);\n }}\n >\n <Typography variant=\"h6\" component=\"span\" sx={{ mr: 0.5 }} data-testid=\"country-flag\">\n {country?.flag}\n </Typography>\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ mr: 0.5 }}\n data-testid=\"country-dial-code\"\n >\n {country?.dial}\n </Typography>\n <ArrowDropDown fontSize=\"small\" data-testid=\"country-dropdown-icon\" />\n </CountrySelector>\n </InputAdornment>\n);\n\n// The trailing icon turns green once the number validates.\nconst ValidityAdornment: React.FC<{ icon?: React.ReactNode; isValid: boolean }> = ({\n icon,\n isValid }) => {\n const theme = useTheme();\n\n if (!icon) return null;\n\n return (\n <InputAdornment position=\"end\" data-testid=\"phone-input-end-adornment\">\n <Box\n sx={{\n color: isValid ? theme.palette.success.main : theme.palette.text.secondary,\n transition: 'color 0.3s ease' }}\n data-testid=\"phone-input-icon\"\n >\n {icon}\n </Box>\n </InputAdornment>\n );\n};\n\nconst CountryPicker: React.FC<{\n anchorEl: HTMLElement | null;\n selectedCode?: CountryData['code'];\n onClose: () => void;\n onSelect: (country: CountryData) => void;\n}> = ({ anchorEl, selectedCode, onClose, onSelect }) => {\n const theme = useTheme();\n\n return (\n <CountryMenu\n anchorEl={anchorEl}\n open={Boolean(anchorEl)}\n onClose={onClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left' }}\n transformOrigin={{\n vertical: 'top',\n horizontal: 'left' }}\n data-testid=\"country-menu\"\n >\n {countries.map((country) => (\n <MenuItem\n key={country.code}\n onClick={() => onSelect(country)}\n selected={country.code === selectedCode}\n data-testid={`country-option-${country.code}`}\n sx={{\n '&:hover': {\n background: `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.1)} 0%, ${alpha(theme.palette.primary.main, 0.05)} 100%)` } }}\n >\n <ListItemIcon sx={{ minWidth: 36 }}>\n <Typography variant=\"h5\" component=\"span\">\n {country.flag}\n </Typography>\n </ListItemIcon>\n <ListItemText\n primary={country.name}\n secondary={country.dial}\n primaryTypographyProps={{ fontSize: '0.9rem' }}\n secondaryTypographyProps={{ fontSize: '0.8rem' }}\n />\n </MenuItem>\n ))}\n </CountryMenu>\n );\n};\n\n// The field shows an error either because the caller says so, or because what\n// has been typed does not validate and the user has moved on.\nconst resolveValidation = ({\n error,\n errorMessage,\n isValid,\n value,\n isFocused,\n helper,\n countryName,\n copy }: {\n error: boolean;\n errorMessage?: string;\n isValid: boolean;\n value: string;\n isFocused: boolean;\n helper?: React.ReactNode;\n countryName?: string;\n copy: PhoneInputCopy;\n}) => {\n const looksInvalid = !isValid && value !== '' && !isFocused;\n\n return {\n hasError: error || looksInvalid,\n helperText:\n errorMessage ||\n (looksInvalid ? copy.invalidNumber(countryName || copy.unknownCountry) : helper) };\n};\n\nexport const PhoneInput: FC<PhoneInputProps> = (props) => {\n const { copy } = props;\n const {\n variant,\n label,\n placeholder,\n icon,\n defaultValue,\n countryCode: initialCountryCode,\n floating,\n onChange,\n helper,\n error,\n errorMessage,\n disabled,\n required,\n fullWidth } = { ...PHONE_INPUT_DEFAULTS, ...definedProps(props) };\n const {\n value,\n selectedCountry,\n anchorEl,\n isValid,\n isFocused,\n setIsFocused,\n setAnchorEl,\n handleCountryClick,\n handleCountryClose,\n handleCountrySelect,\n handleChange,\n handleBlur } = usePhoneInput({ defaultValue, initialCountryCode, onChange });\n\n const validation = resolveValidation({\n error,\n errorMessage,\n isValid,\n value,\n isFocused,\n helper,\n countryName: selectedCountry?.name,\n copy });\n\n const TextFieldComponent = variant === 'glass' ? GlassTextField : TextField;\n\n return (\n <Box data-testid=\"phone-input-container\">\n <PhoneField\n copy={copy}\n TextFieldComponent={TextFieldComponent}\n variant={variant}\n label={floating ? undefined : label}\n placeholder={placeholder}\n value={value}\n validation={validation}\n disabled={disabled}\n required={required}\n fullWidth={fullWidth}\n icon={icon}\n isValid={isValid}\n selectedCountry={selectedCountry}\n anchorEl={anchorEl}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={handleBlur}\n onCountryOpen={handleCountryClick}\n onCountryOpenViaKeyboard={setAnchorEl}\n />\n\n <CountryPicker\n anchorEl={anchorEl}\n selectedCode={selectedCountry?.code}\n onClose={handleCountryClose}\n onSelect={handleCountrySelect}\n />\n </Box>\n );\n};\n\n// Export default\nexport default PhoneInput;\n","import type { CountryCode } from 'libphonenumber-js';\n\nimport type { CountryData } from './PhoneInput.types';\n\n// Dial codes and flags for the countries the input offers, sorted by name.\nexport const countries: CountryData[] = [\n { code: 'US' as CountryCode, name: 'United States', dial: '+1', flag: '🇺🇸' },\n { code: 'GB' as CountryCode, name: 'United Kingdom', dial: '+44', flag: '🇬🇧' },\n { code: 'CA' as CountryCode, name: 'Canada', dial: '+1', flag: '🇨🇦' },\n { code: 'AU' as CountryCode, name: 'Australia', dial: '+61', flag: '🇦🇺' },\n { code: 'DE' as CountryCode, name: 'Germany', dial: '+49', flag: '🇩🇪' },\n { code: 'FR' as CountryCode, name: 'France', dial: '+33', flag: '🇫🇷' },\n { code: 'IT' as CountryCode, name: 'Italy', dial: '+39', flag: '🇮🇹' },\n { code: 'ES' as CountryCode, name: 'Spain', dial: '+34', flag: '🇪🇸' },\n { code: 'NL' as CountryCode, name: 'Netherlands', dial: '+31', flag: '🇳🇱' },\n { code: 'JP' as CountryCode, name: 'Japan', dial: '+81', flag: '🇯🇵' },\n { code: 'CN' as CountryCode, name: 'China', dial: '+86', flag: '🇨🇳' },\n { code: 'IN' as CountryCode, name: 'India', dial: '+91', flag: '🇮🇳' },\n { code: 'BR' as CountryCode, name: 'Brazil', dial: '+55', flag: '🇧🇷' },\n { code: 'MX' as CountryCode, name: 'Mexico', dial: '+52', flag: '🇲🇽' },\n { code: 'KR' as CountryCode, name: 'South Korea', dial: '+82', flag: '🇰🇷' },\n { code: 'SE' as CountryCode, name: 'Sweden', dial: '+46', flag: '🇸🇪' },\n { code: 'NO' as CountryCode, name: 'Norway', dial: '+47', flag: '🇳🇴' },\n { code: 'DK' as CountryCode, name: 'Denmark', dial: '+45', flag: '🇩🇰' },\n { code: 'CH' as CountryCode, name: 'Switzerland', dial: '+41', flag: '🇨🇭' },\n { code: 'AT' as CountryCode, name: 'Austria', dial: '+43', flag: '🇦🇹' },\n { code: 'BE' as CountryCode, name: 'Belgium', dial: '+32', flag: '🇧🇪' },\n { code: 'FI' as CountryCode, name: 'Finland', dial: '+358', flag: '🇫🇮' },\n { code: 'IE' as CountryCode, name: 'Ireland', dial: '+353', flag: '🇮🇪' },\n { code: 'PT' as CountryCode, name: 'Portugal', dial: '+351', flag: '🇵🇹' },\n { code: 'GR' as CountryCode, name: 'Greece', dial: '+30', flag: '🇬🇷' },\n { code: 'PL' as CountryCode, name: 'Poland', dial: '+48', flag: '🇵🇱' },\n { code: 'CZ' as CountryCode, name: 'Czech Republic', dial: '+420', flag: '🇨🇿' },\n { code: 'HU' as CountryCode, name: 'Hungary', dial: '+36', flag: '🇭🇺' },\n { code: 'RU' as CountryCode, name: 'Russia', dial: '+7', flag: '🇷🇺' },\n { code: 'ZA' as CountryCode, name: 'South Africa', dial: '+27', flag: '🇿🇦' },\n { code: 'EG' as CountryCode, name: 'Egypt', dial: '+20', flag: '🇪🇬' },\n { code: 'NG' as CountryCode, name: 'Nigeria', dial: '+234', flag: '🇳🇬' },\n { code: 'KE' as CountryCode, name: 'Kenya', dial: '+254', flag: '🇰🇪' },\n { code: 'AR' as CountryCode, name: 'Argentina', dial: '+54', flag: '🇦🇷' },\n { code: 'CL' as CountryCode, name: 'Chile', dial: '+56', flag: '🇨🇱' },\n { code: 'CO' as CountryCode, name: 'Colombia', dial: '+57', flag: '🇨🇴' },\n { code: 'PE' as CountryCode, name: 'Peru', dial: '+51', flag: '🇵🇪' },\n { code: 'MY' as CountryCode, name: 'Malaysia', dial: '+60', flag: '🇲🇾' },\n { code: 'SG' as CountryCode, name: 'Singapore', dial: '+65', flag: '🇸🇬' },\n { code: 'TH' as CountryCode, name: 'Thailand', dial: '+66', flag: '🇹🇭' },\n { code: 'VN' as CountryCode, name: 'Vietnam', dial: '+84', flag: '🇻🇳' },\n { code: 'PH' as CountryCode, name: 'Philippines', dial: '+63', flag: '🇵🇭' },\n { code: 'ID' as CountryCode, name: 'Indonesia', dial: '+62', flag: '🇮🇩' },\n { code: 'TR' as CountryCode, name: 'Turkey', dial: '+90', flag: '🇹🇷' },\n { code: 'IL' as CountryCode, name: 'Israel', dial: '+972', flag: '🇮🇱' },\n { code: 'AE' as CountryCode, name: 'United Arab Emirates', dial: '+971', flag: '🇦🇪' },\n { code: 'SA' as CountryCode, name: 'Saudi Arabia', dial: '+966', flag: '🇸🇦' },\n].sort((a, b) => a.name.localeCompare(b.name));\n","import type { CountryCode } from 'libphonenumber-js';\nimport React, { useEffect, useRef, useState } from 'react';\n\nimport { countries } from './countries';\nimport { detectCountryFromNumber, formatPhoneNumber, validatePhoneNumber } from './phone';\nimport type { CountryData } from './PhoneInput.types';\n\n// Value, selected country, menu anchor and validity, plus the handlers that move\n// between them. Kept out of the component so its body is markup.\nexport const usePhoneInput = ({\n defaultValue,\n initialCountryCode,\n onChange,\n}: {\n defaultValue: string;\n initialCountryCode: CountryCode;\n onChange?: (value: string, isValid: boolean, country: CountryCode) => void;\n}) => {\nconst [value, setValue] = useState(defaultValue);\nconst [selectedCountry, setSelectedCountry] = useState(\n countries.find((c) => c.code === initialCountryCode) || countries[0],\n);\nconst [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);\nconst [isValid, setIsValid] = useState(false);\nconst [isFocused, setIsFocused] = useState(false);\nconst onChangeRef = useRef(onChange);\nconst isInitialMountRef = useRef(true);\n\n// Update ref when onChange changes\nuseEffect(() => {\n onChangeRef.current = onChange;\n}, [onChange]);\n\nuseEffect(() => {\n if (!selectedCountry) return;\n\n const valid = validatePhoneNumber(value, selectedCountry.code);\n setIsValid(valid);\n\n // Skip onChange call on initial mount\n if (isInitialMountRef.current) {\n isInitialMountRef.current = false;\n return;\n }\n\n // Call onChange for all value/country changes after initial mount\n if (onChangeRef.current) {\n onChangeRef.current(value, valid, selectedCountry.code);\n }\n}, [value, selectedCountry]);\n\n const handlers = usePhoneHandlers({\n value,\n setValue,\n selectedCountry,\n setSelectedCountry,\n setAnchorEl,\n setIsFocused,\n });\n\n return {\n value,\n selectedCountry,\n anchorEl,\n isValid,\n isFocused,\n setIsFocused,\n setAnchorEl,\n ...handlers,\n };\n};\n\n// Opening and closing the country menu, picking a country, typing, and\n// reformatting on blur.\nconst usePhoneHandlers = ({\n value,\n setValue,\n selectedCountry,\n setSelectedCountry,\n setAnchorEl,\n setIsFocused,\n}: {\n value: string;\n setValue: React.Dispatch<React.SetStateAction<string>>;\n selectedCountry?: CountryData;\n setSelectedCountry: React.Dispatch<React.SetStateAction<CountryData | undefined>>;\n setAnchorEl: React.Dispatch<React.SetStateAction<null | HTMLElement>>;\n setIsFocused: React.Dispatch<React.SetStateAction<boolean>>;\n}) => {\n const handleCountryClick = (event: React.MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleCountryClose = () => {\n setAnchorEl(null);\n };\n\n const handleCountrySelect = (country: CountryData) => {\n setSelectedCountry(country);\n handleCountryClose();\n\n // Reformat number with new country code\n if (value) {\n const formatted = formatPhoneNumber(value, country.code);\n setValue(formatted);\n }\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = event.target.value;\n setValue(newValue);\n\n // Auto-detect country if user types international format\n if (newValue.startsWith('+')) {\n const detectedCountry = detectCountryFromNumber(newValue);\n if (detectedCountry) {\n const detectedCountryData = countries.find((c) => c.code === detectedCountry);\n if (detectedCountryData && detectedCountryData.code !== selectedCountry?.code) {\n setSelectedCountry(detectedCountryData);\n }\n }\n }\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n if (value && selectedCountry) {\n const formatted = formatPhoneNumber(value, selectedCountry.code);\n setValue(formatted);\n }\n };\n\n return { handleCountryClick, handleCountryClose, handleCountrySelect, handleChange, handleBlur };\n};\n","import type { CountryCode } from 'libphonenumber-js';\nimport { parsePhoneNumber } from 'libphonenumber-js';\n\n\nexport const formatPhoneNumber = (value: string, country: CountryCode): string => {\n if (!value || value.trim() === '') return value;\n\n try {\n const phoneNumber = parsePhoneNumber(value, country);\n // Format if we have a phone number object, regardless of strict validity\n // This allows formatting of test numbers (like 555 prefix) and partially valid numbers\n if (phoneNumber) {\n return phoneNumber.formatInternational();\n }\n } catch {\n // Silently handle formatting errors\n }\n return value;\n};\n\nexport const validatePhoneNumber = (value: string, country: CountryCode): boolean => {\n if (!value || value.trim() === '') return false;\n\n try {\n const phoneNumber = parsePhoneNumber(value, country);\n // Consider a number valid if it can be parsed and has the right format\n // This allows test numbers (like 555 prefix) which isPossible but not isValid\n return phoneNumber ? phoneNumber.isPossible() : false;\n } catch {\n // Silently handle validation errors\n return false;\n }\n};\n\n// Enhanced helper to detect country from number\nexport const detectCountryFromNumber = (value: string): CountryCode | undefined => {\n if (!value || !value.startsWith('+')) return undefined;\n\n try {\n const phoneNumber = parsePhoneNumber(value);\n return phoneNumber?.country;\n } catch {\n return undefined;\n }\n};\n\n// Main component\n"],"mappings":"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","names":[]}
|
|
@@ -167,7 +167,7 @@ function SubmitAllFooter(props) {
|
|
|
167
167
|
/* @__PURE__ */ jsx(SearchIcon, { fontSize: "small", sx: { color: "text.secondary", flexShrink: 0 } }),
|
|
168
168
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { flex: 1, minWidth: 0 }, noWrap: true, children: props.label }),
|
|
169
169
|
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5, color: "text.secondary" }, children: [
|
|
170
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", children:
|
|
170
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", children: props.keyLabel }),
|
|
171
171
|
/* @__PURE__ */ jsx(KeyboardReturnIcon, { sx: { fontSize: 14 } })
|
|
172
172
|
] })
|
|
173
173
|
]
|
|
@@ -200,6 +200,7 @@ function PaletteBody(view) {
|
|
|
200
200
|
SubmitAllFooter,
|
|
201
201
|
{
|
|
202
202
|
label: view.submitAllLabel(view.trimmed),
|
|
203
|
+
keyLabel: view.submitKeyLabel,
|
|
203
204
|
selected: view.activeIndex === view.footerIndex,
|
|
204
205
|
onSelect: () => view.onSubmitAll?.(view.trimmed),
|
|
205
206
|
onHover: () => view.setActiveIndex(view.footerIndex)
|
|
@@ -265,6 +266,7 @@ function resolveOpts(props) {
|
|
|
265
266
|
function resolveText(props) {
|
|
266
267
|
return {
|
|
267
268
|
submitAllLabel: props.submitAllLabel,
|
|
269
|
+
submitKeyLabel: props.submitKeyLabel,
|
|
268
270
|
noResultsLabel: props.noResultsLabel,
|
|
269
271
|
placeholder: props.placeholder,
|
|
270
272
|
dataTestId: props.dataTestId ?? "search-palette"
|
|
@@ -344,6 +346,7 @@ function SearchPalette(props) {
|
|
|
344
346
|
activeIndex,
|
|
345
347
|
trimmed,
|
|
346
348
|
submitAllLabel: text.submitAllLabel,
|
|
349
|
+
submitKeyLabel: text.submitKeyLabel,
|
|
347
350
|
getKey: props.getKey,
|
|
348
351
|
getPrimary: props.getPrimary,
|
|
349
352
|
getSecondary: props.getSecondary,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/form/SearchPalette/SearchPalette.tsx","../../src/components/form/SearchPalette/SearchPalette.parts.tsx"],"sourcesContent":["import React from 'react';\n\nimport {\n createKeyDownHandler,\n FilterChips,\n ghostRemainder,\n PALETTE_CARD_SX,\n PaletteBody,\n PaletteInput,\n PaletteScrollArea,\n Paper,\n} from './SearchPalette.parts';\nimport type { SearchPaletteProps } from './SearchPalette.types';\n\n/**\n * A Gmail-style search palette (FUT-168): a single card with a search input,\n * a toggleable filter-chip row, a compact list of rich result rows (leading\n * image/avatar/icon + primary/secondary + trailing), and a \"see all results\"\n * footer. Fully app-agnostic — every label, filter, and row field is supplied\n * by the host through accessors, so it drops into any surface.\n *\n * Keyboard: ↑/↓ move the active row across the results and the footer, Enter\n * selects it (row → `onSelect`, footer → `onSubmitAll`), Esc calls `onClose`,\n * and Tab/→ accepts the inline ghost completion of the top result.\n *\n * Presentational pieces + the keydown builder live in `SearchPalette.parts`.\n */\n\nconst DEFAULT_MAX_ITEMS = 5;\n\n/** Non-string option defaults (numbers / flags / arrays). */\nfunction resolveOpts<T>(props: SearchPaletteProps<T>): {\n maxItems: number;\n filters: NonNullable<SearchPaletteProps<T>['filters']>;\n activeFilterIds: string[];\n isLoading: boolean;\n showGhostText: boolean;\n autoFocus: boolean;\n} {\n return {\n maxItems: props.maxItems ?? DEFAULT_MAX_ITEMS,\n filters: props.filters ?? [],\n activeFilterIds: props.activeFilterIds ?? [],\n isLoading: Boolean(props.isLoading),\n showGhostText: props.showGhostText ?? true,\n autoFocus: props.autoFocus ?? true,\n };\n}\n\n/** String / label defaults. */\nfunction resolveText<T>(props: SearchPaletteProps<T>): {\n submitAllLabel: (q: string) => string;\n noResultsLabel: string;\n placeholder: string;\n dataTestId: string;\n} {\n return {\n submitAllLabel: props.submitAllLabel,\n noResultsLabel: props.noResultsLabel,\n placeholder: props.placeholder,\n dataTestId: props.dataTestId ?? 'search-palette',\n };\n}\n\nexport function SearchPalette<T>(props: SearchPaletteProps<T>): React.JSX.Element {\n const opts = resolveOpts(props);\n const text = resolveText(props);\n\n const trimmed = props.value.trim();\n const hasQuery = trimmed.length > 0;\n const visible = props.items.slice(0, opts.maxItems);\n const showFooter = Boolean(props.onSubmitAll) && hasQuery;\n const rowCount = visible.length + (showFooter ? 1 : 0);\n const footerIndex = showFooter ? visible.length : -1;\n\n const [activeIndex, setActiveIndex] = React.useState(0);\n // Keep the active row in range as results change (typing, filtering).\n React.useEffect(() => {\n setActiveIndex((i) => (rowCount === 0 ? 0 : Math.min(i, rowCount - 1)));\n }, [rowCount]);\n\n const top = visible[0];\n const ghost =\n opts.showGhostText && hasQuery && top ? ghostRemainder(props.getPrimary(top), props.value) : '';\n\n const onKeyDown = createKeyDownHandler<T>({\n rowCount,\n activeIndex,\n footerIndex,\n visible,\n hasQuery,\n ghost,\n top,\n trimmed,\n getPrimary: props.getPrimary,\n onChange: props.onChange,\n onSelect: props.onSelect,\n onSubmitAll: props.onSubmitAll,\n onClose: props.onClose,\n setActiveIndex,\n });\n\n return (\n <Paper\n elevation={8}\n className={props.className}\n data-testid={text.dataTestId}\n sx={PALETTE_CARD_SX}\n >\n <PaletteInput\n value={props.value}\n ghost={ghost}\n isLoading={opts.isLoading}\n autoFocus={opts.autoFocus}\n placeholder={text.placeholder}\n inputAriaLabel={props.inputAriaLabel}\n hasQuery={hasQuery}\n onChange={props.onChange}\n onKeyDown={onKeyDown}\n />\n {opts.filters.length > 0 && (\n <FilterChips\n filters={opts.filters}\n activeFilterIds={opts.activeFilterIds}\n onToggleFilter={props.onToggleFilter}\n />\n )}\n <PaletteScrollArea>\n <PaletteBody<T>\n hasQuery={hasQuery} emptyQueryContent={props.emptyQueryContent} visible={visible}\n isLoading={opts.isLoading} noResultsLabel={text.noResultsLabel} showFooter={showFooter}\n footerIndex={footerIndex} activeIndex={activeIndex} trimmed={trimmed}\n submitAllLabel={text.submitAllLabel} getKey={props.getKey} getPrimary={props.getPrimary}\n getSecondary={props.getSecondary} getLead={props.getLead} getTrailing={props.getTrailing}\n onSelect={props.onSelect} onSubmitAll={props.onSubmitAll} setActiveIndex={setActiveIndex}\n />\n </PaletteScrollArea>\n </Paper>\n );\n}\n\nSearchPalette.displayName = 'SearchPalette';\n\nexport default SearchPalette;\n","import KeyboardReturnIcon from '@mui/icons-material/KeyboardReturn';\nimport SearchIcon from '@mui/icons-material/Search';\nimport {\n Avatar,\n Box,\n Chip,\n CircularProgress,\n InputBase,\n List,\n ListItemButton,\n Paper,\n Typography,\n} from '@mui/material';\nimport React from 'react';\n\nimport type { SearchFilterChip, SearchResultLead } from './SearchPalette.types';\n\n/** Bold the first case-insensitive occurrence of `query` within `text`. */\nexport function highlight(text: string, query: string): React.ReactNode {\n const q = query.trim();\n const at = q ? text.toLowerCase().indexOf(q.toLowerCase()) : -1;\n if (at < 0) return text;\n return (\n <>\n {text.slice(0, at)}\n <Box component=\"span\" sx={{ fontWeight: 700 }}>\n {text.slice(at, at + q.length)}\n </Box>\n {text.slice(at + q.length)}\n </>\n );\n}\n\n/** The leading visual of a result row (image / avatar / icon). */\nexport function Lead({ lead }: { lead: SearchResultLead | undefined }): React.JSX.Element | null {\n if (!lead) return null;\n if (lead.kind === 'image') {\n return (\n <Box\n component=\"img\"\n src={lead.src}\n alt={lead.alt ?? ''}\n sx={{ width: 32, height: 32, borderRadius: 1, objectFit: 'cover', flexShrink: 0 }}\n />\n );\n }\n if (lead.kind === 'avatar') {\n return (\n <Avatar src={lead.src} alt={lead.alt ?? ''} sx={{ width: 32, height: 32, fontSize: '0.85rem' }}>\n {lead.fallback}\n </Avatar>\n );\n }\n return (\n <Box sx={{ display: 'flex', width: 32, justifyContent: 'center', flexShrink: 0, color: 'text.secondary' }}>\n {lead.node}\n </Box>\n );\n}\n\n/** Compute the ghost-completion remainder of the top result for `query`. */\nexport function ghostRemainder(topPrimary: string | undefined, query: string): string {\n if (!query || !topPrimary) return '';\n return topPrimary.toLowerCase().startsWith(query.toLowerCase()) && topPrimary.length > query.length\n ? topPrimary.slice(query.length)\n : '';\n}\n\n/** The search input row: leading icon, ghost overlay, the field, and a spinner. */\nexport function PaletteInput(props: {\n value: string;\n ghost: string;\n isLoading: boolean;\n autoFocus: boolean;\n placeholder: string;\n inputAriaLabel?: string;\n hasQuery: boolean;\n onChange: (v: string) => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n}): React.JSX.Element {\n return (\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.25, px: 2, py: 1.5 }}>\n <SearchIcon fontSize=\"small\" sx={{ color: 'text.secondary', flexShrink: 0 }} />\n <Box sx={{ position: 'relative', flex: 1, minWidth: 0 }}>\n {props.ghost && (\n <Box\n aria-hidden\n sx={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n pointerEvents: 'none',\n whiteSpace: 'pre',\n overflow: 'hidden',\n fontSize: '1rem',\n }}\n >\n <Box component=\"span\" sx={{ visibility: 'hidden' }}>\n {props.value}\n </Box>\n <Box component=\"span\" sx={{ color: 'text.secondary', opacity: 0.6 }}>\n {props.ghost}\n </Box>\n </Box>\n )}\n <InputBase\n fullWidth\n autoFocus={props.autoFocus}\n value={props.value}\n onChange={(e) => props.onChange(e.target.value)}\n onKeyDown={props.onKeyDown}\n placeholder={props.placeholder}\n inputProps={{\n role: 'combobox',\n 'aria-expanded': props.hasQuery,\n 'aria-label': props.inputAriaLabel,\n 'aria-autocomplete': 'list',\n }}\n sx={{ fontSize: '1rem' }}\n />\n </Box>\n {props.isLoading && <CircularProgress size={18} sx={{ flexShrink: 0 }} />}\n </Box>\n );\n}\n\n/** The toggleable filter-chip row. */\nexport function FilterChips(props: {\n filters: SearchFilterChip[];\n activeFilterIds: string[];\n onToggleFilter?: (id: string) => void;\n}): React.JSX.Element {\n return (\n <Box\n data-testid=\"search-palette-filters\"\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 1,\n px: 2,\n pb: 1.5,\n borderBottom: (theme) => `1px solid ${theme.palette.divider}`,\n }}\n >\n {props.filters.map((chip) => {\n const active = props.activeFilterIds.includes(chip.id);\n return (\n <Chip\n key={chip.id}\n label={chip.label}\n icon={chip.icon as React.ReactElement | undefined}\n onClick={() => props.onToggleFilter?.(chip.id)}\n variant={active ? 'filled' : 'outlined'}\n color={active ? 'primary' : 'default'}\n size=\"small\"\n aria-pressed={active}\n data-testid={`search-palette-filter-${chip.id}`}\n sx={{ borderRadius: 2 }}\n />\n );\n })}\n </Box>\n );\n}\n\n/** One rich result row. */\nexport function ResultRow(props: {\n index: number;\n active: boolean;\n query: string;\n lead: SearchResultLead | undefined;\n primary: string;\n secondary: string | undefined;\n trailing: React.ReactNode | undefined;\n onSelect: () => void;\n onHover: () => void;\n}): React.JSX.Element {\n return (\n <ListItemButton\n role=\"option\"\n aria-selected={props.active}\n selected={props.active}\n onMouseEnter={props.onHover}\n onMouseDown={(e) => e.preventDefault()}\n onClick={props.onSelect}\n data-testid={`search-palette-result-${props.index}`}\n sx={{ gap: 1.25, px: 2, py: 1 }}\n >\n <Lead lead={props.lead} />\n <Box sx={{ minWidth: 0, flex: 1 }}>\n <Typography variant=\"body2\" noWrap sx={{ color: 'text.primary' }}>\n {highlight(props.primary, props.query)}\n </Typography>\n {props.secondary && (\n <Typography variant=\"caption\" noWrap sx={{ display: 'block', color: 'text.secondary' }}>\n {props.secondary}\n </Typography>\n )}\n </Box>\n {props.trailing != null && (\n <Box sx={{ flexShrink: 0, color: 'text.secondary', fontSize: '0.75rem' }}>{props.trailing}</Box>\n )}\n </ListItemButton>\n );\n}\n\n/** The \"see all results — Press ENTER\" footer row. */\nexport function SubmitAllFooter(props: {\n label: string;\n selected: boolean;\n onSelect: () => void;\n onHover: () => void;\n}): React.JSX.Element {\n return (\n <ListItemButton\n selected={props.selected}\n onMouseEnter={props.onHover}\n onMouseDown={(e) => e.preventDefault()}\n onClick={props.onSelect}\n data-testid=\"search-palette-submit-all\"\n sx={{ gap: 1.25, px: 2, py: 1, borderTop: (theme) => `1px solid ${theme.palette.divider}` }}\n >\n <SearchIcon fontSize=\"small\" sx={{ color: 'text.secondary', flexShrink: 0 }} />\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 0 }} noWrap>\n {props.label}\n </Typography>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, color: 'text.secondary' }}>\n <Typography variant=\"caption\">Enter</Typography>\n <KeyboardReturnIcon sx={{ fontSize: 14 }} />\n </Box>\n </ListItemButton>\n );\n}\n\n/** Resolved view state shared by the body renderer. */\nexport interface BodyView<T> {\n hasQuery: boolean;\n emptyQueryContent: React.ReactNode;\n visible: T[];\n isLoading: boolean;\n noResultsLabel: string;\n showFooter: boolean;\n footerIndex: number;\n activeIndex: number;\n trimmed: string;\n submitAllLabel: (q: string) => string;\n getKey: (item: T) => string;\n getPrimary: (item: T) => string;\n getSecondary?: (item: T) => string | undefined;\n getLead?: (item: T) => SearchResultLead | undefined;\n getTrailing?: (item: T) => React.ReactNode | undefined;\n onSelect: (item: T) => void;\n onSubmitAll?: (query: string) => void;\n setActiveIndex: (i: number) => void;\n}\n\n/** The results list (or the empty-query slot / no-results message) + footer. */\nexport function PaletteBody<T>(view: BodyView<T>): React.JSX.Element | null {\n if (!view.hasQuery) {\n return view.emptyQueryContent ? (\n <Box data-testid=\"search-palette-empty-query\">{view.emptyQueryContent}</Box>\n ) : null;\n }\n const empty = view.visible.length === 0;\n return (\n <>\n {!empty && (\n <List dense disablePadding role=\"listbox\" data-testid=\"search-palette-results\">\n {view.visible.map((item, index) => (\n <ResultRow\n key={view.getKey(item)}\n index={index}\n active={index === view.activeIndex}\n query={view.trimmed}\n lead={view.getLead?.(item)}\n primary={view.getPrimary(item)}\n secondary={view.getSecondary?.(item)}\n trailing={view.getTrailing?.(item)}\n onSelect={() => view.onSelect(item)}\n onHover={() => view.setActiveIndex(index)}\n />\n ))}\n </List>\n )}\n {empty && !view.isLoading && (\n <Box sx={{ px: 2, py: 1.5 }} data-testid=\"search-palette-no-results\">\n <Typography variant=\"body2\" color=\"text.secondary\">\n {view.noResultsLabel}\n </Typography>\n </Box>\n )}\n {view.showFooter && (\n <SubmitAllFooter\n label={view.submitAllLabel(view.trimmed)}\n selected={view.activeIndex === view.footerIndex}\n onSelect={() => view.onSubmitAll?.(view.trimmed)}\n onHover={() => view.setActiveIndex(view.footerIndex)}\n />\n )}\n </>\n );\n}\n\n/**\n * The palette card: a flex column capped at the height its container allows, so\n * the input and the filter chips stay pinned while only the results scroll and a\n * long result set can never push the palette past the bottom of the viewport.\n */\nexport const PALETTE_CARD_SX = {\n width: '100%',\n maxWidth: 640,\n maxHeight: '100%',\n borderRadius: 2,\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n} as const;\n\n/** The scrolling region of the card — the results, and nothing above them. */\nexport function PaletteScrollArea({ children }: { children: React.ReactNode }): React.JSX.Element {\n return (\n <Box sx={{ minHeight: 0, overflowY: 'auto', overscrollBehavior: 'contain' }}>{children}</Box>\n );\n}\n\nexport { Paper };\n\n/** Context for the keydown handler builder. */\nexport interface KeyCtx<T> {\n rowCount: number;\n activeIndex: number;\n footerIndex: number;\n visible: T[];\n hasQuery: boolean;\n ghost: string;\n top: T | undefined;\n trimmed: string;\n getPrimary: (item: T) => string;\n onChange: (v: string) => void;\n onSelect: (item: T) => void;\n onSubmitAll?: (query: string) => void;\n onClose?: () => void;\n setActiveIndex: React.Dispatch<React.SetStateAction<number>>;\n}\n\n/** Build the input's keydown handler (nav / select / ghost-accept / close). */\nexport function createKeyDownHandler<T>(ctx: KeyCtx<T>): (e: React.KeyboardEvent) => void {\n const move = (delta: number): void =>\n ctx.setActiveIndex((i) => Math.min(Math.max(i + delta, 0), Math.max(ctx.rowCount - 1, 0)));\n const commit = (): void => {\n if (!ctx.hasQuery) return;\n if (ctx.activeIndex === ctx.footerIndex) {\n ctx.onSubmitAll?.(ctx.trimmed);\n return;\n }\n const item = ctx.visible[ctx.activeIndex];\n if (item) ctx.onSelect(item);\n };\n // A lookup keeps the handler itself flat (low complexity). Tab/→ is special —\n // it must only swallow the event when there is a ghost to accept, otherwise\n // Tab keeps its native focus behavior.\n const actions: Record<string, (() => void) | undefined> = {\n ArrowDown: () => move(1),\n ArrowUp: () => move(-1),\n Enter: commit,\n Escape: () => ctx.onClose?.(),\n };\n return (e) => {\n if ((e.key === 'Tab' || e.key === 'ArrowRight') && ctx.ghost && ctx.top) {\n e.preventDefault();\n ctx.onChange(ctx.getPrimary(ctx.top));\n return;\n }\n const action = actions[e.key];\n if (!action) return;\n e.preventDefault();\n action();\n };\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/form/SearchPalette/SearchPalette.tsx","../../src/components/form/SearchPalette/SearchPalette.parts.tsx"],"sourcesContent":["import React from 'react';\n\nimport {\n createKeyDownHandler,\n FilterChips,\n ghostRemainder,\n PALETTE_CARD_SX,\n PaletteBody,\n PaletteInput,\n PaletteScrollArea,\n Paper,\n} from './SearchPalette.parts';\nimport type { SearchPaletteProps } from './SearchPalette.types';\n\n/**\n * A Gmail-style search palette (FUT-168): a single card with a search input,\n * a toggleable filter-chip row, a compact list of rich result rows (leading\n * image/avatar/icon + primary/secondary + trailing), and a \"see all results\"\n * footer. Fully app-agnostic — every label, filter, and row field is supplied\n * by the host through accessors, so it drops into any surface.\n *\n * Keyboard: ↑/↓ move the active row across the results and the footer, Enter\n * selects it (row → `onSelect`, footer → `onSubmitAll`), Esc calls `onClose`,\n * and Tab/→ accepts the inline ghost completion of the top result.\n *\n * Presentational pieces + the keydown builder live in `SearchPalette.parts`.\n */\n\nconst DEFAULT_MAX_ITEMS = 5;\n\n/** Non-string option defaults (numbers / flags / arrays). */\nfunction resolveOpts<T>(props: SearchPaletteProps<T>): {\n maxItems: number;\n filters: NonNullable<SearchPaletteProps<T>['filters']>;\n activeFilterIds: string[];\n isLoading: boolean;\n showGhostText: boolean;\n autoFocus: boolean;\n} {\n return {\n maxItems: props.maxItems ?? DEFAULT_MAX_ITEMS,\n filters: props.filters ?? [],\n activeFilterIds: props.activeFilterIds ?? [],\n isLoading: Boolean(props.isLoading),\n showGhostText: props.showGhostText ?? true,\n autoFocus: props.autoFocus ?? true,\n };\n}\n\n/** String / label defaults. */\nfunction resolveText<T>(props: SearchPaletteProps<T>): {\n submitAllLabel: (q: string) => string;\n submitKeyLabel: string;\n noResultsLabel: string;\n placeholder: string;\n dataTestId: string;\n} {\n return {\n submitAllLabel: props.submitAllLabel,\n submitKeyLabel: props.submitKeyLabel,\n noResultsLabel: props.noResultsLabel,\n placeholder: props.placeholder,\n dataTestId: props.dataTestId ?? 'search-palette',\n };\n}\n\nexport function SearchPalette<T>(props: SearchPaletteProps<T>): React.JSX.Element {\n const opts = resolveOpts(props);\n const text = resolveText(props);\n\n const trimmed = props.value.trim();\n const hasQuery = trimmed.length > 0;\n const visible = props.items.slice(0, opts.maxItems);\n const showFooter = Boolean(props.onSubmitAll) && hasQuery;\n const rowCount = visible.length + (showFooter ? 1 : 0);\n const footerIndex = showFooter ? visible.length : -1;\n\n const [activeIndex, setActiveIndex] = React.useState(0);\n // Keep the active row in range as results change (typing, filtering).\n React.useEffect(() => {\n setActiveIndex((i) => (rowCount === 0 ? 0 : Math.min(i, rowCount - 1)));\n }, [rowCount]);\n\n const top = visible[0];\n const ghost =\n opts.showGhostText && hasQuery && top ? ghostRemainder(props.getPrimary(top), props.value) : '';\n\n const onKeyDown = createKeyDownHandler<T>({\n rowCount,\n activeIndex,\n footerIndex,\n visible,\n hasQuery,\n ghost,\n top,\n trimmed,\n getPrimary: props.getPrimary,\n onChange: props.onChange,\n onSelect: props.onSelect,\n onSubmitAll: props.onSubmitAll,\n onClose: props.onClose,\n setActiveIndex,\n });\n\n return (\n <Paper\n elevation={8}\n className={props.className}\n data-testid={text.dataTestId}\n sx={PALETTE_CARD_SX}\n >\n <PaletteInput\n value={props.value}\n ghost={ghost}\n isLoading={opts.isLoading}\n autoFocus={opts.autoFocus}\n placeholder={text.placeholder}\n inputAriaLabel={props.inputAriaLabel}\n hasQuery={hasQuery}\n onChange={props.onChange}\n onKeyDown={onKeyDown}\n />\n {opts.filters.length > 0 && (\n <FilterChips\n filters={opts.filters}\n activeFilterIds={opts.activeFilterIds}\n onToggleFilter={props.onToggleFilter}\n />\n )}\n <PaletteScrollArea>\n <PaletteBody<T>\n hasQuery={hasQuery} emptyQueryContent={props.emptyQueryContent} visible={visible}\n isLoading={opts.isLoading} noResultsLabel={text.noResultsLabel} showFooter={showFooter}\n footerIndex={footerIndex} activeIndex={activeIndex} trimmed={trimmed}\n submitAllLabel={text.submitAllLabel} submitKeyLabel={text.submitKeyLabel}\n getKey={props.getKey} getPrimary={props.getPrimary}\n getSecondary={props.getSecondary} getLead={props.getLead} getTrailing={props.getTrailing}\n onSelect={props.onSelect} onSubmitAll={props.onSubmitAll} setActiveIndex={setActiveIndex}\n />\n </PaletteScrollArea>\n </Paper>\n );\n}\n\nSearchPalette.displayName = 'SearchPalette';\n\nexport default SearchPalette;\n","import KeyboardReturnIcon from '@mui/icons-material/KeyboardReturn';\nimport SearchIcon from '@mui/icons-material/Search';\nimport {\n Avatar,\n Box,\n Chip,\n CircularProgress,\n InputBase,\n List,\n ListItemButton,\n Paper,\n Typography,\n} from '@mui/material';\nimport React from 'react';\n\nimport type { SearchFilterChip, SearchResultLead } from './SearchPalette.types';\n\n/** Bold the first case-insensitive occurrence of `query` within `text`. */\nexport function highlight(text: string, query: string): React.ReactNode {\n const q = query.trim();\n const at = q ? text.toLowerCase().indexOf(q.toLowerCase()) : -1;\n if (at < 0) return text;\n return (\n <>\n {text.slice(0, at)}\n <Box component=\"span\" sx={{ fontWeight: 700 }}>\n {text.slice(at, at + q.length)}\n </Box>\n {text.slice(at + q.length)}\n </>\n );\n}\n\n/** The leading visual of a result row (image / avatar / icon). */\nexport function Lead({ lead }: { lead: SearchResultLead | undefined }): React.JSX.Element | null {\n if (!lead) return null;\n if (lead.kind === 'image') {\n return (\n <Box\n component=\"img\"\n src={lead.src}\n alt={lead.alt ?? ''}\n sx={{ width: 32, height: 32, borderRadius: 1, objectFit: 'cover', flexShrink: 0 }}\n />\n );\n }\n if (lead.kind === 'avatar') {\n return (\n <Avatar src={lead.src} alt={lead.alt ?? ''} sx={{ width: 32, height: 32, fontSize: '0.85rem' }}>\n {lead.fallback}\n </Avatar>\n );\n }\n return (\n <Box sx={{ display: 'flex', width: 32, justifyContent: 'center', flexShrink: 0, color: 'text.secondary' }}>\n {lead.node}\n </Box>\n );\n}\n\n/** Compute the ghost-completion remainder of the top result for `query`. */\nexport function ghostRemainder(topPrimary: string | undefined, query: string): string {\n if (!query || !topPrimary) return '';\n return topPrimary.toLowerCase().startsWith(query.toLowerCase()) && topPrimary.length > query.length\n ? topPrimary.slice(query.length)\n : '';\n}\n\n/** The search input row: leading icon, ghost overlay, the field, and a spinner. */\nexport function PaletteInput(props: {\n value: string;\n ghost: string;\n isLoading: boolean;\n autoFocus: boolean;\n placeholder: string;\n inputAriaLabel?: string;\n hasQuery: boolean;\n onChange: (v: string) => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n}): React.JSX.Element {\n return (\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.25, px: 2, py: 1.5 }}>\n <SearchIcon fontSize=\"small\" sx={{ color: 'text.secondary', flexShrink: 0 }} />\n <Box sx={{ position: 'relative', flex: 1, minWidth: 0 }}>\n {props.ghost && (\n <Box\n aria-hidden\n sx={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n pointerEvents: 'none',\n whiteSpace: 'pre',\n overflow: 'hidden',\n fontSize: '1rem',\n }}\n >\n <Box component=\"span\" sx={{ visibility: 'hidden' }}>\n {props.value}\n </Box>\n <Box component=\"span\" sx={{ color: 'text.secondary', opacity: 0.6 }}>\n {props.ghost}\n </Box>\n </Box>\n )}\n <InputBase\n fullWidth\n autoFocus={props.autoFocus}\n value={props.value}\n onChange={(e) => props.onChange(e.target.value)}\n onKeyDown={props.onKeyDown}\n placeholder={props.placeholder}\n inputProps={{\n role: 'combobox',\n 'aria-expanded': props.hasQuery,\n 'aria-label': props.inputAriaLabel,\n 'aria-autocomplete': 'list',\n }}\n sx={{ fontSize: '1rem' }}\n />\n </Box>\n {props.isLoading && <CircularProgress size={18} sx={{ flexShrink: 0 }} />}\n </Box>\n );\n}\n\n/** The toggleable filter-chip row. */\nexport function FilterChips(props: {\n filters: SearchFilterChip[];\n activeFilterIds: string[];\n onToggleFilter?: (id: string) => void;\n}): React.JSX.Element {\n return (\n <Box\n data-testid=\"search-palette-filters\"\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 1,\n px: 2,\n pb: 1.5,\n borderBottom: (theme) => `1px solid ${theme.palette.divider}`,\n }}\n >\n {props.filters.map((chip) => {\n const active = props.activeFilterIds.includes(chip.id);\n return (\n <Chip\n key={chip.id}\n label={chip.label}\n icon={chip.icon as React.ReactElement | undefined}\n onClick={() => props.onToggleFilter?.(chip.id)}\n variant={active ? 'filled' : 'outlined'}\n color={active ? 'primary' : 'default'}\n size=\"small\"\n aria-pressed={active}\n data-testid={`search-palette-filter-${chip.id}`}\n sx={{ borderRadius: 2 }}\n />\n );\n })}\n </Box>\n );\n}\n\n/** One rich result row. */\nexport function ResultRow(props: {\n index: number;\n active: boolean;\n query: string;\n lead: SearchResultLead | undefined;\n primary: string;\n secondary: string | undefined;\n trailing: React.ReactNode | undefined;\n onSelect: () => void;\n onHover: () => void;\n}): React.JSX.Element {\n return (\n <ListItemButton\n role=\"option\"\n aria-selected={props.active}\n selected={props.active}\n onMouseEnter={props.onHover}\n onMouseDown={(e) => e.preventDefault()}\n onClick={props.onSelect}\n data-testid={`search-palette-result-${props.index}`}\n sx={{ gap: 1.25, px: 2, py: 1 }}\n >\n <Lead lead={props.lead} />\n <Box sx={{ minWidth: 0, flex: 1 }}>\n <Typography variant=\"body2\" noWrap sx={{ color: 'text.primary' }}>\n {highlight(props.primary, props.query)}\n </Typography>\n {props.secondary && (\n <Typography variant=\"caption\" noWrap sx={{ display: 'block', color: 'text.secondary' }}>\n {props.secondary}\n </Typography>\n )}\n </Box>\n {props.trailing != null && (\n <Box sx={{ flexShrink: 0, color: 'text.secondary', fontSize: '0.75rem' }}>{props.trailing}</Box>\n )}\n </ListItemButton>\n );\n}\n\n/** The \"see all results — Press ENTER\" footer row. */\nexport function SubmitAllFooter(props: {\n label: string;\n keyLabel: string;\n selected: boolean;\n onSelect: () => void;\n onHover: () => void;\n}): React.JSX.Element {\n return (\n <ListItemButton\n selected={props.selected}\n onMouseEnter={props.onHover}\n onMouseDown={(e) => e.preventDefault()}\n onClick={props.onSelect}\n data-testid=\"search-palette-submit-all\"\n sx={{ gap: 1.25, px: 2, py: 1, borderTop: (theme) => `1px solid ${theme.palette.divider}` }}\n >\n <SearchIcon fontSize=\"small\" sx={{ color: 'text.secondary', flexShrink: 0 }} />\n <Typography variant=\"body2\" sx={{ flex: 1, minWidth: 0 }} noWrap>\n {props.label}\n </Typography>\n <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, color: 'text.secondary' }}>\n <Typography variant=\"caption\">{props.keyLabel}</Typography>\n <KeyboardReturnIcon sx={{ fontSize: 14 }} />\n </Box>\n </ListItemButton>\n );\n}\n\n/** Resolved view state shared by the body renderer. */\nexport interface BodyView<T> {\n hasQuery: boolean;\n emptyQueryContent: React.ReactNode;\n visible: T[];\n isLoading: boolean;\n noResultsLabel: string;\n showFooter: boolean;\n footerIndex: number;\n activeIndex: number;\n trimmed: string;\n submitAllLabel: (q: string) => string;\n submitKeyLabel: string;\n getKey: (item: T) => string;\n getPrimary: (item: T) => string;\n getSecondary?: (item: T) => string | undefined;\n getLead?: (item: T) => SearchResultLead | undefined;\n getTrailing?: (item: T) => React.ReactNode | undefined;\n onSelect: (item: T) => void;\n onSubmitAll?: (query: string) => void;\n setActiveIndex: (i: number) => void;\n}\n\n/** The results list (or the empty-query slot / no-results message) + footer. */\nexport function PaletteBody<T>(view: BodyView<T>): React.JSX.Element | null {\n if (!view.hasQuery) {\n return view.emptyQueryContent ? (\n <Box data-testid=\"search-palette-empty-query\">{view.emptyQueryContent}</Box>\n ) : null;\n }\n const empty = view.visible.length === 0;\n return (\n <>\n {!empty && (\n <List dense disablePadding role=\"listbox\" data-testid=\"search-palette-results\">\n {view.visible.map((item, index) => (\n <ResultRow\n key={view.getKey(item)}\n index={index}\n active={index === view.activeIndex}\n query={view.trimmed}\n lead={view.getLead?.(item)}\n primary={view.getPrimary(item)}\n secondary={view.getSecondary?.(item)}\n trailing={view.getTrailing?.(item)}\n onSelect={() => view.onSelect(item)}\n onHover={() => view.setActiveIndex(index)}\n />\n ))}\n </List>\n )}\n {empty && !view.isLoading && (\n <Box sx={{ px: 2, py: 1.5 }} data-testid=\"search-palette-no-results\">\n <Typography variant=\"body2\" color=\"text.secondary\">\n {view.noResultsLabel}\n </Typography>\n </Box>\n )}\n {view.showFooter && (\n <SubmitAllFooter\n label={view.submitAllLabel(view.trimmed)}\n keyLabel={view.submitKeyLabel}\n selected={view.activeIndex === view.footerIndex}\n onSelect={() => view.onSubmitAll?.(view.trimmed)}\n onHover={() => view.setActiveIndex(view.footerIndex)}\n />\n )}\n </>\n );\n}\n\n/**\n * The palette card: a flex column capped at the height its container allows, so\n * the input and the filter chips stay pinned while only the results scroll and a\n * long result set can never push the palette past the bottom of the viewport.\n */\nexport const PALETTE_CARD_SX = {\n width: '100%',\n maxWidth: 640,\n maxHeight: '100%',\n borderRadius: 2,\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n} as const;\n\n/** The scrolling region of the card — the results, and nothing above them. */\nexport function PaletteScrollArea({ children }: { children: React.ReactNode }): React.JSX.Element {\n return (\n <Box sx={{ minHeight: 0, overflowY: 'auto', overscrollBehavior: 'contain' }}>{children}</Box>\n );\n}\n\nexport { Paper };\n\n/** Context for the keydown handler builder. */\nexport interface KeyCtx<T> {\n rowCount: number;\n activeIndex: number;\n footerIndex: number;\n visible: T[];\n hasQuery: boolean;\n ghost: string;\n top: T | undefined;\n trimmed: string;\n getPrimary: (item: T) => string;\n onChange: (v: string) => void;\n onSelect: (item: T) => void;\n onSubmitAll?: (query: string) => void;\n onClose?: () => void;\n setActiveIndex: React.Dispatch<React.SetStateAction<number>>;\n}\n\n/** Build the input's keydown handler (nav / select / ghost-accept / close). */\nexport function createKeyDownHandler<T>(ctx: KeyCtx<T>): (e: React.KeyboardEvent) => void {\n const move = (delta: number): void =>\n ctx.setActiveIndex((i) => Math.min(Math.max(i + delta, 0), Math.max(ctx.rowCount - 1, 0)));\n const commit = (): void => {\n if (!ctx.hasQuery) return;\n if (ctx.activeIndex === ctx.footerIndex) {\n ctx.onSubmitAll?.(ctx.trimmed);\n return;\n }\n const item = ctx.visible[ctx.activeIndex];\n if (item) ctx.onSelect(item);\n };\n // A lookup keeps the handler itself flat (low complexity). Tab/→ is special —\n // it must only swallow the event when there is a ghost to accept, otherwise\n // Tab keeps its native focus behavior.\n const actions: Record<string, (() => void) | undefined> = {\n ArrowDown: () => move(1),\n ArrowUp: () => move(-1),\n Enter: commit,\n Escape: () => ctx.onClose?.(),\n };\n return (e) => {\n if ((e.key === 'Tab' || e.key === 'ArrowRight') && ctx.ghost && ctx.top) {\n e.preventDefault();\n ctx.onChange(ctx.getPrimary(ctx.top));\n return;\n }\n const action = actions[e.key];\n if (!action) return;\n e.preventDefault();\n action();\n };\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|