Transitions, focus and windows
Coordinate visual changes across apps, retain focus and move a live app into another window.
This page describes WU Framework 4.0.0. Read the Framework 4.0 and CLI 1.2 migration guide before upgrading an existing project.
Also try Apps in motion: a React editor, Vue inbox, windows and PiP, typed delivery, transitions and keep-alive. The scenario guide explains how to apply it to your application.
Animate an update
wu.transition(mutate, options) measures elements carrying data-wu-vt before and after a change. It returns ready, updateCallbackDone, finished, skipTransition() and mode. Await updateCallbackDone to catch mutation errors: ready and finished always resolve. The default mode uses FLIP and Web Animations inside Shadow DOM and respects reduced motion.
import { wu } from 'wu-framework';
const root = document.querySelector('#panel');
const item = document.createElement('div');
item.dataset.wuVt = 'product-42';
item.textContent = 'Product 42';
root.append(item);
const transition = wu.transition(() => {
item.style.transform = 'translateX(80px)';
}, { root, duration: 250 });
await transition.updateCallbackDone;
await transition.finished;
Create #panel before running the code. In Vue, await nextTick() inside the mutation; in Svelte, await tick(). For React, wuReact.createWuViewTransition(React) and wuReact.createUseWuTransition(React) coordinate renderer commits. mode: "native-document" is explicit opt-in; it does not promise to capture every shadow root interior.
wu.transition.between(fromApp, toApp, key, mutate?, options?) animates an element across apps using the same data-wu-vt key. It accepts null for the shell, fromRoot/toRoot, duration, timeout, easing, fade and ghost. It returns the same promise contract; a visual animation does not transfer business state.
Accessible focus and keep-alive
wu.focus.remember(name, root) records deep focus and restore(name) restores it; both return a boolean. forget(name) releases the record. trap(root, { initial, restore }) returns a cleanup function for a dialog. describe(element, { labelledBy, describedBy }) connects labels across roots and returns the applied mode: element references, IDs or text; the last is a snapshot to refresh when its label changes.
Hiding a keepAlive app retains its instance. Adapters detect React Activity, Vue scope pausing and Angular view detach/attach when those APIs exist. This does not automatically stop your intervals, sockets or requests: use wu:app:deactivated and wu:app:activated to pause and resume them, and remove listeners on unmount.
Move a live app to another window
import { wu } from 'wu-framework/core';
import 'wu-framework/windows';
// Run after the host has mounted the editor app.
document.querySelector('#detach-editor').addEventListener('click', async () => {
try {
const mode = 'documentPictureInPicture' in window ? 'pip' : 'popup';
await wu.detach('editor', { window: mode, width: 900, height: 700 });
} catch (error) {
document.querySelector('#window-status').textContent = error.message;
}
});
document.querySelector('#attach-editor').addEventListener('click', () => {
wu.attach('editor');
});
Create the buttons and #window-status in the shell. detach() requires a mounted, visible app and a user gesture to open the window. It adopts the same DOM nodes; code continues executing in the original window. Use ownerDocument.defaultView to inspect the visual window. It accepts a popup, Picture-in-Picture when available or a same-origin Window.
isDetached(name) reads the state; windowOf(name) returns Window | null; windowsReady() loads the subsystem. Closing the window, hiding or unmounting the app returns it to the host. Handle WU_APP_NOT_MOUNTED, WU_APP_HIDDEN, WU_WINDOW_UNAVAILABLE, WU_WINDOW_DENIED and WU_DETACH_ABORTED. Listen for wu:app:detached and wu:app:attached on the container when the renderer needs to react.