Repository navigation
fix(dia.Paper): give a re-added layer its own view - #3554
Merged
Merged
Conversation
`onGraphLayerRemove()` defers the removal of a layer view to the next update, so that the cell views leave the layer first. A layer added again with the same id before that update found the old view still registered and got no view of its own: its cells were inserted into the old view, whose deferred removal then threw "The layer view is not empty", or lost the view and threw "Unknown layer view". `onGraphLayerAdd()` now removes the old view of a removed layer with the same id and creates a view for the new layer. A paper layer view with that id is kept as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kumilingus
marked this pull request as ready for review
October 21, 2026 16:01
Geliogabalus
approved these changes
Oct 21, 2026
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
The core part of #3499, split out so it ships as a
@joint/corepatch on its own.onGraphLayerRemove()does not remove a layer view right away: it schedules the removal at the layer view'sUPDATE_PRIORITY, which is lower than the cells', so the cell views leave the layer before it goes. When a layer with the same id is added again before that update runs,onGraphLayerAdd()finds the old view still registered under the id and returns without creating a view. Then:dia.Paper: The layer view is not empty.dia.Paper: Unknown layer view "layer1".It happens wherever the update is deferred: on an async paper, on a frozen paper, and on a sync paper inside an
UPDATE_DELAYING_BATCHES(translate) batch. A sync paper otherwise removes the view at once and never hits it.@joint/react(async paper) hits it when the declared layers drop an id and declare it again within one frame.Fix
onGraphLayerAdd()keeps the existing view only when it belongs to this layer, or when it is a paper layer view (not a graph layer view). Otherwise it is the view of a removed layer waiting for its removal: it is removed now and a view is created for the new layer.Differences from the version in #3499:
_dumpLayerViewUpdate(). Removing the view sets itsmvc.viewsregistry entry tonull, andupdateViews()already skips a scheduled update whose view is no longer registered. The pending removal is dropped without a new helper.existingLayerView.model === layer) also matches the paper's own layers:graph.addLayer({ id: 'front' })removed the paper'sfrontlayer view. TheGRAPH_LAYER_VIEW_MARKERcheck keeps the previous behavior for that case.Alternatives considered
<type>ViewinlayerViewNamespace), and a view does not support swapping its model.onGraphLayerRemove()and remove the old view by instance at the update. This keeps the old<g>(and the cell views still in it) in the DOM until the update, but it changeshasLayerView()/getLayerView()right afterremoveLayer(), needsupdateView()to remove the view by instance instead of by id, andpaper.remove()would have to track views that are no longer inviewsMap. Larger change for the same result.The trade-off of the chosen fix: cell views moved out of the layer before it was removed sit in the old
<g>until their pending insertion runs, so removing the<g>detaches them from the DOM until then. It only applies where the bug does (the update is deferred):batchSize(default 1000) the insertion can land a frame or more later, and the cell disappears in the meantimeunfreeze()/updateViews()translatebatchbatch:stopCode that measures such a cell view in between (
getBBox(),getBoundingClientRect()) gets zero sizes. Cells removed from the layer are discarded anyway, and cells added after the re-add go straight into the new view. The end state is covered by a test.Tests
New
Layers › re-adding a layer before its view is removed, run for an async paper and a frozen paper:On
master, 12 of the 16 fail (Unknown layer view "layer1"/The layer view is not empty). The last test fails with the #3499 version of the fix and passes onmaster.karma:joint: the 16 new tests pass, and the only failures are the same 5 timing tests that also fail locally onmaster(debounce,basic transition: *). eslint clean.Motivation and Context
Needed by #3499 (declarative layers in
@joint/react).🤖 Generated with Claude Code