diff --git a/docs/data/api/tooltip-root.json b/docs/data/api/tooltip-root.json index f18183291..268cce71d 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 2c941b6b5..3201fa0d4 100644 --- a/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json +++ b/docs/data/translations/api-docs/tooltip-positioner/tooltip-positioner.json @@ -2,43 +2,39 @@ "componentDescription": "The tooltip positioner element.", "propDescriptions": { "alignment": { - "description": "The alignment of the tooltip popup element to the anchor element along its cross axis." + "description": "The alignment of the tooltip element to the anchor element along its cross axis." }, "alignmentOffset": { - "description": "The offset of the tooltip popup element along its alignment axis." + "description": "The offset of the tooltip element along its alignment axis." }, - "anchor": { "description": "The element to which the tooltip popup element is anchored to." }, + "anchor": { "description": "The element to which the tooltip element is anchored to." }, "arrowPadding": { - "description": "Determines the padding between the arrow and the tooltip popup edges. Useful when the tooltip popup element has rounded corners via border-radius." + "description": "Determines the padding between the arrow and the tooltip edges. Useful when the tooltip element has rounded corners via border-radius." }, "className": { "description": "Class names applied to the element or a function that returns them based on the component's state." }, "collisionBoundary": { - "description": "The boundary that the tooltip popup element should be constrained to." + "description": "The boundary that the tooltip element should be constrained to." }, "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." + "description": "The padding between the tooltip element and the edges of the collision boundary to add whitespace between them to prevent them from touching." }, + "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." + "description": "Whether the tooltip element is hidden if it appears detached from its anchor element due to the anchor element being clipped (or hidden) from view." }, "keepMounted": { - "description": "Whether the tooltip popup remains mounted in the DOM while closed." + "description": "Whether the tooltip remains mounted in the DOM while closed." }, "positionStrategy": { - "description": "The CSS position strategy for positioning the tooltip popup element." + "description": "The CSS position strategy for positioning the tooltip element." }, "render": { "description": "A function to customize rendering of the component." }, "side": { - "description": "The side of the anchor element that the tooltip popup element should be placed at." - }, - "sideOffset": { - "description": "The gap between the anchor element and the tooltip popup element." + "description": "The side of the anchor element that the tooltip element should be placed at." }, + "sideOffset": { "description": "The gap between the anchor element and the tooltip element." }, "sticky": { "description": "Whether to allow the tooltip to remain stuck in view while the anchor element is scrolled out of 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 91758e57d..137b9daae 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 user 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 b70c1e86e..c6d948fa5 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,28 @@ 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 c939c8ac3..000000000 --- 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 937001b4f..09735b9f3 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,14 @@ 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 ca20da129..000000000 --- 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 f3fde617d..08a788d5b 100644 --- a/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx +++ b/packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx @@ -1,12 +1,27 @@ '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'; +import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps'; + +const customStyleHookMapping: CustomStyleHookMapping = { + entering(value) { + return value ? { 'data-entering': '' } : null; + }, + exiting(value) { + return value ? { 'data-exiting': '' } : null; + }, + open(value) { + return { + 'data-state': value ? 'open' : 'closed', + }; + }, +}; /** * The tooltip popup element. @@ -20,7 +35,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 +44,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,30 +61,31 @@ 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, ref: mergedRef, extraProps: otherProps, - customStyleHookMapping: { - entering(value) { - return value ? { 'data-entering': '' } : null; - }, - exiting(value) { - return value ? { 'data-exiting': '' } : null; - }, - open(value) { - return { - 'data-state': value ? 'open' : 'closed', - }; - }, - }, + customStyleHookMapping, }); 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 de2df2cad..000000000 --- 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 cba55f586..000000000 --- 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 0c8b6fbe1..000000000 --- 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 8c41bc631..712a17078 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 { @@ -54,7 +51,7 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( setPositionerElement, mounted, floatingRootContext, - followCursorAxis, + trackCursorAxis, } = useTooltipRootContext(); const positioner = useTooltipPositioner({ @@ -71,13 +68,13 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( collisionPadding, hideWhenDetached, sticky, - followCursorAxis, + trackCursorAxis, arrowPadding, }); 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,23 +114,40 @@ const TooltipPositioner = React.forwardRef(function TooltipPositioner( ); }); +namespace TooltipPositioner { + export interface OwnerState { + open: boolean; + side: Side; + alignment: Alignment; + } + + export interface Props + extends BaseUIComponentProps<'div', OwnerState>, + useTooltipPositioner.SharedParameters { + /** + * 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. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** - * The alignment of the tooltip popup element to the anchor element along its cross axis. + * The alignment of the tooltip element to the anchor element along its cross axis. * @default 'center' */ alignment: PropTypes.oneOf(['center', 'end', 'start']), /** - * The offset of the tooltip popup element along its alignment axis. + * The offset of the tooltip element along its alignment axis. * @default 0 */ alignmentOffset: PropTypes.number, /** - * The element to which the tooltip popup element is anchored to. + * The element to which the tooltip element is anchored to. */ anchor: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ HTMLElementType, @@ -141,8 +155,8 @@ TooltipPositioner.propTypes /* remove-proptypes */ = { PropTypes.func, ]), /** - * Determines the padding between the arrow and the tooltip popup edges. Useful when the tooltip - * popup element has rounded corners via `border-radius`. + * Determines the padding between the arrow and the tooltip edges. Useful when the tooltip + * element has rounded corners via `border-radius`. * @default 5 */ arrowPadding: PropTypes.number, @@ -155,7 +169,7 @@ TooltipPositioner.propTypes /* remove-proptypes */ = { */ className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** - * The boundary that the tooltip popup element should be constrained to. + * The boundary that the tooltip element should be constrained to. * @default 'clippingAncestors' */ collisionBoundary: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ @@ -170,7 +184,7 @@ TooltipPositioner.propTypes /* remove-proptypes */ = { }), ]), /** - * The padding between the tooltip popup element and the edges of the collision boundary to add + * The padding between the tooltip element and the edges of the collision boundary to add * whitespace between them to prevent them from touching. * @default 5 */ @@ -184,25 +198,25 @@ 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, PropTypes.func, ]), /** - * Whether the tooltip popup element is hidden if it appears detached from its anchor element due + * Whether the tooltip 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: PropTypes.bool, /** - * Whether the tooltip popup remains mounted in the DOM while closed. + * Whether the tooltip remains mounted in the DOM while closed. * @default false */ keepMounted: PropTypes.bool, /** - * The CSS position strategy for positioning the tooltip popup element. + * The CSS position strategy for positioning the tooltip element. * @default 'absolute' */ positionStrategy: PropTypes.oneOf(['absolute', 'fixed']), @@ -211,12 +225,12 @@ TooltipPositioner.propTypes /* remove-proptypes */ = { */ render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), /** - * The side of the anchor element that the tooltip popup element should be placed at. + * The side of the anchor element that the tooltip element should be placed at. * @default 'top' */ side: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), /** - * The gap between the anchor element and the tooltip popup element. + * The gap between the anchor element and the tooltip element. * @default 0 */ sideOffset: PropTypes.number, 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 d8e9f2ed7..000000000 --- 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 4439c3aa5..6a73def57 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 1834ebd13..d733a0af7 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,135 @@ export function useTooltipPositioner( [getPositionerProps, arrowRef, arrowUncentered, renderedSide, renderedAlignment, arrowStyles], ); } + +export namespace useTooltipPositioner { + export interface SharedParameters { + /** + * The element to which the tooltip element is anchored to. + */ + anchor?: + | Element + | null + | VirtualElement + | React.MutableRefObject + | (() => Element | VirtualElement | null); + /** + * Whether the tooltip is open. + * @default false + */ + open?: boolean; + /** + * The CSS position strategy for positioning the tooltip element. + * @default 'absolute' + */ + positionStrategy?: 'absolute' | 'fixed'; + /** + * The side of the anchor element that the tooltip element should be placed at. + * @default 'top' + */ + side?: 'top' | 'right' | 'bottom' | 'left'; + /** + * The gap between the anchor element and the tooltip element. + * @default 0 + */ + sideOffset?: number; + /** + * The alignment of the tooltip element to the anchor element along its cross axis. + * @default 'center' + */ + alignment?: 'start' | 'end' | 'center'; + /** + * The offset of the tooltip element along its alignment axis. + * @default 0 + */ + alignmentOffset?: number; + /** + * The boundary that the tooltip element should be constrained to. + * @default 'clippingAncestors' + */ + collisionBoundary?: Boundary; + /** + * The padding between the tooltip 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 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 edges. Useful when the tooltip + * element has rounded corners via `border-radius`. + * @default 5 + */ + arrowPadding?: number; + /** + * Whether the tooltip remains mounted in the DOM while closed. + * @default false + */ + keepMounted?: boolean; + /** + * Whether the tooltip 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 Parameters extends SharedParameters { + /** + * Whether the tooltip is open. + * @default false + */ + open?: boolean; + /** + * The tooltip root context. + */ + floatingRootContext?: FloatingRootContext; + } + + 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 arrow element. + */ + arrowStyles: React.CSSProperties; + /** + * The rendered side of the tooltip element. + */ + side: 'top' | 'right' | 'bottom' | 'left'; + /** + * The rendered alignment of the tooltip 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 e0d13e635..000000000 --- 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 2a52ee32a..6fc2580bb 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 e9c5e4567..000000000 --- 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 37a1073d0..5f03c1aa4 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 6a886b73d..b6851c253 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,14 @@ 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 +133,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 +150,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 user 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 633386762..000000000 --- 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 7cdab58e5..e55cf402a 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 a3a283dfa..916a092f0 100644 --- a/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts +++ b/packages/mui-base/src/Tooltip/Root/useTooltipRoot.ts @@ -1,4 +1,3 @@ -'use client'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { @@ -11,15 +10,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 +28,7 @@ export function useTooltipRoot(params: UseTooltipRootParameters): UseTooltipRoot keepMounted = false, hoverable = true, animated = true, - followCursorAxis = 'none', + trackCursorAxis = 'none', delayType = 'rest', delay, closeDelay, @@ -134,7 +135,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 +145,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 +184,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 user 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 8ced64704..000000000 --- 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 4f549c4d7..129609602 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,20 @@ 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 6ec352cfc..000000000 --- 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 {} diff --git a/packages/mui-base/src/Tooltip/index.barrel.ts b/packages/mui-base/src/Tooltip/index.barrel.ts index 121d7055e..79e7247ec 100644 --- a/packages/mui-base/src/Tooltip/index.barrel.ts +++ b/packages/mui-base/src/Tooltip/index.barrel.ts @@ -4,10 +4,3 @@ export { TooltipPositioner } from './Positioner/TooltipPositioner'; export { TooltipPopup } from './Popup/TooltipPopup'; export { TooltipArrow } from './Arrow/TooltipArrow'; export { TooltipProvider } from './Provider/TooltipProvider'; - -export type { TooltipRootProps } from './Root/TooltipRoot.types'; -export type * from './Trigger/TooltipTrigger.types'; -export type * from './Positioner/TooltipPositioner.types'; -export type * from './Popup/TooltipPopup.types'; -export type * from './Arrow/TooltipArrow.types'; -export type * from './Provider/TooltipProvider.types'; diff --git a/packages/mui-base/src/Tooltip/index.ts b/packages/mui-base/src/Tooltip/index.ts index fed5edcec..00ec741c9 100644 --- a/packages/mui-base/src/Tooltip/index.ts +++ b/packages/mui-base/src/Tooltip/index.ts @@ -4,22 +4,3 @@ export { TooltipPositioner as Positioner } from './Positioner/TooltipPositioner' export { TooltipPopup as Popup } from './Popup/TooltipPopup'; export { TooltipArrow as Arrow } from './Arrow/TooltipArrow'; export { TooltipProvider as Provider } from './Provider/TooltipProvider'; - -export type { TooltipRootProps as RootProps } from './Root/TooltipRoot.types'; -export type { - TooltipTriggerProps as TriggerProps, - TooltipTriggerOwnerState as TriggerOwnerState, -} from './Trigger/TooltipTrigger.types'; -export type { - TooltipPositionerProps as PositionerProps, - TooltipPositionerOwnerState as PositionerOwnerState, -} from './Positioner/TooltipPositioner.types'; -export type { - TooltipPopupProps as PopupProps, - TooltipPopupOwnerState as PopupOwnerState, -} from './Popup/TooltipPopup.types'; -export type { - TooltipArrowProps as ArrowProps, - TooltipArrowOwnerState as ArrowOwnerState, -} from './Arrow/TooltipArrow.types'; -export type { TooltipProviderProps as ProviderProps } from './Provider/TooltipProvider.types';