feat: add generic handle that inherits colors for multiple types (#5414)

* Fixed HandleTooltipComponent to use the type instead of passed color

* Fixed handle colors to use the connected edge color

* Changed color of generic handle

* Fixed edge color when clicking for generic handles

* Updated sidebar filter to contain more than one type

* fixed gradient generalBugs
This commit is contained in:
Lucas Oliveira 2024-12-23 19:18:11 -03:00 committed by GitHub
commit ca8f3cad62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 75 additions and 81 deletions

View file

@ -1,5 +1,6 @@
import { convertTestName } from "@/components/common/storeCardComponent/utils/convert-test-name";
import { Badge } from "@/components/ui/badge";
import { nodeColorsName } from "@/utils/styleUtils";
export default function HandleTooltipComponent({
isInput,
@ -7,8 +8,6 @@ export default function HandleTooltipComponent({
isConnecting,
isCompatible,
isSameNode,
accentColorName,
accentForegroundColorName,
left,
}: {
isInput: boolean;
@ -16,8 +15,6 @@ export default function HandleTooltipComponent({
isConnecting: boolean;
isCompatible: boolean;
isSameNode: boolean;
accentColorName: string;
accentForegroundColorName: string;
left: boolean;
}) {
const tooltips = tooltipTitle.split("\n");
@ -51,11 +48,11 @@ export default function HandleTooltipComponent({
key={`${index}-${word.toLowerCase()}`}
style={{
backgroundColor: left
? `hsl(var(--${accentColorName}))`
: `hsl(var(--${accentColorName}-foreground))`,
? `hsl(var(--datatype-${nodeColorsName[word]}))`
: `hsl(var(--datatype-${nodeColorsName[word]}-foreground))`,
color: left
? `hsl(var(--${accentForegroundColorName}))`
: `hsl(var(--${accentColorName}))`,
? `hsl(var(--datatype-${nodeColorsName[word]}-foreground))`
: `hsl(var(--datatype-${nodeColorsName[word]}))`,
}}
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
>

View file

@ -1,7 +1,8 @@
import { useDarkStore } from "@/stores/darkStore";
import useFlowStore from "@/stores/flowStore";
import { nodeColorsName } from "@/utils/styleUtils";
import { Connection, Handle, Position } from "@xyflow/react";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { memo, useCallback, useEffect, useMemo, useState } from "react";
import ShadTooltip from "../../../../components/common/shadTooltipComponent";
import {
isValidConnection,
@ -31,7 +32,6 @@ const HandleContent = memo(function HandleContent({
showNode,
left,
nodeId,
colorName,
}: {
isNullHandle: boolean;
handleColor: string;
@ -43,7 +43,6 @@ const HandleContent = memo(function HandleContent({
showNode: boolean;
left: boolean;
nodeId: string;
colorName?: string[];
}) {
// Restore animation effect
useEffect(() => {
@ -51,36 +50,36 @@ const HandleContent = memo(function HandleContent({
const styleSheet = document.createElement("style");
styleSheet.id = `pulse-${nodeId}`;
styleSheet.textContent = `
@keyframes pulseNeon {
@keyframes pulseNeon-${nodeId} {
0% {
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
0 0 2px hsl(var(--datatype-${colorName?.[0]})),
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
0 0 6px hsl(var(--datatype-${colorName?.[0]})),
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
0 0 10px hsl(var(--datatype-${colorName?.[0]})),
0 0 15px hsl(var(--datatype-${colorName?.[0]})),
0 0 20px hsl(var(--datatype-${colorName?.[0]}));
0 0 2px ${handleColor},
0 0 4px ${handleColor},
0 0 6px ${handleColor},
0 0 8px ${handleColor},
0 0 10px ${handleColor},
0 0 15px ${handleColor},
0 0 20px ${handleColor};
}
50% {
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
0 0 12px hsl(var(--datatype-${colorName?.[0]})),
0 0 16px hsl(var(--datatype-${colorName?.[0]})),
0 0 20px hsl(var(--datatype-${colorName?.[0]})),
0 0 25px hsl(var(--datatype-${colorName?.[0]})),
0 0 30px hsl(var(--datatype-${colorName?.[0]}));
0 0 4px ${handleColor},
0 0 8px ${handleColor},
0 0 12px ${handleColor},
0 0 16px ${handleColor},
0 0 20px ${handleColor},
0 0 25px ${handleColor},
0 0 30px ${handleColor};
}
100% {
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
0 0 2px hsl(var(--datatype-${colorName?.[0]})),
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
0 0 6px hsl(var(--datatype-${colorName?.[0]})),
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
0 0 10px hsl(var(--datatype-${colorName?.[0]})),
0 0 15px hsl(var(--datatype-${colorName?.[0]})),
0 0 20px hsl(var(--datatype-${colorName?.[0]}));
0 0 2px ${handleColor},
0 0 4px ${handleColor},
0 0 6px ${handleColor},
0 0 8px ${handleColor},
0 0 10px ${handleColor},
0 0 15px ${handleColor},
0 0 20px ${handleColor};
}
}
`;
@ -93,12 +92,12 @@ const HandleContent = memo(function HandleContent({
}
};
}
}, [isHovered, openHandle, isNullHandle, nodeId, colorName]);
}, [isHovered, openHandle, isNullHandle, nodeId, handleColor]);
const getNeonShadow = useCallback(
(color: string, isActive: boolean) => {
if (isNullHandle) return "none";
if (!isActive) return `0 0 0 3px hsl(var(--${color}))`;
if (!isActive) return `0 0 0 3px ${color}`;
return [
"0 0 0 1px hsl(var(--border))",
`0 0 2px ${color}`,
@ -125,7 +124,7 @@ const HandleContent = memo(function HandleContent({
),
animation:
(isHovered || openHandle) && !isNullHandle
? "pulseNeon 1.1s ease-in-out infinite"
? `pulseNeon-${nodeId} 1.1s ease-in-out infinite`
: "none",
border: isNullHandle ? "2px solid hsl(var(--muted))" : "none",
}),
@ -181,10 +180,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
nodeId: string;
colorName?: string[];
}) {
const handleColorName = colorName?.[0] ?? "";
const accentColorName = `datatype-${handleColorName}`;
const accentForegroundColorName = `${accentColorName}-foreground`;
const [isHovered, setIsHovered] = useState(false);
const [openTooltip, setOpenTooltip] = useState(false);
@ -238,6 +233,7 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
currentFilter,
isNullHandle,
handleColor,
accentForegroundColorName,
} = useMemo(() => {
const sameDraggingNode =
(!left ? handleDragging?.target : handleDragging?.source) === nodeId;
@ -272,6 +268,38 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
const openHandle = filterOpenHandle || draggingOpenHandle;
const filterPresent = handleDragging || filterType;
const connectedEdge = edges.find(
(edge) => edge.target === nodeId && edge.targetHandle === myId,
);
const connectedColor =
nodeColorsName[connectedEdge?.data?.sourceHandle?.output_types[0]] ||
"gray";
const isNullHandle =
filterPresent && !(openHandle || ownDraggingHandle || ownFilterHandle);
const handleColorName = connectedEdge
? connectedColor
: colorName!.length > 1
? "secondary-foreground"
: "datatype-" + colorName![0];
const handleColor = isNullHandle
? dark
? "hsl(var(--accent-gray))"
: "hsl(var(--accent-gray-foreground)"
: connectedEdge
? "hsl(var(--datatype-" + connectedColor + "))"
: colorName!.length > 1
? "hsl(var(--secondary-foreground))"
: "hsl(var(--datatype-" + colorName![0] + "))";
const accentForegroundColorName = connectedEdge
? "hsl(var(--datatype-" + connectedColor + "-foreground))"
: colorName!.length > 1
? "hsl(var(--input))"
: "hsl(var(--datatype-" + colorName![0] + "-foreground))";
const currentFilter = left
? {
targetHandle: myId,
@ -290,31 +318,10 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
color: handleColorName,
};
const isNullHandle =
filterPresent && !(openHandle || ownDraggingHandle || ownFilterHandle);
const handleColor = isNullHandle
? dark
? "conic-gradient(hsl(var(--accent-gray)) 0deg 360deg)"
: "conic-gradient(hsl(var(--accent-gray-foreground)) 0deg 360deg)"
: "conic-gradient(" +
colorName!
.concat(colorName![0])
.map(
(color, index) =>
`hsl(var(--datatype-${color}))` +
" " +
((360 / colors.length) * index - 360 / (colors.length * 4)) +
"deg " +
((360 / colors.length) * index + 360 / (colors.length * 4)) +
"deg",
)
.join(" ,") +
")";
return {
sameNode: sameDraggingNode || sameFilterNode,
ownHandle: ownDraggingHandle || ownFilterHandle,
accentForegroundColorName,
openHandle,
filterOpenHandle,
filterPresent,
@ -335,7 +342,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
colors,
colorName,
tooltipTitle,
handleColorName,
]);
const handleMouseDown = useCallback(
@ -402,8 +408,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
isConnecting={!!filterPresent && !ownHandle}
isCompatible={openHandle}
isSameNode={sameNode && !ownHandle}
accentColorName={accentColorName}
accentForegroundColorName={accentForegroundColorName}
left={left}
/>
}
@ -442,7 +446,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
showNode={showNode}
left={left}
nodeId={nodeId}
colorName={colorName}
/>
</Handle>
</ShadTooltip>

View file

@ -493,7 +493,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
const handleEdgeClick = (event, edge) => {
const color =
nodeColorsName[edge?.data?.targetHandle?.inputTypes[0]] || "cyan";
nodeColorsName[edge?.data?.sourceHandle?.output_types[0]] || "cyan";
const accentColor = `hsl(var(--datatype-${color}))`;
reactFlowWrapper.current?.style.setProperty("--selected", accentColor);

View file

@ -13,6 +13,8 @@ export function SidebarFilterComponent({
color: string;
resetFilters: () => void;
}) {
const tooltips = type.split("\n");
const plural = tooltips.length > 1 ? "s" : "";
return (
<div
className={`mb-0.5 flex w-full items-center justify-between rounded border p-2 text-sm text-foreground`}
@ -26,9 +28,10 @@ export function SidebarFilterComponent({
className={`h-4 w-4 shrink-0 stroke-2`}
/>
<div className="flex flex-1">
{isInput ? "Input" : "Output"}:{" "}
{isInput ? "Input" : "Output"}
{plural}:{" "}
<div className="w-[5.7rem] flex-1 overflow-hidden truncate pl-1">
{type}
{tooltips.join(", ")}
</div>
</div>
</div>

View file

@ -113,19 +113,13 @@ test(
const hasGradientUnlocked = await unlockedHandle?.evaluate((el) => {
const style = window.getComputedStyle(el);
return (
style.backgroundImage.includes("conic-gradient") &&
style.backgroundImage.includes("rgb(79, 70, 229)")
);
return style.backgroundColor === "rgb(79, 70, 229)";
});
const secondHasGradientUnlocked = await secondUnlockedHandle?.evaluate(
(el) => {
const style = window.getComputedStyle(el);
return (
style.backgroundImage.includes("conic-gradient") &&
style.backgroundImage.includes("rgb(79, 70, 229)")
);
return style.backgroundColor === "rgb(79, 70, 229)";
},
);
@ -137,10 +131,7 @@ test(
const fourthHasGradientUnlocked = await fourthUnlockedHandle?.evaluate(
(el) => {
const style = window.getComputedStyle(el);
return (
style.backgroundImage.includes("conic-gradient") &&
style.backgroundImage.includes("rgb(79, 70, 229)")
);
return style.backgroundColor === "rgb(79, 70, 229)";
},
);