b9e42c3ef4
Build and Deploy / build-and-deploy (push) Has been cancelled
- lit-force-graph: Refactored for instance reuse, better lifecycle management, and added dynamic imports for AR/VR modes. - lit-html-table: Improved visual design, readability of headers, and fixed reactivity in editable mode. - lit-draggable-dom: Fixed clipping issues by switching to relative/absolute positioning and resolved a panning speed bug caused by CSS variable inheritance.
146 lines
2.9 KiB
TypeScript
146 lines
2.9 KiB
TypeScript
/**
|
|
* Node or Edge ID type
|
|
*/
|
|
export type ID = string;
|
|
|
|
/**
|
|
* Node store with NodeEdges
|
|
*/
|
|
export class Store<T extends BaseNode> {
|
|
nodes: T[] = [];
|
|
edges: NodeEdge[] = [];
|
|
|
|
/**
|
|
* Parse a JSON object into a Store
|
|
*
|
|
* @param value JSON String
|
|
* @returns Store
|
|
*/
|
|
static fromJson<T extends BaseNode>(value: string) {
|
|
const { nodes, edges } = JSON.parse(value);
|
|
const store = new Store<T>();
|
|
if (nodes) store.nodes = nodes;
|
|
if (edges) store.edges = edges;
|
|
return store;
|
|
}
|
|
|
|
/**
|
|
* Save the store to a JSON string
|
|
*
|
|
* @returns JSON String
|
|
*/
|
|
toJson() {
|
|
const value = {
|
|
nodes: this.nodes,
|
|
edges: this.edges,
|
|
};
|
|
return JSON.stringify(value, null, 2);
|
|
}
|
|
|
|
private getNodeIndex(id: ID): number {
|
|
return this.nodes.findIndex((n) => n.id === id);
|
|
}
|
|
|
|
createNode(node: T): void {
|
|
const index = this.getNodeIndex(node.id);
|
|
if (index === -1) {
|
|
this.nodes.push(node);
|
|
} else {
|
|
this.nodes[index] = node;
|
|
}
|
|
}
|
|
|
|
retrieveNode(id: ID): T | undefined {
|
|
return this.nodes.find((node) => node.id === id);
|
|
}
|
|
|
|
retrieveEdgesForNode(id: ID): NodeEdge[] {
|
|
return this.edges.filter(
|
|
(edge) => edge.startNode === id || edge.endNode === id
|
|
);
|
|
}
|
|
|
|
updateNode(node: T): void {
|
|
const index = this.getNodeIndex(node.id);
|
|
if (index === -1) return;
|
|
this.nodes[index] = node;
|
|
}
|
|
|
|
deleteNode(id: ID): void {
|
|
const index = this.getNodeIndex(id);
|
|
if (index === -1) return;
|
|
this.nodes.splice(index, 1);
|
|
|
|
// Delete connected edges
|
|
const nodeEdges = this.retrieveEdgesForNode(id);
|
|
for (const edge of nodeEdges) {
|
|
this.deleteEdge(edge.id);
|
|
}
|
|
}
|
|
|
|
private getEdgeIndex(id: ID): number {
|
|
return this.edges.findIndex((n) => n.id === id);
|
|
}
|
|
|
|
createEdge(edge: NodeEdge): void {
|
|
const index = this.getEdgeIndex(edge.id);
|
|
if (index === -1) {
|
|
this.edges.push(edge);
|
|
} else {
|
|
this.edges[index] = edge;
|
|
}
|
|
}
|
|
|
|
retrieveEdge(id: ID): NodeEdge | undefined {
|
|
return this.edges.find((edge) => edge.id === id);
|
|
}
|
|
|
|
updateEdge(edge: NodeEdge): void {
|
|
const index = this.getEdgeIndex(edge.id);
|
|
if (index === -1) return;
|
|
this.edges[index] = edge;
|
|
}
|
|
|
|
deleteEdge(id: ID): void {
|
|
const index = this.getEdgeIndex(id);
|
|
if (index === -1) return;
|
|
this.edges.splice(index, 1);
|
|
}
|
|
|
|
linkNodes(start: T, end: T, name: string): void {
|
|
const randomId = generateRandomId();
|
|
this.createEdge({
|
|
id: randomId,
|
|
startNode: start.id,
|
|
endNode: end.id,
|
|
name,
|
|
type: "edge",
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Node Edge
|
|
*/
|
|
export interface NodeEdge extends BaseNode {
|
|
startNode: ID;
|
|
endNode: ID;
|
|
type: "edge";
|
|
}
|
|
|
|
/**
|
|
* Base Node
|
|
*/
|
|
export interface BaseNode {
|
|
id: ID;
|
|
name: string;
|
|
readonly type: "node" | "edge";
|
|
}
|
|
|
|
function generateRandomId(): string {
|
|
return (
|
|
Math.random().toString(36).substring(2, 15) +
|
|
Math.random().toString(36).substring(2, 15)
|
|
);
|
|
}
|