Skip to content

Commit

Permalink
chore: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DSil committed Oct 14, 2024
1 parent 6ba3a3c commit 4b3ffa4
Show file tree
Hide file tree
Showing 21 changed files with 316 additions and 300 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@babel/types": "^7.21.4",
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^17.0.3",
"@floating-ui/react": "^0.26.24",
"@kiwicom/browserslist-config": "^4.0.3",
"@octokit/rest": "^19.0.5",
"@size-limit/file": "^8.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/orbit-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"react-dom": ">=17.0.0"
},
"dependencies": {
"@floating-ui/react": "0.26.24",
"@kiwicom/orbit-design-tokens": "^8.1.0",
"@kiwicom/orbit-tailwind-preset": "^5.1.0",
"@popperjs/core": "^2.9.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/orbit-components/src/Popover/Popover.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
PopoverOverlapped,
PopoverLongContent,
} from "./Popover.ct-story";
import { PLACEMENTS } from "../common/consts";
import { PLACEMENTS } from "../common/placements";

test.describe("visual Popover", () => {
test(`screenshot for default`, async ({ mount }, { project }) => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions packages/orbit-components/src/Popover/Popover.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Meta, StoryObj } from "@storybook/react";

import Tooltip from "../Tooltip";
import RenderInRtl from "../utils/rtl/RenderInRtl";
import { PLACEMENTS } from "../common/consts";
import { AUTO_PLACEMENTS, PLACEMENTS } from "../common/placements";
import Stack from "../Stack";
import Button from "../Button";
import Stepper from "../Stepper";
Expand Down Expand Up @@ -133,7 +133,7 @@ export const Placement: Story = {

argTypes: {
placement: {
options: Object.values(PLACEMENTS),
options: [...Object.values(AUTO_PLACEMENTS), ...Object.values(PLACEMENTS)],
control: {
type: "select",
},
Expand Down
257 changes: 140 additions & 117 deletions packages/orbit-components/src/Popover/components/ContentWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
"use client";

import * as React from "react";
import { usePopper } from "react-popper";
import type { Placement } from "@popperjs/core/lib/enums";
import cx from "clsx";
import {
useFloating,
offset,
flip,
shift,
autoUpdate,
autoPlacement,
FloatingFocusManager,
} from "@floating-ui/react";

import type * as Common from "../../common/types";
import Button from "../../Button";
import useMediaQuery from "../../hooks/useMediaQuery";
import useClickOutside from "../../hooks/useClickOutside";
import useLockScrolling from "../../hooks/useLockScrolling";
import { ModalContext } from "../../Modal/ModalContext";
import { PLACEMENTS } from "../../common/consts";
import boundingClientRect from "../../utils/boundingClientRect";
import type { Offset } from "../types";
import {
type AutoPlacement,
type Placement,
getAutoAlignment,
isFixedPlacement,
PLACEMENTS,
} from "../../common/placements";

export interface Props extends Common.Globals {
children: React.ReactNode;
referenceElement: HTMLElement | null;
placement: Placement;
placement: Placement | AutoPlacement;
width?: string;
zIndex?: number;
maxHeight?: string;
Expand All @@ -43,7 +56,7 @@ const PopoverContentWrapper = ({
width,
maxHeight,
noFlip,
offset = { top: 4, left: 0 },
offset: offsetProp = { top: 4, left: 0 },
referenceElement,
dataTest,
id,
Expand All @@ -64,38 +77,46 @@ const PopoverContentWrapper = ({
const scrollingElementRef = React.useRef<HTMLDivElement | null>(null);
useLockScrolling(scrollingElementRef, lockScrolling && !isLargeMobile);

const popoverRef = React.useRef<HTMLDivElement | null>(null);
const windowHeight = typeof window !== "undefined" ? window.innerHeight : 0;

const { styles, update } = usePopper(referenceElement, popoverRef.current, {
placement,
const isAutoPlacement = !isFixedPlacement(placement);

const {
refs,
strategy,
update: updateFloating,
floatingStyles,
context,
} = useFloating({
placement: isAutoPlacement ? undefined : placement,
strategy: fixed ? "fixed" : "absolute",
modifiers: [
{
name: "offset",
enabled: !!offset,
options: {
offset: [offset.left, overlapped ? -Number(referenceElement?.offsetHeight) : offset.top],
},
},
{
name: "flip",
enabled: !noFlip,
},
{ name: "preventOverflow", enabled: !allowOverflow },
whileElementsMounted: autoUpdate,
elements: {
reference: referenceElement,
},
middleware: [
offset({
mainAxis: overlapped ? -Number(referenceElement?.offsetHeight) : offsetProp.top,
crossAxis: offsetProp.left,
}),
isAutoPlacement &&
autoPlacement({
alignment: getAutoAlignment(placement),
autoAlignment: !noFlip,
}),
!noFlip && !isAutoPlacement && flip(),
!allowOverflow && shift(),
],
});

const { popper } = styles;

React.useEffect(() => {
const timer = setTimeout(() => {
if (popoverRef.current) {
popoverRef.current.focus();
if (refs.floating.current) {
refs.floating.current.focus();
}
}, 100);

if (update) update();
if (updateFloating) updateFloating();

if (actionsRef.current) {
const { height } = boundingClientRect({ current: actionsRef.current });
Expand All @@ -114,7 +135,7 @@ const PopoverContentWrapper = ({
clearTimeout(timer);
document.removeEventListener("keydown", handleKeyDown);
};
}, [update, actions, setActionsHeight, onClose]);
}, [updateFloating, actions, setActionsHeight, onClose, refs.floating]);

useClickOutside(
content,
Expand All @@ -125,12 +146,12 @@ const PopoverContentWrapper = ({
);

const cssVars = {
"--popper-top": popper.top,
"--popper-left": popper.left,
"--popper-right": popper.right,
"--popper-bottom": popper.bottom,
"--popper-transform": popper.transform,
"--popper-position": popper.position,
"--popover-top": floatingStyles.top ?? 0,
"--popover-left": floatingStyles.left ?? 0,
"--popover-right": floatingStyles.right ?? "auto",
"--popover-bottom": floatingStyles.bottom ?? "auto",
"--popover-transform": floatingStyles.transform,
"--popover-position": strategy,
"--popover-zIndex": zIndex,
"--popover-width": width,
} as React.CSSProperties;
Expand All @@ -152,107 +173,109 @@ const PopoverContentWrapper = ({
)}
onClick={onClose}
/>
<div
role="dialog"
// @ts-expect-error expected
// eslint-disable-next-line react/no-unknown-property
popover
ref={popoverRef}
data-test={dataTest}
id={id}
className={cx(
"fixed",
"inset-x-0 bottom-0",
"h-auto w-full",
"z-[1000]",
"box-border",
"shadow-level3-reverse",
"bg-white-normal",
"max-h-[calc(100%_-_theme(spacing.800))]",
"focus:outline-none",
"lm:top-[var(--popper-top)]",
"lm:left-[var(--popper-left)]",
"lm:right-[var(--popper-right)]",
"lm:bottom-[var(--popper-bottom)]",
"lm:[position:var(--popper-position)]",
"lm:[transform:var(--popper-transform)]",
"lm:transition-opacity lm:duration-fast lm:ease-in-out",
"lm:rounded-100",
"lm:shadow-level3",
"lm:max-h-none",
isInsideModal ? "lm:z-[1000]" : "lm:z-[var(--popover-zIndex)]",
width ? "lm:w-[var(--popover-width)]" : "lm:w-auto",
shown ? "lm:opacity-100" : "lm:opacity-0",
)}
style={cssVars}
>
<FloatingFocusManager context={context}>
<div
ref={content}
role="dialog"
// @ts-expect-error expected
// eslint-disable-next-line react/no-unknown-property
popover
ref={refs.setFloating}
data-test={dataTest}
id={id}
className={cx(
shown ? "translate-y-0" : "translate-y-full",
"will-change-transform",
"duration-fast transition-[opacity,transform] ease-in-out",
"lm:transform-none",
"lm:transition-none",
"fixed",
"inset-x-0 bottom-0",
"h-auto w-full",
"z-[1000]",
"box-border",
"shadow-level3-reverse",
"bg-white-normal",
"max-h-[calc(100%_-_theme(spacing.800))]",
"focus:outline-none",
"lm:top-[var(--popover-top)]",
"lm:left-[var(--popover-left)]",
"lm:right-[var(--popover-right)]",
"lm:bottom-[var(--popover-bottom)]",
"lm:[position:var(--popover-position)]",
"lm:[transform:var(--popover-transform)]",
"lm:transition-opacity lm:duration-fast lm:ease-in-out",
"lm:rounded-100",
"lm:shadow-level3",
"lm:max-h-none",
isInsideModal ? "lm:z-[1000]" : "lm:z-[var(--popover-zIndex)]",
width ? "lm:w-[var(--popover-width)]" : "lm:w-auto",
shown ? "lm:opacity-100" : "lm:opacity-0",
)}
style={cssVars}
>
<div
ref={scrollingElementRef}
ref={content}
className={cx(
"overflow-auto",
"rounded-t-modal",
"absolute left-0",
"w-full",
"bg-white-normal",
"bottom-[var(--actions-height)]",
windowHeight &&
actionsHeight &&
"max-h-[calc(var(--window-height)-var(--actions-height)-32px)]",
noPadding ? "p-0" : "p-400",
"lm:max-h-[var(--max-height)]",
"lm:rounded-100",
"lm:bottom-auto",
"lm:left-auto",
"lm:relative",
shown ? "translate-y-0" : "translate-y-full",
"will-change-transform",
"duration-fast transition-[opacity,transform] ease-in-out",
"lm:transform-none",
"lm:transition-none",
)}
style={
{
"--actions-height": actionsHeight != null && `${actionsHeight}px`,
"--window-height": windowHeight != null && `${windowHeight}px`,
"--max-height": maxHeight != null ? `${maxHeight}px` : "100%",
} as React.CSSProperties
}
>
{children}
</div>
{actions ? (
<div
ref={actionsRef}
ref={scrollingElementRef}
className={cx(
"fixed",
"bottom-0 left-0",
"overflow-auto",
"rounded-t-modal",
"absolute left-0",
"w-full",
"box-border",
"p-400 pt-300",
"bg-white-normal",
"[&_.orbit-button-primitive]:w-full [&_.orbit-button-primitive]:flex-auto",
"bottom-[var(--actions-height)]",
windowHeight &&
actionsHeight &&
"max-h-[calc(var(--window-height)-var(--actions-height)-32px)]",
noPadding ? "p-0" : "p-400",
"lm:max-h-[var(--max-height)]",
"lm:rounded-100",
"lm:bottom-auto",
"lm:left-auto",
"lm:relative",
"lm:bottom-auto lm:left-auto",
"lm:rounded-b-100",
"lm:[&_.orbit-button-primitive]:w-auto lm:[&_.orbit-button-primitive]:grow-0",
)}
style={
{
"--actions-height": actionsHeight != null && `${actionsHeight}px`,
"--window-height": windowHeight != null && `${windowHeight}px`,
"--max-height": maxHeight != null ? `${maxHeight}px` : "100%",
} as React.CSSProperties
}
>
{actions}
</div>
) : (
<div ref={actionsRef} className="p-400 lm:hidden lm:pb-0">
<Button type="secondary" fullWidth onClick={onClose}>
{labelClose}
</Button>
{children}
</div>
)}
{actions ? (
<div
ref={actionsRef}
className={cx(
"fixed",
"bottom-0 left-0",
"w-full",
"box-border",
"p-400 pt-300",
"bg-white-normal",
"[&_.orbit-button-primitive]:w-full [&_.orbit-button-primitive]:flex-auto",
"lm:relative",
"lm:bottom-auto lm:left-auto",
"lm:rounded-b-100",
"lm:[&_.orbit-button-primitive]:w-auto lm:[&_.orbit-button-primitive]:grow-0",
)}
>
{actions}
</div>
) : (
<div ref={actionsRef} className="p-400 lm:hidden lm:pb-0">
<Button type="secondary" fullWidth onClick={onClose}>
{labelClose}
</Button>
</div>
)}
</div>
</div>
</div>
</FloatingFocusManager>
</>
);
};
Expand Down
Loading

0 comments on commit 4b3ffa4

Please sign in to comment.