Skip to content

Commit

Permalink
replace DOMRect with custom Rect interface to fix SSR issues
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkatz committed Oct 19, 2023
1 parent c3e4e56 commit 950d752
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 36 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## [8.0] - 2023-10-18
## [8.0.1] - 2023-10-19

## Changed

- Rolled back `DOMRect` changes as it interferes with SSR, replaced with custom `Rect` interface that mirrors the same API

## [8.0.0] - 2023-10-18

## Changed

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,16 +328,16 @@ Check out [React's ref forwarding API](https://reactjs.org/docs/forwarding-refs.
| <b>Property<b> | Type | Description |
| -------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| isPositioned | `boolean` | After the popover has positioned its contents, this field is true. Prior, it is false. |
| childRect | `DOMRect` | The current rect of the popover's child (i.e., the source from which the popover renders). |
| popoverRect | `DOMRect` | The current rect of the popover's contents. |
| parentRect | `DOMRect` | The current rect of the popover child's parent. |
| childRect | `Rect` | The current rect of the popover's child (i.e., the source from which the popover renders). |
| popoverRect | `Rect` | The current rect of the popover's contents. |
| parentRect | `Rect` | The current rect of the popover child's parent. |
| position | `'left'` \| `'right'` \| `'top'` \| `'bottom'` \| `undefined` | The current position of the popover in relation to the child. `undefined` implies the user has set an absolute transform. |
| align | `'start'` \| `'center'` \| `'end'` \| `undefined` | The cross-axis alignment of the popover's contents. `undefined` implies the user has set an explicit `contentLocation`. |
| padding | `number` | The distance between the popover's child and contents. If set to zero, the two are touching. |
| nudgedLeft | `number` | If the popover's contents encounter a boundary violation that does not warrant a reposition, the contents are instead "nudged" by the appropriate top and left values to keep the contents within the boundary. This is the left value. |
| nudgedTop | `number` | If the popover's contents encounter a boundary violation that does not warrant a reposition, the contents are instead "nudged" by the appropriate top and left values to keep the contents within the boundary. This is the top value. |
| boundaryInset | `number` | The popover's contents will encounter boundary violations prior to the actual `parentElement`'s boundaries by this number in pixels. Can be negative. |
| boundaryRect | `DOMRect` | The current rect of the popover's boundaries. |
| boundaryRect | `Rect` | The current rect of the popover's boundaries. |
| transform | `{ top?: number; left?: number; }` \| undefined | The values you provided to the `transform` prop, if they exist. |
| violations | `{ top: number; left: number; bottom: number; right: number; }` | An object containing boundary violations. Expect a value of `0` if no boundary violation exists at that bound (i.e., your popover is entirely within that bound), and expect positive values representing pixels beyond that bound if a violation exists (i.e., your popover exceeds the `top` bound by ten pixels, `top` will be `10`). |
| hasViolations | `boolean` | `true` if violations exist at any boundary, `false` otherwise. |
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-tiny-popover",
"version": "8.0.0",
"description": "A simple and highly customizable popover react higher order component with no other dependencies! Typescript friendly.",
"version": "8.0.1",
"description": "A simple and highly customizable popover react higher order component with no other dependencies!",
"keywords": [
"react",
"popover",
Expand Down
3 changes: 3 additions & 0 deletions src/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const PopoverInternal = forwardRef(
Array.isArray(externalPositions) ? externalPositions : [externalPositions],
);

console.log('HI FROM POPOVER');

// TODO: factor prevs out into a custom prevs hook
const prevIsOpen = useRef(false);
const prevPositions = useRef<PopoverPosition[] | undefined>();
Expand Down Expand Up @@ -105,6 +107,7 @@ const PopoverInternal = forwardRef(
positions !== prevPositions.current ||
reposition !== prevReposition.current)
) {
console.log('POSITIONING POPOVER');
positionPopover();
}

Expand Down
33 changes: 21 additions & 12 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Ref, MutableRefObject, CSSProperties, FC } from 'react';

export type Rect = {
top: number;
left: number;
right: number;
bottom: number;
width: number;
height: number;
};

export type BoundaryViolations = {
top: number;
left: number;
Expand All @@ -8,10 +17,10 @@ export type BoundaryViolations = {
};

export type PopoverState = {
childRect: DOMRect;
popoverRect: DOMRect;
parentRect: DOMRect;
boundaryRect: DOMRect;
childRect: Rect;
popoverRect: Rect;
parentRect: Rect;
boundaryRect: Rect;
position?: PopoverPosition;
align?: PopoverAlign;
padding: number;
Expand All @@ -38,8 +47,8 @@ export type PopoverPosition = 'left' | 'right' | 'top' | 'bottom';
export type PopoverAlign = 'start' | 'center' | 'end';

export type UseArrowContainerProps = {
childRect: DOMRect;
popoverRect: DOMRect;
childRect: Rect;
popoverRect: Rect;
position?: PopoverPosition;
arrowSize: number;
arrowColor: string;
Expand Down Expand Up @@ -84,12 +93,12 @@ export type PopoverProps = BasePopoverProps & {

export type PositionPopoverProps = {
positionIndex?: number;
childRect?: DOMRect;
popoverRect?: DOMRect;
parentRect?: DOMRect;
scoutRect?: DOMRect;
parentRectAdjusted?: DOMRect;
boundaryRect?: DOMRect;
childRect?: Rect;
popoverRect?: Rect;
parentRect?: Rect;
scoutRect?: Rect;
parentRectAdjusted?: Rect;
boundaryRect?: Rect;
};

export type PositionPopover = (props?: PositionPopoverProps) => void;
Expand Down
11 changes: 8 additions & 3 deletions src/usePopover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
UsePopoverProps,
UsePopoverResult,
} from '.';
import { EMPTY_RECT, getNewPopoverRect, getNudgedPopoverRect } from './util';
import { EMPTY_RECT, createRect, getNewPopoverRect, getNudgedPopoverRect } from './util';
import { useElementRef } from './useElementRef';

const POPOVER_STYLE: Partial<CSSStyleDeclaration> = {
Expand Down Expand Up @@ -87,7 +87,12 @@ export const usePopover = ({

onPositionPopover({
childRect,
popoverRect: new DOMRect(finalLeft, finalTop, popoverRect.width, popoverRect.height),
popoverRect: createRect({
left: finalLeft,
top: finalTop,
width: popoverRect.width,
height: popoverRect.height,
}),
parentRect,
boundaryRect,
padding,
Expand Down Expand Up @@ -160,7 +165,7 @@ export const usePopover = ({

const popoverState: PopoverState = {
childRect,
popoverRect: new DOMRect(finalLeft, finalTop, width, height),
popoverRect: createRect({ left: finalLeft, top: finalTop, width, height }),
parentRect,
boundaryRect,
position,
Expand Down
51 changes: 37 additions & 14 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import { PopoverPosition, PopoverAlign } from './index';
import { PopoverPosition, PopoverAlign, Rect } from './index';

export const EMPTY_RECT = new DOMRect(0, 0, 0, 0);
export const EMPTY_RECT: Rect = {
top: 0,
left: 0,
right: 0,
bottom: 0,
width: 0,
height: 0,
};

export type CreateRectProps = {
top: number;
left: number;
width: number;
height: number;
};

export const createRect = ({ top, left, width, height }: CreateRectProps) => ({
top,
left,
width,
height,
right: left + width,
bottom: top + height,
});

export const rectsAreEqual = (rectA: DOMRect, rectB: DOMRect) =>
export const rectsAreEqual = (rectA: Rect, rectB: Rect) =>
rectA === rectB ||
(rectA?.bottom === rectB?.bottom &&
rectA?.height === rectB?.height &&
Expand Down Expand Up @@ -31,11 +54,11 @@ export const createContainer = ({

export const popoverRectForPosition = (
position: PopoverPosition,
childRect: DOMRect,
popoverRect: DOMRect,
childRect: Rect,
popoverRect: Rect,
padding: number,
align: PopoverAlign,
): DOMRect => {
): Rect => {
const targetMidX = childRect.left + childRect.width / 2;
const targetMidY = childRect.top + childRect.height / 2;
const { width, height } = popoverRect;
Expand Down Expand Up @@ -85,16 +108,16 @@ export const popoverRectForPosition = (
break;
}

return new DOMRect(left, top, width, height);
return createRect({ left, top, width, height });
};

interface GetNewPopoverRectProps {
position: PopoverPosition;
reposition: boolean;
align: PopoverAlign;
childRect: DOMRect;
popoverRect: DOMRect;
boundaryRect: DOMRect;
childRect: Rect;
popoverRect: Rect;
boundaryRect: Rect;
padding: number;
}

Expand Down Expand Up @@ -126,10 +149,10 @@ export const getNewPopoverRect = (
};

export const getNudgedPopoverRect = (
popoverRect: DOMRect,
boundaryRect: DOMRect,
popoverRect: Rect,
boundaryRect: Rect,
boundaryInset: number,
): DOMRect => {
): Rect => {
const topBoundary = boundaryRect.top + boundaryInset;
const leftBoundary = boundaryRect.left + boundaryInset;
const rightBoundary = boundaryRect.right - boundaryInset;
Expand All @@ -140,5 +163,5 @@ export const getNudgedPopoverRect = (
let left = popoverRect.left < leftBoundary ? leftBoundary : popoverRect.left;
left = left + popoverRect.width > rightBoundary ? rightBoundary - popoverRect.width : left;

return new DOMRect(left, top, popoverRect.width, popoverRect.height);
return createRect({ ...popoverRect, top, left });
};

0 comments on commit 950d752

Please sign in to comment.