Display + edge icon on hover (#12635)
This commit is contained in:
@ -39,6 +39,7 @@ import React, {
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Tag, TagColor } from 'twenty-ui/components';
|
||||
import { THEME_COMMON } from 'twenty-ui/theme';
|
||||
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
|
||||
|
||||
const StyledResetReactflowStyles = styled.div`
|
||||
height: 100%;
|
||||
@ -133,6 +134,11 @@ export const WorkflowDiagramCanvasBase = ({
|
||||
const workflowDiagram = useRecoilComponentValueV2(
|
||||
workflowDiagramComponentState,
|
||||
);
|
||||
|
||||
const setWorkflowInsertStepIds = useSetRecoilComponentStateV2(
|
||||
workflowInsertStepIdsComponentState,
|
||||
);
|
||||
|
||||
const [
|
||||
workflowDiagramFlowInitializationStatus,
|
||||
setWorkflowDiagramFlowInitializationStatus,
|
||||
@ -174,6 +180,10 @@ export const WorkflowDiagramCanvasBase = ({
|
||||
reactflow.setNodes((nodes) =>
|
||||
nodes.map((node) => ({ ...node, selected: false })),
|
||||
);
|
||||
setWorkflowInsertStepIds({
|
||||
parentStepId: undefined,
|
||||
nextStepId: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -1,25 +1,36 @@
|
||||
import { STEP_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth';
|
||||
import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId';
|
||||
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
|
||||
import styled from '@emotion/styled';
|
||||
import { EdgeLabelRenderer } from '@xyflow/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
import { IconButtonGroup } from 'twenty-ui/input';
|
||||
|
||||
const EDGE_OPTION_BUTTON_LEFT_MARGIN = 8;
|
||||
import { useState } from 'react';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
|
||||
|
||||
const StyledIconButtonGroup = styled(IconButtonGroup)`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.strong};
|
||||
pointer-events: all;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div<{
|
||||
labelX?: number;
|
||||
labelY?: number;
|
||||
}>`
|
||||
position: absolute;
|
||||
transform: ${({ labelX, labelY }) =>
|
||||
`translate(${labelX || 0}px, ${isDefined(labelY) ? labelY - STEP_ICON_WIDTH / 2 : 0}px) translateX(${EDGE_OPTION_BUTTON_LEFT_MARGIN}px)`};
|
||||
transform: ${({ labelY }) => `translate(${21}px, ${(labelY || 0) - 14}px)`};
|
||||
`;
|
||||
|
||||
const StyledHoverZone = styled.div`
|
||||
position: absolute;
|
||||
width: 48px;
|
||||
height: 52px;
|
||||
transform: translate(-13px, -16px);
|
||||
background: transparent;
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
type WorkflowDiagramEdgeOptionsProps = {
|
||||
@ -30,31 +41,47 @@ type WorkflowDiagramEdgeOptionsProps = {
|
||||
};
|
||||
|
||||
export const WorkflowDiagramEdgeOptions = ({
|
||||
labelX,
|
||||
labelY,
|
||||
parentStepId,
|
||||
nextStepId,
|
||||
}: WorkflowDiagramEdgeOptionsProps) => {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const { startNodeCreation } = useStartNodeCreation();
|
||||
|
||||
const workflowInsertStepIds = useRecoilComponentValueV2(
|
||||
workflowInsertStepIdsComponentState,
|
||||
);
|
||||
|
||||
const isSelected =
|
||||
workflowInsertStepIds.parentStepId === parentStepId &&
|
||||
workflowInsertStepIds.nextStepId === nextStepId;
|
||||
|
||||
return (
|
||||
<EdgeLabelRenderer>
|
||||
<StyledContainer
|
||||
labelX={labelX}
|
||||
labelY={labelY}
|
||||
data-click-outside-id={WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<StyledIconButtonGroup
|
||||
className="nodrag nopan"
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPlus,
|
||||
onClick: () => {
|
||||
startNodeCreation({ parentStepId, nextStepId });
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<StyledWrapper
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
<StyledHoverZone />
|
||||
{(hovered || isSelected) && (
|
||||
<StyledIconButtonGroup
|
||||
className="nodrag nopan"
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPlus,
|
||||
onClick: () => {
|
||||
startNodeCreation({ parentStepId, nextStepId });
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
</StyledContainer>
|
||||
</EdgeLabelRenderer>
|
||||
);
|
||||
|
||||
@ -11,6 +11,7 @@ export const useStartNodeCreation = () => {
|
||||
const setWorkflowInsertStepIds = useSetRecoilComponentStateV2(
|
||||
workflowInsertStepIdsComponentState,
|
||||
);
|
||||
|
||||
const { openStepSelectInCommandMenu } = useWorkflowCommandMenu();
|
||||
|
||||
const workflowVisualizerWorkflowId = useRecoilComponentValueV2(
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion';
|
||||
import { workflowLastCreatedStepIdComponentState } from '@/workflow/states/workflowLastCreatedStepIdComponentState';
|
||||
@ -11,6 +10,7 @@ import { useCreateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/us
|
||||
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
|
||||
export const useCreateStep = ({
|
||||
workflow,
|
||||
@ -26,9 +26,8 @@ export const useCreateStep = ({
|
||||
workflowLastCreatedStepIdComponentState,
|
||||
);
|
||||
|
||||
const workflowInsertStepIds = useRecoilComponentValueV2(
|
||||
workflowInsertStepIdsComponentState,
|
||||
);
|
||||
const [workflowInsertStepIds, setWorkflowInsertStepIds] =
|
||||
useRecoilComponentStateV2(workflowInsertStepIdsComponentState);
|
||||
|
||||
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||
|
||||
@ -63,9 +62,10 @@ export const useCreateStep = ({
|
||||
|
||||
setWorkflowSelectedNode(createdStep.id);
|
||||
setWorkflowLastCreatedStepId(createdStep.id);
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
throw error;
|
||||
setWorkflowInsertStepIds({
|
||||
parentStepId: undefined,
|
||||
nextStepId: undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@ -31,6 +31,15 @@ const StyledButton = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledAnimatedIconWrapper = styled.span`
|
||||
display: flex;
|
||||
transition: transform 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-3%);
|
||||
}
|
||||
`;
|
||||
|
||||
export const InsideButton = ({
|
||||
className,
|
||||
Icon,
|
||||
@ -41,7 +50,11 @@ export const InsideButton = ({
|
||||
|
||||
return (
|
||||
<StyledButton className={className} onClick={onClick} disabled={disabled}>
|
||||
{Icon && <Icon size={theme.icon.size.sm} />}
|
||||
{Icon && (
|
||||
<StyledAnimatedIconWrapper>
|
||||
<Icon size={theme.icon.size.sm} />
|
||||
</StyledAnimatedIconWrapper>
|
||||
)}
|
||||
</StyledButton>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user