Доработки

This commit is contained in:
NikitaKalitov
2026-07-15 09:16:04 +03:00
parent 8bbee2dc7e
commit be2504d60d
7 changed files with 133 additions and 54 deletions
+15 -39
View File
@@ -1,4 +1,4 @@
import { createContext, useContext, type CSSProperties } from 'react';
import { createContext, useContext } from 'react';
import { Handle, NodeResizer, Position, type Node, type NodeProps } from '@xyflow/react';
import type { DiagramNodeData, NodeKind } from './types';
@@ -42,33 +42,6 @@ function ShapeHandles() {
);
}
/**
* The diamond div is a rotated square (`transform: rotate(45deg)`). Handles
* placed at the square's own corners land on the diamond's tips once rotated
* — corners, not `Position.Top/Right/Bottom/Left` (those are edge-midpoints,
* which rotate onto the diamond's flat sides instead).
*/
const DIAMOND_HANDLES: { id: string; position: Position; style: CSSProperties }[] = [
{ id: 'top', position: Position.Top, style: { top: 0, left: 0, transform: 'translate(-50%, -50%)' } },
{ id: 'right', position: Position.Right, style: { top: 0, left: '100%', transform: 'translate(-50%, -50%)' } },
{
id: 'bottom',
position: Position.Bottom,
style: { top: '100%', left: '100%', transform: 'translate(-50%, -50%)' },
},
{ id: 'left', position: Position.Left, style: { top: '100%', left: 0, transform: 'translate(-50%, -50%)' } },
];
function DiamondHandles() {
return (
<>
{DIAMOND_HANDLES.map(({ id, position, style }) => (
<Handle key={id} id={id} type="source" position={position} style={style} />
))}
</>
);
}
function SubProcessBadge({ id }: { id: string }) {
const withChildren = useContext(NodesWithChildrenContext);
if (!withChildren.has(id)) return null;
@@ -145,19 +118,22 @@ function GatewayNode({ id, data, type, selected }: NodeProps<ShapeNode>) {
<div className="diagram-node--gateway-outer">
<ShapeResizer selected={selected} minWidth={32} minHeight={32} />
{/*
The diamond is an inner square inset to 1/sqrt(2) of the outer box and
rotated 45deg, so its tips exactly reach the outer box's edges without
clipping. The caption sits on the (unrotated) outer box instead of the
diamond itself, so it renders upright below the shape.
The diamond is drawn with clip-path on a plain (unrotated) box, not
`transform: rotate(45deg)` — a rotated element makes React Flow's
getBoundingClientRect-based handle measurement pick up sub-pixel
rounding from the rotation, which showed up as a visible kink in
otherwise-straight horizontal/vertical edges. With clip-path, the
standard Position.Top/Right/Bottom/Left handles already land exactly
on the diamond's tips (they're at the box's edge midpoints either
way), so this reuses the same ShapeHandles as every other shape.
Handles/badge/caption are siblings of the clipped div, not children
of it, since clip-path would otherwise cut off anything of theirs
that renders outside the diamond outline.
*/}
<div
className="diagram-node diagram-node--diamond"
style={{ background: data.color ?? DEFAULT_NODE_COLOR[kind] }}
>
<DiamondHandles />
<span className="diagram-node__icon">{GATEWAY_ICON[kind]}</span>
</div>
<div className="diagram-node--diamond-bg" style={{ background: data.color ?? DEFAULT_NODE_COLOR[kind] }} />
<ShapeHandles />
<SubProcessBadge id={id} />
<span className="diagram-node__icon">{GATEWAY_ICON[kind]}</span>
<span className="diagram-node__caption">{data.label}</span>
</div>
);