Skip to content

Commit

Permalink
Reorg code
Browse files Browse the repository at this point in the history
  • Loading branch information
stephl3 committed Aug 28, 2024
1 parent 228a36a commit 3c16270
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 90 deletions.
25 changes: 25 additions & 0 deletions packages/popover/src/Popover.components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { forwardRef, ReactNode } from 'react';

import { contentClassName, hiddenPlaceholderStyle } from './Popover.styles';

export const HiddenPlaceholder = forwardRef<HTMLSpanElement, {}>((_, fwdRef) => {
/**
* Using \<span\> as placeholder to prevent validateDOMNesting warnings
* Warnings will still show up if `usePortal` is false
*/
return <span ref={fwdRef} className={hiddenPlaceholderStyle} />;
});

HiddenPlaceholder.displayName = 'HiddenPlaceholder';

export const ContentWrapper = forwardRef<HTMLDivElement, { children: ReactNode }>(
({ children }, fwdRef) => {
return (
<div ref={fwdRef} className={contentClassName}>
{children}
</div>
);
},
);

ContentWrapper.displayName = 'ContentWrapper';
93 changes: 3 additions & 90 deletions packages/popover/src/Popover.hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, ReactNode, useMemo, useRef, useState } from 'react';
import React, { useMemo, useRef, useState } from 'react';

import {
useIsomorphicLayoutEffect,
Expand All @@ -12,8 +12,8 @@ import {
getElementDocumentPosition,
getElementViewportPosition,
} from './utils/positionUtils';
import { contentClassName, hiddenPlaceholderStyle } from './Popover.styles';
import { Align, Justify, PopoverProps } from './Popover.types';
import { ContentWrapper, HiddenPlaceholder } from './Popover.components';
import { Align, Justify, PopoverProps, UseContentNodeReturnObj, UsePopoverPositioningProps, UseReferenceElementReturnObj } from './Popover.types';

const mutationOptions = {
// If attributes changes, such as className which affects layout
Expand All @@ -26,31 +26,6 @@ const mutationOptions = {
subtree: true,
};

interface UseReferenceElementReturnObj {
/**
* `HiddenPlaceholder` is used if `refEl` is undefined. The placeholder's parent is
* used as the element against which the popover component will be positioned
*/
HiddenPlaceholder: React.ForwardRefExoticComponent<
React.RefAttributes<HTMLSpanElement>
>;

/**
* Ref to access hidden placeholder element
*/
placeholderRef: React.MutableRefObject<HTMLSpanElement | null>;

/**
* Element against which the popover component will be positioned
*/
referenceElement: HTMLElement | null;

/**
* Boolean to determine if a hidden placeholder should be rendered
*/
renderHiddenPlaceholder: boolean;
}

export function useReferenceElement(
refEl?: PopoverProps['refEl'],
): UseReferenceElementReturnObj {
Expand Down Expand Up @@ -79,48 +54,6 @@ export function useReferenceElement(
};
}

const HiddenPlaceholder = forwardRef<HTMLSpanElement, {}>((_, fwdRef) => {
/**
* Using \<span\> as placeholder to prevent validateDOMNesting warnings
* Warnings will still show up if `usePortal` is false
*/
return <span ref={fwdRef} className={hiddenPlaceholderStyle} />;
});

HiddenPlaceholder.displayName = 'HiddenPlaceholder';

interface UseContentNodeReturnObj {
/**
* `contentNode` is the direct child of the popover element and wraps the children. It
* is used to calculate the position of the popover because its parent has a transition.
* This prevents getting the width of the popover until the transition completes
*/
contentNode: HTMLDivElement | null;

/**
* We shadow the `contentNode` onto this `contentNodeRef` as <Transition> from
* react-transition-group only accepts useRef objects. Without this, StrictMode
* warnings are produced by react-transition-group.
*/
contentNodeRef: React.MutableRefObject<HTMLDivElement | null>;

/**
* `ContentWrapper` is used to wrap the children of the popover component. We need
* an inner wrapper with a ref because placing the ref on the parent will create an
* infinite loop in some cases when dynamic styles are applied.
*/
ContentWrapper: React.ForwardRefExoticComponent<
{
children: ReactNode;
} & React.RefAttributes<HTMLDivElement>
>;

/**
* Dispatch method to attach `contentNode` to the `ContentWrapper`
*/
setContentNode: React.Dispatch<React.SetStateAction<HTMLDivElement | null>>;
}

export function useContentNode(): UseContentNodeReturnObj {
const [contentNode, setContentNode] = React.useState<HTMLDivElement | null>(
null,
Expand All @@ -137,26 +70,6 @@ export function useContentNode(): UseContentNodeReturnObj {
};
}

const ContentWrapper = forwardRef<HTMLDivElement, { children: ReactNode }>(
({ children }, fwdRef) => {
return (
<div ref={fwdRef} className={contentClassName}>
{children}
</div>
);
},
);

ContentWrapper.displayName = 'ContentWrapper';

type UsePopoverPositioningProps = Pick<
PopoverProps,
'active' | 'adjustOnMutation' | 'align' | 'justify' | 'scrollContainer'
> & {
contentNode: HTMLDivElement | null;
referenceElement: HTMLElement | null;
};

export function usePopoverPositioning({
active,
adjustOnMutation,
Expand Down
65 changes: 65 additions & 0 deletions packages/popover/src/Popover.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,68 @@ export type PopoverProps = {
/** Props used by the popover component */
export type PopoverComponentProps = Omit<HTMLElementProps<'div'>, 'children'> &
PopoverProps;

export interface UseReferenceElementReturnObj {
/**
* `HiddenPlaceholder` is used if `refEl` is undefined. The placeholder's parent is
* used as the element against which the popover component will be positioned
*/
HiddenPlaceholder: React.ForwardRefExoticComponent<
React.RefAttributes<HTMLSpanElement>
>;

/**
* Ref to access hidden placeholder element
*/
placeholderRef: React.MutableRefObject<HTMLSpanElement | null>;

/**
* Element against which the popover component will be positioned
*/
referenceElement: HTMLElement | null;

/**
* Boolean to determine if a hidden placeholder should be rendered
*/
renderHiddenPlaceholder: boolean;
}

export interface UseContentNodeReturnObj {
/**
* `contentNode` is the direct child of the popover element and wraps the children. It
* is used to calculate the position of the popover because its parent has a transition.
* This prevents getting the width of the popover until the transition completes
*/
contentNode: HTMLDivElement | null;

/**
* We shadow the `contentNode` onto this `contentNodeRef` as <Transition> from
* react-transition-group only accepts useRef objects. Without this, StrictMode
* warnings are produced by react-transition-group.
*/
contentNodeRef: React.MutableRefObject<HTMLDivElement | null>;

/**
* `ContentWrapper` is used to wrap the children of the popover component. We need
* an inner wrapper with a ref because placing the ref on the parent will create an
* infinite loop in some cases when dynamic styles are applied.
*/
ContentWrapper: React.ForwardRefExoticComponent<
{
children: React.ReactNode;
} & React.RefAttributes<HTMLDivElement>
>;

/**
* Dispatch method to attach `contentNode` to the `ContentWrapper`
*/
setContentNode: React.Dispatch<React.SetStateAction<HTMLDivElement | null>>;
}

export type UsePopoverPositioningProps = Pick<
PopoverProps,
'active' | 'adjustOnMutation' | 'align' | 'justify' | 'scrollContainer'
> & {
contentNode: HTMLDivElement | null;
referenceElement: HTMLElement | null;
};

0 comments on commit 3c16270

Please sign in to comment.