Frontmatter
| id | 8615 |
| title | Refactor Container to support atomic component moves |
| state | Closed |
| labels | airefactoringcore |
| assignees | tobiu |
| createdAt | Jan 13, 2026, 8:49 PM |
| updatedAt | Jan 13, 2026, 11:01 PM |
| githubUrl | https://github.com/neomjs/neo/issues/8615 |
| author | tobiu |
| commentsCount | 2 |
| parentIssue | 8601 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 13, 2026, 11:01 PM |
Refactor Container to support atomic component moves
tobiu added parent issue #8601 on Jan 13, 2026, 8:49 PM
tobiu assigned to @tobiu on Jan 13, 2026, 8:59 PM

tobiu
Jan 13, 2026, 10:53 PM
Input from Gemini 3 Pro:
✦ I have successfully refactored
Neo.container.Baseto support atomic component moves, preserving DOM state like input values and focus.Key Changes:
Container.Base.insert:
- Refactored to handle moving existing
NeoInstanceitems.- Implemented "Silent Remove + Update New Parent" strategy.
- Calls
oldParent.remove(item, false, true, true)(destroy=false, silent=true, keepMounted=true) to detach the item from the old parent's state without triggering a DOM update (which would send aremoveNodedelta).- Then, it updates the new parent (
me). The VDOM diff generates aninsertNodedelta.DeltaUpdatesusesinsertBefore/appendChildwhich effectively moves the existing DOM node instead of destroying and recreating it.
Container.Base.remove/removeAt:
- Added
keepMountedargument to preventitem.mounted = falseduring the silent move process. This is critical to keep the component's lifecycle active.
Neo.worker.App.moveComponent:
- Simplified to delegate the atomic move logic entirely to
parent.insert(orparent.add).- Restored guardrails for
component,parentexistence, andparent.isContainer.
Neo.main.DeltaUpdates.moveNode:
- Addressed a critical browser behavior (Chromium/WebKit) where reparenting a focused element causes it to lose focus (
blurevent).- Implemented logic to capture
document.activeElementbefore the move.- If the active element is within the moving node,
focus()is called on it immediately after the move operation (insertBeforeorreplaceWith).- This ensures the user experience is seamless, even though it may trigger a rapid
blur->focusevent sequence.Verification:
- Created a new Playwright test suite
test/playwright/component/container/AtomicMoves.spec.mjs.- Baseline Test: Confirmed that native DOM
appendChildloses focus in the test environment (Chromium), validating the need for manual restoration. Also confirmedmoveBefore(if available) would preserve it.- Input State: Confirmed input values are preserved during a move between sibling containers.
- Focus State: Confirmed focus is preserved during a move between sibling containers.
- DOM State: Confirmed custom DOM properties are preserved.
The feature is implemented and robustly tested.

tobiu
Jan 13, 2026, 11:01 PM
Input from Gemini 3 Pro:
✦ I have updated the JSDoc for
Neo.container.Base,Neo.worker.App, andNeo.main.DeltaUpdatesto explicitly document the new atomic move capabilities and focus preservation logic. This ensures that the knowledge about these significant architectural improvements is preserved in the codebase.
tobiu closed this issue on Jan 13, 2026, 11:01 PM
Currently,
Neo.worker.App.moveComponentimplements custom logic to perform a "silent remove" from the old parent before inserting into the new parent. This is necessary to ensureDeltaUpdatesreceives amoveNodedelta instead ofremoveNode+insertNode(which destroys DOM state like focus/iframes).This logic should be moved into
Neo.container.Base(or a Manager) to allow any component move operation (not just viamoveComponent) to be atomic and state-preserving.Goals:
Neo.container.Baseto support moving items from another container.moveTo(newParent, index)or enhanceadd/insertto detect existing parent and handle transition.moveNodedelta where possible.