Skip to content

feat(dia.Paper): support useModelGeometry: { filter } in content area measurement - #3561

Open
Geliogabalus wants to merge 4 commits into
clientIO:devfrom
Geliogabalus:feat/content-area-filter
Open

Geliogabalus wants to merge 4 commits into
clientIO:devfrom
Geliogabalus:feat/content-area-filter

Conversation

@Geliogabalus

@Geliogabalus Geliogabalus commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Description

paper.getContentArea({ useModelGeometry: true }) returns graph.getBBox(), which includes every cell, also the ones the paper hides through cellVisibility. Anything that sizes or fits the paper from the model geometry therefore makes room for content that is not shown:

  • fitToContent();
  • getFitToContentArea();
  • transformToFitContent();
  • components built on these methods.

This PR lets useModelGeometry take an options object with a filter. It is non-breaking: useModelGeometry: true and false work as before.

dia.Paper

interface Paper.ModelGeometryOptions {
    filter?: (cell: Cell) => boolean;
}

interface Paper.ContentAreaOptions {
    useModelGeometry?: boolean | Paper.ModelGeometryOptions;
}
paper.fitToContent({
    useModelGeometry: { filter: (cell) => !cell.get('collapsed') }
});
  • Accepted by getContentArea() and getContentBBox(). FitToContentOptions and TransformToFitContentOptions now extend ContentAreaOptions, so getFitToContentArea(), fitToContent() and transformToFitContent() pass it through.
  • The filter sits under useModelGeometry because it only applies there. On the view path, hidden cells are not mounted, so they are never measured anyway.
  • The filter gets only the cell: no this binding and no paper argument, since the caller already holds the paper. It is the same (cell) => boolean signature as the filter of Element.fitToChildren().
  • An object without a filter (useModelGeometry: {}) measures every cell, the same as true.

Why only on the paper

Code that measures content through these paper methods passes an options object, so an option is the only way to tell it which cells count. graph.getCellsBBox(cells) has no such need, because its caller already chooses the cells: graph.getCellsBBox(graph.getCells().filter(fn)).

Why cellVisibility is not applied automatically

cellVisibility is also used for viewport culling, where it hides cells outside the visible area. Applying it while measuring would shrink the content area to the viewport, and a paper resized to fit its content would shrink with it. The callback also takes an isMounted argument that has no meaning outside the update loop. Only the caller can tell hidden cells from culled ones, so the filter is explicit.

Tests

  • QUnit:
    • getContentArea() / getContentBBox() / getFitToContentArea() / fitToContent() / transformToFitContent() with useModelGeometry: { filter };
    • the filter is called once per cell, with the cell only;
    • an empty area when nothing passes the filter;
    • useModelGeometry: {} measures every cell.
  • TypeScript usage in test/ts/index.test.ts.
  • One changeset, for dia.Paper.

🤖 Generated with Claude Code

Geliogabalus and others added 3 commits October 26, 2026 12:52
…ement

`getContentArea()` with `useModelGeometry: true` returned the bbox of every
cell in the graph, including the cells the paper hides through
`cellVisibility`. The paper scroller, the navigator and "fit to content"
helpers therefore sized themselves to content the paper does not show.

- dia.Graph: `getBBox()` and `getCellsBBox()` accept a `filter(cell)` option
- dia.Paper: `getContentArea()`, `getContentBBox()`, `getFitToContentArea()`,
  `fitToContent()` and `transformToFitContent()` accept a
  `filter(cell, paper)` option, used with `useModelGeometry: true`

The paper's `cellVisibility` is deliberately not applied automatically: with
virtual rendering it also culls cells outside the viewport, which would
shrink the content area to the viewport.

Refs clientIO/joint-plus#836

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Match the `filter(cell)` signature of `dia.Graph` and `Element.fitToChildren()`
instead of binding the paper as `this` and passing it as an argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…BBox()

Callers of `getCellsBBox()` already choose the cells, so filtering them is
`cells.filter(fn)`. `paper.getContentArea()` now filters the cells itself,
which keeps the new option on the paper only, where `ui.PaperScroller` and
`ui.Navigator` can pass it through their content options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Geliogabalus
Geliogabalus requested a balanced review from Copilot October 29, 2026 09:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@kumilingus kumilingus left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we nest the filter under useModelGeometry instead of making it a separate option?

paper.getContentArea({ useModelGeometry: { filter: (cell) => !cell.get('hidden') } });

interface ContentAreaOptions {
    useModelGeometry?: boolean | { filter?: (cell: Cell) => boolean };
}
  • filter only works with useModelGeometry. As a separate option, { filter } alone is silently ignored. Nesting it rules that combination out.
  • It follows the existing boolean | { ...options } pattern (e.g. snapLinks: boolean | { radius }).
  • Code that only forwards useModelGeometry picks up the filter with no changes, and the core check stays truthy.

The filter applies only to the model-based measurement, so it now lives in
`useModelGeometry: { filter }` instead of next to the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Geliogabalus Geliogabalus changed the title feat(dia.Paper): add filter option to model-based content area measurement feat(dia.Paper): support useModelGeometry: { filter } in content area measurement Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants