feat: auto-position tooltip and suport overflowing container (#3631)
This commit is contained in:
parent
0bbb4535cf
commit
357266e9ab
@ -67,11 +67,7 @@ export const actionChangeExportEmbedScene = register({
|
|||||||
onChange={(event) => updateData(event.target.checked)}
|
onChange={(event) => updateData(event.target.checked)}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
{t("labels.exportEmbedScene")}
|
{t("labels.exportEmbedScene")}
|
||||||
<Tooltip
|
<Tooltip label={t("labels.exportEmbedScene_details")} long={true}>
|
||||||
label={t("labels.exportEmbedScene_details")}
|
|
||||||
position="above"
|
|
||||||
long={true}
|
|
||||||
>
|
|
||||||
<div className="TooltipIcon">{questionCircle}</div>
|
<div className="TooltipIcon">{questionCircle}</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</label>
|
</label>
|
||||||
|
@ -1,58 +1,25 @@
|
|||||||
@import "../css/variables.module";
|
@import "../css/variables.module";
|
||||||
.excalidraw {
|
.excalidraw-tooltip {
|
||||||
.Tooltip {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Tooltip__label {
|
|
||||||
--arrow-size: 4px;
|
|
||||||
visibility: hidden;
|
|
||||||
background: $oc-black;
|
|
||||||
color: $oc-white;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 8px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 1000;
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.5;
|
padding: 8px;
|
||||||
font-weight: 500;
|
border-radius: 6px;
|
||||||
// extra pixel offset for unknown reasons
|
box-sizing: border-box;
|
||||||
left: calc(50% + var(--arrow-size) / 2 - 1px);
|
pointer-events: none;
|
||||||
transform: translateX(-50%);
|
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
&::after {
|
background: $oc-black;
|
||||||
content: "";
|
|
||||||
border: var(--arrow-size) solid transparent;
|
|
||||||
position: absolute;
|
|
||||||
left: calc(50% - var(--arrow-size));
|
|
||||||
}
|
|
||||||
|
|
||||||
&--above {
|
line-height: 1.5;
|
||||||
bottom: calc(100% + var(--arrow-size) + 3px);
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $oc-white;
|
||||||
|
|
||||||
&::after {
|
display: none;
|
||||||
border-top-color: $oc-black;
|
|
||||||
top: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--below {
|
&.excalidraw-tooltip--visible {
|
||||||
top: calc(100% + var(--arrow-size) + 3px);
|
display: block;
|
||||||
|
|
||||||
&::after {
|
|
||||||
border-bottom-color: $oc-black;
|
|
||||||
bottom: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.Tooltip:hover .Tooltip__label {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Tooltip__label:hover {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,92 @@
|
|||||||
import "./Tooltip.scss";
|
import "./Tooltip.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
|
const getTooltipDiv = () => {
|
||||||
|
const existingDiv = document.querySelector<HTMLDivElement>(
|
||||||
|
".excalidraw-tooltip",
|
||||||
|
);
|
||||||
|
if (existingDiv) {
|
||||||
|
return existingDiv;
|
||||||
|
}
|
||||||
|
const div = document.createElement("div");
|
||||||
|
document.body.appendChild(div);
|
||||||
|
div.classList.add("excalidraw-tooltip");
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTooltip = (
|
||||||
|
item: HTMLDivElement,
|
||||||
|
tooltip: HTMLDivElement,
|
||||||
|
label: string,
|
||||||
|
long: boolean,
|
||||||
|
) => {
|
||||||
|
tooltip.classList.add("excalidraw-tooltip--visible");
|
||||||
|
tooltip.style.minWidth = long ? "50ch" : "10ch";
|
||||||
|
tooltip.style.maxWidth = long ? "50ch" : "15ch";
|
||||||
|
|
||||||
|
tooltip.textContent = label;
|
||||||
|
|
||||||
|
const {
|
||||||
|
x: itemX,
|
||||||
|
bottom: itemBottom,
|
||||||
|
top: itemTop,
|
||||||
|
width: itemWidth,
|
||||||
|
} = item.getBoundingClientRect();
|
||||||
|
|
||||||
|
const {
|
||||||
|
width: labelWidth,
|
||||||
|
height: labelHeight,
|
||||||
|
} = tooltip.getBoundingClientRect();
|
||||||
|
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
const margin = 5;
|
||||||
|
|
||||||
|
const left = itemX + itemWidth / 2 - labelWidth / 2;
|
||||||
|
const offsetLeft =
|
||||||
|
left + labelWidth >= viewportWidth ? left + labelWidth - viewportWidth : 0;
|
||||||
|
|
||||||
|
const top = itemBottom + margin;
|
||||||
|
const offsetTop =
|
||||||
|
top + labelHeight >= viewportHeight
|
||||||
|
? itemBottom - itemTop + labelHeight + margin * 2
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
Object.assign(tooltip.style, {
|
||||||
|
top: `${top - offsetTop}px`,
|
||||||
|
left: `${left - offsetLeft}px`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
type TooltipProps = {
|
type TooltipProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
label: string;
|
label: string;
|
||||||
position?: "above" | "below";
|
|
||||||
long?: boolean;
|
long?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Tooltip = ({
|
export const Tooltip = ({ children, label, long = false }: TooltipProps) => {
|
||||||
children,
|
useEffect(() => {
|
||||||
|
return () =>
|
||||||
|
getTooltipDiv().classList.remove("excalidraw-tooltip--visible");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onPointerEnter={(event) =>
|
||||||
|
updateTooltip(
|
||||||
|
event.currentTarget as HTMLDivElement,
|
||||||
|
getTooltipDiv(),
|
||||||
label,
|
label,
|
||||||
position = "below",
|
long,
|
||||||
long = false,
|
)
|
||||||
}: TooltipProps) => (
|
}
|
||||||
<div className="Tooltip">
|
onPointerLeave={() =>
|
||||||
<span
|
getTooltipDiv().classList.remove("excalidraw-tooltip--visible")
|
||||||
className={
|
|
||||||
position === "above"
|
|
||||||
? "Tooltip__label Tooltip__label--above"
|
|
||||||
: "Tooltip__label Tooltip__label--below"
|
|
||||||
}
|
}
|
||||||
style={{ width: long ? "50ch" : "10ch" }}
|
|
||||||
>
|
>
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -340,7 +340,7 @@ const ExcalidrawWrapper = () => {
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
aria-label={t("encrypted.link")}
|
aria-label={t("encrypted.link")}
|
||||||
>
|
>
|
||||||
<Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
|
<Tooltip label={t("encrypted.tooltip")} long={true}>
|
||||||
{shield}
|
{shield}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</a>
|
</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user