Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooltip] Modernize implementation #606

Merged
merged 8 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/data/api/tooltip-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -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'<br>&#124;&nbsp;'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'<br>&#124;&nbsp;'none'<br>&#124;&nbsp;'x'<br>&#124;&nbsp;'y'"
},
"default": "'none'"
},
"hoverable": { "type": { "name": "bool" }, "default": "true" },
"onOpenChange": { "type": { "name": "func" } },
"open": { "type": { "name": "bool" } }
}
},
"name": "TooltipRoot",
"imports": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>border-radius</code>."
"description": "Determines the padding between the arrow and the tooltip edges. Useful when the tooltip element has rounded corners via <code>border-radius</code>."
},
"className": {
"description": "Class names applied to the element or a function that returns them based on the component&#39;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."
}
Expand Down
12 changes: 5 additions & 7 deletions docs/data/translations/api-docs/tooltip-root/tooltip-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -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. <code>rest</code> means the <code>delay</code> represents how long the user&#39;s cursor must rest on the trigger before the tooltip popup is opened. <code>hover</code> means the <code>delay</code> represents how long to wait as soon as the user&#39;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 &quot;safe polygon&quot; technique."
},
"onOpenChange": {
"description": "Callback fired when the tooltip popup is requested to be opened or closed. Use when controlled."
},
"open": {
"description": "If <code>true</code>, 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": {}
Expand Down
39 changes: 29 additions & 10 deletions packages/mui-base/src/Tooltip/Arrow/TooltipArrow.tsx
Original file line number Diff line number Diff line change
@@ -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<TooltipArrow.OwnerState> = {
open(value) {
return {
'data-state': value ? 'open' : 'closed',
atomiks marked this conversation as resolved.
Show resolved Hide resolved
};
},
};

/**
* Renders an arrow that points to the center of the anchor element.
Expand All @@ -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<HTMLDivElement>,
) {
const { className, render, hideWhenUncentered = false, ...otherProps } = props;
Expand All @@ -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,
Expand All @@ -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. │
Expand Down
16 changes: 0 additions & 16 deletions packages/mui-base/src/Tooltip/Arrow/TooltipArrow.types.ts

This file was deleted.

27 changes: 18 additions & 9 deletions packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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;
}
}
11 changes: 0 additions & 11 deletions packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.types.ts

This file was deleted.

26 changes: 17 additions & 9 deletions packages/mui-base/src/Tooltip/Popup/TooltipPopup.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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<HTMLDivElement>,
) {
const { className, render, ...otherProps } = props;
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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. │
Expand Down
13 changes: 0 additions & 13 deletions packages/mui-base/src/Tooltip/Popup/TooltipPopup.types.ts

This file was deleted.

30 changes: 0 additions & 30 deletions packages/mui-base/src/Tooltip/Popup/useTooltipPopup.ts

This file was deleted.

This file was deleted.

Loading