Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ export const REACHED_NODES_LIMIT = 'REACHED_NODES_LIMIT';
export const graphResponseSchema = () =>
schema.object({
nodes: schema.arrayOf(
schema.oneOf([entityNodeDataSchema, groupNodeDataSchema, labelNodeDataSchema])
schema.oneOf([
entityNodeDataSchema,
groupNodeDataSchema,
labelNodeDataSchema,
relationshipNodeDataSchema,
])
),
edges: schema.arrayOf(edgeDataSchema),
messages: schema.maybe(schema.arrayOf(schema.oneOf([schema.literal(REACHED_NODES_LIMIT)]))),
Expand All @@ -115,6 +120,7 @@ export const nodeShapeSchema = schema.oneOf([
schema.literal('diamond'),
schema.literal('label'),
schema.literal('group'),
schema.literal('relationship'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a side note, the naming convention here is not aligned with the current design. However, I'm thinking to propose a new change to the existing node's names.

Instead of shape, we will have element type: relationship/entity/event/stack.

So I'm ok with keeping it now as it is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"relationship" sounds very business-domain specific to me, though I don't have a better suggestion.

On the other hand, I like the idea of future renaming label nodes to event, since we also have labels in each node

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with both comments - right now we are coupling shape with node type. As I see it we should have the following:

  • node.type = element type - as mentioned @kfirpeled relationship/entity/event/stack.
  • one of the node params should be the shape. so we could reuse the component to display similar types - although need to check if its possible without major changes.

this would also require us to return additional property for each node - type.
For now i would leave current behavior and do it in a separate task.

]);

export const nodeBaseDataSchema = schema.object({
Expand Down Expand Up @@ -164,6 +170,14 @@ export const labelNodeDataSchema = schema.allOf([
}),
]);

export const relationshipNodeDataSchema = schema.allOf([
nodeBaseDataSchema,
schema.object({
shape: schema.literal('relationship'),
parentId: schema.maybe(schema.string()),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we would like to stack relationships - worth to double check with design

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a question.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decided to support stacked nodes for relationships as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depending on the proposed design - we could add more fields such as count and documentsData as a placeholder to store entities data if we decide to add some popover menu actions later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kfirpeled as discussed in the sync - we are going to show relationship nodes in stacked groupes as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since then I think we reverted that decision not to stack.. anyway. Lets leave it here for now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I would leave it for now to see if all cases are covered - if not ill remove it.

}),
]);

export const edgeDataSchema = schema.object({
id: schema.string(),
source: schema.string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
graphResponseSchema,
groupNodeDataSchema,
labelNodeDataSchema,
relationshipNodeDataSchema,
nodeColorSchema,
nodeShapeSchema,
nodeDocumentDataSchema,
Expand Down Expand Up @@ -49,9 +50,15 @@ export type GroupNodeDataModel = TypeOf<typeof groupNodeDataSchema>;

export type LabelNodeDataModel = TypeOf<typeof labelNodeDataSchema>;

export type RelationshipNodeDataModel = TypeOf<typeof relationshipNodeDataSchema>;

export type EdgeDataModel = TypeOf<typeof edgeDataSchema>;

export type NodeDataModel = EntityNodeDataModel | GroupNodeDataModel | LabelNodeDataModel;
export type NodeDataModel =
| EntityNodeDataModel
| GroupNodeDataModel
| LabelNodeDataModel
| RelationshipNodeDataModel;

export type NodeDocumentDataModel = TypeOf<typeof nodeDocumentDataSchema>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { getMarkerEnd } from './markers';
import { useEdgeColor } from './styles';
import { STACK_NODE_HORIZONTAL_PADDING } from '../constants';
import { GRAPH_EDGE_ID } from '../test_ids';
import { isConnectorShape } from '../utils';

type EdgeColor = EdgeViewModel['color'];

const dashedStyle = {
strokeDasharray: '2 2',
};

const NODES_WITHOUT_MARKER = ['label', 'group'];
const NODES_WITHOUT_MARKER = ['label', 'group', 'relationship'];

export const DefaultEdge = memo(
({
Expand All @@ -35,9 +36,9 @@ export const DefaultEdge = memo(
data,
}: EdgeProps) => {
const color: EdgeColor = data?.color || 'primary';
const entityToLabel = data?.sourceShape !== 'group' && data?.targetShape === 'label';
const labelToEntity = data?.sourceShape === 'label' && data?.targetShape !== 'group';
const isExtraAlignment = entityToLabel || labelToEntity;
const entityToConnector = data?.sourceShape !== 'group' && isConnectorShape(data?.targetShape);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

const connectorToEntity = isConnectorShape(data?.sourceShape) && data?.targetShape !== 'group';
const isExtraAlignment = entityToConnector || connectorToEntity;
const sourceMargin = getShapeHandlePosition(data?.sourceShape);
const targetMargin = getShapeHandlePosition(data?.targetShape);
const markerEnd =
Expand All @@ -56,7 +57,7 @@ export const DefaultEdge = memo(
? targetX + xOffset
: targetX -
xOffset +
(isExtraAlignment ? STACK_NODE_HORIZONTAL_PADDING / 2 : 0) * (labelToEntity ? 1 : -1);
(isExtraAlignment ? STACK_NODE_HORIZONTAL_PADDING / 2 : 0) * (connectorToEntity ? 1 : -1);

const [edgePath] = getSmoothStepPath({
// sourceX and targetX are adjusted to account for the shape handle position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function getShapeHandlePosition(shape?: NodeShape) {
case 'diamond':
return 14;
case 'label':
case 'relationship':

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

return 3;
case 'group':
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ import {
DiamondNode,
LabelNode,
EdgeGroupNode,
RelationshipNode,
} from '../node';
import { layoutGraph } from './layout_graph';
import { DefaultEdge } from '../edge';
import { Minimap } from '../minimap/minimap';
import type { EdgeViewModel, NodeViewModel } from '../types';
import { isConnectorShape } from '../utils';
import { ONLY_RENDER_VISIBLE_ELEMENTS, GRID_SIZE } from '../constants';

import '@xyflow/react/dist/style.css';
Expand Down Expand Up @@ -91,6 +93,7 @@ const nodeTypes = {
diamond: DiamondNode,
label: LabelNode,
group: EdgeGroupNode,
relationship: RelationshipNode,
};

const edgeTypes = {
Expand Down Expand Up @@ -329,7 +332,10 @@ const processGraph = (
node.targetPosition = Position.Left;
node.resizing = false;
node.focusable = false;
} else if (nodeData.shape === 'label' && nodeData.parentId) {
} else if (
(nodeData.shape === 'label' || nodeData.shape === 'relationship') &&
nodeData.parentId
) {
node.parentId = nodeData.parentId;
node.extent = 'parent';
node.expandParent = false;
Expand All @@ -342,18 +348,13 @@ const processGraph = (
const initialEdges: Array<Edge<EdgeViewModel>> = edgesModel
.filter((edgeData) => nodesById[edgeData.source] && nodesById[edgeData.target])
.map((edgeData) => {
const isIn =
nodesById[edgeData.source].shape !== 'label' &&
nodesById[edgeData.target].shape === 'group';
const isInside =
nodesById[edgeData.source].shape === 'group' &&
nodesById[edgeData.target].shape === 'label';
const isOut =
nodesById[edgeData.source].shape === 'label' &&
nodesById[edgeData.target].shape === 'group';
const isOutside =
nodesById[edgeData.source].shape === 'group' &&
nodesById[edgeData.target].shape !== 'label';
const sourceShape = nodesById[edgeData.source].shape;
const targetShape = nodesById[edgeData.target].shape;

const isIn = !isConnectorShape(sourceShape) && targetShape === 'group';
const isInside = sourceShape === 'group' && isConnectorShape(targetShape);
const isOut = isConnectorShape(sourceShape) && targetShape === 'group';
const isOutside = sourceShape === 'group' && !isConnectorShape(targetShape);

return {
id: edgeData.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Dagre from '@dagrejs/dagre';
import type { Node, Edge } from '@xyflow/react';
import type { EdgeViewModel, NodeViewModel, Size } from '../types';
import { getStackNodeStyle } from '../node/styles';
import { isEntityNode, isLabelNode, isStackNode, isStackedLabel } from '../utils';
import { isEntityNode, isConnectorNode, isStackNode, isStackedLabel } from '../utils';
import {
GRID_SIZE,
STACK_NODE_VERTICAL_PADDING,
Expand Down Expand Up @@ -55,7 +55,7 @@ export const layoutGraph = (
nodes.forEach((node) => {
let size = { width: NODE_WIDTH, height: node.measured?.height ?? NODE_HEIGHT };

if (isLabelNode(node.data)) {
if (isConnectorNode(node.data)) {
size = {
height: NODE_LABEL_TOTAL_HEIGHT,
width: NODE_LABEL_WIDTH,
Expand Down Expand Up @@ -100,7 +100,7 @@ export const layoutGraph = (

const layoutedNodes = nodes.map((node) => {
// For stacked nodes, we want to keep the original position relative to the parent
if (isLabelNode(node.data) && node.data.parentId) {
if (isConnectorNode(node.data) && node.data.parentId) {
return {
...node,
position: nodesById[node.data.id].position,
Expand All @@ -114,7 +114,7 @@ export const layoutGraph = (

const dagreNode = g.node(node.data.id);

if (isLabelNode(node.data)) {
if (isConnectorNode(node.data)) {
const x = snapped(Math.round(dagreNode.x - (dagreNode.width ?? 0) / 2));
const y = Math.round(dagreNode.y - NODE_LABEL_HEIGHT / 2);

Expand Down Expand Up @@ -166,7 +166,7 @@ const layoutStackedLabels = (
nodes: Array<Node<NodeViewModel>>
): { size: Size; children: Array<Node<NodeViewModel>> } => {
const children = nodes.filter(
(child) => isLabelNode(child.data) && child.parentId === groupNode.id
(child) => isConnectorNode(child.data) && child.parentId === groupNode.id
);
const stackSize = children.length;
const stackWidth = NODE_LABEL_WIDTH + STACK_NODE_HORIZONTAL_PADDING * 2;
Expand Down
Loading
Loading