Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/demo-app-ts/src/demos/stylesDemo/Styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,32 @@ export const NodeDecoratorStyles = withTopologySetup(() => {
return null;
});

export const NodeShowDragGhostStyles = withTopologySetup(() => {
useComponentFactory(defaultComponentFactory);
useComponentFactory(stylesComponentFactory);
const nodes: NodeModel[] = createGroupNodes();
const nodes2: NodeModel[] = createGroupNodes('2', 600);

nodes.forEach((n) => (n.data.showDecorators = true));
nodes.forEach((n) => (n.data.labelPosition = LabelPosition.bottom));
nodes.forEach((n) => (n.data.showDragGhost = true));
nodes2.forEach((n) => (n.data.showDecorators = true));
nodes2.forEach((n) => (n.data.showDragGhost = true));
useModel(
useMemo(
(): Model => ({
graph: {
id: 'g1',
type: 'graph'
},
nodes: [...nodes, ...nodes2]
}),
[nodes, nodes2]
)
);
return null;
});

export const NodeLabelStyles = withTopologySetup(() => {
useComponentFactory(defaultComponentFactory);
useComponentFactory(stylesComponentFactory);
Expand Down Expand Up @@ -928,6 +954,9 @@ export const StyleNodes: React.FunctionComponent = () => {
<Tab eventKey={4} title={<TabTitleText>Decorators</TabTitleText>}>
<NodeDecoratorStyles />
</Tab>
<Tab eventKey={5} title={<TabTitleText>Show Drag Ghost While Dragging</TabTitleText>}>
<NodeShowDragGhostStyles />
</Tab>
</Tabs>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export class DemoModel {
badges: false,
icons: false,
contextMenus: false,
hulledOutline: true
hulledOutline: true,
showDragGhost: false
};
protected edgeOptionsP: GeneratorEdgeOptions = {
showStyles: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ const DemoNode: React.FunctionComponent<DemoNodeProps> = observer(
options.showDecorators &&
renderDecorators(options, nodeElement, rest.getShapeDecoratorCenter)
}
showDragGhost={options.showDragGhost}
>
{(focused || detailsLevel !== ScaleDetailsLevel.low) && renderIcon(data, nodeElement)}
</DefaultNode>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ const OptionsContextBar: React.FC = observer(() => {
>
Rectangle Groups
</SelectOption>
<SelectOption
hasCheckbox
value="Show Drag Ghost"
isSelected={options.nodeOptions.showDragGhost}
onClick={() =>
options.setNodeOptions({ ...options.nodeOptions, showDragGhost: !options.nodeOptions.showDragGhost })
}
>
Show Drag Ghost
</SelectOption>
</SelectList>
</Select>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export interface GeneratorNodeOptions {
contextMenus?: boolean;
hideKebabMenu?: boolean;
hulledOutline?: boolean;
showDragGhost?: boolean;
}

export interface GeneratorEdgeOptions {
Expand Down
8 changes: 2 additions & 6 deletions packages/module/src/components/edges/DefaultEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ interface DefaultEdgeProps {
/**
* When true, the edge path and terminals stay visually fixed (last position) while
* an associated node is being dragged. When false or omitted, the edge updates
* during drag as before. Can be set per edge via this prop, element state, or
* in the model as edge data: `data: { freezeEdgeDuringNodeDrag: true }`.
* during drag as before.
*/
freezeEdgeDuringNodeDrag?: boolean;
}
Expand Down Expand Up @@ -124,10 +123,7 @@ const DefaultEdgeInner: React.FunctionComponent<DefaultEdgeInnerProps> = observe
onContextMenu,
freezeEdgeDuringNodeDrag
}) => {
const freezeDuringDrag =
freezeEdgeDuringNodeDrag ??
(element.getData() as { freezeEdgeDuringNodeDrag?: boolean } | undefined)?.freezeEdgeDuringNodeDrag ??
false;
const freezeDuringDrag = freezeEdgeDuringNodeDrag ?? false;

const [hover, hoverRef] = useHover();
const edgeRef = useCombineRefs(hoverRef, dndDropRef);
Expand Down
102 changes: 79 additions & 23 deletions packages/module/src/components/nodes/DefaultNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import styles from '../../css/topology-components';
import { BadgeLocation, GraphElement, isNode, LabelPosition, Node, NodeStatus, TopologyQuadrant } from '../../types';
import {
BadgeLocation,
GraphElement,
isNode,
LabelPosition,
Node,
NodeStatus,
ScaleDetailsLevel,
TopologyQuadrant
} from '../../types';
import { ConnectDragSource, ConnectDropTarget, OnSelect, WithDndDragProps } from '../../behavior';
import Decorator from '../decorators/Decorator';
import { Layer } from '../layers';
Expand Down Expand Up @@ -122,10 +131,18 @@ interface DefaultNodeProps {
raiseLabelOnHover?: boolean; // TODO: Update default to be false, assume demo code will be followed
/** Hide context menu kebab for the node */
hideContextMenuKebab?: boolean;
/**
* When true, a non-interactive copy of the node is drawn at the pre-drag position while dragging.
* When false or omitted, only the live node is shown (default behavior).
*/
showDragGhost?: boolean;
}

const SCALE_UP_TIME = 200;

/** Scale factor for the drag ghost when the graph is not at low details level. */
const DRAG_GHOST_SCALE = 0.7;

type DefaultNodeInnerProps = Omit<DefaultNodeProps, 'element'> & { element: Node };

const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observer(
Expand Down Expand Up @@ -172,8 +189,10 @@ const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observe
onContextMenu,
contextMenuOpen,
raiseLabelOnHover = true,
hideContextMenuKebab
hideContextMenuKebab,
showDragGhost
}) => {
const showDragGhostResolved = showDragGhost ?? false;
const [nodeHovered, hoverRef] = useHover();
const [labelHovered, labelRef] = useHover();
const hovered = nodeHovered || labelHovered;
Expand All @@ -183,6 +202,8 @@ const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observe
const isHover = hover !== undefined ? hover : hovered;
const [nodeScale, setNodeScale] = useState<number>(1);
const decoratorRef = useRef(null);
const boxXRef = useRef<number | null>(null);
const boxYRef = useRef<number | null>(null);

const statusDecorator = useMemo(() => {
if (!status || !showStatusDecorator) {
Expand Down Expand Up @@ -258,6 +279,8 @@ const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observe

const nodeLabelPosition = labelPosition || element.getLabelPosition();
const scale = element.getGraph().getScale();
const detailsLevel = element.getGraph().getDetailsLevel();
const isLowDetailsLevel = detailsLevel === ScaleDetailsLevel.low;

const animationRef = useRef<number>(null);
const scaleGoal = useRef<number>(1);
Expand Down Expand Up @@ -325,6 +348,12 @@ const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observe
return { translateX, translateY };
}, [element, nodeScale, scaleNode]);

const box = element.getBounds();
if (!showDragGhostResolved || !dragging || boxXRef.current === null || boxYRef.current === null) {
boxXRef.current = box.x;
boxYRef.current = box.y;
}

const renderLabel = () => {
if (!showLabel || !(label || element.getLabel())) {
return null;
Expand Down Expand Up @@ -398,28 +427,55 @@ const DefaultNodeInner: React.FunctionComponent<DefaultNodeInnerProps> = observe
};

return (
<g
className={groupClassName}
transform={`${scaleNode ? `translate(${translateX}, ${translateY})` : ''} scale(${nodeScale})`}
>
<NodeShadows />
<g ref={refs} onClick={onSelect} onContextMenu={onContextMenu}>
{ShapeComponent && (
<ShapeComponent
className={backgroundClassName}
element={element}
width={width}
height={height}
dndDropRef={dndDropRef}
filter={filter}
/>
)}
{renderLabel()}
{children}
<>
{dragging && showDragGhostResolved && (
<g
className={css(groupClassName, styles.modifiers.dragGhost)}
transform={`translate(${boxXRef.current - box.x}, ${boxYRef.current - box.y}) ${
isLowDetailsLevel ? '' : `scale(${DRAG_GHOST_SCALE})`
}`}
>
<NodeShadows />
<g>
{ShapeComponent && (
<ShapeComponent
className={backgroundClassName}
element={element}
width={width}
height={height}
filter={filter}
/>
)}
{!isLowDetailsLevel && renderLabel()}
{!isLowDetailsLevel && children}
</g>
{statusDecorator}
{attachments}
</g>
)}
<g
className={groupClassName}
transform={`${scaleNode ? `translate(${translateX}, ${translateY})` : ''} scale(${nodeScale})`}
>
<NodeShadows />
<g ref={refs} onClick={onSelect} onContextMenu={onContextMenu}>
{ShapeComponent && (
<ShapeComponent
className={backgroundClassName}
element={element}
width={width}
height={height}
dndDropRef={dndDropRef}
filter={filter}
/>
)}
{renderLabel()}
{children}
</g>
{statusDecorator}
{attachments}
</g>
{statusDecorator}
{attachments}
</g>
</>
);
}
);
Expand Down
5 changes: 5 additions & 0 deletions packages/module/src/css/topology-components.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
--pf-topology__node__background--Stroke: var(--pf-t--global--border--color--default);
--pf-topology__node__background--StrokeWidth: var(--pf-t--global--border--width--regular);
--pf-topology__node--m-dragging--background--StrokeWidth: var(--pf-t--global--border--width--regular);
--pf-topology__node--m-drag-ghost--Opacity: 0.6;

/* Status changes */
--pf-topology__node--m-disabled--Background--Fill: var(--pf-t--global--background--color--disabled--default);
Expand Down Expand Up @@ -318,6 +319,10 @@
cursor: grab;
}

.pf-topology__node.pf-m-drag-ghost {
opacity: var(--pf-topology__node--m-drag-ghost--Opacity);
}

.pf-topology__node.pf-m-selected .pf-topology__node__background {
stroke-width: var(--pf-topology__node--m-selected__background--StrokeWidth);
--pf-topology__node__background--Stroke: var(--pf-topology__node--m-selected--Background--Stroke);
Expand Down
Loading