Skip to content

Commit 39f406c

Browse files
committed
add DecoupledOverlay component
1 parent dc09382 commit 39f406c

5 files changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from "react";
2+
import { Meta, StoryFn } from "@storybook/react";
3+
4+
import { DecoupledOverlay, DecoupledOverlayProps, Tag, WhiteSpaceContainer } from "../../../index";
5+
6+
export default {
7+
title: "Components/DecoupledOverlay",
8+
component: DecoupledOverlay,
9+
argTypes: {},
10+
} as Meta<typeof DecoupledOverlay>;
11+
12+
const Template: StoryFn<typeof DecoupledOverlay> = (args: DecoupledOverlayProps) => {
13+
return (
14+
<>
15+
<Tag id={"decoupledTarget"}>Decoupled target</Tag>
16+
<DecoupledOverlay {...args} />
17+
</>
18+
);
19+
};
20+
21+
export const Default = Template.bind({});
22+
23+
Default.args = {
24+
children: (
25+
<WhiteSpaceContainer marginTop={"small"} marginRight={"small"} marginBottom={"small"} marginLeft={"small"}>
26+
Decoupled overlay
27+
</WhiteSpaceContainer>
28+
),
29+
targetSelectorOrElement: "#decoupledTarget",
30+
};
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React from "react";
2+
import { createPortal } from "react-dom";
3+
import { Classes as BlueprintClasses } from "@blueprintjs/core";
4+
import { createPopper } from "@popperjs/core";
5+
6+
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
7+
import { ContextOverlayProps, TestableComponent, TooltipSize } from "../../index";
8+
9+
/**
10+
* Overlay without the necessity to use a target that need to be rendered in place.
11+
* The target is referenced by a selector string or element object.
12+
* It can exist somewhere in the DOM, but it must exist when the overlay is rendered.
13+
* It is always displayed, close it by removement.
14+
*/
15+
export interface DecoupledOverlayProps
16+
extends React.HTMLAttributes<HTMLDivElement>,
17+
TestableComponent,
18+
Pick<ContextOverlayProps, "usePortal" | "portalContainer" | "placement" | "minimal"> {
19+
/**
20+
* Element that should be used. The step content is displayed as a tooltip instead of a modal.
21+
* In case of an array, the first match is highlighted. */
22+
targetSelectorOrElement: string | Element;
23+
/**
24+
* The size of the overlay.
25+
* */
26+
size?: TooltipSize;
27+
}
28+
29+
/** Popover that is displayed and points at the highlighted element. */
30+
export const DecoupledOverlay = ({
31+
targetSelectorOrElement,
32+
usePortal = true,
33+
portalContainer = document.body,
34+
minimal = false,
35+
placement = "auto",
36+
size = "large",
37+
children,
38+
}: DecoupledOverlayProps) => {
39+
const overlayRef = React.useCallback(
40+
(overlay: HTMLDivElement | null) => {
41+
const target =
42+
typeof targetSelectorOrElement === "string"
43+
? document.querySelector(targetSelectorOrElement)
44+
: targetSelectorOrElement;
45+
if (overlay && target) {
46+
createPopper(target, overlay, {
47+
placement: placement,
48+
modifiers: [
49+
{
50+
name: "offset",
51+
options: {
52+
offset: [0, 15],
53+
},
54+
},
55+
],
56+
});
57+
}
58+
},
59+
[targetSelectorOrElement]
60+
);
61+
62+
const overlay = (
63+
<div
64+
className={
65+
`${eccgui}-visual-tour__overlay` +
66+
` ${eccgui}-visual-tour__overlay--${size}` +
67+
` ${BlueprintClasses.POPOVER}` +
68+
(minimal ? ` ${BlueprintClasses.MINIMAL}` : "")
69+
}
70+
role="tooltip"
71+
ref={overlayRef}
72+
>
73+
{!minimal && (
74+
<div
75+
className={`${eccgui}-visual-tour__arrow ${BlueprintClasses.POPOVER_ARROW}`}
76+
data-popper-arrow
77+
aria-hidden
78+
/>
79+
)}
80+
<div className={`${BlueprintClasses.POPOVER_CONTENT} ${eccgui}-visual-tour__overlay__content`}>
81+
{children}
82+
</div>
83+
</div>
84+
);
85+
86+
return usePortal ? createPortal(overlay, portalContainer) : overlay;
87+
};
88+
89+
export default DecoupledOverlay;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.#{$eccgui}-visual-tour__arrow {
2+
&::before {
3+
background: $card-background-color;
4+
}
5+
6+
.#{$eccgui}-visual-tour__overlay[data-popper-placement="top"] & {
7+
bottom: -0.5 * $eccgui-size-block-whitespace;
8+
}
9+
.#{$eccgui}-visual-tour__overlay[data-popper-placement="right"] & {
10+
left: -0.5 * $eccgui-size-block-whitespace;
11+
}
12+
.#{$eccgui}-visual-tour__overlay[data-popper-placement="bottom"] & {
13+
top: -0.5 * $eccgui-size-block-whitespace;
14+
}
15+
.#{$eccgui}-visual-tour__overlay[data-popper-placement="left"] & {
16+
right: -0.5 * $eccgui-size-block-whitespace;
17+
}
18+
}
19+
20+
.#{$eccgui}-visual-tour__overlay {
21+
&.#{$prefix-blueprintjs}-popover {
22+
z-index: 8002; // 2 over application header
23+
}
24+
25+
&--small {
26+
@extend .#{$eccgui}-tooltip--small;
27+
}
28+
29+
&--medium {
30+
@extend .#{$eccgui}-tooltip--medium;
31+
}
32+
33+
&--large {
34+
@extend .#{$eccgui}-tooltip--large;
35+
}
36+
37+
&:has(.#{$eccgui}-visual-tour__arrow) {
38+
.#{$eccgui}-visual-tour__overlay__content {
39+
min-height: 30px; // height of blueprint arrow
40+
}
41+
}
42+
}
43+
44+
.#{$eccgui}-visual-tour__overlay__content {
45+
padding: 0.1px; // force margins of children to stay inside
46+
}

src/components/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@import "./Card/card";
66
@import "./Chat/chat";
77
@import "./Checkbox/checkbox";
8+
@import "./DecoupledOverlay/decoupledoverlay";
89
@import "./Depiction/depiction";
910
@import "./Dialog/dialog";
1011
@import "./FlexibleLayout/flexiblelayout";

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export * from "./Checkbox/Checkbox";
99
export * from "./CodeAutocompleteField";
1010
export * from "./ContentGroup/ContentGroup";
1111
export * from "./ContextOverlay";
12+
export * from "./DecoupledOverlay/DecoupledOverlay";
1213
export * from "./Depiction/Depiction";
1314
export * from "./Dialog";
1415
export * from "./FlexibleLayout";

0 commit comments

Comments
 (0)