Skip to content
D
Documentation

DiagramEntity

reference
2 min readUpdated

Import it from @grafloria/engine.

ts
abstract class DiagramEntity

Properties

NameTypeDefaultDescription
idstring
uuidstring
versionnumber1
metadataMap<string, any>

Methods

  • constructor(id?: string, uuid?: string)
  • get isDirty(): boolean — Check if entity has been modified since last render
  • markDirty(reason?: string): void — Mark entity as dirty (needs re-render)
  • markClean(): void — Mark entity as clean (rendered)
  • getDirtyReasons(): string[] — Get reasons why entity is dirty
  • getDirtyTimestamp(): number | null — Get timestamp when entity was marked dirty
  • beginBatch(): void — Begin batch update Delays dirty marking until batch ends
  • endBatch(): void — End batch update Marks dirty once if any changes occurred
  • isBatching(): boolean — Check if currently in batch mode
  • reportRegisterWrite(property: string, oldValue: unknown, newValue: unknown): void — REPORT A REGISTER WRITE THE COLLAB REDUCER JUST PERFORMED.

trackChange() is protected, and applyOp lives outside the class — so every register the reducer wrote by plain field assignment (anything with no dedicated mutator: a link's style, flexConfig, a group's isCollapsed, layoutConfig, bounds, parentId, …) mutated the model WITHOUT passing the funnel.

That is not a cosmetic gap. UndoStack.undo() applies the inverse THROUGH THE MODEL WITH CAPTURE LIVE and then reads back whatever capture minted. No trackChange means no op, which means THE UNDO REACHES NOBODY. Measured before this existed:

link style execute → 1 op undo → 0 ops flexConfig execute → 1 op undo → 0 ops isCollapsed execute → 1 op undo → 0 ops position execute → 1 op undo → 1 op ← a real mutator: always worked

The author's own document was correct every time — which is exactly why this survived. It is the UpdateLinkStyleCommand defect (see style-undo.spec.ts) living one layer down, in the reducer, and hitting every mutator-less register at once.

So the reducer gets a seam onto the funnel rather than a copy of it. There is still ONE funnel; this is a door onto it, not a second source of truth.

  • on(event: string, handler: Function): () => void — Subscribe to changes
  • onPropertyChange(property: string, handler: (data: any) => void): () => void — Subscribe to property changes
  • getChangeLog(): ReadonlyArray<ChangeEntry> — Get change history
  • clearChangeLog(): void — Clear change history
  • getLabel(): string | undefined — CANONICAL label read. metadata.label wins; legacy data['label'] (on subclasses that carry a data bag) is honoured as a fallback so pre-canon documents and third-party writers still surface their labels everywhere — rendering, a11y names, and Mermaid export alike.
  • setLabel(label: string): void — CANONICAL label write. Writes metadata.label (tracked → dirty → repaint) and mirrors into the legacy data['label'] slot when the entity has a data bag, so any reader still on the old field sees the same value. The mirror is a shadow: metadata is authoritative on every read path.
  • setMetadata(key: string, value: any): void — Set metadata value
  • getMetadata(key: string): any — Get metadata value
  • deleteMetadata(key: string): void — Remove metadata
  • abstract serialize(): SerializedEntity — Serialize entity to JSON
  • isDisposed(): boolean — Check if entity has been disposed
  • dispose(): void — Dispose entity and clean up resources Prevents memory leaks by:
  • Removing all event listeners
  • Clearing change log
  • Clearing metadata
  • Marking as disposed

Was this page helpful?

DiagramEntity — Grafloria