@kumu/hydra
Advanced tools
Comparing version 0.0.0-kumu.12 to 0.0.0-kumu.13
@@ -946,5 +946,47 @@ import { AST as Selector } from 'parsel-js'; | ||
private invalidateValueCache; | ||
/** | ||
* Mapping for all the callbacks that have been registered for event emitters. | ||
*/ | ||
private eventCallbacks; | ||
/** | ||
* Emit an event from {@link HydraEventMap}. | ||
*/ | ||
private emit; | ||
/** | ||
* Listen for events on the core. Useful for syncing changes to internal | ||
* state out into an external app. | ||
* | ||
* @returns An unsubscribe function that will stop listening for this event. | ||
*/ | ||
on<E extends keyof HydraEventMap>(event: E, callback: (data: HydraEventMap[E]) => void): () => void; | ||
} | ||
/** | ||
* The typed mapping from event names to event payloads. | ||
*/ | ||
declare interface HydraEventMap { | ||
/** | ||
* Emitted whenever entities are added to the core. | ||
*/ | ||
"entities/added": Entity[]; | ||
/** | ||
* Emitted whenever entities are removed from the core. | ||
*/ | ||
"entities/removed": Entity[]; | ||
/** | ||
* Emitted whenever entities are grabbed as part of a gesture. | ||
*/ | ||
"entities/grabbed": Entity[]; | ||
/** | ||
* Emitted whenever entities are released (from being grabbed) as part of a | ||
* gesture. | ||
*/ | ||
"entities/released": Entity[]; | ||
/** | ||
* Emitted whenever the selected entities change. | ||
*/ | ||
"entities/selected": Entity[]; | ||
} | ||
/** | ||
* Invalidate the geometry of every element in a collection in dependent | ||
@@ -951,0 +993,0 @@ * order (nodes first, then edges, which rely on node positions). |
{ | ||
"name": "@kumu/hydra", | ||
"version": "0.0.0-kumu.12", | ||
"version": "0.0.0-kumu.13", | ||
"type": "module", | ||
@@ -5,0 +5,0 @@ "types": "./dist/index.d.ts", |
@@ -374,29 +374,82 @@ import { expect, test, vi } from "vitest"; | ||
// Bun doesn't provide `vi.fn`. | ||
{ | ||
test.skipIf(process.versions.bun)("add requests a restyle", () => { | ||
let core = new Core(); | ||
core.requestRestyle = vi.fn(); | ||
let a = new Node({ data: { id: "a" } }); | ||
core.add(a); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([a]); | ||
}); | ||
test("add requests a restyle", () => { | ||
let core = new Core(); | ||
core.requestRestyle = vi.fn(); | ||
let a = new Node({ data: { id: "a" } }); | ||
core.add(a); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([a]); | ||
}); | ||
test.skipIf(process.versions.bun)("move requests a restyle", () => { | ||
let a = new Node({ data: { id: "a" } }); | ||
let b = new Node({ data: { id: "b" } }); | ||
let ab = new Edge({ data: { id: "ab", source: "a", target: "b" } }); | ||
let core = new Core({ entities: [a, b, ab] }); | ||
core.requestRestyle = vi.fn(); | ||
core.move(ab, { source: "b" }); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([ab]); | ||
}); | ||
test("move requests a restyle", () => { | ||
let a = new Node({ data: { id: "a" } }); | ||
let b = new Node({ data: { id: "b" } }); | ||
let ab = new Edge({ data: { id: "ab", source: "a", target: "b" } }); | ||
let core = new Core({ entities: [a, b, ab] }); | ||
core.requestRestyle = vi.fn(); | ||
core.move(ab, { source: "b" }); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([ab]); | ||
}); | ||
test.skipIf(process.versions.bun)("setData requests a restyle", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
core.requestRestyle = vi.fn(); | ||
core.setData(a, { foo: "bar" }); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([a]); | ||
}); | ||
} | ||
test("setData requests a restyle", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
core.requestRestyle = vi.fn(); | ||
core.setData(a, { foo: "bar" }); | ||
expect(core.requestRestyle).toHaveBeenCalledWith([a]); | ||
}); | ||
test("adding entities emits an entities/added event", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core(); | ||
let callback = vi.fn(); | ||
core.on("entities/added", callback); | ||
core.add(a); | ||
expect(callback).toHaveBeenCalledWith([a]); | ||
}); | ||
test("removing entities emits an entities/removed event", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
let callback = vi.fn(); | ||
core.on("entities/removed", callback); | ||
core.remove(a); | ||
expect(callback).toHaveBeenCalledWith([a]); | ||
}); | ||
test("grabbing entities emits an entities/grabbed event", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
let callback = vi.fn(); | ||
core.on("entities/grabbed", callback); | ||
core.setGrabbedEntities([a]); | ||
expect(callback).toHaveBeenCalledWith([a]); | ||
}); | ||
test("releasing entities emits an entities/released event", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
let callback = vi.fn(); | ||
core.setGrabbedEntities([a]); | ||
core.on("entities/released", callback); | ||
core.setGrabbedEntities([]); | ||
expect(callback).toHaveBeenCalledWith([a]); | ||
}); | ||
test("selecting entities emits an entities/selected event", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [a] }); | ||
let callback = vi.fn(); | ||
core.on("entities/selected", callback); | ||
core.setSelectedEntities([a]); | ||
expect(callback).toHaveBeenCalledWith([a]); | ||
}); | ||
test("unsubscribing to events", () => { | ||
let a = new Node({ data: { id: "a", foo: "" } }); | ||
let core = new Core({ entities: [] }); | ||
let callback = vi.fn(); | ||
let unsubscribe = core.on("entities/added", callback); | ||
unsubscribe(); | ||
core.add(a); | ||
expect(callback).not.toHaveBeenCalled(); | ||
}); |
@@ -258,2 +258,6 @@ import { | ||
if (this.grabbedEntities.size > 0) { | ||
this.emit("entities/released", Array.from(this.grabbedEntities)); | ||
} | ||
for (let entity of entities) { | ||
@@ -267,2 +271,3 @@ entity.state.grabbed = true; | ||
this.requestRestyle(entitiesToRestyle); | ||
this.emit("entities/grabbed", Array.from(this.grabbedEntities)); | ||
} | ||
@@ -290,2 +295,3 @@ } | ||
this.requestRestyle(entitiesToRestyle); | ||
this.emit("entities/selected", Array.from(this.selectedEntities)); | ||
} | ||
@@ -355,2 +361,3 @@ } | ||
this.requestRestyle(entities); | ||
this.emit("entities/added", Array.from(entities)); | ||
} | ||
@@ -402,2 +409,3 @@ | ||
debug("removed", arrayOfRemovedEntities); | ||
this.emit("entities/removed", arrayOfRemovedEntities); | ||
@@ -661,5 +669,73 @@ return arrayOfRemovedEntities; | ||
} | ||
/** | ||
* Mapping for all the callbacks that have been registered for event emitters. | ||
*/ | ||
private eventCallbacks: { | ||
[K in keyof HydraEventMap]?: ((data: HydraEventMap[K]) => void)[]; | ||
} = {}; | ||
/** | ||
* Emit an event from {@link HydraEventMap}. | ||
*/ | ||
private emit<E extends keyof HydraEventMap>( | ||
event: E, | ||
data: HydraEventMap[E], | ||
) { | ||
let callbacks = this.eventCallbacks[event]; | ||
if (callbacks) { | ||
for (let callback of callbacks) { | ||
callback(data); | ||
} | ||
} | ||
} | ||
/** | ||
* Listen for events on the core. Useful for syncing changes to internal | ||
* state out into an external app. | ||
* | ||
* @returns An unsubscribe function that will stop listening for this event. | ||
*/ | ||
on<E extends keyof HydraEventMap>( | ||
event: E, | ||
callback: (data: HydraEventMap[E]) => void, | ||
) { | ||
this.eventCallbacks[event] ||= []; | ||
this.eventCallbacks[event]?.push(callback); | ||
return () => { | ||
this.eventCallbacks[event] = this.eventCallbacks[event]?.filter( | ||
(other) => other !== callback, | ||
); | ||
}; | ||
} | ||
} | ||
/** | ||
* The typed mapping from event names to event payloads. | ||
*/ | ||
interface HydraEventMap { | ||
/** | ||
* Emitted whenever entities are added to the core. | ||
*/ | ||
"entities/added": Entity[]; | ||
/** | ||
* Emitted whenever entities are removed from the core. | ||
*/ | ||
"entities/removed": Entity[]; | ||
/** | ||
* Emitted whenever entities are grabbed as part of a gesture. | ||
*/ | ||
"entities/grabbed": Entity[]; | ||
/** | ||
* Emitted whenever entities are released (from being grabbed) as part of a | ||
* gesture. | ||
*/ | ||
"entities/released": Entity[]; | ||
/** | ||
* Emitted whenever the selected entities change. | ||
*/ | ||
"entities/selected": Entity[]; | ||
} | ||
/** | ||
* Because `requestRestyle` is an asynchronous operation (the restyle happens | ||
@@ -666,0 +742,0 @@ * during the next microtask), it's possible to remove entities from the graph |
@@ -18,3 +18,2 @@ /** | ||
EdgeStyleRule, | ||
ValueType, | ||
DataValue, | ||
@@ -24,2 +23,4 @@ ScaleValue, | ||
export { ValueType } from "./style"; | ||
export type { Selector } from "./selectors"; | ||
@@ -26,0 +27,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
645923
11830