Skip to content

Commit

Permalink
Replace Tooltip element as it's very slow. (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjkw31 authored Nov 13, 2024
1 parent 36da4f1 commit e6c7d87
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 31 deletions.
18 changes: 10 additions & 8 deletions src/components/HelpIcon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
import { Tooltip } from "@mui/material";
import { Tooltip } from "../Tooltip";

export type HelpIconProps = {
title: string;
Expand All @@ -9,13 +9,15 @@ export type HelpIconProps = {
export const HelpIcon = (props: HelpIconProps) => {
return (
<Tooltip style={props.style ?? {}} title={props.title}>
<HelpOutlineIcon
sx={{
color: "rgba(34, 51, 84, 0.7)",
padding: "0 0 0 8px",
fontSize: "25px",
}}
/>
<span>
<HelpOutlineIcon
sx={{
color: "rgba(34, 51, 84, 0.7)",
padding: "0 0 0 8px",
fontSize: "25px",
}}
/>
</span>
</Tooltip>
);
};
97 changes: 97 additions & 0 deletions src/components/Tooltip/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from "react";

type TooltipParams = {
title: string;
onMouseEnter?: () => void;
placement?: "top" | "bottom" | "left" | "right"
style?: React.CSSProperties;
children: JSX.Element;
}

const div = document.createElement("div"),
mo = new MutationObserver(r => {
const target = r.at(0)?.target as HTMLElement | null;

if (target) {
showTooltip({ target }, false);
}
}),
showTooltip = ({ target }: { target: HTMLElement }, observe = true) => {
const { width: tWidth, height: tHeight } = target.getBoundingClientRect(),
content = target.dataset.tooltip ?? null,
{ scrollHeight, scrollWidth } = document.documentElement;


if (content) {
let x = 0,
y = 0,
op = target as HTMLElement | null,
p: HTMLElement | null = target;

while (p && p !== document.body) {
if (p === op) {

x += op.offsetLeft;
y += op.offsetTop;
op = op.offsetParent as HTMLElement | null;
}

x -= p.scrollLeft;
y -= p.scrollTop;

p = p.parentElement;
}

div.textContent = content;

document.body.append(div);

const { width: dWidth, height: dHeight } = div.getBoundingClientRect();

let left = 0, top = 0;

switch (target.dataset.tooltipPos) {
case "top":
top = y - dHeight - 10;
left = x + (tWidth - dWidth) / 2;

break;
case "left":
top = y + (tHeight - dHeight) / 2;
left = x - dWidth - 10;
}

top = Math.min(Math.max(0, top), scrollHeight - dHeight);
left = Math.min(Math.max(0, left), scrollWidth - dWidth);

if (top === 0 && target.dataset.tooltipPos === "top" && top < y + tHeight && top + dHeight > y) {
top = y + tHeight + 10;
}

if (left === 0 && target.dataset.tooltipPos === "left" && left < x + tWidth && left + dWidth > x) {
left = x + tWidth + 10;
}

div.setAttribute("style", `top: ${top | 0}px; left: ${left | 0}px`);
}

if (observe) {
mo.observe(target, { "attributes": true, "attributeFilter": ["data-tooltip-pos", "data-tooltip"] });
}
},
removeTooltip = () => {
div.remove()
mo.disconnect();
};

div.setAttribute("id", "tooltip");

export function Tooltip({ title, placement, onMouseEnter, children }: TooltipParams) {
return React.cloneElement(children, {
"data-tooltip-pos": placement || "top",
"data-tooltip": title,
"onMouseEnter": onMouseEnter,
"onMouseOver": showTooltip,
"onMouseOut": removeTooltip
});
}
29 changes: 10 additions & 19 deletions src/components/ViewEnvironments/Drawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Box,
Breadcrumbs,
Button,
Chip,
Dialog,
DialogActions,
DialogContent,
Expand All @@ -13,9 +12,9 @@ import {
Drawer,
Icon,
Stack,
Tooltip,
Typography,
} from "@mui/material";
import { Tooltip } from '../../Tooltip';
import { createContext, useContext, useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
Expand Down Expand Up @@ -67,7 +66,7 @@ export function wrapRecipe(pkg: Package, node: JSX.Element, recipeDescriptions:
return <Tooltip title={description} placement="top">{node}</Tooltip>
}

return <Tooltip title="" onMouseOver={() => getRecipeDescription(pkg.name)}>{node}</Tooltip>
return <Tooltip title="" onMouseEnter={() => getRecipeDescription(pkg.name)}>{node}</Tooltip>
}


Expand Down Expand Up @@ -287,22 +286,14 @@ function EnvironmentDrawer({ env, open, onClose, recipeDescriptions, getRecipeDe
<Divider />
<Box style={{ padding: "0 18px" }}>
<h1>Packages</h1>
{(env?.packages ?? []).map((pkg, i) => {
return wrapIfInterpreted(env!, pkg,
<Box key={i} sx={{ float: "left" }}>
<Chip
className={isInterpreter(env!, pkg) ? " interpreter" : ""}
label={pkg.name + (pkg.version ? `@${pkg.version}` : "")}
sx={{
m: "4px",
color: "#5569ff",
border: "1px solid rgba(85, 105, 255, 0.7)",
backgroundColor: "transparent",
}}
/>
</Box>, recipeDescriptions, getRecipeDescription
);
})}
<ul className="packages">
{(env?.packages ?? []).map((pkg, i) => wrapIfInterpreted(env!, pkg,
<li key={i} className={isInterpreter(env!, pkg) ? " interpreter" : ""}>
{pkg.name + (pkg.version ? `@${pkg.version}` : "")}
</li>,
recipeDescriptions, getRecipeDescription
))}
</ul>
</Box>
</Box>
{env?.type === "softpack" &&
Expand Down
3 changes: 2 additions & 1 deletion src/components/ViewEnvironments/EnvironmentTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { isInterpreter, recipeDescriptionContext, wrapIfInterpreted } from "../D
import { useContext } from "react";

import { Masonry } from "@mui/lab";
import { LinearProgress, Tooltip } from "@mui/material";
import { LinearProgress } from "@mui/material";
import { Tooltip } from '../../Tooltip';
import { humanize } from "../../../humanize";
import { Environment, Package } from "../../../endpoints";

Expand Down
6 changes: 4 additions & 2 deletions src/routes/Root/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import ErrorIcon from "@mui/icons-material/Error";
import { InputAdornment, TextField, Tooltip } from "@mui/material";
import { InputAdornment, TextField } from "@mui/material";
import { Tooltip } from '../../components/Tooltip';
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline";
Expand Down Expand Up @@ -52,8 +53,9 @@ const Root = () => {
}

getPackageDescription(recipe)
.catch(() => ({ "description": "Unknown Module Package" }))
.then(desc => {
recipeDescriptions[recipe] = desc["description"] ?? "Unknown Module Package";
recipeDescriptions[recipe] = desc["description"];
setRecipeDescriptions({ ...recipeDescriptions });
})
};
Expand Down
26 changes: 25 additions & 1 deletion src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ summary {
overflow-y: auto;
}

#environments > li > ul:nth-of-type(3) li {
#environments > li > ul:nth-of-type(3) li, .packages li {
font-size: 0.8125rem;
color: #5569ff;
border: 1px solid rgba(85, 105, 255, 0.7);
Expand Down Expand Up @@ -317,3 +317,27 @@ button:disabled[data-reason]:not([data-reason=""]):hover:after {
border-radius: 10px;
z-index: 100;
}

[data-tooltip] svg {
pointer-events: none;
}

#tooltip {
position: absolute;
z-index: 10000;
padding: 8px 16px;
font-size: 14px;
background-color: #2d3c5c;
color: #fff;
border-radius: 0.25em;
white-space: pre;
}

.packages {
list-style: none;
padding: 0;
}

.packages li {
display: inline-block;
}

0 comments on commit e6c7d87

Please sign in to comment.