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:
parent
bd7f913d28
commit
ca8f3cad62
5 changed files with 75 additions and 81 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { convertTestName } from "@/components/common/storeCardComponent/utils/convert-test-name";
|
import { convertTestName } from "@/components/common/storeCardComponent/utils/convert-test-name";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { nodeColorsName } from "@/utils/styleUtils";
|
||||||
|
|
||||||
export default function HandleTooltipComponent({
|
export default function HandleTooltipComponent({
|
||||||
isInput,
|
isInput,
|
||||||
|
|
@ -7,8 +8,6 @@ export default function HandleTooltipComponent({
|
||||||
isConnecting,
|
isConnecting,
|
||||||
isCompatible,
|
isCompatible,
|
||||||
isSameNode,
|
isSameNode,
|
||||||
accentColorName,
|
|
||||||
accentForegroundColorName,
|
|
||||||
left,
|
left,
|
||||||
}: {
|
}: {
|
||||||
isInput: boolean;
|
isInput: boolean;
|
||||||
|
|
@ -16,8 +15,6 @@ export default function HandleTooltipComponent({
|
||||||
isConnecting: boolean;
|
isConnecting: boolean;
|
||||||
isCompatible: boolean;
|
isCompatible: boolean;
|
||||||
isSameNode: boolean;
|
isSameNode: boolean;
|
||||||
accentColorName: string;
|
|
||||||
accentForegroundColorName: string;
|
|
||||||
left: boolean;
|
left: boolean;
|
||||||
}) {
|
}) {
|
||||||
const tooltips = tooltipTitle.split("\n");
|
const tooltips = tooltipTitle.split("\n");
|
||||||
|
|
@ -51,11 +48,11 @@ export default function HandleTooltipComponent({
|
||||||
key={`${index}-${word.toLowerCase()}`}
|
key={`${index}-${word.toLowerCase()}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: left
|
backgroundColor: left
|
||||||
? `hsl(var(--${accentColorName}))`
|
? `hsl(var(--datatype-${nodeColorsName[word]}))`
|
||||||
: `hsl(var(--${accentColorName}-foreground))`,
|
: `hsl(var(--datatype-${nodeColorsName[word]}-foreground))`,
|
||||||
color: left
|
color: left
|
||||||
? `hsl(var(--${accentForegroundColorName}))`
|
? `hsl(var(--datatype-${nodeColorsName[word]}-foreground))`
|
||||||
: `hsl(var(--${accentColorName}))`,
|
: `hsl(var(--datatype-${nodeColorsName[word]}))`,
|
||||||
}}
|
}}
|
||||||
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
|
data-testid={`${isInput ? "input" : "output"}-tooltip-${convertTestName(word)}`}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { useDarkStore } from "@/stores/darkStore";
|
import { useDarkStore } from "@/stores/darkStore";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { nodeColorsName } from "@/utils/styleUtils";
|
||||||
import { Connection, Handle, Position } from "@xyflow/react";
|
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 ShadTooltip from "../../../../components/common/shadTooltipComponent";
|
||||||
import {
|
import {
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
|
|
@ -31,7 +32,6 @@ const HandleContent = memo(function HandleContent({
|
||||||
showNode,
|
showNode,
|
||||||
left,
|
left,
|
||||||
nodeId,
|
nodeId,
|
||||||
colorName,
|
|
||||||
}: {
|
}: {
|
||||||
isNullHandle: boolean;
|
isNullHandle: boolean;
|
||||||
handleColor: string;
|
handleColor: string;
|
||||||
|
|
@ -43,7 +43,6 @@ const HandleContent = memo(function HandleContent({
|
||||||
showNode: boolean;
|
showNode: boolean;
|
||||||
left: boolean;
|
left: boolean;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
colorName?: string[];
|
|
||||||
}) {
|
}) {
|
||||||
// Restore animation effect
|
// Restore animation effect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -51,36 +50,36 @@ const HandleContent = memo(function HandleContent({
|
||||||
const styleSheet = document.createElement("style");
|
const styleSheet = document.createElement("style");
|
||||||
styleSheet.id = `pulse-${nodeId}`;
|
styleSheet.id = `pulse-${nodeId}`;
|
||||||
styleSheet.textContent = `
|
styleSheet.textContent = `
|
||||||
@keyframes pulseNeon {
|
@keyframes pulseNeon-${nodeId} {
|
||||||
0% {
|
0% {
|
||||||
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
||||||
0 0 2px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 2px ${handleColor},
|
||||||
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 4px ${handleColor},
|
||||||
0 0 6px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 6px ${handleColor},
|
||||||
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 8px ${handleColor},
|
||||||
0 0 10px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 10px ${handleColor},
|
||||||
0 0 15px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 15px ${handleColor},
|
||||||
0 0 20px hsl(var(--datatype-${colorName?.[0]}));
|
0 0 20px ${handleColor};
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
||||||
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 4px ${handleColor},
|
||||||
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 8px ${handleColor},
|
||||||
0 0 12px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 12px ${handleColor},
|
||||||
0 0 16px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 16px ${handleColor},
|
||||||
0 0 20px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 20px ${handleColor},
|
||||||
0 0 25px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 25px ${handleColor},
|
||||||
0 0 30px hsl(var(--datatype-${colorName?.[0]}));
|
0 0 30px ${handleColor};
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
box-shadow: 0 0 0 2px hsl(var(--node-ring)),
|
||||||
0 0 2px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 2px ${handleColor},
|
||||||
0 0 4px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 4px ${handleColor},
|
||||||
0 0 6px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 6px ${handleColor},
|
||||||
0 0 8px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 8px ${handleColor},
|
||||||
0 0 10px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 10px ${handleColor},
|
||||||
0 0 15px hsl(var(--datatype-${colorName?.[0]})),
|
0 0 15px ${handleColor},
|
||||||
0 0 20px hsl(var(--datatype-${colorName?.[0]}));
|
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(
|
const getNeonShadow = useCallback(
|
||||||
(color: string, isActive: boolean) => {
|
(color: string, isActive: boolean) => {
|
||||||
if (isNullHandle) return "none";
|
if (isNullHandle) return "none";
|
||||||
if (!isActive) return `0 0 0 3px hsl(var(--${color}))`;
|
if (!isActive) return `0 0 0 3px ${color}`;
|
||||||
return [
|
return [
|
||||||
"0 0 0 1px hsl(var(--border))",
|
"0 0 0 1px hsl(var(--border))",
|
||||||
`0 0 2px ${color}`,
|
`0 0 2px ${color}`,
|
||||||
|
|
@ -125,7 +124,7 @@ const HandleContent = memo(function HandleContent({
|
||||||
),
|
),
|
||||||
animation:
|
animation:
|
||||||
(isHovered || openHandle) && !isNullHandle
|
(isHovered || openHandle) && !isNullHandle
|
||||||
? "pulseNeon 1.1s ease-in-out infinite"
|
? `pulseNeon-${nodeId} 1.1s ease-in-out infinite`
|
||||||
: "none",
|
: "none",
|
||||||
border: isNullHandle ? "2px solid hsl(var(--muted))" : "none",
|
border: isNullHandle ? "2px solid hsl(var(--muted))" : "none",
|
||||||
}),
|
}),
|
||||||
|
|
@ -181,10 +180,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
colorName?: string[];
|
colorName?: string[];
|
||||||
}) {
|
}) {
|
||||||
const handleColorName = colorName?.[0] ?? "";
|
|
||||||
const accentColorName = `datatype-${handleColorName}`;
|
|
||||||
const accentForegroundColorName = `${accentColorName}-foreground`;
|
|
||||||
|
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const [openTooltip, setOpenTooltip] = useState(false);
|
const [openTooltip, setOpenTooltip] = useState(false);
|
||||||
|
|
||||||
|
|
@ -238,6 +233,7 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
currentFilter,
|
currentFilter,
|
||||||
isNullHandle,
|
isNullHandle,
|
||||||
handleColor,
|
handleColor,
|
||||||
|
accentForegroundColorName,
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
const sameDraggingNode =
|
const sameDraggingNode =
|
||||||
(!left ? handleDragging?.target : handleDragging?.source) === nodeId;
|
(!left ? handleDragging?.target : handleDragging?.source) === nodeId;
|
||||||
|
|
@ -272,6 +268,38 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
const openHandle = filterOpenHandle || draggingOpenHandle;
|
const openHandle = filterOpenHandle || draggingOpenHandle;
|
||||||
const filterPresent = handleDragging || filterType;
|
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
|
const currentFilter = left
|
||||||
? {
|
? {
|
||||||
targetHandle: myId,
|
targetHandle: myId,
|
||||||
|
|
@ -290,31 +318,10 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
color: handleColorName,
|
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 {
|
return {
|
||||||
sameNode: sameDraggingNode || sameFilterNode,
|
sameNode: sameDraggingNode || sameFilterNode,
|
||||||
ownHandle: ownDraggingHandle || ownFilterHandle,
|
ownHandle: ownDraggingHandle || ownFilterHandle,
|
||||||
|
accentForegroundColorName,
|
||||||
openHandle,
|
openHandle,
|
||||||
filterOpenHandle,
|
filterOpenHandle,
|
||||||
filterPresent,
|
filterPresent,
|
||||||
|
|
@ -335,7 +342,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
colors,
|
colors,
|
||||||
colorName,
|
colorName,
|
||||||
tooltipTitle,
|
tooltipTitle,
|
||||||
handleColorName,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleMouseDown = useCallback(
|
const handleMouseDown = useCallback(
|
||||||
|
|
@ -402,8 +408,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
isConnecting={!!filterPresent && !ownHandle}
|
isConnecting={!!filterPresent && !ownHandle}
|
||||||
isCompatible={openHandle}
|
isCompatible={openHandle}
|
||||||
isSameNode={sameNode && !ownHandle}
|
isSameNode={sameNode && !ownHandle}
|
||||||
accentColorName={accentColorName}
|
|
||||||
accentForegroundColorName={accentForegroundColorName}
|
|
||||||
left={left}
|
left={left}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
@ -442,7 +446,6 @@ const HandleRenderComponent = memo(function HandleRenderComponent({
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
left={left}
|
left={left}
|
||||||
nodeId={nodeId}
|
nodeId={nodeId}
|
||||||
colorName={colorName}
|
|
||||||
/>
|
/>
|
||||||
</Handle>
|
</Handle>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
|
||||||
|
|
||||||
const handleEdgeClick = (event, edge) => {
|
const handleEdgeClick = (event, edge) => {
|
||||||
const color =
|
const color =
|
||||||
nodeColorsName[edge?.data?.targetHandle?.inputTypes[0]] || "cyan";
|
nodeColorsName[edge?.data?.sourceHandle?.output_types[0]] || "cyan";
|
||||||
|
|
||||||
const accentColor = `hsl(var(--datatype-${color}))`;
|
const accentColor = `hsl(var(--datatype-${color}))`;
|
||||||
reactFlowWrapper.current?.style.setProperty("--selected", accentColor);
|
reactFlowWrapper.current?.style.setProperty("--selected", accentColor);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ export function SidebarFilterComponent({
|
||||||
color: string;
|
color: string;
|
||||||
resetFilters: () => void;
|
resetFilters: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
const tooltips = type.split("\n");
|
||||||
|
const plural = tooltips.length > 1 ? "s" : "";
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`mb-0.5 flex w-full items-center justify-between rounded border p-2 text-sm text-foreground`}
|
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`}
|
className={`h-4 w-4 shrink-0 stroke-2`}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1">
|
||||||
{isInput ? "Input" : "Output"}:{" "}
|
{isInput ? "Input" : "Output"}
|
||||||
|
{plural}:{" "}
|
||||||
<div className="w-[5.7rem] flex-1 overflow-hidden truncate pl-1">
|
<div className="w-[5.7rem] flex-1 overflow-hidden truncate pl-1">
|
||||||
{type}
|
{tooltips.join(", ")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -113,19 +113,13 @@ test(
|
||||||
|
|
||||||
const hasGradientUnlocked = await unlockedHandle?.evaluate((el) => {
|
const hasGradientUnlocked = await unlockedHandle?.evaluate((el) => {
|
||||||
const style = window.getComputedStyle(el);
|
const style = window.getComputedStyle(el);
|
||||||
return (
|
return style.backgroundColor === "rgb(79, 70, 229)";
|
||||||
style.backgroundImage.includes("conic-gradient") &&
|
|
||||||
style.backgroundImage.includes("rgb(79, 70, 229)")
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const secondHasGradientUnlocked = await secondUnlockedHandle?.evaluate(
|
const secondHasGradientUnlocked = await secondUnlockedHandle?.evaluate(
|
||||||
(el) => {
|
(el) => {
|
||||||
const style = window.getComputedStyle(el);
|
const style = window.getComputedStyle(el);
|
||||||
return (
|
return style.backgroundColor === "rgb(79, 70, 229)";
|
||||||
style.backgroundImage.includes("conic-gradient") &&
|
|
||||||
style.backgroundImage.includes("rgb(79, 70, 229)")
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -137,10 +131,7 @@ test(
|
||||||
const fourthHasGradientUnlocked = await fourthUnlockedHandle?.evaluate(
|
const fourthHasGradientUnlocked = await fourthUnlockedHandle?.evaluate(
|
||||||
(el) => {
|
(el) => {
|
||||||
const style = window.getComputedStyle(el);
|
const style = window.getComputedStyle(el);
|
||||||
return (
|
return style.backgroundColor === "rgb(79, 70, 229)";
|
||||||
style.backgroundImage.includes("conic-gradient") &&
|
|
||||||
style.backgroundImage.includes("rgb(79, 70, 229)")
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue