@graphprotocol/gds-react 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/GDSProvider.d.ts +12 -10
- package/dist/GDSProvider.d.ts.map +1 -1
- package/dist/GDSProvider.js +10 -8
- package/dist/GDSProvider.js.map +1 -1
- package/dist/components/Address.js +2 -2
- package/dist/components/Address.meta.d.ts +1 -13
- package/dist/components/Address.meta.d.ts.map +1 -1
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar.js +2 -10
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/Avatar.meta.d.ts +0 -2
- package/dist/components/Avatar.meta.d.ts.map +1 -1
- package/dist/components/AvatarGroup.meta.d.ts +8 -2
- package/dist/components/AvatarGroup.meta.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.meta.d.ts +2 -3
- package/dist/components/Breadcrumbs.meta.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.meta.js +3 -1
- package/dist/components/Breadcrumbs.meta.js.map +1 -1
- package/dist/components/Breadcrumbs.parts.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.parts.js +13 -21
- package/dist/components/Breadcrumbs.parts.js.map +1 -1
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Button.js +10 -9
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Button.meta.d.ts +1 -4
- package/dist/components/Button.meta.d.ts.map +1 -1
- package/dist/components/ButtonGroup.d.ts.map +1 -1
- package/dist/components/ButtonGroup.js +1 -5
- package/dist/components/ButtonGroup.js.map +1 -1
- package/dist/components/ButtonGroup.meta.d.ts +1 -5
- package/dist/components/ButtonGroup.meta.d.ts.map +1 -1
- package/dist/components/ButtonGroup.meta.js +8 -0
- package/dist/components/ButtonGroup.meta.js.map +1 -1
- package/dist/components/Card.meta.d.ts +1 -2
- package/dist/components/Card.meta.d.ts.map +1 -1
- package/dist/components/Card.meta.js +1 -0
- package/dist/components/Card.meta.js.map +1 -1
- package/dist/components/Checkbox.meta.d.ts +1 -6
- package/dist/components/Checkbox.meta.d.ts.map +1 -1
- package/dist/components/Checkbox.meta.js +1 -5
- package/dist/components/Checkbox.meta.js.map +1 -1
- package/dist/components/Chip.meta.d.ts +2 -4
- package/dist/components/Chip.meta.d.ts.map +1 -1
- package/dist/components/Chip.parts.d.ts.map +1 -1
- package/dist/components/Chip.parts.js +1 -9
- package/dist/components/Chip.parts.js.map +1 -1
- package/dist/components/Cluster.meta.d.ts +8 -2
- package/dist/components/Cluster.meta.d.ts.map +1 -1
- package/dist/components/CodeBlock.d.ts +1 -1
- package/dist/components/CodeBlock.meta.d.ts +2 -4
- package/dist/components/CodeBlock.meta.d.ts.map +1 -1
- package/dist/components/CodeBlock.parts.d.ts +6 -7
- package/dist/components/CodeBlock.parts.d.ts.map +1 -1
- package/dist/components/CodeBlock.parts.js +28 -10
- package/dist/components/CodeBlock.parts.js.map +1 -1
- package/dist/components/CodeInline.js +3 -3
- package/dist/components/CodeInline.meta.d.ts +1 -1
- package/dist/components/CodeInline.meta.d.ts.map +1 -1
- package/dist/components/CopyButton.d.ts.map +1 -1
- package/dist/components/CopyButton.js +2 -4
- package/dist/components/CopyButton.js.map +1 -1
- package/dist/components/CopyButton.meta.d.ts +1 -12
- package/dist/components/CopyButton.meta.d.ts.map +1 -1
- package/dist/components/CopyButton.meta.js +1 -6
- package/dist/components/CopyButton.meta.js.map +1 -1
- package/dist/components/CurrencyInput.meta.d.ts +1 -6
- package/dist/components/CurrencyInput.meta.d.ts.map +1 -1
- package/dist/components/CurrencyInput.meta.js +1 -5
- package/dist/components/CurrencyInput.meta.js.map +1 -1
- package/dist/components/DescriptionList.meta.d.ts +2 -5
- package/dist/components/DescriptionList.meta.d.ts.map +1 -1
- package/dist/components/DescriptionList.parts.d.ts +3 -0
- package/dist/components/DescriptionList.parts.d.ts.map +1 -1
- package/dist/components/DescriptionList.parts.js +1 -0
- package/dist/components/DescriptionList.parts.js.map +1 -1
- package/dist/components/Divider.meta.d.ts +1 -3
- package/dist/components/Divider.meta.d.ts.map +1 -1
- package/dist/components/Icon.js +4 -4
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/Icon.meta.d.ts +0 -2
- package/dist/components/Icon.meta.d.ts.map +1 -1
- package/dist/components/Icon.meta.js +1 -0
- package/dist/components/Icon.meta.js.map +1 -1
- package/dist/components/Input.d.ts +5 -4
- package/dist/components/Input.d.ts.map +1 -1
- package/dist/components/Input.js +1 -0
- package/dist/components/Input.js.map +1 -1
- package/dist/components/Input.meta.d.ts +1 -6
- package/dist/components/Input.meta.d.ts.map +1 -1
- package/dist/components/Input.meta.js +1 -5
- package/dist/components/Input.meta.js.map +1 -1
- package/dist/components/Keyboard.js +1 -1
- package/dist/components/Keyboard.meta.d.ts +0 -1
- package/dist/components/Keyboard.meta.d.ts.map +1 -1
- package/dist/components/Label.meta.d.ts +1 -3
- package/dist/components/Label.meta.d.ts.map +1 -1
- package/dist/components/Link.d.ts +1 -1
- package/dist/components/Link.d.ts.map +1 -1
- package/dist/components/Link.js +1 -2
- package/dist/components/Link.js.map +1 -1
- package/dist/components/Link.meta.d.ts +1 -2
- package/dist/components/Link.meta.d.ts.map +1 -1
- package/dist/components/Link.meta.js +1 -0
- package/dist/components/Link.meta.js.map +1 -1
- package/dist/components/Menu.meta.d.ts +31 -2
- package/dist/components/Menu.meta.d.ts.map +1 -1
- package/dist/components/Menu.meta.js +39 -1
- package/dist/components/Menu.meta.js.map +1 -1
- package/dist/components/Menu.parts.d.ts +23 -32
- package/dist/components/Menu.parts.d.ts.map +1 -1
- package/dist/components/Menu.parts.js +284 -303
- package/dist/components/Menu.parts.js.map +1 -1
- package/dist/components/Modal.d.ts +1 -1
- package/dist/components/Modal.meta.d.ts +1 -3
- package/dist/components/Modal.meta.d.ts.map +1 -1
- package/dist/components/Modal.meta.js +1 -1
- package/dist/components/Modal.meta.js.map +1 -1
- package/dist/components/Modal.parts.d.ts +14 -15
- package/dist/components/Modal.parts.d.ts.map +1 -1
- package/dist/components/Modal.parts.js +36 -32
- package/dist/components/Modal.parts.js.map +1 -1
- package/dist/components/OTCInput.js +1 -1
- package/dist/components/OTCInput.meta.d.ts +1 -6
- package/dist/components/OTCInput.meta.d.ts.map +1 -1
- package/dist/components/OTCInput.meta.js +1 -5
- package/dist/components/OTCInput.meta.js.map +1 -1
- package/dist/components/Radio.meta.d.ts +1 -6
- package/dist/components/Radio.meta.d.ts.map +1 -1
- package/dist/components/Radio.meta.js +1 -5
- package/dist/components/Radio.meta.js.map +1 -1
- package/dist/components/Search.meta.d.ts +1 -3
- package/dist/components/Search.meta.d.ts.map +1 -1
- package/dist/components/SegmentedControl.meta.d.ts +2 -3
- package/dist/components/SegmentedControl.meta.d.ts.map +1 -1
- package/dist/components/SegmentedControl.meta.js +3 -1
- package/dist/components/SegmentedControl.meta.js.map +1 -1
- package/dist/components/SegmentedControl.parts.d.ts.map +1 -1
- package/dist/components/SegmentedControl.parts.js +4 -9
- package/dist/components/SegmentedControl.parts.js.map +1 -1
- package/dist/components/Status.meta.d.ts +0 -2
- package/dist/components/Status.meta.d.ts.map +1 -1
- package/dist/components/Stepper.meta.d.ts +1 -2
- package/dist/components/Stepper.meta.d.ts.map +1 -1
- package/dist/components/Stepper.meta.js +1 -0
- package/dist/components/Stepper.meta.js.map +1 -1
- package/dist/components/Stepper.parts.d.ts.map +1 -1
- package/dist/components/Stepper.parts.js +1 -1
- package/dist/components/Stepper.parts.js.map +1 -1
- package/dist/components/Switch.meta.d.ts +1 -6
- package/dist/components/Switch.meta.d.ts.map +1 -1
- package/dist/components/Switch.meta.js +1 -5
- package/dist/components/Switch.meta.js.map +1 -1
- package/dist/components/TabSet.meta.d.ts +2 -5
- package/dist/components/TabSet.meta.d.ts.map +1 -1
- package/dist/components/TabSet.meta.js +3 -1
- package/dist/components/TabSet.meta.js.map +1 -1
- package/dist/components/Tag.meta.d.ts +0 -2
- package/dist/components/Tag.meta.d.ts.map +1 -1
- package/dist/components/TextArea.meta.d.ts +1 -6
- package/dist/components/TextArea.meta.d.ts.map +1 -1
- package/dist/components/TextArea.meta.js +1 -5
- package/dist/components/TextArea.meta.js.map +1 -1
- package/dist/components/ToggleButton.js +2 -2
- package/dist/components/ToggleButton.js.map +1 -1
- package/dist/components/ToggleButton.meta.d.ts +1 -12
- package/dist/components/ToggleButton.meta.d.ts.map +1 -1
- package/dist/components/ToggleButton.meta.js +1 -6
- package/dist/components/ToggleButton.meta.js.map +1 -1
- package/dist/components/Tooltip.d.ts +2 -2
- package/dist/components/Tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip.js +2 -2
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/components/Tooltip.meta.d.ts +12 -7
- package/dist/components/Tooltip.meta.d.ts.map +1 -1
- package/dist/components/Tooltip.meta.js +13 -2
- package/dist/components/Tooltip.meta.js.map +1 -1
- package/dist/components/Tooltip.parts.d.ts +20 -20
- package/dist/components/Tooltip.parts.d.ts.map +1 -1
- package/dist/components/Tooltip.parts.js +129 -88
- package/dist/components/Tooltip.parts.js.map +1 -1
- package/dist/components/base/Addon.meta.d.ts +1 -1
- package/dist/components/base/Addon.meta.d.ts.map +1 -1
- package/dist/components/base/Addon.meta.js +3 -1
- package/dist/components/base/Addon.meta.js.map +1 -1
- package/dist/components/base/ButtonOrLink.d.ts +1 -1
- package/dist/components/base/ButtonOrLink.parts.d.ts +6 -5
- package/dist/components/base/ButtonOrLink.parts.d.ts.map +1 -1
- package/dist/components/base/ButtonOrLink.parts.js +28 -31
- package/dist/components/base/ButtonOrLink.parts.js.map +1 -1
- package/dist/components/base/Checkable.meta.d.ts +1 -2
- package/dist/components/base/Checkable.meta.d.ts.map +1 -1
- package/dist/components/base/Checkable.parts.d.ts +6 -6
- package/dist/components/base/Checkable.parts.d.ts.map +1 -1
- package/dist/components/base/Checkable.parts.js +2 -2
- package/dist/components/base/Checkable.parts.js.map +1 -1
- package/dist/components/base/Field.meta.d.ts +1 -2
- package/dist/components/base/Field.meta.d.ts.map +1 -1
- package/dist/components/base/Field.parts.d.ts +5 -4
- package/dist/components/base/Field.parts.d.ts.map +1 -1
- package/dist/components/base/Field.parts.js +1 -1
- package/dist/components/base/Field.parts.js.map +1 -1
- package/dist/components/base/MaybeButtonOrLink.d.ts +1 -1
- package/dist/components/base/MaybeButtonOrLink.d.ts.map +1 -1
- package/dist/components/base/Portal.d.ts +1 -1
- package/dist/components/base/Portal.d.ts.map +1 -1
- package/dist/components/base/Portal.js +3 -6
- package/dist/components/base/Portal.js.map +1 -1
- package/dist/components/base/Render.d.ts +21 -6
- package/dist/components/base/Render.d.ts.map +1 -1
- package/dist/components/base/Render.js +3 -2
- package/dist/components/base/Render.js.map +1 -1
- package/dist/components/base/Transition.js +2 -2
- package/dist/components/base/Transition.meta.d.ts +1 -1
- package/dist/components/base/Transition.meta.d.ts.map +1 -1
- package/dist/components/base/Transition.meta.js +1 -0
- package/dist/components/base/Transition.meta.js.map +1 -1
- package/dist/components/base/index.d.ts +1 -2
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/base/index.js +1 -2
- package/dist/components/base/index.js.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useCSSProp.d.ts.map +1 -1
- package/dist/hooks/useCSSProp.js +6 -6
- package/dist/hooks/useCSSProp.js.map +1 -1
- package/dist/hooks/useCSSProps.d.ts +11 -13
- package/dist/hooks/useCSSProps.d.ts.map +1 -1
- package/dist/hooks/useCSSProps.js +11 -19
- package/dist/hooks/useCSSProps.js.map +1 -1
- package/dist/hooks/useCSSPropsPolyfill.d.ts +1 -1
- package/dist/hooks/useCSSPropsPolyfill.d.ts.map +1 -1
- package/dist/hooks/useCSSPropsPolyfill.js +12 -20
- package/dist/hooks/useCSSPropsPolyfill.js.map +1 -1
- package/dist/hooks/useCSSState.d.ts.map +1 -1
- package/dist/hooks/useCSSState.js +7 -3
- package/dist/hooks/useCSSState.js.map +1 -1
- package/dist/hooks/useEffectWithRefDeps.d.ts +2 -2
- package/dist/hooks/useEffectWithRefDeps.d.ts.map +1 -1
- package/dist/hooks/useEffectWithRefDeps.js +1 -1
- package/dist/hooks/useEffectWithRefDeps.js.map +1 -1
- package/dist/hooks/useFirstRender.d.ts +14 -0
- package/dist/hooks/useFirstRender.d.ts.map +1 -0
- package/dist/hooks/useFirstRender.js +20 -0
- package/dist/hooks/useFirstRender.js.map +1 -0
- package/dist/hooks/useGDS.d.ts +1 -1
- package/dist/hooks/usePrevious.d.ts +6 -4
- package/dist/hooks/usePrevious.d.ts.map +1 -1
- package/dist/hooks/usePrevious.js +6 -4
- package/dist/hooks/usePrevious.js.map +1 -1
- package/dist/hooks/useRefWithInit.d.ts +2 -2
- package/dist/hooks/useRefWithInit.d.ts.map +1 -1
- package/dist/hooks/useRefWithInit.js.map +1 -1
- package/dist/hooks/useStyleObserver.d.ts +2 -2
- package/dist/hooks/useStyleObserver.d.ts.map +1 -1
- package/dist/hooks/useStyleObserver.js.map +1 -1
- package/dist/icons/CalendarDynamicIcon.d.ts +8 -5
- package/dist/icons/CalendarDynamicIcon.d.ts.map +1 -1
- package/dist/icons/CalendarDynamicIcon.js +5 -2
- package/dist/icons/CalendarDynamicIcon.js.map +1 -1
- package/dist/icons/CopyInteractiveIcon.d.ts +5 -4
- package/dist/icons/CopyInteractiveIcon.d.ts.map +1 -1
- package/dist/icons/CopyInteractiveIcon.js +2 -2
- package/dist/icons/CopyInteractiveIcon.js.map +1 -1
- package/dist/icons/SidebarLeftInteractiveIcon.d.ts +4 -3
- package/dist/icons/SidebarLeftInteractiveIcon.d.ts.map +1 -1
- package/dist/icons/SidebarLeftInteractiveIcon.js +2 -2
- package/dist/icons/SidebarLeftInteractiveIcon.js.map +1 -1
- package/dist/icons/SidebarRightInteractiveIcon.d.ts +4 -3
- package/dist/icons/SidebarRightInteractiveIcon.d.ts.map +1 -1
- package/dist/icons/SidebarRightInteractiveIcon.js +2 -2
- package/dist/icons/SidebarRightInteractiveIcon.js.map +1 -1
- package/dist/tailwind-plugin.js +5 -5
- package/dist/tailwind-plugin.js.map +1 -1
- package/dist/utils/cn.d.ts +3 -1
- package/dist/utils/cn.d.ts.map +1 -1
- package/dist/utils/cn.js +3 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/getCSSPropsAttributes.d.ts +10 -3
- package/dist/utils/getCSSPropsAttributes.d.ts.map +1 -1
- package/dist/utils/getCSSPropsAttributes.js +4 -5
- package/dist/utils/getCSSPropsAttributes.js.map +1 -1
- package/dist/utils/splitProps.d.ts +1 -4
- package/dist/utils/splitProps.d.ts.map +1 -1
- package/dist/utils/splitProps.js +2 -7
- package/dist/utils/splitProps.js.map +1 -1
- package/dist/utils/trimReactNode.d.ts +10 -8
- package/dist/utils/trimReactNode.d.ts.map +1 -1
- package/dist/utils/trimReactNode.js +10 -8
- package/dist/utils/trimReactNode.js.map +1 -1
- package/package.json +17 -17
- package/src/GDSProvider.tsx +11 -9
- package/src/components/Address.tsx +2 -2
- package/src/components/Avatar.tsx +5 -10
- package/src/components/Breadcrumbs.meta.ts +3 -1
- package/src/components/Breadcrumbs.parts.tsx +16 -28
- package/src/components/Button.tsx +14 -10
- package/src/components/ButtonGroup.meta.ts +8 -0
- package/src/components/ButtonGroup.tsx +1 -5
- package/src/components/Card.meta.ts +1 -0
- package/src/components/Checkbox.meta.ts +1 -5
- package/src/components/Chip.parts.tsx +1 -11
- package/src/components/CodeBlock.parts.tsx +75 -50
- package/src/components/CodeInline.tsx +3 -3
- package/src/components/CopyButton.meta.ts +1 -6
- package/src/components/CopyButton.tsx +3 -4
- package/src/components/CurrencyInput.meta.ts +1 -5
- package/src/components/DescriptionList.parts.tsx +1 -0
- package/src/components/Icon.meta.ts +1 -0
- package/src/components/Icon.tsx +4 -4
- package/src/components/Input.meta.ts +1 -5
- package/src/components/Input.tsx +5 -6
- package/src/components/Keyboard.tsx +1 -1
- package/src/components/Link.meta.ts +1 -0
- package/src/components/Link.tsx +2 -3
- package/src/components/Menu.meta.ts +39 -1
- package/src/components/Menu.parts.tsx +553 -549
- package/src/components/Modal.meta.ts +1 -1
- package/src/components/Modal.parts.tsx +67 -68
- package/src/components/OTCInput.meta.ts +1 -5
- package/src/components/OTCInput.tsx +1 -1
- package/src/components/Radio.meta.ts +1 -5
- package/src/components/SegmentedControl.meta.ts +3 -1
- package/src/components/SegmentedControl.parts.tsx +7 -10
- package/src/components/Stepper.meta.ts +1 -0
- package/src/components/Stepper.parts.tsx +3 -1
- package/src/components/Switch.meta.ts +1 -5
- package/src/components/TabSet.meta.ts +3 -1
- package/src/components/TextArea.meta.ts +1 -5
- package/src/components/ToggleButton.meta.ts +1 -6
- package/src/components/ToggleButton.tsx +1 -1
- package/src/components/Tooltip.meta.ts +13 -2
- package/src/components/Tooltip.parts.tsx +215 -158
- package/src/components/Tooltip.tsx +2 -2
- package/src/components/base/Addon.meta.ts +3 -1
- package/src/components/base/ButtonOrLink.parts.tsx +52 -51
- package/src/components/base/Checkable.parts.tsx +6 -13
- package/src/components/base/Field.parts.tsx +5 -5
- package/src/components/base/Portal.tsx +5 -7
- package/src/components/base/Render.tsx +37 -15
- package/src/components/base/Transition.meta.ts +1 -0
- package/src/components/base/Transition.tsx +2 -2
- package/src/components/base/index.ts +8 -2
- package/src/components/index.ts +1 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useCSSProp.ts +6 -8
- package/src/hooks/useCSSProps.ts +13 -22
- package/src/hooks/useCSSPropsPolyfill.ts +15 -23
- package/src/hooks/useCSSState.ts +11 -6
- package/src/hooks/useEffectWithRefDeps.ts +2 -2
- package/src/hooks/useFirstRender.ts +36 -0
- package/src/hooks/usePrevious.ts +6 -4
- package/src/hooks/useRefWithInit.ts +2 -2
- package/src/hooks/useStyleObserver.ts +5 -1
- package/src/icons/CalendarDynamicIcon.tsx +16 -6
- package/src/icons/CopyInteractiveIcon.tsx +10 -5
- package/src/icons/SidebarLeftInteractiveIcon.tsx +9 -5
- package/src/icons/SidebarRightInteractiveIcon.tsx +9 -5
- package/src/tailwind-plugin.ts +5 -5
- package/src/utils/cn.ts +3 -1
- package/src/utils/getCSSPropsAttributes.ts +13 -8
- package/src/utils/splitProps.ts +9 -9
- package/src/utils/trimReactNode.tsx +10 -8
- package/dist/components/base/ButtonOrLink.meta.d.ts +0 -2
- package/dist/components/base/ButtonOrLink.meta.d.ts.map +0 -1
- package/dist/components/base/ButtonOrLink.meta.js +0 -6
- package/dist/components/base/ButtonOrLink.meta.js.map +0 -1
- package/src/components/base/ButtonOrLink.meta.ts +0 -6
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
createContext,
|
|
5
|
-
isValidElement,
|
|
6
|
-
useContext,
|
|
7
|
-
useRef,
|
|
8
|
-
type ComponentProps,
|
|
9
|
-
type HTMLProps,
|
|
10
|
-
type ReactElement,
|
|
11
|
-
type ReactNode,
|
|
12
|
-
} from 'react'
|
|
3
|
+
import { createContext, useContext, useRef, type ComponentProps, type ReactNode } from 'react'
|
|
13
4
|
import { Menu } from '@base-ui/react/menu'
|
|
14
5
|
import { useMergedRefs } from '@base-ui/utils/useMergedRefs'
|
|
15
6
|
import type { Merge } from 'type-fest'
|
|
@@ -25,27 +16,24 @@ import {
|
|
|
25
16
|
XIcon,
|
|
26
17
|
} from '@graphprotocol/gds-react/icons'
|
|
27
18
|
|
|
28
|
-
import {
|
|
29
|
-
|
|
19
|
+
import {
|
|
20
|
+
useAutoValue,
|
|
21
|
+
useControlled,
|
|
22
|
+
useCSSPropsPolyfill,
|
|
23
|
+
useCSSState,
|
|
24
|
+
useGDS,
|
|
25
|
+
} from '../hooks/index.ts'
|
|
26
|
+
import { cn, getCSSPropsAttributes, splitProps, type OptionValue } from '../utils/index.ts'
|
|
30
27
|
import { renderAddon, type AddonValue } from './base/Addon.tsx'
|
|
31
28
|
import { ButtonOrLink, type ButtonOrLinkProps } from './base/ButtonOrLink.tsx'
|
|
32
|
-
import { Render } from './base/Render.tsx'
|
|
29
|
+
import { Render, type RenderFnProps, type RenderProp } from './base/Render.tsx'
|
|
33
30
|
import { Button } from './Button.tsx'
|
|
34
31
|
import { Checkbox } from './Checkbox.tsx'
|
|
35
|
-
import
|
|
32
|
+
import { MenuMeta, type MenuItemMeta } from './Menu.meta.js'
|
|
33
|
+
import { Tooltip } from './Tooltip.tsx'
|
|
36
34
|
|
|
37
35
|
const MenuTriggerContext = createContext<boolean>(false)
|
|
38
36
|
|
|
39
|
-
type MenuPosition = {
|
|
40
|
-
align: 'start' | 'center' | 'end'
|
|
41
|
-
alignOffset: number
|
|
42
|
-
side: 'top' | 'bottom' | 'inline-start' | 'inline-end'
|
|
43
|
-
gap: number
|
|
44
|
-
matchWidth: boolean | 'at-least' | 'at-most'
|
|
45
|
-
matchHeight: boolean | 'at-least' | 'at-most'
|
|
46
|
-
openOnHover: boolean | number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
37
|
type MenuItemType = 'plain' | 'checkbox' | 'radio'
|
|
50
38
|
|
|
51
39
|
declare namespace GroupProps {
|
|
@@ -71,37 +59,30 @@ declare namespace MenuProps {
|
|
|
71
59
|
extends
|
|
72
60
|
Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'>,
|
|
73
61
|
GDSComponentProps<typeof MenuMeta> {
|
|
74
|
-
open?: boolean | undefined
|
|
75
|
-
defaultOpen?: boolean | undefined
|
|
76
|
-
onOpenChange?: ((open: boolean) => void) | undefined
|
|
77
|
-
/** Element that opens the menu when clicked, or function that returns such an element. */
|
|
78
|
-
trigger:
|
|
79
|
-
| ReactElement
|
|
80
|
-
| ((
|
|
81
|
-
renderProps: Record<string, unknown>,
|
|
82
|
-
state: {
|
|
83
|
-
open: boolean
|
|
84
|
-
setOpen: (open: boolean) => void
|
|
85
|
-
openMenu: () => void
|
|
86
|
-
},
|
|
87
|
-
) => ReactElement)
|
|
88
62
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
63
|
+
* Element that opens the menu when clicked or hovered (depending on `triggerMode`), or a
|
|
64
|
+
* function that returns such an element. If not provided, the menu is rendered inline rather
|
|
65
|
+
* than as a floating overlay, and all positioning props are ignored (`side`, `gap`, `align`,
|
|
66
|
+
* `alignOffset`, `matchTriggerWidth`, and `matchTriggerHeight`).
|
|
67
|
+
*/
|
|
68
|
+
trigger?:
|
|
69
|
+
| RenderProp<
|
|
70
|
+
{ open: boolean; setOpen: (open: boolean) => void; openMenu: () => void },
|
|
71
|
+
{ disabled?: boolean | undefined }
|
|
72
|
+
>
|
|
73
|
+
| undefined
|
|
74
|
+
/**
|
|
75
|
+
* Controls how the menu is opened when `trigger` is an element. Ignored if `trigger` is a
|
|
76
|
+
* function, since you can fully customize the interaction that opens the menu (e.g.
|
|
77
|
+
* `trigger={(props, { openMenu }) => <button {...props} onMouseEnter={openMenu} />}`).
|
|
93
78
|
*
|
|
94
|
-
* @default
|
|
95
|
-
* align: 'start'
|
|
96
|
-
* alignOffset: 0
|
|
97
|
-
* side: 'bottom'
|
|
98
|
-
* gap: 1
|
|
99
|
-
* matchWidth: false
|
|
100
|
-
* matchHeight: false
|
|
101
|
-
* openOnHover: false
|
|
102
|
-
* }
|
|
79
|
+
* @default 'click' // or for nested menus, 'hover'
|
|
103
80
|
*/
|
|
104
|
-
|
|
81
|
+
triggerMode?: 'click' | 'hover' | undefined
|
|
82
|
+
/** Defaults to `true` if `trigger` is not provided, or `undefined` otherwise. */
|
|
83
|
+
open?: boolean | undefined
|
|
84
|
+
defaultOpen?: boolean | undefined
|
|
85
|
+
onOpenChange?: ((open: boolean) => void) | undefined
|
|
105
86
|
header?: ReactNode | undefined
|
|
106
87
|
footer?: ReactNode | undefined
|
|
107
88
|
}
|
|
@@ -115,11 +96,17 @@ export type MenuProps<T extends OptionValue = OptionValue> =
|
|
|
115
96
|
|
|
116
97
|
export function MenuRoot<T extends OptionValue>({
|
|
117
98
|
ref: passedRef,
|
|
99
|
+
trigger,
|
|
100
|
+
triggerMode: passedTriggerMode,
|
|
101
|
+
side,
|
|
102
|
+
gap,
|
|
103
|
+
align,
|
|
104
|
+
alignOffset,
|
|
105
|
+
matchTriggerWidth,
|
|
106
|
+
matchTriggerHeight,
|
|
118
107
|
open: controlledOpen,
|
|
119
108
|
defaultOpen,
|
|
120
109
|
onOpenChange,
|
|
121
|
-
trigger,
|
|
122
|
-
position: passedPosition = {},
|
|
123
110
|
header,
|
|
124
111
|
footer,
|
|
125
112
|
label,
|
|
@@ -128,351 +115,373 @@ export function MenuRoot<T extends OptionValue>({
|
|
|
128
115
|
defaultValue,
|
|
129
116
|
onChange,
|
|
130
117
|
className,
|
|
118
|
+
style,
|
|
131
119
|
children,
|
|
132
120
|
...props
|
|
133
121
|
}: MenuProps<T>) {
|
|
134
|
-
useGDS()
|
|
135
|
-
|
|
136
|
-
const popupRef = useRef<HTMLDivElement>(null)
|
|
137
|
-
const popupPassedRef = useMergedRefs(popupRef, passedRef)
|
|
138
|
-
|
|
139
|
-
const isNested = useContext(MenuItemsContext) !== null
|
|
140
|
-
const Root = isNested ? Menu.SubmenuRoot : Menu.Root
|
|
141
|
-
const Trigger = isNested ? Menu.SubmenuTrigger : Menu.Trigger
|
|
142
|
-
const triggerIsElement = isValidElement<{ disabled?: boolean }>(trigger)
|
|
143
|
-
|
|
144
|
-
const [open, setOpen] = useControlled(controlledOpen, defaultOpen ?? false, onOpenChange)
|
|
145
|
-
|
|
146
|
-
const defaultPosition: MenuPosition = {
|
|
147
|
-
align: 'start',
|
|
148
|
-
alignOffset: 0,
|
|
149
|
-
side: 'bottom',
|
|
150
|
-
gap: 1,
|
|
151
|
-
matchWidth: false,
|
|
152
|
-
matchHeight: false,
|
|
153
|
-
openOnHover: false,
|
|
154
|
-
}
|
|
155
|
-
const defaultNestedPosition: MenuPosition = {
|
|
156
|
-
...defaultPosition,
|
|
157
|
-
alignOffset:
|
|
158
|
-
passedPosition &&
|
|
159
|
-
(passedPosition.side === 'top' ||
|
|
160
|
-
passedPosition.side === 'bottom' ||
|
|
161
|
-
passedPosition.align === 'center')
|
|
162
|
-
? 0
|
|
163
|
-
: -2,
|
|
164
|
-
side: 'inline-end',
|
|
165
|
-
gap:
|
|
166
|
-
passedPosition && (passedPosition.side === 'top' || passedPosition.side === 'bottom') ? 0 : 3,
|
|
167
|
-
openOnHover: true,
|
|
168
|
-
}
|
|
169
|
-
const position = isNested
|
|
170
|
-
? passedPosition === false
|
|
171
|
-
? defaultPosition
|
|
172
|
-
: { ...defaultNestedPosition, ...passedPosition }
|
|
173
|
-
: passedPosition === false
|
|
174
|
-
? false
|
|
175
|
-
: {
|
|
176
|
-
...defaultPosition,
|
|
177
|
-
...passedPosition,
|
|
178
|
-
}
|
|
122
|
+
const { dirProps } = useGDS()
|
|
179
123
|
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
124
|
+
const { rootProps, nestedProps } = splitProps(props)
|
|
125
|
+
|
|
126
|
+
const portalRef = useRef<HTMLDivElement>(null)
|
|
127
|
+
|
|
128
|
+
const [cssPropsPolyfillRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
|
|
129
|
+
MenuMeta,
|
|
130
|
+
{
|
|
131
|
+
side,
|
|
132
|
+
align,
|
|
133
|
+
matchTriggerWidth,
|
|
134
|
+
matchTriggerHeight,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
returnPropValues: { side, gap, align, alignOffset, matchTriggerWidth, matchTriggerHeight },
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
const isNested = useContext(MenuGroupContext) !== null
|
|
142
|
+
const hasTrigger = trigger !== undefined
|
|
143
|
+
const triggerIsElement = hasTrigger && typeof trigger !== 'function'
|
|
144
|
+
const triggerMode = passedTriggerMode ?? (isNested ? 'hover' : 'click')
|
|
145
|
+
const MenuRoot = isNested ? Menu.SubmenuRoot : Menu.Root
|
|
146
|
+
const MenuTrigger = isNested ? Menu.SubmenuTrigger : Menu.Trigger
|
|
147
|
+
|
|
148
|
+
const [open, setOpen] = useControlled(
|
|
149
|
+
controlledOpen ?? (!hasTrigger ? true : undefined),
|
|
150
|
+
defaultOpen ?? false,
|
|
151
|
+
onOpenChange,
|
|
152
|
+
)
|
|
153
|
+
const initialOpen = useRef(open)
|
|
154
|
+
if (!open) {
|
|
155
|
+
initialOpen.current = false
|
|
192
156
|
}
|
|
193
157
|
|
|
194
158
|
return (
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
disabled={triggerIsElement ? Boolean(trigger.props.disabled) : (undefined as never)}
|
|
216
|
-
closeParentOnEsc
|
|
159
|
+
<div
|
|
160
|
+
ref={cssPropsPolyfillRef}
|
|
161
|
+
data-nested={isNested || undefined}
|
|
162
|
+
data-has-trigger={hasTrigger || undefined}
|
|
163
|
+
className={cn(
|
|
164
|
+
`gds-menu root-contents [anchor-scope:all]
|
|
165
|
+
not-data-has-trigger:**:data-base-ui-focus-guard:hidden
|
|
166
|
+
u:rounded-12
|
|
167
|
+
u:data-nested:default-align-offset-[-2]
|
|
168
|
+
u:data-nested:default-gap-3
|
|
169
|
+
u:data-nested:default-side-end`,
|
|
170
|
+
className,
|
|
171
|
+
)}
|
|
172
|
+
{...getCSSPropsAttributes(
|
|
173
|
+
MenuMeta,
|
|
174
|
+
{ side, gap, align, alignOffset, matchTriggerWidth, matchTriggerHeight },
|
|
175
|
+
style,
|
|
176
|
+
)}
|
|
177
|
+
{...cssPropsPolyfillAttributes}
|
|
178
|
+
{...rootProps}
|
|
217
179
|
>
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
`}
|
|
228
|
-
render={
|
|
229
|
-
typeof trigger === 'function'
|
|
230
|
-
? (renderProps: HTMLProps<unknown>) => {
|
|
231
|
-
// Don't pass event handlers and other potentially too specific props (e.g. `type`)
|
|
232
|
-
const filteredRenderProps = Object.fromEntries(
|
|
233
|
-
Object.entries(renderProps).filter(
|
|
234
|
-
([prop]) =>
|
|
235
|
-
prop === 'ref' ||
|
|
236
|
-
prop === 'id' ||
|
|
237
|
-
prop.startsWith('aria-') ||
|
|
238
|
-
prop.startsWith('data-'),
|
|
239
|
-
),
|
|
240
|
-
)
|
|
241
|
-
return trigger(filteredRenderProps, {
|
|
242
|
-
open,
|
|
243
|
-
setOpen,
|
|
244
|
-
openMenu: () => setOpen(true),
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
: // oxlint-disable-next-line no-explicit-any
|
|
248
|
-
(trigger satisfies ReactElement<any> as ReactElement<any>)
|
|
249
|
-
}
|
|
250
|
-
/>
|
|
251
|
-
</MenuTriggerContext.Provider>
|
|
252
|
-
<MaybePortal
|
|
253
|
-
position={position}
|
|
254
|
-
data-positioned={position !== false || undefined}
|
|
255
|
-
data-match-width={position ? position.matchWidth || undefined : undefined}
|
|
256
|
-
data-match-height={position ? position.matchHeight || undefined : undefined}
|
|
257
|
-
className={cn(
|
|
258
|
-
`gds-menu root-flex
|
|
259
|
-
[--available-height-but-not-too-small:max(var(--available-height),--spacing(16))]
|
|
260
|
-
has-nested/menu-footer:[--available-height-but-not-too-small:max(var(--available-height),--spacing(32))]
|
|
261
|
-
has-nested/menu-header:[--available-height-but-not-too-small:max(var(--available-height),--spacing(32))]
|
|
262
|
-
u:w-max
|
|
263
|
-
u:max-w-full
|
|
264
|
-
u:rounded-12
|
|
265
|
-
u:data-positioned:max-h-(--available-height-but-not-too-small)
|
|
266
|
-
u:data-positioned:max-w-(--available-width)
|
|
267
|
-
u:i:data-[match-height=at-least]:min-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
|
|
268
|
-
u:i:data-[match-height=at-most]:max-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
|
|
269
|
-
u:i:data-[match-height=true]:h-(--anchor-height)
|
|
270
|
-
u:i:data-[match-width=at-least]:min-w-[min(var(--anchor-width),var(--available-width))]
|
|
271
|
-
u:i:data-[match-width=at-most]:max-w-[min(var(--anchor-width),var(--available-width))]
|
|
272
|
-
u:i:data-[match-width=true]:w-(--anchor-width)`,
|
|
273
|
-
className,
|
|
274
|
-
)}
|
|
180
|
+
<MenuRoot
|
|
181
|
+
key={`${hasTrigger}-${triggerMode}`}
|
|
182
|
+
open={open}
|
|
183
|
+
onOpenChange={(newOpen) => {
|
|
184
|
+
// A menu with no trigger can still be opened/closed by the consumer, but it should not send events
|
|
185
|
+
if (hasTrigger) setOpen(newOpen)
|
|
186
|
+
}}
|
|
187
|
+
modal={!isNested ? hasTrigger : (undefined as never)}
|
|
188
|
+
closeParentOnEsc
|
|
275
189
|
>
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
190
|
+
<MenuTriggerContext.Provider value={true}>
|
|
191
|
+
{hasTrigger ? (
|
|
192
|
+
// Hoist any tooltip used in `trigger` for its interactions to work properly
|
|
193
|
+
<Tooltip>
|
|
194
|
+
<MenuTrigger
|
|
195
|
+
nativeButton={!isNested}
|
|
196
|
+
// Ensure submenu triggers get disabled properly (see https://github.com/mui/base-ui/issues/3850)
|
|
197
|
+
disabled={triggerIsElement && trigger.props.disabled === true}
|
|
198
|
+
openOnHover={triggerMode === 'hover'}
|
|
199
|
+
data-open-on-hover={triggerMode === 'hover' || undefined}
|
|
200
|
+
className={`
|
|
201
|
+
u:i:open:data-open-on-hover:state-hover
|
|
202
|
+
u:iii:open:not-data-open-on-hover:state-active
|
|
203
|
+
`}
|
|
204
|
+
render={
|
|
205
|
+
triggerIsElement
|
|
206
|
+
? trigger
|
|
207
|
+
: (renderProps: RenderFnProps) => {
|
|
208
|
+
// Don't pass event handlers and other potentially too specific props (e.g. `type`)
|
|
209
|
+
const filteredRenderProps = Object.fromEntries(
|
|
210
|
+
Object.entries(renderProps).filter(
|
|
211
|
+
([prop]) =>
|
|
212
|
+
prop === 'ref' ||
|
|
213
|
+
prop === 'id' ||
|
|
214
|
+
prop === 'className' ||
|
|
215
|
+
prop.startsWith('aria-') ||
|
|
216
|
+
prop.startsWith('data-'),
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
return trigger(filteredRenderProps, {
|
|
220
|
+
open,
|
|
221
|
+
setOpen,
|
|
222
|
+
openMenu: () => setOpen(true),
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/>
|
|
227
|
+
</Tooltip>
|
|
228
|
+
) : (
|
|
288
229
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
230
|
+
* Base UI doesn't explicitly support menus with no `Menu.Trigger`, `Menu.Portal`, or
|
|
231
|
+
* `Menu.Positioner`, so we work around it. We render an invisible trigger to satisfy
|
|
232
|
+
* focus and keyboard expectations (note that it is visible when focused, as an
|
|
233
|
+
* outline). Then, to ensure the menu is rendered inline and not anchored to that
|
|
234
|
+
* trigger, we customize the portal container to be a sibling element (with `contents
|
|
235
|
+
* *:contents` so it's a transparent wrapper), and we apply positioning styles only when
|
|
236
|
+
* a trigger is provided (see `Menu.Positioner`'s `render` prop below). Finally, to
|
|
237
|
+
* prevent focus-trapping behavior that assumes a floating menu, we disable `modal` and
|
|
238
|
+
* hide Base UI’s focus guards with CSS (see root styles above).
|
|
292
239
|
*/
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
240
|
+
<>
|
|
241
|
+
<Menu.Trigger
|
|
242
|
+
aria-label={
|
|
243
|
+
props['aria-label'] ||
|
|
244
|
+
(label && typeof label === 'string' ? label : undefined) ||
|
|
245
|
+
'Menu'
|
|
246
|
+
}
|
|
247
|
+
className={`
|
|
248
|
+
pointer-events-none absolute inset-[anchor(inside)] rounded-inherit [position-anchor:--gds-menu]
|
|
249
|
+
not-open:hidden
|
|
250
|
+
`}
|
|
251
|
+
/>
|
|
252
|
+
<div ref={portalRef} className="contents *:contents" />
|
|
253
|
+
</>
|
|
254
|
+
)}
|
|
255
|
+
</MenuTriggerContext.Provider>
|
|
256
|
+
<Menu.Portal
|
|
257
|
+
container={
|
|
258
|
+
!hasTrigger
|
|
259
|
+
? portalRef
|
|
260
|
+
: /**
|
|
261
|
+
* Specifying `document.body` because while it is the default for top-level menus, a nested menu
|
|
262
|
+
* defaults to using the same portal as its parent, which is not desirable when the parent is
|
|
263
|
+
* rendered inline (it can cause the nested menu to appear behind other elements).
|
|
264
|
+
*/
|
|
265
|
+
document?.body
|
|
266
|
+
}
|
|
267
|
+
>
|
|
268
|
+
<Menu.Positioner
|
|
269
|
+
side={
|
|
270
|
+
cssProps.side === 'start'
|
|
271
|
+
? 'inline-start'
|
|
272
|
+
: cssProps.side === 'end'
|
|
273
|
+
? 'inline-end'
|
|
274
|
+
: cssProps.side
|
|
322
275
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
276
|
+
sideOffset={twToPx(cssProps.gap)}
|
|
277
|
+
align={cssProps.align}
|
|
278
|
+
alignOffset={twToPx(cssProps.alignOffset)}
|
|
279
|
+
collisionPadding={8}
|
|
280
|
+
collisionAvoidance={{
|
|
281
|
+
side: 'flip',
|
|
282
|
+
align: 'flip',
|
|
283
|
+
fallbackAxisSide:
|
|
284
|
+
cssProps.side === 'start' || cssProps.side === 'end' ? 'end' : 'none',
|
|
285
|
+
}}
|
|
286
|
+
data-has-trigger={hasTrigger || undefined}
|
|
287
|
+
data-match-trigger-width={cssProps.matchTriggerWidth || undefined}
|
|
288
|
+
data-match-trigger-height={cssProps.matchTriggerHeight || undefined}
|
|
289
|
+
className={cn(
|
|
290
|
+
`root-flex
|
|
291
|
+
[--available-height-but-not-too-small:max(var(--available-height),--spacing(16))]
|
|
292
|
+
[anchor-name:--gds-menu]
|
|
293
|
+
has-nested/menu-footer:[--available-height-but-not-too-small:max(var(--available-height),--spacing(32))]
|
|
294
|
+
has-nested/menu-header:[--available-height-but-not-too-small:max(var(--available-height),--spacing(32))]
|
|
295
|
+
u:w-max
|
|
296
|
+
u:max-w-full
|
|
297
|
+
u:rounded-12
|
|
298
|
+
u:data-has-trigger:max-h-(--available-height-but-not-too-small)
|
|
299
|
+
u:data-has-trigger:max-w-(--available-width)
|
|
300
|
+
u:i:data-[match-trigger-height=at-least]:min-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
|
|
301
|
+
u:i:data-[match-trigger-height=at-most]:max-h-[min(var(--anchor-height),var(--available-height-but-not-too-small))]
|
|
302
|
+
u:i:data-[match-trigger-height=true]:h-(--anchor-height)
|
|
303
|
+
u:i:data-[match-trigger-width=at-least]:min-w-[min(var(--anchor-width),var(--available-width))]
|
|
304
|
+
u:i:data-[match-trigger-width=at-most]:max-w-[min(var(--anchor-width),var(--available-width))]
|
|
305
|
+
u:i:data-[match-trigger-width=true]:w-(--anchor-width)`,
|
|
306
|
+
className,
|
|
307
|
+
)}
|
|
308
|
+
{...dirProps}
|
|
309
|
+
render={(renderProps) => (
|
|
310
|
+
<div {...renderProps} style={hasTrigger ? renderProps.style : undefined} />
|
|
311
|
+
)}
|
|
312
|
+
>
|
|
313
|
+
<Menu.Popup
|
|
314
|
+
ref={passedRef}
|
|
315
|
+
data-initial-open={initialOpen.current || undefined}
|
|
316
|
+
className={`
|
|
317
|
+
flex grow origin-(--transform-origin) flex-col overflow-clip rounded-inherit bg-muted outline-0 transition
|
|
318
|
+
data-ending-style:scale-95
|
|
319
|
+
data-ending-style:opacity-0
|
|
320
|
+
data-starting-style:not-data-initial-open:scale-95
|
|
321
|
+
data-starting-style:not-data-initial-open:opacity-0
|
|
322
|
+
`}
|
|
323
|
+
{...(nestedProps as ComponentProps<typeof Menu.Popup>)}
|
|
324
|
+
onKeyDown={(event) => {
|
|
325
|
+
nestedProps.onKeyDown?.(event)
|
|
343
326
|
/**
|
|
344
|
-
*
|
|
345
|
-
* a
|
|
346
|
-
*
|
|
327
|
+
* Ensure Shift + Tab doesn't close the menu if there is a previous focusable
|
|
328
|
+
* element that is not a menu item (or if the previous focusable element is a menu
|
|
329
|
+
* item while the element that is currently focused is not).
|
|
347
330
|
*/
|
|
348
|
-
if (event.key === '
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
331
|
+
if (event.key === 'Tab' && event.shiftKey) {
|
|
332
|
+
const focusableElements = [
|
|
333
|
+
...event.currentTarget.querySelectorAll<HTMLElement>(`
|
|
334
|
+
a[href],
|
|
335
|
+
button:enabled,
|
|
336
|
+
input:enabled,
|
|
337
|
+
select:enabled,
|
|
338
|
+
textarea:enabled,
|
|
339
|
+
summary,
|
|
340
|
+
[tabindex]:not([tabindex="-1"]),
|
|
341
|
+
[contenteditable="true"]
|
|
342
|
+
`),
|
|
343
|
+
]
|
|
344
|
+
const previousElement = focusableElements.reverse().find((element) => {
|
|
345
|
+
return (
|
|
346
|
+
element.compareDocumentPosition(document.activeElement!) &
|
|
347
|
+
Node.DOCUMENT_POSITION_FOLLOWING
|
|
348
|
+
)
|
|
349
|
+
})
|
|
350
|
+
const activeElementIsMenuItem =
|
|
351
|
+
document.activeElement?.classList.contains('gds-menu-item')
|
|
352
|
+
const previousElementIsMenuItem =
|
|
353
|
+
previousElement?.classList.contains('gds-menu-item')
|
|
354
|
+
if (
|
|
355
|
+
(previousElement && !previousElementIsMenuItem) ||
|
|
356
|
+
(!activeElementIsMenuItem && previousElementIsMenuItem)
|
|
357
|
+
) {
|
|
358
|
+
event.preventBaseUIHandler()
|
|
359
|
+
return
|
|
360
|
+
}
|
|
360
361
|
}
|
|
361
|
-
// If
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
// If there is a search input in the menu...
|
|
363
|
+
const searchInput = event.currentTarget.querySelector('input[type="search"]')
|
|
364
|
+
if (searchInput instanceof HTMLInputElement) {
|
|
365
|
+
// If the user typed some text, ensure it goes in the input instead of Base UI focusing the item that matches
|
|
366
|
+
if (
|
|
367
|
+
(event.key.length === 1 && event.key !== ' ') ||
|
|
368
|
+
event.key === 'Backspace' ||
|
|
369
|
+
event.key === 'ArrowLeft' ||
|
|
370
|
+
event.key === 'ArrowRight'
|
|
371
|
+
) {
|
|
372
|
+
event.preventBaseUIHandler()
|
|
373
|
+
searchInput.focus()
|
|
374
|
+
} else {
|
|
375
|
+
const searchInputIsFocused = document.activeElement === searchInput
|
|
376
|
+
const menuItems = [
|
|
377
|
+
...event.currentTarget.querySelectorAll<HTMLElement>('.gds-menu-item'),
|
|
378
|
+
]
|
|
379
|
+
const highlightedMenuItem = menuItems.find((item) =>
|
|
380
|
+
item.matches('[data-highlighted]'),
|
|
381
|
+
)
|
|
382
|
+
/**
|
|
383
|
+
* If the user pressed the down arrow while the search input is focused and
|
|
384
|
+
* there is a single menu item in the results, move the focus to it because it
|
|
385
|
+
* may already be highlighted, which would prevent Base UI from focusing it.
|
|
386
|
+
*/
|
|
387
|
+
if (
|
|
388
|
+
event.key === 'ArrowDown' &&
|
|
389
|
+
searchInputIsFocused &&
|
|
390
|
+
menuItems.length === 1
|
|
391
|
+
) {
|
|
392
|
+
event.preventBaseUIHandler()
|
|
393
|
+
menuItems[0]!.focus()
|
|
394
|
+
}
|
|
395
|
+
// Inversely, if the user pressed the up arrow and the focus is on the only menu item, move it back to the search input
|
|
396
|
+
else if (
|
|
397
|
+
event.key === 'ArrowUp' &&
|
|
398
|
+
menuItems.length === 1 &&
|
|
399
|
+
document.activeElement === menuItems[0]
|
|
400
|
+
) {
|
|
401
|
+
event.preventDefault()
|
|
402
|
+
searchInput.focus()
|
|
403
|
+
}
|
|
404
|
+
// If the user pressed Enter while the search input is focused and a menu item is highlighted, select it
|
|
405
|
+
else if (event.key === 'Enter' && searchInputIsFocused && highlightedMenuItem) {
|
|
406
|
+
highlightedMenuItem.focus()
|
|
407
|
+
window.setTimeout(() => {
|
|
408
|
+
highlightedMenuItem.dispatchEvent(
|
|
409
|
+
new KeyboardEvent('keydown', {
|
|
410
|
+
key: 'Enter',
|
|
411
|
+
code: 'Enter',
|
|
412
|
+
keyCode: 13,
|
|
413
|
+
which: 13,
|
|
414
|
+
bubbles: true,
|
|
415
|
+
cancelable: true,
|
|
416
|
+
}),
|
|
417
|
+
)
|
|
418
|
+
}, 0)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
365
421
|
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
{
|
|
397
|
-
|
|
422
|
+
}}
|
|
423
|
+
>
|
|
424
|
+
{/**
|
|
425
|
+
* This div gives the menu a "minimum width" that is not as strict as `min-width` (a fixed width
|
|
426
|
+
* overrides it, for example). A more elegant way to do this would be to change `u:w-max` to
|
|
427
|
+
* `u:w-[calc-size(max-content,max(size,theme(spacing.58)))]`, which would also allow to override
|
|
428
|
+
* that min width if needed, but `calc-size()` is not supported in Safari or Firefox yet.
|
|
429
|
+
*/}
|
|
430
|
+
<div className="w-58" />
|
|
431
|
+
{header ? (
|
|
432
|
+
<div className="nested/menu-header u:shrink-0 u:border-b u:border-muted u:p-4">
|
|
433
|
+
{header}
|
|
434
|
+
</div>
|
|
435
|
+
) : null}
|
|
436
|
+
<div className="scrollbar-thin grow scroll-p-8 overflow-x-clip overflow-y-auto gradient-mask-y">
|
|
437
|
+
<div className="overflow-clip p-2">
|
|
438
|
+
<MenuGroupContext.Provider value="root">
|
|
439
|
+
{/* When `label` or `type` is provided at the root level, automatically wrap `children` in a group */}
|
|
440
|
+
{label !== undefined || type !== undefined ? (
|
|
441
|
+
type === 'radio' ? (
|
|
442
|
+
<MenuGroup
|
|
443
|
+
label={label}
|
|
444
|
+
type={type}
|
|
445
|
+
value={value}
|
|
446
|
+
defaultValue={defaultValue}
|
|
447
|
+
onChange={onChange}
|
|
448
|
+
>
|
|
449
|
+
{children}
|
|
450
|
+
</MenuGroup>
|
|
451
|
+
) : (
|
|
452
|
+
<MenuGroup label={label} type={type}>
|
|
453
|
+
{children}
|
|
454
|
+
</MenuGroup>
|
|
455
|
+
)
|
|
398
456
|
) : (
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
<div className="pointer-events-none absolute inset-0 rounded-inherit border border-muted" />
|
|
417
|
-
</Menu.Popup>
|
|
418
|
-
</MaybePortal>
|
|
419
|
-
</Root>
|
|
457
|
+
children
|
|
458
|
+
)}
|
|
459
|
+
<div className="mt-[calc(-2*(--spacing(2))-1px)] not-preceded-by-peer/menu-group:hidden" />
|
|
460
|
+
</MenuGroupContext.Provider>
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
463
|
+
{footer ? (
|
|
464
|
+
<div className="nested/menu-footer u:shrink-0 u:border-t u:border-muted u:p-4">
|
|
465
|
+
{footer}
|
|
466
|
+
</div>
|
|
467
|
+
) : null}
|
|
468
|
+
<div className="pointer-events-none absolute inset-0 rounded-inherit border border-muted" />
|
|
469
|
+
</Menu.Popup>
|
|
470
|
+
</Menu.Positioner>
|
|
471
|
+
</Menu.Portal>
|
|
472
|
+
</MenuRoot>
|
|
473
|
+
</div>
|
|
420
474
|
)
|
|
421
475
|
}
|
|
422
476
|
MenuRoot.displayName = 'Menu'
|
|
423
477
|
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}: ComponentProps<'div'> & { position: MenuPosition | false }) => {
|
|
432
|
-
const containerRef = useRef<HTMLDivElement>(null)
|
|
433
|
-
const { dirProps } = useGDS()
|
|
434
|
-
|
|
435
|
-
return (
|
|
436
|
-
<>
|
|
437
|
-
{/**
|
|
438
|
-
* Ideally, we simply wouldn't render a `Menu.Portal` or `Menu.Positioner` when `position` is
|
|
439
|
-
* `false`, but they are required by Base UI, so we hack our way around them.
|
|
440
|
-
*/}
|
|
441
|
-
{!position ? <div ref={containerRef} className="contents *:contents" /> : null}
|
|
442
|
-
<Menu.Portal
|
|
443
|
-
container={
|
|
444
|
-
!position
|
|
445
|
-
? containerRef
|
|
446
|
-
: /**
|
|
447
|
-
* Specifying `document.body` because while it is the default for top-level menus, a nested menu
|
|
448
|
-
* defaults to using the same portal as its parent, which is not desirable when the parent is not
|
|
449
|
-
* positioned (it can cause the nested menu to appear behind other elements).
|
|
450
|
-
*/
|
|
451
|
-
document?.body
|
|
452
|
-
}
|
|
453
|
-
>
|
|
454
|
-
<Menu.Positioner
|
|
455
|
-
align={position ? position.align : 'start'}
|
|
456
|
-
alignOffset={position ? twToPx(position.alignOffset) : 0}
|
|
457
|
-
side={position ? position.side : 'bottom'}
|
|
458
|
-
sideOffset={position ? twToPx(position.gap) : 0}
|
|
459
|
-
collisionPadding={8}
|
|
460
|
-
{...dirProps}
|
|
461
|
-
render={({ style, ...otherPositionerProps }) => {
|
|
462
|
-
const allowedPositionerProps = position
|
|
463
|
-
? { style, ...otherPositionerProps }
|
|
464
|
-
: otherPositionerProps
|
|
465
|
-
return <div {...allowedPositionerProps} {...props} />
|
|
466
|
-
}}
|
|
467
|
-
/>
|
|
468
|
-
</Menu.Portal>
|
|
469
|
-
</>
|
|
470
|
-
)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const MenuGroupContext = createContext<{
|
|
474
|
-
type: MenuItemType
|
|
475
|
-
} | null>(null)
|
|
478
|
+
const MenuGroupContext = createContext<
|
|
479
|
+
| {
|
|
480
|
+
type: MenuItemType
|
|
481
|
+
}
|
|
482
|
+
| 'root'
|
|
483
|
+
| null
|
|
484
|
+
>(null)
|
|
476
485
|
|
|
477
486
|
declare namespace MenuGroupProps {
|
|
478
487
|
interface BaseProps extends Omit<ComponentProps<'div'>, 'defaultValue' | 'onChange'> {}
|
|
@@ -495,7 +504,8 @@ export function MenuGroup<T extends OptionValue>({
|
|
|
495
504
|
children,
|
|
496
505
|
...props
|
|
497
506
|
}: MenuGroupProps<T>) {
|
|
498
|
-
const
|
|
507
|
+
const groupContext = useContext(MenuGroupContext)
|
|
508
|
+
const isNested = groupContext !== null && groupContext !== 'root'
|
|
499
509
|
|
|
500
510
|
if (isNested) {
|
|
501
511
|
throw new Error(
|
|
@@ -617,179 +627,173 @@ export function MenuItem<T extends OptionValue>({
|
|
|
617
627
|
const hasAncestorMenuTrigger = useContext(MenuTriggerContext) !== false
|
|
618
628
|
const type = hasAncestorMenuTrigger
|
|
619
629
|
? 'submenu-trigger'
|
|
620
|
-
: (passedType ??
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
630
|
+
: (passedType ??
|
|
631
|
+
(groupContext !== null && groupContext !== 'root' ? groupContext.type : undefined) ??
|
|
632
|
+
'plain')
|
|
633
|
+
|
|
634
|
+
const renderContent = (renderProps: RenderFnProps) => {
|
|
635
|
+
let addonBefore: AddonValue = passedAddonBefore
|
|
636
|
+
let addonInside: AddonValue = undefined
|
|
637
|
+
let addonAfter: AddonValue = passedAddonAfter
|
|
638
|
+
if (type === 'checkbox') {
|
|
639
|
+
if (addonBefore) addonInside = addonBefore
|
|
640
|
+
addonBefore = (
|
|
641
|
+
<Checkbox
|
|
642
|
+
inert={true}
|
|
643
|
+
aria-label="" // Prevent console warning about missing label
|
|
644
|
+
className={`
|
|
645
|
+
@state-checked/menu-item:state-checked
|
|
646
|
+
@state-indeterminate/menu-item:state-indeterminate
|
|
647
|
+
@state-hover/menu-item:state-hover
|
|
648
|
+
@state-active/menu-item:state-active
|
|
649
|
+
@state-disabled/menu-item:state-idle
|
|
650
|
+
@state-disabled/menu-item:@state-checked/menu-item:state-disabled
|
|
651
|
+
`}
|
|
652
|
+
/>
|
|
653
|
+
)
|
|
632
654
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
case 'plain':
|
|
637
|
-
return { disabled } satisfies ComponentProps<typeof Menu.Item>
|
|
638
|
-
case 'checkbox':
|
|
639
|
-
return {
|
|
640
|
-
disabled,
|
|
641
|
-
checked,
|
|
642
|
-
defaultChecked,
|
|
643
|
-
onCheckedChange: onChange,
|
|
644
|
-
closeOnClick: false,
|
|
645
|
-
} satisfies ComponentProps<typeof CheckboxItem>
|
|
646
|
-
case 'radio':
|
|
647
|
-
return {
|
|
648
|
-
value: passedValue !== undefined ? passedValue : autoValue,
|
|
649
|
-
disabled,
|
|
650
|
-
closeOnClick: true,
|
|
651
|
-
} satisfies ComponentProps<typeof Menu.RadioItem>
|
|
652
|
-
case 'submenu-trigger':
|
|
653
|
-
return {}
|
|
655
|
+
if (type === 'radio') {
|
|
656
|
+
if (addonBefore) addonInside = addonBefore
|
|
657
|
+
addonBefore = <CheckIcon alt="" className="@state-unchecked/menu-item:invisible" />
|
|
654
658
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
href !== undefined
|
|
682
|
-
) {
|
|
683
|
-
event.preventBaseUIHandler()
|
|
684
|
-
elementRef.current?.click()
|
|
685
|
-
}
|
|
686
|
-
}}
|
|
687
|
-
render={(renderProps: Record<string, unknown>) => {
|
|
688
|
-
let addonBefore: AddonValue = passedAddonBefore
|
|
689
|
-
let addonInside: AddonValue = undefined
|
|
690
|
-
let addonAfter: AddonValue = passedAddonAfter
|
|
691
|
-
if (type === 'checkbox') {
|
|
692
|
-
if (addonBefore) addonInside = addonBefore
|
|
693
|
-
addonBefore = (
|
|
694
|
-
<Checkbox
|
|
695
|
-
inert={true}
|
|
696
|
-
aria-label="" // Prevent console warning about missing label
|
|
697
|
-
className={`
|
|
698
|
-
@state-checked/menu-item:state-checked
|
|
699
|
-
@state-indeterminate/menu-item:state-indeterminate
|
|
700
|
-
@state-hover/menu-item:state-hover
|
|
701
|
-
@state-active/menu-item:state-active
|
|
702
|
-
@state-disabled/menu-item:state-idle
|
|
703
|
-
@state-disabled/menu-item:@state-checked/menu-item:state-disabled
|
|
704
|
-
`}
|
|
705
|
-
/>
|
|
706
|
-
)
|
|
707
|
-
}
|
|
708
|
-
if (type === 'radio') {
|
|
709
|
-
if (addonBefore) addonInside = addonBefore
|
|
710
|
-
addonBefore = <CheckIcon alt="" className="@state-unchecked/menu-item:invisible" />
|
|
711
|
-
}
|
|
712
|
-
if (loading) {
|
|
713
|
-
addonAfter = <LoadingIcon />
|
|
714
|
-
}
|
|
715
|
-
if (type === 'submenu-trigger' && addonAfter === undefined) {
|
|
716
|
-
addonAfter = <CaretRightIcon alt="" />
|
|
717
|
-
}
|
|
718
|
-
if (href !== undefined && addonAfter === undefined) {
|
|
719
|
-
addonAfter = (
|
|
720
|
-
<ButtonOrLink.Consumer
|
|
721
|
-
render={(buttonOrLinkState) =>
|
|
722
|
-
buttonOrLinkState?.target === '_blank' ? (
|
|
723
|
-
<ArrowUpRightInteractiveIcon hiddenByDefault />
|
|
724
|
-
) : (
|
|
725
|
-
<ArrowRightInteractiveIcon hiddenByDefault />
|
|
726
|
-
)
|
|
727
|
-
}
|
|
728
|
-
/>
|
|
659
|
+
if (loading) {
|
|
660
|
+
addonAfter = <LoadingIcon />
|
|
661
|
+
}
|
|
662
|
+
if (type === 'submenu-trigger' && addonAfter === undefined) {
|
|
663
|
+
addonAfter = <CaretRightIcon alt="" />
|
|
664
|
+
}
|
|
665
|
+
if (href !== undefined && addonAfter === undefined) {
|
|
666
|
+
addonAfter = (
|
|
667
|
+
<ButtonOrLink.Consumer
|
|
668
|
+
render={(buttonOrLinkState) =>
|
|
669
|
+
buttonOrLinkState?.target === '_blank' ? (
|
|
670
|
+
<ArrowUpRightInteractiveIcon hiddenByDefault />
|
|
671
|
+
) : (
|
|
672
|
+
<ArrowRightInteractiveIcon hiddenByDefault />
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
/>
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
return (
|
|
679
|
+
<Render
|
|
680
|
+
render={
|
|
681
|
+
href !== undefined ? (
|
|
682
|
+
<ButtonOrLink href={href} target={target} disabled={disabled} />
|
|
683
|
+
) : (
|
|
684
|
+
<div />
|
|
729
685
|
)
|
|
730
686
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
687
|
+
data-type={type}
|
|
688
|
+
data-variant={variant}
|
|
689
|
+
className={cn(
|
|
690
|
+
// TODO: Replace `active:` with `data-pressed:` when https://github.com/mui/base-ui/issues/1726 is added, to style Space presses as well
|
|
691
|
+
`gds-menu-item root-flex w-full items-center outline-0 select-none u:rounded-6 u:p-2 u:text-16
|
|
692
|
+
u:state-idle
|
|
693
|
+
u:checked:state-checked
|
|
694
|
+
u:indeterminate:state-indeterminate
|
|
695
|
+
u:disabled:pointer-events-none
|
|
696
|
+
u:disabled:state-disabled
|
|
697
|
+
u:data-highlighted:state-hover
|
|
698
|
+
u:data-[variant=danger]:text-status-error-default
|
|
699
|
+
u:ii:active:state-active
|
|
700
|
+
u:ii:data-[type=submenu-trigger]:not-[a]:cursor-default
|
|
701
|
+
u:ii:data-[type=submenu-trigger]:not-[a]:active:state-hover
|
|
702
|
+
${/* Disabled styles */ ''}
|
|
703
|
+
u:*:transition-opacity
|
|
704
|
+
u:disabled:*:opacity-disabled
|
|
705
|
+
u:disabled:*:grayscale`,
|
|
706
|
+
className,
|
|
707
|
+
)}
|
|
708
|
+
{...renderProps}
|
|
709
|
+
>
|
|
710
|
+
<span
|
|
711
|
+
className={`
|
|
712
|
+
absolute inset-0 rounded-inherit opacity-100 filter-none transition
|
|
713
|
+
@state-hover/menu-item:bg-elevated
|
|
714
|
+
@state-active/menu-item:bg-default
|
|
715
|
+
@state-active/menu-item:transition-none
|
|
716
|
+
`}
|
|
717
|
+
/>
|
|
718
|
+
{addonBefore ? (
|
|
719
|
+
<MenuItemAddon
|
|
720
|
+
className={`
|
|
721
|
+
me-2
|
|
722
|
+
has-checkbox:@state-checked/menu-item:opacity-100
|
|
723
|
+
has-checkbox:@state-checked/menu-item:filter-none
|
|
724
|
+
`}
|
|
762
725
|
>
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
726
|
+
{renderAddon(addonBefore)}
|
|
727
|
+
</MenuItemAddon>
|
|
728
|
+
) : null}
|
|
729
|
+
{addonInside ? (
|
|
730
|
+
<MenuItemAddon className="me-1">{renderAddon(addonInside)}</MenuItemAddon>
|
|
731
|
+
) : null}
|
|
732
|
+
<span className="grow truncate pe-1">{children || <> </>}</span>
|
|
733
|
+
{addonAfter ? (
|
|
734
|
+
<MenuItemAddon className="ms-2">{renderAddon(addonAfter)}</MenuItemAddon>
|
|
735
|
+
) : null}
|
|
736
|
+
</Render>
|
|
737
|
+
)
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Filter out undefined values to satisfy Base UI's `exactOptionalPropertyTypes`
|
|
741
|
+
// TODO: Remove when https://github.com/mui/base-ui/pull/3302 is released
|
|
742
|
+
const definedProps = Object.fromEntries(
|
|
743
|
+
Object.entries(props).filter(([, value]) => value !== undefined),
|
|
744
|
+
)
|
|
745
|
+
const baseProps = {
|
|
746
|
+
ref: stateElementPassedRef,
|
|
747
|
+
...state.polyfillAttributes,
|
|
748
|
+
...definedProps,
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (type === 'submenu-trigger') {
|
|
752
|
+
return renderContent(baseProps)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const onKeyDown: ComponentProps<typeof Menu.Item>['onKeyDown'] = (event) => {
|
|
756
|
+
props.onKeyDown?.(event)
|
|
757
|
+
/**
|
|
758
|
+
* Allow Space to activate menu items that are links. This may not be needed in the future (see
|
|
759
|
+
* https://github.com/mui/base-ui/issues/1746).
|
|
760
|
+
*/
|
|
761
|
+
if (!event.baseUIHandlerPrevented && event.key === ' ' && href !== undefined) {
|
|
762
|
+
event.preventBaseUIHandler()
|
|
763
|
+
elementRef.current?.click()
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (type === 'checkbox') {
|
|
768
|
+
return (
|
|
769
|
+
<CheckboxItem
|
|
770
|
+
{...baseProps}
|
|
771
|
+
closeOnClick={false}
|
|
772
|
+
checked={checked}
|
|
773
|
+
defaultChecked={defaultChecked}
|
|
774
|
+
onCheckedChange={onChange}
|
|
775
|
+
onKeyDown={onKeyDown}
|
|
776
|
+
disabled={disabled}
|
|
777
|
+
render={renderContent}
|
|
778
|
+
/>
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (type === 'radio') {
|
|
783
|
+
return (
|
|
784
|
+
<Menu.RadioItem
|
|
785
|
+
{...baseProps}
|
|
786
|
+
closeOnClick={true}
|
|
787
|
+
value={passedValue !== undefined ? passedValue : autoValue}
|
|
788
|
+
onKeyDown={onKeyDown}
|
|
789
|
+
disabled={disabled}
|
|
790
|
+
render={renderContent}
|
|
791
|
+
/>
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return (
|
|
796
|
+
<Menu.Item {...baseProps} onKeyDown={onKeyDown} disabled={disabled} render={renderContent} />
|
|
793
797
|
)
|
|
794
798
|
}
|
|
795
799
|
MenuItem.displayName = 'Menu.Item'
|
|
@@ -869,8 +873,8 @@ export function MenuSearch({
|
|
|
869
873
|
MenuSearch.displayName = 'Menu.Search'
|
|
870
874
|
|
|
871
875
|
/**
|
|
872
|
-
* Transparent wrapper for `Menu.CheckboxItem` to support `indeterminate`.
|
|
873
|
-
*
|
|
876
|
+
* Transparent wrapper for `Menu.CheckboxItem` to support `indeterminate`. This may not be needed in
|
|
877
|
+
* the future (see https://github.com/mui/base-ui/issues/1983).
|
|
874
878
|
*/
|
|
875
879
|
function CheckboxItem({
|
|
876
880
|
checked: controlledChecked,
|