From 47eacd8f45b75bede390ca59d08a5e67dabc49b8 Mon Sep 17 00:00:00 2001 From: atomiks Date: Fri, 13 Sep 2024 15:48:24 +1000 Subject: [PATCH] [Tooltip] Modernize implementation --- docs/data/api/tooltip-root.json | 12 +- .../tooltip-positioner.json | 4 +- .../api-docs/tooltip-root/tooltip-root.json | 12 +- .../src/Tooltip/Arrow/TooltipArrow.tsx | 39 +++-- .../src/Tooltip/Arrow/TooltipArrow.types.ts | 16 -- .../src/Tooltip/Arrow/useTooltipArrow.ts | 27 ++-- .../Tooltip/Arrow/useTooltipArrow.types.ts | 11 -- .../src/Tooltip/Popup/TooltipPopup.tsx | 26 ++-- .../src/Tooltip/Popup/TooltipPopup.types.ts | 13 -- .../src/Tooltip/Popup/useTooltipPopup.ts | 30 ---- .../Tooltip/Popup/useTooltipPopup.types.ts | 9 -- .../Tooltip/Positioner/TooltipPositioner.tsx | 33 +++-- .../Positioner/TooltipPositioner.types.ts | 28 ---- .../Positioner/TooltipPositionerContext.ts | 15 +- .../Positioner/useTooltipPositioner.ts | 139 ++++++++++++++++-- .../Positioner/useTooltipPositioner.types.ts | 124 ---------------- .../src/Tooltip/Provider/TooltipProvider.tsx | 23 ++- .../Tooltip/Provider/TooltipProvider.types.ts | 17 --- .../src/Tooltip/Root/TooltipRoot.test.tsx | 2 +- .../mui-base/src/Tooltip/Root/TooltipRoot.tsx | 41 +++--- .../src/Tooltip/Root/TooltipRoot.types.ts | 76 ---------- .../src/Tooltip/Root/TooltipRootContext.ts | 27 +++- .../src/Tooltip/Root/useTooltipRoot.ts | 89 ++++++++++- .../src/Tooltip/Root/useTooltipRoot.types.ts | 77 ---------- .../src/Tooltip/Trigger/TooltipTrigger.tsx | 30 ++-- .../Tooltip/Trigger/TooltipTrigger.types.ts | 7 - 26 files changed, 412 insertions(+), 515 deletions(-) delete mode 100644 packages/mui-base/src/Tooltip/Arrow/TooltipArrow.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Popup/TooltipPopup.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Popup/useTooltipPopup.ts delete mode 100644 packages/mui-base/src/Tooltip/Popup/useTooltipPopup.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Provider/TooltipProvider.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Root/TooltipRoot.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Root/useTooltipRoot.types.ts delete mode 100644 packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.types.ts diff --git a/docs/data/api/tooltip-root.json b/docs/data/api/tooltip-root.json index f18183291c..268cce71d0 100644 --- a/docs/data/api/tooltip-root.json +++ b/docs/data/api/tooltip-root.json @@ -2,22 +2,22 @@ "props": { "animated": { "type": { "name": "bool" }, "default": "true" }, "closeDelay": { "type": { "name": "number" }, "default": "0" }, - "defaultOpen": { "type": { "name": "bool" } }, + "defaultOpen": { "type": { "name": "bool" }, "default": "false" }, "delay": { "type": { "name": "number" }, "default": "600" }, "delayType": { "type": { "name": "enum", "description": "'hover'
| 'rest'" }, "default": "'rest'" }, - "followCursorAxis": { + "hoverable": { "type": { "name": "bool" }, "default": "true" }, + "onOpenChange": { "type": { "name": "func" } }, + "open": { "type": { "name": "bool" }, "default": "false" }, + "trackCursorAxis": { "type": { "name": "enum", "description": "'both'
| 'none'
| 'x'
| 'y'" }, "default": "'none'" - }, - "hoverable": { "type": { "name": "bool" }, "default": "true" }, - "onOpenChange": { "type": { "name": "func" } }, - "open": { "type": { "name": "bool" } } + } }, "name": "TooltipRoot", "imports": [ diff --git a/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json b/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json index 2c941b6b55..15e86822b7 100644 --- a/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json +++ b/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json @@ -20,9 +20,7 @@ "collisionPadding": { "description": "The padding between the tooltip popup element and the edges of the collision boundary to add whitespace between them to prevent them from touching." }, - "container": { - "description": "The container element to which the tooltip positioner is appended to." - }, + "container": { "description": "The container element the tooltip positioner is appended to." }, "hideWhenDetached": { "description": "Whether the tooltip popup element is hidden if it appears detached from its anchor element due to the anchor element being clipped (or hidden) from view." }, diff --git a/docs/data/translations/api-docs/tooltip-root/tooltip-root.json b/docs/data/translations/api-docs/tooltip-root/tooltip-root.json index 91758e57d3..bd3d672d3e 100644 --- a/docs/data/translations/api-docs/tooltip-root/tooltip-root.json +++ b/docs/data/translations/api-docs/tooltip-root/tooltip-root.json @@ -6,23 +6,21 @@ }, "closeDelay": { "description": "The delay in milliseconds until the tooltip popup is closed." }, "defaultOpen": { - "description": "Specifies whether the tooltip is open initially when uncontrolled." + "description": "Whether the tooltip popup is open by default. Use when uncontrolled." }, "delay": { "description": "The delay in milliseconds until the tooltip popup is opened." }, "delayType": { "description": "The delay type to use. rest means the delay represents how long the user's cursor must rest on the trigger before the tooltip popup is opened. hover means the delay represents how long to wait as soon as the user's cursor has entered the trigger." }, - "followCursorAxis": { - "description": "Determines which axis the tooltip should follow the cursor on." - }, "hoverable": { - "description": "Whether the user can move their cursor from the trigger to the tooltip popup without it closing." + "description": "Whether the can move their cursor from the trigger element toward the tooltip popup element without it closing using a "safe polygon" technique." }, "onOpenChange": { "description": "Callback fired when the tooltip popup is requested to be opened or closed. Use when controlled." }, - "open": { - "description": "If true, the tooltip popup is open. Use when controlled." + "open": { "description": "Whether the tooltip popup is open. Use when controlled." }, + "trackCursorAxis": { + "description": "Determines which axis the tooltip should track the cursor on." } }, "classDescriptions": {} diff --git a/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.tsx b/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.tsx index b70c1e86e4..5e428d9110 100644 --- a/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.tsx +++ b/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.tsx @@ -1,11 +1,21 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import type { TooltipArrowOwnerState, TooltipArrowProps } from './TooltipArrow.types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useForkRef } from '../../utils/useForkRef'; import { useTooltipPositionerContext } from '../Positioner/TooltipPositionerContext'; import { useTooltipArrow } from './useTooltipArrow'; +import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps'; +import type { BaseUIComponentProps } from '../../utils/types'; +import type { Side, Alignment } from '../../utils/useAnchorPositioning'; + +const customStyleHookMapping: CustomStyleHookMapping = { + open(value) { + return { + 'data-state': value ? 'open' : 'closed', + }; + }, +}; /** * Renders an arrow that points to the center of the anchor element. @@ -19,7 +29,7 @@ import { useTooltipArrow } from './useTooltipArrow'; * - [TooltipArrow API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipArrow) */ const TooltipArrow = React.forwardRef(function TooltipArrow( - props: TooltipArrowProps, + props: TooltipArrow.Props, forwardedRef: React.ForwardedRef, ) { const { className, render, hideWhenUncentered = false, ...otherProps } = props; @@ -32,7 +42,7 @@ const TooltipArrow = React.forwardRef(function TooltipArrow( hidden: hideWhenUncentered && arrowUncentered, }); - const ownerState: TooltipArrowOwnerState = React.useMemo( + const ownerState: TooltipArrow.OwnerState = React.useMemo( () => ({ open, side, @@ -50,18 +60,27 @@ const TooltipArrow = React.forwardRef(function TooltipArrow( className, ref: mergedRef, extraProps: otherProps, - customStyleHookMapping: { - open(value) { - return { - 'data-state': value ? 'open' : 'closed', - }; - }, - }, + customStyleHookMapping, }); return renderElement(); }); +namespace TooltipArrow { + export interface OwnerState { + open: boolean; + side: Side; + alignment: Alignment; + } + export interface Props extends BaseUIComponentProps<'div', OwnerState> { + /** + * If `true`, the arrow will be hidden when it can't point to the center of the anchor element. + * @default false + */ + hideWhenUncentered?: boolean; + } +} + TooltipArrow.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ diff --git a/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.types.ts b/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.types.ts deleted file mode 100644 index c939c8ac38..0000000000 --- a/packages/mui-base/src/Tooltip/Arrow/TooltipArrow.types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Side, Alignment } from '../../utils/useAnchorPositioning'; -import type { BaseUIComponentProps } from '../../utils/types'; - -export type TooltipArrowOwnerState = { - open: boolean; - side: Side; - alignment: Alignment; -}; - -export interface TooltipArrowProps extends BaseUIComponentProps<'div', TooltipArrowOwnerState> { - /** - * If `true`, the arrow will be hidden when it can't point to the center of the anchor element. - * @default false - */ - hideWhenUncentered?: boolean; -} diff --git a/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts b/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts index 937001b4fc..85c05cb2cf 100644 --- a/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts +++ b/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts @@ -1,21 +1,20 @@ -'use client'; import * as React from 'react'; import { mergeReactProps } from '../../utils/mergeReactProps'; -import type { - UseTooltipArrowParameters, - UseTooltipArrowReturnValue, -} from './useTooltipArrow.types'; +import type { GenericHTMLProps } from '../../utils/types'; -export function useTooltipArrow(params: UseTooltipArrowParameters): UseTooltipArrowReturnValue { - const { arrowStyles } = params; +export function useTooltipArrow(params: useTooltipArrow.Parameters): useTooltipArrow.ReturnValue { + const { arrowStyles, hidden } = params; const getArrowProps = React.useCallback( (externalProps = {}) => { return mergeReactProps<'div'>(externalProps, { - style: arrowStyles, + style: { + ...arrowStyles, + ...(hidden && { visibility: 'hidden' }), + }, }); }, - [arrowStyles], + [arrowStyles, hidden], ); return React.useMemo( @@ -25,3 +24,13 @@ export function useTooltipArrow(params: UseTooltipArrowParameters): UseTooltipAr [getArrowProps], ); } + +namespace useTooltipArrow { + export interface Parameters { + arrowStyles: React.CSSProperties; + hidden?: boolean; + } + export interface ReturnValue { + getArrowProps: (props?: GenericHTMLProps) => GenericHTMLProps; + } +} diff --git a/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.types.ts b/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.types.ts deleted file mode 100644 index ca20da129d..0000000000 --- a/packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type * as React from 'react'; -import type { GenericHTMLProps } from '../../utils/types'; - -export interface UseTooltipArrowParameters { - arrowStyles: React.CSSProperties; - hidden?: boolean; -} - -export interface UseTooltipArrowReturnValue { - getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; -} diff --git a/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx b/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx index f3fde617de..9cf7eabd38 100644 --- a/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx +++ b/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx @@ -1,12 +1,12 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import type { TooltipPopupOwnerState, TooltipPopupProps } from './TooltipPopup.types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useTooltipRootContext } from '../Root/TooltipRootContext'; import { useTooltipPositionerContext } from '../Positioner/TooltipPositionerContext'; -import { useTooltipPopup } from './useTooltipPopup'; import { useForkRef } from '../../utils/useForkRef'; +import type { BaseUIComponentProps } from '../../utils/types'; +import type { Alignment, Side } from '../../utils/useAnchorPositioning'; /** * The tooltip popup element. @@ -20,7 +20,7 @@ import { useForkRef } from '../../utils/useForkRef'; * - [TooltipPopup API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipPopup) */ const TooltipPopup = React.forwardRef(function TooltipPopup( - props: TooltipPopupProps, + props: TooltipPopup.Props, forwardedRef: React.ForwardedRef, ) { const { className, render, ...otherProps } = props; @@ -29,11 +29,7 @@ const TooltipPopup = React.forwardRef(function TooltipPopup( useTooltipRootContext(); const { side, alignment } = useTooltipPositionerContext(); - const { getPopupProps } = useTooltipPopup({ - getProps: getRootPopupProps, - }); - - const ownerState: TooltipPopupOwnerState = React.useMemo( + const ownerState: TooltipPopup.OwnerState = React.useMemo( () => ({ open, side, @@ -50,7 +46,7 @@ const TooltipPopup = React.forwardRef(function TooltipPopup( // The content element needs to be a child of a wrapper floating element in order to avoid // conflicts with CSS transitions and the positioning transform. const { renderElement } = useComponentRenderer({ - propGetter: getPopupProps, + propGetter: getRootPopupProps, render: render ?? 'div', className, ownerState, @@ -74,6 +70,18 @@ const TooltipPopup = React.forwardRef(function TooltipPopup( return renderElement(); }); +namespace TooltipPopup { + export interface OwnerState { + open: boolean; + side: Side; + alignment: Alignment; + instant: 'delay' | 'focus' | 'dismiss' | undefined; + entering: boolean; + exiting: boolean; + } + export interface Props extends BaseUIComponentProps<'div', OwnerState> {} +} + TooltipPopup.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ diff --git a/packages/mui-base/src/Tooltip/Popup/TooltipPopup.types.ts b/packages/mui-base/src/Tooltip/Popup/TooltipPopup.types.ts deleted file mode 100644 index de2df2cadd..0000000000 --- a/packages/mui-base/src/Tooltip/Popup/TooltipPopup.types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Side, Alignment } from '../../utils/useAnchorPositioning'; -import type { BaseUIComponentProps } from '../../utils/types'; - -export type TooltipPopupOwnerState = { - open: boolean; - side: Side; - alignment: Alignment; - instant: 'delay' | 'focus' | 'dismiss' | undefined; - entering: boolean; - exiting: boolean; -}; - -export interface TooltipPopupProps extends BaseUIComponentProps<'div', TooltipPopupOwnerState> {} diff --git a/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.ts b/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.ts deleted file mode 100644 index cba55f5864..0000000000 --- a/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.ts +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; -import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; -import type { - UseTooltipPopupParameters, - UseTooltipPopupReturnValue, -} from './useTooltipPopup.types'; - -export function useTooltipPopup(params: UseTooltipPopupParameters): UseTooltipPopupReturnValue { - const { getProps } = params; - - const getPopupProps = React.useCallback( - (externalProps = {}) => { - return mergeReactProps<'div'>(getProps(externalProps), { - style: { - // must be relative to the element. - position: 'relative', - }, - }); - }, - [getProps], - ); - - return React.useMemo( - () => ({ - getPopupProps, - }), - [getPopupProps], - ); -} diff --git a/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.types.ts b/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.types.ts deleted file mode 100644 index 0c8b6fbe1b..0000000000 --- a/packages/mui-base/src/Tooltip/Popup/useTooltipPopup.types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { GenericHTMLProps } from '../../utils/types'; - -export interface UseTooltipPopupParameters { - getProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; -} - -export interface UseTooltipPopupReturnValue { - getPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; -} diff --git a/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.tsx b/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.tsx index 8c41bc631d..37e91db414 100644 --- a/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.tsx +++ b/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.tsx @@ -5,14 +5,11 @@ import { FloatingPortal } from '@floating-ui/react'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { HTMLElementType } from '../../utils/proptypes'; import { useForkRef } from '../../utils/useForkRef'; -import type { - TooltipPositionerContextValue, - TooltipPositionerOwnerState, - TooltipPositionerProps, -} from './TooltipPositioner.types'; import { useTooltipRootContext } from '../Root/TooltipRootContext'; -import { useTooltipPositioner } from './useTooltipPositioner'; import { TooltipPositionerContext } from './TooltipPositionerContext'; +import { useTooltipPositioner } from './useTooltipPositioner'; +import type { BaseUIComponentProps } from '../../utils/types'; +import type { Side, Alignment } from '../../utils/useAnchorPositioning'; /** * The tooltip positioner element. @@ -26,7 +23,7 @@ import { TooltipPositionerContext } from './TooltipPositionerContext'; * - [TooltipPositioner API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipPositioner) */ const TooltipPositioner = React.forwardRef(function TooltipPositioner( - props: TooltipPositionerProps, + props: TooltipPositioner.Props, forwardedRef: React.ForwardedRef, ) { const { @@ -77,7 +74,7 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( const mergedRef = useForkRef(forwardedRef, setPositionerElement); - const ownerState: TooltipPositionerOwnerState = React.useMemo( + const ownerState: TooltipPositioner.OwnerState = React.useMemo( () => ({ open, side: positioner.side, @@ -86,7 +83,7 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( [open, positioner.side, positioner.alignment], ); - const contextValue: TooltipPositionerContextValue = React.useMemo( + const contextValue: TooltipPositionerContext = React.useMemo( () => ({ ...ownerState, arrowRef: positioner.arrowRef, @@ -117,6 +114,22 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( ); }); +namespace TooltipPositioner { + export interface OwnerState { + open: boolean; + side: Side; + alignment: Alignment; + } + export interface Props + extends BaseUIComponentProps<'div', OwnerState>, + Omit { + /** + * The container element the tooltip positioner is appended to. + */ + container?: HTMLElement | null | React.MutableRefObject; + } +} + TooltipPositioner.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ @@ -184,7 +197,7 @@ TooltipPositioner.propTypes /* remove-proptypes */ = { }), ]), /** - * The container element to which the tooltip positioner is appended to. + * The container element the tooltip positioner is appended to. */ container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ HTMLElementType, diff --git a/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.types.ts b/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.types.ts deleted file mode 100644 index d8e9f2ed7e..0000000000 --- a/packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.types.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type * as React from 'react'; -import type { Side, Alignment } from '../../utils/useAnchorPositioning'; -import type { BaseUIComponentProps } from '../../utils/types'; -import { TooltipPositionerParameters } from './useTooltipPositioner.types'; - -export interface TooltipPositionerContextValue { - open: boolean; - side: Side; - alignment: Alignment; - arrowRef: React.MutableRefObject; - arrowUncentered: boolean; - arrowStyles: React.CSSProperties; -} - -export type TooltipPositionerOwnerState = { - open: boolean; - side: Side; - alignment: Alignment; -}; - -export interface TooltipPositionerProps - extends TooltipPositionerParameters, - BaseUIComponentProps<'div', TooltipPositionerOwnerState> { - /** - * The container element to which the tooltip positioner is appended to. - */ - container?: HTMLElement | null | React.MutableRefObject; -} diff --git a/packages/mui-base/src/Tooltip/Positioner/TooltipPositionerContext.ts b/packages/mui-base/src/Tooltip/Positioner/TooltipPositionerContext.ts index 4439c3aa5d..6a73def57e 100644 --- a/packages/mui-base/src/Tooltip/Positioner/TooltipPositionerContext.ts +++ b/packages/mui-base/src/Tooltip/Positioner/TooltipPositionerContext.ts @@ -1,9 +1,16 @@ import * as React from 'react'; -import type { TooltipPositionerContextValue } from './TooltipPositioner.types'; +import type { Side, Alignment } from '../../utils/useAnchorPositioning'; -export const TooltipPositionerContext = React.createContext( - null, -); +export interface TooltipPositionerContext { + open: boolean; + side: Side; + alignment: Alignment; + arrowRef: React.MutableRefObject; + arrowUncentered: boolean; + arrowStyles: React.CSSProperties; +} + +export const TooltipPositionerContext = React.createContext(null); export function useTooltipPositionerContext() { const context = React.useContext(TooltipPositionerContext); diff --git a/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.ts b/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.ts index 1834ebd134..4b423ed56a 100644 --- a/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.ts +++ b/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.ts @@ -1,16 +1,13 @@ -'use client'; import * as React from 'react'; -import type { - UseTooltipPositionerParameters, - UseTooltipPositionerReturnValue, -} from './useTooltipPositioner.types'; +import type { Boundary, Padding, VirtualElement, FloatingRootContext } from '@floating-ui/react'; import { mergeReactProps } from '../../utils/mergeReactProps'; import { useAnchorPositioning } from '../../utils/useAnchorPositioning'; +import type { GenericHTMLProps } from '../../utils/types'; export function useTooltipPositioner( - params: UseTooltipPositionerParameters, -): UseTooltipPositionerReturnValue { - const { open = false, keepMounted = false, followCursorAxis = 'none' } = params; + params: useTooltipPositioner.Parameters, +): useTooltipPositioner.ReturnValue { + const { open = false, keepMounted = false, trackCursorAxis = 'none' } = params; const { positionerStyles, @@ -22,7 +19,7 @@ export function useTooltipPositioner( renderedAlignment, } = useAnchorPositioning(params); - const getPositionerProps: UseTooltipPositionerReturnValue['getPositionerProps'] = + const getPositionerProps: useTooltipPositioner.ReturnValue['getPositionerProps'] = React.useCallback( (externalProps = {}) => { const hiddenStyles: React.CSSProperties = {}; @@ -31,7 +28,7 @@ export function useTooltipPositioner( hiddenStyles.pointerEvents = 'none'; } - if (followCursorAxis === 'both') { + if (trackCursorAxis === 'both') { hiddenStyles.pointerEvents = 'none'; } @@ -46,7 +43,7 @@ export function useTooltipPositioner( }, }); }, - [positionerStyles, hidden, followCursorAxis, open, keepMounted], + [positionerStyles, hidden, trackCursorAxis, open, keepMounted], ); return React.useMemo( @@ -61,3 +58,123 @@ export function useTooltipPositioner( [getPositionerProps, arrowRef, arrowUncentered, renderedSide, renderedAlignment, arrowStyles], ); } + +export namespace useTooltipPositioner { + export interface Parameters { + /** + * The element to which the tooltip popup element is anchored to. + */ + anchor?: + | Element + | null + | VirtualElement + | React.MutableRefObject + | (() => Element | VirtualElement | null); + /** + * Whether the tooltip popup is open. + * @default false + */ + open?: boolean; + /** + * The CSS position strategy for positioning the tooltip popup element. + * @default 'absolute' + */ + positionStrategy?: 'absolute' | 'fixed'; + /** + * The side of the anchor element that the tooltip popup element should be placed at. + * @default 'top' + */ + side?: 'top' | 'right' | 'bottom' | 'left'; + /** + * The gap between the anchor element and the tooltip popup element. + * @default 0 + */ + sideOffset?: number; + /** + * The alignment of the tooltip popup element to the anchor element along its cross axis. + * @default 'center' + */ + alignment?: 'start' | 'end' | 'center'; + /** + * The offset of the tooltip popup element along its alignment axis. + * @default 0 + */ + alignmentOffset?: number; + /** + * The boundary that the tooltip popup element should be constrained to. + * @default 'clippingAncestors' + */ + collisionBoundary?: Boundary; + /** + * The padding between the tooltip popup element and the edges of the collision boundary to add + * whitespace between them to prevent them from touching. + * @default 5 + */ + collisionPadding?: Padding; + /** + * Whether the tooltip popup element is hidden if it appears detached from its anchor element due + * to the anchor element being clipped (or hidden) from view. + * @default false + */ + hideWhenDetached?: boolean; + /** + * Whether to allow the tooltip to remain stuck in view while the anchor element is scrolled out + * of view. + * @default false + */ + sticky?: boolean; + /** + * Determines the padding between the arrow and the tooltip popup edges. Useful when the tooltip + * popup element has rounded corners via `border-radius`. + * @default 5 + */ + arrowPadding?: number; + /** + * Whether the tooltip popup remains mounted in the DOM while closed. + * @default false + */ + keepMounted?: boolean; + /** + * Whether the tooltip popup continuously tracks its anchor after the initial positioning upon + * mount. + * @default true + */ + trackAnchor?: boolean; + /** + * The tooltip root context. + */ + floatingRootContext?: FloatingRootContext; + /** + * Determines which axis the tooltip should track the cursor on. + * @default 'none' + */ + trackCursorAxis?: 'none' | 'x' | 'y' | 'both'; + } + + export interface ReturnValue { + /** + * Props to spread on the positioner element. + */ + getPositionerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; + /** + * The ref for the arrow element. + */ + arrowRef: React.MutableRefObject; + /** + * Determines if the arrow cannot be centered. + */ + arrowUncentered: boolean; + /** + * Styles to apply to the popup arrow element. + */ + arrowStyles: React.CSSProperties; + /** + * The rendered side of the tooltip popup element. + */ + side: 'top' | 'right' | 'bottom' | 'left'; + /** + * The rendered alignment of the tooltip popup element. + */ + alignment: 'start' | 'end' | 'center'; + } +} diff --git a/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.types.ts b/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.types.ts deleted file mode 100644 index e0d13e6355..0000000000 --- a/packages/mui-base/src/Tooltip/Positioner/useTooltipPositioner.types.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type * as React from 'react'; -import type { Boundary, Padding, VirtualElement, FloatingRootContext } from '@floating-ui/react'; -import type { GenericHTMLProps } from '../../utils/types'; - -export interface TooltipPositionerParameters { - /** - * The element to which the tooltip popup element is anchored to. - */ - anchor?: - | Element - | null - | VirtualElement - | React.MutableRefObject - | (() => Element | VirtualElement | null); - /** - * Whether the tooltip popup is open. - * @default false - */ - open?: boolean; - /** - * The CSS position strategy for positioning the tooltip popup element. - * @default 'absolute' - */ - positionStrategy?: 'absolute' | 'fixed'; - /** - * The side of the anchor element that the tooltip popup element should be placed at. - * @default 'top' - */ - side?: 'top' | 'right' | 'bottom' | 'left'; - /** - * The gap between the anchor element and the tooltip popup element. - * @default 0 - */ - sideOffset?: number; - /** - * The alignment of the tooltip popup element to the anchor element along its cross axis. - * @default 'center' - */ - alignment?: 'start' | 'end' | 'center'; - /** - * The offset of the tooltip popup element along its alignment axis. - * @default 0 - */ - alignmentOffset?: number; - /** - * The boundary that the tooltip popup element should be constrained to. - * @default 'clippingAncestors' - */ - collisionBoundary?: Boundary; - /** - * The padding between the tooltip popup element and the edges of the collision boundary to add - * whitespace between them to prevent them from touching. - * @default 5 - */ - collisionPadding?: Padding; - /** - * Whether the tooltip popup element is hidden if it appears detached from its anchor element due - * to the anchor element being clipped (or hidden) from view. - * @default false - */ - hideWhenDetached?: boolean; - /** - * Whether to allow the tooltip to remain stuck in view while the anchor element is scrolled out - * of view. - * @default false - */ - sticky?: boolean; - /** - * Determines the padding between the arrow and the tooltip popup edges. Useful when the tooltip - * popup element has rounded corners via `border-radius`. - * @default 5 - */ - arrowPadding?: number; - /** - * Whether the tooltip popup remains mounted in the DOM while closed. - * @default false - */ - keepMounted?: boolean; - /** - * Whether the tooltip popup continuously tracks its anchor after the initial positioning upon - * mount. - * @default true - */ - trackAnchor?: boolean; -} - -export interface UseTooltipPositionerParameters extends TooltipPositionerParameters { - /** - * The tooltip root context. - */ - floatingRootContext?: FloatingRootContext; - /** - * Determines which axis the tooltip should follow the cursor on. - * @default 'none' - */ - followCursorAxis?: 'none' | 'x' | 'y' | 'both'; -} - -export interface UseTooltipPositionerReturnValue { - /** - * Props to spread on the positioner element. - */ - getPositionerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - /** - * The ref for the arrow element. - */ - arrowRef: React.MutableRefObject; - /** - * Determines if the arrow cannot be centered. - */ - arrowUncentered: boolean; - /** - * Styles to apply to the popup arrow element. - */ - arrowStyles: React.CSSProperties; - /** - * The rendered side of the tooltip popup element. - */ - side: 'top' | 'right' | 'bottom' | 'left'; - /** - * The rendered alignment of the tooltip popup element. - */ - alignment: 'start' | 'end' | 'center'; -} diff --git a/packages/mui-base/src/Tooltip/Provider/TooltipProvider.tsx b/packages/mui-base/src/Tooltip/Provider/TooltipProvider.tsx index 2a52ee32a7..6fc2580bbf 100644 --- a/packages/mui-base/src/Tooltip/Provider/TooltipProvider.tsx +++ b/packages/mui-base/src/Tooltip/Provider/TooltipProvider.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { FloatingDelayGroup } from '@floating-ui/react'; -import type { TooltipProviderProps } from './TooltipProvider.types'; /** * Provides a shared delay for tooltips so that once a tooltip is shown, the rest of the tooltips in @@ -16,7 +15,7 @@ import type { TooltipProviderProps } from './TooltipProvider.types'; * * - [TooltipProvider API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipProvider) */ -function TooltipProvider(props: TooltipProviderProps) { +function TooltipProvider(props: TooltipProvider.Props) { const { delay, closeDelay, timeout = 400 } = props; return ( @@ -25,6 +24,26 @@ function TooltipProvider(props: TooltipProviderProps) { ); } +namespace TooltipProvider { + export interface Props { + children?: React.ReactNode; + /** + * The delay in milliseconds until tooltips within the group are open. + */ + delay?: number; + /** + * The delay in milliseconds until tooltips within the group are closed. + */ + closeDelay?: number; + /** + * The timeout in milliseconds until the grouping logic is no longer active after the last tooltip + * in the group has closed. + * @default 400 + */ + timeout?: number; + } +} + TooltipProvider.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ diff --git a/packages/mui-base/src/Tooltip/Provider/TooltipProvider.types.ts b/packages/mui-base/src/Tooltip/Provider/TooltipProvider.types.ts deleted file mode 100644 index e9c5e4567c..0000000000 --- a/packages/mui-base/src/Tooltip/Provider/TooltipProvider.types.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface TooltipProviderProps { - children?: React.ReactNode; - /** - * The delay in milliseconds until tooltips within the group are open. - */ - delay?: number; - /** - * The delay in milliseconds until tooltips within the group are closed. - */ - closeDelay?: number; - /** - * The timeout in milliseconds until the grouping logic is no longer active after the last tooltip - * in the group has closed. - * @default 400 - */ - timeout?: number; -} diff --git a/packages/mui-base/src/Tooltip/Root/TooltipRoot.test.tsx b/packages/mui-base/src/Tooltip/Root/TooltipRoot.test.tsx index 37a1073d06..5f03c1aa4f 100644 --- a/packages/mui-base/src/Tooltip/Root/TooltipRoot.test.tsx +++ b/packages/mui-base/src/Tooltip/Root/TooltipRoot.test.tsx @@ -6,7 +6,7 @@ import { spy } from 'sinon'; import { createRenderer } from '#test-utils'; import { OPEN_DELAY } from '../utils/constants'; -function Root(props: Tooltip.RootProps) { +function Root(props: Tooltip.Root.Props) { return ; } diff --git a/packages/mui-base/src/Tooltip/Root/TooltipRoot.tsx b/packages/mui-base/src/Tooltip/Root/TooltipRoot.tsx index 6a886b73db..6839ad95f8 100644 --- a/packages/mui-base/src/Tooltip/Root/TooltipRoot.tsx +++ b/packages/mui-base/src/Tooltip/Root/TooltipRoot.tsx @@ -1,7 +1,6 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import type { TooltipRootProps } from './TooltipRoot.types'; import { TooltipRootContext } from './TooltipRootContext'; import { useTooltipRoot } from './useTooltipRoot'; import { OPEN_DELAY } from '../utils/constants'; @@ -17,14 +16,14 @@ import { OPEN_DELAY } from '../utils/constants'; * * - [TooltipRoot API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipRoot) */ -function TooltipRoot(props: TooltipRootProps) { +function TooltipRoot(props: TooltipRoot.Props) { const { delayType = 'rest', delay, closeDelay, hoverable = true, animated = true, - followCursorAxis = 'none', + trackCursorAxis = 'none', } = props; const delayWithDefault = delay ?? OPEN_DELAY; @@ -48,7 +47,7 @@ function TooltipRoot(props: TooltipRootProps) { } = useTooltipRoot({ hoverable, animated, - followCursorAxis, + trackCursorAxis, delay, delayType, closeDelay, @@ -75,7 +74,7 @@ function TooltipRoot(props: TooltipRootProps) { getRootTriggerProps, getRootPopupProps, floatingRootContext, - followCursorAxis, + trackCursorAxis, transitionStatus, }), [ @@ -95,7 +94,7 @@ function TooltipRoot(props: TooltipRootProps) { getRootTriggerProps, getRootPopupProps, floatingRootContext, - followCursorAxis, + trackCursorAxis, transitionStatus, ], ); @@ -105,6 +104,13 @@ function TooltipRoot(props: TooltipRootProps) { ); } +namespace TooltipRoot { + export interface OwnerState {} + export interface Props extends useTooltipRoot.Parameters { + children?: React.ReactNode; + } +} + TooltipRoot.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ @@ -126,7 +132,8 @@ TooltipRoot.propTypes /* remove-proptypes */ = { */ closeDelay: PropTypes.number, /** - * Specifies whether the tooltip is open initially when uncontrolled. + * Whether the tooltip popup is open by default. Use when uncontrolled. + * @default false */ defaultOpen: PropTypes.bool, /** @@ -142,25 +149,25 @@ TooltipRoot.propTypes /* remove-proptypes */ = { */ delayType: PropTypes.oneOf(['hover', 'rest']), /** - * Determines which axis the tooltip should follow the cursor on. - * @default 'none' - */ - followCursorAxis: PropTypes.oneOf(['both', 'none', 'x', 'y']), - /** - * Whether the user can move their cursor from the trigger to the tooltip popup without it - * closing. + * Whether the can move their cursor from the trigger element toward the tooltip popup element + * without it closing using a "safe polygon" technique. * @default true */ hoverable: PropTypes.bool, /** - * Callback fired when the tooltip popup is requested to be opened or closed. Use when - * controlled. + * Callback fired when the tooltip popup is requested to be opened or closed. Use when controlled. */ onOpenChange: PropTypes.func, /** - * If `true`, the tooltip popup is open. Use when controlled. + * Whether the tooltip popup is open. Use when controlled. + * @default false */ open: PropTypes.bool, + /** + * Determines which axis the tooltip should track the cursor on. + * @default 'none' + */ + trackCursorAxis: PropTypes.oneOf(['both', 'none', 'x', 'y']), } as any; export { TooltipRoot }; diff --git a/packages/mui-base/src/Tooltip/Root/TooltipRoot.types.ts b/packages/mui-base/src/Tooltip/Root/TooltipRoot.types.ts deleted file mode 100644 index 6333867621..0000000000 --- a/packages/mui-base/src/Tooltip/Root/TooltipRoot.types.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type * as React from 'react'; -import type { FloatingRootContext, OpenChangeReason } from '@floating-ui/react'; -import type { TransitionStatus } from '../../utils/useTransitionStatus'; -import type { GenericHTMLProps } from '../../utils/types'; - -export interface TooltipRootContextValue { - open: boolean; - setOpen: (open: boolean, event?: Event, reason?: OpenChangeReason) => void; - triggerElement: Element | null; - setTriggerElement: (el: Element | null) => void; - positionerElement: HTMLElement | null; - setPositionerElement: (el: HTMLElement | null) => void; - popupRef: React.RefObject; - delay: number; - closeDelay: number; - delayType: 'rest' | 'hover'; - mounted: boolean; - setMounted: React.Dispatch>; - getRootTriggerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - instantType: 'delay' | 'dismiss' | 'focus' | undefined; - floatingRootContext: FloatingRootContext; - followCursorAxis: 'none' | 'x' | 'y' | 'both'; - transitionStatus: TransitionStatus; -} - -export interface TooltipRootProps { - children: React.ReactNode; - /** - * If `true`, the tooltip popup is open. Use when controlled. - */ - open?: boolean; - /** - * Callback fired when the tooltip popup is requested to be opened or closed. Use when - * controlled. - */ - onOpenChange?: (isOpen: boolean, event?: Event, reason?: OpenChangeReason) => void; - /** - * Specifies whether the tooltip is open initially when uncontrolled. - */ - defaultOpen?: boolean; - /** - * The delay in milliseconds until the tooltip popup is opened. - * @default 600 - */ - delay?: number; - /** - * The delay in milliseconds until the tooltip popup is closed. - * @default 0 - */ - closeDelay?: number; - /** - * The delay type to use. `rest` means the `delay` represents how long the user's cursor must - * rest on the trigger before the tooltip popup is opened. `hover` means the `delay` represents - * how long to wait as soon as the user's cursor has entered the trigger. - * @default 'rest' - */ - delayType?: 'rest' | 'hover'; - /** - * Whether the user can move their cursor from the trigger to the tooltip popup without it - * closing. - * @default true - */ - hoverable?: boolean; - /** - * Whether the tooltip can animate, adding animation-related attributes and allowing for exit - * animations to play. Useful to disable in tests to remove async behavior. - * @default true - */ - animated?: boolean; - /** - * Determines which axis the tooltip should follow the cursor on. - * @default 'none' - */ - followCursorAxis?: 'none' | 'x' | 'y' | 'both'; -} diff --git a/packages/mui-base/src/Tooltip/Root/TooltipRootContext.ts b/packages/mui-base/src/Tooltip/Root/TooltipRootContext.ts index 7cdab58e5c..e55cf402ae 100644 --- a/packages/mui-base/src/Tooltip/Root/TooltipRootContext.ts +++ b/packages/mui-base/src/Tooltip/Root/TooltipRootContext.ts @@ -1,7 +1,30 @@ import * as React from 'react'; -import type { TooltipRootContextValue } from './TooltipRoot.types'; +import type { FloatingRootContext, OpenChangeReason } from '@floating-ui/react'; +import type { GenericHTMLProps } from '../../utils/types'; +import type { TransitionStatus } from '../../utils/useTransitionStatus'; -export const TooltipRootContext = React.createContext(null); +export interface TooltipRootContext { + open: boolean; + setOpen: (open: boolean, event?: Event, reason?: OpenChangeReason) => void; + triggerElement: Element | null; + setTriggerElement: (el: Element | null) => void; + positionerElement: HTMLElement | null; + setPositionerElement: (el: HTMLElement | null) => void; + popupRef: React.RefObject; + delay: number; + closeDelay: number; + delayType: 'rest' | 'hover'; + mounted: boolean; + setMounted: React.Dispatch>; + getRootTriggerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; + getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; + instantType: 'delay' | 'dismiss' | 'focus' | undefined; + floatingRootContext: FloatingRootContext; + trackCursorAxis: 'none' | 'x' | 'y' | 'both'; + transitionStatus: TransitionStatus; +} + +export const TooltipRootContext = React.createContext(null); export function useTooltipRootContext() { const context = React.useContext(TooltipRootContext); diff --git a/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts b/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts index a3a283dfab..beb8cc1b20 100644 --- a/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts +++ b/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts @@ -11,15 +11,17 @@ import { useHover, useInteractions, type OpenChangeReason, + type FloatingRootContext, } from '@floating-ui/react'; -import type { UseTooltipRootParameters, UseTooltipRootReturnValue } from './useTooltipRoot.types'; import { useControlled } from '../../utils/useControlled'; import { useTransitionStatus } from '../../utils/useTransitionStatus'; import { useAnimationsFinished } from '../../utils/useAnimationsFinished'; import { useEventCallback } from '../../utils/useEventCallback'; import { OPEN_DELAY } from '../utils/constants'; +import type { TransitionStatus } from '../../utils/useTransitionStatus'; +import type { GenericHTMLProps } from '../../utils/types'; -export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRootReturnValue { +export function useTooltipRoot(params: useTooltipRoot.Parameters): useTooltipRoot.ReturnValue { const { open: externalOpen, onOpenChange: onOpenChangeProp = () => {}, @@ -27,7 +29,7 @@ export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRoot keepMounted = false, hoverable = true, animated = true, - followCursorAxis = 'none', + trackCursorAxis = 'none', delayType = 'rest', delay, closeDelay, @@ -134,7 +136,7 @@ export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRoot const hover = useHover(context, { mouseOnly: true, move: false, - handleClose: hoverable && followCursorAxis !== 'both' ? safePolygon() : null, + handleClose: hoverable && trackCursorAxis !== 'both' ? safePolygon() : null, restMs: computedRestMs, delay: { open: computedOpenDelay, @@ -144,8 +146,8 @@ export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRoot const focus = useFocus(context); const dismiss = useDismiss(context, { referencePress: true }); const clientPoint = useClientPoint(context, { - enabled: followCursorAxis !== 'none', - axis: followCursorAxis === 'none' ? undefined : followCursorAxis, + enabled: trackCursorAxis !== 'none', + axis: trackCursorAxis === 'none' ? undefined : trackCursorAxis, }); const { getReferenceProps: getRootTriggerProps, getFloatingProps: getRootPopupProps } = @@ -183,3 +185,78 @@ export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRoot ], ); } + +export namespace useTooltipRoot { + export interface Parameters { + /** + * Whether the tooltip popup is open by default. Use when uncontrolled. + * @default false + */ + defaultOpen?: boolean; + /** + * Whether the tooltip popup is open. Use when controlled. + * @default false + */ + open?: boolean; + /** + * Callback fired when the tooltip popup is requested to be opened or closed. Use when controlled. + */ + onOpenChange?: (isOpen: boolean, event?: Event, reason?: OpenChangeReason) => void; + /** + * Whether the can move their cursor from the trigger element toward the tooltip popup element + * without it closing using a "safe polygon" technique. + * @default true + */ + hoverable?: boolean; + /** + * Whether the tooltip can animate, adding animation-related attributes and allowing for exit + * animations to play. Useful to disable in tests to remove async behavior. + * @default true + */ + animated?: boolean; + /** + * Determines which axis the tooltip should track the cursor on. + * @default 'none' + */ + trackCursorAxis?: 'none' | 'x' | 'y' | 'both'; + /** + * The delay in milliseconds until the tooltip popup is opened. + * @default 600 + */ + delay?: number; + /** + * The delay in milliseconds until the tooltip popup is closed. + * @default 0 + */ + closeDelay?: number; + /** + * The delay type to use. `rest` means the `delay` represents how long the user's cursor must + * rest on the trigger before the tooltip popup is opened. `hover` means the `delay` represents + * how long to wait as soon as the user's cursor has entered the trigger. + * @default 'rest' + */ + delayType?: 'rest' | 'hover'; + /** + * Whether the tooltip popup element stays mounted in the DOM when closed. + * @default false + */ + keepMounted?: boolean; + } + + export interface ReturnValue { + open: boolean; + setOpen: (value: boolean, event?: Event, reason?: OpenChangeReason) => void; + mounted: boolean; + setMounted: React.Dispatch>; + getRootTriggerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; + getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; + floatingRootContext: FloatingRootContext; + instantType: 'delay' | 'dismiss' | 'focus' | undefined; + transitionStatus: TransitionStatus; + triggerElement: Element | null; + positionerElement: HTMLElement | null; + setTriggerElement: React.Dispatch>; + setPositionerElement: React.Dispatch>; + popupRef: React.RefObject; + } +} diff --git a/packages/mui-base/src/Tooltip/Root/useTooltipRoot.types.ts b/packages/mui-base/src/Tooltip/Root/useTooltipRoot.types.ts deleted file mode 100644 index 8ced647048..0000000000 --- a/packages/mui-base/src/Tooltip/Root/useTooltipRoot.types.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type * as React from 'react'; -import type { FloatingRootContext, OpenChangeReason } from '@floating-ui/react'; -import type { TransitionStatus } from '../../utils/useTransitionStatus'; -import type { GenericHTMLProps } from '../../utils/types'; - -export interface UseTooltipRootParameters { - /** - * Whether the tooltip popup is open by default. Use when uncontrolled. - * @default false - */ - defaultOpen?: boolean; - /** - * Whether the tooltip popup is open. Use when controlled. - * @default false - */ - open?: boolean; - /** - * Callback fired when the tooltip popup is requested to be opened or closed. - */ - onOpenChange?: (isOpen: boolean, event?: Event, reason?: OpenChangeReason) => void; - /** - * Whether the can move their cursor from the trigger element toward the tooltip popup element - * without it closing using a "safe polygon" technique. - * @default true - */ - hoverable?: boolean; - /** - * Whether the tooltip can animate, adding animation-related attributes and allowing for exit - * animations to play. Useful to disable in tests to remove async behavior. - * @default true - */ - animated?: boolean; - /** - * Determines which axis the tooltip should follow the cursor on. - * @default 'none' - */ - followCursorAxis?: 'none' | 'x' | 'y' | 'both'; - /** - * The delay in milliseconds until the tooltip popup is opened. - * @default 600 - */ - delay?: number; - /** - * The delay in milliseconds until the tooltip popup is closed. - * @default 0 - */ - closeDelay?: number; - /** - * The delay type to use. `rest` means the `delay` represents how long the user's cursor must - * rest on the trigger before the tooltip popup is opened. `hover` means the `delay` represents - * how long to wait as soon as the user's cursor has entered the trigger. - * @default 'rest' - */ - delayType?: 'rest' | 'hover'; - /** - * Whether the tooltip popup element stays mounted in the DOM when closed. - * @default false - */ - keepMounted?: boolean; -} - -export interface UseTooltipRootReturnValue { - open: boolean; - setOpen: (value: boolean, event?: Event, reason?: OpenChangeReason) => void; - mounted: boolean; - setMounted: React.Dispatch>; - getRootTriggerProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - floatingRootContext: FloatingRootContext; - instantType: 'delay' | 'dismiss' | 'focus' | undefined; - transitionStatus: TransitionStatus; - triggerElement: Element | null; - positionerElement: HTMLElement | null; - setTriggerElement: React.Dispatch>; - setPositionerElement: React.Dispatch>; - popupRef: React.RefObject; -} diff --git a/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.tsx b/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.tsx index 4f549c4d73..a1c6078926 100644 --- a/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.tsx +++ b/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.tsx @@ -2,9 +2,18 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useTooltipRootContext } from '../Root/TooltipRootContext'; -import type { TooltipTriggerProps } from './TooltipTrigger.types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useForkRef } from '../../utils/useForkRef'; +import type { BaseUIComponentProps } from '../../utils/types'; +import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps'; + +const customStyleHookMapping: CustomStyleHookMapping = { + open(value: boolean) { + return { + 'data-state': value ? 'open' : 'closed', + }; + }, +}; /** * Renders a trigger element that opens the tooltip. @@ -18,14 +27,14 @@ import { useForkRef } from '../../utils/useForkRef'; * - [TooltipTrigger API](https://base-ui.netlify.app/components/react-tooltip/#api-reference-TooltipTrigger) */ const TooltipTrigger = React.forwardRef(function TooltipTrigger( - props: TooltipTriggerProps, + props: TooltipTrigger.Props, forwardedRef: React.ForwardedRef, ) { const { className, render, ...otherProps } = props; const { open, setTriggerElement, getRootTriggerProps } = useTooltipRootContext(); - const ownerState = React.useMemo(() => ({ open }), [open]); + const ownerState: TooltipTrigger.OwnerState = React.useMemo(() => ({ open }), [open]); const mergedRef = useForkRef(forwardedRef, setTriggerElement); @@ -36,18 +45,19 @@ const TooltipTrigger = React.forwardRef(function TooltipTrigger( ownerState, ref: mergedRef, extraProps: otherProps, - customStyleHookMapping: { - open(value) { - return { - 'data-state': value ? 'open' : 'closed', - }; - }, - }, + customStyleHookMapping, }); return renderElement(); }); +namespace TooltipTrigger { + export interface OwnerState { + open: boolean; + } + export interface Props extends BaseUIComponentProps {} +} + TooltipTrigger.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ diff --git a/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.types.ts b/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.types.ts deleted file mode 100644 index 6ec352cfc4..0000000000 --- a/packages/mui-base/src/Tooltip/Trigger/TooltipTrigger.types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { BaseUIComponentProps } from '../../utils/types'; - -export type TooltipTriggerOwnerState = { - open: boolean; -}; - -export interface TooltipTriggerProps extends BaseUIComponentProps {}