Add design for selected filter (#13498)

On select, filter design should be updated as it is for other nodes

<img width="106" height="70" alt="Capture d’écran 2025-07-30 à 12 12
09"
src="https://github.com/user-attachments/assets/66aa7838-2fb3-4593-af32-a9b361eb3225"
/>
<img width="106" height="70" alt="Capture d’écran 2025-07-30 à 12 12
25"
src="https://github.com/user-attachments/assets/5627427b-4c36-4592-b5bb-e5d87d59232d"
/>
This commit is contained in:
Thomas Trompette
2025-07-30 15:24:01 +02:00
committed by GitHub
parent 29a4f4d685
commit d4011edbbf
7 changed files with 131 additions and 38 deletions
@@ -10,6 +10,7 @@ import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightD
import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState';
import { workflowDiagramWaitingNodesDimensionsComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramWaitingNodesDimensionsComponentState';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import {
WorkflowDiagram,
WorkflowDiagramEdge,
@@ -145,6 +146,9 @@ export const WorkflowDiagramCanvasBase = ({
const setWorkflowInsertStepIds = useSetRecoilComponentStateV2(
workflowInsertStepIdsComponentState,
);
const setWorkflowSelectedNode = useSetRecoilComponentStateV2(
workflowSelectedNodeComponentState,
);
const workflowDiagramWaitingNodesDimensionsState =
useRecoilComponentCallbackStateV2(
workflowDiagramWaitingNodesDimensionsComponentState,
@@ -192,6 +196,7 @@ export const WorkflowDiagramCanvasBase = ({
parentStepId: undefined,
nextStepId: undefined,
});
setWorkflowSelectedNode(undefined);
});
const containerRef = useRef<HTMLDivElement>(null);
@@ -16,13 +16,15 @@ import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workf
import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu';
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import {
WorkflowDiagramEdge,
WorkflowDiagramEdgeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors';
import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep';
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
import { useTheme } from '@emotion/react';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import {
@@ -54,8 +56,17 @@ const assertFilterEdgeDataOrThrow: (
}
};
const StyledIconButtonGroup = styled(IconButtonGroup)`
const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>`
pointer-events: all;
${({ selected, theme }) => {
if (!selected) return '';
const colors = getWorkflowDiagramNodeSelectedColors('default', theme);
return css`
background-color: ${colors.background};
border: 1px solid ${colors.borderColor};
`;
}}
`;
const StyledConfiguredFilterContainer = styled.div`
@@ -104,11 +115,18 @@ export const WorkflowDiagramFilterEdgeEditable = ({
workflowInsertStepIdsComponentState,
);
const isSelected =
workflowInsertStepIds.nextStepId === source &&
(workflowInsertStepIds.parentStepId === target ||
(isNonEmptyString(data.stepId) &&
workflowInsertStepIds.parentStepId === data.stepId));
const isEdgeSelected =
(workflowInsertStepIds.nextStepId === source &&
workflowInsertStepIds.parentStepId === target) ||
(isNonEmptyString(data.stepId) &&
workflowInsertStepIds.parentStepId === data.stepId);
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
const isFilterNodeSelected =
isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
const dropdownId = `${WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID}-${source}-${target}`;
@@ -154,7 +172,7 @@ export const WorkflowDiagramFilterEdgeEditable = ({
>
<WorkflowDiagramEdgeV2VisibilityContainer shouldDisplay>
<StyledConfiguredFilterContainer>
{hovered || isDropdownOpen || isSelected ? (
{hovered || isDropdownOpen || isEdgeSelected ? (
<StyledIconButtonGroup
className="nodrag nopan"
iconButtons={[
@@ -171,6 +189,7 @@ export const WorkflowDiagramFilterEdgeEditable = ({
},
},
]}
selected={isFilterNodeSelected}
/>
) : (
<StyledIconButtonGroup
@@ -181,6 +200,7 @@ export const WorkflowDiagramFilterEdgeEditable = ({
onClick: handleFilterButtonClick,
},
]}
selected={isFilterNodeSelected}
/>
)}
</StyledConfiguredFilterContainer>
@@ -1,14 +1,18 @@
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container';
import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer';
import { CREATE_STEP_NODE_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth';
import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId';
import { useOpenWorkflowViewFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import {
WorkflowDiagramEdge,
WorkflowDiagramEdgeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { useTheme } from '@emotion/react';
import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import {
BaseEdge,
EdgeLabelRenderer,
@@ -30,8 +34,17 @@ const assertFilterEdgeDataOrThrow: (
}
};
const StyledIconButtonGroup = styled(IconButtonGroup)`
const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>`
pointer-events: all;
${({ selected, theme }) => {
if (!selected) return '';
const colors = getWorkflowDiagramNodeSelectedColors('default', theme);
return css`
background-color: ${colors.background};
border: 1px solid ${colors.borderColor};
`;
}}
`;
const StyledConfiguredFilterContainer = styled.div`
@@ -57,6 +70,13 @@ export const WorkflowDiagramFilterEdgeReadonly = ({
targetY,
});
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
const isFilterNodeSelected =
isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
const { openWorkflowViewFilterInCommandMenu } =
useOpenWorkflowViewFilterInCommandMenu();
@@ -92,6 +112,7 @@ export const WorkflowDiagramFilterEdgeReadonly = ({
onClick: handleFilterButtonClick,
},
]}
selected={isFilterNodeSelected}
/>
</StyledConfiguredFilterContainer>
</WorkflowDiagramEdgeV2VisibilityContainer>
@@ -1,14 +1,21 @@
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container';
import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer';
import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge';
import { CREATE_STEP_NODE_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth';
import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId';
import { useOpenWorkflowRunFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
import {
WorkflowDiagramEdge,
WorkflowDiagramEdgeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { getNodeVariantFromStepRunStatus } from '@/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus';
import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import { EdgeLabelRenderer, EdgeProps, getStraightPath } from '@xyflow/react';
import { isDefined } from 'twenty-shared/utils';
import { IconFilter } from 'twenty-ui/display';
@@ -26,8 +33,20 @@ const assertFilterEdgeDataOrThrow: (
}
};
const StyledIconButtonGroup = styled(IconButtonGroup)`
const StyledIconButtonGroup = styled(IconButtonGroup)<{
selected?: boolean;
variant: WorkflowDiagramNodeVariant;
}>`
pointer-events: all;
${({ selected, variant, theme }) => {
if (!selected) return '';
const colors = getWorkflowDiagramNodeSelectedColors(variant, theme);
return css`
background-color: ${colors.background};
border: 1px solid ${colors.borderColor};
`;
}}
`;
const StyledConfiguredFilterContainer = styled.div`
@@ -49,6 +68,13 @@ export const WorkflowDiagramFilterEdgeRun = ({
targetY,
});
const workflowSelectedNode = useRecoilComponentValueV2(
workflowSelectedNodeComponentState,
);
const isFilterNodeSelected =
isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
const { openWorkflowRunFilterInCommandMenu } =
useOpenWorkflowRunFilterInCommandMenu();
@@ -87,6 +113,8 @@ export const WorkflowDiagramFilterEdgeRun = ({
onClick: handleFilterButtonClick,
},
]}
selected={isFilterNodeSelected}
variant={getNodeVariantFromStepRunStatus(data.runStatus)}
/>
</StyledConfiguredFilterContainer>
</WorkflowDiagramEdgeV2VisibilityContainer>
@@ -3,6 +3,7 @@ import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBor
import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramStepNodeClickOutsideId';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { Position } from '@xyflow/react';
@@ -106,32 +107,11 @@ const StyledStepNodeInnerContainer = styled.div<{
:is(.workflow-node-container, .workflow-node-container:hover)
& {
${({ theme, variant }) => {
switch (variant) {
case 'running': {
return css`
background: ${theme.adaptiveColors.yellow1};
border-color: ${theme.adaptiveColors.yellow4};
`;
}
case 'success': {
return css`
background: ${theme.adaptiveColors.turquoise1};
border-color: ${theme.adaptiveColors.turquoise4};
`;
}
case 'failure': {
return css`
background: ${theme.background.danger};
border-color: ${theme.color.red};
`;
}
default: {
return css`
background: ${theme.adaptiveColors.blue1};
border-color: ${theme.color.blue};
`;
}
}
const colors = getWorkflowDiagramNodeSelectedColors(variant, theme);
return css`
background: ${colors.background};
border-color: ${colors.borderColor};
`;
}}
}
`;
@@ -47,7 +47,7 @@ export const useOpenWorkflowEditFilterInCommandMenu = () => {
...diagram,
nodes: diagram.nodes.map((node) => ({
...node,
selected: false,
selected: node.id === stepId,
})),
};
});
@@ -0,0 +1,39 @@
import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant';
import { Theme } from '@emotion/react';
export type WorkflowDiagramNodeSelectedColors = {
background: string;
borderColor: string;
};
export const getWorkflowDiagramNodeSelectedColors = (
variant: WorkflowDiagramNodeVariant,
theme: Theme,
): WorkflowDiagramNodeSelectedColors => {
switch (variant) {
case 'running': {
return {
background: theme.adaptiveColors.yellow1,
borderColor: theme.adaptiveColors.yellow4,
};
}
case 'success': {
return {
background: theme.adaptiveColors.turquoise1,
borderColor: theme.adaptiveColors.turquoise4,
};
}
case 'failure': {
return {
background: theme.background.danger,
borderColor: theme.color.red,
};
}
default: {
return {
background: theme.adaptiveColors.blue1,
borderColor: theme.color.blue,
};
}
}
};