Repository navigation
feat(layout-elk): add layoutSync() and settle main-thread layout() before paint - #3589
Open
Geliogabalus wants to merge 1 commit into
Open
Geliogabalus wants to merge 1 commit into
Geliogabalus wants to merge 1 commit into
Conversation
…fore paint elkjs only reaches its layout handler after two setTimeout(…, 0) - one in its fake worker, one in its promise wrapper - so a main-thread layout() always settled after the browser painted the graph as it was before. Call the handler of the default ELK instance's own fake worker directly (an elkjs internal, guarded, with a fallback to elk.layout()). layout() then settles in microtasks once ELK is loaded, and the new layoutSync() lays out synchronously; loadElk() loads ELK ahead of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
layout()without anelkoption now settles before the next paint once ELK is loaded.layoutSync(graphOrCells, opt?)lays out synchronously and returns theLayoutResult. It takeslayout()'s options exceptelkandsignal, and throws if ELK isn't loaded yet, before any export callback runs.loadElk()loads the main-thread ELK ahead of time. It reuses the existing dynamic chunk, so nothing new is imported statically.How
elkjs (0.12.0) only reaches its layout handler after two
setTimeout(…, 0): one in its fake worker (FakeWorker.postMessage) and one in its promise wrapper (PromisedWorker'sonmessage). ELK itself computes synchronously.The default ELK instance already holds its fake worker at
elk.worker.worker. That worker'sdispatcher.dispatch()runs a command synchronously, lays the graph out in place and throws on failure.defaultElk.mtscalls it directly. No worker is created, andelk-worker.jsis not imported.Because this relies on elkjs internals (elkjs is pinned), the path is guarded:
layout()falls back toelk.layout()andlayoutSync()throws a clear error.ELKconstructor's ownregisteris still waiting on asetTimeoutat that point.__java$exceptionis stripped from thrown errors, as elkjs' wrapper does on the async path.layout()is split into prepare and apply halves thatlayoutSync()shares. README and changesets are updated.Motivation and Context
On the main thread,
layout()always settled after the browser had painted at least one frame. Anything laid out right after a graph change (a cell added, a label edited) was first drawn at its pre-layout position, then jumped.@joint/layout-directed-graphdoesn't have this problem because it is synchronous.With this change:
layout()no longer jumps; existing callers need no changes.layoutSync()covers callers that need the layout applied before anything else runs. One example is@joint/react'suseOnElementsMeasured, which flushes the paper as soon as its callback returns.Synchronous means blocking. That suits small and medium graphs; large ones should still use
createWorkerElk().Tests
layoutSync()throws before ELK is loaded and after a failed load, without running export callbacks.layout()settles before asetTimeout(…, 0)fires.layoutSync()gives the same positions and vertices aslayout(), applies in a singlelayoutbatch, and handles cell lists and callbacks.layout()with elkjs' ownELKinstance is unaffected.layoutSync()rejects.Not covered: the fallback when the dispatcher is missing. The shared instance is cached once it loads, so the test bundle can't replace it.
yarn workspace @joint/layout-elk run testpasses: 113 tests, coverage floors met.🤖 Generated with Claude Code