Filter Menu Components on Click Edge (#932)
This branch implements the edge filtering feature.
This commit is contained in:
commit
795bf73752
6 changed files with 116 additions and 30 deletions
|
|
@ -21,6 +21,7 @@ import KeypairListComponent from "../../../../components/keypairListComponent";
|
||||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||||
|
import { Button } from "../../../../components/ui/button";
|
||||||
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
||||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||||
import { typesContext } from "../../../../contexts/typesContext";
|
import { typesContext } from "../../../../contexts/typesContext";
|
||||||
|
|
@ -75,7 +76,9 @@ export default function ParameterComponent({
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
}, [data.id, position, updateNodeInternals]);
|
}, [data.id, position, updateNodeInternals]);
|
||||||
|
|
||||||
const { reactFlowInstance } = useContext(typesContext);
|
const groupedEdge = useRef(null);
|
||||||
|
|
||||||
|
const { reactFlowInstance, setFilterEdge } = useContext(typesContext);
|
||||||
let disabled =
|
let disabled =
|
||||||
reactFlowInstance?.getEdges().some((edge) => edge.targetHandle === id) ??
|
reactFlowInstance?.getEdges().some((edge) => edge.targetHandle === id) ??
|
||||||
false;
|
false;
|
||||||
|
|
@ -123,10 +126,10 @@ export default function ParameterComponent({
|
||||||
}, [info]);
|
}, [info]);
|
||||||
|
|
||||||
function renderTooltips() {
|
function renderTooltips() {
|
||||||
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
|
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, flow!);
|
||||||
|
groupedEdge.current = groupedObj;
|
||||||
|
|
||||||
if (groupedObj && groupedObj.length > 0) {
|
if (groupedObj && groupedObj.length > 0) {
|
||||||
//@ts-ignore
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
refHtml.current = groupedObj.map((item, index) => {
|
refHtml.current = groupedObj.map((item, index) => {
|
||||||
const Icon: any =
|
const Icon: any =
|
||||||
|
|
@ -271,29 +274,36 @@ export default function ParameterComponent({
|
||||||
!optionalHandle ? (
|
!optionalHandle ? (
|
||||||
<></>
|
<></>
|
||||||
) : (
|
) : (
|
||||||
<ShadTooltip
|
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
|
||||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
<div className="flex">
|
||||||
delayDuration={0}
|
<ShadTooltip
|
||||||
content={refHtml.current}
|
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
||||||
side={left ? "left" : "right"}
|
delayDuration={0}
|
||||||
>
|
content={refHtml.current}
|
||||||
<Handle
|
side={left ? "left" : "right"}
|
||||||
type={left ? "target" : "source"}
|
>
|
||||||
position={left ? Position.Left : Position.Right}
|
<Handle
|
||||||
id={id}
|
type={left ? "target" : "source"}
|
||||||
isValidConnection={(connection) =>
|
position={left ? Position.Left : Position.Right}
|
||||||
isValidConnection(connection, reactFlowInstance!)
|
id={id}
|
||||||
}
|
isValidConnection={(connection) =>
|
||||||
className={classNames(
|
isValidConnection(connection, reactFlowInstance!)
|
||||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
}
|
||||||
"h-3 w-3 rounded-full border-2 bg-background"
|
className={classNames(
|
||||||
)}
|
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||||
style={{
|
"h-3 w-3 rounded-full border-2 bg-background"
|
||||||
borderColor: color,
|
)}
|
||||||
top: position,
|
style={{
|
||||||
}}
|
borderColor: color,
|
||||||
></Handle>
|
top: position,
|
||||||
</ShadTooltip>
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setFilterEdge(groupedEdge.current);
|
||||||
|
}}
|
||||||
|
></Handle>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{left === true &&
|
{left === true &&
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ export default function GenericNode({
|
||||||
const [data, setData] = useState(olddata);
|
const [data, setData] = useState(olddata);
|
||||||
const { updateFlow, flows, tabId } = useContext(TabsContext);
|
const { updateFlow, flows, tabId } = useContext(TabsContext);
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
const { types, deleteNode, reactFlowInstance } = useContext(typesContext);
|
const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } =
|
||||||
|
useContext(typesContext);
|
||||||
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
||||||
const [validationStatus, setValidationStatus] =
|
const [validationStatus, setValidationStatus] =
|
||||||
useState<validationStatusType | null>(null);
|
useState<validationStatusType | null>(null);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ const initialValue: typesContextType = {
|
||||||
setData: () => {},
|
setData: () => {},
|
||||||
setFetchError: () => {},
|
setFetchError: () => {},
|
||||||
fetchError: false,
|
fetchError: false,
|
||||||
|
setFilterEdge: (filter) => {},
|
||||||
|
getFilterEdge: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const typesContext = createContext<typesContextType>(initialValue);
|
export const typesContext = createContext<typesContextType>(initialValue);
|
||||||
|
|
@ -39,6 +41,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
||||||
const [fetchError, setFetchError] = useState(false);
|
const [fetchError, setFetchError] = useState(false);
|
||||||
const { setLoading } = useContext(alertContext);
|
const { setLoading } = useContext(alertContext);
|
||||||
const { getAuthentication } = useContext(AuthContext);
|
const { getAuthentication } = useContext(AuthContext);
|
||||||
|
const [getFilterEdge, setFilterEdge] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If the user is authenticated, fetch the types. This code is important to check if the user is auth because of the execution order of the useEffect hooks.
|
// If the user is authenticated, fetch the types. This code is important to check if the user is auth because of the execution order of the useEffect hooks.
|
||||||
|
|
@ -113,6 +116,8 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
||||||
setData,
|
setData,
|
||||||
fetchError,
|
fetchError,
|
||||||
setFetchError,
|
setFetchError,
|
||||||
|
setFilterEdge,
|
||||||
|
getFilterEdge,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,13 @@ export default function Page({
|
||||||
setTabsState,
|
setTabsState,
|
||||||
tabId,
|
tabId,
|
||||||
} = useContext(TabsContext);
|
} = useContext(TabsContext);
|
||||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
const {
|
||||||
useContext(typesContext);
|
types,
|
||||||
|
reactFlowInstance,
|
||||||
|
setReactFlowInstance,
|
||||||
|
templates,
|
||||||
|
setFilterEdge,
|
||||||
|
} = useContext(typesContext);
|
||||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { takeSnapshot } = useContext(undoRedoContext);
|
const { takeSnapshot } = useContext(undoRedoContext);
|
||||||
|
|
@ -382,6 +387,10 @@ export default function Page({
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onPaneClick = useCallback((flow) => {
|
||||||
|
setFilterEdge([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full overflow-hidden">
|
<div className="flex h-full overflow-hidden">
|
||||||
{!view && <ExtraSidebar />}
|
{!view && <ExtraSidebar />}
|
||||||
|
|
@ -429,6 +438,7 @@ export default function Page({
|
||||||
zoomOnPinch={!view}
|
zoomOnPinch={!view}
|
||||||
panOnDrag={!view}
|
panOnDrag={!view}
|
||||||
proOptions={{ hideAttribution: true }}
|
proOptions={{ hideAttribution: true }}
|
||||||
|
onPaneClick={onPaneClick}
|
||||||
>
|
>
|
||||||
<Background className="" />
|
<Background className="" />
|
||||||
{!view && (
|
{!view && (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent from "../../../../components/genericIconComponent";
|
||||||
|
|
@ -18,7 +19,8 @@ import { classNames } from "../../../../utils/utils";
|
||||||
import DisclosureComponent from "../DisclosureComponent";
|
import DisclosureComponent from "../DisclosureComponent";
|
||||||
|
|
||||||
export default function ExtraSidebar(): JSX.Element {
|
export default function ExtraSidebar(): JSX.Element {
|
||||||
const { data, templates } = useContext(typesContext);
|
const { data, templates, getFilterEdge, setFilterEdge } =
|
||||||
|
useContext(typesContext);
|
||||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||||
useContext(TabsContext);
|
useContext(TabsContext);
|
||||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||||
|
|
@ -42,6 +44,10 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
|
|
||||||
// Handle showing components after use search input
|
// Handle showing components after use search input
|
||||||
function handleSearchInput(e: string) {
|
function handleSearchInput(e: string) {
|
||||||
|
if (e === "") {
|
||||||
|
setFilterData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setFilterData((_) => {
|
setFilterData((_) => {
|
||||||
let ret = {};
|
let ret = {};
|
||||||
Object.keys(data).forEach((d: keyof APIObjectType, i) => {
|
Object.keys(data).forEach((d: keyof APIObjectType, i) => {
|
||||||
|
|
@ -69,6 +75,57 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
setErrorData({ title: " Components with errors: ", list: errors });
|
setErrorData({ title: " Components with errors: ", list: errors });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function handleBlur() {
|
||||||
|
setFilterData(data);
|
||||||
|
setFilterEdge([]);
|
||||||
|
setSearch("");
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (getFilterEdge.length === 0 && search === "") {
|
||||||
|
setFilterData(data);
|
||||||
|
setFilterEdge([]);
|
||||||
|
setSearch("");
|
||||||
|
}
|
||||||
|
}, [getFilterEdge]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (getFilterEdge?.length > 0) {
|
||||||
|
setFilterData((_) => {
|
||||||
|
let dataClone = cloneDeep(data);
|
||||||
|
let ret = {};
|
||||||
|
Object.keys(dataClone).forEach((d: keyof APIObjectType, i) => {
|
||||||
|
ret[d] = {};
|
||||||
|
if (getFilterEdge.some((x) => x.family === d)) {
|
||||||
|
ret[d] = dataClone[d];
|
||||||
|
|
||||||
|
const filtered = getFilterEdge
|
||||||
|
.filter((x) => x.family === d)
|
||||||
|
.pop()
|
||||||
|
.type.split(",");
|
||||||
|
|
||||||
|
for (let i = 0; i < filtered.length; i++) {
|
||||||
|
filtered[i] = filtered[i].trimStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtered.some((x) => x !== "")) {
|
||||||
|
let keys = Object.keys(dataClone[d]).filter((nd) =>
|
||||||
|
filtered.includes(nd)
|
||||||
|
);
|
||||||
|
Object.keys(dataClone[d]).forEach((element) => {
|
||||||
|
if (!keys.includes(element)) {
|
||||||
|
delete ret[d][element];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSearch("search");
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [getFilterEdge]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="side-bar-arrangement">
|
<div className="side-bar-arrangement">
|
||||||
<div className="side-bar-buttons-arrangement">
|
<div className="side-bar-buttons-arrangement">
|
||||||
|
|
@ -141,6 +198,7 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
<Separator />
|
<Separator />
|
||||||
<div className="side-bar-search-div-placement">
|
<div className="side-bar-search-div-placement">
|
||||||
<Input
|
<Input
|
||||||
|
onFocusCapture={() => handleBlur()}
|
||||||
type="text"
|
type="text"
|
||||||
name="search"
|
name="search"
|
||||||
id="search"
|
id="search"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ export type typesContextType = {
|
||||||
setData: (newState: {}) => void;
|
setData: (newState: {}) => void;
|
||||||
fetchError: boolean;
|
fetchError: boolean;
|
||||||
setFetchError: (newState: boolean) => void;
|
setFetchError: (newState: boolean) => void;
|
||||||
|
setFilterEdge: (newState) => void;
|
||||||
|
getFilterEdge: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type alertContextType = {
|
export type alertContextType = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue