diff --git a/frontend/src/lib/components/ResourcePicker.svelte b/frontend/src/lib/components/ResourcePicker.svelte index 6e3033f72d..87b99f6a09 100644 --- a/frontend/src/lib/components/ResourcePicker.svelte +++ b/frontend/src/lib/components/ResourcePicker.svelte @@ -75,6 +75,8 @@ }} /> +{initialValue} +{value}
- - - - - -
- - -
-
-{/if} - - diff --git a/frontend/src/lib/components/graph/svelvet/resizableNodes/controllers/util.ts b/frontend/src/lib/components/graph/svelvet/resizableNodes/controllers/util.ts deleted file mode 100644 index f8bc6dab57..0000000000 --- a/frontend/src/lib/components/graph/svelvet/resizableNodes/controllers/util.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { StoreType, ResizeNodeType } from '../../store/types/types'; -import { get } from 'svelte/store'; -/** - * Finds all resizeNodes that matches the conditions specified in the filter parameter from a Svelvet store and returns these resizeNodes in an array - * - * @param store The Svelvet store containing the state of a Svelvet component - * @param filter An object to specify conditions. - * @returns An array of resizeNode objects that matches the conditions specified in filter parameter - */ -export function getResizeNodes( - store: StoreType, - filter?: { [key: string]: any } -) { - let resizeNodes = Object.values(get(store.resizeNodesStore)); - // filter the array for elements that match filter - if (filter !== undefined) { - resizeNodes = resizeNodes.filter((resizeNode) => { - for (let filterKey in filter) { - const filterValue = filter[filterKey]; - if (resizeNode[filterKey as keyof ResizeNodeType] !== filterValue) - return false; - } - return true; - }); - } - // return list of anchors - return resizeNodes; -} diff --git a/frontend/src/lib/components/graph/svelvet/resizableNodes/models/ResizeNode.ts b/frontend/src/lib/components/graph/svelvet/resizableNodes/models/ResizeNode.ts deleted file mode 100644 index a23fec9317..0000000000 --- a/frontend/src/lib/components/graph/svelvet/resizableNodes/models/ResizeNode.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { ResizeNodeType } from '../../store/types/types'; -// import { writable, derived, get, readable } from 'svelte/store'; -import { stores } from '../../store/models/store'; - -/** A ResizeNode class that implements ResizeNodeType interface -* @param {string} id The id of the ResizeNode -* @param {string} canvasId The canvasId of the Svelvet component that the instantiated ResizeNode will be on. -* @param {string} nodeId The id of the Node that the instantiated ResizedNode will be attached to. -* @param {number} positionX The X-axis position of the ResizeNode (left top corner of the ResizeNode) -* @param {number} positionY The Y-axis position of the ResizeNode (left top corner of the ResizeNode) -*/ -export class ResizeNode implements ResizeNodeType { - constructor( - public id: string, - public canvasId: string, - public nodeId: string, - public positionX: number, - public positionY: number - ) {} - - /** - * setPosition will update the positionX and positionY of the ResizeNode. This will be invoked when the Node that this ResizeNode attached to is moving so that the ResizeNode will follow the Node. - * - * @param movementX The mouse movement value on the X-axis - * @param movementY The mouse movement value on the Y-axis - */ - setPosition(movementX: number, movementY: number) { - this.positionX += movementX; - this.positionY += movementY; - } - - /** - * setPositionAndCascade will update the positionX and positionY of the ResizeNode and also cascade changes to related Node. This will be invoked when the user drags the ResizeNode so the position of the ResizeNode and the width and height of the Node would change accordingly. - * @param movementX The mouse movement value on the X-axis - * @param movementY The mouse movement value on the Y-axis - */ - setPositionAndCascade(movementX: number, movementY: number) { - //declare variables needed to interact with the corresponding node to this resize anchor - const nodeId = this.nodeId; - const { nodesStore } = stores[this.canvasId]; - - //Updates the width/height of the corresponding Node - nodesStore.update((nodes) => { - const node = nodes[nodeId]; - - //sets condition so node cannot be less than 20 px in width or height. - if (node.width + movementX > 20 && node.height + movementY > 20) { - //Updates position to this resizeNode anchor. Must be done within the update method so ensure the position doesn't change if the width goes below 20px - this.positionX += movementX; - this.positionY += movementY; - //setSizeFromMovement will then update the anchors position as the width & height changes - node.setSizeFromMovement(movementX, movementY); - } - return { ...nodes }; - }); - } - - /** - * delete is going to delete the ResizeNode from the resizeNodesStore. - */ - delete() { - const store = stores[this.canvasId]; - const { resizeNodesStore } = stores[this.canvasId]; - resizeNodesStore.update((resizeNodes) => { - for (const resizeNodeId in resizeNodes) { - if (resizeNodes[resizeNodeId].id === this.id) { - delete resizeNodes[resizeNodeId]; - } - } - return { ...resizeNodes }; - }); - } -} diff --git a/frontend/src/lib/components/graph/svelvet/resizableNodes/views/ResizeNode.svelte b/frontend/src/lib/components/graph/svelvet/resizableNodes/views/ResizeNode.svelte deleted file mode 100644 index 53c593bdd7..0000000000 --- a/frontend/src/lib/components/graph/svelvet/resizableNodes/views/ResizeNode.svelte +++ /dev/null @@ -1,85 +0,0 @@ - - - { - e.preventDefault(); - if (isSelected) { - resizeNodesStore.update((resNode) => { - const newResNode = resNode[resizeId]; - const d3Scale = get(store.d3Scale); - newResNode.setPositionAndCascade( - e.movementX / d3Scale, - e.movementY / d3Scale, - resizeId - ); - return { ...resNode }; - }); - } - }} - on:mouseup={(e) => { - e.preventDefault(); - // don't need to set $nodeSelected = false because Node component will do it for you - isSelected = false; - }} -/> - -
{ - e.preventDefault(); - $nodeSelected = true; - isSelected = true; - }} - on:mouseover={(e) => ($nodeSelected = true)} - on:focus - on:mouseleave={(e) => ($nodeSelected = false)} - on:mouseenter={(e) => ($nodeSelected = true)} - on:wheel|preventDefault - class="ResizeNode" - style=" - left: {reactResizeNode.positionX - 10}px; - top: {reactResizeNode.positionY - 10}px; - width: {20}px; - height: {20}px; - background-color: purple; - border-color: purple; - border-radius: 50%;" - id="svelvet-{resizeId}" -/> - - diff --git a/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts b/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts index 64bff839c1..e70f9dbda6 100644 --- a/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts +++ b/frontend/src/lib/components/graph/svelvet/store/controllers/storeApi.ts @@ -30,18 +30,13 @@ populateSvelvetStoreFromUserInput(canvasId, nodes, edges) - edges: same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS. - Returns: store */ -import { stores } from '../models/store'; -import { writable, get } from 'svelte/store'; +import { stores } from '../models/store' +import { writable, get } from 'svelte/store' -import type { StoreType } from '../types/types'; -import type { UserNodeType, UserEdgeType } from '../../types/types'; -import { - populateAnchorsStore, - populateNodesStore, - populateEdgesStore, - populateResizeNodeStore, -} from './util'; -import { populateCollapsibleStore } from '../../collapsible/controllers/util'; +import type { StoreType } from '../types/types' +import type { UserNodeType, UserEdgeType } from '../../types/types' +import { populateAnchorsStore, populateNodesStore, populateEdgesStore } from './util' +import { populateCollapsibleStore } from '../../collapsible/controllers/util' /** * findStore is going to return the target Svelvet store with the canvasId provided as argument. @@ -50,7 +45,7 @@ import { populateCollapsibleStore } from '../../collapsible/controllers/util'; * @returns The store of a Svelvet component that matches the canvasId */ export function findStore(canvasId: string): StoreType { - return stores[canvasId]; + return stores[canvasId] } /** @@ -68,33 +63,31 @@ export function findStore(canvasId: string): StoreType { * @returns An empty store for the newly created Svelvet component. */ export function createStoreEmpty(canvasId: string): StoreType { - stores[canvasId] = { - nodesStore: writable({}), - edgesStore: writable({}), - anchorsStore: writable({}), - resizeNodesStore: writable({}), - potentialAnchorsStore: writable({}), - widthStore: writable(600), - heightStore: writable(600), - backgroundStore: writable(false), - movementStore: writable(true), - nodeSelected: writable(false), - nodeIdSelected: writable(-1), - d3Scale: writable(1), - options: writable({}), - temporaryEdgeStore: writable([]), - nodeCreate: writable(false), // this option sets whether the "nodeEdit" feature is enabled - boundary: writable(false), - edgeEditModal: writable(null), // this is used for edgeEditModal feature. When an edge is right clicked, store.edgeEditModal is set to the edgeId string. This causes a modal to be rendered - collapsibleStore: writable([]), // this is used for the collaspsible node feature. If the feature is enabled, store.collapsible will be populated with Collapsible objects which will track whether the node should be displayed or not - collapsibleOption: writable(false), - lockedOption: writable(false), - editableOption: writable(false), // true if you want nodes/edges to be editable. See feature editEdges - d3ZoomParameters: writable({}), // this stores d3 parameters x, y, and zoom. This isn't used for anything other than giving users a way to access d3 zoom parameters if they want to build on top of Svelvet - resizableOption: writable(true), // option to turn on/off resizable nodes. - highlightEdgesOption: writable(true), // option to turn on/off highlightable edges - }; - return stores[canvasId]; + stores[canvasId] = { + nodesStore: writable({}), + edgesStore: writable({}), + anchorsStore: writable({}), + potentialAnchorsStore: writable({}), + widthStore: writable(600), + heightStore: writable(600), + backgroundStore: writable(false), + movementStore: writable(true), + nodeSelected: writable(false), + nodeIdSelected: writable(-1), + d3Scale: writable(1), + options: writable({}), + temporaryEdgeStore: writable([]), + nodeCreate: writable(false), // this option sets whether the "nodeEdit" feature is enabled + boundary: writable(false), + edgeEditModal: writable(null), // this is used for edgeEditModal feature. When an edge is right clicked, store.edgeEditModal is set to the edgeId string. This causes a modal to be rendered + collapsibleStore: writable([]), // this is used for the collaspsible node feature. If the feature is enabled, store.collapsible will be populated with Collapsible objects which will track whether the node should be displayed or not + collapsibleOption: writable(false), + lockedOption: writable(false), + editableOption: writable(false), // true if you want nodes/edges to be editable. See feature editEdges + d3ZoomParameters: writable({}), // this stores d3 parameters x, y, and zoom. This isn't used for anything other than giving users a way to access d3 zoom parameters if they want to build on top of Svelvet + highlightEdgesOption: writable(true) // option to turn on/off highlightable edges + } + return stores[canvasId] } /** @@ -105,24 +98,20 @@ export function createStoreEmpty(canvasId: string): StoreType { * @param edges Same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS. */ export function populateSvelvetStoreFromUserInput( - canvasId: string, - nodes: UserNodeType[], - edges: UserEdgeType[] + canvasId: string, + nodes: UserNodeType[], + edges: UserEdgeType[] ): void { - // find the store - const store = findStore(canvasId); + // find the store + const store = findStore(canvasId) - // populate store.nodesStore with user nodes - populateNodesStore(store, nodes, canvasId); - // populate store.anchorsStore with anchors. Note the userdoes not explictly define anchors; anchors are calculated from the edges - populateAnchorsStore(store, nodes, edges, canvasId); - // populate edges - populateEdgesStore(store, edges, canvasId); - //populate resize Store - if (get(store.resizableOption)) - populateResizeNodeStore(store, nodes, canvasId); + // populate store.nodesStore with user nodes + populateNodesStore(store, nodes, canvasId) + // populate store.anchorsStore with anchors. Note the userdoes not explictly define anchors; anchors are calculated from the edges + populateAnchorsStore(store, nodes, edges, canvasId) + // populate edges + populateEdgesStore(store, edges, canvasId) - // populatate collapsible objects if "collapsible" feature is turned on - if (get(store.collapsibleOption)) - populateCollapsibleStore(store, nodes, edges, canvasId); + // populatate collapsible objects if "collapsible" feature is turned on + if (get(store.collapsibleOption)) populateCollapsibleStore(store, nodes, edges, canvasId) } diff --git a/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts b/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts index dd84ca8da3..424546ca83 100644 --- a/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts +++ b/frontend/src/lib/components/graph/svelvet/store/controllers/util.ts @@ -1,51 +1,15 @@ -const pkStringGenerator = () => (Math.random() + 1).toString(36).substring(7); -import { - rightCb, - leftCb, - topCb, - bottomCb, -} from '../../edges/controllers/anchorCbUser'; // these are callbacks used to calculate anchor position relative to node -import { - dynamicCbCreator, - fixedCbCreator, -} from '../../edges/controllers/anchorCbDev'; +const pkStringGenerator = () => (Math.random() + 1).toString(36).substring(7) // these are callbacks used to calculate anchor position relative to node +import { dynamicCbCreator, fixedCbCreator } from '../../edges/controllers/anchorCbDev' -import type { AnchorType, AnchorCbType } from '../../edges/types/types'; +import type { AnchorType, AnchorCbType } from '../../edges/types/types' -import type { - NodeType, - EdgeType, - StoreType, - ResizeNodeType, - PotentialAnchorType, -} from '../types/types'; -import type { UserNodeType, UserEdgeType } from '../../types/types'; +import type { NodeType, EdgeType, StoreType, ResizeNodeType } from '../types/types' +import type { UserNodeType, UserEdgeType } from '../../types/types' -import { ResizeNode } from '../../resizableNodes/models/ResizeNode'; -import { Anchor } from '../../edges/models/Anchor'; -import { Node } from '../../nodes/models/Node'; -import { Edge } from '../../edges/models/Edge'; -import { writable, derived, get, readable } from 'svelte/store'; -import { getEdgeById, getAnchors } from '../../edges/controllers/util'; - -/** - * Creates resize node on the bottom right corner of the targeted Node - * @param canvasId The canvasId of the Svelvet component that holds the targeted Node - * @param nodeId The id of the Node that the resize node attached to - * @param posX The number of pixels on the x-axis relative to the left top corner of the targeted Node - * @param posY The number of pixels on the y-axis relative to the left top corner of the targeted Node - * @returns A ReziseNode object with randomized id, canvasId, nodeId, posX, and posY - */ -function createResizeNode( - canvasId: string, - nodeId: string, - posX: number, - posY: number -) { - const id = pkStringGenerator(); - const resizeNode = new ResizeNode(id, canvasId, nodeId, posX, posY); - return resizeNode; -} +import { Anchor } from '../../edges/models/Anchor' +import { Node } from '../../nodes/models/Node' +import { Edge } from '../../edges/models/Edge' +import { getAnchors } from '../../edges/controllers/util' /** * Creates an Anchor on the targeted Node with infomation the userNode holds @@ -58,45 +22,44 @@ function createResizeNode( */ function createAnchor( - store: StoreType, - userNode: UserNodeType | null, - sourceOrTarget: 'source' | 'target', - canvasId: string, - edge: UserEdgeType + store: StoreType, + userNode: UserNodeType | null, + sourceOrTarget: 'source' | 'target', + canvasId: string, + edge: UserEdgeType ) { - // edge case - if (userNode === null) - throw `you cannot create an anchor without a user node (for now)`; + // edge case + if (userNode === null) throw `you cannot create an anchor without a user node (for now)` - const edgeId = edge.id; - const anchorId = pkStringGenerator(); + const edgeId = edge.id + const anchorId = pkStringGenerator() - // userCb is the appropriate source or taret callback from userEdge object. It is - // possible the user to NOT set userCb in which case userCb will be undefined - let userCb: Function | undefined; - if (sourceOrTarget === 'target') userCb = edge.targetAnchorCb; - else userCb = edge.sourceAnchorCb; + // userCb is the appropriate source or taret callback from userEdge object. It is + // possible the user to NOT set userCb in which case userCb will be undefined + let userCb: Function | undefined + if (sourceOrTarget === 'target') userCb = edge.targetAnchorCb + else userCb = edge.sourceAnchorCb - // create anchor callbacks + // create anchor callbacks - let cb: AnchorCbType; - if (userCb === undefined) cb = dynamicCbCreator(store, edgeId, anchorId); - else cb = fixedCbCreator(store, edgeId, anchorId, userNode.id, userCb); + let cb: AnchorCbType + if (userCb === undefined) cb = dynamicCbCreator(store, edgeId, anchorId) + else cb = fixedCbCreator(store, edgeId, anchorId, userNode.id, userCb) - // Create a new anchor. - const anchor = new Anchor( - anchorId, - userNode.id, - edgeId, - sourceOrTarget, - -1, // dummy variables for x,y,angle for now - -1, // dummy variables for x,y,angle for now - cb, - canvasId, - 0 // dummy variables for x,y,angle for now - ); - // return - return anchor; + // Create a new anchor. + const anchor = new Anchor( + anchorId, + userNode.id, + edgeId, + sourceOrTarget, + -1, // dummy variables for x,y,angle for now + -1, // dummy variables for x,y,angle for now + cb, + canvasId, + 0 // dummy variables for x,y,angle for now + ) + // return + return anchor } /** @@ -105,71 +68,67 @@ function createAnchor( * @param edges An edge that the user specifies. This is NOT the same as a Edge object. * @param canvasId The canvasId of the Svelvet component that holds the Edges */ -export function populateEdgesStore( - store: StoreType, - edges: UserEdgeType[], - canvasId: string -) { - const edgesStore: { [key: string]: EdgeType } = {}; - for (let i = 0; i < edges.length; i++) { - const userEdge = edges[i]; - // { id: 'e1-2', source: 1, type: 'straight', target: 2, label: 'e1-2' }, - // source is node.id for the source node - // target is node.id for the target node - // We need to get the anchors - const { - source: sourceNodeId, - target: targetNodeId, - id: edgeId, - type, - label, - labelBgColor, - labelTextColor, - edgeColor, - animate, - noHandle, - arrow, - clickCallback, - className, - } = userEdge; +export function populateEdgesStore(store: StoreType, edges: UserEdgeType[], canvasId: string) { + const edgesStore: { [key: string]: EdgeType } = {} + for (let i = 0; i < edges.length; i++) { + const userEdge = edges[i] + // { id: 'e1-2', source: 1, type: 'straight', target: 2, label: 'e1-2' }, + // source is node.id for the source node + // target is node.id for the target node + // We need to get the anchors + const { + source: sourceNodeId, + target: targetNodeId, + id: edgeId, + type, + label, + labelBgColor, + labelTextColor, + edgeColor, + animate, + noHandle, + arrow, + clickCallback, + className + } = userEdge - const anchors = getAnchors(store, { edgeId: edgeId }); - // check that we have two anchors for every edge - if (anchors.length !== 2) throw 'We should have two anchors for every node'; - // check that we have 1 source anchor and 1 target anchor. Since sourceOrTarget is typed to be either 'source' - // or 'target', it suffices to check whether there are two unique elements - if (new Set(anchors.map((e) => e.sourceOrTarget)).size !== 2) - throw 'we should have one source and one target anchor'; - // get source and target anchor - let sourceAnchor, targetAnchor; - if (anchors[0].sourceOrTarget === 'source') { - sourceAnchor = anchors[0]; - targetAnchor = anchors[1]; - } else { - sourceAnchor = anchors[1]; - targetAnchor = anchors[0]; - } + const anchors = getAnchors(store, { edgeId: edgeId }) + // check that we have two anchors for every edge + if (anchors.length !== 2) throw 'We should have two anchors for every node' + // check that we have 1 source anchor and 1 target anchor. Since sourceOrTarget is typed to be either 'source' + // or 'target', it suffices to check whether there are two unique elements + if (new Set(anchors.map((e) => e.sourceOrTarget)).size !== 2) + throw 'we should have one source and one target anchor' + // get source and target anchor + let sourceAnchor, targetAnchor + if (anchors[0].sourceOrTarget === 'source') { + sourceAnchor = anchors[0] + targetAnchor = anchors[1] + } else { + sourceAnchor = anchors[1] + targetAnchor = anchors[0] + } - edgesStore[edgeId] = new Edge( - edgeId, - sourceAnchor.positionX, - sourceAnchor.positionY, - targetAnchor.positionX, - targetAnchor.positionY, - canvasId, - userEdge.label === undefined ? '' : userEdge.label, - userEdge.type === undefined ? 'bezier' : userEdge.type, - userEdge.labelBgColor === undefined ? 'white' : userEdge.labelBgColor, - userEdge.labelTextColor === undefined ? 'black' : userEdge.labelTextColor, - userEdge.edgeColor === undefined ? 'black' : userEdge.edgeColor, - userEdge.animate === undefined ? false : userEdge.animate, - userEdge.noHandle === undefined ? false : userEdge.noHandle, - userEdge.arrow === undefined ? false : userEdge.arrow, - userEdge.clickCallback === undefined ? () => { } : userEdge.clickCallback, - userEdge.className === undefined ? '' : userEdge.className - ); - } - store.edgesStore.set(edgesStore); + edgesStore[edgeId] = new Edge( + edgeId, + sourceAnchor.positionX, + sourceAnchor.positionY, + targetAnchor.positionX, + targetAnchor.positionY, + canvasId, + userEdge.label === undefined ? '' : userEdge.label, + userEdge.type === undefined ? 'bezier' : userEdge.type, + userEdge.labelBgColor === undefined ? 'white' : userEdge.labelBgColor, + userEdge.labelTextColor === undefined ? 'black' : userEdge.labelTextColor, + userEdge.edgeColor === undefined ? 'black' : userEdge.edgeColor, + userEdge.animate === undefined ? false : userEdge.animate, + userEdge.noHandle === undefined ? false : userEdge.noHandle, + userEdge.arrow === undefined ? false : userEdge.arrow, + userEdge.clickCallback === undefined ? () => {} : userEdge.clickCallback, + userEdge.className === undefined ? '' : userEdge.className + ) + } + store.edgesStore.set(edgesStore) } /** @@ -178,18 +137,14 @@ export function populateEdgesStore( * @param userNodes The array of userNodes (NOT the same as Node object) * @returns The node that user specified or null if not found */ -function findUserNodeById( - id: string, - userNodes: UserNodeType[] -): UserNodeType | null { - for (let i = 0; i < userNodes.length; i++) { - const userNode = userNodes[i]; - if (userNode.id === id) return userNode; - } - return null; +function findUserNodeById(id: string, userNodes: UserNodeType[]): UserNodeType | null { + for (let i = 0; i < userNodes.length; i++) { + const userNode = userNodes[i] + if (userNode.id === id) return userNode + } + return null } - /** * Populates the anchorsStore. This will overwrite any data in the AnchorsStore. * @param store The Svelvet store containing the state of the Svelvet component @@ -198,50 +153,38 @@ function findUserNodeById( * @param canvasId The canvasId of the Svelvet component that holds the nodes and edges */ export function populateAnchorsStore( - store: StoreType, - nodes: UserNodeType[], - edges: UserEdgeType[], - canvasId: string + store: StoreType, + nodes: UserNodeType[], + edges: UserEdgeType[], + canvasId: string ) { - // anchorsStore will populated and eventaully synchronized to store.anchorsStore - const anchorsStore: { [key: string]: AnchorType } = {}; - // iterate through user edges. Note the user never explicitly defines anchors; we calculate anchors - // from the user edge/node information - for (let i = 0; i < edges.length; i++) { - const userEdge = edges[i]; - // find the source and target userNodes. These will be used to create the nodeId foreign key and - // determine placement of the anchor based on userNode.targetPosition, useNode.sourcePosition - const { source: sourceNodeId, target: targetNodeId } = userEdge; - const sourceUserNode = findUserNodeById(sourceNodeId, nodes); - const targetUserNode = findUserNodeById(targetNodeId, nodes); - // create source anchor - const sourceAnchor = createAnchor( - store, - sourceUserNode, - 'source', - canvasId, - userEdge - ); - // create target anchor - const targetAnchor = createAnchor( - store, - targetUserNode, - 'target', - canvasId, - userEdge - ); - // store source and target anchors - anchorsStore[sourceAnchor.id] = sourceAnchor; - anchorsStore[targetAnchor.id] = targetAnchor; - } + // anchorsStore will populated and eventaully synchronized to store.anchorsStore + const anchorsStore: { [key: string]: AnchorType } = {} + // iterate through user edges. Note the user never explicitly defines anchors; we calculate anchors + // from the user edge/node information + for (let i = 0; i < edges.length; i++) { + const userEdge = edges[i] + // find the source and target userNodes. These will be used to create the nodeId foreign key and + // determine placement of the anchor based on userNode.targetPosition, useNode.sourcePosition + const { source: sourceNodeId, target: targetNodeId } = userEdge + const sourceUserNode = findUserNodeById(sourceNodeId, nodes) + const targetUserNode = findUserNodeById(targetNodeId, nodes) + // create source anchor + const sourceAnchor = createAnchor(store, sourceUserNode, 'source', canvasId, userEdge) + // create target anchor + const targetAnchor = createAnchor(store, targetUserNode, 'target', canvasId, userEdge) + // store source and target anchors + anchorsStore[sourceAnchor.id] = sourceAnchor + anchorsStore[targetAnchor.id] = targetAnchor + } - //populates the anchorsStore - store.anchorsStore.set(anchorsStore); + //populates the anchorsStore + store.anchorsStore.set(anchorsStore) - // set anchor positions. We can only set anchor positions after anchorsStore and nodesStore - // has been populated. TODO: maybe add a check to see that anchorsStore and NodesStore populated? - const anchors = getAnchors(store); - for (const anchor of anchors) anchor.callback(); + // set anchor positions. We can only set anchor positions after anchorsStore and nodesStore + // has been populated. TODO: maybe add a check to see that anchorsStore and NodesStore populated? + const anchors = getAnchors(store) + for (const anchor of anchors) anchor.callback() } /** @@ -250,68 +193,36 @@ export function populateAnchorsStore( * @param nodes An array of user specifed nodes * @param canvasId The canvasId of the Svelvet component that holds the nodes */ -export function populateNodesStore( - store: StoreType, - nodes: UserNodeType[], - canvasId: string -) { - // this is the nodesStore object. THIS IS NOT THE SAME AS A NODESTORE - const nodesStore: { [key: string]: NodeType } = {}; - // iterate through user nodes and create node objects - for (let i = 0; i < nodes.length; i++) { - const userNode: UserNodeType = nodes[i]; - const nodeId = userNode.id; +export function populateNodesStore(store: StoreType, nodes: UserNodeType[], canvasId: string) { + // this is the nodesStore object. THIS IS NOT THE SAME AS A NODESTORE + const nodesStore: { [key: string]: NodeType } = {} + // iterate through user nodes and create node objects + for (let i = 0; i < nodes.length; i++) { + const userNode: UserNodeType = nodes[i] + const nodeId = userNode.id - // TODO: move sanitizing default values to middleware - const node = new Node( - nodeId.toString(), - userNode.position.x, - userNode.position.y, - userNode.width, - userNode.height, - userNode.bgColor ?? 'white', - userNode.data, - canvasId, - userNode.borderColor === undefined ? 'black' : userNode.borderColor, - userNode.image === undefined ? false : userNode.image, - userNode.src === undefined ? '' : userNode.src, - userNode.textColor === undefined ? '' : userNode.textColor, - userNode.borderRadius === undefined ? 0 : userNode.borderRadius, - userNode.childNodes === undefined ? [] : userNode.childNodes, - userNode.className === undefined ? '' : userNode.className, - userNode.clickCallback === undefined ? () => { } : userNode.clickCallback - ); + // TODO: move sanitizing default values to middleware + const node = new Node( + nodeId.toString(), + userNode.position.x, + userNode.position.y, + userNode.width, + userNode.height, + userNode.bgColor ?? 'white', + userNode.data, + canvasId, + userNode.borderColor === undefined ? 'black' : userNode.borderColor, + userNode.image === undefined ? false : userNode.image, + userNode.src === undefined ? '' : userNode.src, + userNode.textColor === undefined ? '' : userNode.textColor, + userNode.borderRadius === undefined ? 0 : userNode.borderRadius, + userNode.childNodes === undefined ? [] : userNode.childNodes, + userNode.className === undefined ? '' : userNode.className, + userNode.clickCallback === undefined ? () => {} : userNode.clickCallback + ) - nodesStore[nodeId] = node; - } - // This is actually what sets the store - store.nodesStore.set(nodesStore); -} - -/** - * Populates the resizeNodeStore. If a Node is resizable, a small ResizeNode object is going to be attached to the Node's right bottom corner to react to the mouse drag. - * @param store The Svelvet store containing the state of the Svelvet component - * @param nodes An array of user specifed nodes (NOT the same as Node) - * @param canvasId The canvasId of the Svelvet component that holds the resizeNodes - */ -export function populateResizeNodeStore( - store: StoreType, - nodes: UserNodeType[], - canvasId: string -) { - const resizeNodeStore: { [key: string]: ResizeNodeType } = {}; - - for (let i = 0; i < nodes.length; i++) { - const userNode = nodes[i]; - const { id, width, height, position } = userNode; - const resizeNode = createResizeNode( - canvasId, - id, - position.x + width, - position.y + height - ); - resizeNodeStore[resizeNode.id] = resizeNode; - } - - store.resizeNodesStore.set(resizeNodeStore); + nodesStore[nodeId] = node + } + // This is actually what sets the store + store.nodesStore.set(nodesStore) } diff --git a/frontend/src/lib/components/graph/svelvet/store/types/types.ts b/frontend/src/lib/components/graph/svelvet/store/types/types.ts index e4f22dcd6d..ee61465a49 100644 --- a/frontend/src/lib/components/graph/svelvet/store/types/types.ts +++ b/frontend/src/lib/components/graph/svelvet/store/types/types.ts @@ -1,124 +1,125 @@ -import type { CollapsibleType } from '../../collapsible/types/types'; -import type { Writable } from 'svelte/store'; -import type { AnchorType } from '../../edges/types/types'; -import type { SvelteComponentTyped } from "svelte"; +import type { CollapsibleType } from '../../collapsible/types/types' +import type { Writable } from 'svelte/store' +import type { AnchorType } from '../../edges/types/types' export interface ResizeNodeType { - id: string; - nodeId: string; - edgeId?: string; - canvasId: string; - anchorId?: string; - positionX: number; - positionY: number; - setPositionAndCascade: Function; - setPosition: Function; - delete: Function; + id: string + nodeId: string + edgeId?: string + canvasId: string + anchorId?: string + positionX: number + positionY: number + setPositionAndCascade: Function + setPosition: Function + delete: Function } /* Type for a single svelvet store */ export interface StoreType { - nodesStore: Writable<{ [key: string]: NodeType }>; - edgesStore: Writable<{ [key: string]: EdgeType }>; - anchorsStore: Writable<{ [key: string]: AnchorType }>; - resizeNodesStore: Writable<{ [key: string]: ResizeNodeType }>; - potentialAnchorsStore: Writable<{ [key: string]: PotentialAnchorType }>; - widthStore: Writable; - heightStore: Writable; - backgroundStore: Writable; - movementStore: Writable; - nodeIdSelected: Writable; - nodeSelected: Writable; // this is used to stop d3 panning when node is being dragged - d3Scale: Writable; // for zoom and pan - options: Writable<{ [key: string]: any }>; - temporaryEdgeStore: Writable; - nodeCreate: Writable; // this option sets whether the "nodeEdit" feature is enabled - boundary: Writable; - edgeEditModal: Writable; // this options is used to place the edgeEdit modal when an edge is right-clicked. null is no modal, positionType if modal should be placed at position defined by postionType.x, positionType.y - collapsibleStore: Writable; - collapsibleOption: Writable; - lockedOption: Writable; - editableOption: Writable; - d3ZoomParameters: Writable<{ - [key: string]: number; - }>; - resizableOption: Writable; - highlightEdgesOption: Writable; + nodesStore: Writable<{ [key: string]: NodeType }> + edgesStore: Writable<{ [key: string]: EdgeType }> + anchorsStore: Writable<{ [key: string]: AnchorType }> + potentialAnchorsStore: Writable<{ [key: string]: PotentialAnchorType }> + widthStore: Writable + heightStore: Writable + backgroundStore: Writable + movementStore: Writable + nodeIdSelected: Writable + nodeSelected: Writable // this is used to stop d3 panning when node is being dragged + d3Scale: Writable // for zoom and pan + options: Writable<{ [key: string]: any }> + temporaryEdgeStore: Writable + nodeCreate: Writable // this option sets whether the "nodeEdit" feature is enabled + boundary: Writable + edgeEditModal: Writable // this options is used to place the edgeEdit modal when an edge is right-clicked. null is no modal, positionType if modal should be placed at position defined by postionType.x, positionType.y + collapsibleStore: Writable + collapsibleOption: Writable + lockedOption: Writable + editableOption: Writable + d3ZoomParameters: Writable<{ + [key: string]: number + }> + highlightEdgesOption: Writable } export interface PositionType { - x: number; - y: number; + x: number + y: number } export interface NodeType { - id: string; - width: number; - height: number; - positionX: number; - positionY: number; - bgColor: string; - data: { html?: any, custom?: { component: any, props?: any, cb?: (e: string, detail: any) => void }, img?: any, label?: string } - canvasId: string; - setPositionFromMovement: Function; - delete: Function; //This is the method to delete the node from the store - setSizeFromMovement: Function; - setExportableData: Function; - borderColor: string; - image: boolean; - src: string; - textColor: string; - borderRadius: number; - childNodes: string[]; - className: string; //This is for custom className for node - clickCallback: Function; // user-supplied callback that executes when the node is clicked + id: string + width: number + height: number + positionX: number + positionY: number + bgColor: string + data: { + html?: any + custom?: { component: any; props?: any; cb?: (e: string, detail: any) => void } + img?: any + label?: string + } + canvasId: string + setPositionFromMovement: Function + setSizeFromMovement: Function + setExportableData: Function + borderColor: string + image: boolean + src: string + textColor: string + borderRadius: number + childNodes: string[] + className: string //This is for custom className for node + clickCallback: Function // user-supplied callback that executes when the node is clicked } export interface EdgeType { - id: string; - sourceX: number; - sourceY: number; - targetX: number; - targetY: number; - canvasId: string; - label: string; - type: 'straight' | 'smoothstep' | 'step' | 'bezier'; - labelBgColor: string; - labelTextColor: string; - edgeColor: string; - animate: boolean; - noHandle: boolean; - arrow: boolean; - clickCallback: Function; - className: string; - delete: Function; - setExportableData: Function; + id: string + sourceX: number + sourceY: number + targetX: number + targetY: number + canvasId: string + label: string + type: 'straight' | 'smoothstep' | 'step' | 'bezier' + labelBgColor: string + labelTextColor: string + edgeColor: string + animate: boolean + noHandle: boolean + arrow: boolean + clickCallback: Function + className: string + delete: Function + setExportableData: Function } export interface PotentialAnchorType { - id: string; - nodeId: string; - callback: Function; // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better - positionX: number; - positionY: number; - angle: number; - canvasId: string; - delete: Function; + id: string + nodeId: string + callback: Function // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better + positionX: number + positionY: number + angle: number + canvasId: string + delete: Function } export interface TemporaryEdgeType { - id: string; - sourcePotentialAnchorId: string; // this will always be set - sourceX: number; - sourceY: number; - targetPotentialAnchorId: string | null; // this will be null until the temporary edge reaches another temporary anchor - targetX: number; - targetY: number; - canvasId: string; - type: string; - edgeColor: string; - createEdge: Function; - createNode: Function; + id: string + sourcePotentialAnchorId: string // this will always be set + sourceX: number + sourceY: number + targetPotentialAnchorId: string | null // this will be null until the temporary edge reaches another temporary anchor + targetX: number + targetY: number + canvasId: string + type: string + edgeColor: string + createEdge: Function + createNode: Function } diff --git a/frontend/tslint-imports.json b/frontend/tslint-imports.json new file mode 100644 index 0000000000..6441ae5574 --- /dev/null +++ b/frontend/tslint-imports.json @@ -0,0 +1,6 @@ +{ + "extends": ["tslint-etc"], + "rules": { + "no-unused-declaration": true + } +}