Skip to content

Commit

Permalink
refactor(devtools): updated ui selector logic (#5898)
Browse files Browse the repository at this point in the history
Co-authored-by: Omer Aplak <omer@refine.dev>
  • Loading branch information
aliemir and omeraplak committed Apr 29, 2024
1 parent 6fdbfad commit 93c35d8
Show file tree
Hide file tree
Showing 10 changed files with 457 additions and 451 deletions.
7 changes: 7 additions & 0 deletions .changeset/lucky-parents-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@refinedev/devtools": minor
---

feat: devtools selector with all selectables

Updated devtools selector to display all available elements instead of relying on the user's pointer to select the element.
19 changes: 19 additions & 0 deletions packages/devtools/src/components/apply-styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

type Props = {
children: string;
};

export const ApplyStyles = ({ children }: Props) => {
React.useEffect(() => {
const element = document.createElement("style");
element.innerHTML = children;
document.head.appendChild(element);

return () => {
document.head.removeChild(element);
};
}, [children]);

return null;
};
63 changes: 37 additions & 26 deletions packages/devtools/src/components/devtools-pin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,25 @@ import React from "react";
import { DevtoolsSelector } from "./devtools-selector";
import { DevtoolsIcon } from "./icons/devtools-icon";
import { SelectorButtonIcon } from "./icons/selector-button";
import { ApplyStyles } from "./apply-styles";

type Props = {
onClick?: () => void;
groupHover?: boolean;
onSelectorHighlight: (name: string) => void;
onSelectorOpen: () => void;
selectorActive: boolean;
setSelectorActive: React.Dispatch<React.SetStateAction<boolean>>;
};

export const DevtoolsPin = ({
onClick,
onSelectorHighlight,
onSelectorOpen,
selectorActive,
setSelectorActive,
}: Props) => {
const [hover, setHover] = React.useState(false);

return (
<div
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
style={{
position: "relative",
userSelect: "none",
WebkitUserSelect: "none",
background: "none",
border: "none",
padding: 0,
paddingRight: "1px",
margin: 0,
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
color: hover ? "#0FBDBD" : "#6C7793",
transition: "color 0.1s ease-in-out",
}}
onClick={onClick}
>
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<div role="button" className="devtools-selector-pin-box" onClick={onClick}>
<DevtoolsIcon />
<DevtoolsSelector
style={{
Expand All @@ -55,9 +37,38 @@ export const DevtoolsPin = ({
style={{ pointerEvents: "none" }}
/>
}
onSelectorOpen={onSelectorOpen}
onHighlight={onSelectorHighlight}
active={selectorActive}
setActive={setSelectorActive}
/>
<ApplyStyles>
{
/* css */ `
.devtools-selector-pin-box {
z-index: 9999;
position: relative;
user-select: none;
-webkit-user-select: none;
background: none;
border: none;
padding: 0;
margin: 0;
appearance: none;
padding-right: 1px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #6C7793;
transition: color 0.1s ease-in-out;
}
.devtools-selector-pin-box:hover {
color: #0FBDBD;
}
`
}
</ApplyStyles>
</div>
);
};
110 changes: 34 additions & 76 deletions packages/devtools/src/components/devtools-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,109 +1,67 @@
import React from "react";
import { createPortal } from "react-dom";
import { useSelector } from "src/utilities/use-selector";
import { SelectorBox } from "./selector-box";
import { SelectorHint } from "./selector-hint";
import { ApplyStyles } from "./apply-styles";
import { SelectableElements } from "./selectable-elements";

type Props = {
onSelectorOpen: () => void;
active: boolean;
setActive: React.Dispatch<React.SetStateAction<boolean>>;
onHighlight: (name: string) => void;
icon?: React.ReactNode;
style?: React.CSSProperties;
};

export const DevtoolsSelector = ({
onSelectorOpen,
active,
setActive,
onHighlight,
icon,
style,
}: Props) => {
const [hover, setHover] = React.useState(false);
const [active, setActive] = React.useState(false);
const { rect, name } = useSelector(active);
const { selectableElements } = useSelector(active);

const [selectorBoxRoot, setSelectorBoxRoot] =
React.useState<HTMLElement | null>(null);

React.useEffect(() => {
if (!selectorBoxRoot) {
const element = document.createElement("div");
element.id = "selector-box-root";

document.body.appendChild(element);

setSelectorBoxRoot(element);
}
}, []);

React.useEffect(() => {
if (active) {
document.body.style.cursor = "crosshair";
} else {
document.body.style.cursor = "default";
}
}, [active]);

React.useEffect(() => {
const onMouseClick = (e: MouseEvent) => {
if (!active) return;
if (!name) return;

e?.preventDefault();
e?.stopPropagation();
e.stopImmediatePropagation();
onHighlight(name);
setActive(false);
};

if (active) {
document.addEventListener("click", onMouseClick, {
capture: true,
});

return () => {
document.removeEventListener("click", onMouseClick, {
capture: true,
});
};
}

return () => 0;
}, [name, onHighlight, active]);

React.useEffect(() => {
if (active) {
onSelectorOpen();
}
}, [active, onSelectorOpen]);
const onSelect = (name: string) => {
onHighlight(name);
setActive(false);
};

return (
<div style={style}>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<div
role="button"
title="Element Selector"
onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)}
className="refine-devtools-selector-button"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
(document?.activeElement as HTMLElement)?.blur();
setActive((active) => !active);
}}
style={{
padding: 0,
margin: 0,
height: "100%",
width: "100%",
transform: hover ? "rotate(180deg)" : "rotate(0deg)",
transition: "transform 0.2s ease-in-out",
}}
>
{icon}
</div>
<SelectorHint active={active} />
{active &&
selectorBoxRoot &&
createPortal(<SelectorBox {...rect} name={name} />, selectorBoxRoot)}
{active && (
<SelectableElements elements={selectableElements} onSelect={onSelect} />
)}
<ApplyStyles>
{
/* css */ `
.refine-devtools-selector-button {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
transform: rotate(0deg);
transition: transform 0.2s ease-in-out;
}
.refine-devtools-selector-button:hover {
transform: rotate(180deg);
}
`
}
</ApplyStyles>
</div>
);
};
18 changes: 18 additions & 0 deletions packages/devtools/src/components/icons/selector-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,21 @@ export const SelectorButtonIcon = (props: React.SVGProps<SVGSVGElement>) => (
/>
</svg>
);

export const SelectorIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
fill="#14141F"
fillRule="evenodd"
d="M9 1a1 1 0 0 0-2 0v2.1A5.006 5.006 0 0 0 3.1 7H1a1 1 0 0 0 0 2h2.1A5.006 5.006 0 0 0 7 12.9V15a1 1 0 1 0 2 0v-2.1A5.006 5.006 0 0 0 12.9 9H15a1 1 0 1 0 0-2h-2.1A5.006 5.006 0 0 0 9 3.1V1Zm2 7a3 3 0 1 0-6 0 3 3 0 0 0 6 0Z"
clipRule="evenodd"
/>
</svg>
);

0 comments on commit 93c35d8

Please sign in to comment.