Skip to content

Commit

Permalink
[Tooltip] Modernize implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Sep 13, 2024
1 parent f8906ef commit fb9d0e2
Show file tree
Hide file tree
Showing 23 changed files with 423 additions and 463 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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."
},
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',
};
},
};

/**
* 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
23 changes: 15 additions & 8 deletions packages/mui-base/src/Tooltip/Arrow/useTooltipArrow.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
'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,
...(hidden && { visibility: 'hidden' }),
});
},
[arrowStyles],
[arrowStyles, hidden],
);

return React.useMemo(
Expand All @@ -25,3 +22,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.

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

This file was deleted.

33 changes: 23 additions & 10 deletions packages/mui-base/src/Tooltip/Positioner/TooltipPositioner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<HTMLDivElement>,
) {
const {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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<useTooltipPositioner.Parameters, 'floatingRootContext'> {
/**
* The container element the tooltip positioner is appended to.
*/
container?: HTMLElement | null | React.MutableRefObject<HTMLElement | null>;
}
}

TooltipPositioner.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
Expand Down Expand Up @@ -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,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<TooltipPositionerContextValue | null>(
null,
);
export interface TooltipPositionerContext {
open: boolean;
side: Side;
alignment: Alignment;
arrowRef: React.MutableRefObject<Element | null>;
arrowUncentered: boolean;
arrowStyles: React.CSSProperties;
}

export const TooltipPositionerContext = React.createContext<TooltipPositionerContext | null>(null);

export function useTooltipPositionerContext() {
const context = React.useContext(TooltipPositionerContext);
Expand Down
Loading

0 comments on commit fb9d0e2

Please sign in to comment.