XMachines 6.1.0: a layout and its pages
XMachines 6.1.0 composes views. A parent state can show its view together with the view of its active child, so a layout and its pages come from one machine. The release also fixes the Vue and Svelte renderers, which did not render in a browser in any release from 2.0.0 to 6.0.0.
A layout and its pages: outlets
Before 6.1.0, an actor rendered one meta.view: the deepest view of the first active branch. A layout on a parent state stayed off the screen, and a parallel state showed one region.
A meta.view can now declare outlets. Each outlet names an element of the parent view, and an optional named slot of that element. The key of an outlet is the key of a child state, or "*" for each active child without an outlet of its own:
const shell = typedSpec({ root: "shell", state: { collapsed: false }, elements: { shell: { type: "Shell", props: {}, children: ["title"], slots: { sidebar: [] } }, title: { type: "Heading", props: { text: "App" }, children: [] }, }, outlets: { menu: { element: "shell", slot: "sidebar" }, // the region "menu" "*": { element: "shell" }, // every other child },});
const appMachine = setup({/* ... */}).createMachine({ id: "app", type: "parallel", meta: { view: shell }, states: { menu: { initial: "open", states: { open: { meta: { view: menuView } } } }, page: { initial: "home", states: { home: { meta: { route: "/", view: homeView } }, search: { meta: { route: "/search", view: searchView } }, }, }, },});A compound state fills one outlet with its active child. A parallel state fills one outlet for each region. A child view can declare outlets of its own for a nested layout. A machine that declares no outlets keeps the derivation that it had.
@xmachines/play-view grafts the views into one flat spec. The keys of the outer view stay as you wrote them, and each key of a child view gets the id of its state as a prefix, for example app.page.search/root. The viewKey of a composed view is the id of the outermost state that declares outlets, so a change of the page keeps the store of the layout.
An outlet that names an element that its view does not hold throws InvalidViewOutletError. The actor reports the error through its onError option, and it keeps the last good view.
The state machines guide of 6.1 describes the rules, and how a layout survives a navigation.
play-dom renders a new spec of the same view in place
@xmachines/play-dom used to refresh only /context when the viewKey stayed the same. A composed view changes its page under the same viewKey, so the old page stayed on the screen.
The renderer now compares the spec with the spec on the screen. The same spec refreshes /context alone. A new spec of the same view renders in place: the layout keeps its nodes and its focus while its page changes. This needs @xmachines/json-render-dom ^0.21.0-xm.2.
The Vue and Svelte renderers render in a browser
The build of the monorepo compiled @xmachines/play-vue and @xmachines/play-svelte for server-side rendering. Each setup of play-vue called useSSRContext(), and play-svelte compiled against svelte/internal/server. Every release from 2.0.0 to 6.0.0 shipped that output, and neither package rendered in a browser.
In 6.1.0 both packages build for the browser. play-svelte also ships its runes module without the .svelte infix, so the Svelte plugin of your application does not compile it a second time.
Use 6.1.0 or later for Vue and Svelte. An earlier release of these two packages does not render in a browser.
A parallel machine takes its URL from its routed region
@xmachines/play-xstate derived the URL of a parallel machine from its first region. A machine that put an unrouted region first, such as the menu region of the example above, derived a null URL on every state, and the address bar never moved.
The URL now comes from the first region whose active states declare a meta.route. A view without outlets follows the same region, so the URL and the view always come from one region. Keep the routes of one layout in one region. The URL shows the route of the first routed region only.
json-render 0.21.0-xm.2
Every @xmachines/json-render-* range moves from ^0.21.0-xm.1 to ^0.21.0-xm.2, and the peer ranges too. The fork release adds the parts that view composition needs:
- dom:
setSpecof the incremental renderer renders a new spec in place. - solid: a container keeps its children when its element changes.
- solid and svelte: named slots.
Upgrade
- Move the
@xmachines/play-*packages to 6.1.0. - Move the
@xmachines/json-render-*packages to^0.21.0-xm.2.
The release removes no API, and a machine without outlets keeps its view. One change shows on the screen: a parallel machine whose first region holds a view and whose second region holds the routes now shows the view of the routed region.
The previous release is 6.0.0. The changelog of 6.1.0 lists every change of the release.