<scxml-view>
<scxml-view> draws a whole chart and runs it. States are nested boxes, transitions are
orthogonal arrows, and the active configuration is highlighted. Transitions that just fired light
up. Each transition label is a button that sends its event.
It needs no JavaScript of your own. Import the module once to register the element:
<script type="module">import "@tinyactors/scxmljs/view";</script>
<scxml-view src="traffic-light.scxml"></scxml-view>
For charts with hundreds of states, or systems of several machines, use
<scxml-explorer> instead; see large charts.
Where the chart comes from#
In order of precedence:
- The
sessionproperty: a session you created. The element shows it and never starts, restarts or disposes it. No engine is loaded. - The
sourceproperty: SCXML text. - The
srcattribute: a URL, fetched withfetch(). Relativesrcattributes inside the chart (<script src>,<data src>,<invoke src>) resolve against it. - An inline
<script type="application/scxml+xml">child:
<script type="module">import "@tinyactors/scxmljs/view";</script>
<scxml-view trusted>
<script type="application/scxml+xml">
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="idle">
<state id="idle"><transition event="go" target="busy"/></state>
<state id="busy"><transition event="done" target="idle"/></state>
</scxml>
</script>
</scxml-view>An inline chart can't contain a <script> element: the HTML parser ends the outer
<script type="application/scxml+xml"> at the inner </script>. Use src for charts with
scripts (or express the logic with <assign> and expressions).
With none of these, the element shows a short hint ("No SCXML to show…") and fires no event: a
framework that creates the element and sets session later is the normal case, not an error.
In the cases 2 to 4 the element creates the session itself. It runs the chart in the QuickJS
sandbox unless the element has the trusted attribute (see sandboxed or trusted).
It loads the engine with a dynamic import() when it first needs it. When the element is removed
from the page, it disposes the session it created.
Attributes#
| Attribute | Default | |
|---|---|---|
src |
URL of the chart | |
trusted |
absent | run the chart in the host's engine instead of the sandbox; only for charts you trust |
autostart |
true |
"false" draws the chart without starting the session |
interactive |
true |
"false" makes transition labels plain text instead of buttons |
data |
JSON object of initial values; each key replaces the <data> element with that id |
|
direction |
auto |
right (left to right), down (top to bottom), or auto (down when the element is 760 px wide or less) |
max-states |
150 |
fold the largest compound states until at most this many boxes are drawn |
fit |
absent | scale the diagram all the way down to the element's width instead of scrolling. Without it, a diagram wider than the element is still scaled down, but not below --scxml-min-scale (0.65), and the rest scrolls |
event-data |
JSON object from event names to the data a click on that event's label sends, e.g. {"login": {"user": "ada"}}. Host code can also set detail.data in an scxml-send listener |
|
announce |
all |
what the live region reads out: all (steps and sent events), sends, or off |
warnings |
show |
off hides the list of authoring warnings above the diagram |
Changing src, trusted, autostart or data loads the chart again with a fresh session.
Properties#
Every attribute has a matching property (maxStates for max-states). In addition:
| Property | |
|---|---|
session |
the session shown. Set it to show a session of your own; set undefined to go back to src or the inline source. |
model |
the compiled chart (read-only) |
source |
SCXML text to show instead of src |
options |
extra SessionOptions for sessions the element creates: I/O processors, invokers, a loader… (not clock) |
clock |
the clock for sessions the element creates. A PlaybackClock adds play, pause, step and speed controls. |
strings |
translations; see custom UI and translations |
reload() |
load the chart again and start a fresh session |
Setting options or clock reloads the chart.
import { PlaybackClock } from "@tinyactors/scxmljs";
import "@tinyactors/scxmljs/view";
const view = document.querySelector("scxml-view")!;
view.clock = new PlaybackClock({ speed: 0.5, playing: false }); // adds playback controls
view.options = {
invokers: {
// handles <invoke type="http://example.com/timer">; see the invokers guide
"http://example.com/timer": () => ({ send() {}, cancel() {} }),
},
};Events#
All events bubble and cross the shadow boundary.
| Event | detail |
When |
|---|---|---|
scxml-load |
ViewLoadDetail: { session, model } |
the chart compiled and its session is ready. The element starts the session right after (unless autostart="false"), so listeners added now see the first step. |
scxml-error |
ViewErrorDetail: { message, problems?, error } |
nothing to show, the fetch failed, or the chart is invalid. problems lists validation problems one per line. The element shows them too. |
scxml-send |
ViewSendDetail: { session, name } |
a transition label is about to send its event. Cancelable: preventDefault() stops it. |
import type { ViewErrorDetail, ViewLoadDetail, ViewSendDetail } from "@tinyactors/scxmljs/view";
const view = document.querySelector("scxml-view")!;
view.addEventListener("scxml-load", (e) => {
const { session } = (e as CustomEvent<ViewLoadDetail>).detail;
session.addEventListener("done", () => console.log("finished"));
});
view.addEventListener("scxml-error", (e) => {
console.error((e as CustomEvent<ViewErrorDetail>).detail.problems);
});
view.addEventListener("scxml-send", (e) => {
const { name } = (e as CustomEvent<ViewSendDetail>).detail;
if (name === "delete" && !confirm("Really delete?")) e.preventDefault();
});Use scxml-load to reach the session the element created, for example to send events from your
own controls (see driving charts).
Large charts#
When a chart has more boxes than max-states, the element folds the largest compound states into
single boxes, shows a notice, and keeps the active states visible. Each folded box has an
Expand button, and the notice has Expand all. See large charts.
Keyboard and screen readers#
- Transition labels are buttons: Tab to them, Enter or Space sends the event.
- The diagram scrolls with the arrow keys once it has focus.
- With a
PlaybackClock: Space plays and pauses,.steps to the next event. - A polite live region announces each step ("go: now in busy") and each sent event.
- Colour is never the only signal: active states also get a dot and a bolder border, and the accessible name of each box says "active".
- When the session has terminated, the states it ended in stay marked, with the
reachedmodifier (their accessible name says "reached"): the spec empties the configuration on exit, so without it a finished chart would show nothing. - The diagram's geometry is left to right, also on right-to-left pages; the rest of the element (controls, notices, text in labels) follows the page's direction.
Styling#
The element uses the shared --scxml-* tokens and exposes these parts:
| Part | Modifiers | |
|---|---|---|
frame |
everything inside the element | |
canvas |
the scrolling area | |
state |
atomic compound parallel parallel-region final history collapsed active reached |
a state's box (reached: where a terminated session ended) |
state-name |
the name in a box | |
transition, edge-label |
live fired |
a transition label (a button unless interactive="false") |
event, cond |
the event and the condition inside a label | |
internal |
a targetless transition listed inside its state's box | |
edge |
live fired |
an arrow |
initial |
the initial-state marker | |
expand, collapse |
fold buttons | |
empty |
the hint shown while there's nothing to show | |
notice |
the "large chart" notice | |
send-status |
what became of an event sent by clicking a label, when it changed nothing or raised an error | |
error |
the error panel | |
warnings, warning |
the authoring-warnings list and each item | |
controls, play, step, speeds, clock |
the playback bar (with a PlaybackClock) |
live marks transitions whose source state is active, fired those that fired within the last
second. --scxml-height caps the element's height (default: none). Details and examples are in
theming.
Drawing charts yourself#
The layout is exported from @tinyactors/scxmljs/view. layoutChart(model.root, options) takes
a function that measures each box and label, and returns boxes and routed edges in pixels.
autoCollapse(model.root, maxBoxes) picks the states to fold. It's pure and deterministic, so you
can draw with SVG, canvas or your framework's components. See custom UI
and the API reference.