jssm 5.138.0 → 5.141.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,9 +9,9 @@ export { JssmViz } from './viz.js';
9
9
  *
10
10
  * Both tags render identically; `<fsl-viz>` is provided as an alternative
11
11
  * spelling for users whose mental model is "FSL viz" rather than "jssm
12
- * viz".
13
- *
14
- * TODO: parent-context binding from #647 Stage 2 lands once #648 exists.
12
+ * viz". Parent-context binding for both tags lives on the shared
13
+ * `JssmViz` base class (see {@link JssmViz}); the synonym inherits it
14
+ * automatically.
15
15
  */
16
16
  class FslViz extends JssmViz {
17
17
  }
package/dist/wc/viz.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { css, LitElement, html } from 'lit';
2
2
  import { property, state } from 'lit/decorators.js';
3
3
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
4
- import { fsl_to_svg_string } from 'jssm/viz';
4
+ import { machine_to_svg_string, fsl_to_svg_string } from 'jssm/viz';
5
5
 
6
6
  var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
7
7
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -43,6 +43,17 @@ function normalize_viz_error(e) {
43
43
  /**
44
44
  * Web component that renders a jssm machine as inline SVG.
45
45
  *
46
+ * Two operating modes:
47
+ *
48
+ * 1. **Standalone** (no parent `<jssm-instance>` ancestor): render from
49
+ * the element's own `fsl=""` attribute / property. Re-renders on
50
+ * attribute change.
51
+ * 2. **Nested** (inside a `<jssm-instance>` ancestor, found via
52
+ * `closest('jssm-instance')` at `connectedCallback`): bind to the
53
+ * parent's machine and re-render on every `transition` event. The
54
+ * element's own `fsl` attribute is ignored in this mode; supplying it
55
+ * emits a `console.warn` for developer feedback.
56
+ *
46
57
  * @element jssm-viz
47
58
  * @cssproperty [--jssm-viz-min-height=100px] - Minimum height of the rendered SVG container.
48
59
  * @fires {CustomEvent<{ message: string; location?: unknown }>} viz-error - Fires when the FSL source fails to parse or render.
@@ -55,18 +66,137 @@ class JssmViz extends LitElement {
55
66
  /** Optional Graphviz layout engine override (e.g. 'dot', 'neato'). */
56
67
  this.engine = undefined;
57
68
  this._svg = '';
69
+ /**
70
+ * Parent `<jssm-instance>` host reference, set in `connectedCallback`
71
+ * when a parent is found. When non-null the viz is in nested mode and
72
+ * renders the parent's machine instead of its own `fsl` attribute.
73
+ */
74
+ this._parent_host = null;
75
+ /**
76
+ * Unsubscribe callback returned from `host.machine.on('transition', ...)`.
77
+ * Held so `disconnectedCallback` can release the subscription.
78
+ */
79
+ this._parent_sub = null;
58
80
  }
59
81
  /**
60
82
  * Lit lifecycle hook. Triggers an async SVG render whenever `fsl` or
61
- * `engine` change.
83
+ * `engine` change — but only in standalone mode. In nested mode the
84
+ * `fsl` attribute is ignored; renders are driven by the parent machine's
85
+ * transition events instead.
62
86
  *
63
87
  * @param changed - Map of changed reactive properties supplied by Lit.
64
88
  */
65
89
  willUpdate(changed) {
90
+ if (this._parent_host !== null) {
91
+ // Nested mode: ignore `fsl` attr changes; renders come from the
92
+ // parent's transition events. `engine` changes still re-render
93
+ // because they apply to whichever source is in use.
94
+ if (changed.has('engine')) {
95
+ this._rerenderFromHostMachine();
96
+ }
97
+ return;
98
+ }
66
99
  if (changed.has('fsl') || changed.has('engine')) {
67
100
  this._renderSvg();
68
101
  }
69
102
  }
103
+ /**
104
+ * Web Components lifecycle hook. Walks up to find a parent
105
+ * `<jssm-instance>` ancestor; if found, switches into nested mode and
106
+ * subscribes to the parent machine's `transition` events. Otherwise
107
+ * leaves standalone behavior intact.
108
+ *
109
+ * Subscription setup is deferred via `customElements.whenDefined` so the
110
+ * parent has had a chance to upgrade and construct its machine before
111
+ * we touch `host.machine`.
112
+ */
113
+ connectedCallback() {
114
+ super.connectedCallback();
115
+ const host = this.closest('jssm-instance');
116
+ if (host === null) {
117
+ return; // standalone: existing behavior, willUpdate handles render
118
+ }
119
+ // Conflicting-configuration feedback: nested viz with its own `fsl`
120
+ // attribute is almost certainly a bug. Warn but proceed — the parent
121
+ // owns the machine.
122
+ if (typeof this.fsl === 'string' && this.fsl.trim().length > 0) {
123
+ // eslint-disable-next-line no-console
124
+ console.warn('<jssm-viz>: `fsl` ignored when nested inside <jssm-instance>; parent owns the machine');
125
+ }
126
+ this._parent_host = host;
127
+ // Defer to whenDefined so a not-yet-upgraded host has its machine
128
+ // available before we access `host.machine` (which throws when called
129
+ // pre-connection).
130
+ void customElements.whenDefined('jssm-instance').then(() => {
131
+ // Re-check the host is still attached and the viz still belongs to
132
+ // it — disconnection between the deferred resolution and now is
133
+ // legal and should not error.
134
+ if (this._parent_host !== host) {
135
+ return;
136
+ }
137
+ try {
138
+ this._parent_sub = host.machine.on('transition', () => {
139
+ this._rerenderFromHostMachine();
140
+ });
141
+ }
142
+ catch (e) {
143
+ // The parent existed but its machine wasn't ready / threw. Emit
144
+ // a viz-error so the consumer learns about it instead of silently
145
+ // showing nothing.
146
+ this.dispatchEvent(new CustomEvent('viz-error', {
147
+ detail: normalize_viz_error(e),
148
+ bubbles: true,
149
+ composed: true,
150
+ }));
151
+ return;
152
+ }
153
+ this._rerenderFromHostMachine();
154
+ });
155
+ }
156
+ /**
157
+ * Web Components lifecycle hook. Releases any installed
158
+ * parent-transition subscription and clears the host reference so a
159
+ * subsequent re-attach goes through the full `connectedCallback` path
160
+ * again.
161
+ */
162
+ disconnectedCallback() {
163
+ super.disconnectedCallback();
164
+ if (this._parent_sub !== null) {
165
+ this._parent_sub();
166
+ this._parent_sub = null;
167
+ }
168
+ this._parent_host = null;
169
+ }
170
+ /**
171
+ * Nested-mode render path. Renders the bound parent's machine via the
172
+ * {@link machine_to_svg_string} pipeline and commits the result to
173
+ * `_svg`. On failure emits a `viz-error` `CustomEvent` and clears the
174
+ * SVG.
175
+ *
176
+ * @returns A promise that resolves once the render attempt has finished.
177
+ */
178
+ async _rerenderFromHostMachine() {
179
+ const host = this._parent_host;
180
+ if (host === null) {
181
+ return;
182
+ }
183
+ try {
184
+ const m = host.machine;
185
+ const result = await machine_to_svg_string(m, this.engine ? { engine: this.engine } : undefined);
186
+ // Guard against the parent disappearing mid-render.
187
+ if (this._parent_host === host) {
188
+ this._svg = result;
189
+ }
190
+ }
191
+ catch (e) {
192
+ this._svg = '';
193
+ this.dispatchEvent(new CustomEvent('viz-error', {
194
+ detail: normalize_viz_error(e),
195
+ bubbles: true,
196
+ composed: true,
197
+ }));
198
+ }
199
+ }
70
200
  /**
71
201
  * Render the current `fsl` source to an SVG string via the headless
72
202
  * `fsl_to_svg_string` pipeline. Updates `_svg` on success; emits a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jssm",
3
- "version": "5.138.0",
3
+ "version": "5.141.0",
4
4
  "engines": {
5
5
  "node": ">=10.0.0"
6
6
  },