pixelize-design-library 2.4.2-beta.66 → 2.4.2-beta.68
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/CHANGELOG.md +71 -4
- package/COMPONENT-GOTCHAS.md +105 -66
- package/DESIGN-GUIDE.md +18 -6
- package/README.md +14 -11
- package/dist/Components/Accordion/AccordionProps.d.ts +10 -0
- package/dist/Components/AddressCard/AddressCardProps.d.ts +15 -0
- package/dist/Components/Apexcharts/ApexAreaChart/ApexAreaChartProps.d.ts +11 -0
- package/dist/Components/Apexcharts/ApexBarChart/ApexBarChartProps.d.ts +17 -1
- package/dist/Components/Apexcharts/ApexLineChart/ApexLineChartProps.d.ts +10 -0
- package/dist/Components/Apexcharts/ApexPieChart/ApexPieChartProps.d.ts +14 -0
- package/dist/Components/Apexcharts/ApexPolarCharts/ApexPolarChartsProps.d.ts +9 -0
- package/dist/Components/Apexcharts/ApexRadialChart/ApexRadialChartProps.d.ts +10 -0
- package/dist/Components/AppSwitcher/AppSwitcher.d.ts +1 -0
- package/dist/Components/AppSwitcher/AppSwitcher.js +1 -0
- package/dist/Components/AppSwitcher/AppSwitcherProps.d.ts +23 -0
- package/dist/Components/AppSwitcher/pixelizeSuite.d.ts +1 -0
- package/dist/Components/AppearanceSettings/AppearanceSettings.d.ts +1 -0
- package/dist/Components/AppearanceSettings/AppearanceSettings.js +1 -0
- package/dist/Components/AppearanceSettings/AppearanceSettingsProps.d.ts +11 -0
- package/dist/Components/AudioRecorder/AudioRecorder.d.ts +1 -0
- package/dist/Components/AudioRecorder/AudioRecorder.js +1 -0
- package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +6 -0
- package/dist/Components/Avatar/AvatarGroup.d.ts +1 -0
- package/dist/Components/Avatar/AvatarGroup.js +1 -0
- package/dist/Components/Avatar/AvatarGroupProps.d.ts +2 -0
- package/dist/Components/Avatar/AvatarProps.d.ts +2 -2
- package/dist/Components/Avatar/avatarColorway.d.ts +1 -0
- package/dist/Components/BagBar/BagBarProps.d.ts +8 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +1 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +1 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +6 -0
- package/dist/Components/Button/Button.js +1 -0
- package/dist/Components/Button/ButtonProps.d.ts +3 -0
- package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.d.ts +1 -0
- package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -0
- package/dist/Components/ButtonGroupIcon/ButtonGroupIconProps.d.ts +19 -0
- package/dist/Components/CalendarGrid/CalendarGrid.d.ts +1 -0
- package/dist/Components/CalendarGrid/CalendarGrid.js +1 -0
- package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +11 -0
- package/dist/Components/CalendarGrid/calendarMath.d.ts +13 -0
- package/dist/Components/Card/CardProps.d.ts +4 -0
- package/dist/Components/Card/PaymentCard/PaymentCard.d.ts +1 -0
- package/dist/Components/Card/PaymentCard/PaymentCard.js +1 -0
- package/dist/Components/Card/PaymentCard/PaymentCardProps.d.ts +19 -0
- package/dist/Components/CartItem/CartItemProps.d.ts +10 -0
- package/dist/Components/Checkbox/CheckboxProps.d.ts +1 -0
- package/dist/Components/ChoiceCard/ChoiceCard.d.ts +1 -0
- package/dist/Components/ChoiceCard/ChoiceCard.js +1 -0
- package/dist/Components/ChoiceCard/ChoiceCardGroup.d.ts +1 -0
- package/dist/Components/ChoiceCard/ChoiceCardGroup.js +1 -0
- package/dist/Components/ChoiceCard/ChoiceCardProps.d.ts +3 -0
- package/dist/Components/ConfirmDialog/ConfirmDialog.d.ts +1 -0
- package/dist/Components/ConfirmDialog/ConfirmDialog.js +1 -0
- package/dist/Components/ConfirmDialog/ConfirmDialogProps.d.ts +5 -0
- package/dist/Components/ContactForm/ContactFormProps.d.ts +13 -0
- package/dist/Components/CouponField/CouponFieldProps.d.ts +16 -0
- package/dist/Components/CustomModulesTable/CustomModulesTable.d.ts +1 -0
- package/dist/Components/CustomModulesTable/CustomModulesTable.js +1 -0
- package/dist/Components/CustomModulesTable/CustomModulesTableProps.d.ts +17 -0
- package/dist/Components/CustomModulesTable/DeleteModuleModal.d.ts +1 -0
- package/dist/Components/CustomModulesTable/DeleteModuleModal.js +1 -0
- package/dist/Components/CustomModulesTable/EditModuleModal.d.ts +1 -0
- package/dist/Components/CustomModulesTable/EditModuleModal.js +1 -0
- package/dist/Components/DatePicker/DatePickerProps.d.ts +7 -1
- package/dist/Components/DetailSections/DetailSectionsProps.d.ts +22 -0
- package/dist/Components/Drawer/DrawerProps.d.ts +1 -1
- package/dist/Components/Dropdown/DropdownProps.d.ts +1 -0
- package/dist/Components/Editor/EditorProps.d.ts +33 -0
- package/dist/Components/Editor/editorTokens.d.ts +3 -0
- package/dist/Components/EmailCards/VerifyEmailOtp/VerifyEmailOtpProps.d.ts +8 -0
- package/dist/Components/FilePreview/FilePreviewProps.d.ts +4 -0
- package/dist/Components/FileUploader/FileUploaderProps.d.ts +1 -0
- package/dist/Components/FilterBar/FilterBarProps.d.ts +2 -0
- package/dist/Components/FormDrawer/FormDrawer.d.ts +1 -0
- package/dist/Components/FormDrawer/FormDrawer.js +1 -0
- package/dist/Components/FormDrawer/FormDrawerProps.d.ts +3 -0
- package/dist/Components/FormField/FormField.d.ts +1 -0
- package/dist/Components/FormField/FormField.js +1 -0
- package/dist/Components/FormField/FormFieldProps.d.ts +4 -0
- package/dist/Components/Header/Header.d.ts +1 -0
- package/dist/Components/Header/Header.js +1 -0
- package/dist/Components/Header/HeaderActions.d.ts +16 -0
- package/dist/Components/Header/HeaderActions.js +1 -0
- package/dist/Components/Header/HeaderProps.d.ts +22 -0
- package/dist/Components/Header/components/desktopMenu.d.ts +1 -0
- package/dist/Components/Header/components/desktopMenu.js +1 -0
- package/dist/Components/Header/components/mobileMenu.d.ts +1 -0
- package/dist/Components/Header/components/mobileMenu.js +1 -0
- package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.d.ts +1 -1
- package/dist/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
- package/dist/Components/KanbanBoard/KanbanBoardProps.d.ts +11 -0
- package/dist/Components/Kbd/Kbd.d.ts +1 -0
- package/dist/Components/Kbd/Kbd.js +1 -0
- package/dist/Components/Kbd/KbdProps.d.ts +4 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +1 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.js +1 -0
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +1 -0
- package/dist/Components/LimitReachedModal/LimitReachedModal.d.ts +1 -0
- package/dist/Components/LimitReachedModal/LimitReachedModal.js +1 -0
- package/dist/Components/LimitReachedModal/LimitReachedModalProps.d.ts +7 -0
- package/dist/Components/Loading/LoadingProps.d.ts +2 -0
- package/dist/Components/MediaGallery/MediaGalleryProps.d.ts +7 -1
- package/dist/Components/MediaGallery/MediaLightboxProps.d.ts +5 -0
- package/dist/Components/MediaViewer/components/MediaAudioPlayer.d.ts +1 -0
- package/dist/Components/MediaViewer/components/MediaAudioPlayer.js +1 -0
- package/dist/Components/MediaViewer/components/MediaDocumentStage.d.ts +1 -0
- package/dist/Components/MediaViewer/components/MediaDocumentStage.js +1 -0
- package/dist/Components/MediaViewer/components/MediaImageViewer.d.ts +1 -0
- package/dist/Components/MediaViewer/components/MediaImageViewer.js +1 -0
- package/dist/Components/MediaViewer/components/MediaVideoPlayer.d.ts +1 -0
- package/dist/Components/MediaViewer/components/MediaVideoPlayer.js +1 -0
- package/dist/Components/MediaViewer/components/MediaVideoPlayerProps.d.ts +1 -0
- package/dist/Components/MediaViewer/hooks/useFileViewer.d.ts +1 -0
- package/dist/Components/MediaViewer/mediaItems.d.ts +25 -0
- package/dist/Components/MediaViewer/mediaItems.js +5 -0
- package/dist/Components/Modal/Modal.d.ts +3 -0
- package/dist/Components/Modal/Modal.js +3 -0
- package/dist/Components/Modal/ModalProps.d.ts +6 -1
- package/dist/Components/MoreItems/MoreItemsProps.d.ts +2 -0
- package/dist/Components/MultiSelect/MultiSelect.d.ts +1 -0
- package/dist/Components/MultiSelect/MultiSelect.js +1 -0
- package/dist/Components/MultiSelect/MultiSelectProps.d.ts +13 -0
- package/dist/Components/NavLauncher/NavLauncher.js +3 -0
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +58 -0
- package/dist/Components/NavLauncher/engine/engine.d.ts +19 -0
- package/dist/Components/NavLauncher/launcherMotion.d.ts +19 -0
- package/dist/Components/NavLauncher/launcherMotion.js +15 -0
- package/dist/Components/NavLauncher/match.d.ts +34 -0
- package/dist/Components/NavigationBar/NavigationBar.d.ts +1 -0
- package/dist/Components/NavigationBar/NavigationBar.js +1 -0
- package/dist/Components/NavigationBar/NavigationBarProps.d.ts +15 -0
- package/dist/Components/NoteTextArea/NoteTextAreaProps.d.ts +2 -1
- package/dist/Components/NotificationBell/NotificationBellProps.d.ts +27 -0
- package/dist/Components/NotificationBell/notificationHelpers.d.ts +13 -0
- package/dist/Components/NumberInput/NumberInputProps.d.ts +1 -1
- package/dist/Components/OffersList/OffersListProps.d.ts +12 -1
- package/dist/Components/OptionChip/OptionChipProps.d.ts +5 -0
- package/dist/Components/OrderStatusTimeline/OrderStatusTimelineProps.d.ts +10 -0
- package/dist/Components/OrderSummary/OrderSummaryProps.d.ts +10 -0
- package/dist/Components/OrgSwitcher/OrgSwitcher.d.ts +1 -0
- package/dist/Components/OrgSwitcher/OrgSwitcher.js +11 -3
- package/dist/Components/OrgSwitcher/OrgSwitcherProps.d.ts +1 -0
- package/dist/Components/OrganizationDetails/OrganizationDetailsProps.d.ts +52 -0
- package/dist/Components/PageHeader/PageHeaderProps.d.ts +1 -0
- package/dist/Components/Pagination/PaginationProps.d.ts +4 -0
- package/dist/Components/PaymentOption/PaymentOptionProps.d.ts +9 -0
- package/dist/Components/PincodeChecker/PincodeCheckerProps.d.ts +13 -0
- package/dist/Components/PlanPill/PlanPill.d.ts +1 -0
- package/dist/Components/PlanPill/PlanPill.js +1 -0
- package/dist/Components/PlanPill/PlanPillProps.d.ts +1 -0
- package/dist/Components/PriceBlock/PriceBlockProps.d.ts +6 -0
- package/dist/Components/ProductBadges/ProductBadgesProps.d.ts +3 -0
- package/dist/Components/ProductCard/ProductCardProps.d.ts +10 -0
- package/dist/Components/ProductCard/QuickAddSheetProps.d.ts +8 -0
- package/dist/Components/ProductDetails/ProductDetailsProps.d.ts +25 -1
- package/dist/Components/ProfileCard/ProfileCardProps.d.ts +1 -1
- package/dist/Components/QuantityStepper/QuantityStepperProps.d.ts +9 -0
- package/dist/Components/QueryState/QueryStateProps.d.ts +2 -0
- package/dist/Components/RadioButton/RadioButtonProps.d.ts +1 -1
- package/dist/Components/Rating/RatingProps.d.ts +2 -0
- package/dist/Components/Reorder/ReorderProps.d.ts +6 -0
- package/dist/Components/ReviewList/ReviewListProps.d.ts +21 -0
- package/dist/Components/ReviewSummary/ReviewSummaryProps.d.ts +8 -1
- package/dist/Components/RolesPermission/RolesPermissionProps.d.ts +59 -1
- package/dist/Components/SearchSelect/SearchSelect.js +17 -5
- package/dist/Components/SearchSelect/SearchSelectProps.d.ts +12 -0
- package/dist/Components/SectionRail/SectionRail.d.ts +1 -0
- package/dist/Components/SectionRail/SectionRail.js +1 -0
- package/dist/Components/SectionRail/SectionRailProps.d.ts +4 -0
- package/dist/Components/SegmentedControl/SegmentedControlProps.d.ts +4 -0
- package/dist/Components/Select/Select.styles.js +25 -0
- package/dist/Components/Select/SelectNative.d.ts +10 -0
- package/dist/Components/Select/SelectNative.js +11 -4
- package/dist/Components/Select/SelectPopup.js +45 -14
- package/dist/Components/Select/SelectProps.d.ts +46 -2
- package/dist/Components/Select/selectKeyboardRing.d.ts +2 -0
- package/dist/Components/Select/selectKeyboardRing.js +7 -0
- package/dist/Components/Select/selectRowInk.d.ts +14 -0
- package/dist/Components/Select/selectRowInk.js +41 -0
- package/dist/Components/SelectSearch/SelectSearch.js +8 -2
- package/dist/Components/SelectSearch/SelectSearchProps.d.ts +9 -0
- package/dist/Components/SelectV2/SelectV2Props.d.ts +7 -0
- package/dist/Components/SideBar/SecondaryBar.d.ts +1 -0
- package/dist/Components/SideBar/SecondaryBar.js +1 -0
- package/dist/Components/SideBar/SideBar.d.ts +1 -0
- package/dist/Components/SideBar/SideBar.js +1 -0
- package/dist/Components/SideBar/SideBarProps.d.ts +32 -0
- package/dist/Components/SideBar/sidebarAccentColors.d.ts +2 -0
- package/dist/Components/SignInActivityTable/SignInActivityTable.d.ts +1 -0
- package/dist/Components/SignInActivityTable/SignInActivityTable.js +1 -0
- package/dist/Components/SignInActivityTable/SignInActivityTableProps.d.ts +2 -0
- package/dist/Components/SpecTable/SpecTableProps.d.ts +9 -0
- package/dist/Components/StageProgress/StageProgressProps.d.ts +2 -0
- package/dist/Components/StageProgress/StepperStage.d.ts +1 -0
- package/dist/Components/StageProgress/StepperStage.js +1 -0
- package/dist/Components/StatCard/StatCardProps.d.ts +1 -0
- package/dist/Components/StatusTag/StatusTagProps.d.ts +4 -0
- package/dist/Components/StickyBuyBar/StickyBuyBar.d.ts +1 -0
- package/dist/Components/StickyBuyBar/StickyBuyBar.js +1 -0
- package/dist/Components/StickyBuyBar/StickyBuyBarProps.d.ts +6 -0
- package/dist/Components/StockStatus/StockStatusProps.d.ts +2 -0
- package/dist/Components/Switch/SwitchProps.d.ts +4 -0
- package/dist/Components/Table/TableProps.d.ts +20 -1
- package/dist/Components/Table/components/TableBody.js +10 -1
- package/dist/Components/Table/components/rowFocusRing.d.ts +21 -0
- package/dist/Components/Table/components/rowFocusRing.js +20 -0
- package/dist/Components/Table/variants/tokens.d.ts +4 -0
- package/dist/Components/Tabs/TabsContext.d.ts +14 -0
- package/dist/Components/Tabs/TabsProps.d.ts +18 -0
- package/dist/Components/Tabs/components/CountBadge.d.ts +4 -0
- package/dist/Components/Tabs/components/TabPanels.d.ts +1 -0
- package/dist/Components/Tabs/components/TabPanels.js +1 -0
- package/dist/Components/Tabs/tabStyles.d.ts +4 -0
- package/dist/Components/Tag/TagProps.d.ts +3 -0
- package/dist/Components/TagInput/TagInput.d.ts +1 -0
- package/dist/Components/TagInput/TagInput.js +1 -0
- package/dist/Components/TagInput/TagInputProps.d.ts +2 -0
- package/dist/Components/TimePicker/TimePicker.d.ts +1 -0
- package/dist/Components/TimePicker/TimePicker.js +1 -0
- package/dist/Components/TimePicker/TimePickerProps.d.ts +2 -0
- package/dist/Components/Timeline/TimelineProps.d.ts +7 -1
- package/dist/Components/Toggle/TableToggleProps.d.ts +2 -0
- package/dist/Components/ToggleSwitch/ToggleSwitchProps.d.ts +2 -0
- package/dist/Components/ToolTip/OverflowToolTip.d.ts +1 -0
- package/dist/Components/ToolTip/OverflowToolTip.js +1 -1
- package/dist/Components/ToolTip/ToolTipProps.d.ts +1 -0
- package/dist/Components/TrustBadges/TrustBadgesProps.d.ts +4 -0
- package/dist/Components/UpgradeButton/UpgradeButton.d.ts +1 -0
- package/dist/Components/UpgradeButton/UpgradeButton.js +1 -0
- package/dist/Components/UpgradeButton/UpgradeButtonProps.d.ts +4 -1
- package/dist/Components/UserDetails/UserDetailsProps.d.ts +58 -0
- package/dist/Components/VariantSelector/VariantSelectorProps.d.ts +4 -0
- package/dist/Components/WishlistToggle/WishlistToggleProps.d.ts +4 -0
- package/dist/Components/WizardDialog/WizardDialogProps.d.ts +15 -0
- package/dist/Components/WizardDialog/wizardSteps.d.ts +17 -5
- package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.d.ts +1 -0
- package/dist/Components/WorkspaceWindow/WorkspaceTrayPreview.js +1 -1
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.d.ts +2 -0
- package/dist/Components/WorkspaceWindow/WorkspaceWindow.js +2 -1
- package/dist/Components/WorkspaceWindow/WorkspaceWindowProps.d.ts +13 -0
- package/dist/Theme/tokens/brands/index.d.ts +1 -1
- package/dist/Theme/tokens/brands/index.js +1 -1
- package/dist/Theme/tokens/builders/rowStates.d.ts +6 -0
- package/dist/Theme/tokens/builders/rowStates.js +19 -1
- package/dist/Theme/tokens/builders/scrollShadow.d.ts +1 -0
- package/dist/Theme/tokens/builders/scrollShadow.js +16 -0
- package/dist/esm/Components/AppSwitcher/AppSwitcher.js +1 -0
- package/dist/esm/Components/AppearanceSettings/AppearanceSettings.js +1 -0
- package/dist/esm/Components/AudioRecorder/AudioRecorder.js +1 -0
- package/dist/esm/Components/Avatar/AvatarGroup.js +1 -0
- package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +1 -0
- package/dist/esm/Components/Button/Button.js +1 -0
- package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -0
- package/dist/esm/Components/CalendarGrid/CalendarGrid.js +1 -0
- package/dist/esm/Components/Card/PaymentCard/PaymentCard.js +1 -0
- package/dist/esm/Components/ChoiceCard/ChoiceCard.js +1 -0
- package/dist/esm/Components/ChoiceCard/ChoiceCardGroup.js +1 -0
- package/dist/esm/Components/ConfirmDialog/ConfirmDialog.js +1 -0
- package/dist/esm/Components/CustomModulesTable/CustomModulesTable.js +1 -0
- package/dist/esm/Components/CustomModulesTable/DeleteModuleModal.js +1 -0
- package/dist/esm/Components/CustomModulesTable/EditModuleModal.js +1 -0
- package/dist/esm/Components/FormDrawer/FormDrawer.js +1 -0
- package/dist/esm/Components/FormField/FormField.js +1 -0
- package/dist/esm/Components/Header/Header.js +1 -0
- package/dist/esm/Components/Header/HeaderActions.js +1 -0
- package/dist/esm/Components/Header/components/desktopMenu.js +1 -0
- package/dist/esm/Components/Header/components/mobileMenu.js +1 -0
- package/dist/esm/Components/KanbanBoard/KanbanActions/KanbanActions.js +1 -1
- package/dist/esm/Components/Kbd/Kbd.js +1 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +1 -0
- package/dist/esm/Components/LimitReachedModal/LimitReachedModal.js +1 -0
- package/dist/esm/Components/MediaViewer/components/MediaAudioPlayer.js +1 -0
- package/dist/esm/Components/MediaViewer/components/MediaDocumentStage.js +1 -0
- package/dist/esm/Components/MediaViewer/components/MediaImageViewer.js +1 -0
- package/dist/esm/Components/MediaViewer/components/MediaVideoPlayer.js +1 -0
- package/dist/esm/Components/MediaViewer/mediaItems.js +5 -0
- package/dist/esm/Components/Modal/Modal.js +3 -0
- package/dist/esm/Components/MultiSelect/MultiSelect.js +1 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +4 -1
- package/dist/esm/Components/NavLauncher/launcherMotion.js +11 -0
- package/dist/esm/Components/NavigationBar/NavigationBar.js +1 -0
- package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +13 -5
- package/dist/esm/Components/PlanPill/PlanPill.js +1 -0
- package/dist/esm/Components/SearchSelect/SearchSelect.js +19 -7
- package/dist/esm/Components/SectionRail/SectionRail.js +1 -0
- package/dist/esm/Components/Select/Select.styles.js +26 -1
- package/dist/esm/Components/Select/SelectNative.js +12 -5
- package/dist/esm/Components/Select/SelectPopup.js +47 -16
- package/dist/esm/Components/Select/selectKeyboardRing.js +3 -0
- package/dist/esm/Components/Select/selectRowInk.js +33 -0
- package/dist/esm/Components/SelectSearch/SelectSearch.js +10 -4
- package/dist/esm/Components/SideBar/SecondaryBar.js +1 -0
- package/dist/esm/Components/SideBar/SideBar.js +1 -0
- package/dist/esm/Components/SignInActivityTable/SignInActivityTable.js +1 -0
- package/dist/esm/Components/StageProgress/StepperStage.js +1 -0
- package/dist/esm/Components/StickyBuyBar/StickyBuyBar.js +1 -0
- package/dist/esm/Components/Table/components/TableBody.js +10 -1
- package/dist/esm/Components/Table/components/rowFocusRing.js +16 -0
- package/dist/esm/Components/Tabs/components/TabPanels.js +1 -0
- package/dist/esm/Components/TagInput/TagInput.js +1 -0
- package/dist/esm/Components/TimePicker/TimePicker.js +1 -0
- package/dist/esm/Components/ToolTip/OverflowToolTip.js +1 -1
- package/dist/esm/Components/UpgradeButton/UpgradeButton.js +1 -0
- package/dist/esm/Components/WorkspaceWindow/WorkspaceTrayPreview.js +1 -1
- package/dist/esm/Components/WorkspaceWindow/WorkspaceWindow.js +2 -1
- package/dist/esm/Theme/tokens/brands/index.js +1 -1
- package/dist/esm/Theme/tokens/builders/rowStates.js +18 -1
- package/dist/esm/Theme/tokens/builders/scrollShadow.js +15 -0
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { Search as SearchIcon, X } from "lucide-react";
|
|
|
4
4
|
import { memo, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
|
|
5
5
|
import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
|
|
6
6
|
import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
|
|
7
|
-
import { reducedMotionSx } from "../../Theme/chakra/motion.js";
|
|
7
|
+
import { reducedMotionSx, usePrefersReducedMotion } from "../../Theme/chakra/motion.js";
|
|
8
8
|
import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
|
|
9
9
|
import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
|
|
10
10
|
import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
@@ -24,6 +24,7 @@ import { LauncherTabs, panelId, tabId, } from "./LauncherTabs.js";
|
|
|
24
24
|
import { filterItems, searchLauncherSections, } from "./match.js";
|
|
25
25
|
import NewTabLink from "./NewTabLink.js";
|
|
26
26
|
import PinToggle from "./PinToggle.js";
|
|
27
|
+
import { launcherSlide } from "./launcherMotion.js";
|
|
27
28
|
import { moveResult } from "./resultNavigation.js";
|
|
28
29
|
import { ScopeBar } from "./ScopeBar.js";
|
|
29
30
|
import { copiesOnEnter, countByScope, inScope, layoutSearch, scopeGroups, takeTopHit, } from "./searchLayout.js";
|
|
@@ -204,6 +205,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
|
|
|
204
205
|
const isDesktop = useMatchMedia(MD_QUERY, true);
|
|
205
206
|
const isLg = useMatchMedia(LG_QUERY, true);
|
|
206
207
|
const isWide = useMatchMedia(XL_QUERY, false);
|
|
208
|
+
const isReducedMotion = usePrefersReducedMotion();
|
|
207
209
|
const columns = isWide ? 3 : isDesktop ? 2 : 1;
|
|
208
210
|
const [innerQuery, setInnerQuery] = useState("");
|
|
209
211
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -789,6 +791,7 @@ export default function NavLauncher({ isOpen, onClose, sections: rawSections, pi
|
|
|
789
791
|
borderBottomRightRadius: { base: 0, lg: "16px" },
|
|
790
792
|
boxShadow: colors.boxShadow.default,
|
|
791
793
|
overflow: "hidden",
|
|
794
|
+
motionProps: launcherSlide(isReducedMotion),
|
|
792
795
|
onKeyDown: onContentKeyDown,
|
|
793
796
|
...{ "data-testid": testId },
|
|
794
797
|
}, children: [_jsxs(DrawerHeader, { p: 0, flexShrink: 0, children: [_jsxs(Flex, { "data-testid": "launcher-header-row", align: "center", gap: { base: "8px", md: "12px" }, minH: { base: "60px", md: "68px" }, pl: { base: "12px", md: "18px" }, pr: { base: "8px", md: "12px" }, children: [_jsx(VisuallyHidden, { as: "h2", id: titleId, children: labels.title }), _jsx(SearchField, { inputRef: inputRef, value: query, onChange: setQuery, onKeyDown: onInputKeyDown, labels: labels, placeholder: (_c = currentScope === null || currentScope === void 0 ? void 0 : currentScope.placeholder) !== null && _c !== void 0 ? _c : labels.searchPlaceholder, hint: searchHint, listboxId: listboxId, activeId: isListView && hasResults ? optionId(active) : undefined, isExpanded: isListView, hasResults: hasResults, minW: inlineScopes ? FIELD_MIN_WIDTH : 0 }), inlineScopes, _jsx(Box, { "aria-hidden": true, "data-testid": "launcher-count", display: { base: "none", sm: "block" }, flexShrink: 0, overflow: "hidden", w: countText ? "88px" : "0", opacity: countText ? 1 : 0, textAlign: "right", fontSize: "13px", whiteSpace: "nowrap", color: colors.textMuted, transition: transitionFor(["width", "opacity"]), sx: { fontVariantNumeric: "tabular-nums", ...reducedMotionSx() }, children: countText }), headerActions ? (_jsx(Flex, { align: "center", gap: "4px", flexShrink: 0, children: headerActions })) : null, _jsx(IconButton, { "aria-label": labels.close, icon: _jsx(X, { size: 18, "aria-hidden": true }), variant: "ghost", boxSize: { base: "40px", md: "44px" }, minW: { base: "40px", md: "44px" }, borderRadius: "10px", flexShrink: 0, color: colors.textMuted, onClick: onClose })] }), scopeRow ? (_jsx(Box, { "data-testid": "launcher-scope-row", pl: "7px", pr: "3px", mt: "-6px", pb: "4px", children: scopeRow })) : null] }), _jsx(DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflowY: "hidden", minH: 0, children: _jsxs(Flex, { ref: bodyRef, direction: "column", flex: 1, minH: 0, minW: 0, children: [_jsx(VisuallyHidden, { "data-testid": "launcher-live", "aria-live": "polite", "aria-atomic": "true", children: countText }), _jsx(VisuallyHidden, { "data-testid": "launcher-announce", "aria-live": "polite", "aria-atomic": "true", children: _jsx("span", { children: announcement.text }, announcement.n) }), isTabbed ? (_jsx(LauncherTabs, { tabs: tabs, selected: activeTab, onSelect: setTab, idBase: baseId, label: labels.tabsLabel, onLeaveUp: () => focusField(), onLeaveDown: () => moveFocus("first") })) : null, isLg ? (_jsx(Flex, { direction: "row", flex: 1, minH: 0, minW: 0, children: body })) : (compactBody), dock] }) }), _jsxs(DrawerFooter, { p: 0, minH: "32px", flexShrink: 0, px: "18px", py: "6px", gap: "6px 14px", flexWrap: "wrap", bg: colors.backgroundColor.subtle, fontSize: "xs-minus", color: colors.textMuted, whiteSpace: "nowrap", justifyContent: "flex-start", children: [_jsxs(Flex, { display: { base: "none", md: "flex" }, gap: "6px 14px", align: "center", wrap: "wrap", "aria-hidden": true, sx: { "@media (hover: none)": { display: "none" } }, children: [_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Flex, { as: "span", gap: "2px", children: ARROW_KEYS.map((arrow) => (_jsx(Kbd, { size: "xs", children: arrow }, arrow))) }), labels.hintMove] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "enter", size: "xs" }), _jsx(HintLabel, { text: enterLabel, reserve: enterReserve })] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", opacity: canOpenInTab ? 1 : 0.45, transition: transitionFor(["opacity"]), sx: reducedMotionSx(), children: [modKeyLabel ? (_jsxs(Flex, { as: "span", gap: "4px", children: [_jsx(Kbd, { size: "xs", children: modKeyLabel }), _jsx(Kbd, { size: "xs", children: ENTER_KEY })] })) : (_jsx(KbdShortcut, { shortcut: "mod+enter", size: "xs" })), labels.hintNewTab] }), _jsxs(Flex, { as: "span", align: "center", gap: "5px", display: { base: "none", lg: "flex" }, children: [_jsx(Kbd, { size: "xs", children: modKeyLabel !== null && modKeyLabel !== void 0 ? modKeyLabel : MOD_KEY }), labels.hintClickNewTab] }), onTogglePin ? (_jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(Kbd, { size: "xs", children: "P" }), labels.hintPin] })) : null, _jsxs(Flex, { as: "span", align: "center", gap: "5px", children: [_jsx(KbdShortcut, { shortcut: "esc", size: "xs" }), labels.hintClose] })] }), _jsx(Text, { as: "span", ml: "auto", children: labels.footerNote })] })] }) }));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EASINGS } from "@chakra-ui/react";
|
|
2
|
+
import { motionTransition } from "../../Theme/tokens/primitives/motion.js";
|
|
3
|
+
const ENTER = motionTransition("enter", "out");
|
|
4
|
+
const INSTANT = { duration: 0 };
|
|
5
|
+
const EXIT = { duration: 0.15, ease: [...EASINGS.easeInOut] };
|
|
6
|
+
export const launcherSlide = (isReducedMotion) => ({
|
|
7
|
+
variants: {
|
|
8
|
+
enter: { x: 0, y: 0, transition: isReducedMotion ? INSTANT : ENTER },
|
|
9
|
+
exit: { x: "-100%", y: 0, transition: EXIT },
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -14,6 +14,7 @@ import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
|
14
14
|
const BACKGROUND_TRANSITION = transitionFor(["background"]);
|
|
15
15
|
// Roomy enough to hover without aiming: the glyph is only 1.25rem.
|
|
16
16
|
const SIDEBAR_TOGGLE_HIT = "2.5rem";
|
|
17
|
+
/** Top bar with sidebar toggle, optional back button and title, a right-hand slot, and an avatar menu of navigation entries plus logout. */
|
|
17
18
|
export default function NavBar({ userAvathar, userName, userRole, navMenu, moreIcon, handleNavOnClick, handleLogout, logoutText, navigationBarText, sideBarToggole, onSideBarToggole, forceSideBarToggle = false, onSideBarPeek, leading, }) {
|
|
18
19
|
const theme = useCustomTheme();
|
|
19
20
|
const rowBackgrounds = listRowBackgrounds(theme.colors);
|
|
@@ -7,13 +7,16 @@ import { accentTextOnCanvas, isDarkCanvas, onFilled } from "../../Theme/tokens/b
|
|
|
7
7
|
import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
|
|
8
8
|
import { resolveHoverTint } from "../../Theme/tokens/builders/outlineRung.js";
|
|
9
9
|
import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
11
|
+
import { optionRowWeight } from "../Select/optionRowInk.js";
|
|
12
|
+
import { keyboardRingShadow } from "../Select/selectKeyboardRing.js";
|
|
13
|
+
import { selectRowDescriptionInk, selectRowInk } from "../Select/selectRowInk.js";
|
|
12
14
|
import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
|
|
13
15
|
import { useMatchMedia } from "../../Hooks/useMatchMedia.js";
|
|
14
16
|
import ToolTip from "../ToolTip/ToolTip.js";
|
|
15
17
|
import { avatarColor, orgInitial, } from "./OrgSwitcherProps.js";
|
|
16
18
|
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
19
|
+
import { OPTION_ROW_RADIUS } from "../../Theme/tokens/primitives/optionRowSizes.js";
|
|
17
20
|
const BACKGROUND_TRANSITION = transitionFor(["background"]);
|
|
18
21
|
const sizeMap = {
|
|
19
22
|
sm: { h: "32px", avatarH: "32px", avatar: "22px", font: "0.8rem", sub: "0.65rem", px: 2 },
|
|
@@ -29,12 +32,17 @@ const Avatar = ({ org, boxSize, fontSize, }) => {
|
|
|
29
32
|
const fill = avatarColor(org.name, chartMode(colors));
|
|
30
33
|
return (_jsx(Flex, { "aria-hidden": "true", "data-initials": orgInitial(org.name), _before: { content: "attr(data-initials)" }, align: "center", justify: "center", boxSize: boxSize, borderRadius: "lg", flexShrink: 0, bg: fill, color: onFilled(fill, colors.white, colors.black), fontSize: fontSize, fontWeight: 700 }));
|
|
31
34
|
};
|
|
35
|
+
/** Session-scoped organization switcher: a trigger plus searchable popover that reports the chosen org via `onChange`. */
|
|
32
36
|
const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSearchable, searchThreshold = 6, triggerVariant = "ghost", size = "md", maxWidth = "200px", menuMinWidth = "280px", placeholder = "Select organization", ariaLabel = "Switch organization", showRole = true, display = "full", }) => {
|
|
33
37
|
var _a, _b, _c;
|
|
34
38
|
const theme = useCustomTheme();
|
|
35
39
|
const isMd = useMatchMedia(MD_QUERY, true);
|
|
36
40
|
const avatarOnly = display === "avatar" || (display === "auto" && !isMd);
|
|
37
|
-
const rowBackgrounds = useMemo(() =>
|
|
41
|
+
const rowBackgrounds = useMemo(() => selectRowBackgrounds(theme.colors), [theme.colors]);
|
|
42
|
+
const keyboardRing = {
|
|
43
|
+
outline: "2px solid transparent",
|
|
44
|
+
boxShadow: keyboardRingShadow(rowBackgrounds.keyboardRing),
|
|
45
|
+
};
|
|
38
46
|
const s = sizeMap[size];
|
|
39
47
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
40
48
|
const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
|
|
@@ -105,7 +113,7 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
|
|
|
105
113
|
return (_jsxs(Popover, { isOpen: isOpen, onOpen: handlePopoverOpen, onClose: () => {
|
|
106
114
|
onClose();
|
|
107
115
|
setQuery("");
|
|
108
|
-
}, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [avatarOnly ? (_jsx(ToolTip, { label: (_c = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _c !== void 0 ? _c : placeholder, placement: "bottom", isDisabled: isOpen, children: _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: _jsx(PopoverTrigger, { children: trigger }) }) })) : (_jsx(PopoverTrigger, { children: trigger })), _jsx(PopoverContent, { minW: menuMinWidth, maxW: "calc(100vw - 32px)", w: "auto",
|
|
116
|
+
}, placement: "bottom-start", initialFocusRef: showSearch ? searchRef : firstOptionRef, isLazy: true, children: [avatarOnly ? (_jsx(ToolTip, { label: (_c = selected === null || selected === void 0 ? void 0 : selected.name) !== null && _c !== void 0 ? _c : placeholder, placement: "bottom", isDisabled: isOpen, children: _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: _jsx(PopoverTrigger, { children: trigger }) }) })) : (_jsx(PopoverTrigger, { children: trigger })), _jsx(PopoverContent, { "aria-label": ariaLabel, minW: menuMinWidth, maxW: "calc(100vw - 32px)", w: "auto",
|
|
109
117
|
// NOTE: Chakra's --popper-bg is hardcoded white via dead _dark — without an explicit bg the panel stayed white.
|
|
110
118
|
bg: theme.colors.surfaceRaised, borderRadius: "xl", overflow: "hidden", boxShadow: "float", _focus: { outline: "none", boxShadow: "float" }, children: _jsxs(PopoverBody, { p: 0, children: [showSearch && (_jsx(Box, { p: 2, borderBottom: "1px solid", borderColor: searchRule, children: _jsxs(InputGroup, { size: "sm", children: [_jsx(InputLeftElement, { pointerEvents: "none", children: _jsx(Search, { size: 15, color: theme.colors.textMuted }) }), _jsx(Input, { ref: searchRef, placeholder: "Search organizations", value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => {
|
|
111
119
|
if (e.key === "ArrowDown" && filtered.length > 0) {
|
|
@@ -118,7 +126,7 @@ const OrgSwitcher = ({ organizations, value, onChange, isLoading = false, isSear
|
|
|
118
126
|
optionRefs.current[idx] = el;
|
|
119
127
|
if (idx === 0)
|
|
120
128
|
firstOptionRef.current = el;
|
|
121
|
-
}, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "100%", textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, cursor: "pointer", bg: "transparent", transition: BACKGROUND_TRANSITION,
|
|
129
|
+
}, tabIndex: idx === activeIndex ? 0 : -1, align: "center", gap: "10px", w: "calc(100% - 0.5rem)", mx: 1, textAlign: "left", border: "none", fontFamily: "inherit", px: 3, py: 2, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg: isActive ? rowBackgrounds.selected : "transparent", transition: BACKGROUND_TRANSITION, _hover: { bg: isActive ? rowBackgrounds.selectedActive : rowBackgrounds.active }, _focus: { bg: isActive ? rowBackgrounds.selectedActive : rowBackgrounds.active }, _focusVisible: keyboardRing, onClick: () => handleSelect(org), children: [_jsx(Avatar, { org: org, boxSize: s.avatar, fontSize: s.sub }), _jsxs(Box, { flex: "1", minW: 0, children: [_jsx(Text, { fontSize: s.font, fontWeight: optionRowWeight(isActive), color: selectRowInk(theme.colors, isActive), overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.name }), showRole && org.role && (_jsx(Text, { fontSize: s.sub, color: selectRowDescriptionInk(theme.colors), overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: org.role }))] }), isActive && (_jsx(Box, { as: Check, size: 16, color: theme.colors.accentText, flexShrink: 0 }))] }, org.id));
|
|
122
130
|
}), filtered.length === 0 && (_jsx(Text, { fontSize: "sm", color: theme.colors.textMuted, textAlign: "center", py: 6, children: "No organizations found" }))] })] }) })] }));
|
|
123
131
|
};
|
|
124
132
|
export default OrgSwitcher;
|
|
@@ -13,6 +13,7 @@ const TONE_ICON = {
|
|
|
13
13
|
warning: Clock,
|
|
14
14
|
danger: AlertCircle,
|
|
15
15
|
};
|
|
16
|
+
/** Gradient pill button with plan label, optional time-left chip and a tone-based icon; always renders a `<button>`. */
|
|
16
17
|
const PlanPill = ({ label, mobileLabel, status, tone = "normal", icon, gradient, maxLabelWidth = "150px", ariaLabel, onClick, }) => {
|
|
17
18
|
const theme = useCustomTheme();
|
|
18
19
|
const c = theme.colors;
|
|
@@ -7,9 +7,11 @@ import Tag from "../Tag/Tag.js";
|
|
|
7
7
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
8
8
|
import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
|
|
9
9
|
import { ruleOn } from "../../Theme/tokens/builders/planeStep.js";
|
|
10
|
-
import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
10
|
+
import { listRowBackgrounds, selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
11
11
|
import { OPTION_ROW_RADIUS, optionRowSpec } from "../../Theme/tokens/primitives/optionRowSizes.js";
|
|
12
|
-
import { optionRowCursorSx,
|
|
12
|
+
import { optionRowCursorSx, optionRowInk, optionRowWeight } from "../Select/optionRowInk.js";
|
|
13
|
+
import { keyboardRingShadow } from "../Select/selectKeyboardRing.js";
|
|
14
|
+
import { selectRowFill, selectRowInk } from "../Select/selectRowInk.js";
|
|
13
15
|
import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
|
|
14
16
|
import FieldMessage from "../Common/FieldMessage.js";
|
|
15
17
|
import { useFieldIds } from "../Common/useFieldIds.js";
|
|
@@ -41,9 +43,9 @@ const normalizeMultiValue = (val) => {
|
|
|
41
43
|
return [];
|
|
42
44
|
};
|
|
43
45
|
const DECORATIVE_CHECKBOX_SX = { pointerEvents: "none", "& input": { visibility: "hidden" } };
|
|
44
|
-
const OptionRow = React.memo(function OptionRow({ option, index, optionId, isSelected, isActive, isMultiple, spec, colors, rowBackgrounds, checkboxSize, onSelect, onActivate, }) {
|
|
46
|
+
const OptionRow = React.memo(function OptionRow({ option, index, optionId, isSelected, isActive, showRing, isMultiple, spec, colors, rowBackgrounds, checkboxSize, onSelect, onActivate, }) {
|
|
45
47
|
const singleSelected = isSelected && !isMultiple;
|
|
46
|
-
return (_jsxs(Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, mx: spec.mx, py: spec.py, px: spec.px, minH: spec.minH, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg:
|
|
48
|
+
return (_jsxs(Box, { id: optionId, "data-index": index, role: "option", "aria-selected": isSelected, mx: spec.mx, py: spec.py, px: spec.px, minH: spec.minH, borderRadius: OPTION_ROW_RADIUS, cursor: "pointer", bg: selectRowFill(rowBackgrounds, { isSelected, isActive }), boxShadow: showRing ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, onClick: () => onSelect(option), onMouseEnter: () => onActivate(index), display: "flex", alignItems: "center", gap: spec.gap, color: selectRowInk(colors, singleSelected),
|
|
47
49
|
// `view` is consumer JSX with no size of its own; it inherited the 16px body font.
|
|
48
50
|
fontSize: spec.fontSize, fontWeight: optionRowWeight(singleSelected), children: [isMultiple && (_jsx(Checkbox, { isChecked: isSelected, sx: DECORATIVE_CHECKBOX_SX, size: checkboxSize })), option.view ? (_jsx(_Fragment, { children: option.view })) : (_jsx(Text, { flex: 1, minW: 0, isTruncated: true, children: option.label })), singleSelected && (_jsx(Box, { as: Check, size: spec.iconSize, color: colors.accentText, flexShrink: 0 }))] }));
|
|
49
51
|
});
|
|
@@ -79,7 +81,8 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
79
81
|
width: 0,
|
|
80
82
|
});
|
|
81
83
|
const theme = useCustomTheme();
|
|
82
|
-
const rowBackgrounds = useMemo(() =>
|
|
84
|
+
const rowBackgrounds = useMemo(() => selectRowBackgrounds(theme.colors), [theme.colors]);
|
|
85
|
+
const insideRowBackgrounds = useMemo(() => listRowBackgrounds(theme.colors), [theme.colors]);
|
|
83
86
|
// NOTE: inset — the trigger sits flush in the field and the menu clips overflow.
|
|
84
87
|
const insideFocusRing = {
|
|
85
88
|
outline: "2px solid transparent",
|
|
@@ -93,6 +96,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
93
96
|
const selectAllId = `${baseId}-select-all`;
|
|
94
97
|
const addNewId = `${baseId}-add-new`;
|
|
95
98
|
const [activeIndex, setActiveIndex] = useState(NO_ROW);
|
|
99
|
+
const [isKeyboardNav, setIsKeyboardNav] = useState(false);
|
|
96
100
|
const pendingFocusRestoreRef = useRef(false);
|
|
97
101
|
// NOTE: the ARIA host follows real focus; opening from a button doesn't move it.
|
|
98
102
|
const [focusedTrigger, setFocusedTrigger] = useState(null);
|
|
@@ -246,7 +250,12 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
246
250
|
setCustomSelectOpen(false);
|
|
247
251
|
setIsOpen(true);
|
|
248
252
|
};
|
|
253
|
+
const handlePointerActivate = useCallback((index) => {
|
|
254
|
+
setIsKeyboardNav(false);
|
|
255
|
+
setActiveIndex(index);
|
|
256
|
+
}, []);
|
|
249
257
|
const moveActive = (delta) => {
|
|
258
|
+
setIsKeyboardNav(true);
|
|
250
259
|
setActiveIndex((prev) => stepRow(rowLayout, prev, delta));
|
|
251
260
|
};
|
|
252
261
|
const restoreFocus = () => {
|
|
@@ -284,12 +293,14 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
284
293
|
case "Home":
|
|
285
294
|
if (isOpen && firstRow(rowLayout) !== NO_ROW && !isTextInputTarget) {
|
|
286
295
|
e.preventDefault();
|
|
296
|
+
setIsKeyboardNav(true);
|
|
287
297
|
setActiveIndex(firstRow(rowLayout));
|
|
288
298
|
}
|
|
289
299
|
break;
|
|
290
300
|
case "End":
|
|
291
301
|
if (isOpen && lastRow(rowLayout) !== NO_ROW && !isTextInputTarget) {
|
|
292
302
|
e.preventDefault();
|
|
303
|
+
setIsKeyboardNav(true);
|
|
293
304
|
setActiveIndex(lastRow(rowLayout));
|
|
294
305
|
}
|
|
295
306
|
break;
|
|
@@ -337,6 +348,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
337
348
|
useEffect(() => {
|
|
338
349
|
if (!isOpen) {
|
|
339
350
|
setActiveIndex(NO_ROW);
|
|
351
|
+
setIsKeyboardNav(false);
|
|
340
352
|
return;
|
|
341
353
|
}
|
|
342
354
|
const layout = { optionCount: filteredOptions.length, hasSelectAll: hasSelectAllRow, hasAddNew: hasAddNewRow };
|
|
@@ -613,7 +625,7 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
613
625
|
}, children: [_jsxs(Box, { minW: "4.5rem", cursor: "pointer", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 1, width: insideSelect.width, height: insideSelectBoxHeight, pl: "0.5rem", pr: "0.375rem", children: [insideLabel ? (_jsx(SelectTruncatedLabel, { label: insideLabel, maxWidth: insideSelect.width || "6rem", fontSize: s.fieldFontSize })) : (_jsx(Text, { fontSize: s.fieldFontSize, fontWeight: 500, color: theme.colors.textMuted, isTruncated: true, maxW: insideSelect.width || "6rem", children: insidePlaceholder })), _jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, color: customSelectOpen ? theme.colors.accentText : theme.colors.textMuted, transition: CHEVRON_TRANSITION, transform: customSelectOpen ? "rotate(180deg)" : "rotate(0deg)", children: _jsx(ChevronDown, { size: s.chevronSize }) })] }), customSelectOpen && (_jsx(Portal, { children: _jsx(Box, { ref: setInsideSelectMenuRef, id: insideSelectPortalId, role: "listbox", "aria-label": insidePlaceholder, onKeyDown: handleInsideMenuKeyDown, onFocus: stopFocusBubbling, onBlur: stopFocusBubbling, position: "fixed", style: { top: `${customSelectPos.top}px`, left: `${customSelectPos.left}px` }, zIndex: zIndices.popoverNested, bg: theme.colors.surfaceRaised, border: `${BORDER_WIDTHS.hairline} solid`, borderColor: theme.colors.boxborder[200], borderRadius: "xl", boxShadow: theme.shadows.lg, minW: `${customSelectPos.width}px`, maxW: "20rem", py: 1, overflow: "hidden", children: (_c = insideSelect.option) === null || _c === void 0 ? void 0 : _c.map((item) => {
|
|
614
626
|
var _a;
|
|
615
627
|
const selected = ((_a = insideSelect.value) === null || _a === void 0 ? void 0 : _a.id) === item.id;
|
|
616
|
-
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: optionRowWeight(selected), color: optionRowInk(theme.colors, selected), bg: "transparent", ...optionRowCursorSx(
|
|
628
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: spec.gap, minH: spec.minH, py: spec.py, px: spec.px, fontSize: spec.fontSize, fontWeight: optionRowWeight(selected), color: optionRowInk(theme.colors, selected), bg: "transparent", ...optionRowCursorSx(insideRowBackgrounds), cursor: "pointer", role: "option", "aria-selected": selected, tabIndex: -1, _focusVisible: insideFocusRing, onClick: (e) => {
|
|
617
629
|
e.stopPropagation();
|
|
618
630
|
handleCustomSelect(item);
|
|
619
631
|
}, children: [_jsx(Text, { isTruncated: true, children: item.label }), selected && (_jsx(Check, { size: spec.iconSize, color: theme.colors.accentText }))] }, item.id));
|
|
@@ -652,6 +664,6 @@ const SearchSelect = ({ id, label, options, onChange, onSelect, onSearch, isOpti
|
|
|
652
664
|
top: `${dropdownPos.top}px`,
|
|
653
665
|
left: `${dropdownPos.left}px`,
|
|
654
666
|
width: `${dropdownPos.width}px`,
|
|
655
|
-
}, zIndex: zIndices.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: activeIndex === SELECT_ALL_ROW ? rowBackgrounds.
|
|
667
|
+
}, zIndex: zIndices.popover, maxH: "20rem", borderWidth: 1, borderColor: theme.colors.boxborder[200], borderRadius: "xl", overflow: "hidden", bg: theme.colors.surfaceRaised, boxShadow: theme.shadows.lg, display: "flex", flexDirection: "column", children: [hasSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, bg: selectRowFill(rowBackgrounds, { isSelected: false, isActive: activeIndex === SELECT_ALL_ROW }), boxShadow: isKeyboardNav && activeIndex === SELECT_ALL_ROW ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, py: spec.py, px: spec.px, minH: spec.minH, cursor: "pointer", onClick: handleSelectAll, onMouseEnter: () => handlePointerActivate(SELECT_ALL_ROW), display: "flex", alignItems: "center", gap: spec.gap, color: selectRowInk(theme.colors, false), borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(theme.colors)}`, children: [_jsx(Checkbox, { isChecked: allFilteredSelected, sx: DECORATIVE_CHECKBOX_SX, size: s.checkboxSize }), _jsx(Text, { fontSize: spec.fontSize, children: allFilteredSelected ? "Unselect All" : "Select All" })] })), _jsx(Box, { ref: optionsListRef, flex: "1", overflowY: "auto", onScroll: handleScroll, maxH: "15rem", py: 1, borderBottom: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), children: isOptionLoading && filteredOptions.length === 0 ? (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] })) : !isOptionLoading && filteredOptions.length === 0 ? (_jsx(Text, { p: 3, fontSize: spec.fontSize, color: theme.colors.textMuted, children: "No results found" })) : (_jsxs(_Fragment, { children: [filteredOptions.map((option, index) => (_jsx(OptionRow, { option: option, index: index, optionId: getOptionId(index), isSelected: isSelected(option.id), isActive: index === activeIndex, showRing: isKeyboardNav && index === activeIndex, isMultiple: isMultiple, spec: spec, colors: theme.colors, rowBackgrounds: rowBackgrounds, checkboxSize: s.checkboxSize, onSelect: selectOption, onActivate: handlePointerActivate }, option.id))), ((pagination === null || pagination === void 0 ? void 0 : pagination.scrollLoading) || (isOptionLoading && filteredOptions.length > 0)) && (_jsxs(Box, { py: spec.py, px: spec.px, display: "flex", alignItems: "center", justifyContent: "center", gap: spec.gap, minH: spec.minH, color: theme.colors.textMuted, children: [_jsx(Spinner, { size: s.spinnerSize }), _jsx(Text, { fontSize: spec.fontSize, children: loadingText })] }))] })) }), (addNew === null || addNew === void 0 ? void 0 : addNew.enabled) && (_jsxs(Box, { id: addNewId, role: "option", "aria-selected": false, py: spec.py, px: spec.px, borderTop: `${BORDER_WIDTHS.hairline} solid`, borderColor: raisedRule(theme.colors), bg: activeIndex === ADD_NEW_ROW ? rowBackgrounds.active : theme.colors.surfaceRaised, boxShadow: isKeyboardNav && activeIndex === ADD_NEW_ROW ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined, display: "flex", alignItems: "center", gap: spec.gap, color: theme.colors.accentText, fontWeight: 500, minH: spec.minH, cursor: "pointer", onClick: addNew.onClick, _hover: { bg: rowBackgrounds.active }, children: [addNew.icon || _jsx(Plus, { size: s.addNewIconSize }), _jsx(Text, { fontSize: spec.fontSize, children: addNew.text || "Add New" })] }))] }) })), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
656
668
|
};
|
|
657
669
|
export default SearchSelect;
|
|
@@ -24,6 +24,7 @@ const useMinWidth = (minWidth) => {
|
|
|
24
24
|
}, [query]);
|
|
25
25
|
return matches;
|
|
26
26
|
};
|
|
27
|
+
/** Section navigation for a long screen: a vertical rail from `collapseBelow` up, a Select below it; dirty sections are flagged. */
|
|
27
28
|
const SectionRail = ({ sections, activeId, onSelect, "aria-label": ariaLabel, dirtyHint, collapseBelow = "md", }) => {
|
|
28
29
|
var _a;
|
|
29
30
|
const { colors } = useCustomTheme();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { placeholderInk } from "../../Theme/tokens/builders/placeholderInk.js";
|
|
2
2
|
import { CONTROL_SIZE_NAMES } from "../../Theme/tokens/primitives/controlSizes.js";
|
|
3
|
-
import {
|
|
3
|
+
import { fieldFocusVisible, focusVisibleRing } from "../../Theme/chakra/focusRing.styles.js";
|
|
4
|
+
import { FIELD_RADIUS, controlSizeStyles, fieldControlStyles, fieldHoverBorder } from "../Common/fieldStyles.js";
|
|
4
5
|
const sizeField = (size) => {
|
|
5
6
|
const { h, minH, fontSize, px } = controlSizeStyles(size);
|
|
6
7
|
return { field: { h, minH, fontSize, ps: px, pe: "2rem", py: 0 } };
|
|
@@ -28,6 +29,30 @@ export const Select = {
|
|
|
28
29
|
sizes: Object.fromEntries(CONTROL_SIZE_NAMES.map((size) => [size, sizeField(size)])),
|
|
29
30
|
variants: {
|
|
30
31
|
outline: ({ theme }) => ({ field: fieldControlStyles(theme.colors) }),
|
|
32
|
+
filled: ({ theme }) => ({
|
|
33
|
+
field: {
|
|
34
|
+
borderRadius: FIELD_RADIUS,
|
|
35
|
+
_focusVisible: { borderColor: theme.colors.primary[500], ...fieldFocusVisible(theme.colors) },
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
flushed: ({ theme }) => {
|
|
39
|
+
const { colors } = theme;
|
|
40
|
+
return {
|
|
41
|
+
field: {
|
|
42
|
+
borderColor: colors.boxborder[500],
|
|
43
|
+
_hover: { borderColor: fieldHoverBorder(colors) },
|
|
44
|
+
_focusVisible: {
|
|
45
|
+
borderColor: colors.primary[500],
|
|
46
|
+
borderBottomWidth: "2px",
|
|
47
|
+
...fieldFocusVisible(colors),
|
|
48
|
+
},
|
|
49
|
+
_invalid: { borderColor: colors.semanticText.error, borderBottomWidth: "2px", boxShadow: "none" },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
unstyled: ({ theme }) => ({
|
|
54
|
+
field: { borderRadius: FIELD_RADIUS, _focusVisible: focusVisibleRing(theme.colors) },
|
|
55
|
+
}),
|
|
31
56
|
},
|
|
32
57
|
defaultProps: {
|
|
33
58
|
variant: "outline",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useCallback } from "react";
|
|
2
|
+
import { forwardRef, useCallback, useRef, useState } from "react";
|
|
3
3
|
import { Select as ChakraSelect, FormControl } from "@chakra-ui/react";
|
|
4
4
|
import { CONTROL_SIZES, DEFAULT_CONTROL_SIZE } from "../../Theme/tokens/primitives/controlSizes.js";
|
|
5
5
|
import { TextLabel } from "../Common/FormLabel.js";
|
|
@@ -19,8 +19,13 @@ const optionGroups = (options) => {
|
|
|
19
19
|
return segments;
|
|
20
20
|
};
|
|
21
21
|
/** Native `<select>` renderer — today's `Select`, unchanged, plus optgroup/disabled from `option.group`/`isDisabled`. */
|
|
22
|
-
const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Option", size = DEFAULT_CONTROL_SIZE, variant = "outline", icon, onChange, onBlur, onFocus, errorBorderColor, isDisabled, isReadOnly, isRequired = false, value
|
|
22
|
+
const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Option", size = DEFAULT_CONTROL_SIZE, variant = "outline", icon, onChange, onBlur, onFocus, errorBorderColor, isDisabled, isReadOnly, isRequired = false, value, defaultValue, colorScheme, label, id, name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, error = false, errorMessage, helperText, options = [], width, height, formControlStyle, isInformation = false, informationMessage, selectStyle, noOptionsMessage, }, ref) {
|
|
23
23
|
const ids = useFieldIds(id, { error, errorMessage, helperText });
|
|
24
|
+
const [internalValue, setInternalValue] = useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : "");
|
|
25
|
+
const wasControlledRef = useRef(false);
|
|
26
|
+
if (value !== undefined)
|
|
27
|
+
wasControlledRef.current = true;
|
|
28
|
+
const shownValue = value !== null && value !== void 0 ? value : (wasControlledRef.current ? "" : internalValue);
|
|
24
29
|
const getOptionsList = useCallback(() => {
|
|
25
30
|
if (!options.length) {
|
|
26
31
|
return _jsx("option", { value: "", children: String(noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options") });
|
|
@@ -36,9 +41,11 @@ const SelectNative = forwardRef(function SelectNative({ placeholder = "Select Op
|
|
|
36
41
|
if (isReadOnly)
|
|
37
42
|
return;
|
|
38
43
|
const selectedOption = options.find((option) => option.id.toString() === event.target.value.toString());
|
|
39
|
-
if (selectedOption)
|
|
40
|
-
|
|
44
|
+
if (!selectedOption)
|
|
45
|
+
return;
|
|
46
|
+
setInternalValue(selectedOption.id);
|
|
47
|
+
onChange(selectedOption);
|
|
41
48
|
};
|
|
42
|
-
return (_jsxs(FormControl, { isInvalid: error, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsx(ChakraSelect, { ref: ref, ...ids.controlProps, name: name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, placeholder: placeholder, size: size, variant: variant, icon: icon, iconSize: `${CONTROL_SIZES[size].iconSize}px`, isInvalid: error, isRequired: isRequired, value:
|
|
49
|
+
return (_jsxs(FormControl, { isInvalid: error, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsx(ChakraSelect, { ref: ref, ...ids.controlProps, name: name, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, placeholder: placeholder, size: size, variant: variant, icon: icon, iconSize: `${CONTROL_SIZES[size].iconSize}px`, isInvalid: error, isRequired: isRequired, value: shownValue, onChange: handleOnChange, onBlur: onBlur, onFocus: onFocus, isDisabled: isDisabled, isReadOnly: isReadOnly, colorScheme: colorScheme, errorBorderColor: errorBorderColor, style: selectStyle, ...(height != null && height !== "" ? { minH: height, h: height } : {}), children: getOptionsList() }), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
43
50
|
});
|
|
44
51
|
export default SelectNative;
|
|
@@ -5,10 +5,13 @@ import { Check, ChevronDown, Plus, X } from "lucide-react";
|
|
|
5
5
|
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
6
6
|
import { raisedRule } from "../../Theme/tokens/builders/surfaceRaised.js";
|
|
7
7
|
import { focusGlowShadow } from "../../Theme/chakra/focusRing.styles.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
9
|
+
import { optionRowWeight } from "./optionRowInk.js";
|
|
10
|
+
import { SELECT_ROW_DISABLED_CONTENT_OPACITY, selectFilterPlaceholderInk, selectRowDescriptionInk, selectRowDisabledInk, selectRowFill, selectRowInk, } from "./selectRowInk.js";
|
|
11
|
+
import { keyboardRingShadow, NAVIGATION_KEYS } from "./selectKeyboardRing.js";
|
|
10
12
|
import { optionRowSpec } from "../../Theme/tokens/primitives/optionRowSizes.js";
|
|
11
13
|
import { scrollbarStyles } from "../../Theme/tokens/builders/scrollbar.js";
|
|
14
|
+
import { scrollShadowStylesVertical } from "../../Theme/tokens/builders/scrollShadow.js";
|
|
12
15
|
import { CONTROL_SIZES, DEFAULT_CONTROL_SIZE } from "../../Theme/tokens/primitives/controlSizes.js";
|
|
13
16
|
import { transitionFor } from "../../Theme/tokens/primitives/motion.js";
|
|
14
17
|
import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
|
|
@@ -29,11 +32,13 @@ const DEFAULT_MAX_VISIBLE_CHIPS = 6;
|
|
|
29
32
|
const VIRTUAL_OVERSCAN = 4;
|
|
30
33
|
const DECORATIVE_CHECKBOX_SX = { "& input": { visibility: "hidden" } };
|
|
31
34
|
const GROUP_HEADER_ESTIMATE_PX = 29;
|
|
32
|
-
const LIST_PADDING_PX = 4;
|
|
33
35
|
const NO_ROW = -1;
|
|
34
36
|
const SELECT_ALL_ROW = -2;
|
|
35
37
|
const CREATE_ROW = -3;
|
|
36
38
|
const FOOTER_ROW = -4;
|
|
39
|
+
const CHECK_SLOT_MIN_PX = 16;
|
|
40
|
+
const SELECT_ALL_RULE_MARGIN_PX = 4;
|
|
41
|
+
const DISABLED_CONTENT_SX = { "& > *": { opacity: SELECT_ROW_DISABLED_CONTENT_OPACITY } };
|
|
37
42
|
const truncateLabel = (label, max) => max !== undefined && label.length > max ? `${label.slice(0, max)}…` : label;
|
|
38
43
|
const asIdSet = (values) => new Set((values !== null && values !== void 0 ? values : []).map((v) => v.toString()));
|
|
39
44
|
/** Regression-guarded id scheme — tickets e2e (`e2e/tests/helpers/selectV2.ts`) resolves it verbatim. */
|
|
@@ -42,19 +47,26 @@ const parseRemToPx = (value, fallbackPx) => {
|
|
|
42
47
|
const match = /^(\d*\.?\d+)rem$/.exec(value);
|
|
43
48
|
return match ? parseFloat(match[1]) * 16 : fallbackPx;
|
|
44
49
|
};
|
|
45
|
-
const OptionRow = React.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, spec, renderOption, onSelect, onActivate, }) {
|
|
50
|
+
const OptionRow = React.memo(function OptionRow({ option, index, listboxId, isSelected, isActive, isMulti, theme, backgrounds, ink, showRing, spec, renderOption, renderOptionEnd, optionAction, checkPlacement, onSelect, onActivate, }) {
|
|
46
51
|
const isSingleSelected = isSelected && !isMulti;
|
|
47
|
-
|
|
52
|
+
const solidDisabledInk = option.isDisabled ? ink.disabled : null;
|
|
53
|
+
const checkSlotPx = Math.max(CHECK_SLOT_MIN_PX, spec.iconSize);
|
|
54
|
+
const checkSlot = (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", justifyContent: "center", flexShrink: 0, width: `${checkSlotPx}px`, children: isSingleSelected ? _jsx(Box, { as: Check, size: spec.iconSize, color: theme.colors.accentText }) : null }));
|
|
55
|
+
return (_jsxs(Box, { id: optionDomId(listboxId, option.id), "data-index": index, role: "option", "aria-selected": isSelected, "aria-disabled": option.isDisabled, onMouseEnter: () => onActivate(index), onClick: () => onSelect(option), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: option.isDisabled ? "not-allowed" : "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: optionRowWeight(isSingleSelected), color: solidDisabledInk !== null && solidDisabledInk !== void 0 ? solidDisabledInk : (isSingleSelected ? ink.selected : ink.rest), bg: selectRowFill(backgrounds, { isSelected, isActive }), boxShadow: showRing ? keyboardRingShadow(backgrounds.keyboardRing) : undefined, transition: ROW_TRANSITION, sx: option.isDisabled && !ink.disabled ? { ...reducedMotionSx(), ...DISABLED_CONTENT_SX } : reducedMotionSx(), children: [!isMulti && checkPlacement === "start" ? checkSlot : null, isMulti ? (_jsx(Checkbox, { id: `${optionDomId(listboxId, option.id)}-checkbox`, isChecked: isSelected, isDisabled: option.isDisabled, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX })) : null, renderOption ? (_jsx(Box, { flex: 1, minW: 0, children: renderOption(option, { isSelected, isActive }) })) : (_jsxs(_Fragment, { children: [option.icon ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: option.icon })) : null, _jsxs(Box, { flex: 1, minW: 0, isTruncated: true, children: [_jsx(Box, { as: "span", isTruncated: true, children: option.label }), option.description ? (_jsx(Box, { as: "span", display: "block", fontSize: spec.descriptionFontSize, color: solidDisabledInk !== null && solidDisabledInk !== void 0 ? solidDisabledInk : ink.description, isTruncated: true, children: option.description })) : null] })] })), renderOptionEnd ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: renderOptionEnd(option, { isSelected, isActive }) })) : null, optionAction ? (_jsx(Box, { as: "span", "aria-hidden": true, title: optionAction.label, onMouseDown: (event) => event.preventDefault(), onClick: (event) => {
|
|
56
|
+
event.stopPropagation();
|
|
57
|
+
optionAction.onAction(option);
|
|
58
|
+
}, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, cursor: "pointer", color: ink.description, _hover: { color: ink.selected }, children: optionAction.icon })) : null, !isMulti && checkPlacement === "end" ? checkSlot : null] }));
|
|
48
59
|
});
|
|
49
60
|
/** The unified `Select`'s popup mode (`native={false}`) — absorbs SelectV2's behaviour, plus clear/create/groups/virtualization. */
|
|
50
61
|
const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
51
62
|
var _a, _b, _c, _d, _e, _f;
|
|
52
|
-
const { options = [], id, name, placeholder = "Select Option", label, error = false, errorMessage, helperText, isDisabled = false, isReadOnly = false, isRequired = false, isInformation = false, informationMessage, size = DEFAULT_CONTROL_SIZE, isSearchable = true, width, formControlStyle, onBlur, onFocus, renderOption, renderValue, maxMenuHeight = "16rem", defaultOpen = false, isLoading = false, loadingText = "Loading...", onLoadMore, hasMore, onSearchChange, searchValue, defaultSearchValue, searchDebounceMs = 0, filterOptions, isClearable = false, onClear, noOptionsMessage, onCreate, createLabel, createWhen = "no-options", footerAction, searchFields, filterOption, isFieldLoading = false, rightIcon, virtualizeAfter, "data-testid": dataTestId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, } = props;
|
|
63
|
+
const { options = [], id, name, placeholder = "Select Option", label, error = false, errorMessage, helperText, isDisabled = false, isReadOnly = false, isRequired = false, isInformation = false, informationMessage, size = DEFAULT_CONTROL_SIZE, isSearchable = true, width, formControlStyle, onBlur, onFocus, renderOption, renderOptionEnd, optionAction, checkPlacement = "end", renderValue, maxMenuHeight = "16rem", defaultOpen = false, isLoading = false, loadingText = "Loading...", onLoadMore, hasMore, onSearchChange, searchValue, defaultSearchValue, searchDebounceMs = 0, filterOptions, isClearable = false, onClear, noOptionsMessage, onCreate, createLabel, createWhen = "no-options", footerAction, searchFields, filterOption, isFieldLoading = false, rightIcon, virtualizeAfter, "data-testid": dataTestId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, } = props;
|
|
53
64
|
const theme = useCustomTheme();
|
|
54
65
|
const { colors } = theme;
|
|
55
66
|
const { isOpen, onOpen, onClose } = useDisclosure({ defaultIsOpen: defaultOpen });
|
|
56
67
|
const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
|
|
57
68
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
69
|
+
const [isKeyboardNav, setIsKeyboardNav] = useState(false);
|
|
58
70
|
const triggerRef = useRef(null);
|
|
59
71
|
const mergedTriggerRef = useMergeRefs(triggerRef, ref);
|
|
60
72
|
const filterInputRef = useRef(null);
|
|
@@ -116,6 +128,7 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
116
128
|
if (!isSearchControlled)
|
|
117
129
|
setUncontrolledSearch("");
|
|
118
130
|
setActiveIndex(-1);
|
|
131
|
+
setIsKeyboardNav(false);
|
|
119
132
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur();
|
|
120
133
|
}, [onClose, onBlur, props.isMulti, isSearchControlled]);
|
|
121
134
|
const handleOpen = () => {
|
|
@@ -218,13 +231,14 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
218
231
|
return () => cancelAnimationFrame(frame);
|
|
219
232
|
}, [isOpen, isSearchable]);
|
|
220
233
|
const spec = useMemo(() => optionRowSpec(size), [size]);
|
|
234
|
+
const listPaddingPx = parseRemToPx(spec.mx, 6);
|
|
221
235
|
const maxMenuHeightPx = useMemo(() => parseRemToPx(maxMenuHeight, 256), [maxMenuHeight]);
|
|
222
236
|
const shouldVirtualize = !!virtualizeAfter && filteredOptions.length > virtualizeAfter;
|
|
223
237
|
const [scrollTop, setScrollTop] = useState(0);
|
|
224
238
|
// A description, an avatar or a renderOption row outgrows the spec's single-line height.
|
|
225
239
|
const [rowHeightPx, setRowHeightPx] = useState(spec.virtualRowHeightPx);
|
|
226
240
|
const [headerHeightPx, setHeaderHeightPx] = useState(GROUP_HEADER_ESTIMATE_PX);
|
|
227
|
-
const selectAllLeadPx = showSelectAllRow ? rowHeightPx : 0;
|
|
241
|
+
const selectAllLeadPx = showSelectAllRow ? rowHeightPx + parseInt(BORDER_WIDTHS.hairline, 10) + 2 * SELECT_ALL_RULE_MARGIN_PX : 0;
|
|
228
242
|
const windowTop = Math.max(0, scrollTop - selectAllLeadPx);
|
|
229
243
|
const visibleCount = Math.ceil(maxMenuHeightPx / rowHeightPx) + VIRTUAL_OVERSCAN * 2;
|
|
230
244
|
const groupedRows = useMemo(() => (hasGroups ? buildGroupedRows(filteredOptions) : []), [hasGroups, filteredOptions]);
|
|
@@ -292,8 +306,8 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
292
306
|
targetTop = groupedLayout.tops[startsGroup ? rowIndex - 1 : rowIndex];
|
|
293
307
|
targetBottom = groupedLayout.tops[rowIndex + 1];
|
|
294
308
|
}
|
|
295
|
-
targetTop += selectAllLeadPx +
|
|
296
|
-
targetBottom += selectAllLeadPx +
|
|
309
|
+
targetTop += selectAllLeadPx + listPaddingPx;
|
|
310
|
+
targetBottom += selectAllLeadPx + listPaddingPx;
|
|
297
311
|
if (targetTop < el.scrollTop) {
|
|
298
312
|
el.scrollTop = targetTop;
|
|
299
313
|
setScrollTop(targetTop);
|
|
@@ -306,7 +320,7 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
306
320
|
return;
|
|
307
321
|
}
|
|
308
322
|
(_d = (_c = el.querySelector(`[data-index="${activeIndex}"]`)) === null || _c === void 0 ? void 0 : _c.scrollIntoView) === null || _d === void 0 ? void 0 : _d.call(_c, { block: "nearest" });
|
|
309
|
-
}, [activeIndex, shouldVirtualize, maxMenuHeightPx, rowHeightPx, groupedLayout, groupedRows, selectAllLeadPx, selectAllId, createRowId, footerId]);
|
|
323
|
+
}, [activeIndex, shouldVirtualize, maxMenuHeightPx, rowHeightPx, groupedLayout, groupedRows, selectAllLeadPx, listPaddingPx, selectAllId, createRowId, footerId]);
|
|
310
324
|
const loadMoreInFlightRef = useRef(false);
|
|
311
325
|
const prevOptionsLengthRef = useRef(options.length);
|
|
312
326
|
const prevIsLoadingRef = useRef(isLoading);
|
|
@@ -392,6 +406,8 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
392
406
|
const handleListKeyDown = (event) => {
|
|
393
407
|
var _a, _b;
|
|
394
408
|
const isTextInput = ((_a = event.target) === null || _a === void 0 ? void 0 : _a.tagName) === "INPUT";
|
|
409
|
+
if (NAVIGATION_KEYS.has(event.key))
|
|
410
|
+
setIsKeyboardNav(true);
|
|
395
411
|
switch (event.key) {
|
|
396
412
|
case "ArrowDown":
|
|
397
413
|
event.preventDefault();
|
|
@@ -498,8 +514,23 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
498
514
|
: showCreateRow && (isCreateImplicit || activeIndex === CREATE_ROW)
|
|
499
515
|
? createRowId
|
|
500
516
|
: undefined;
|
|
501
|
-
const listScrollStyles = useMemo(() =>
|
|
502
|
-
|
|
517
|
+
const listScrollStyles = useMemo(() => ({
|
|
518
|
+
...scrollbarStyles(colors, "overlay"),
|
|
519
|
+
...scrollShadowStylesVertical(colors.surfaceRaised),
|
|
520
|
+
}), [colors]);
|
|
521
|
+
const rowBackgrounds = useMemo(() => selectRowBackgrounds(colors), [colors]);
|
|
522
|
+
const rowInks = useMemo(() => ({
|
|
523
|
+
rest: selectRowInk(colors, false),
|
|
524
|
+
selected: selectRowInk(colors, true),
|
|
525
|
+
description: selectRowDescriptionInk(colors),
|
|
526
|
+
disabled: selectRowDisabledInk(colors),
|
|
527
|
+
}), [colors]);
|
|
528
|
+
const filterPlaceholderInk = useMemo(() => selectFilterPlaceholderInk(colors), [colors]);
|
|
529
|
+
const handlePointerActivate = useCallback((index) => {
|
|
530
|
+
setIsKeyboardNav(false);
|
|
531
|
+
setActiveIndex(index);
|
|
532
|
+
}, []);
|
|
533
|
+
const ringShadowFor = (isActive) => isActive && isKeyboardNav ? keyboardRingShadow(rowBackgrounds.keyboardRing) : undefined;
|
|
503
534
|
const showChips = !!props.isMulti && !renderValue && selectedOptions.length > 0;
|
|
504
535
|
const chipSize = size === "xs" ? "xs" : "sm";
|
|
505
536
|
const maxVisibleChips = props.isMulti ? ((_a = props.maxVisibleChips) !== null && _a !== void 0 ? _a : DEFAULT_MAX_VISIBLE_CHIPS) : 0;
|
|
@@ -542,14 +573,14 @@ const SelectPopup = forwardRef(function SelectPopup(props, ref) {
|
|
|
542
573
|
return (_jsxs(FormControl, { isInvalid: error, isDisabled: isDisabled, isReadOnly: isReadOnly, style: formControlStyle, width: width, children: [label && (_jsx(TextLabel, { label: label, id: ids.inputId, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isOpen: isOpen, onClose: () => resetAndClose(), placement: "bottom-start", autoFocus: false, returnFocusOnClose: false, gutter: 4, children: [_jsx(PopoverAnchor, { children: _jsxs(Box, { as: "div", ref: mergedTriggerRef, ...ids.controlProps, role: "combobox", tabIndex: isDisabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-owns": `${listboxId} ${filterInputId}`, "aria-activedescendant": activeDescendantId, "aria-disabled": isDisabled || undefined, "aria-busy": isFieldLoading || undefined, "data-readonly": isReadOnly || undefined, "aria-required": isRequired || undefined, "aria-label": triggerAriaLabel(), "aria-labelledby": ariaLabelledBy, onClick: handleOpen, onKeyDown: handleTriggerKeyDown, display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", textAlign: "left", cursor: isDisabled ? "not-allowed" : isReadOnly ? "default" : "pointer", "data-testid": dataTestId, sx: triggerSx, children: [_jsx(Box, { as: "span", display: "inline-flex", flexWrap: showChips ? "wrap" : "nowrap", alignItems: "center", gap: showChips ? 1 : 1.5, minW: 0, flex: 1, isTruncated: !showChips, children: showChips ? (_jsxs(_Fragment, { children: [visibleChipOptions.map((option) => (_jsx(Tag, { label: truncateLabel(String(option.label), chipMaxLength), size: chipSize, variant: "subtle", icon: canRemoveChips ? X : undefined, onIconClick: canRemoveChips ? () => handleSelect(option) : undefined }, option.id))), overflowChipCount > 0 && (_jsx(Tag, { label: `+${overflowChipCount}`, size: chipSize, variant: "subtle", colorScheme: "gray" }))] })) : (_jsxs(_Fragment, { children: [triggerIcon ? (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, children: triggerIcon })) : null, _jsx(Box, { as: "span", isTruncated: true, children: renderValue ? renderValue(selectedOptions) : defaultTriggerContent() })] })) }), showClear && (_jsx(Box, { as: "button", type: "button", "aria-label": "Clear selection", onClick: handleClear, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: _jsx(X, { size: CONTROL_SIZES[size].iconSize }) })), isFieldLoading ? (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, children: _jsx(Spinner, { size: "xs", label: "" }) })) : rightIcon ? ("onClick" in rightIcon && isInteractive ? (_jsx(Box, { as: "button", type: "button", "aria-label": rightIcon.ariaLabel, onClick: (event) => {
|
|
543
574
|
event.stopPropagation();
|
|
544
575
|
rightIcon.onClick();
|
|
545
|
-
}, onKeyDown: (event) => event.stopPropagation(), display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: rightIcon.icon })) : (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, color: colors.textMuted, children: rightIcon.icon }))) : (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: reducedMotionSx(), children: _jsx(ChevronDown, { size: CONTROL_SIZES[size].iconSize }) }))] }) }), _jsx(Portal, { children: _jsxs(PopoverContent, { "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [_jsx(Box, { p: spec.mx, borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, display: isSearchable ? undefined : "none", children: _jsx(Input, { ref: filterInputRef, id: filterInputId, size: size, px: spec.px, placeholder: "Type to filter...", value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), _jsxs(Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_e = label !== null && label !== void 0 ? label : ariaLabel) !== null && _e !== void 0 ? _e : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py:
|
|
576
|
+
}, onKeyDown: (event) => event.stopPropagation(), display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, ml: 2, color: colors.textMuted, _hover: { color: colors.text[700] }, children: rightIcon.icon })) : (_jsx(Box, { as: "span", "aria-hidden": true, display: "inline-flex", flexShrink: 0, ml: 2, color: colors.textMuted, children: rightIcon.icon }))) : (_jsx(Box, { as: "span", display: "inline-flex", flexShrink: 0, ml: 2, transition: CHEVRON_TRANSITION, transform: isOpen ? "rotate(180deg)" : "rotate(0deg)", color: colors.textMuted, sx: reducedMotionSx(), children: _jsx(ChevronDown, { size: CONTROL_SIZES[size].iconSize }) }))] }) }), _jsx(Portal, { children: _jsxs(PopoverContent, { "aria-label": (_d = label !== null && label !== void 0 ? label : ariaLabel) !== null && _d !== void 0 ? _d : placeholder, width: "max-content", minWidth: triggerWidth ? `${triggerWidth}px` : undefined, maxWidth: "min(24rem, calc(100vw - 2rem))", borderRadius: "xl", boxShadow: theme.shadows.lg, overflow: "hidden", onKeyDown: handleListKeyDown, children: [_jsx(Box, { p: spec.mx, borderBottom: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, display: isSearchable ? undefined : "none", children: _jsx(Input, { ref: filterInputRef, id: filterInputId, size: size, px: spec.px, placeholder: "Type to filter...", _placeholder: filterPlaceholderInk ? { color: filterPlaceholderInk } : undefined, value: currentSearch, onChange: handleFilterChange, "aria-activedescendant": activeDescendantId, "aria-controls": listboxId, "aria-label": "Filter options", tabIndex: isSearchable ? undefined : -1 }) }), _jsxs(Box, { id: listboxId, ref: listRef, role: "listbox", "aria-label": (_e = label !== null && label !== void 0 ? label : ariaLabel) !== null && _e !== void 0 ? _e : placeholder, "aria-multiselectable": props.isMulti || undefined, "aria-busy": isLoading || undefined, maxH: maxMenuHeight, overflowY: "auto", py: spec.mx, sx: listScrollStyles, onScroll: handleListScroll, children: [filteredOptions.length === 0 && !isLoading && !showCreateRow && (_jsx(Box, { mx: spec.mx, px: spec.px, py: spec.py, fontSize: spec.fontSize, color: colors.textMuted, children: noOptionsMessage !== null && noOptionsMessage !== void 0 ? noOptionsMessage : "No Options" })), showSelectAllRow && (_jsxs(Box, { id: selectAllId, role: "option", "aria-selected": allFilteredSelected, onClick: handleSelectAll, onMouseEnter: () => handlePointerActivate(SELECT_ALL_ROW), mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: rowInks.rest, bg: selectRowFill(rowBackgrounds, { isSelected: false, isActive: activeIndex === SELECT_ALL_ROW }), boxShadow: ringShadowFor(activeIndex === SELECT_ALL_ROW), transition: ROW_TRANSITION, sx: reducedMotionSx(), children: [_jsx(Checkbox, { isChecked: allFilteredSelected, pointerEvents: "none", tabIndex: -1, sx: DECORATIVE_CHECKBOX_SX }), allFilteredSelected ? "Unselect All" : "Select All"] })), showSelectAllRow && (_jsx(Box, { role: "presentation", mx: spec.mx, my: `${SELECT_ALL_RULE_MARGIN_PX}px`, height: BORDER_WIDTHS.hairline, bg: raisedRule(colors) })), hasGroups
|
|
546
577
|
? (_jsxs(_Fragment, { children: [topPadPx > 0 && _jsx(Box, { height: `${topPadPx}px` }), renderedGroupedRows.map((row) => {
|
|
547
578
|
var _a;
|
|
548
|
-
return row.kind === "header" ? (_jsx(Box, { "data-group-header": shouldVirtualize ? "" : undefined, mx: spec.mx, px: spec.px, pt: 2, pb: 1, fontSize: "xs-minus", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.03em", color: colors.textMuted, children: row.group }, row.key)) : (_jsx(OptionRow, { option: row.option, index: row.index, listboxId: listboxId, isSelected: selectedIds.has(row.option.id.toString()), isActive: row.index === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate:
|
|
579
|
+
return row.kind === "header" ? (_jsx(Box, { "data-group-header": shouldVirtualize ? "" : undefined, mx: spec.mx, px: spec.px, pt: 2, pb: 1, fontSize: "xs-minus", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.03em", color: colors.textMuted, children: row.group }, row.key)) : (_jsx(OptionRow, { option: row.option, index: row.index, listboxId: listboxId, isSelected: selectedIds.has(row.option.id.toString()), isActive: row.index === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, ink: rowInks, showRing: isKeyboardNav && row.index === activeIndex, spec: spec, renderOption: renderOption, renderOptionEnd: renderOptionEnd, optionAction: optionAction, checkPlacement: checkPlacement, onSelect: handleSelect, onActivate: handlePointerActivate }, row.key));
|
|
549
580
|
}), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] }))
|
|
550
581
|
: (_jsxs(_Fragment, { children: [topPadPx > 0 && _jsx(Box, { height: `${topPadPx}px` }), renderedOptions.map((option, i) => {
|
|
551
582
|
var _a;
|
|
552
|
-
return (_jsx(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, spec: spec, renderOption: renderOption, onSelect: handleSelect, onActivate:
|
|
553
|
-
}), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] })), showCreateRow && (_jsx(Box, { id: createRowId, role: "option", "aria-selected": false, "data-index": -1, onClick: handleCreate, mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", fontSize: spec.fontSize, fontWeight: 600, color: colors.primary[600], bg: activeDescendantId === createRowId ? rowBackgrounds.active : "transparent", children: createLabel ? createLabel(currentSearch.trim()) : `Create "${currentSearch.trim()}"` })), isLoading && (_jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: colors.textMuted, children: [_jsx(Spinner, { size: "xs" }), loadingText] })), footerAction && (_jsx(Box, { id: footerId, role: "option", "aria-selected": false, onClick: handleFooterAction, onMouseEnter: () =>
|
|
583
|
+
return (_jsx(OptionRow, { option: option, index: startIndex + i, listboxId: listboxId, isSelected: selectedIds.has(option.id.toString()), isActive: startIndex + i === activeIndex, isMulti: (_a = props.isMulti) !== null && _a !== void 0 ? _a : false, theme: theme, backgrounds: rowBackgrounds, ink: rowInks, showRing: isKeyboardNav && startIndex + i === activeIndex, spec: spec, renderOption: renderOption, renderOptionEnd: renderOptionEnd, optionAction: optionAction, checkPlacement: checkPlacement, onSelect: handleSelect, onActivate: handlePointerActivate }, option.id));
|
|
584
|
+
}), bottomPadPx > 0 && _jsx(Box, { height: `${bottomPadPx}px` })] })), showCreateRow && (_jsx(Box, { id: createRowId, role: "option", "aria-selected": false, "data-index": -1, onClick: handleCreate, mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", fontSize: spec.fontSize, fontWeight: 600, color: colors.primary[600], bg: activeDescendantId === createRowId ? rowBackgrounds.active : "transparent", boxShadow: ringShadowFor(activeDescendantId === createRowId), children: createLabel ? createLabel(currentSearch.trim()) : `Create "${currentSearch.trim()}"` })), isLoading && (_jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, color: colors.textMuted, children: [_jsx(Spinner, { size: "xs" }), loadingText] })), footerAction && (_jsx(Box, { id: footerId, role: "option", "aria-selected": false, onClick: handleFooterAction, onMouseEnter: () => handlePointerActivate(FOOTER_ROW), position: "sticky", bottom: `-${spec.mx}`, pt: 1, bg: colors.surfaceRaised, borderTop: `${BORDER_WIDTHS.hairline} solid ${raisedRule(colors)}`, children: _jsxs(Box, { mx: spec.mx, px: spec.px, py: spec.py, minH: spec.minH, borderRadius: "md", cursor: "pointer", display: "flex", alignItems: "center", gap: spec.gap, fontSize: spec.fontSize, fontWeight: 500, color: colors.accentText, bg: activeIndex === FOOTER_ROW ? rowBackgrounds.active : "transparent", boxShadow: ringShadowFor(activeIndex === FOOTER_ROW), children: [(_f = footerAction.icon) !== null && _f !== void 0 ? _f : _jsx(Plus, { size: spec.iconSize }), footerAction.label] }) }))] })] }) })] }), hiddenValues.map((value) => (_jsx("input", { type: "hidden", name: name, value: value }, value))), _jsx(FieldMessage, { error: error, errorMessage: errorMessage, helperText: helperText, helperId: ids.helperId, errorId: ids.errorId })] }));
|
|
554
585
|
});
|
|
555
586
|
export default SelectPopup;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { BORDER_WIDTHS } from "../../Theme/tokens/primitives/borderWidths.js";
|
|
2
|
+
export const NAVIGATION_KEYS = new Set(["ArrowDown", "ArrowUp", "Home", "End", "PageUp", "PageDown"]);
|
|
3
|
+
export const keyboardRingShadow = (ring) => `inset 0 0 0 ${BORDER_WIDTHS.hairline} ${ring}`;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isDarkCanvas, AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
|
|
2
|
+
import { liftToContrast, neutralStep } from "../../Theme/tokens/builders/outlineRung.js";
|
|
3
|
+
import { selectRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
|
|
4
|
+
import { optionRowInk } from "./optionRowInk.js";
|
|
5
|
+
export const SELECT_ROW_DISABLED_CONTENT_OPACITY = 0.55;
|
|
6
|
+
const NEUTRAL_INK_STEPS = {
|
|
7
|
+
label: 12.8,
|
|
8
|
+
selected: 15.4,
|
|
9
|
+
description: 7,
|
|
10
|
+
disabled: 3.6,
|
|
11
|
+
placeholder: 4.6,
|
|
12
|
+
};
|
|
13
|
+
// NOTE: light is a brand-free grey ladder off the panel; dark keeps the shared inks.
|
|
14
|
+
const neutralInk = (colors, role) => neutralStep(colors.surfaceRaised, NEUTRAL_INK_STEPS[role]);
|
|
15
|
+
const isDarkPanel = (colors) => isDarkCanvas(colors.surfaceRaised);
|
|
16
|
+
export const selectRowInk = (colors, isSingleSelected) => {
|
|
17
|
+
if (isDarkPanel(colors))
|
|
18
|
+
return optionRowInk(colors, isSingleSelected);
|
|
19
|
+
return neutralInk(colors, isSingleSelected ? "selected" : "label");
|
|
20
|
+
};
|
|
21
|
+
export const selectRowDescriptionInk = (colors) => isDarkPanel(colors)
|
|
22
|
+
? liftToContrast(colors.textMuted, selectRowBackgrounds(colors).selectedActive, AA_NORMAL_TEXT)
|
|
23
|
+
: neutralInk(colors, "description");
|
|
24
|
+
/** Solid ink for a disabled row's content in light; dark dims content by opacity instead (null). */
|
|
25
|
+
export const selectRowDisabledInk = (colors) => isDarkPanel(colors) ? null : neutralInk(colors, "disabled");
|
|
26
|
+
/** Placeholder of the popup's filter input in light; dark keeps the shared placeholder ink (null). */
|
|
27
|
+
export const selectFilterPlaceholderInk = (colors) => isDarkPanel(colors) ? null : neutralInk(colors, "placeholder");
|
|
28
|
+
/** Cursor and selection each paint a rung; the stacked state paints the third. */
|
|
29
|
+
export const selectRowFill = (rows, state) => {
|
|
30
|
+
if (state.isActive)
|
|
31
|
+
return state.isSelected ? rows.selectedActive : rows.active;
|
|
32
|
+
return state.isSelected ? rows.selected : "transparent";
|
|
33
|
+
};
|