Skip to content

Instantly share code, notes, and snippets.

@burdiuz
Last active July 10, 2022 09:18
Show Gist options
  • Save burdiuz/1cf854f394fba042b76fa99b9e921733 to your computer and use it in GitHub Desktop.
Save burdiuz/1cf854f394fba042b76fa99b9e921733 to your computer and use it in GitHub Desktop.

Popover Alignment

A wrapper element that controls position of inner popover element. Accepts a render function as "children" prop.

Example with alignment:

<PopoverAlignment
  horizontal={HorizontalAlignment.RIGHT_OUT}
  vertical={VerticalAlignment.CENTER}
>
  {({ container, popover }) => (
    <div className={container}>
      <input />

      <div
        className={popover}
        style={{
          padding: "5px",
          border: "1px solid #ccc",
          borderRadius: "5px",
          boxSizing: "border-box",
          backgroundColor: "#ffffff",
          overflowY: "auto",
          zIndex: "100"
        }}
      >
        <ul>
          <li>Hello World #1</li>
          <li>Hello World #2</li>
          <li>Hello World #3</li>
          <li>Hello World #4</li>
          <li>Hello World #5</li>
          <li>Hello World #6</li>
        </ul>
      </div>
    </div>
  )}
</PopoverAlignment>

Example with container:

<PopoverContainer
  horizontal={HorizontalAlignment.RIGHT_OUT}
  vertical={VerticalAlignment.CENTER}
>
  {(className) => (
    <>
      <input />

      <div
        className={className}
        style={{
          padding: "5px",
          border: "1px solid #ccc",
          borderRadius: "5px",
          boxSizing: "border-box",
          backgroundColor: "#ffffff",
          overflowY: "auto",
          zIndex: "100"
        }}
      >
        <ul>
          <li>Hello World #1</li>
          <li>Hello World #2</li>
          <li>Hello World #3</li>
          <li>Hello World #4</li>
          <li>Hello World #5</li>
          <li>Hello World #6</li>
        </ul>
      </div>
    </>
  )}
</PopoverContainer>
import styles from "./alignments.module.css";
export const HorizontalAlignment = {
LEFT: styles.left,
RIGHT: styles.right,
CENTER: styles.center,
STRETCH: styles.stretch,
LEFT_OUT: styles["left-out"],
RIGHT_OUT: styles["right-out"]
};
export const VerticalAlignment = {
TOP: styles.top,
BOTTOM: styles.bottom,
CENTER: styles["center-vertical"],
STRETCH: styles["stretch-vertical"],
TOP_OVER: styles["top-over"],
BOTTOM_OVER: styles["bottom-over"]
};
export const Alignment = {
...VerticalAlignment,
...HorizontalAlignment,
CENTER_VERTICAL: VerticalAlignment.CENTER,
STRETCH_VERTICAL: VerticalAlignment.STRETCH
};
/**
*
*
* Base
*
*
*/
.container {
display: inline-block;
position: relative;
overflow: visible; /* -- include? */
}
.popover {
position: absolute;
width: fit-content;
/* z-index: 10; --- should not be included, only X/Y positioning */
}
/**
*
*
* Alignments
*
*
*/
.top/* - out */ {
bottom: 100%;
}
.top-over {
bottom: 0;
}
.bottom/* - out */ {
/* default */
}
.bottom-over {
top: 0;
}
.left/* -over */ {
/* default */
}
.left-out {
right: 100%;
}
.right/* -over */ {
right: 0;
}
.right-out {
left: 100%;
}
.center {
left: 50%;
transform: translateX(-50%);
}
.stretch {
left: 0;
right: 0;
width: auto;
}
.stretch-vertical {
top: 0;
bottom: 0;
height: auto;
}
.center-vertical {
top: 50%;
transform: translateY(-50%);
}
.center-vertical.center {
left: 50%;
transform: translate(-50%, -50%);
}
export * from "./alignments";
export * from "./useAlignmentClassNames";
export * from "./PopoverAlignment";
export * from "./PopoverContainer";
import { forwardRef } from "react";
import { HorizontalAlignment, VerticalAlignment } from "./alignments";
import { getAlignmentClassNames } from "./useAlignmentClassNames";
const PopoverAlignment = (
{
children = () => null,
horizontal = HorizontalAlignment.LEFT,
vertical = VerticalAlignment.BOTTOM,
},
ref
) => children(getAlignmentClassNames(vertical, horizontal));
import { forwardRef } from "react";
import { HorizontalAlignment, VerticalAlignment } from "./alignments";
import { getAlignmentClassNames } from "./useAlignmentClassNames";
const PopoverContainerInner = (
{
className = "",
children = () => null,
horizontal = HorizontalAlignment.LEFT,
vertical = VerticalAlignment.BOTTOM,
...props
},
ref
) => {
const { container, popover } = getAlignmentClassNames(vertical, horizontal);
return (
<div ref={ref} className={`${container} ${className}`} {...props}>
{children(popover, props)}
</div>
);
};
export const PopoverContainer = forwardRef(PopoverContainerInner);
import { useMemo } from "react";
import { HorizontalAlignment } from "./alignments";
import styles from "./alignments.module.css";
export const getAlignmentClassNames = (
vertical,
horizontal = HorizontalAlignment.LEFT
) => ({
container: styles.container,
popover: `${styles.popover} ${vertical} ${horizontal}`
});
export const useAlignmentClassNames = (vertical, horizontal) =>
useMemo(() => getAlignmentClassNames(vertical, horizontal), [
vertical,
horizontal
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment