@cube-dev/ui-kit 0.0.0-canary-1cd97ae → 0.0.0-canary-5a4d7bc
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/CHANGELOG.md +6 -0
- package/dist/_internal/hooks/use-chained-callback.js +1 -1
- package/dist/_internal/hooks/use-debounced-value.js +1 -1
- package/dist/_internal/hooks/use-deprecation-warning.js +1 -1
- package/dist/_internal/hooks/use-event.js +1 -1
- package/dist/_internal/hooks/use-is-first-render.js +1 -1
- package/dist/_internal/hooks/use-sync-ref.js +1 -1
- package/dist/_internal/hooks/use-timer/timer.js +1 -1
- package/dist/_internal/hooks/use-timer/use-timer.js +1 -1
- package/dist/_internal/hooks/use-warn.js +1 -1
- package/dist/components/Block.js +1 -1
- package/dist/components/CollectionItem.js +1 -1
- package/dist/components/GlobalStyles.js +1 -1
- package/dist/components/GridProvider.js +1 -1
- package/dist/components/HiddenInput.js +1 -1
- package/dist/components/Root.js +1 -1
- package/dist/components/actions/Action/Action.js +1 -1
- package/dist/components/actions/Banner/Banner.js +1 -1
- package/dist/components/actions/Button/Button.js +17 -3
- package/dist/components/actions/Button/Button.js.map +1 -1
- package/dist/components/actions/ButtonGroup/ButtonGroup.js +1 -1
- package/dist/components/actions/ButtonSplit/ButtonSplit.js +1 -1
- package/dist/components/actions/ButtonSplit/context.js +1 -1
- package/dist/components/actions/CommandMenu/CommandMenu.js +3 -4
- package/dist/components/actions/CommandMenu/CommandMenu.js.map +1 -1
- package/dist/components/actions/CommandMenu/styled.js +1 -1
- package/dist/components/actions/ItemAction/ItemAction.js +1 -1
- package/dist/components/actions/ItemActionContext.js +1 -1
- package/dist/components/actions/ItemButton/ItemButton.js +18 -2
- package/dist/components/actions/ItemButton/ItemButton.js.map +1 -1
- package/dist/components/actions/Link/Link.js +1 -1
- package/dist/components/actions/Menu/Menu.js +3 -4
- package/dist/components/actions/Menu/Menu.js.map +1 -1
- package/dist/components/actions/Menu/MenuItem.js +1 -1
- package/dist/components/actions/Menu/MenuSection.js +1 -1
- package/dist/components/actions/Menu/MenuTrigger.js +8 -5
- package/dist/components/actions/Menu/MenuTrigger.js.map +1 -1
- package/dist/components/actions/Menu/SubMenuTrigger.js +10 -7
- package/dist/components/actions/Menu/SubMenuTrigger.js.map +1 -1
- package/dist/components/actions/Menu/SubmenuTriggerContext.js +1 -1
- package/dist/components/actions/Menu/context.js +1 -1
- package/dist/components/actions/Menu/styled.js +1 -1
- package/dist/components/actions/index.js +1 -1
- package/dist/components/actions/use-action.js +1 -1
- package/dist/components/actions/use-anchored-menu.js +4 -3
- package/dist/components/actions/use-anchored-menu.js.map +1 -1
- package/dist/components/actions/use-context-menu.js +4 -3
- package/dist/components/actions/use-context-menu.js.map +1 -1
- package/dist/components/content/ActiveZone/ActiveZone.js +1 -1
- package/dist/components/content/Alert/Alert.js +1 -1
- package/dist/components/content/Alert/use-alert.js +1 -1
- package/dist/components/content/Avatar/Avatar.js +1 -1
- package/dist/components/content/Badge/Badge.js +1 -1
- package/dist/components/content/Card/Card.js +1 -1
- package/dist/components/content/Content.js +1 -1
- package/dist/components/content/CopyPasteBlock/CopyPasteBlock.js +1 -1
- package/dist/components/content/CopySnippet/CopySnippet.js +1 -1
- package/dist/components/content/Disclosure/Disclosure.js +1 -1
- package/dist/components/content/Divider.js +1 -1
- package/dist/components/content/Footer.js +1 -1
- package/dist/components/content/Header.js +1 -1
- package/dist/components/content/HotKeys/HotKeys.js +1 -1
- package/dist/components/content/InlineInput/InlineInput.js +1 -1
- package/dist/components/content/Item/Item.js +1 -1
- package/dist/components/content/ItemBadge/ItemBadge.js +1 -1
- package/dist/components/content/ItemCard/ItemCard.js +1 -1
- package/dist/components/content/Layout/GridLayout.js +1 -1
- package/dist/components/content/Layout/Layout.js +1 -1
- package/dist/components/content/Layout/LayoutBlock.js +1 -1
- package/dist/components/content/Layout/LayoutCenter.js +1 -1
- package/dist/components/content/Layout/LayoutContainer.js +1 -1
- package/dist/components/content/Layout/LayoutContent.js +1 -1
- package/dist/components/content/Layout/LayoutContext.js +1 -1
- package/dist/components/content/Layout/LayoutFlex.js +1 -1
- package/dist/components/content/Layout/LayoutFooter.js +1 -1
- package/dist/components/content/Layout/LayoutGrid.js +1 -1
- package/dist/components/content/Layout/LayoutHeader.js +1 -1
- package/dist/components/content/Layout/LayoutPane.js +1 -1
- package/dist/components/content/Layout/LayoutPanel.js +1 -1
- package/dist/components/content/Layout/LayoutPanelHeader.js +1 -1
- package/dist/components/content/Layout/LayoutToolbar.js +1 -1
- package/dist/components/content/Layout/hooks/useTinyScrollbar.js +1 -1
- package/dist/components/content/Layout/index.js +1 -1
- package/dist/components/content/Layout/utils.js +1 -1
- package/dist/components/content/Paragraph.js +1 -1
- package/dist/components/content/Placeholder/Placeholder.js +1 -1
- package/dist/components/content/PrismCode/PrismCode.js +1 -1
- package/dist/components/content/PrismCode/prismSetup.js +1 -1
- package/dist/components/content/PrismDiffCode/PrismDiffCode.js +1 -1
- package/dist/components/content/Result/Result.js +1 -1
- package/dist/components/content/Skeleton/Skeleton.js +1 -1
- package/dist/components/content/Tag/Tag.js +1 -1
- package/dist/components/content/Text.js +1 -1
- package/dist/components/content/TextItem/TextItem.js +1 -1
- package/dist/components/content/Title.js +1 -1
- package/dist/components/content/Tree/Tree.js +1 -1
- package/dist/components/content/Tree/TreeNode.js +1 -1
- package/dist/components/content/Tree/styled.js +1 -1
- package/dist/components/content/Tree/tree-index.js +1 -1
- package/dist/components/content/Tree/use-checkbox-tree.js +1 -1
- package/dist/components/content/Tree/use-load-data.js +1 -1
- package/dist/components/content/highlightText.js +1 -1
- package/dist/components/content/use-auto-tooltip.js +1 -1
- package/dist/components/fields/Checkbox/Checkbox.js +1 -1
- package/dist/components/fields/Checkbox/CheckboxGroup.js +3 -3
- package/dist/components/fields/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/components/fields/Checkbox/context.js +1 -1
- package/dist/components/fields/ComboBox/ComboBox.js +11 -7
- package/dist/components/fields/ComboBox/ComboBox.js.map +1 -1
- package/dist/components/fields/DatePicker/DateInput.js +1 -1
- package/dist/components/fields/DatePicker/DateInputBase.js +1 -1
- package/dist/components/fields/DatePicker/DatePicker.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerButton.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerElement.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerInput.js +1 -1
- package/dist/components/fields/DatePicker/DatePickerSegment.js +1 -1
- package/dist/components/fields/DatePicker/DateRangePicker.js +1 -1
- package/dist/components/fields/DatePicker/DateRangeSeparatedPicker.js +1 -1
- package/dist/components/fields/DatePicker/TimeInput.js +1 -1
- package/dist/components/fields/DatePicker/intl.js +1 -1
- package/dist/components/fields/DatePicker/parseDate.js +1 -1
- package/dist/components/fields/DatePicker/props.js +1 -1
- package/dist/components/fields/DatePicker/utils.js +1 -1
- package/dist/components/fields/FileInput/FileInput.js +1 -1
- package/dist/components/fields/FilterListBox/FilterListBox.js +1 -1
- package/dist/components/fields/FilterPicker/FilterPicker.js +1 -9
- package/dist/components/fields/FilterPicker/FilterPicker.js.map +1 -1
- package/dist/components/fields/Input/Input.js +1 -1
- package/dist/components/fields/ListBox/DraggableListBox.js +1 -1
- package/dist/components/fields/ListBox/ListBox.js +1 -1
- package/dist/components/fields/NumberInput/NumberInput.js +1 -1
- package/dist/components/fields/NumberInput/StepButton.js +1 -1
- package/dist/components/fields/PasswordInput/PasswordInput.js +1 -1
- package/dist/components/fields/Picker/Picker.js +1 -9
- package/dist/components/fields/Picker/Picker.js.map +1 -1
- package/dist/components/fields/RadioGroup/Radio.js +1 -1
- package/dist/components/fields/RadioGroup/RadioGroup.js +3 -3
- package/dist/components/fields/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/components/fields/RadioGroup/context.js +1 -1
- package/dist/components/fields/SearchInput/SearchInput.js +1 -1
- package/dist/components/fields/Select/Select.js +10 -7
- package/dist/components/fields/Select/Select.js.map +1 -1
- package/dist/components/fields/Slider/Gradation.js +1 -1
- package/dist/components/fields/Slider/HueSlider.js +1 -1
- package/dist/components/fields/Slider/RangeSlider.js +1 -1
- package/dist/components/fields/Slider/Slider.js +1 -1
- package/dist/components/fields/Slider/SliderBase.js +1 -1
- package/dist/components/fields/Slider/SliderThumb.js +1 -1
- package/dist/components/fields/Slider/SliderTrack.js +1 -1
- package/dist/components/fields/Slider/elements.js +1 -1
- package/dist/components/fields/Slider/index.js +1 -1
- package/dist/components/fields/Switch/Switch.js +1 -1
- package/dist/components/fields/TextArea/TextArea.js +1 -1
- package/dist/components/fields/TextInput/TextInput.js +1 -1
- package/dist/components/fields/TextInput/TextInputBase.js +1 -1
- package/dist/components/fields/TextInputMapper/TextInputMapper.js +1 -1
- package/dist/components/form/FieldWrapper/FieldWrapper.js +1 -1
- package/dist/components/form/FieldWrapper/extract-field-wrapper-props.js +1 -1
- package/dist/components/form/Form/Field.js +1 -1
- package/dist/components/form/Form/Form.d.ts +1 -2
- package/dist/components/form/Form/Form.js +3 -3
- package/dist/components/form/Form/Form.js.map +1 -1
- package/dist/components/form/Form/ResetButton/ResetButton.js +1 -1
- package/dist/components/form/Form/SubmitButton/SubmitButton.js +1 -1
- package/dist/components/form/Form/SubmitError.js +1 -1
- package/dist/components/form/Form/index.d.ts +1 -1
- package/dist/components/form/Form/index.js +1 -1
- package/dist/components/form/Form/use-field/use-field-props.js +1 -1
- package/dist/components/form/Form/use-field/use-field.js +1 -1
- package/dist/components/form/Form/use-form.js +1 -1
- package/dist/components/form/Form/validation.js +1 -1
- package/dist/components/form/Label.js +3 -3
- package/dist/components/form/Label.js.map +1 -1
- package/dist/components/form/wrapper.js +1 -1
- package/dist/components/helpers/DisplayTransition/DisplayTransition.js +1 -1
- package/dist/components/helpers/IconSwitch/IconSwitch.js +1 -1
- package/dist/components/layout/Flex.js +1 -1
- package/dist/components/layout/Flow.js +1 -1
- package/dist/components/layout/Grid.js +1 -1
- package/dist/components/layout/Panel.js +1 -1
- package/dist/components/layout/Prefix.js +1 -1
- package/dist/components/layout/ResizablePanel.js +1 -1
- package/dist/components/layout/Space.js +1 -1
- package/dist/components/layout/Suffix.js +1 -1
- package/dist/components/navigation/Tabs/DraggableTabList.js +1 -1
- package/dist/components/navigation/Tabs/TabButton.js +1 -1
- package/dist/components/navigation/Tabs/TabDropIndicator.js +1 -1
- package/dist/components/navigation/Tabs/TabPanel.js +1 -1
- package/dist/components/navigation/Tabs/TabPicker.js +1 -1
- package/dist/components/navigation/Tabs/Tabs.js +1 -1
- package/dist/components/navigation/Tabs/TabsAction.js +1 -1
- package/dist/components/navigation/Tabs/TabsContext.js +1 -1
- package/dist/components/navigation/Tabs/popover-placement.js +1 -1
- package/dist/components/navigation/Tabs/styled.js +1 -1
- package/dist/components/navigation/Tabs/types.js +1 -1
- package/dist/components/navigation/Tabs/use-tab-editing.js +1 -1
- package/dist/components/navigation/Tabs/use-tab-indicator.js +1 -1
- package/dist/components/organisms/FileTabs/FileTabs.js +1 -1
- package/dist/components/organisms/StatsCard/StatsCard.js +1 -1
- package/dist/components/other/Calendar/Calendar.js +1 -1
- package/dist/components/other/Calendar/CalendarCell.js +1 -1
- package/dist/components/other/Calendar/CalendarGrid.js +1 -1
- package/dist/components/other/Calendar/RangeCalendar.js +1 -1
- package/dist/components/other/CloudLogo/CloudLogo.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialog.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialogApiProvider.js +1 -1
- package/dist/components/overlays/AlertDialog/AlertDialogZone.js +1 -1
- package/dist/components/overlays/Dialog/Dialog.d.ts +1 -2
- package/dist/components/overlays/Dialog/Dialog.js +3 -3
- package/dist/components/overlays/Dialog/Dialog.js.map +1 -1
- package/dist/components/overlays/Dialog/DialogContainer.js +1 -1
- package/dist/components/overlays/Dialog/DialogForm.js +1 -1
- package/dist/components/overlays/Dialog/DialogTrigger.js +32 -3
- package/dist/components/overlays/Dialog/DialogTrigger.js.map +1 -1
- package/dist/components/overlays/Dialog/context.js +1 -1
- package/dist/components/overlays/Dialog/use-dialog-container.js +1 -1
- package/dist/components/overlays/Modal/Modal.js +3 -3
- package/dist/components/overlays/Modal/Modal.js.map +1 -1
- package/dist/components/overlays/Modal/OpenTransitionContext.js +1 -1
- package/dist/components/overlays/Modal/Overlay.js +1 -1
- package/dist/components/overlays/Modal/Popover.js +1 -1
- package/dist/components/overlays/Modal/Tray.js +3 -3
- package/dist/components/overlays/Modal/Tray.js.map +1 -1
- package/dist/components/overlays/Modal/Underlay.js +1 -1
- package/dist/components/overlays/Notifications/Notification.js +1 -1
- package/dist/components/overlays/Notifications/NotificationAction.js +1 -1
- package/dist/components/overlays/Notifications/NotificationCard.js +1 -1
- package/dist/components/overlays/Notifications/NotificationContext.js +1 -1
- package/dist/components/overlays/Notifications/NotificationItem.js +1 -1
- package/dist/components/overlays/Notifications/OverlayContainer.js +1 -1
- package/dist/components/overlays/Notifications/OverlayProvider.js +1 -1
- package/dist/components/overlays/Notifications/PersistentNotificationsList.js +1 -1
- package/dist/components/overlays/Notifications/dismissed-storage.js +1 -1
- package/dist/components/overlays/Notifications/format-relative-time.js +1 -1
- package/dist/components/overlays/Notifications/index.js +1 -1
- package/dist/components/overlays/Notifications/use-notification-state.js +1 -1
- package/dist/components/overlays/Notifications/use-notifications.js +1 -1
- package/dist/components/overlays/Notifications/use-overlay-timers.js +1 -1
- package/dist/components/overlays/Notifications/use-persistent-notifications.js +1 -1
- package/dist/components/overlays/Notifications/use-persistent-state.js +1 -1
- package/dist/components/overlays/Notifications/use-toast-state.js +1 -1
- package/dist/components/overlays/Toast/ToastItem.js +1 -1
- package/dist/components/overlays/Toast/index.js +1 -1
- package/dist/components/overlays/Toast/useProgressToast.js +1 -1
- package/dist/components/overlays/Toast/useToast.js +1 -1
- package/dist/components/overlays/Tooltip/Tooltip.js +1 -1
- package/dist/components/overlays/Tooltip/TooltipProvider.js +1 -1
- package/dist/components/overlays/Tooltip/TooltipTrigger.js +1 -1
- package/dist/components/overlays/Tooltip/context.js +1 -1
- package/dist/components/portal/Portal.js +1 -1
- package/dist/components/portal/PortalProvider.js +1 -1
- package/dist/components/portal/usePortal.js +1 -1
- package/dist/components/shared/DraggableCollection.js +1 -1
- package/dist/components/shared/InvalidIcon.js +1 -1
- package/dist/components/shared/ValidIcon.js +1 -1
- package/dist/components/status/LoadingAnimation/LoadingAnimation.js +1 -1
- package/dist/components/status/Spin/Cube.js +1 -1
- package/dist/components/status/Spin/InternalSpinner.js +1 -1
- package/dist/components/status/Spin/Spin.js +1 -1
- package/dist/components/status/Spin/SpinsContainer.js +1 -1
- package/dist/data/item-themes.js +1 -1
- package/dist/data/themes.js +1 -1
- package/dist/icons/AdjustmentsHorizontalIcon.js +1 -1
- package/dist/icons/AdjustmentsIcon.js +1 -1
- package/dist/icons/AiIcon.js +1 -1
- package/dist/icons/AreaChartIcon.js +1 -1
- package/dist/icons/BackwardIcon.js +1 -1
- package/dist/icons/BarChartIcon.js +1 -1
- package/dist/icons/BellFilledIcon.js +1 -1
- package/dist/icons/BellIcon.js +1 -1
- package/dist/icons/BooleanIcon.js +1 -1
- package/dist/icons/CalendarEditIcon.js +1 -1
- package/dist/icons/CalendarIcon.js +1 -1
- package/dist/icons/CaretDownIcon.js +1 -1
- package/dist/icons/CaretUpIcon.js +1 -1
- package/dist/icons/ChartAreaStackedIcon.js +1 -1
- package/dist/icons/ChartAreaStackedPercentageIcon.js +1 -1
- package/dist/icons/ChartBarGroupedHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarGroupedIcon.js +1 -1
- package/dist/icons/ChartBarHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarLineIcon.js +1 -1
- package/dist/icons/ChartBarStackedHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarStackedIcon.js +1 -1
- package/dist/icons/ChartBarStackedPercentageHorizontalIcon.js +1 -1
- package/dist/icons/ChartBarStackedPercentageIcon.js +1 -1
- package/dist/icons/ChartBoxPlot2Icon.js +1 -1
- package/dist/icons/ChartBoxPlotIcon.js +1 -1
- package/dist/icons/ChartBubbleIcon.js +1 -1
- package/dist/icons/ChartDonut2Icon.js +1 -1
- package/dist/icons/ChartFunnelIcon.js +1 -1
- package/dist/icons/ChartHeatmapIcon.js +1 -1
- package/dist/icons/ChartKPIIcon.js +1 -1
- package/dist/icons/ChartPie2Icon.js +1 -1
- package/dist/icons/ChartScatterIcon.js +1 -1
- package/dist/icons/CheckCircleFilledIcon.js +1 -1
- package/dist/icons/CheckCircleIcon.js +1 -1
- package/dist/icons/CheckIcon.js +1 -1
- package/dist/icons/CircleFilledIcon.js +1 -1
- package/dist/icons/ClearIcon.js +1 -1
- package/dist/icons/CloseCircleFilledIcon.js +1 -1
- package/dist/icons/CloseCircleIcon.js +1 -1
- package/dist/icons/CloseIcon.js +1 -1
- package/dist/icons/CodeIcon.js +1 -1
- package/dist/icons/ColumnTotalIcon.js +1 -1
- package/dist/icons/CopyIcon.js +1 -1
- package/dist/icons/CountIcon.js +1 -1
- package/dist/icons/CubeIcon.js +1 -1
- package/dist/icons/CubePauseIcon.js +1 -1
- package/dist/icons/CubePlayIcon.js +1 -1
- package/dist/icons/CurrencyDollarIcon.js +1 -1
- package/dist/icons/DangerIcon.js +1 -1
- package/dist/icons/DashboardIcon.js +1 -1
- package/dist/icons/DatabaseIcon.js +1 -1
- package/dist/icons/DecimalDecreaseIcon.js +1 -1
- package/dist/icons/DecimalIncreaseIcon.js +1 -1
- package/dist/icons/DirectionIcon.js +1 -1
- package/dist/icons/DonutIcon.js +1 -1
- package/dist/icons/DownIcon.js +1 -1
- package/dist/icons/EditIcon.js +1 -1
- package/dist/icons/ExclamationCircleFilledIcon.js +1 -1
- package/dist/icons/ExclamationCircleIcon.js +1 -1
- package/dist/icons/ExclamationIcon.js +1 -1
- package/dist/icons/EyeIcon.js +1 -1
- package/dist/icons/EyeInvisibleIcon.js +1 -1
- package/dist/icons/FilterIcon.js +1 -1
- package/dist/icons/FolderFilledIcon.js +1 -1
- package/dist/icons/FolderIcon.js +1 -1
- package/dist/icons/FolderOpenFilledIcon.js +1 -1
- package/dist/icons/FolderOpenIcon.js +1 -1
- package/dist/icons/ForwardIcon.js +1 -1
- package/dist/icons/GripVerticalIcon.js +1 -1
- package/dist/icons/HierarchyIcon.js +1 -1
- package/dist/icons/HierarchyOpenIcon.js +1 -1
- package/dist/icons/Icon.js +1 -1
- package/dist/icons/InfoCircleIcon.js +1 -1
- package/dist/icons/InfoIcon.js +1 -1
- package/dist/icons/KeyIcon.js +1 -1
- package/dist/icons/LeftIcon.js +1 -1
- package/dist/icons/LineChartIcon.js +1 -1
- package/dist/icons/LoadingIcon.js +1 -1
- package/dist/icons/LockFilledIcon.js +1 -1
- package/dist/icons/LockIcon.js +1 -1
- package/dist/icons/MoreIcon.js +1 -1
- package/dist/icons/NotAllowedIcon.js +1 -1
- package/dist/icons/Number123Icon.js +1 -1
- package/dist/icons/NumberIcon.js +1 -1
- package/dist/icons/PauseCircleFilledIcon.js +1 -1
- package/dist/icons/PauseCircleIcon.js +1 -1
- package/dist/icons/PauseIcon.js +1 -1
- package/dist/icons/PercentageIcon.js +1 -1
- package/dist/icons/PieChartIcon.js +1 -1
- package/dist/icons/PlayCircleIcon.js +1 -1
- package/dist/icons/PlayIcon.js +1 -1
- package/dist/icons/PlusIcon.js +1 -1
- package/dist/icons/ProgressBarIcon.js +1 -1
- package/dist/icons/ReloadIcon.js +1 -1
- package/dist/icons/ReportIcon.js +1 -1
- package/dist/icons/ReturnIcon.js +1 -1
- package/dist/icons/RightIcon.js +1 -1
- package/dist/icons/RowTotalsIcon.js +1 -1
- package/dist/icons/SchemeIcon.js +1 -1
- package/dist/icons/SearchIcon.js +1 -1
- package/dist/icons/SemanticQueryIcon.js +1 -1
- package/dist/icons/SettingsIcon.js +1 -1
- package/dist/icons/ShieldFilledIcon.js +1 -1
- package/dist/icons/ShieldIcon.js +1 -1
- package/dist/icons/SlashIcon.js +1 -1
- package/dist/icons/SparklesIcon.js +1 -1
- package/dist/icons/SqlIcon.js +1 -1
- package/dist/icons/StatsIcon.js +1 -1
- package/dist/icons/StopIcon.js +1 -1
- package/dist/icons/StringIcon.js +1 -1
- package/dist/icons/SubtotalsIcon.js +1 -1
- package/dist/icons/SwitchIcon.js +1 -1
- package/dist/icons/TableIcon.js +1 -1
- package/dist/icons/ThumbsDownIcon.js +1 -1
- package/dist/icons/ThumbsUpIcon.js +1 -1
- package/dist/icons/ThunderboltCrossedIcon.js +1 -1
- package/dist/icons/ThunderboltFilledIcon.js +1 -1
- package/dist/icons/ThunderboltIcon.js +1 -1
- package/dist/icons/TimeIcon.js +1 -1
- package/dist/icons/TrashIcon.js +1 -1
- package/dist/icons/UnlockIcon.js +1 -1
- package/dist/icons/UpIcon.js +1 -1
- package/dist/icons/UserGroupIcon.js +1 -1
- package/dist/icons/UserIcon.js +1 -1
- package/dist/icons/UserLockIcon.js +1 -1
- package/dist/icons/ViewIcon.js +1 -1
- package/dist/icons/WarningFilledIcon.js +1 -1
- package/dist/icons/WarningIcon.js +1 -1
- package/dist/icons/wrap-icon.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/provider.js +1 -1
- package/dist/providers/TrackingProvider.js +1 -1
- package/dist/providers/navigationAdapter.default.js +1 -1
- package/dist/tokens/base.js +1 -1
- package/dist/tokens/colors.js +1 -1
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/layout.js +1 -1
- package/dist/tokens/palette.js +1 -1
- package/dist/tokens/shadows.js +1 -1
- package/dist/tokens/sizes.js +1 -1
- package/dist/tokens/spacing.js +1 -1
- package/dist/tokens/typography.js +1 -1
- package/dist/utils/ResizeSensor.js +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/is-dev-env.js +1 -1
- package/dist/utils/modules.js +1 -1
- package/dist/utils/promise.js +1 -1
- package/dist/utils/raf.js +1 -1
- package/dist/utils/random.js +1 -1
- package/dist/utils/range.js +1 -1
- package/dist/utils/react/RenderCache.js +1 -1
- package/dist/utils/react/Slots.js +1 -1
- package/dist/utils/react/chain.js +1 -1
- package/dist/utils/react/forwardRefWithGenerics.js +1 -1
- package/dist/utils/react/index.d.ts +1 -0
- package/dist/utils/react/index.js +2 -1
- package/dist/utils/react/interactions.js +1 -1
- package/dist/utils/react/isTextOnly.js +1 -1
- package/dist/utils/react/mapProps.js +1 -1
- package/dist/utils/react/mergeProps.js +1 -1
- package/dist/utils/react/nullableValue.js +1 -1
- package/dist/utils/react/resolveIcon.js +1 -1
- package/dist/utils/react/sharedStore.js +1 -1
- package/dist/utils/react/useCombinedRefs.js +1 -1
- package/dist/utils/react/useControlledFocusVisible.js +1 -1
- package/dist/utils/react/useEventBus.js +25 -15
- package/dist/utils/react/useEventBus.js.map +1 -1
- package/dist/utils/react/useId.js +1 -1
- package/dist/utils/react/useIsDarwin.js +1 -1
- package/dist/utils/react/useKeySymbols.js +1 -1
- package/dist/utils/react/useLayoutEffect.js +1 -1
- package/dist/utils/react/useLocalStorage.js +1 -1
- package/dist/utils/react/useMergeStyles.js +1 -1
- package/dist/utils/react/usePopoverSync.d.ts +105 -0
- package/dist/utils/react/usePopoverSync.js +130 -10
- package/dist/utils/react/usePopoverSync.js.map +1 -1
- package/dist/utils/react/useQaProps.js +1 -1
- package/dist/utils/react/useViewportSize.js +1 -1
- package/dist/utils/react/wrapNodeIfPlain.js +1 -1
- package/dist/utils/selection.js +1 -1
- package/dist/utils/styles.js +1 -1
- package/dist/utils/tree.js +1 -1
- package/dist/utils/warnings.js +1 -1
- package/dist/version.js +2 -2
- package/docs/components/actions/Button.md +102 -24
- package/docs/components/actions/ItemButton.md +71 -34
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ItemButton.js","names":["Item","mergeProps"],"sources":["../../../../src/components/actions/ItemButton/ItemButton.tsx"],"sourcesContent":["import { FocusableRef } from '@react-types/shared';\nimport { Styles, tasty } from '@tenphi/tasty';\nimport {\n CSSProperties,\n forwardRef,\n ReactNode,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useFocusWithin, useHover } from 'react-aria';\n\nimport { mergeProps } from '../../../utils/react';\nimport { CubeItemProps, Item } from '../../content/Item';\nimport { ItemBadge } from '../../content/ItemBadge';\nimport { DisplayTransition } from '../../helpers';\nimport { CubeItemActionProps, ItemAction } from '../ItemAction';\nimport { ItemActionProvider } from '../ItemActionContext';\nimport { CubeUseActionProps, useAction } from '../use-action';\n\nexport interface CubeItemButtonProps\n extends Omit<CubeItemProps, 'size'>,\n Omit<CubeUseActionProps, 'as'> {\n actions?: ReactNode;\n size?: Omit<CubeItemProps['size'], 'inline'>;\n wrapperStyles?: Styles;\n}\n\nconst StyledItem = tasty(Item, {\n as: 'button',\n type: 'item',\n theme: 'default',\n styles: {\n recipe: 'reset button',\n placeContent: 'center stretch',\n },\n});\n\nconst ActionsWrapper = tasty({\n styles: {\n display: 'grid',\n position: 'relative',\n placeContent: 'stretch',\n placeItems: 'stretch',\n\n $size: {\n '': '$size-md',\n 'size=xsmall': '$size-xs',\n 'size=small': '$size-sm',\n 'size=medium': '$size-md',\n 'size=large': '$size-lg',\n 'size=xlarge': '$size-xl',\n },\n\n Actions: {\n $: '>',\n position: 'absolute',\n inset: {\n '': '1bw 1bw auto auto',\n 'type=card': '(1bw + .5x) (1bw + .5x) auto auto',\n },\n display: 'flex',\n gap: '1bw',\n placeItems: 'center',\n placeContent: 'center end',\n pointerEvents: {\n '': 'auto',\n '!actions-shown': 'none',\n },\n padding: '0 $side-padding',\n height: 'min ($size - 2bw)',\n opacity: {\n '': 1,\n '!actions-shown': 0,\n },\n translate: {\n '': '0 0',\n '!actions-shown': '.5x 0',\n },\n transition: 'theme, translate',\n\n // Size for the action buttons\n '$action-size': 'min(max((2x + 2bw), ($size - 1x - 2bw)), (3x - 2bw))',\n // Side padding for the button\n '$side-padding': '(($size - $action-size - 2bw) / 2)',\n },\n },\n});\n\nconst ItemButton = forwardRef(function ItemButton(\n allProps: CubeItemButtonProps,\n ref: FocusableRef<HTMLElement>,\n) {\n const {\n mods,\n to,\n htmlType,\n as,\n type = 'item',\n theme = 'default',\n onPress,\n // Extract react-aria press callbacks to prevent them from leaking to DOM via rest.\n // These are handled by useButton inside useAction.\n onPressStart: _onPressStart,\n onPressEnd: _onPressEnd,\n onPressChange: _onPressChange,\n onPressUp: _onPressUp,\n actions,\n size = 'medium',\n wrapperStyles,\n autoHideActions = false,\n disableActionsFocus = false,\n isDisabled,\n isLoading = false,\n ...rest\n } = allProps as CubeItemButtonProps & {\n as?: 'a' | 'button' | 'div' | 'span';\n };\n\n // Loading state makes the component disabled (same logic as Item)\n const finalIsDisabled =\n isDisabled === true || (isLoading && isDisabled !== false);\n\n const actionsRef = useRef<HTMLDivElement>(null);\n const [actionsWidth, setActionsWidth] = useState(0);\n const [areActionsVisible, setAreActionsVisible] = useState(false);\n const [areActionsShown, setAreActionsShown] = useState(false);\n\n useLayoutEffect(() => {\n if (actions && actionsRef.current) {\n const width = Math.round(actionsRef.current.offsetWidth);\n if (width !== actionsWidth) {\n setActionsWidth(width);\n }\n }\n }, [actions, areActionsVisible]);\n\n const [isFocusWithin, setIsFocusWithin] = useState(false);\n const [hasPressed, setHasPressed] = useState(false);\n const { hoverProps, isHovered } = useHover({});\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: setIsFocusWithin,\n });\n\n // Watch for data-pressed attribute on any descendant element\n useLayoutEffect(() => {\n const actionsEl = actionsRef.current;\n\n if (!actionsEl || !autoHideActions) return;\n\n const checkPressed = () => {\n setHasPressed(actionsEl.querySelector('[data-pressed]') !== null);\n };\n\n const observer = new MutationObserver(checkPressed);\n\n observer.observe(actionsEl, {\n attributes: true,\n attributeFilter: ['data-pressed'],\n subtree: true,\n });\n\n checkPressed();\n\n return () => observer.disconnect();\n }, [areActionsVisible, autoHideActions]);\n\n const shouldShowActions =\n isHovered || isFocusWithin || hasPressed || !autoHideActions;\n\n const { actionProps } = useAction(\n {\n ...(allProps as any),\n htmlType,\n to,\n as,\n mods,\n isDisabled: finalIsDisabled,\n },\n ref,\n );\n\n const finalMods = useMemo(() => {\n return shouldShowActions ? { ...mods, 'actions-shown': true } : mods;\n }, [mods, shouldShowActions]);\n\n const button = (\n <StyledItem\n insideWrapper={!!actions}\n showActions={shouldShowActions}\n actions={actions ? true : undefined}\n {...(mergeProps(rest, actionProps) as any)}\n data-popover-dismiss=\"\"\n htmlType={actionProps.type}\n type={type}\n theme={theme}\n size={size}\n isLoading={isLoading}\n isDisabled={isDisabled}\n />\n );\n\n if (actions) {\n return (\n <ActionsWrapper\n {...hoverProps}\n data-size={size}\n data-type={type}\n data-theme={theme}\n mods={finalMods}\n styles={wrapperStyles}\n style={\n {\n '--actions-width':\n areActionsVisible || !autoHideActions\n ? `${actionsWidth}px`\n : '0px',\n ...(typeof size === 'number' && { '--size': `${size}px` }),\n } as CSSProperties\n }\n >\n {button}\n <ItemActionProvider\n type={type}\n theme={theme}\n disableActionsFocus={disableActionsFocus}\n isDisabled={finalIsDisabled}\n >\n {autoHideActions ? (\n <DisplayTransition\n exposeUnmounted\n isShown={shouldShowActions}\n onPhaseChange={(phase) => {\n setAreActionsVisible(phase !== 'unmounted');\n }}\n onToggle={(isShown) => {\n setAreActionsShown(isShown);\n }}\n >\n {({ ref: transitionRef }) => {\n return (\n <div\n {...focusWithinProps}\n ref={(node: any) => {\n actionsRef.current = node;\n transitionRef(node);\n }}\n data-element=\"Actions\"\n >\n {actions}\n </div>\n );\n }}\n </DisplayTransition>\n ) : (\n <div ref={actionsRef} data-element=\"Actions\">\n {actions}\n </div>\n )}\n </ItemActionProvider>\n </ActionsWrapper>\n );\n }\n\n return button;\n});\n\nconst _ItemButton = Object.assign(ItemButton, {\n Action: ItemAction,\n Badge: ItemBadge,\n});\n\nexport { _ItemButton as ItemButton };\nexport type {\n CubeItemButtonProps as ItemButtonProps,\n CubeItemActionProps as ItemActionProps,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ItemButton.js","names":["Item","mergeProps"],"sources":["../../../../src/components/actions/ItemButton/ItemButton.tsx"],"sourcesContent":["import { FocusableRef, PressEvent } from '@react-types/shared';\nimport { Styles, tasty } from '@tenphi/tasty';\nimport {\n CSSProperties,\n forwardRef,\n ReactNode,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useFocusWithin, useHover } from 'react-aria';\n\nimport { useEvent } from '../../../_internal';\nimport {\n mergeProps,\n mergeRefs,\n useDismissParentPopover,\n} from '../../../utils/react';\nimport { CubeItemProps, Item } from '../../content/Item';\nimport { ItemBadge } from '../../content/ItemBadge';\nimport { DisplayTransition } from '../../helpers';\nimport { CubeItemActionProps, ItemAction } from '../ItemAction';\nimport { ItemActionProvider } from '../ItemActionContext';\nimport { CubeUseActionProps, useAction } from '../use-action';\n\nexport interface CubeItemButtonProps\n extends Omit<CubeItemProps, 'size'>,\n Omit<CubeUseActionProps, 'as'> {\n actions?: ReactNode;\n size?: Omit<CubeItemProps['size'], 'inline'>;\n wrapperStyles?: Styles;\n}\n\nconst StyledItem = tasty(Item, {\n as: 'button',\n type: 'item',\n theme: 'default',\n styles: {\n recipe: 'reset button',\n placeContent: 'center stretch',\n },\n});\n\nconst ActionsWrapper = tasty({\n styles: {\n display: 'grid',\n position: 'relative',\n placeContent: 'stretch',\n placeItems: 'stretch',\n\n $size: {\n '': '$size-md',\n 'size=xsmall': '$size-xs',\n 'size=small': '$size-sm',\n 'size=medium': '$size-md',\n 'size=large': '$size-lg',\n 'size=xlarge': '$size-xl',\n },\n\n Actions: {\n $: '>',\n position: 'absolute',\n inset: {\n '': '1bw 1bw auto auto',\n 'type=card': '(1bw + .5x) (1bw + .5x) auto auto',\n },\n display: 'flex',\n gap: '1bw',\n placeItems: 'center',\n placeContent: 'center end',\n pointerEvents: {\n '': 'auto',\n '!actions-shown': 'none',\n },\n padding: '0 $side-padding',\n height: 'min ($size - 2bw)',\n opacity: {\n '': 1,\n '!actions-shown': 0,\n },\n translate: {\n '': '0 0',\n '!actions-shown': '.5x 0',\n },\n transition: 'theme, translate',\n\n // Size for the action buttons\n '$action-size': 'min(max((2x + 2bw), ($size - 1x - 2bw)), (3x - 2bw))',\n // Side padding for the button\n '$side-padding': '(($size - $action-size - 2bw) / 2)',\n },\n },\n});\n\nconst ItemButton = forwardRef(function ItemButton(\n allProps: CubeItemButtonProps,\n ref: FocusableRef<HTMLElement>,\n) {\n const {\n mods,\n to,\n htmlType,\n as,\n type = 'item',\n theme = 'default',\n onPress,\n // Extract react-aria press callbacks to prevent them from leaking to DOM via rest.\n // These are handled by useButton inside useAction.\n onPressStart: _onPressStart,\n onPressEnd: _onPressEnd,\n onPressChange: _onPressChange,\n onPressUp: _onPressUp,\n actions,\n size = 'medium',\n wrapperStyles,\n autoHideActions = false,\n disableActionsFocus = false,\n isDisabled,\n isLoading = false,\n ...rest\n } = allProps as CubeItemButtonProps & {\n as?: 'a' | 'button' | 'div' | 'span';\n };\n\n // Loading state makes the component disabled (same logic as Item)\n const finalIsDisabled =\n isDisabled === true || (isLoading && isDisabled !== false);\n\n const actionsRef = useRef<HTMLDivElement>(null);\n const [actionsWidth, setActionsWidth] = useState(0);\n const [areActionsVisible, setAreActionsVisible] = useState(false);\n const [areActionsShown, setAreActionsShown] = useState(false);\n\n useLayoutEffect(() => {\n if (actions && actionsRef.current) {\n const width = Math.round(actionsRef.current.offsetWidth);\n if (width !== actionsWidth) {\n setActionsWidth(width);\n }\n }\n }, [actions, areActionsVisible]);\n\n const [isFocusWithin, setIsFocusWithin] = useState(false);\n const [hasPressed, setHasPressed] = useState(false);\n const { hoverProps, isHovered } = useHover({});\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: setIsFocusWithin,\n });\n\n // Watch for data-pressed attribute on any descendant element\n useLayoutEffect(() => {\n const actionsEl = actionsRef.current;\n\n if (!actionsEl || !autoHideActions) return;\n\n const checkPressed = () => {\n setHasPressed(actionsEl.querySelector('[data-pressed]') !== null);\n };\n\n const observer = new MutationObserver(checkPressed);\n\n observer.observe(actionsEl, {\n attributes: true,\n attributeFilter: ['data-pressed'],\n subtree: true,\n });\n\n checkPressed();\n\n return () => observer.disconnect();\n }, [areActionsVisible, autoHideActions]);\n\n const shouldShowActions =\n isHovered || isFocusWithin || hasPressed || !autoHideActions;\n\n // Default: pressing an ItemButton inside an open popover closes that\n // popover. Opt-outs: `data-popover-trigger` on self (applied by\n // FilterPicker / Picker / Select / MenuTrigger for their own triggers) and\n // `data-popover-keep` on self or any ancestor. Modals don't subscribe.\n const dismissParentPopover = useDismissParentPopover();\n const buttonElementRef = useRef<HTMLElement | null>(null);\n\n const wrappedOnPress = useEvent((e: PressEvent) => {\n onPress?.(e);\n const el = buttonElementRef.current;\n if (!el) return;\n if (el.hasAttribute('data-popover-trigger')) return;\n if (el.closest('[data-popover-keep]')) return;\n dismissParentPopover(el);\n });\n\n const { actionProps } = useAction(\n {\n ...(allProps as any),\n htmlType,\n to,\n as,\n mods,\n isDisabled: finalIsDisabled,\n onPress: wrappedOnPress,\n },\n ref,\n );\n\n const finalMods = useMemo(() => {\n return shouldShowActions ? { ...mods, 'actions-shown': true } : mods;\n }, [mods, shouldShowActions]);\n\n // Merge the useAction-supplied ref with our internal ref so the dismiss\n // wrapper can read the rendered DOM node at press time.\n const combinedRef = useMemo(\n () => mergeRefs(actionProps.ref as any, buttonElementRef),\n [actionProps.ref],\n );\n\n const button = (\n <StyledItem\n insideWrapper={!!actions}\n showActions={shouldShowActions}\n actions={actions ? true : undefined}\n {...(mergeProps(rest, actionProps) as any)}\n ref={combinedRef}\n data-popover-dismiss=\"\"\n htmlType={actionProps.type}\n type={type}\n theme={theme}\n size={size}\n isLoading={isLoading}\n isDisabled={isDisabled}\n />\n );\n\n if (actions) {\n return (\n <ActionsWrapper\n {...hoverProps}\n data-size={size}\n data-type={type}\n data-theme={theme}\n mods={finalMods}\n styles={wrapperStyles}\n style={\n {\n '--actions-width':\n areActionsVisible || !autoHideActions\n ? `${actionsWidth}px`\n : '0px',\n ...(typeof size === 'number' && { '--size': `${size}px` }),\n } as CSSProperties\n }\n >\n {button}\n <ItemActionProvider\n type={type}\n theme={theme}\n disableActionsFocus={disableActionsFocus}\n isDisabled={finalIsDisabled}\n >\n {autoHideActions ? (\n <DisplayTransition\n exposeUnmounted\n isShown={shouldShowActions}\n onPhaseChange={(phase) => {\n setAreActionsVisible(phase !== 'unmounted');\n }}\n onToggle={(isShown) => {\n setAreActionsShown(isShown);\n }}\n >\n {({ ref: transitionRef }) => {\n return (\n <div\n {...focusWithinProps}\n ref={(node: any) => {\n actionsRef.current = node;\n transitionRef(node);\n }}\n data-element=\"Actions\"\n >\n {actions}\n </div>\n );\n }}\n </DisplayTransition>\n ) : (\n <div ref={actionsRef} data-element=\"Actions\">\n {actions}\n </div>\n )}\n </ItemActionProvider>\n </ActionsWrapper>\n );\n }\n\n return button;\n});\n\nconst _ItemButton = Object.assign(ItemButton, {\n Action: ItemAction,\n Badge: ItemBadge,\n});\n\nexport { _ItemButton as ItemButton };\nexport type {\n CubeItemButtonProps as ItemButtonProps,\n CubeItemActionProps as ItemActionProps,\n};\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { Button } from "../Button/Button.js";
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { extractStyles } from "../../../utils/styles.js";
|
|
3
3
|
import { mergeProps as mergeProps$1 } from "../../../utils/react/mergeProps.js";
|
|
4
4
|
import { _CollectionItem } from "../../CollectionItem.js";
|
|
@@ -12,14 +12,13 @@ import { CONTAINER_STYLES, filterBaseProps } from "@tenphi/tasty";
|
|
|
12
12
|
import React, { useMemo } from "react";
|
|
13
13
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
14
|
import { useMenu } from "react-aria";
|
|
15
|
-
import { useDOMRef } from "@react-spectrum/utils";
|
|
16
15
|
import { Section, useTreeState } from "react-stately";
|
|
17
|
-
import { useSyncRef } from "@react-aria/utils";
|
|
16
|
+
import { useObjectRef, useSyncRef } from "@react-aria/utils";
|
|
18
17
|
|
|
19
18
|
//#region src/components/actions/Menu/Menu.tsx
|
|
20
19
|
function Menu(props, ref) {
|
|
21
20
|
const { header, footer, menuStyles, headerStyles, footerStyles, itemStyles, sectionStyles, sectionHeadingStyles, size = "medium", focusOnHover = false, qa, selectedKeys, defaultSelectedKeys, onSelectionChange, ...rest } = props;
|
|
22
|
-
const domRef =
|
|
21
|
+
const domRef = useObjectRef(ref);
|
|
23
22
|
const contextProps = useMenuContext();
|
|
24
23
|
const completeProps = mergeProps$1(contextProps, rest, {
|
|
25
24
|
focusOnHover,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.js","names":["mergeProps","Section","BaseSection","CollectionItem","MenuTrigger"],"sources":["../../../../src/components/actions/Menu/Menu.tsx"],"sourcesContent":["import { useSyncRef } from '@react-aria/utils';\nimport { useDOMRef } from '@react-spectrum/utils';\nimport { DOMRef, FocusStrategy, ItemProps } from '@react-types/shared';\nimport {\n BasePropsWithoutChildren,\n CONTAINER_STYLES,\n ContainerStyleProps,\n filterBaseProps,\n Styles,\n} from '@tenphi/tasty';\nimport React, { ReactElement, ReactNode, useMemo } from 'react';\nimport { AriaMenuProps, useMenu } from 'react-aria';\nimport { Section as BaseSection, useTreeState } from 'react-stately';\n\nimport { mergeProps } from '../../../utils/react';\nimport { extractStyles } from '../../../utils/styles';\nimport { CollectionItem } from '../../CollectionItem';\n\nimport { useMenuContext } from './context';\nimport { MenuItem } from './MenuItem';\nimport { MenuSection } from './MenuSection';\nimport { MenuTrigger } from './MenuTrigger';\nimport {\n StyledDivider,\n StyledFooter,\n StyledHeader,\n StyledMenu,\n StyledMenuWrapper,\n} from './styled';\nimport { SubMenuTrigger } from './SubMenuTrigger';\n\nexport interface CubeMenuProps<T>\n extends BasePropsWithoutChildren,\n ContainerStyleProps,\n Omit<\n AriaMenuProps<T>,\n 'selectedKeys' | 'defaultSelectedKeys' | 'onSelectionChange'\n > {\n // @deprecated\n header?: ReactNode;\n footer?: ReactNode;\n menuStyles?: Styles;\n headerStyles?: Styles;\n footerStyles?: Styles;\n styles?: Styles;\n itemStyles?: Styles;\n sectionStyles?: Styles;\n sectionHeadingStyles?: Styles;\n /**\n * Whether keyboard navigation should wrap around when reaching the start/end of the collection.\n * This directly maps to the `shouldFocusWrap` option supported by React-Aria's `useMenu` hook.\n */\n shouldFocusWrap?: boolean;\n\n /**\n * Whether the menu should automatically receive focus when it mounts.\n * This directly maps to the `autoFocus` option supported by React-Aria's `useMenu` hook.\n */\n autoFocus?: boolean | FocusStrategy;\n shouldUseVirtualFocus?: boolean;\n\n /** Size of the menu items */\n size?: 'medium' | 'large' | (string & {});\n\n /** Currently selected keys (controlled) */\n selectedKeys?: string[];\n /** Initially selected keys (uncontrolled) */\n defaultSelectedKeys?: string[];\n /** Handler for selection changes */\n onSelectionChange?: (keys: string[]) => void;\n}\n\nfunction Menu<T extends object>(\n props: CubeMenuProps<T>,\n ref: DOMRef<HTMLUListElement>,\n) {\n const {\n header,\n footer,\n menuStyles,\n headerStyles,\n footerStyles,\n itemStyles,\n sectionStyles,\n sectionHeadingStyles,\n size = 'medium',\n focusOnHover = false,\n qa,\n selectedKeys,\n defaultSelectedKeys,\n onSelectionChange,\n ...rest\n } = props;\n const domRef = useDOMRef(ref);\n const contextProps = useMenuContext();\n\n // Convert string[] to Set<Key> for React Aria compatibility\n const ariaSelectedKeys = selectedKeys ? new Set(selectedKeys) : undefined;\n const ariaDefaultSelectedKeys = defaultSelectedKeys\n ? new Set(defaultSelectedKeys)\n : undefined;\n\n const handleSelectionChange = onSelectionChange\n ? (keys: any) => {\n if (keys === 'all') {\n // Handle 'all' selection case - collect all available keys\n const allKeys = Array.from(state.collection.getKeys()).map(\n (key: any) => String(key),\n );\n onSelectionChange(allKeys);\n } else if (keys instanceof Set) {\n onSelectionChange(Array.from(keys).map((key) => String(key)));\n } else {\n onSelectionChange([]);\n }\n }\n : undefined;\n\n const completeProps = mergeProps(contextProps, rest, {\n focusOnHover,\n selectedKeys: ariaSelectedKeys,\n defaultSelectedKeys: ariaDefaultSelectedKeys,\n onSelectionChange: handleSelectionChange,\n });\n\n // Props used for collection building.\n const treeProps = completeProps as typeof completeProps;\n\n const state = useTreeState(treeProps as typeof completeProps);\n const collectionItems = [...state.collection];\n const hasSections = collectionItems.some((item) => item.type === 'section');\n\n const { menuProps } = useMenu(completeProps, state, domRef);\n const styles = useMemo(\n () => extractStyles(completeProps, CONTAINER_STYLES),\n [completeProps],\n );\n\n const wrapperMods = useMemo(() => {\n return {\n popover: completeProps.mods?.popover,\n footer: !!footer,\n header: !!header,\n };\n }, [completeProps.mods?.popover, footer, header]);\n\n const menuMods = useMemo(() => {\n return {\n sections: hasSections,\n };\n }, [hasSections]);\n\n // Sync the ref stored in the context object with the DOM ref returned by useDOMRef.\n // The helper from @react-aria/utils expects the context object as the first argument\n // to keep it up-to-date, and a ref object as the second.\n useSyncRef(contextProps, domRef);\n\n const renderedItems = useMemo(() => {\n const items: React.ReactNode[] = [];\n let isFirstSection = true;\n\n collectionItems.forEach((item) => {\n if (item.type === 'section') {\n if (!isFirstSection) {\n items.push(\n <StyledDivider\n key={`divider-${String(item.key)}`}\n role=\"separator\"\n aria-orientation=\"horizontal\"\n />,\n );\n }\n\n items.push(\n <MenuSection\n key={item.key}\n item={item}\n state={state}\n styles={sectionStyles}\n itemStyles={itemStyles}\n headingStyles={sectionHeadingStyles}\n size={size}\n />,\n );\n\n isFirstSection = false;\n return;\n }\n\n let menuItem = (\n <MenuItem\n key={item.key}\n item={item}\n state={state}\n styles={itemStyles}\n size={size}\n onAction={item.onAction}\n />\n );\n\n // Apply tooltip wrapper if tooltip property is provided\n // if (item.props.tooltip) {\n // const tooltipProps =\n // typeof item.props.tooltip === 'string'\n // ? { title: item.props.tooltip }\n // : item.props.tooltip;\n\n // menuItem = (\n // <TooltipProvider key={item.key} placement=\"right\" {...tooltipProps}>\n // {menuItem}\n // </TooltipProvider>\n // );\n // }\n\n // Apply custom wrapper if provided\n if (item.props?.wrapper) {\n menuItem = item.props.wrapper(menuItem);\n } else if ((item as any).wrapper) {\n // Handle wrapper from collection nodes (e.g., SubMenuTrigger)\n menuItem = (item as any).wrapper(menuItem);\n }\n\n // Ensure every child has a stable key, even if the wrapper component didn't set one.\n items.push(React.cloneElement(menuItem, { key: item.key }));\n });\n\n return items;\n }, [collectionItems, state, sectionStyles, itemStyles, sectionHeadingStyles]);\n\n return (\n <StyledMenuWrapper\n qa={qa}\n styles={styles}\n mods={wrapperMods}\n {...(filterBaseProps(completeProps) as Record<string, unknown>)}\n >\n {header ? (\n <StyledHeader data-size={size} styles={headerStyles}>\n {header}\n </StyledHeader>\n ) : (\n <div role=\"presentation\" />\n )}\n <StyledMenu\n {...mergeProps(\n {\n styles: menuStyles,\n 'data-size': size,\n mods: menuMods,\n },\n menuProps,\n )}\n ref={domRef}\n role={menuProps.role ?? 'menu'}\n >\n {renderedItems}\n </StyledMenu>\n {footer ? (\n <StyledFooter data-size={size} styles={footerStyles}>\n {footer}\n </StyledFooter>\n ) : (\n <div role=\"presentation\" />\n )}\n </StyledMenuWrapper>\n );\n}\n\n// forwardRef doesn't support generic parameters, so cast the result to the correct type\n// https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref\nconst _Menu = React.forwardRef(Menu) as <T>(\n props: CubeMenuProps<T> & React.RefAttributes<HTMLUListElement>,\n) => ReactElement;\n\ntype SectionComponent = typeof BaseSection;\n\nconst Section = Object.assign(BaseSection, {\n displayName: 'Section',\n}) as SectionComponent;\n\ntype __MenuComponent = typeof _Menu & {\n Item: typeof CollectionItem;\n Section: typeof Section;\n SubMenuTrigger: typeof SubMenuTrigger;\n Trigger: typeof MenuTrigger;\n};\n\nconst __Menu = Object.assign(_Menu as __MenuComponent, {\n Item: CollectionItem,\n Section,\n SubMenuTrigger,\n Trigger: MenuTrigger,\n displayName: 'Menu',\n});\n\n__Menu.displayName = 'Menu';\n\nexport { __Menu as Menu };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.js","names":["mergeProps","Section","BaseSection","CollectionItem","MenuTrigger"],"sources":["../../../../src/components/actions/Menu/Menu.tsx"],"sourcesContent":["import { useObjectRef, useSyncRef } from '@react-aria/utils';\nimport { FocusStrategy, ItemProps } from '@react-types/shared';\nimport {\n BasePropsWithoutChildren,\n CONTAINER_STYLES,\n ContainerStyleProps,\n filterBaseProps,\n Styles,\n} from '@tenphi/tasty';\nimport React, { ReactElement, ReactNode, Ref, useMemo } from 'react';\nimport { AriaMenuProps, useMenu } from 'react-aria';\nimport { Section as BaseSection, useTreeState } from 'react-stately';\n\nimport { mergeProps } from '../../../utils/react';\nimport { extractStyles } from '../../../utils/styles';\nimport { CollectionItem } from '../../CollectionItem';\n\nimport { useMenuContext } from './context';\nimport { MenuItem } from './MenuItem';\nimport { MenuSection } from './MenuSection';\nimport { MenuTrigger } from './MenuTrigger';\nimport {\n StyledDivider,\n StyledFooter,\n StyledHeader,\n StyledMenu,\n StyledMenuWrapper,\n} from './styled';\nimport { SubMenuTrigger } from './SubMenuTrigger';\n\nexport interface CubeMenuProps<T>\n extends BasePropsWithoutChildren,\n ContainerStyleProps,\n Omit<\n AriaMenuProps<T>,\n 'selectedKeys' | 'defaultSelectedKeys' | 'onSelectionChange'\n > {\n // @deprecated\n header?: ReactNode;\n footer?: ReactNode;\n menuStyles?: Styles;\n headerStyles?: Styles;\n footerStyles?: Styles;\n styles?: Styles;\n itemStyles?: Styles;\n sectionStyles?: Styles;\n sectionHeadingStyles?: Styles;\n /**\n * Whether keyboard navigation should wrap around when reaching the start/end of the collection.\n * This directly maps to the `shouldFocusWrap` option supported by React-Aria's `useMenu` hook.\n */\n shouldFocusWrap?: boolean;\n\n /**\n * Whether the menu should automatically receive focus when it mounts.\n * This directly maps to the `autoFocus` option supported by React-Aria's `useMenu` hook.\n */\n autoFocus?: boolean | FocusStrategy;\n shouldUseVirtualFocus?: boolean;\n\n /** Size of the menu items */\n size?: 'medium' | 'large' | (string & {});\n\n /** Currently selected keys (controlled) */\n selectedKeys?: string[];\n /** Initially selected keys (uncontrolled) */\n defaultSelectedKeys?: string[];\n /** Handler for selection changes */\n onSelectionChange?: (keys: string[]) => void;\n}\n\nfunction Menu<T extends object>(\n props: CubeMenuProps<T>,\n ref: Ref<HTMLUListElement>,\n) {\n const {\n header,\n footer,\n menuStyles,\n headerStyles,\n footerStyles,\n itemStyles,\n sectionStyles,\n sectionHeadingStyles,\n size = 'medium',\n focusOnHover = false,\n qa,\n selectedKeys,\n defaultSelectedKeys,\n onSelectionChange,\n ...rest\n } = props;\n const domRef = useObjectRef(ref);\n const contextProps = useMenuContext();\n\n // Convert string[] to Set<Key> for React Aria compatibility\n const ariaSelectedKeys = selectedKeys ? new Set(selectedKeys) : undefined;\n const ariaDefaultSelectedKeys = defaultSelectedKeys\n ? new Set(defaultSelectedKeys)\n : undefined;\n\n const handleSelectionChange = onSelectionChange\n ? (keys: any) => {\n if (keys === 'all') {\n // Handle 'all' selection case - collect all available keys\n const allKeys = Array.from(state.collection.getKeys()).map(\n (key: any) => String(key),\n );\n onSelectionChange(allKeys);\n } else if (keys instanceof Set) {\n onSelectionChange(Array.from(keys).map((key) => String(key)));\n } else {\n onSelectionChange([]);\n }\n }\n : undefined;\n\n const completeProps = mergeProps(contextProps, rest, {\n focusOnHover,\n selectedKeys: ariaSelectedKeys,\n defaultSelectedKeys: ariaDefaultSelectedKeys,\n onSelectionChange: handleSelectionChange,\n });\n\n // Props used for collection building.\n const treeProps = completeProps as typeof completeProps;\n\n const state = useTreeState(treeProps as typeof completeProps);\n const collectionItems = [...state.collection];\n const hasSections = collectionItems.some((item) => item.type === 'section');\n\n const { menuProps } = useMenu(completeProps, state, domRef);\n const styles = useMemo(\n () => extractStyles(completeProps, CONTAINER_STYLES),\n [completeProps],\n );\n\n const wrapperMods = useMemo(() => {\n return {\n popover: completeProps.mods?.popover,\n footer: !!footer,\n header: !!header,\n };\n }, [completeProps.mods?.popover, footer, header]);\n\n const menuMods = useMemo(() => {\n return {\n sections: hasSections,\n };\n }, [hasSections]);\n\n // Sync the ref stored in the context object with the menu's DOM ref.\n // `useSyncRef` from @react-aria/utils expects the context object as the\n // first argument to keep it up-to-date, and a ref object as the second.\n useSyncRef(contextProps, domRef);\n\n const renderedItems = useMemo(() => {\n const items: React.ReactNode[] = [];\n let isFirstSection = true;\n\n collectionItems.forEach((item) => {\n if (item.type === 'section') {\n if (!isFirstSection) {\n items.push(\n <StyledDivider\n key={`divider-${String(item.key)}`}\n role=\"separator\"\n aria-orientation=\"horizontal\"\n />,\n );\n }\n\n items.push(\n <MenuSection\n key={item.key}\n item={item}\n state={state}\n styles={sectionStyles}\n itemStyles={itemStyles}\n headingStyles={sectionHeadingStyles}\n size={size}\n />,\n );\n\n isFirstSection = false;\n return;\n }\n\n let menuItem = (\n <MenuItem\n key={item.key}\n item={item}\n state={state}\n styles={itemStyles}\n size={size}\n onAction={item.onAction}\n />\n );\n\n // Apply tooltip wrapper if tooltip property is provided\n // if (item.props.tooltip) {\n // const tooltipProps =\n // typeof item.props.tooltip === 'string'\n // ? { title: item.props.tooltip }\n // : item.props.tooltip;\n\n // menuItem = (\n // <TooltipProvider key={item.key} placement=\"right\" {...tooltipProps}>\n // {menuItem}\n // </TooltipProvider>\n // );\n // }\n\n // Apply custom wrapper if provided\n if (item.props?.wrapper) {\n menuItem = item.props.wrapper(menuItem);\n } else if ((item as any).wrapper) {\n // Handle wrapper from collection nodes (e.g., SubMenuTrigger)\n menuItem = (item as any).wrapper(menuItem);\n }\n\n // Ensure every child has a stable key, even if the wrapper component didn't set one.\n items.push(React.cloneElement(menuItem, { key: item.key }));\n });\n\n return items;\n }, [collectionItems, state, sectionStyles, itemStyles, sectionHeadingStyles]);\n\n return (\n <StyledMenuWrapper\n qa={qa}\n styles={styles}\n mods={wrapperMods}\n {...(filterBaseProps(completeProps) as Record<string, unknown>)}\n >\n {header ? (\n <StyledHeader data-size={size} styles={headerStyles}>\n {header}\n </StyledHeader>\n ) : (\n <div role=\"presentation\" />\n )}\n <StyledMenu\n {...mergeProps(\n {\n styles: menuStyles,\n 'data-size': size,\n mods: menuMods,\n },\n menuProps,\n )}\n ref={domRef}\n role={menuProps.role ?? 'menu'}\n >\n {renderedItems}\n </StyledMenu>\n {footer ? (\n <StyledFooter data-size={size} styles={footerStyles}>\n {footer}\n </StyledFooter>\n ) : (\n <div role=\"presentation\" />\n )}\n </StyledMenuWrapper>\n );\n}\n\n// forwardRef doesn't support generic parameters, so cast the result to the correct type\n// https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref\nconst _Menu = React.forwardRef(Menu) as <T>(\n props: CubeMenuProps<T> & React.RefAttributes<HTMLUListElement>,\n) => ReactElement;\n\ntype SectionComponent = typeof BaseSection;\n\nconst Section = Object.assign(BaseSection, {\n displayName: 'Section',\n}) as SectionComponent;\n\ntype __MenuComponent = typeof _Menu & {\n Item: typeof CollectionItem;\n Section: typeof Section;\n SubMenuTrigger: typeof SubMenuTrigger;\n Trigger: typeof MenuTrigger;\n};\n\nconst __Menu = Object.assign(_Menu as __MenuComponent, {\n Item: CollectionItem,\n Section,\n SubMenuTrigger,\n Trigger: MenuTrigger,\n displayName: 'Menu',\n});\n\n__Menu.displayName = 'Menu';\n\nexport { __Menu as Menu };\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { mergeProps as mergeProps$1 } from "../../../utils/react/mergeProps.js";
|
|
3
3
|
import { filterCollectionItemProps } from "../../CollectionItem.js";
|
|
4
4
|
import { _Item } from "../../content/Item/Item.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { TooltipProvider } from "../../overlays/Tooltip/TooltipProvider.js";
|
|
3
3
|
import { MenuItem } from "./MenuItem.js";
|
|
4
4
|
import { StyledMenu, StyledSection, StyledSectionHeading } from "./styled.js";
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { SlotProvider } from "../../../utils/react/Slots.js";
|
|
3
|
+
import { usePopoverSync } from "../../../utils/react/usePopoverSync.js";
|
|
3
4
|
import { useEvent } from "../../../_internal/hooks/use-event.js";
|
|
4
5
|
import { MenuContext } from "./context.js";
|
|
5
6
|
import { generateRandomId } from "../../../utils/random.js";
|
|
6
|
-
import { usePopoverSync } from "../../../utils/react/usePopoverSync.js";
|
|
7
7
|
import { _Tray } from "../../overlays/Modal/Tray.js";
|
|
8
8
|
import { _Popover } from "../../overlays/Modal/Popover.js";
|
|
9
9
|
import { Fragment, forwardRef, useEffect, useMemo, useRef } from "react";
|
|
10
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { DismissButton, FocusScope, useMenuTrigger, useOverlayPosition } from "react-aria";
|
|
12
|
-
import {
|
|
12
|
+
import { useIsMobileDevice } from "@react-spectrum/utils";
|
|
13
13
|
import { useMenuTriggerState } from "react-stately";
|
|
14
|
+
import { useObjectRef } from "@react-aria/utils";
|
|
14
15
|
import { PressResponder } from "@react-aria/interactions";
|
|
15
16
|
|
|
16
17
|
//#region src/components/actions/Menu/MenuTrigger.tsx
|
|
17
18
|
function MenuTrigger(props, ref) {
|
|
18
19
|
const menuPopoverRef = useRef(null);
|
|
19
20
|
const triggerRef = useRef(null);
|
|
20
|
-
const domRef =
|
|
21
|
+
const domRef = useObjectRef(ref);
|
|
21
22
|
const menuTriggerRef = props.targetRef || domRef || triggerRef;
|
|
22
23
|
const menuRef = useRef(null);
|
|
23
24
|
const wasOpenRef = useRef(false);
|
|
@@ -30,7 +31,9 @@ function MenuTrigger(props, ref) {
|
|
|
30
31
|
menuId,
|
|
31
32
|
isOpen: state.isOpen,
|
|
32
33
|
onClose: () => state.close(),
|
|
33
|
-
enabled: !isDummy
|
|
34
|
+
enabled: !isDummy,
|
|
35
|
+
triggerRef: menuTriggerRef,
|
|
36
|
+
containerRef: menuPopoverRef
|
|
34
37
|
});
|
|
35
38
|
useEffect(() => {
|
|
36
39
|
if (!state.isOpen && wasOpenRef.current && !isDummy) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuTrigger.js","names":["Tray","Popover"],"sources":["../../../../src/components/actions/Menu/MenuTrigger.tsx"],"sourcesContent":["import { PressResponder } from '@react-aria/interactions';\nimport { useDOMRef, useIsMobileDevice } from '@react-spectrum/utils';\nimport { DOMRef } from '@react-types/shared';\nimport {\n forwardRef,\n Fragment,\n ReactElement,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\nimport {\n AriaMenuTriggerProps,\n DismissButton,\n FocusScope,\n Placement,\n PositionProps,\n useMenuTrigger,\n useOverlayPosition,\n} from 'react-aria';\nimport { MenuTriggerState, useMenuTriggerState } from 'react-stately';\n\nimport { useEvent } from '../../../_internal';\nimport { generateRandomId } from '../../../utils/random';\nimport { SlotProvider } from '../../../utils/react';\nimport { usePopoverSync } from '../../../utils/react/usePopoverSync';\nimport { Popover, Tray } from '../../overlays/Modal';\n\nimport { MenuContext, MenuContextValue } from './context';\n\nexport type { AriaMenuTriggerProps };\n\nexport type CubeMenuTriggerProps = AriaMenuTriggerProps &\n PositionProps & {\n isDisabled?: boolean;\n children: [\n ReactElement | ((state: MenuTriggerState) => ReactElement),\n ReactElement,\n ];\n\n closeOnSelect?: boolean;\n isDummy?: boolean;\n /**\n * Overlay variant to use on mobile screens. Defaults to `'popover'`, which\n * keeps the desktop overlay even on small viewports. Pass `'tray'` to opt\n * into the bottom-sheet `Tray` overlay on mobile (the previous implicit\n * default). Mirrors the `mobileType` API on `DialogTrigger`.\n */\n mobileType?: 'popover' | 'tray';\n };\n\nfunction MenuTrigger(props: CubeMenuTriggerProps, ref: DOMRef<HTMLElement>) {\n const menuPopoverRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const domRef = useDOMRef(ref);\n const menuTriggerRef = props.targetRef || domRef || triggerRef;\n const menuRef = useRef<HTMLUListElement>(null);\n const wasOpenRef = useRef(false);\n const {\n children,\n shouldFlip = true,\n closeOnSelect,\n trigger = 'press',\n isDisabled,\n isDummy,\n mobileType = 'popover',\n } = props;\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n if (!Array.isArray(children) || children.length > 2) {\n throw new Error('MenuTrigger must have exactly 2 children');\n }\n\n let [menuTrigger, menu] = children;\n const state: MenuTriggerState = useMenuTriggerState(props);\n\n usePopoverSync({\n menuId,\n isOpen: state.isOpen,\n onClose: () => state.close(),\n enabled: !isDummy,\n });\n\n // Restore focus manually when the menu closes\n useEffect(() => {\n if (!state.isOpen && wasOpenRef.current && !isDummy) {\n wasOpenRef.current = false;\n // Use setTimeout to ensure focus restoration happens after any animations\n setTimeout(() => {\n menuTriggerRef.current?.focus();\n }, 0);\n } else if (state.isOpen) {\n wasOpenRef.current = true;\n }\n }, [state.isOpen, menuTriggerRef, isDummy]);\n\n if (typeof menuTrigger === 'function') {\n menuTrigger = (menuTrigger as CubeMenuTriggerProps['children'][0])(state);\n }\n\n const { menuTriggerProps, menuProps } = useMenuTrigger(\n { isDisabled },\n state,\n menuTriggerRef,\n );\n\n let initialPlacement: Placement = props.placement ?? 'bottom start';\n\n // Tray rendering is now opt-in via `mobileType=\"tray\"` (matches DialogTrigger).\n // Without that opt-in, MenuTrigger always renders a Popover so that environments\n // like jsdom (where `window.screen.width === 0` makes useIsMobileDevice() true)\n // don't accidentally swap in the tray overlay.\n const isMobileDevice = useIsMobileDevice();\n const isTray = mobileType === 'tray' && isMobileDevice;\n const { overlayProps: positionProps, placement } = useOverlayPosition({\n targetRef: menuTriggerRef,\n overlayRef: menuPopoverRef,\n scrollRef: menuRef,\n placement: initialPlacement,\n shouldFlip: shouldFlip,\n isOpen: state.isOpen && !isTray,\n onClose: state.close,\n containerPadding: props.containerPadding,\n offset: props.offset ?? 8,\n crossOffset: props.crossOffset ?? 0,\n });\n\n const menuContext = {\n ...menuProps,\n ref: menuRef,\n onClose: state.close,\n closeOnSelect,\n autoFocus: (state.focusStrategy as any) ?? 'first',\n style: isTray\n ? {\n width: '100%',\n maxHeight: 'inherit',\n }\n : undefined,\n mods: {\n popover: !isTray,\n tray: isTray,\n },\n isClosing: !state.isOpen,\n } as MenuContextValue;\n\n // Wrap in a FocusScope so the menu popover registers as a child of any\n // outer contained FocusScope (e.g. a parent Dialog/Popover). Without this,\n // when the menu opens inside an outer contained FocusScope, the outer\n // scope rejects focus moving into the menu items (which live in a portal\n // and would otherwise belong to no scope) and yanks focus back to the\n // menu trigger.\n const contents = (\n <FocusScope restoreFocus>\n <DismissButton onDismiss={state.close} />\n {menu}\n <DismissButton onDismiss={state.close} />\n </FocusScope>\n );\n\n // Shared between the Popover and Tray branches so both react-aria\n // `useOverlay` calls see the same predicate. Without this, the Tray branch\n // falls back to unconditional dismiss-on-outside-interaction, which\n // `useOverlay` translates into stopPropagation/preventDefault in the\n // capture phase — that swallows clicks on sibling triggers (see Menu\n // rapid-open test).\n //\n // `useEvent` gives us a single stable callback reference for the lifetime\n // of the component while always reading the latest closure values. This\n // matters because `useMenuTriggerState` returns a fresh `state` object on\n // every render, so a vanilla `useCallback([..., state])` would produce a\n // new function every render and defeat any stability guarantees consumers\n // rely on.\n const shouldCloseOnInteractOutside = useEvent((el: Element) => {\n // While `Popover` is animating out, `useInteractOutside`'s capture-phase\n // listener is still attached (jsdom 29+ uses pointerdown/click capture).\n // The animation lasts ~350ms; without this guard, clicks on a sibling\n // trigger during the exit window get stopPropagation()'d and the\n // sibling's `onClick` never runs — breaking rapid-open and \"open menu\n // again with new props\" flows. Reading `state.isOpen` directly is safe\n // because `useEvent` always sees the latest closure.\n if (!state.isOpen) return false;\n\n const menuTriggerEl = el.closest('[data-popover-trigger]');\n if (!menuTriggerEl) {\n // Plain interactive controls (Button, ItemButton) opt in via\n // `data-popover-dismiss`. We schedule the close via setTimeout(0) so\n // it lands AFTER the click event finishes — the button's onPress\n // fires first, then the popover closes. Without this, useOverlay\n // would stopPropagation() the click and the user would need a second\n // click to actually press the button.\n if (el.closest('[data-popover-dismiss]')) {\n setTimeout(() => state.close(), 0);\n return false;\n }\n return true;\n }\n if (\n isDummy &&\n (menuTriggerEl === menuTriggerRef.current ||\n menuTriggerRef.current?.contains(el))\n ) {\n return true;\n }\n if (menuTriggerEl === menuTriggerRef.current) return true;\n return false;\n });\n\n let overlay;\n if (isTray) {\n overlay = (\n <Tray\n isOpen={state.isOpen}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n >\n {contents}\n </Tray>\n );\n } else {\n overlay = (\n <Popover\n ref={menuPopoverRef}\n hideArrow\n isNonModal\n isOpen={state.isOpen}\n style={positionProps.style}\n placement={placement}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n >\n {contents}\n </Popover>\n );\n }\n\n return (\n <Fragment>\n <SlotProvider\n slots={{ actionButton: { holdAffordance: trigger === 'longPress' } }}\n >\n {!isDummy ? (\n <PressResponder\n {...menuTriggerProps}\n ref={menuTriggerRef}\n data-popover-trigger\n isPressed={state.isOpen}\n >\n {menuTrigger}\n </PressResponder>\n ) : null}\n </SlotProvider>\n <MenuContext.Provider value={menuContext}>{overlay}</MenuContext.Provider>\n </Fragment>\n );\n}\n\n/**\n * The MenuTrigger serves as a wrapper around a Menu and its associated trigger,\n * linking the Menu's open state with the trigger's press state.\n */\nlet _MenuTrigger = forwardRef(MenuTrigger);\n\n_MenuTrigger.displayName = 'MenuTrigger';\n\nexport { _MenuTrigger as MenuTrigger };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MenuTrigger.js","names":["Tray","Popover"],"sources":["../../../../src/components/actions/Menu/MenuTrigger.tsx"],"sourcesContent":["import { PressResponder } from '@react-aria/interactions';\nimport { useObjectRef } from '@react-aria/utils';\nimport { useIsMobileDevice } from '@react-spectrum/utils';\nimport {\n forwardRef,\n Fragment,\n ReactElement,\n Ref,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\nimport {\n AriaMenuTriggerProps,\n DismissButton,\n FocusScope,\n Placement,\n PositionProps,\n useMenuTrigger,\n useOverlayPosition,\n} from 'react-aria';\nimport { MenuTriggerState, useMenuTriggerState } from 'react-stately';\n\nimport { useEvent } from '../../../_internal';\nimport { generateRandomId } from '../../../utils/random';\nimport { SlotProvider } from '../../../utils/react';\nimport { usePopoverSync } from '../../../utils/react/usePopoverSync';\nimport { Popover, Tray } from '../../overlays/Modal';\n\nimport { MenuContext, MenuContextValue } from './context';\n\nexport type { AriaMenuTriggerProps };\n\nexport type CubeMenuTriggerProps = AriaMenuTriggerProps &\n PositionProps & {\n isDisabled?: boolean;\n children: [\n ReactElement | ((state: MenuTriggerState) => ReactElement),\n ReactElement,\n ];\n\n closeOnSelect?: boolean;\n isDummy?: boolean;\n /**\n * Overlay variant to use on mobile screens. Defaults to `'popover'`, which\n * keeps the desktop overlay even on small viewports. Pass `'tray'` to opt\n * into the bottom-sheet `Tray` overlay on mobile (the previous implicit\n * default). Mirrors the `mobileType` API on `DialogTrigger`.\n */\n mobileType?: 'popover' | 'tray';\n };\n\nfunction MenuTrigger(props: CubeMenuTriggerProps, ref: Ref<HTMLElement>) {\n const menuPopoverRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const domRef = useObjectRef(ref);\n const menuTriggerRef = props.targetRef || domRef || triggerRef;\n const menuRef = useRef<HTMLUListElement>(null);\n const wasOpenRef = useRef(false);\n const {\n children,\n shouldFlip = true,\n closeOnSelect,\n trigger = 'press',\n isDisabled,\n isDummy,\n mobileType = 'popover',\n } = props;\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n if (!Array.isArray(children) || children.length > 2) {\n throw new Error('MenuTrigger must have exactly 2 children');\n }\n\n let [menuTrigger, menu] = children;\n const state: MenuTriggerState = useMenuTriggerState(props);\n\n usePopoverSync({\n menuId,\n isOpen: state.isOpen,\n onClose: () => state.close(),\n enabled: !isDummy,\n triggerRef: menuTriggerRef,\n containerRef: menuPopoverRef,\n });\n\n // Restore focus manually when the menu closes\n useEffect(() => {\n if (!state.isOpen && wasOpenRef.current && !isDummy) {\n wasOpenRef.current = false;\n // Use setTimeout to ensure focus restoration happens after any animations\n setTimeout(() => {\n menuTriggerRef.current?.focus();\n }, 0);\n } else if (state.isOpen) {\n wasOpenRef.current = true;\n }\n }, [state.isOpen, menuTriggerRef, isDummy]);\n\n if (typeof menuTrigger === 'function') {\n menuTrigger = (menuTrigger as CubeMenuTriggerProps['children'][0])(state);\n }\n\n const { menuTriggerProps, menuProps } = useMenuTrigger(\n { isDisabled },\n state,\n menuTriggerRef,\n );\n\n let initialPlacement: Placement = props.placement ?? 'bottom start';\n\n // Tray rendering is now opt-in via `mobileType=\"tray\"` (matches DialogTrigger).\n // Without that opt-in, MenuTrigger always renders a Popover so that environments\n // like jsdom (where `window.screen.width === 0` makes useIsMobileDevice() true)\n // don't accidentally swap in the tray overlay.\n const isMobileDevice = useIsMobileDevice();\n const isTray = mobileType === 'tray' && isMobileDevice;\n const { overlayProps: positionProps, placement } = useOverlayPosition({\n targetRef: menuTriggerRef,\n overlayRef: menuPopoverRef,\n scrollRef: menuRef,\n placement: initialPlacement,\n shouldFlip: shouldFlip,\n isOpen: state.isOpen && !isTray,\n onClose: state.close,\n containerPadding: props.containerPadding,\n offset: props.offset ?? 8,\n crossOffset: props.crossOffset ?? 0,\n });\n\n const menuContext = {\n ...menuProps,\n ref: menuRef,\n onClose: state.close,\n closeOnSelect,\n autoFocus: (state.focusStrategy as any) ?? 'first',\n style: isTray\n ? {\n width: '100%',\n maxHeight: 'inherit',\n }\n : undefined,\n mods: {\n popover: !isTray,\n tray: isTray,\n },\n isClosing: !state.isOpen,\n } as MenuContextValue;\n\n // Wrap in a FocusScope so the menu popover registers as a child of any\n // outer contained FocusScope (e.g. a parent Dialog/Popover). Without this,\n // when the menu opens inside an outer contained FocusScope, the outer\n // scope rejects focus moving into the menu items (which live in a portal\n // and would otherwise belong to no scope) and yanks focus back to the\n // menu trigger.\n const contents = (\n <FocusScope restoreFocus>\n <DismissButton onDismiss={state.close} />\n {menu}\n <DismissButton onDismiss={state.close} />\n </FocusScope>\n );\n\n // Shared between the Popover and Tray branches so both react-aria\n // `useOverlay` calls see the same predicate. Without this, the Tray branch\n // falls back to unconditional dismiss-on-outside-interaction, which\n // `useOverlay` translates into stopPropagation/preventDefault in the\n // capture phase — that swallows clicks on sibling triggers (see Menu\n // rapid-open test).\n //\n // `useEvent` gives us a single stable callback reference for the lifetime\n // of the component while always reading the latest closure values. This\n // matters because `useMenuTriggerState` returns a fresh `state` object on\n // every render, so a vanilla `useCallback([..., state])` would produce a\n // new function every render and defeat any stability guarantees consumers\n // rely on.\n const shouldCloseOnInteractOutside = useEvent((el: Element) => {\n // While `Popover` is animating out, `useInteractOutside`'s capture-phase\n // listener is still attached (jsdom 29+ uses pointerdown/click capture).\n // The animation lasts ~350ms; without this guard, clicks on a sibling\n // trigger during the exit window get stopPropagation()'d and the\n // sibling's `onClick` never runs — breaking rapid-open and \"open menu\n // again with new props\" flows. Reading `state.isOpen` directly is safe\n // because `useEvent` always sees the latest closure.\n if (!state.isOpen) return false;\n\n const menuTriggerEl = el.closest('[data-popover-trigger]');\n if (!menuTriggerEl) {\n // Plain interactive controls (Button, ItemButton) opt in via\n // `data-popover-dismiss`. We schedule the close via setTimeout(0) so\n // it lands AFTER the click event finishes — the button's onPress\n // fires first, then the popover closes. Without this, useOverlay\n // would stopPropagation() the click and the user would need a second\n // click to actually press the button.\n if (el.closest('[data-popover-dismiss]')) {\n setTimeout(() => state.close(), 0);\n return false;\n }\n return true;\n }\n if (\n isDummy &&\n (menuTriggerEl === menuTriggerRef.current ||\n menuTriggerRef.current?.contains(el))\n ) {\n return true;\n }\n if (menuTriggerEl === menuTriggerRef.current) return true;\n return false;\n });\n\n let overlay;\n if (isTray) {\n overlay = (\n <Tray\n isOpen={state.isOpen}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n >\n {contents}\n </Tray>\n );\n } else {\n overlay = (\n <Popover\n ref={menuPopoverRef}\n hideArrow\n isNonModal\n isOpen={state.isOpen}\n style={positionProps.style}\n placement={placement}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n >\n {contents}\n </Popover>\n );\n }\n\n return (\n <Fragment>\n <SlotProvider\n slots={{ actionButton: { holdAffordance: trigger === 'longPress' } }}\n >\n {!isDummy ? (\n <PressResponder\n {...menuTriggerProps}\n ref={menuTriggerRef}\n data-popover-trigger\n isPressed={state.isOpen}\n >\n {menuTrigger}\n </PressResponder>\n ) : null}\n </SlotProvider>\n <MenuContext.Provider value={menuContext}>{overlay}</MenuContext.Provider>\n </Fragment>\n );\n}\n\n/**\n * The MenuTrigger serves as a wrapper around a Menu and its associated trigger,\n * linking the Menu's open state with the trigger's press state.\n */\nlet _MenuTrigger = forwardRef(MenuTrigger);\n\n_MenuTrigger.displayName = 'MenuTrigger';\n\nexport { _MenuTrigger as MenuTrigger };\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
|
+
import { usePopoverSync } from "../../../utils/react/usePopoverSync.js";
|
|
2
3
|
import { useEvent } from "../../../_internal/hooks/use-event.js";
|
|
3
4
|
import { MenuContext, useMenuContext } from "./context.js";
|
|
4
5
|
import { SubmenuTriggerContext } from "./SubmenuTriggerContext.js";
|
|
5
6
|
import { generateRandomId } from "../../../utils/random.js";
|
|
6
|
-
import { usePopoverSync } from "../../../utils/react/usePopoverSync.js";
|
|
7
7
|
import { _Popover } from "../../overlays/Modal/Popover.js";
|
|
8
8
|
import React, { useEffect, useMemo, useRef } from "react";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -23,14 +23,17 @@ function InternalSubMenuTrigger(props) {
|
|
|
23
23
|
const { children, placement = DEFAULT_PLACEMENT, offset = DEFAULT_OFFSET, crossOffset = DEFAULT_CROSS_OFFSET, shouldFlip = true, isDisabled, autoFocus = "first", onAction, targetKey, ...overlayProps } = props;
|
|
24
24
|
const [menuTrigger, menu] = React.Children.toArray(children);
|
|
25
25
|
const state = useMenuTriggerState(props);
|
|
26
|
-
|
|
27
|
-
menuId: useMemo(() => generateRandomId(), []),
|
|
28
|
-
isOpen: state.isOpen,
|
|
29
|
-
onClose: () => state.close()
|
|
30
|
-
});
|
|
26
|
+
const submenuId = useMemo(() => generateRandomId(), []);
|
|
31
27
|
const domTriggerRef = useRef(null);
|
|
32
28
|
const popoverRef = useRef(null);
|
|
33
29
|
const menuRef = useRef(null);
|
|
30
|
+
usePopoverSync({
|
|
31
|
+
menuId: submenuId,
|
|
32
|
+
isOpen: state.isOpen,
|
|
33
|
+
onClose: () => state.close(),
|
|
34
|
+
triggerRef: domTriggerRef,
|
|
35
|
+
containerRef: popoverRef
|
|
36
|
+
});
|
|
34
37
|
const { menuTriggerProps: rawTriggerProps, menuProps } = useMenuTrigger({
|
|
35
38
|
type: "submenu",
|
|
36
39
|
isDisabled
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubMenuTrigger.js","names":["Popover"],"sources":["../../../../src/components/actions/Menu/SubMenuTrigger.tsx"],"sourcesContent":["import { useSyncRef } from '@react-aria/utils';\nimport React, {\n Key,\n ReactElement,\n ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n AriaMenuTriggerProps,\n DismissButton,\n Placement,\n useMenuTrigger,\n useOverlayPosition,\n} from 'react-aria';\nimport { useMenuTriggerState } from 'react-stately';\n\nimport { useEvent } from '../../../_internal';\nimport { generateRandomId } from '../../../utils/random';\nimport { usePopoverSync } from '../../../utils/react/usePopoverSync';\nimport { Popover } from '../../overlays/Modal';\n\nimport { MenuContext, MenuContextValue, useMenuContext } from './context';\nimport { SubmenuTriggerContext } from './SubmenuTriggerContext';\n\n// Default placement & offset for sub-menus (matches Spectrum / Aria)\nconst DEFAULT_PLACEMENT: Placement = 'right top';\nconst DEFAULT_OFFSET = 4;\nconst DEFAULT_CROSS_OFFSET = -5;\n\nexport interface CubeSubMenuTriggerProps extends AriaMenuTriggerProps {\n /** The preferred placement of the sub-menu relative to the trigger */\n placement?: Placement;\n\n /** Distance in pixels between the trigger and sub-menu */\n offset?: number;\n\n /** Distance in pixels along the cross axis from the default alignment */\n crossOffset?: number;\n\n /** Whether the sub-menu should flip to the opposite side when it would overflow */\n shouldFlip?: boolean;\n\n /** Minimum padding in pixels between the sub-menu and viewport edges */\n containerPadding?: number;\n\n /**\n * Sub-menu trigger and the sub-menu itself. Must be exactly two React nodes.\n *\n * 1) Trigger – **must render `<li role=\"menuitem\">`** (usually `Menu.Item`).\n * 2) Sub-menu – a `<Menu>` element.\n */\n children: [ReactNode, ReactElement];\n\n /** Whether the trigger is disabled and cannot be activated */\n isDisabled?: boolean;\n\n /**\n * Callback fired when an action is selected from the sub-menu.\n * Bubbles the `onAction` event from the sub-menu to parent components.\n */\n onAction?: (key: Key) => void;\n\n /**\n * Controls focus behavior when the sub-menu opens.\n * - `true` or `'first'`: Focus the first item\n * - `'last'`: Focus the last item\n * - `false`: Don't auto-focus any item\n * @default 'first'\n */\n autoFocus?: boolean | 'first' | 'last';\n}\n\ninterface InternalSubMenuTriggerProps extends CubeSubMenuTriggerProps {\n targetKey?: Key;\n}\n\n/**\n * Internal SubMenuTrigger that receives the already-rendered trigger element\n * from the collection system and wraps it with submenu behavior.\n */\nfunction InternalSubMenuTrigger(props: InternalSubMenuTriggerProps) {\n const {\n children,\n placement = DEFAULT_PLACEMENT,\n offset = DEFAULT_OFFSET,\n crossOffset = DEFAULT_CROSS_OFFSET,\n shouldFlip = true,\n isDisabled,\n autoFocus = 'first',\n onAction,\n targetKey,\n ...overlayProps\n } = props;\n\n // Children: [menuTrigger (already rendered MenuItem), menu (Menu component)]\n const [menuTrigger, menu] = React.Children.toArray(children) as [\n ReactElement,\n ReactElement,\n ];\n\n const state = useMenuTriggerState(props as AriaMenuTriggerProps);\n\n // Generate a unique ID for this submenu instance\n const submenuId = useMemo(() => generateRandomId(), []);\n\n usePopoverSync({\n menuId: submenuId,\n isOpen: state.isOpen,\n onClose: () => state.close(),\n });\n\n // Refs – trigger (MenuItem <li>) and overlay (<div> from Popover)\n const domTriggerRef = useRef<HTMLElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Strip keyboard/press handlers that we will implement ourselves\n const { menuTriggerProps: rawTriggerProps, menuProps } = useMenuTrigger(\n { type: 'submenu', isDisabled },\n state,\n domTriggerRef,\n );\n\n // Get parent context to check if parent menu is closing\n const parentContext = useMenuContext();\n\n // Remove default onKeyDown/onPress handlers from trigger props – we implement custom ones\n const {\n onKeyDown: _mtOnKeyDown,\n onPress: _mtOnPress,\n onPressStart: _mtOnPressStart,\n onKeyUp: _mtOnKeyUp,\n ...menuTriggerProps\n } = rawTriggerProps;\n\n const { overlayProps: positionProps } = useOverlayPosition({\n targetRef: domTriggerRef,\n overlayRef: popoverRef,\n scrollRef: menuRef,\n placement,\n offset: offset as number,\n crossOffset: crossOffset as number,\n shouldFlip,\n isOpen: state.isOpen,\n onClose: state.close,\n });\n\n /**\n * Build a MenuContext for the nested menu so it behaves just like a regular\n * popover-based Menu (selection handling, focus management, etc.).\n */\n const nestedMenuContext = useMemo(() => {\n const ctx: MenuContextValue = {\n ...menuProps,\n ref: menuRef,\n // Pass the parent's onClose to close the entire menu hierarchy\n onClose: () => {\n // Close this submenu state immediately\n state.close();\n\n // Then close the parent menu\n if (parentContext.onClose) {\n parentContext.onClose();\n }\n },\n closeOnSelect: true,\n autoFocus,\n mods: {\n popover: true,\n },\n // Propagate closing state from parent\n isClosing: parentContext.isClosing || !state.isOpen,\n };\n\n return ctx;\n }, [\n menuProps,\n autoFocus,\n state,\n parentContext.onClose,\n parentContext.isClosing,\n ]);\n\n // Sync the parent selection manager focus with DOM ref (for virtual focus scenarios)\n useSyncRef(parentContext, domTriggerRef);\n\n // Cleanup hover timers and reset refs on unmount\n useEffect(() => {\n return () => {\n if (hoverOpenTimerRef.current) {\n clearTimeout(hoverOpenTimerRef.current);\n }\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n }\n isHoveringRef.current = false;\n isHoveringSubmenuRef.current = false;\n };\n }, []);\n\n // ----- Render -----\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n // Arrow right opens submenu\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n e.stopPropagation();\n state.open();\n }\n // Enter or Space opens submenu (suppress subsequent onAction)\n else if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n // Stop immediate propagation to prevent React Aria's handlers from running\n e.nativeEvent.stopImmediatePropagation();\n state.open();\n suppressNextActionRef.current = true; // Remember it was keyboard\n }\n // Arrow left closes submenu if open\n else if (e.key === 'ArrowLeft' && state.isOpen) {\n e.preventDefault();\n e.stopPropagation();\n state.close();\n }\n };\n\n // Use refs to store timer IDs\n const hoverOpenTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hoverCloseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Track if mouse is over trigger or submenu\n const isHoveringRef = useRef(false);\n const isHoveringSubmenuRef = useRef(false);\n\n // Track whether the next onAction comes from a keyboard press\n const suppressNextActionRef = useRef(false);\n\n const checkShouldClose = () => {\n // Only close if mouse is not over trigger OR submenu\n if (!isHoveringRef.current && !isHoveringSubmenuRef.current) {\n hoverCloseTimerRef.current = setTimeout(() => {\n // Double check before closing\n if (!isHoveringRef.current && !isHoveringSubmenuRef.current) {\n state.close();\n }\n }, 300);\n }\n };\n\n // Handle mouse interactions on trigger\n const handleMouseEnter = () => {\n if (isDisabled) return;\n\n isHoveringRef.current = true;\n\n // Clear any pending close timer\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n\n // Use a small delay to prevent accidental opens\n if (!state.isOpen) {\n hoverOpenTimerRef.current = setTimeout(() => {\n if (isHoveringRef.current) {\n state.open();\n }\n }, 200);\n }\n };\n\n const handleMouseLeave = () => {\n isHoveringRef.current = false;\n\n // Clear open timer if it exists\n if (hoverOpenTimerRef.current) {\n clearTimeout(hoverOpenTimerRef.current);\n hoverOpenTimerRef.current = null;\n }\n\n // Check if we should close\n checkShouldClose();\n };\n\n // Handle mouse events on the submenu popover\n const handlePopoverMouseEnter = () => {\n isHoveringSubmenuRef.current = true;\n\n // Clear any pending close timer when entering submenu\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n };\n\n const handlePopoverMouseLeave = () => {\n isHoveringSubmenuRef.current = false;\n\n // Check if we should close\n checkShouldClose();\n };\n\n // Merge event handlers\n const mergeHandler = (handler1: any, handler2: any) => {\n return (...args: any[]) => {\n handler1?.(...args);\n handler2?.(...args);\n };\n };\n\n // Outside-click handling for the nested submenu Popover.\n //\n // React Aria's `useOverlay` keeps a module-level `visibleOverlays` stack and\n // only invokes `onClose` for the topmost overlay (`onHide` gates on the\n // stack tail). With root + N nested submenus open, a single outside click\n // would otherwise only close the deepest submenu. Scheduling\n // `parentContext.onClose()` here propagates the close upward; the existing\n // `parentContext.isClosing` cascade (see `nestedMenuContext.isClosing` and\n // the Popover's `isOpen={!parentContext.isClosing}`) collapses every\n // intermediate submenu visually.\n //\n // Mirrors `MenuTrigger`'s predicate so clicks on `[data-popover-trigger]`\n // and `[data-popover-dismiss]` elements behave consistently across nesting\n // levels.\n const shouldCloseOnInteractOutside = useEvent((el: Element) => {\n if (!state.isOpen) return false;\n\n const menuTriggerEl = el.closest('[data-popover-trigger]');\n if (!menuTriggerEl) {\n if (el.closest('[data-popover-dismiss]')) {\n // Let the button's onPress fire first, then collapse the chain.\n setTimeout(() => parentContext.onClose?.(), 0);\n return false;\n }\n // True outside click: schedule parent close so the entire hierarchy\n // cascades. `useOverlay`'s own `onHide` will close this submenu via the\n // topmost-overlay rule.\n setTimeout(() => parentContext.onClose?.(), 0);\n return true;\n }\n // Click landed on a popover trigger (parent menu item, sibling submenu\n // trigger, or a different menu's button). Don't close ourselves here —\n // either the trigger handles it or `usePopoverSync` closes us via\n // `popover:open`.\n return false;\n });\n\n // Provide context to the trigger element (already rendered MenuItem)\n const triggerContextValue = useMemo(\n () => ({\n triggerRef: domTriggerRef,\n triggerProps: menuTriggerProps,\n isOpen: state.isOpen,\n isDisabled,\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onKeyDown: handleKeyDown,\n onAction: isDisabled\n ? undefined\n : () => {\n // Ignore the synthetic press generated by the same Enter/Space\n if (suppressNextActionRef.current) {\n suppressNextActionRef.current = false;\n return;\n }\n // This is a mouse click, open the submenu\n state.open();\n },\n }),\n [\n state,\n isDisabled,\n handleMouseEnter,\n handleMouseLeave,\n handleKeyDown,\n menuTriggerProps,\n ],\n );\n\n return (\n <>\n <SubmenuTriggerContext.Provider value={triggerContextValue}>\n {menuTrigger}\n </SubmenuTriggerContext.Provider>\n {state.isOpen && (\n <Popover\n ref={popoverRef}\n hideArrow\n isNonModal\n isOpen={!parentContext.isClosing}\n style={positionProps.style}\n placement={placement as Placement}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n // Spread any additional overlay props\n {...overlayProps}\n >\n {/* Wrap content in a div to capture mouse events */}\n <div\n style={{ display: 'contents' }}\n onMouseEnter={handlePopoverMouseEnter}\n onMouseLeave={handlePopoverMouseLeave}\n >\n {/* Dismiss buttons for screen reader / keyboard support */}\n <DismissButton onDismiss={state.close} />\n {/* Provide updated menu context so nested menu behaves correctly */}\n <MenuContext.Provider value={nestedMenuContext}>\n {/* Clone nested menu to inject required props */}\n {React.cloneElement(menu, {\n ...(menu.props as any),\n autoFocus,\n onAction: (key: Key) => {\n // Call original menu onAction first\n (menu.props as any).onAction?.(key);\n onAction?.(key);\n // Don't close here - MenuItem will handle it via context.onClose\n },\n onKeyDown: (e: React.KeyboardEvent) => {\n // Handle keyboard navigation for closing submenu\n if (e.key === 'ArrowLeft' || e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n state.close();\n // Return focus to trigger\n domTriggerRef.current?.focus();\n }\n // Call original handler if exists\n (menu.props as any).onKeyDown?.(e);\n },\n ref: menuRef,\n })}\n </MenuContext.Provider>\n <DismissButton onDismiss={state.close} />\n </div>\n </Popover>\n )}\n </>\n );\n}\n\n/**\n * Public SubMenuTrigger component that users interact with.\n * It just provides the getCollectionNode for the collection system.\n */\nexport function SubMenuTrigger(props: CubeSubMenuTriggerProps) {\n // This component is never actually rendered directly.\n // The collection system uses getCollectionNode to build the menu.\n return null;\n}\n\n// Allow React Stately collection builder to treat SubMenuTrigger as an Item\n(SubMenuTrigger as any).getCollectionNode = function* (\n props: CubeSubMenuTriggerProps,\n) {\n const [trigger, menu] = React.Children.toArray(\n props.children,\n ) as ReactElement[];\n\n // Yield a collection node that tells the Menu to wrap this item with InternalSubMenuTrigger\n yield {\n element: trigger,\n wrapper: (element: ReactElement) => (\n <InternalSubMenuTrigger\n key={element.key || undefined}\n targetKey={element.key || undefined}\n {...props}\n >\n {element}\n {menu}\n </InternalSubMenuTrigger>\n ),\n };\n};\n\nSubMenuTrigger.displayName = 'SubMenuTrigger';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SubMenuTrigger.js","names":["Popover"],"sources":["../../../../src/components/actions/Menu/SubMenuTrigger.tsx"],"sourcesContent":["import { useSyncRef } from '@react-aria/utils';\nimport React, {\n Key,\n ReactElement,\n ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n AriaMenuTriggerProps,\n DismissButton,\n Placement,\n useMenuTrigger,\n useOverlayPosition,\n} from 'react-aria';\nimport { useMenuTriggerState } from 'react-stately';\n\nimport { useEvent } from '../../../_internal';\nimport { generateRandomId } from '../../../utils/random';\nimport { usePopoverSync } from '../../../utils/react/usePopoverSync';\nimport { Popover } from '../../overlays/Modal';\n\nimport { MenuContext, MenuContextValue, useMenuContext } from './context';\nimport { SubmenuTriggerContext } from './SubmenuTriggerContext';\n\n// Default placement & offset for sub-menus (matches Spectrum / Aria)\nconst DEFAULT_PLACEMENT: Placement = 'right top';\nconst DEFAULT_OFFSET = 4;\nconst DEFAULT_CROSS_OFFSET = -5;\n\nexport interface CubeSubMenuTriggerProps extends AriaMenuTriggerProps {\n /** The preferred placement of the sub-menu relative to the trigger */\n placement?: Placement;\n\n /** Distance in pixels between the trigger and sub-menu */\n offset?: number;\n\n /** Distance in pixels along the cross axis from the default alignment */\n crossOffset?: number;\n\n /** Whether the sub-menu should flip to the opposite side when it would overflow */\n shouldFlip?: boolean;\n\n /** Minimum padding in pixels between the sub-menu and viewport edges */\n containerPadding?: number;\n\n /**\n * Sub-menu trigger and the sub-menu itself. Must be exactly two React nodes.\n *\n * 1) Trigger – **must render `<li role=\"menuitem\">`** (usually `Menu.Item`).\n * 2) Sub-menu – a `<Menu>` element.\n */\n children: [ReactNode, ReactElement];\n\n /** Whether the trigger is disabled and cannot be activated */\n isDisabled?: boolean;\n\n /**\n * Callback fired when an action is selected from the sub-menu.\n * Bubbles the `onAction` event from the sub-menu to parent components.\n */\n onAction?: (key: Key) => void;\n\n /**\n * Controls focus behavior when the sub-menu opens.\n * - `true` or `'first'`: Focus the first item\n * - `'last'`: Focus the last item\n * - `false`: Don't auto-focus any item\n * @default 'first'\n */\n autoFocus?: boolean | 'first' | 'last';\n}\n\ninterface InternalSubMenuTriggerProps extends CubeSubMenuTriggerProps {\n targetKey?: Key;\n}\n\n/**\n * Internal SubMenuTrigger that receives the already-rendered trigger element\n * from the collection system and wraps it with submenu behavior.\n */\nfunction InternalSubMenuTrigger(props: InternalSubMenuTriggerProps) {\n const {\n children,\n placement = DEFAULT_PLACEMENT,\n offset = DEFAULT_OFFSET,\n crossOffset = DEFAULT_CROSS_OFFSET,\n shouldFlip = true,\n isDisabled,\n autoFocus = 'first',\n onAction,\n targetKey,\n ...overlayProps\n } = props;\n\n // Children: [menuTrigger (already rendered MenuItem), menu (Menu component)]\n const [menuTrigger, menu] = React.Children.toArray(children) as [\n ReactElement,\n ReactElement,\n ];\n\n const state = useMenuTriggerState(props as AriaMenuTriggerProps);\n\n // Generate a unique ID for this submenu instance\n const submenuId = useMemo(() => generateRandomId(), []);\n\n // Refs – trigger (MenuItem <li>) and overlay (<div> from Popover)\n const domTriggerRef = useRef<HTMLElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLUListElement>(null);\n\n usePopoverSync({\n menuId: submenuId,\n isOpen: state.isOpen,\n onClose: () => state.close(),\n triggerRef: domTriggerRef,\n containerRef: popoverRef,\n });\n\n // Strip keyboard/press handlers that we will implement ourselves\n const { menuTriggerProps: rawTriggerProps, menuProps } = useMenuTrigger(\n { type: 'submenu', isDisabled },\n state,\n domTriggerRef,\n );\n\n // Get parent context to check if parent menu is closing\n const parentContext = useMenuContext();\n\n // Remove default onKeyDown/onPress handlers from trigger props – we implement custom ones\n const {\n onKeyDown: _mtOnKeyDown,\n onPress: _mtOnPress,\n onPressStart: _mtOnPressStart,\n onKeyUp: _mtOnKeyUp,\n ...menuTriggerProps\n } = rawTriggerProps;\n\n const { overlayProps: positionProps } = useOverlayPosition({\n targetRef: domTriggerRef,\n overlayRef: popoverRef,\n scrollRef: menuRef,\n placement,\n offset: offset as number,\n crossOffset: crossOffset as number,\n shouldFlip,\n isOpen: state.isOpen,\n onClose: state.close,\n });\n\n /**\n * Build a MenuContext for the nested menu so it behaves just like a regular\n * popover-based Menu (selection handling, focus management, etc.).\n */\n const nestedMenuContext = useMemo(() => {\n const ctx: MenuContextValue = {\n ...menuProps,\n ref: menuRef,\n // Pass the parent's onClose to close the entire menu hierarchy\n onClose: () => {\n // Close this submenu state immediately\n state.close();\n\n // Then close the parent menu\n if (parentContext.onClose) {\n parentContext.onClose();\n }\n },\n closeOnSelect: true,\n autoFocus,\n mods: {\n popover: true,\n },\n // Propagate closing state from parent\n isClosing: parentContext.isClosing || !state.isOpen,\n };\n\n return ctx;\n }, [\n menuProps,\n autoFocus,\n state,\n parentContext.onClose,\n parentContext.isClosing,\n ]);\n\n // Sync the parent selection manager focus with DOM ref (for virtual focus scenarios)\n useSyncRef(parentContext, domTriggerRef);\n\n // Cleanup hover timers and reset refs on unmount\n useEffect(() => {\n return () => {\n if (hoverOpenTimerRef.current) {\n clearTimeout(hoverOpenTimerRef.current);\n }\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n }\n isHoveringRef.current = false;\n isHoveringSubmenuRef.current = false;\n };\n }, []);\n\n // ----- Render -----\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n // Arrow right opens submenu\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n e.stopPropagation();\n state.open();\n }\n // Enter or Space opens submenu (suppress subsequent onAction)\n else if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n // Stop immediate propagation to prevent React Aria's handlers from running\n e.nativeEvent.stopImmediatePropagation();\n state.open();\n suppressNextActionRef.current = true; // Remember it was keyboard\n }\n // Arrow left closes submenu if open\n else if (e.key === 'ArrowLeft' && state.isOpen) {\n e.preventDefault();\n e.stopPropagation();\n state.close();\n }\n };\n\n // Use refs to store timer IDs\n const hoverOpenTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const hoverCloseTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Track if mouse is over trigger or submenu\n const isHoveringRef = useRef(false);\n const isHoveringSubmenuRef = useRef(false);\n\n // Track whether the next onAction comes from a keyboard press\n const suppressNextActionRef = useRef(false);\n\n const checkShouldClose = () => {\n // Only close if mouse is not over trigger OR submenu\n if (!isHoveringRef.current && !isHoveringSubmenuRef.current) {\n hoverCloseTimerRef.current = setTimeout(() => {\n // Double check before closing\n if (!isHoveringRef.current && !isHoveringSubmenuRef.current) {\n state.close();\n }\n }, 300);\n }\n };\n\n // Handle mouse interactions on trigger\n const handleMouseEnter = () => {\n if (isDisabled) return;\n\n isHoveringRef.current = true;\n\n // Clear any pending close timer\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n\n // Use a small delay to prevent accidental opens\n if (!state.isOpen) {\n hoverOpenTimerRef.current = setTimeout(() => {\n if (isHoveringRef.current) {\n state.open();\n }\n }, 200);\n }\n };\n\n const handleMouseLeave = () => {\n isHoveringRef.current = false;\n\n // Clear open timer if it exists\n if (hoverOpenTimerRef.current) {\n clearTimeout(hoverOpenTimerRef.current);\n hoverOpenTimerRef.current = null;\n }\n\n // Check if we should close\n checkShouldClose();\n };\n\n // Handle mouse events on the submenu popover\n const handlePopoverMouseEnter = () => {\n isHoveringSubmenuRef.current = true;\n\n // Clear any pending close timer when entering submenu\n if (hoverCloseTimerRef.current) {\n clearTimeout(hoverCloseTimerRef.current);\n hoverCloseTimerRef.current = null;\n }\n };\n\n const handlePopoverMouseLeave = () => {\n isHoveringSubmenuRef.current = false;\n\n // Check if we should close\n checkShouldClose();\n };\n\n // Merge event handlers\n const mergeHandler = (handler1: any, handler2: any) => {\n return (...args: any[]) => {\n handler1?.(...args);\n handler2?.(...args);\n };\n };\n\n // Outside-click handling for the nested submenu Popover.\n //\n // React Aria's `useOverlay` keeps a module-level `visibleOverlays` stack and\n // only invokes `onClose` for the topmost overlay (`onHide` gates on the\n // stack tail). With root + N nested submenus open, a single outside click\n // would otherwise only close the deepest submenu. Scheduling\n // `parentContext.onClose()` here propagates the close upward; the existing\n // `parentContext.isClosing` cascade (see `nestedMenuContext.isClosing` and\n // the Popover's `isOpen={!parentContext.isClosing}`) collapses every\n // intermediate submenu visually.\n //\n // Mirrors `MenuTrigger`'s predicate so clicks on `[data-popover-trigger]`\n // and `[data-popover-dismiss]` elements behave consistently across nesting\n // levels.\n const shouldCloseOnInteractOutside = useEvent((el: Element) => {\n if (!state.isOpen) return false;\n\n const menuTriggerEl = el.closest('[data-popover-trigger]');\n if (!menuTriggerEl) {\n if (el.closest('[data-popover-dismiss]')) {\n // Let the button's onPress fire first, then collapse the chain.\n setTimeout(() => parentContext.onClose?.(), 0);\n return false;\n }\n // True outside click: schedule parent close so the entire hierarchy\n // cascades. `useOverlay`'s own `onHide` will close this submenu via the\n // topmost-overlay rule.\n setTimeout(() => parentContext.onClose?.(), 0);\n return true;\n }\n // Click landed on a popover trigger (parent menu item, sibling submenu\n // trigger, or a different menu's button). Don't close ourselves here —\n // either the trigger handles it or `usePopoverSync` closes us via\n // `popover:open`.\n return false;\n });\n\n // Provide context to the trigger element (already rendered MenuItem)\n const triggerContextValue = useMemo(\n () => ({\n triggerRef: domTriggerRef,\n triggerProps: menuTriggerProps,\n isOpen: state.isOpen,\n isDisabled,\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n onKeyDown: handleKeyDown,\n onAction: isDisabled\n ? undefined\n : () => {\n // Ignore the synthetic press generated by the same Enter/Space\n if (suppressNextActionRef.current) {\n suppressNextActionRef.current = false;\n return;\n }\n // This is a mouse click, open the submenu\n state.open();\n },\n }),\n [\n state,\n isDisabled,\n handleMouseEnter,\n handleMouseLeave,\n handleKeyDown,\n menuTriggerProps,\n ],\n );\n\n return (\n <>\n <SubmenuTriggerContext.Provider value={triggerContextValue}>\n {menuTrigger}\n </SubmenuTriggerContext.Provider>\n {state.isOpen && (\n <Popover\n ref={popoverRef}\n hideArrow\n isNonModal\n isOpen={!parentContext.isClosing}\n style={positionProps.style}\n placement={placement as Placement}\n shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n onClose={state.close}\n // Spread any additional overlay props\n {...overlayProps}\n >\n {/* Wrap content in a div to capture mouse events */}\n <div\n style={{ display: 'contents' }}\n onMouseEnter={handlePopoverMouseEnter}\n onMouseLeave={handlePopoverMouseLeave}\n >\n {/* Dismiss buttons for screen reader / keyboard support */}\n <DismissButton onDismiss={state.close} />\n {/* Provide updated menu context so nested menu behaves correctly */}\n <MenuContext.Provider value={nestedMenuContext}>\n {/* Clone nested menu to inject required props */}\n {React.cloneElement(menu, {\n ...(menu.props as any),\n autoFocus,\n onAction: (key: Key) => {\n // Call original menu onAction first\n (menu.props as any).onAction?.(key);\n onAction?.(key);\n // Don't close here - MenuItem will handle it via context.onClose\n },\n onKeyDown: (e: React.KeyboardEvent) => {\n // Handle keyboard navigation for closing submenu\n if (e.key === 'ArrowLeft' || e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n state.close();\n // Return focus to trigger\n domTriggerRef.current?.focus();\n }\n // Call original handler if exists\n (menu.props as any).onKeyDown?.(e);\n },\n ref: menuRef,\n })}\n </MenuContext.Provider>\n <DismissButton onDismiss={state.close} />\n </div>\n </Popover>\n )}\n </>\n );\n}\n\n/**\n * Public SubMenuTrigger component that users interact with.\n * It just provides the getCollectionNode for the collection system.\n */\nexport function SubMenuTrigger(props: CubeSubMenuTriggerProps) {\n // This component is never actually rendered directly.\n // The collection system uses getCollectionNode to build the menu.\n return null;\n}\n\n// Allow React Stately collection builder to treat SubMenuTrigger as an Item\n(SubMenuTrigger as any).getCollectionNode = function* (\n props: CubeSubMenuTriggerProps,\n) {\n const [trigger, menu] = React.Children.toArray(\n props.children,\n ) as ReactElement[];\n\n // Yield a collection node that tells the Menu to wrap this item with InternalSubMenuTrigger\n yield {\n element: trigger,\n wrapper: (element: ReactElement) => (\n <InternalSubMenuTrigger\n key={element.key || undefined}\n targetKey={element.key || undefined}\n {...props}\n >\n {element}\n {menu}\n </InternalSubMenuTrigger>\n ),\n };\n};\n\nSubMenuTrigger.displayName = 'SubMenuTrigger';\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { DEFAULT_CLEAR_STYLES } from "../../../data/item-themes.js";
|
|
3
3
|
import { Space } from "../../layout/Space.js";
|
|
4
4
|
import { DEFAULT_BUTTON_STYLES } from "../Button/Button.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { ItemActionProvider, useItemActionContext } from "./ItemActionContext.js";
|
|
3
3
|
import { openLink, parseTo, performClickHandler, useAction } from "./use-action.js";
|
|
4
4
|
import { ItemAction } from "./ItemAction/ItemAction.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { mergeProps as mergeProps$1 } from "../../utils/react/mergeProps.js";
|
|
3
3
|
import { useEvent } from "../../_internal/hooks/use-event.js";
|
|
4
4
|
import { useFocus as useFocus$1 } from "../../utils/react/interactions.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { mergeProps as mergeProps$1 } from "../../utils/react/mergeProps.js";
|
|
3
|
+
import { usePopoverSync } from "../../utils/react/usePopoverSync.js";
|
|
3
4
|
import { useEvent } from "../../_internal/hooks/use-event.js";
|
|
4
5
|
import { generateRandomId } from "../../utils/random.js";
|
|
5
|
-
import { usePopoverSync } from "../../utils/react/usePopoverSync.js";
|
|
6
6
|
import { _MenuTrigger } from "./Menu/MenuTrigger.js";
|
|
7
7
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -36,7 +36,8 @@ function useAnchoredMenu(Component, defaultTriggerProps, defaultMenuProps) {
|
|
|
36
36
|
usePopoverSync({
|
|
37
37
|
menuId: useMemo(() => generateRandomId(), []),
|
|
38
38
|
isOpen,
|
|
39
|
-
onClose: () => setIsOpen(false)
|
|
39
|
+
onClose: () => setIsOpen(false),
|
|
40
|
+
triggerRef: anchorRef
|
|
40
41
|
});
|
|
41
42
|
function setupCheck() {
|
|
42
43
|
if (!setupRef.current) throw new Error("useAnchoredMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-anchored-menu.js","names":["MenuTrigger","mergeProps"],"sources":["../../../src/components/actions/use-anchored-menu.tsx"],"sourcesContent":["import { Pressable } from '@react-aria/interactions';\nimport {\n ComponentProps,\n ComponentType,\n ReactElement,\n RefObject,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { VisuallyHidden } from 'react-aria';\n\nimport { useEvent } from '../../_internal';\nimport { generateRandomId } from '../../utils/random';\nimport { mergeProps } from '../../utils/react';\nimport { usePopoverSync } from '../../utils/react/usePopoverSync';\n\nimport { MenuTrigger } from './Menu';\n\nexport interface UseAnchoredMenuReturn<P, T> {\n /** Ref to attach to the anchor element for positioning the menu. */\n anchorRef: RefObject<HTMLElement | null>;\n\n /**\n * Programmatically opens the menu with the provided props.\n * @param props - Props to pass to the menu component\n * @param triggerProps - Additional props for MenuTrigger (merged with defaultTriggerProps)\n */\n open(props?: P, triggerProps?: T): void;\n\n /**\n * Updates the props of the currently open menu.\n * Props are merged if defaults are provided.\n */\n update(props: P, triggerProps?: T): void;\n\n /** Closes the menu programmatically. */\n close(): void;\n\n /** Current open/closed state of the menu. */\n isOpen: boolean;\n\n /**\n * JSX element that must be rendered in your component tree.\n * Contains the MenuTrigger and positioning logic.\n */\n get rendered(): ReactElement | null;\n}\n\n/**\n * Generic hook to manage an anchored menu component.\n *\n * @param Component - A React component that represents the menu content (Menu or CommandMenu).\n * @param defaultTriggerProps - Default props to pass to the MenuTrigger.\n * @param defaultMenuProps - Default props to pass to the Menu component.\n * @returns An object with `anchorRef` to position the menu, `open` function to open the menu with provided props, `close` function to close the menu, and `rendered` JSX element to include in your component tree.\n */\nexport function useAnchoredMenu<P, T = ComponentProps<typeof MenuTrigger>>(\n Component: ComponentType<P>,\n defaultTriggerProps?: Omit<\n ComponentProps<typeof MenuTrigger>,\n 'children' | 'isOpen' | 'onOpenChange' | 'targetRef'\n >,\n defaultMenuProps?: P,\n): UseAnchoredMenuReturn<P, T> {\n const [isOpen, setIsOpen] = useState(false);\n const [componentProps, setComponentProps] = useState<P | null>(null);\n const [triggerProps, setTriggerProps] = useState<T | null>(null);\n const anchorRef = useRef<HTMLElement>(null);\n const setupRef = useRef(false);\n\n useEffect(() => {\n const el = anchorRef.current;\n if (el) {\n el.dataset.popoverTrigger = '';\n return () => {\n delete el.dataset.popoverTrigger;\n };\n }\n }, []);\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n usePopoverSync({\n menuId,\n isOpen,\n onClose: () => setIsOpen(false),\n });\n\n function setupCheck() {\n if (!setupRef.current) {\n throw new Error(\n 'useAnchoredMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.',\n );\n }\n }\n\n // 'open' accepts props required by the Component and opens the menu\n const open = useEvent((props: P = {} as P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n setIsOpen(true);\n });\n\n const update = useEvent((props: P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n });\n\n const close = useEvent(() => {\n setIsOpen(false);\n });\n\n // Render the menu only when componentProps is set\n const renderedMenu = useMemo(() => {\n if (!componentProps) return null;\n\n return (\n <MenuTrigger\n isDummy\n isOpen={isOpen}\n targetRef={anchorRef}\n placement=\"bottom start\"\n onOpenChange={setIsOpen}\n {...mergeProps(defaultTriggerProps, triggerProps || undefined)}\n >\n <VisuallyHidden>\n <Pressable>\n <button aria-label=\"context-menu\" />\n </Pressable>\n </VisuallyHidden>\n <Component {...componentProps} />\n </MenuTrigger>\n );\n }, [componentProps, triggerProps, isOpen, defaultTriggerProps]);\n\n return {\n anchorRef,\n open,\n update,\n close,\n isOpen,\n get rendered() {\n setupRef.current = true;\n\n return renderedMenu;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0DA,SAAgB,gBACd,WACA,qBAIA,kBAC6B;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB,KAAK;CACpE,MAAM,CAAC,cAAc,mBAAmB,SAAmB,KAAK;CAChE,MAAM,YAAY,OAAoB,KAAK;CAC3C,MAAM,WAAW,OAAO,MAAM;AAE9B,iBAAgB;EACd,MAAM,KAAK,UAAU;AACrB,MAAI,IAAI;AACN,MAAG,QAAQ,iBAAiB;AAC5B,gBAAa;AACX,WAAO,GAAG,QAAQ;;;IAGrB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"use-anchored-menu.js","names":["MenuTrigger","mergeProps"],"sources":["../../../src/components/actions/use-anchored-menu.tsx"],"sourcesContent":["import { Pressable } from '@react-aria/interactions';\nimport {\n ComponentProps,\n ComponentType,\n ReactElement,\n RefObject,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { VisuallyHidden } from 'react-aria';\n\nimport { useEvent } from '../../_internal';\nimport { generateRandomId } from '../../utils/random';\nimport { mergeProps } from '../../utils/react';\nimport { usePopoverSync } from '../../utils/react/usePopoverSync';\n\nimport { MenuTrigger } from './Menu';\n\nexport interface UseAnchoredMenuReturn<P, T> {\n /** Ref to attach to the anchor element for positioning the menu. */\n anchorRef: RefObject<HTMLElement | null>;\n\n /**\n * Programmatically opens the menu with the provided props.\n * @param props - Props to pass to the menu component\n * @param triggerProps - Additional props for MenuTrigger (merged with defaultTriggerProps)\n */\n open(props?: P, triggerProps?: T): void;\n\n /**\n * Updates the props of the currently open menu.\n * Props are merged if defaults are provided.\n */\n update(props: P, triggerProps?: T): void;\n\n /** Closes the menu programmatically. */\n close(): void;\n\n /** Current open/closed state of the menu. */\n isOpen: boolean;\n\n /**\n * JSX element that must be rendered in your component tree.\n * Contains the MenuTrigger and positioning logic.\n */\n get rendered(): ReactElement | null;\n}\n\n/**\n * Generic hook to manage an anchored menu component.\n *\n * @param Component - A React component that represents the menu content (Menu or CommandMenu).\n * @param defaultTriggerProps - Default props to pass to the MenuTrigger.\n * @param defaultMenuProps - Default props to pass to the Menu component.\n * @returns An object with `anchorRef` to position the menu, `open` function to open the menu with provided props, `close` function to close the menu, and `rendered` JSX element to include in your component tree.\n */\nexport function useAnchoredMenu<P, T = ComponentProps<typeof MenuTrigger>>(\n Component: ComponentType<P>,\n defaultTriggerProps?: Omit<\n ComponentProps<typeof MenuTrigger>,\n 'children' | 'isOpen' | 'onOpenChange' | 'targetRef'\n >,\n defaultMenuProps?: P,\n): UseAnchoredMenuReturn<P, T> {\n const [isOpen, setIsOpen] = useState(false);\n const [componentProps, setComponentProps] = useState<P | null>(null);\n const [triggerProps, setTriggerProps] = useState<T | null>(null);\n const anchorRef = useRef<HTMLElement>(null);\n const setupRef = useRef(false);\n\n useEffect(() => {\n const el = anchorRef.current;\n if (el) {\n el.dataset.popoverTrigger = '';\n return () => {\n delete el.dataset.popoverTrigger;\n };\n }\n }, []);\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n // `containerRef` is intentionally omitted: the underlying `MenuTrigger` is\n // `isDummy`, so its own popover container ref isn't reachable from here.\n // Passing `triggerRef` is enough to let peers (e.g. a `DialogTrigger`\n // opened inside the menu's content) identify themselves as nested and stay\n // open — the menu's own `MenuTrigger` already maintains its container ref\n // via its non-dummy usage elsewhere; here the dummy proxies a real one.\n usePopoverSync({\n menuId,\n isOpen,\n onClose: () => setIsOpen(false),\n triggerRef: anchorRef,\n });\n\n function setupCheck() {\n if (!setupRef.current) {\n throw new Error(\n 'useAnchoredMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.',\n );\n }\n }\n\n // 'open' accepts props required by the Component and opens the menu\n const open = useEvent((props: P = {} as P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n setIsOpen(true);\n });\n\n const update = useEvent((props: P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n });\n\n const close = useEvent(() => {\n setIsOpen(false);\n });\n\n // Render the menu only when componentProps is set\n const renderedMenu = useMemo(() => {\n if (!componentProps) return null;\n\n return (\n <MenuTrigger\n isDummy\n isOpen={isOpen}\n targetRef={anchorRef}\n placement=\"bottom start\"\n onOpenChange={setIsOpen}\n {...mergeProps(defaultTriggerProps, triggerProps || undefined)}\n >\n <VisuallyHidden>\n <Pressable>\n <button aria-label=\"context-menu\" />\n </Pressable>\n </VisuallyHidden>\n <Component {...componentProps} />\n </MenuTrigger>\n );\n }, [componentProps, triggerProps, isOpen, defaultTriggerProps]);\n\n return {\n anchorRef,\n open,\n update,\n close,\n isOpen,\n get rendered() {\n setupRef.current = true;\n\n return renderedMenu;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0DA,SAAgB,gBACd,WACA,qBAIA,kBAC6B;CAC7B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB,KAAK;CACpE,MAAM,CAAC,cAAc,mBAAmB,SAAmB,KAAK;CAChE,MAAM,YAAY,OAAoB,KAAK;CAC3C,MAAM,WAAW,OAAO,MAAM;AAE9B,iBAAgB;EACd,MAAM,KAAK,UAAU;AACrB,MAAI,IAAI;AACN,MAAG,QAAQ,iBAAiB;AAC5B,gBAAa;AACX,WAAO,GAAG,QAAQ;;;IAGrB,EAAE,CAAC;AAWN,gBAAe;EACb,QATa,cAAc,kBAAkB,EAAE,EAAE,CAAC;EAUlD;EACA,eAAe,UAAU,MAAM;EAC/B,YAAY;EACb,CAAC;CAEF,SAAS,aAAa;AACpB,MAAI,CAAC,SAAS,QACZ,OAAM,IAAI,MACR,+GACD;;CAKL,MAAM,OAAO,UAAU,QAAW,EAAE,EAAO,iBAAqB;AAC9D,cAAY;AAOZ,oBAJmB,mBACf;GAAE,GAAG;GAAkB,GAAG;GAAO,GACjC,MAEyB;AAC7B,kBAAgB,gBAAgB,KAAK;AACrC,YAAU,KAAK;GACf;CAEF,MAAM,SAAS,UAAU,OAAU,iBAAqB;AACtD,cAAY;AAOZ,oBAJmB,mBACf;GAAE,GAAG;GAAkB,GAAG;GAAO,GACjC,MAEyB;AAC7B,kBAAgB,gBAAgB,KAAK;GACrC;CAEF,MAAM,QAAQ,eAAe;AAC3B,YAAU,MAAM;GAChB;CAGF,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,eAAgB,QAAO;AAE5B,SACE,qBAACA;GACC;GACQ;GACR,WAAW;GACX,WAAU;GACV,cAAc;GACd,GAAIC,aAAW,qBAAqB,gBAAgB,OAAU;cAE9D,oBAAC,4BACC,oBAAC,uBACC,oBAAC,YAAO,cAAW,iBAAiB,GAC1B,GACG,EACjB,oBAAC,aAAU,GAAI,iBAAkB;IACrB;IAEf;EAAC;EAAgB;EAAc;EAAQ;EAAoB,CAAC;AAE/D,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,IAAI,WAAW;AACb,YAAS,UAAU;AAEnB,UAAO;;EAEV"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-
|
|
1
|
+
/** @license MIT | @cube-dev/ui-kit v0.0.0-canary-5a4d7bc | Cube Dev Team */
|
|
2
2
|
import { mergeProps as mergeProps$1 } from "../../utils/react/mergeProps.js";
|
|
3
|
+
import { usePopoverSync } from "../../utils/react/usePopoverSync.js";
|
|
3
4
|
import { useEvent } from "../../_internal/hooks/use-event.js";
|
|
4
5
|
import { generateRandomId } from "../../utils/random.js";
|
|
5
|
-
import { usePopoverSync } from "../../utils/react/usePopoverSync.js";
|
|
6
6
|
import { _MenuTrigger } from "./Menu/MenuTrigger.js";
|
|
7
7
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -41,7 +41,8 @@ function useContextMenu(Component, defaultTriggerProps, defaultMenuProps) {
|
|
|
41
41
|
onClose: () => {
|
|
42
42
|
setIsOpen(false);
|
|
43
43
|
setAnchorPosition(null);
|
|
44
|
-
}
|
|
44
|
+
},
|
|
45
|
+
triggerRef: targetRef
|
|
45
46
|
});
|
|
46
47
|
function setupCheck() {
|
|
47
48
|
if (!setupRef.current) throw new Error("useContextMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-context-menu.js","names":["MenuTrigger","mergeProps"],"sources":["../../../src/components/actions/use-context-menu.tsx"],"sourcesContent":["import { Pressable } from '@react-aria/interactions';\nimport {\n ComponentProps,\n ComponentType,\n MouseEvent,\n PointerEvent,\n ReactElement,\n RefObject,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { VisuallyHidden } from 'react-aria';\n\nimport { useEvent } from '../../_internal';\nimport { generateRandomId } from '../../utils/random';\nimport { mergeProps } from '../../utils/react';\nimport { usePopoverSync } from '../../utils/react/usePopoverSync';\n\nimport { MenuTrigger } from './Menu';\n\ntype NativeMouseEvent = globalThis.MouseEvent;\ntype NativePointerEvent = globalThis.PointerEvent;\n\nexport interface UseContextMenuReturn<\n E extends HTMLElement = HTMLElement,\n P extends object = {},\n T = ComponentProps<typeof MenuTrigger>,\n> {\n /** Container element that receives context menu events. Attach this ref to your target element. */\n targetRef: RefObject<E | null>;\n\n /**\n * Programmatically opens the menu at the specified coordinates or element center.\n * Runtime props are merged with defaultMenuProps (runtime props take precedence).\n *\n * @param props - Props to pass to the menu component (optional, defaults to defaultMenuProps)\n * @param triggerProps - Additional props for MenuTrigger (merged with defaultTriggerProps)\n * @param event - The pointer/mouse event containing coordinates for positioning (optional, centers on element if not provided)\n */\n open(\n props?: P,\n triggerProps?: T,\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ): void;\n\n /**\n * Updates the props of the currently open menu without repositioning.\n * Props are merged with defaultMenuProps.\n */\n update(props: P, triggerProps?: T): void;\n\n /** Closes the menu programmatically. */\n close(): void;\n\n /** Current open/closed state of the menu. */\n isOpen: boolean;\n\n /**\n * JSX element that must be rendered in your component tree.\n * Contains the MenuTrigger and positioning logic.\n * IMPORTANT: Must be placed directly inside the target container (the element with targetRef).\n */\n get rendered(): ReactElement | null;\n}\n\n/**\n * Generic hook to manage a context menu component that opens at pointer coordinates.\n *\n * @param Component - A React component that represents the menu content (Menu or CommandMenu).\n * @param defaultTriggerProps - Default props to pass to the MenuTrigger.\n * @param defaultMenuProps - Default props to pass to the Menu component.\n * @returns An object with `targetRef` to attach to the container element, `open` function to open the menu at event coordinates, `close` function to close the menu, and `rendered` JSX element to include in your component tree.\n */\nexport function useContextMenu<\n E extends HTMLElement = HTMLElement,\n P extends object = {},\n T = ComponentProps<typeof MenuTrigger>,\n>(\n Component: ComponentType<P>,\n defaultTriggerProps?: Omit<\n ComponentProps<typeof MenuTrigger>,\n 'children' | 'isOpen' | 'onOpenChange' | 'targetRef'\n >,\n defaultMenuProps?: P,\n): UseContextMenuReturn<E, P, T> {\n const [isOpen, setIsOpen] = useState(false);\n const [componentProps, setComponentProps] = useState<P | null>(null);\n const [triggerProps, setTriggerProps] = useState<T | null>(null);\n const [anchorPosition, setAnchorPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const targetRef = useRef<E>(null);\n const invisibleAnchorRef = useRef<HTMLSpanElement>(null);\n const setupRef = useRef(false);\n\n // Mark the container as a popover trigger so that other open menus' close-on-\n // outside predicates treat clicks inside it (including programmatic\n // open buttons rendered alongside a context-menu target) as a legitimate\n // trigger interaction instead of a generic outside click. This mirrors the\n // pattern in `useAnchoredMenu`.\n useEffect(() => {\n const el = targetRef.current;\n if (el) {\n el.dataset.popoverTrigger = '';\n return () => {\n delete el.dataset.popoverTrigger;\n };\n }\n }, []);\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n usePopoverSync({\n menuId,\n isOpen,\n onClose: () => {\n setIsOpen(false);\n setAnchorPosition(null);\n },\n });\n\n function setupCheck() {\n if (!setupRef.current) {\n throw new Error(\n 'useContextMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.',\n );\n }\n }\n\n // Helper function to calculate position relative to targetRef, taking the\n // element's scroll offset into account. Without the scroll offset the menu\n // would be rendered at the wrong place inside scrollable containers.\n const calculatePosition = (\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ) => {\n const container = targetRef.current;\n\n // If no event is provided, position at the center of the element\n if (!event) {\n if (!container) {\n return { x: 0, y: 0 };\n }\n\n const containerRect = container.getBoundingClientRect();\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const computed = window.getComputedStyle(container);\n const borderLeft = parseFloat(computed.borderLeftWidth) || 0;\n const borderTop = parseFloat(computed.borderTopWidth) || 0;\n\n // Position at the center of the element's content area\n const x = container.clientWidth / 2 + scrollLeft;\n const y = container.clientHeight / 2 + scrollTop;\n\n // Clamp to the full scroll size\n const clampedX = Math.max(0, Math.min(x, container.scrollWidth));\n const clampedY = Math.max(0, Math.min(y, container.scrollHeight));\n\n return { x: clampedX, y: clampedY };\n }\n\n // If the target reference is missing, fall back to viewport coordinates.\n if (!container) {\n const { clientX = 0, clientY = 0 } = event;\n\n return { x: clientX, y: clientY };\n }\n\n const containerRect = container.getBoundingClientRect();\n\n // Get coordinates from the event (viewport-relative)\n const { clientX, clientY } = event;\n\n // Take the element's scroll offset into account so that the coordinates are\n // relative to the **content** box, not the visible viewport of the\n // element.\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const computed = window.getComputedStyle(container);\n const borderLeft = parseFloat(computed.borderLeftWidth) || 0;\n const borderTop = parseFloat(computed.borderTopWidth) || 0;\n\n const x = clientX - containerRect.left - borderLeft + scrollLeft;\n const y = clientY - containerRect.top - borderTop + scrollTop;\n\n // Clamp to the full scroll size so that the invisible anchor always stays\n // inside the element regardless of the scroll position.\n const clampedX = Math.max(0, Math.min(x, container.scrollWidth));\n const clampedY = Math.max(0, Math.min(y, container.scrollHeight));\n\n return { x: clampedX, y: clampedY };\n };\n\n // 'open' accepts props, trigger props, and optional event for positioning, then opens the menu\n const open = useEvent(\n (\n props: P = {} as P,\n triggerProps?: T,\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ) => {\n setupCheck();\n\n // Ensure the target element can serve as a positioning context for the\n // invisible target element. If the consumer hasn't explicitly set\n // `position: relative | absolute | fixed | sticky` we switch it to\n // `relative` so that absolutely-positioned children are laid out correctly.\n if (targetRef.current) {\n const computedStyle = window.getComputedStyle(targetRef.current);\n\n if (computedStyle.position === 'static') {\n targetRef.current.style.position = 'relative';\n }\n }\n\n // Prevent default context menu if it's a context menu event\n if (\n event &&\n 'preventDefault' in event &&\n typeof event.preventDefault === 'function'\n ) {\n event.preventDefault();\n }\n\n const { x, y } = calculatePosition(event);\n setAnchorPosition({ x, y });\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n setIsOpen(true);\n },\n );\n\n const update = useEvent((props: P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps as P);\n setTriggerProps(triggerProps ?? null);\n });\n\n const close = useEvent(() => {\n setIsOpen(false);\n setAnchorPosition(null);\n });\n\n // Context menu event handler\n const onContextMenu = useEvent(\n (event: MouseEvent | PointerEvent | MouseEvent | PointerEvent) => {\n event.preventDefault();\n if (isOpen) {\n const pos = calculatePosition(event);\n setAnchorPosition(pos);\n } else {\n open(defaultMenuProps, undefined, event);\n }\n },\n );\n\n // Bind the onContextMenu event to targetRef\n useEffect(() => {\n const element = targetRef.current;\n if (!element) return;\n\n element.addEventListener('contextmenu', onContextMenu as any);\n\n return () => {\n element.removeEventListener('contextmenu', onContextMenu as any);\n };\n }, [onContextMenu]);\n\n // Render the menu only when componentProps is set\n const renderedMenu = useMemo(() => {\n if (!componentProps || !anchorPosition) return null;\n\n return (\n <>\n {/* Invisible anchor element positioned at click coordinates */}\n <span\n ref={invisibleAnchorRef}\n style={{\n position: 'absolute',\n left: `${anchorPosition.x}px`,\n top: `${anchorPosition.y}px`,\n width: '0px',\n height: '0px',\n lineHeight: '0',\n pointerEvents: 'none',\n visibility: 'hidden',\n }}\n />\n <MenuTrigger\n isDummy\n isOpen={isOpen}\n targetRef={invisibleAnchorRef}\n offset={0}\n crossOffset={0}\n placement={\n (triggerProps as ComponentProps<typeof MenuTrigger>)?.placement ||\n defaultTriggerProps?.placement ||\n 'bottom start'\n }\n onOpenChange={setIsOpen}\n {...mergeProps(defaultTriggerProps, triggerProps || undefined)}\n >\n <VisuallyHidden>\n <Pressable>\n <button aria-label=\"Open context menu\" />\n </Pressable>\n </VisuallyHidden>\n <Component {...componentProps} />\n </MenuTrigger>\n </>\n );\n }, [\n componentProps,\n triggerProps,\n isOpen,\n defaultTriggerProps,\n anchorPosition,\n ]);\n\n return {\n targetRef,\n open,\n update,\n close,\n isOpen,\n get rendered() {\n setupRef.current = true;\n\n return renderedMenu;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,eAKd,WACA,qBAIA,kBAC+B;CAC/B,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB,KAAK;CACpE,MAAM,CAAC,cAAc,mBAAmB,SAAmB,KAAK;CAChE,MAAM,CAAC,gBAAgB,qBAAqB,SAGlC,KAAK;CACf,MAAM,YAAY,OAAU,KAAK;CACjC,MAAM,qBAAqB,OAAwB,KAAK;CACxD,MAAM,WAAW,OAAO,MAAM;AAO9B,iBAAgB;EACd,MAAM,KAAK,UAAU;AACrB,MAAI,IAAI;AACN,MAAG,QAAQ,iBAAiB;AAC5B,gBAAa;AACX,WAAO,GAAG,QAAQ;;;IAGrB,EAAE,CAAC;AAKN,gBAAe;EACb,QAHa,cAAc,kBAAkB,EAAE,EAAE,CAAC;EAIlD;EACA,eAAe;AACb,aAAU,MAAM;AAChB,qBAAkB,KAAK;;EAE1B,CAAC;CAEF,SAAS,aAAa;AACpB,MAAI,CAAC,SAAS,QACZ,OAAM,IAAI,MACR,8GACD;;CAOL,MAAM,qBACJ,UACG;EACH,MAAM,YAAY,UAAU;AAG5B,MAAI,CAAC,OAAO;AACV,OAAI,CAAC,UACH,QAAO;IAAE,GAAG;IAAG,GAAG;IAAG;AAGD,aAAU,uBAAuB;GACvD,MAAM,aAAa,UAAU;GAC7B,MAAM,YAAY,UAAU;GAE5B,MAAM,WAAW,OAAO,iBAAiB,UAAU;AAChC,cAAW,SAAS,gBAAgB;AACrC,cAAW,SAAS,eAAe;GAGrD,MAAM,IAAI,UAAU,cAAc,IAAI;GACtC,MAAM,IAAI,UAAU,eAAe,IAAI;AAMvC,UAAO;IAAE,GAHQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,YAAY,CAAC;IAG1C,GAFL,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,aAAa,CAAC;IAE9B;;AAIrC,MAAI,CAAC,WAAW;GACd,MAAM,EAAE,UAAU,GAAG,UAAU,MAAM;AAErC,UAAO;IAAE,GAAG;IAAS,GAAG;IAAS;;EAGnC,MAAM,gBAAgB,UAAU,uBAAuB;EAGvD,MAAM,EAAE,SAAS,YAAY;EAK7B,MAAM,aAAa,UAAU;EAC7B,MAAM,YAAY,UAAU;EAE5B,MAAM,WAAW,OAAO,iBAAiB,UAAU;EACnD,MAAM,aAAa,WAAW,SAAS,gBAAgB,IAAI;EAC3D,MAAM,YAAY,WAAW,SAAS,eAAe,IAAI;EAEzD,MAAM,IAAI,UAAU,cAAc,OAAO,aAAa;EACtD,MAAM,IAAI,UAAU,cAAc,MAAM,YAAY;AAOpD,SAAO;GAAE,GAHQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,YAAY,CAAC;GAG1C,GAFL,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,aAAa,CAAC;GAE9B;;CAIrC,MAAM,OAAO,UAET,QAAW,EAAE,EACb,cACA,UACG;AACH,cAAY;AAMZ,MAAI,UAAU,SAGZ;OAFsB,OAAO,iBAAiB,UAAU,QAAQ,CAE9C,aAAa,SAC7B,WAAU,QAAQ,MAAM,WAAW;;AAKvC,MACE,SACA,oBAAoB,SACpB,OAAO,MAAM,mBAAmB,WAEhC,OAAM,gBAAgB;EAGxB,MAAM,EAAE,GAAG,MAAM,kBAAkB,MAAM;AACzC,oBAAkB;GAAE;GAAG;GAAG,CAAC;AAO3B,oBAJmB,mBACf;GAAE,GAAG;GAAkB,GAAG;GAAO,GACjC,MAEyB;AAC7B,kBAAgB,gBAAgB,KAAK;AACrC,YAAU,KAAK;GAElB;CAED,MAAM,SAAS,UAAU,OAAU,iBAAqB;AACtD,cAAY;AAOZ,oBAJmB,mBACf;GAAE,GAAG;GAAkB,GAAG;GAAO,GACjC,MAE8B;AAClC,kBAAgB,gBAAgB,KAAK;GACrC;CAEF,MAAM,QAAQ,eAAe;AAC3B,YAAU,MAAM;AAChB,oBAAkB,KAAK;GACvB;CAGF,MAAM,gBAAgB,UACnB,UAAiE;AAChE,QAAM,gBAAgB;AACtB,MAAI,OAEF,mBADY,kBAAkB,MAAM,CACd;MAEtB,MAAK,kBAAkB,QAAW,MAAM;GAG7C;AAGD,iBAAgB;EACd,MAAM,UAAU,UAAU;AAC1B,MAAI,CAAC,QAAS;AAEd,UAAQ,iBAAiB,eAAe,cAAqB;AAE7D,eAAa;AACX,WAAQ,oBAAoB,eAAe,cAAqB;;IAEjE,CAAC,cAAc,CAAC;CAGnB,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,kBAAkB,CAAC,eAAgB,QAAO;AAE/C,SACE,8CAEE,oBAAC;GACC,KAAK;GACL,OAAO;IACL,UAAU;IACV,MAAM,GAAG,eAAe,EAAE;IAC1B,KAAK,GAAG,eAAe,EAAE;IACzB,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,eAAe;IACf,YAAY;IACb;IACD,EACF,qBAACA;GACC;GACQ;GACR,WAAW;GACX,QAAQ;GACR,aAAa;GACb,WACG,cAAqD,aACtD,qBAAqB,aACrB;GAEF,cAAc;GACd,GAAIC,aAAW,qBAAqB,gBAAgB,OAAU;cAE9D,oBAAC,4BACC,oBAAC,uBACC,oBAAC,YAAO,cAAW,sBAAsB,GAC/B,GACG,EACjB,oBAAC,aAAU,GAAI,iBAAkB;IACrB,IACb;IAEJ;EACD;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,IAAI,WAAW;AACb,YAAS,UAAU;AAEnB,UAAO;;EAEV"}
|
|
1
|
+
{"version":3,"file":"use-context-menu.js","names":["MenuTrigger","mergeProps"],"sources":["../../../src/components/actions/use-context-menu.tsx"],"sourcesContent":["import { Pressable } from '@react-aria/interactions';\nimport {\n ComponentProps,\n ComponentType,\n MouseEvent,\n PointerEvent,\n ReactElement,\n RefObject,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { VisuallyHidden } from 'react-aria';\n\nimport { useEvent } from '../../_internal';\nimport { generateRandomId } from '../../utils/random';\nimport { mergeProps } from '../../utils/react';\nimport { usePopoverSync } from '../../utils/react/usePopoverSync';\n\nimport { MenuTrigger } from './Menu';\n\ntype NativeMouseEvent = globalThis.MouseEvent;\ntype NativePointerEvent = globalThis.PointerEvent;\n\nexport interface UseContextMenuReturn<\n E extends HTMLElement = HTMLElement,\n P extends object = {},\n T = ComponentProps<typeof MenuTrigger>,\n> {\n /** Container element that receives context menu events. Attach this ref to your target element. */\n targetRef: RefObject<E | null>;\n\n /**\n * Programmatically opens the menu at the specified coordinates or element center.\n * Runtime props are merged with defaultMenuProps (runtime props take precedence).\n *\n * @param props - Props to pass to the menu component (optional, defaults to defaultMenuProps)\n * @param triggerProps - Additional props for MenuTrigger (merged with defaultTriggerProps)\n * @param event - The pointer/mouse event containing coordinates for positioning (optional, centers on element if not provided)\n */\n open(\n props?: P,\n triggerProps?: T,\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ): void;\n\n /**\n * Updates the props of the currently open menu without repositioning.\n * Props are merged with defaultMenuProps.\n */\n update(props: P, triggerProps?: T): void;\n\n /** Closes the menu programmatically. */\n close(): void;\n\n /** Current open/closed state of the menu. */\n isOpen: boolean;\n\n /**\n * JSX element that must be rendered in your component tree.\n * Contains the MenuTrigger and positioning logic.\n * IMPORTANT: Must be placed directly inside the target container (the element with targetRef).\n */\n get rendered(): ReactElement | null;\n}\n\n/**\n * Generic hook to manage a context menu component that opens at pointer coordinates.\n *\n * @param Component - A React component that represents the menu content (Menu or CommandMenu).\n * @param defaultTriggerProps - Default props to pass to the MenuTrigger.\n * @param defaultMenuProps - Default props to pass to the Menu component.\n * @returns An object with `targetRef` to attach to the container element, `open` function to open the menu at event coordinates, `close` function to close the menu, and `rendered` JSX element to include in your component tree.\n */\nexport function useContextMenu<\n E extends HTMLElement = HTMLElement,\n P extends object = {},\n T = ComponentProps<typeof MenuTrigger>,\n>(\n Component: ComponentType<P>,\n defaultTriggerProps?: Omit<\n ComponentProps<typeof MenuTrigger>,\n 'children' | 'isOpen' | 'onOpenChange' | 'targetRef'\n >,\n defaultMenuProps?: P,\n): UseContextMenuReturn<E, P, T> {\n const [isOpen, setIsOpen] = useState(false);\n const [componentProps, setComponentProps] = useState<P | null>(null);\n const [triggerProps, setTriggerProps] = useState<T | null>(null);\n const [anchorPosition, setAnchorPosition] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const targetRef = useRef<E>(null);\n const invisibleAnchorRef = useRef<HTMLSpanElement>(null);\n const setupRef = useRef(false);\n\n // Mark the container as a popover trigger so that other open menus' close-on-\n // outside predicates treat clicks inside it (including programmatic\n // open buttons rendered alongside a context-menu target) as a legitimate\n // trigger interaction instead of a generic outside click. This mirrors the\n // pattern in `useAnchoredMenu`.\n useEffect(() => {\n const el = targetRef.current;\n if (el) {\n el.dataset.popoverTrigger = '';\n return () => {\n delete el.dataset.popoverTrigger;\n };\n }\n }, []);\n\n // Generate a unique ID for this menu instance\n const menuId = useMemo(() => generateRandomId(), []);\n\n // `containerRef` is intentionally omitted: the underlying `MenuTrigger` is\n // `isDummy`, so its own popover container ref isn't reachable from here.\n // Providing `triggerRef` (the context-menu target element) is enough to let\n // peers identify themselves correctly when they emit `popover:open`.\n usePopoverSync({\n menuId,\n isOpen,\n onClose: () => {\n setIsOpen(false);\n setAnchorPosition(null);\n },\n triggerRef: targetRef as RefObject<HTMLElement | null>,\n });\n\n function setupCheck() {\n if (!setupRef.current) {\n throw new Error(\n 'useContextMenu: MenuTrigger must be rendered. Use `rendered` property to include it in your component tree.',\n );\n }\n }\n\n // Helper function to calculate position relative to targetRef, taking the\n // element's scroll offset into account. Without the scroll offset the menu\n // would be rendered at the wrong place inside scrollable containers.\n const calculatePosition = (\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ) => {\n const container = targetRef.current;\n\n // If no event is provided, position at the center of the element\n if (!event) {\n if (!container) {\n return { x: 0, y: 0 };\n }\n\n const containerRect = container.getBoundingClientRect();\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const computed = window.getComputedStyle(container);\n const borderLeft = parseFloat(computed.borderLeftWidth) || 0;\n const borderTop = parseFloat(computed.borderTopWidth) || 0;\n\n // Position at the center of the element's content area\n const x = container.clientWidth / 2 + scrollLeft;\n const y = container.clientHeight / 2 + scrollTop;\n\n // Clamp to the full scroll size\n const clampedX = Math.max(0, Math.min(x, container.scrollWidth));\n const clampedY = Math.max(0, Math.min(y, container.scrollHeight));\n\n return { x: clampedX, y: clampedY };\n }\n\n // If the target reference is missing, fall back to viewport coordinates.\n if (!container) {\n const { clientX = 0, clientY = 0 } = event;\n\n return { x: clientX, y: clientY };\n }\n\n const containerRect = container.getBoundingClientRect();\n\n // Get coordinates from the event (viewport-relative)\n const { clientX, clientY } = event;\n\n // Take the element's scroll offset into account so that the coordinates are\n // relative to the **content** box, not the visible viewport of the\n // element.\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const computed = window.getComputedStyle(container);\n const borderLeft = parseFloat(computed.borderLeftWidth) || 0;\n const borderTop = parseFloat(computed.borderTopWidth) || 0;\n\n const x = clientX - containerRect.left - borderLeft + scrollLeft;\n const y = clientY - containerRect.top - borderTop + scrollTop;\n\n // Clamp to the full scroll size so that the invisible anchor always stays\n // inside the element regardless of the scroll position.\n const clampedX = Math.max(0, Math.min(x, container.scrollWidth));\n const clampedY = Math.max(0, Math.min(y, container.scrollHeight));\n\n return { x: clampedX, y: clampedY };\n };\n\n // 'open' accepts props, trigger props, and optional event for positioning, then opens the menu\n const open = useEvent(\n (\n props: P = {} as P,\n triggerProps?: T,\n event?: NativeMouseEvent | NativePointerEvent | MouseEvent | PointerEvent,\n ) => {\n setupCheck();\n\n // Ensure the target element can serve as a positioning context for the\n // invisible target element. If the consumer hasn't explicitly set\n // `position: relative | absolute | fixed | sticky` we switch it to\n // `relative` so that absolutely-positioned children are laid out correctly.\n if (targetRef.current) {\n const computedStyle = window.getComputedStyle(targetRef.current);\n\n if (computedStyle.position === 'static') {\n targetRef.current.style.position = 'relative';\n }\n }\n\n // Prevent default context menu if it's a context menu event\n if (\n event &&\n 'preventDefault' in event &&\n typeof event.preventDefault === 'function'\n ) {\n event.preventDefault();\n }\n\n const { x, y } = calculatePosition(event);\n setAnchorPosition({ x, y });\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps);\n setTriggerProps(triggerProps ?? null);\n setIsOpen(true);\n },\n );\n\n const update = useEvent((props: P, triggerProps?: T) => {\n setupCheck();\n\n // Merge defaultMenuProps with provided props\n const finalProps = defaultMenuProps\n ? { ...defaultMenuProps, ...props }\n : props;\n\n setComponentProps(finalProps as P);\n setTriggerProps(triggerProps ?? null);\n });\n\n const close = useEvent(() => {\n setIsOpen(false);\n setAnchorPosition(null);\n });\n\n // Context menu event handler\n const onContextMenu = useEvent(\n (event: MouseEvent | PointerEvent | MouseEvent | PointerEvent) => {\n event.preventDefault();\n if (isOpen) {\n const pos = calculatePosition(event);\n setAnchorPosition(pos);\n } else {\n open(defaultMenuProps, undefined, event);\n }\n },\n );\n\n // Bind the onContextMenu event to targetRef\n useEffect(() => {\n const element = targetRef.current;\n if (!element) return;\n\n element.addEventListener('contextmenu', onContextMenu as any);\n\n return () => {\n element.removeEventListener('contextmenu', onContextMenu as any);\n };\n }, [onContextMenu]);\n\n // Render the menu only when componentProps is set\n const renderedMenu = useMemo(() => {\n if (!componentProps || !anchorPosition) return null;\n\n return (\n <>\n {/* Invisible anchor element positioned at click coordinates */}\n <span\n ref={invisibleAnchorRef}\n style={{\n position: 'absolute',\n left: `${anchorPosition.x}px`,\n top: `${anchorPosition.y}px`,\n width: '0px',\n height: '0px',\n lineHeight: '0',\n pointerEvents: 'none',\n visibility: 'hidden',\n }}\n />\n <MenuTrigger\n isDummy\n isOpen={isOpen}\n targetRef={invisibleAnchorRef}\n offset={0}\n crossOffset={0}\n placement={\n (triggerProps as ComponentProps<typeof MenuTrigger>)?.placement ||\n defaultTriggerProps?.placement ||\n 'bottom start'\n }\n onOpenChange={setIsOpen}\n {...mergeProps(defaultTriggerProps, triggerProps || undefined)}\n >\n <VisuallyHidden>\n <Pressable>\n <button aria-label=\"Open context menu\" />\n </Pressable>\n </VisuallyHidden>\n <Component {...componentProps} />\n </MenuTrigger>\n </>\n );\n }, [\n componentProps,\n triggerProps,\n isOpen,\n defaultTriggerProps,\n anchorPosition,\n ]);\n\n return {\n targetRef,\n open,\n update,\n close,\n isOpen,\n get rendered() {\n setupRef.current = true;\n\n return renderedMenu;\n },\n };\n}\n"],"mappings":"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"}
|