Import these from @grafloria/element.
On their own pages
JoinColumn: Join guidance — live "which column should I join to?" scoring + tinting for
Functions
addColumnAt
Append a column (immutably) — returns the new column array.
tsfunction addColumnAt(columns: ErColumn[], column: ErColumn, at = columns.length): ErColumn[]
assignTiers
Tier every candidate score at once: the FIRST candidate holding the maximum
score is top when that maximum is >= 2 — exactly one gold row per drag.
tsfunction assignTiers(scores: number[]): MatchTier[]
bindCardEditing
Wire the editing gestures onto a kit container. Idempotent per container
(a re-bind disposes the previous one), matching bindRowInteractions.
tsfunction bindCardEditing(api: EditApi): CardEditingHandle
bindJoinGuidance
Bind live join guidance to a diagram instance. Listens to the engine's connection lifecycle; while a drag is live, every kit-card row on every other table wears its tier class (and the best one its "★ BEST" chip), and the matching port glyphs glow via a generated stylesheet. Everything clears when the drag ends, however it ends.
tsfunction bindJoinGuidance(api: JoinGuidanceApi, options: JoinGuidanceOptions = {}): JoinGuidanceHandle
ensureDiagramKitStyles
Inject the kit stylesheet once. Safe to call repeatedly and in SSR.
tsfunction ensureDiagramKitStyles(doc: Document | undefined = typeof document !== 'undefined' ? document : undefined): void
ensureJoinGuidanceStyles
Inject the guidance tint stylesheet once. Safe to call repeatedly / in SSR.
tsfunction ensureJoinGuidanceStyles(
doc: Document | undefined = typeof document !== 'undefined' ? document : undefined
): void
erDiagram
Build a render() spec for an ER diagram.
Entities become HTML table cards; relationships become orthogonal edges with
crow's-foot cardinality. A TABLE.column end pins the edge to that row via
an absolute-layout port (the FK→PK look). Two edges landing on the same
column are automatically spread apart.
tsfunction erDiagram(options: ErDiagramOptions): {
nodes: Array<Record<string, unknown>>;
edges: Array<Record<string, unknown>>;
finalize: (api: unknown) => void;
}
erRowCenterY
Node-local y of a row's centre (the +1 offsets past the card's top border).
tsfunction erRowCenterY(rowIndex: number): number
erTable
Typed handle for a kit ER table. Throws for unknown ids / non-kit nodes.
tsfunction erTable(api: HandleApi, id: string): ErTable
erTables
Every kit ER table in the diagram, as handles.
tsfunction erTables(api: HandleApi): ErTable[]
matchColumns
Match old columns to new columns for port reconciliation. Returns a map from OLD index → NEW index; an old index absent from the map is a REMOVED column (its pinned ports and their edges are dropped).
Three passes, most-certain first, so a single edit is always read the way a human means it:
- object identity — reorder / add / remove that preserved references;
- name — a retype (same name, new object) or a rebuilt array;
- positional remainder — the leftovers matched in order, which is exactly what a rename is (one old + one new in the same slot). Without pass 3 a rename reads as remove+add and the edge is severed.
tsfunction matchColumns(oldCols: ErColumn[], newCols: ErColumn[]): Map<number, number>
matchTier
Per-score tier (rank-free half; assignTiers promotes the single best to top).
tsfunction matchTier(score: number): Exclude<MatchTier, 'top'>
removeColumnAt
Remove the column at index — returns the new column array.
tsfunction removeColumnAt(columns: ErColumn[], index: number): ErColumn[]
renameColumnAt
Rename the column at index — returns a new array with a NEW object there.
tsfunction renameColumnAt(columns: ErColumn[], index: number, name: string): ErColumn[]
rowIndexFromY
Inverse of {@link erRowCenterY}: which column row a pinned port sits on.
tsfunction rowIndexFromY(y: number): number
scoreMatch
Score a candidate join between two columns. Pure; the exact production logic. Higher is better; 0 means "no reason to join these".
tsfunction scoreMatch(a: JoinEnd, b: JoinEnd): 0 | 1 | 2 | 3
singularize
Naive English singular — enough for schema names (orders, categories, statuses).
tsfunction singularize(word: string): string
umlClass
Typed handle for a kit UML class. Throws for unknown ids / non-kit nodes.
tsfunction umlClass(api: HandleApi, id: string): UmlClass
umlClasses
Every kit UML class in the diagram, as handles.
tsfunction umlClasses(api: HandleApi): UmlClass[]
umlDiagram
Build a render() spec for a UML class diagram. Call spec.finalize(api)
after render() — multiplicity chips are positioned labels, which only the
live model's link.addLabel can express.
tsfunction umlDiagram(options: UmlDiagramOptions): {
nodes: Array<Record<string, unknown>>;
edges: Array<Record<string, unknown>>;
finalize: (api: unknown) => void;
}
updateClass
Edit a rendered UML class in place. delta may change the name, stereotype,
abstract flag, attributes, methods, and width/height. One undoable step.
tsfunction updateClass(api: KitApi, classId: string, delta: UmlClassDelta): Promise<boolean>
updateEntity
Edit a rendered ER table in place. delta may change the name, the column
list (add / remove / reorder / rename / retype), and the width/height. Field
ports and their edges are reconciled so the diagram stays correct, as ONE
undoable step.
tsfunction updateEntity(api: KitApi, entityId: string, delta: ErEntityDelta): Promise<boolean>
Returns whether the node existed and the edit was applied.
Classes
CardHandle
tsabstract class CardHandle
Methods
constructor( protected readonly api: HandleApi, readonly id: string )get exists(): booleanget node(): unknown— Escape hatch to the underlying NodeModel (typed loosely on purpose).get width(): numberget height(): numberabstract get name(): stringabstract rename(name: string): Promise<boolean>— Rename the card title — one undoable step.abstract resize(size: { width?: number; height?: number }): Promise<boolean>— Resize the card — one undoable step (kit body scrolls when capped).select(): voidasync undo(): Promise<void>async redo(): Promise<void>onRowSelect( cb: (e: { field: ErField | null; selected: RowRef | null }) => void ): () => void— Row-selection events for THIS card only (the kit'saxk:row-select, filtered). ER cards resolve the selection to a typed {@link ErField} when possible. Returns an unbind function.
ErColumnList
The columns collection of an {@link ErTable}. Iterable of {@link ErField}.
tsclass ErColumnList implements Iterable<ErField>
Methods
constructor(private readonly table: ErTable)get length(): numbernames(): string[]at(index: number): ErField | undefinedget(name: string): ErField | undefined[Symbol.iterator](): Iterator<ErField>add(column: ErColumn, opts: { at?: number } = {}): Promise<boolean>removeAt(index: number): Promise<boolean>renameAt(index: number, name: string): Promise<boolean>patchAt(index: number, patch: Partial<ErColumn>): Promise<boolean>move(from: number, to: number): Promise<boolean>
ErField
A column of an {@link ErTable} — itself just (table, name): stateless.
tsclass ErField
Methods
constructor( readonly table: ErTable, private readonly columnName: string )get exists(): booleanget name(): stringget index(): numberget type(): string | undefinedget pk(): booleanget fk(): booleanrename(name: string): Promise<boolean>— Rename — the field port keeps its id, so attached edges stay glued.setType(type: string): Promise<boolean>setKeys(keys: { pk?: boolean; fk?: boolean }): Promise<boolean>remove(): Promise<boolean>— Remove this column (its ports and attached edges are dropped).
ErTable
tsclass ErTable extends CardHandle
Properties
| Name | Type | Default | Description |
|---|---|---|---|
columns |
Methods
get spec(): ErEntitySpec— A deep COPY of the stored entity spec — never a live reference.get name(): stringrename(name: string): Promise<boolean>— Rename the card title — one undoable step.resize(size: { width?: number; height?: number }): Promise<boolean>— Resize the card — one undoable step (kit body scrolls when capped).update(delta: Parameters<typeof updateEntity>[2]): Promise<boolean>— The raw delta path — everything above funnels through here.
UmlClass
tsclass UmlClass extends CardHandle
Properties
| Name | Type | Default | Description |
|---|---|---|---|
attributes | |||
methods |
Methods
get spec(): UmlClassSpec— A deep COPY of the stored class spec — never a live reference.get name(): stringget abstract(): booleanget stereotype(): string | undefinedrename(name: string): Promise<boolean>— Rename the card title — one undoable step.resize(size: { width?: number; height?: number }): Promise<boolean>— Resize the card — one undoable step (kit body scrolls when capped).setAbstract(abstract: boolean): Promise<boolean>setStereotype(stereotype: string | undefined): Promise<boolean>update(delta: Parameters<typeof updateClass>[2]): Promise<boolean>
UmlMemberList
attributes / methods of a {@link UmlClass} (members are plain strings).
tsclass UmlMemberList
Methods
constructor( private readonly cls: UmlClass, private readonly kind: 'attributes' | 'methods' )list(): string[]get length(): numberat(index: number): string | undefinedadd(member: string, opts: { at?: number } = {}): Promise<boolean>removeAt(index: number): Promise<boolean>renameAt(index: number, member: string): Promise<boolean>
Constants
DIAGRAM_KIT_STYLE_ID
Diagram-kit stylesheet — injected once, on first use of any kit builder.
Everything is prefixed axk- and the selection overrides are scoped with
:has(...) to kit cards only, so embedding the kit can never restyle a
host's own nodes. The rules encode the lessons the diagrams/* demos learned
the hard way:
- the card fills the node and draws the ONLY border (the node's own rect is
hidden by the builders, and suppressed again on selection because the
theme paints
.selectedwith an accent stroke that overrides inline transparency); - the default
.selection-highlightoutline (a dashed rect a few px OUTSIDE the node) reads as a second floating box around a bordered card — the kit hides it and rings the card itself instead.
tsconst DIAGRAM_KIT_STYLE_ID: "grafloria-diagram-kit-styles"
ER_HEAD_H
tsconst ER_HEAD_H: 28
ER_ROW_H
Row height / header height of the entity card — sizing is derived from these.
tsconst ER_ROW_H: 25
JOIN_GUIDANCE_STYLE_ID
tsconst JOIN_GUIDANCE_STYLE_ID: "grafloria-join-guidance-styles"
Interfaces
CardEditingHandle
tsinterface CardEditingHandle
Members
dispose(): void
ErColumn
tsinterface ErColumn
Properties
| Name | Type | Default | Description |
|---|---|---|---|
name | string | ||
type? | string | ||
pk? | boolean | ||
fk? | boolean |
ErDiagramOptions
tsinterface ErDiagramOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
entities | ErEntitySpec[] | ||
relationships? | ErRelationshipSpec[] | ||
rowSelection? | boolean | Rows are selectable by default: click a column to select it (painted with .axk-row-selected; axk / axk CustomEvents fire on the container). Set false to opt out. | |
editable? | boolean | In-canvas editing (opt-in, default false — read-only diagrams are unchanged). When true the card grows editing chrome: double-click the header to rename the table, double-click a column name to rename it, an "add column" affordance and a per-row delete control. Every change routes through {@link updateEntity } as ONE undoable step. |
ErEntityDelta
What an ER table edit can change. Omitted fields are left as they were.
tsinterface ErEntityDelta
Properties
| Name | Type | Default | Description |
|---|---|---|---|
name? | string | ||
width? | number | ||
height? | number | ||
columns? | ErColumn[] | The NEW full column list. Survivors are matched to the old columns to move their ports (object identity → name → positional remainder), so a rename, a retype, a reorder, an insert and a delete are all understood without the caller preserving object references. See {@link matchColumns}. |
ErEntitySpec
tsinterface ErEntitySpec
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | ||
name? | string | Header text. Defaults to the id. | |
columns | ErColumn[] | ||
position? | { x: number; y: number } | ||
width? | number | ||
height? | number | Fixed card height. When smaller than the computed height the column list SCROLLS (the kit body is overflow-y, and the canvas yields the wheel to it). Omit for auto-height from the column count. |
ErRelationshipSpec
tsinterface ErRelationshipSpec
Properties
| Name | Type | Default | Description |
|---|---|---|---|
from | string | Entity id, or ENTITY.column to attach at that column's row. | |
to | string | ||
label? | string | ||
cardinality? | ErCardinality | { tail: string; head: string } | Named cardinality (default one-to-many) or explicit marker types. | |
fromSide? | ErSide | ||
toSide? | ErSide | ||
color? | string | ||
id? | string |
HandleApi
The slice of DiagramInstance the handles need (same shape update.ts uses).
tsinterface HandleApi
Properties
| Name | Type | Default | Description |
|---|---|---|---|
container | HTMLElement |
Members
getModel(): { getNode(id: string): | { getMetadata?(key: string): unknown; size: { width: number; height: number }; state?: { selected?: boolean }; } | undefined; selectNode?(node: unknown): void; }getEngine?(): { undo(): Promise<void> | void; redo(): Promise<void> | void } | undefinedrenderNow?(): void
JoinEnd
One end of a candidate join: a table (node) and one of its columns.
tsinterface JoinEnd
Properties
| Name | Type | Default | Description |
|---|---|---|---|
table | string | ||
column | JoinColumn |
JoinGuidanceApi
The slice of DiagramInstance the binding needs (matches HandleApi's shape).
tsinterface JoinGuidanceApi
Properties
| Name | Type | Default | Description |
|---|---|---|---|
container | HTMLElement |
Members
getModel(): { getNode(id: string): NodeLike | undefined; getNodes?(): NodeLike[]; }getEngine?(): | { eventBus?: { on(event: string, handler: (data: unknown) => void): () => void } } | undefined
JoinGuidanceHandle
tsinterface JoinGuidanceHandle
Members
activeTiers(): Map<string, Map<number, MatchTier>>— nodeId → rowIndex → tier for the drag in progress (empty when idle).dispose(): void
JoinGuidanceOptions
tsinterface JoinGuidanceOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
resolvePort? | (portId: string, nodeId: string | undefined) | Resolve a PORT id to its table column. The default understands both the ER kit's convention (TABLE__col__side__n) and the query-builder dot convention (table.col-in / table.col-out), checked against the node's actual kitEntity columns so an ambiguous name never mis-resolves. | |
chipText? | string | Text of the gold chip on the best row. Default '★ BEST'. | |
portCss? | (tier: MatchTier, selector: string) | Extra per-tier CSS for the target columns' PORT glyphs, injected per drag. Default paints the production glows (gold/green/blue drop-shadows). |
UmlClassDelta
What a UML class edit can change.
tsinterface UmlClassDelta
Properties
| Name | Type | Default | Description |
|---|---|---|---|
name? | string | ||
stereotype? | string | ||
abstract? | boolean | ||
width? | number | ||
height? | number | ||
attributes? | string[] | ||
methods? | string[] |
UmlClassSpec
tsinterface UmlClassSpec
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | ||
name? | string | Displayed name. Defaults to the id. | |
stereotype? | string | Renders as «stereotype» above the name (e.g. 'interface', 'abstract', 'enum'). | |
abstract? | boolean | Italicises the name (also set automatically for stereotype 'abstract'/'interface'). | |
attributes? | string[] | ||
methods? | string[] | ||
position? | { x: number; y: number } | ||
width? | number | ||
height? | number | Fixed card height — smaller than the content makes the compartments scroll. |
UmlDiagramOptions
tsinterface UmlDiagramOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
classes | UmlClassSpec[] | ||
relationships? | UmlRelationshipSpec[] | ||
rowSelection? | boolean | Members are selectable by default: click one to select it (painted with .axk-row-selected; axk / axk CustomEvents fire on the container). Set false to opt out. | |
editable? | boolean | In-canvas editing (opt-in, default false). When true: double-click the class name to rename it, double-click a member to rename it, an "add" affordance per compartment and a per-member delete control — all routed through {@link updateClass } as one undoable step. |
UmlRelationshipSpec
tsinterface UmlRelationshipSpec
Properties
| Name | Type | Default | Description |
|---|---|---|---|
from | string | ||
to | string | ||
kind? | UmlRelationKind | Default 'association'. | |
label? | string | ||
multiplicity? | [string, string] | Multiplicity / role chips at the [from, to] ends (e.g. ['0..*', '1']). | |
fromSide? | UmlSide | ||
toSide? | UmlSide | ||
id? | string |
Types
ErCardinality
tstype ErCardinality =
| 'one-to-many'
| 'one-to-one'
| 'many-to-many'
| 'one-to-zero-or-many'
| 'one-to-one-or-many';
ErSide
tstype ErSide = 'left' | 'right' | 'top' | 'bottom';
MatchTier
tstype MatchTier = 'top' | 'good' | 'ok' | 'none';
UmlRelationKind
tstype UmlRelationKind =
| 'inheritance'
| 'realization'
| 'association'
| 'directed-association'
| 'aggregation'
| 'composition'
| 'dependency';
UmlSide
tstype UmlSide = 'left' | 'right' | 'top' | 'bottom';
Was this page helpful?