Skip to content
D
Documentation

GroupModel

reference
4 min readUpdated

Import it from @grafloria/engine.

ts
class GroupModel extends DiagramEntity

Properties

NameTypeDefaultDescription
namestring
membersSet<string>
isCollapsedbooleanfalse
bounds?{ x: number; y: number; width: number; height: number }
layoutTypeLayoutType'none'
layoutConfig?LayoutConfig
position{ x: number; y: number }
size?{ width: number; height: number; depth: number }
parentGroupId?string
memberValidation?MemberValidation
isHoveredbooleanfalse
padding?GroupPadding
headerHeight
zIndex0
fitModeGroupFitMode'exact'
constrainChildrenfalse
collapsedState?CollapsedState
subgraphLayout?SubgraphGroupConfig
laneConfig?LaneConfig
membershipRule?MembershipRule
capacity?number

Methods

  • constructor(config: { id?: string; name: string })
  • canAddMember(candidateId: string, diagram?: DiagramModel): boolean — Whether candidateId may legally join this group. Rejects self-membership, ancestor cycles (adding an ancestor group as a member would create a containment loop), and candidates failing the per-group memberValidation predicate. Node candidates only run the predicate check (nodes can't form group cycles).
  • getWipState(): { count: number; capacity?: number; state: 'unlimited' | 'under' | 'full' | 'over' } — WIP state for the capacity limit. 'under' = room to spare, 'full' = exactly at the limit (the visual warning threshold), 'over' = past it (only reachable by lowering capacity below the current count). Returns 'unlimited' when no capacity is set.
  • isOverCapacity(): boolean — True when at or beyond the capacity limit (warning state).
  • setCapacity(capacity: number | undefined): void — Set (or clear) the capacity / WIP limit, tracked for undo/diff.
  • addMember(entityId: string, diagram?: DiagramModel): void — Add member to group.

When the member is itself a group, this establishes containment by setting the child's parentGroupId (detaching it from any previous parent), which keeps the nesting tree consistent for getAncestors/getDescendants. Members that would create a cycle or fail memberValidation are rejected (no-op).

  • removeMember(entityId: string, diagram?: DiagramModel): boolean — Remove member from group. When the member is a group whose parent is this group, its parentGroupId back-pointer is cleared so the nesting tree stays consistent.
  • setParent(newParentId: string | undefined, diagram?: DiagramModel): boolean — Reparent this group under newParentId (or detach when undefined), keeping both the parentGroupId pointer and the parents' members sets consistent. Rejects cycles (a group cannot be nested under one of its own descendants) and returns false without mutating anything.
  • setHovered(hovered: boolean): void — Set transient drag-hover highlight state and notify listeners. Renderers/canvas can subscribe to 'hover
    ' to outline a drop target.
  • expand(): void — Expand the group
  • collapse(): void — Collapse the group
  • setCollapsedState(state: CollapsedState | undefined): void — Set (or clear) the reversible collapse snapshot. Tracked as a change so the incremental diff-capture serializes it and undo/redo see it.
  • setLayout(type: 'flexbox', config: FlexboxLayoutConfig): void — Set layout configuration
  • setLayout(type: 'grid', config: GridLayoutConfig): void — Set layout configuration
  • setLayout(type: 'flexbox' | 'grid', config: LayoutConfig): void — Set layout configuration
  • clearLayout(): void — Clear layout configuration
  • getLayout(): { type: LayoutType; config?: LayoutConfig } — Get layout configuration
  • hasLayout(): boolean — Check if group has layout configured
  • getFlexboxLayout(): FlexboxLayoutConfig — Get layout as flexbox config
  • getGridLayout(): GridLayoutConfig — Get layout as grid config
  • calculateBounds(diagram: DiagramModel): void — Calculate bounds from member nodes using global bounds
  • getPadding(): { top: number; right: number; bottom: number; left: number } — Resolve {@link padding} to a full four-sided rectangle. undefined (the author never said) resolves to {@link DEFAULT_GROUP_PADDING}; an explicit value — including the 0 that loaders pin for legacy documents — is taken as written.
  • getOuterBounds(): GroupRect — The group's outer frame rectangle in world coordinates. Prefers explicit geometry (position + size) and falls back to the derived member bounds, matching how GroupMembershipService hit-tests a group.
  • getInnerBounds(): GroupRect — The rectangle members must stay inside: the outer frame minus padding and minus the header band (which is reserved at the top). Never returns a negative width/height.
  • fitToContents(diagram?: DiagramModel, options?: FitToContentsOptions): void — Auto-fit the group frame to its members' bounding box plus padding and the header band. This is the real consumer of padding/headerHeight that calculateBounds (a tight, padding-free box) never was.

Member groups contribute their own OUTER frame (so a parent fits around a nested group's full extent, not just its raw member points). With deepRecursive, descendant groups are fitted first (deepest first) so the parent fits around already-fitted children.

The computed content rectangle is reconciled with the current frame per the effective fit mode (grow-only / shrink-only / exact) and written to both position/size (authoritative geometry) and bounds (hit-test rect). No-op with no positioned members (nothing to fit around).

  • setFrame(rect: GroupRect): void — Write a world rectangle into the group's authoritative geometry (position + size) and the derived hit-test bounds, keeping the existing depth when present. Tracks a single 'bounds' change for undo/diff.
  • clampChildToExtent(nodeId: string, diagram?: DiagramModel): boolean — Clamp a member node so it stays fully inside the group's inner extent. No-op unless constrainChildren is set and the node is a direct member. Returns true when the node was actually moved. Absolute-coordinate model: this adjusts the node's world position directly.
  • restoreGeometry(geo: { position: { x: number; y: number }; size?: { width: number; height: number; depth: number }; bounds?: GroupRect; }): void — Restore raw geometry (position + optional size + bounds) captured before a collapse. Unlike setFrame this permits size === undefined so a group that had no explicit frame is restored to exactly that.
  • setZIndex(z: number): void — Set the group's stacking index (lower renders further back).
  • bringToFront(diagram?: DiagramModel): void — Bring this group in front of every other group (highest zIndex + 1). Minimal z-order API — the renderer honors zIndex ordering.
  • sendToBack(diagram?: DiagramModel): void — Send this group behind every other group (lowest zIndex - 1).
  • isAutoLayoutEnabled(): boolean — Is push-driven reflow active for this container?

DEFAULT ON for any group that declares a layout — a container that says "I am a 12-column grid" and then lets its children drift is not a layout container. metadata('autoLayout') survives as the explicit override: set it to false to freeze the children (useful while dragging, or for a container whose positions are authored by hand). Setting it to true remains valid and is now simply the default.

  • requestLayout(diagram?: DiagramModel): void — THE push entry point: "something changed, reflow if you are supposed to." Distinct from {@link applyLayout}, which is the unconditional PULL — an explicit applyLayout() still works on an opted-out container.
  • applyLayout(diagram?: DiagramModel): void — Apply layout to member nodes Positions child nodes based on flex or grid layout configuration
  • serialize(): SerializedGroup — Serialize to JSON
  • static fromJSON(data: SerializedGroup): GroupModel (static) — Deserialize from JSON

Was this page helpful?