Created
January 17, 2025 16:47
-
-
Save jobelenus/1b3a3992c0912e0db9988329a51ec797 to your computer and use it in GitHub Desktop.
Graph Vue Store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { defineStore } from "pinia"; | |
import { addStoreHooks } from "@si/vue-lib/pinia"; | |
import { reactive } from "vue"; | |
import { ChangeSetId } from "@/api/sdf/dal/change_set"; | |
import { useWorkspacesStore } from "../workspaces.store"; | |
import { useChangeSetsStore } from "../change_sets.store"; | |
import { | |
Edge, | |
RootComponent, | |
Ulid, | |
kindsToRoots, | |
Node, | |
EdgeWithoutId, | |
Neighbors, | |
EdgeKind, | |
UniqueEdgeKind, | |
ASYMETRIC_EDGE_KINDS, | |
} from "./roots"; | |
export const useViewsStore = (forceChangeSetId?: ChangeSetId) => { | |
const workspacesStore = useWorkspacesStore(); | |
const workspaceId = workspacesStore.selectedWorkspacePk; | |
const changeSetsStore = useChangeSetsStore(); | |
let changeSetId: ChangeSetId | undefined; | |
if (forceChangeSetId) { | |
changeSetId = forceChangeSetId; | |
} else { | |
changeSetId = changeSetsStore.selectedChangeSetId; | |
} | |
return addStoreHooks( | |
workspaceId, | |
changeSetId, | |
defineStore( | |
`ws${workspaceId || "NONE"}/cs${changeSetId || "NONE"}/pool`, | |
() => { | |
const pool = reactive({ | |
nodes: new Map<Ulid, Node>(), | |
edges: new Map<Ulid, Edge>(), | |
}); | |
const addNodeToPool = (node: Node) => { | |
pool.nodes.set(node.id, node); | |
addNodeOnGraph(node); | |
const root = kindsToRoots.get(node.kind); | |
if (!root) throw new Error(`Root node not found for ${node}`); | |
addEdgeOnGraph("root", root, node); | |
}; | |
// add all root nodes | |
addNodeToPool(RootComponent); | |
const setEdgeId = (edge: EdgeWithoutId): Edge => { | |
edge.id = `${edge.kind}_${edge.fromId}_${edge.toId}`; // i dont like how to/from order matters | |
return edge as Edge; | |
}; | |
const addEdgeToPool = (e: EdgeWithoutId) => { | |
const edge = setEdgeId(e); | |
pool.edges.set(edge.id, edge); | |
const node1 = pool.nodes.get(edge.fromId); | |
const node2 = pool.nodes.get(edge.toId); | |
if (!node1) throw new Error(`{edge.fromId} node not found`); | |
if (!node2) throw new Error(`{edge.toId} node not found`); | |
addEdgeOnGraph(edge.kind, node1, node2); | |
}; | |
const deleteEdgeFromPool = (e: EdgeWithoutId) => { | |
const edge = setEdgeId(e); | |
pool.edges.delete(edge.id); | |
const node1 = pool.nodes.get(e.fromId); | |
const node2 = pool.nodes.get(e.toId); | |
if (!node1) throw new Error(`{edge.fromId} node not found`); | |
if (!node2) throw new Error(`{edge.toId} node not found`); | |
getNeighborsForKind(node1, edge.kind)?.delete(node2); | |
const asymetricKind = ASYMETRIC_EDGE_KINDS[edge.kind]; | |
getNeighborsForKind(node2, asymetricKind ?? edge.kind)?.delete(node1); | |
}; | |
const deleteNodeFromPool = (node: Node) => { | |
const neighbors = getNeighbors(node); | |
pool.nodes.delete(node.id); | |
neighbors?.forEach((forEdge, edgeKind) => { | |
const asymetricKind = ASYMETRIC_EDGE_KINDS[edgeKind]; | |
forEdge.forEach((n) => { | |
getNeighborsForKind(n, asymetricKind ?? edgeKind)?.delete(node); | |
}); | |
}); | |
}; | |
const updateNodeInPool = (updatedNode: Node) => { | |
const node = pool.nodes.get(updatedNode.id); | |
if (!node) throw new Error(`${updatedNode.id} not found in pool`); | |
Object.assign(node, updatedNode); // reference remains in place (e.g. no new object created) | |
}; | |
const adjacencyList = reactive(new Map<Node, Neighbors>()); | |
const addNodeOnGraph = (node: Node) => { | |
adjacencyList.set(node, new Map()); | |
}; | |
const getNeighbors = (node: Node) => { | |
return adjacencyList.get(node); | |
}; | |
const getNeighborsForKind = (node: Node, kind: EdgeKind) => { | |
return adjacencyList.get(node)?.get(kind); | |
}; | |
const getEdgeKinds = (neighbors: Neighbors, kind: EdgeKind) => { | |
let n = neighbors.get(kind); | |
if (!n) { | |
n = new Set<Node>(); | |
neighbors.set(kind, n); | |
} | |
return n; | |
}; | |
const _addEdge = (kind: EdgeKind, node1: Node, node2: Node) => { | |
const neighbors = getNeighbors(node1); | |
if (!neighbors) throw new Error(`node1 ${node1} does not exist`); | |
const forEdge = getEdgeKinds(neighbors, kind); | |
forEdge.add(node2); | |
}; | |
const addEdgeOnGraph = (kind: EdgeKind, from: Node, to: Node) => { | |
const asymetricKind = ASYMETRIC_EDGE_KINDS[kind]; | |
_addEdge(kind, from, to); | |
_addEdge(asymetricKind ?? kind, to, from); | |
}; | |
// unique edges are not bi-directional | |
const addUniqueEdgeOnGraph = ( | |
kind: UniqueEdgeKind, | |
from: Node, | |
to: Node, | |
) => { | |
getNeighborsForKind(from, kind)?.clear(); | |
_addEdge(kind, from, to); | |
}; | |
const hasEdgeKind = ( | |
node1: Node, | |
node2: Node, | |
kind: EdgeKind, | |
): boolean => { | |
return !!getNeighborsForKind(node1, kind)?.has(node2); | |
}; | |
const onActivated = () => { | |
// TODO subscribe | |
}; | |
return { | |
pool, | |
adjacencyList, | |
addNodeToPool, | |
addEdgeToPool, | |
deleteNodeFromPool, | |
deleteEdgeFromPool, | |
addUniqueEdgeOnGraph, | |
updateNodeInPool, | |
getNeighbors, | |
getNeighborsForKind, | |
getEdgeKinds, | |
hasEdgeKind, | |
onActivated, | |
}; | |
}, | |
), | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type Version = number; | |
export type Ulid = string; | |
export type NodeKind = string; | |
export type EdgeKind = string; | |
export type UniqueEdgeKind = string; | |
export type Node = { | |
id: Ulid; | |
kind: NodeKind; | |
v: Version; | |
}; | |
export type EdgeWithoutId = { | |
id?: string; | |
toKind: NodeKind; | |
toId: Ulid; | |
fromId: Ulid; | |
fromKind: NodeKind; | |
kind: EdgeKind; | |
v: Version; | |
}; | |
export type Edge = Required<EdgeWithoutId>; | |
export type Neighbors = Map<EdgeKind, Set<Node>>; | |
export const RootComponent: Node = { | |
id: "RootComponent", | |
kind: "RootComponent", | |
v: 1, | |
}; | |
export const kindsToRoots: Map<NodeKind, Node> = new Map(); | |
kindsToRoots.set("Component" as NodeKind, RootComponent); | |
export const ASYMETRIC_EDGE_KINDS: Record<EdgeKind, EdgeKind> = { | |
FrameContains: "FrameChild", | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment