Skip to content

Commit

Permalink
[Popover] Modernize implementation (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks authored Sep 19, 2024
1 parent 6127e9d commit 9197824
Show file tree
Hide file tree
Showing 42 changed files with 549 additions and 676 deletions.
8 changes: 2 additions & 6 deletions docs/data/api/popover-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
"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": "300" },
"delayType": {
"type": { "name": "enum", "description": "'hover'<br>&#124;&nbsp;'rest'" },
"default": "'rest'"
},
"followCursorAxis": {
"type": { "name": "enum", "description": "'none'<br>&#124;&nbsp;'x'<br>&#124;&nbsp;'y'" },
"default": "'none'"
},
"onOpenChange": { "type": { "name": "func" } },
"open": { "type": { "name": "bool" } },
"open": { "type": { "name": "bool" }, "default": "false" },
"openOnHover": { "type": { "name": "bool" }, "default": "false" }
},
"name": "PopoverRoot",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,39 @@
"componentDescription": "The popover positioner element.",
"propDescriptions": {
"alignment": {
"description": "The alignment of the popover popup element to the anchor element along its cross axis."
"description": "The alignment of the popover element to the anchor element along its cross axis."
},
"alignmentOffset": {
"description": "The offset of the popover popup element along its alignment axis."
"description": "The offset of the popover element along its alignment axis."
},
"anchor": { "description": "The element to which the popover popup element is anchored to." },
"anchor": { "description": "The element to which the popover element is anchored to." },
"arrowPadding": {
"description": "Determines the padding between the arrow and the popover popup edges. Useful when the popover popup element has rounded corners via <code>border-radius</code>."
"description": "Determines the padding between the arrow and the popover edges. Useful when the popover 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 popover popup element should be constrained to."
"description": "The boundary that the popover element should be constrained to."
},
"collisionPadding": {
"description": "The padding between the popover 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 popover positioner is appended to."
"description": "The padding between the popover element and the edges of the collision boundary to add whitespace between them to prevent them from touching."
},
"container": { "description": "The element the popover positioner element is appended to." },
"hideWhenDetached": {
"description": "Whether the popover 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 popover 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 popover popup remains mounted in the DOM while closed."
"description": "Whether the popover remains mounted in the DOM while closed."
},
"positionStrategy": {
"description": "The CSS position strategy for positioning the popover popup element."
"description": "The CSS position strategy for positioning the popover element."
},
"render": { "description": "A function to customize rendering of the component." },
"side": {
"description": "The side of the anchor element that the popover popup element should be placed at."
},
"sideOffset": {
"description": "The gap between the anchor element and the popover popup element."
"description": "The side of the anchor element that the popover element should be placed at."
},
"sideOffset": { "description": "The gap between the anchor element and the popover element." },
"sticky": {
"description": "Whether to allow the popover to remain stuck in view while the anchor element is scrolled out of view."
}
Expand Down
13 changes: 4 additions & 9 deletions docs/data/translations/api-docs/popover-root/popover-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,26 @@
"componentDescription": "The foundation for building custom-styled popovers.",
"propDescriptions": {
"animated": {
"description": "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."
"description": "Whether the popover can animate, adding animation-related attributes and allowing for exit animations to play. Useful to disable in tests to remove async behavior."
},
"closeDelay": {
"description": "The delay in milliseconds until the popover popup is closed when <code>openOnHover</code> is <code>true</code>."
},
"defaultOpen": {
"description": "Specifies whether the popover is open initially when uncontrolled."
"description": "Whether the popover popup is open by default. Use when uncontrolled."
},
"delay": {
"description": "The delay in milliseconds until the popover popup is opened when <code>openOnHover</code> is <code>true</code>."
},
"delayType": {
"description": "The delay type to use when <code>openOnHover</code> is <code>true</code>. <code>rest</code> means the <code>delay</code> represents how long the user&#39;s cursor must rest on the trigger before the popover 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."
},
"onOpenChange": {
"description": "Callback fired when the popover popup is requested to be opened or closed. Use when controlled."
},
"open": {
"description": "If <code>true</code>, the popover popup is open. Use when controlled."
},
"open": { "description": "Whether the popover popup is open. Use when controlled." },
"openOnHover": {
"description": "If <code>true</code>, the popover popup opens when the trigger is hovered."
"description": "Whether the popover popup opens when the trigger is hovered after the provided <code>delay</code>."
}
},
"classDescriptions": {}
Expand Down
41 changes: 31 additions & 10 deletions packages/mui-base/src/Popover/Arrow/PopoverArrow.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import type { PopoverArrowOwnerState, PopoverArrowProps } from './PopoverArrow.types';
import { usePopoverPositionerContext } from '../Positioner/PopoverPositionerContext';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import { useForkRef } from '../../utils/useForkRef';
import { usePopoverArrow } from './usePopoverArrow';
import type { Alignment, Side } from '../../utils/useAnchorPositioning';
import type { BaseUIComponentProps } from '../../utils/types';
import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps';

const customStyleHookMapping: CustomStyleHookMapping<PopoverArrow.OwnerState> = {
open(value) {
return {
'data-state': value ? 'open' : 'closed',
};
},
};

/**
* Renders an arrow that points to the center of the anchor element.
Expand All @@ -20,7 +30,7 @@ import { usePopoverArrow } from './usePopoverArrow';
* - [PopoverArrow API](https://base-ui.netlify.app/components/react-popover/#api-reference-PopoverArrow)
*/
const PopoverArrow = React.forwardRef(function PopoverArrow(
props: PopoverArrowProps,
props: PopoverArrow.Props,
forwardedRef: React.ForwardedRef<HTMLDivElement>,
) {
const { className, render, hideWhenUncentered = false, ...otherProps } = props;
Expand All @@ -33,7 +43,7 @@ const PopoverArrow = React.forwardRef(function PopoverArrow(
hidden: hideWhenUncentered && arrowUncentered,
});

const ownerState: PopoverArrowOwnerState = React.useMemo(
const ownerState: PopoverArrow.OwnerState = React.useMemo(
() => ({
open,
side,
Expand All @@ -52,18 +62,29 @@ const PopoverArrow = React.forwardRef(function PopoverArrow(
ownerState,
ref: mergedRef,
extraProps: otherProps,
customStyleHookMapping: {
open(value) {
return {
'data-state': value ? 'open' : 'closed',
};
},
},
customStyleHookMapping,
});

return renderElement();
});

namespace PopoverArrow {
export interface OwnerState {
open: boolean;
side: Side;
alignment: Alignment;
arrowUncentered: boolean;
}

export interface Props extends BaseUIComponentProps<'div', OwnerState> {
/**
* If `true`, the arrow is hidden when it can't point to the center of the anchor element.
* @default false
*/
hideWhenUncentered?: boolean;
}
}

PopoverArrow.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
Expand Down
17 changes: 0 additions & 17 deletions packages/mui-base/src/Popover/Arrow/PopoverArrow.types.ts

This file was deleted.

27 changes: 18 additions & 9 deletions packages/mui-base/src/Popover/Arrow/usePopoverArrow.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 {
UsePopoverArrowParameters,
UsePopoverArrowReturnValue,
} from './usePopoverArrow.types';
import type { GenericHTMLProps } from '../../utils/types';

export function usePopoverArrow(params: UsePopoverArrowParameters): UsePopoverArrowReturnValue {
const { arrowStyles } = params;
export function usePopoverArrow(params: usePopoverArrow.Parameters): usePopoverArrow.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 usePopoverArrow(params: UsePopoverArrowParameters): UsePopoverAr
[getArrowProps],
);
}

namespace usePopoverArrow {
export interface Parameters {
arrowStyles: React.CSSProperties;
hidden?: boolean;
}
export interface ReturnValue {
getArrowProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
}
}
11 changes: 0 additions & 11 deletions packages/mui-base/src/Popover/Arrow/usePopoverArrow.types.ts

This file was deleted.

23 changes: 21 additions & 2 deletions packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { FloatingPortal } from '@floating-ui/react';
import type { PopoverBackdropProps } from './PopoverBackdrop.types';
import { usePopoverRootContext } from '../Root/PopoverRootContext';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import { HTMLElementType } from '../../utils/proptypes';
import { usePopoverBackdrop } from './usePopoverBackdrop';
import type { BaseUIComponentProps } from '../../utils/types';

/**
* Renders a backdrop for the popover.
Expand All @@ -20,7 +20,7 @@ import { usePopoverBackdrop } from './usePopoverBackdrop';
* - [PopoverBackdrop API](https://base-ui.netlify.app/components/react-popover/#api-reference-PopoverBackdrop)
*/
const PopoverBackdrop = React.forwardRef(function PopoverBackdrop(
props: PopoverBackdropProps,
props: PopoverBackdrop.Props,
forwardedRef: React.ForwardedRef<HTMLDivElement>,
) {
const { className, render, keepMounted = false, container, ...otherProps } = props;
Expand Down Expand Up @@ -48,6 +48,25 @@ const PopoverBackdrop = React.forwardRef(function PopoverBackdrop(
return <FloatingPortal root={container}>{renderElement()}</FloatingPortal>;
});

namespace PopoverBackdrop {
export interface OwnerState {
open: boolean;
}

export interface Props extends BaseUIComponentProps<'div', OwnerState> {
/**
* If `true`, the backdrop remains mounted when the popover content is closed.
* @default false
*/
keepMounted?: boolean;
/**
* The container element to which the backdrop is appended to.
* @default false
*/
container?: HTMLElement | null | React.MutableRefObject<HTMLElement | null>;
}
}

PopoverBackdrop.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
Expand Down
19 changes: 0 additions & 19 deletions packages/mui-base/src/Popover/Backdrop/PopoverBackdrop.types.ts

This file was deleted.

11 changes: 8 additions & 3 deletions packages/mui-base/src/Popover/Backdrop/usePopoverBackdrop.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
'use client';
import * as React from 'react';
import { mergeReactProps } from '../../utils/mergeReactProps';
import type { UsePopoverBackdropReturnValue } from './usePopoverBackdrop.types';
import type { GenericHTMLProps } from '../../utils/types';

export function usePopoverBackdrop(): UsePopoverBackdropReturnValue {
export function usePopoverBackdrop(): usePopoverBackdrop.ReturnValue {
const getBackdropProps = React.useCallback((externalProps = {}) => {
return mergeReactProps<'div'>(externalProps, {
role: 'presentation',
Expand All @@ -23,3 +22,9 @@ export function usePopoverBackdrop(): UsePopoverBackdropReturnValue {
[getBackdropProps],
);
}

namespace usePopoverBackdrop {
export interface ReturnValue {
getBackdropProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
}
}

This file was deleted.

Loading

0 comments on commit 9197824

Please sign in to comment.