@blockquote-web-components/blockquote-controller-xstate 1.1.5 → 2.0.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.
- package/README.md +76 -40
- package/package.json +6 -6
- package/src/BlockquoteControllerXstate.js +108 -46
package/README.md
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
### Connect XState machines with Lit
|
|
6
|
-
The
|
|
7
|
-
This controller allows you to subscribe to an XState actor, updating a specified reactive property whenever the state machine transitions.
|
|
5
|
+
### Connect XState machines with Lit
|
|
6
|
+
The BlockquoteControllerXstate is a Lit Reactive Controller that is specifically designed to facilitate a integration with XState. This controller provides the capability to subscribe to an XState actor. It also provides a callback function to handle the state changes.
|
|
8
7
|
|
|
9
8
|
- [xstate v5](https://stately.ai/docs/installation)
|
|
10
9
|
- [xstate v5 - examples](https://stately.ai/docs/examples)
|
|
11
|
-
- [Original idea](https://codesandbox.io/s/z3o0s?file=/src/toggleMachine.ts)
|
|
12
10
|
|
|
13
11
|
<hr>
|
|
14
12
|
|
|
@@ -93,12 +91,15 @@ export const counterMachine = createMachine(
|
|
|
93
91
|
);
|
|
94
92
|
```
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
**`new BlockquoteControllerXstate(this, {machine, options?, callback?})`**
|
|
95
|
+
|
|
96
|
+
***Usage***
|
|
97
97
|
|
|
98
98
|
```javascript
|
|
99
99
|
import { html, LitElement } from 'lit';
|
|
100
|
-
import { BlockquoteControllerXstate } from '
|
|
100
|
+
import { BlockquoteControllerXstate } from '../index.js';
|
|
101
101
|
import { counterMachine } from './counterMachine.js';
|
|
102
|
+
import { styles } from './styles/xstate-counter-styles.css.js';
|
|
102
103
|
|
|
103
104
|
export class XstateCounter extends LitElement {
|
|
104
105
|
static properties = {
|
|
@@ -108,10 +109,31 @@ export class XstateCounter extends LitElement {
|
|
|
108
109
|
},
|
|
109
110
|
};
|
|
110
111
|
|
|
112
|
+
static styles = [styles];
|
|
113
|
+
|
|
111
114
|
constructor() {
|
|
112
115
|
super();
|
|
113
116
|
this._xstate = {};
|
|
114
|
-
this.
|
|
117
|
+
this._inspectEvents = this._inspectEvents.bind(this);
|
|
118
|
+
this._callbackCounterController = this._callbackCounterController.bind(this);
|
|
119
|
+
|
|
120
|
+
this.counterController = new BlockquoteControllerXstate(this, {
|
|
121
|
+
machine: counterMachine,
|
|
122
|
+
options: {
|
|
123
|
+
inspect: this._inspectEvents,
|
|
124
|
+
},
|
|
125
|
+
callback: this._callbackCounterController,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
_callbackCounterController(snapshot) {
|
|
130
|
+
this._xstate = snapshot;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_inspectEvents(inspEvent) {
|
|
134
|
+
if (inspEvent.type === '@xstate.snapshot' && inspEvent.event.type === 'xstate.stop') {
|
|
135
|
+
this._xstate = {};
|
|
136
|
+
}
|
|
115
137
|
}
|
|
116
138
|
|
|
117
139
|
updated(props) {
|
|
@@ -126,32 +148,39 @@ export class XstateCounter extends LitElement {
|
|
|
126
148
|
}
|
|
127
149
|
}
|
|
128
150
|
|
|
129
|
-
// ...
|
|
130
|
-
|
|
131
151
|
get #disabled() {
|
|
132
|
-
return this.counterController.
|
|
152
|
+
return this.counterController.snapshot.matches('disabled');
|
|
133
153
|
}
|
|
134
154
|
|
|
135
155
|
render() {
|
|
136
156
|
return html`
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
<slot></slot>
|
|
158
|
+
<div aria-disabled="${this.#disabled}">
|
|
159
|
+
<span>
|
|
160
|
+
<button
|
|
161
|
+
?disabled="${this.#disabled}"
|
|
162
|
+
data-counter="increment"
|
|
163
|
+
\@click=${() => this.counterController.send({ type: 'INC' })}
|
|
164
|
+
>
|
|
165
|
+
Increment
|
|
166
|
+
</button>
|
|
167
|
+
<button
|
|
168
|
+
?disabled="${this.#disabled}"
|
|
169
|
+
data-counter="decrement"
|
|
170
|
+
\@click=${() => this.counterController.send({ type: 'DEC' })}
|
|
171
|
+
>
|
|
172
|
+
Decrement
|
|
173
|
+
</button>
|
|
174
|
+
</span>
|
|
175
|
+
<p>${this.counterController.snapshot.context.counter}</p>
|
|
176
|
+
</div>
|
|
177
|
+
<div>
|
|
178
|
+
<button \@click=${() => this.counterController.send({ type: 'TOGGLE' })}>
|
|
179
|
+
${this.#disabled ? 'Enabled counter' : 'Disabled counter'}
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
151
182
|
`;
|
|
152
183
|
}
|
|
153
|
-
|
|
154
|
-
// ...
|
|
155
184
|
}
|
|
156
185
|
```
|
|
157
186
|
<hr>
|
|
@@ -159,31 +188,38 @@ export class XstateCounter extends LitElement {
|
|
|
159
188
|
|
|
160
189
|
### `src/BlockquoteControllerXstate.js`:
|
|
161
190
|
|
|
162
|
-
#### class: `
|
|
191
|
+
#### class: `UseMachine`
|
|
163
192
|
|
|
164
193
|
##### Fields
|
|
165
194
|
|
|
166
|
-
| Name
|
|
167
|
-
|
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
195
|
+
| Name | Privacy | Type | Default | Description | Inherited From |
|
|
196
|
+
| ----------------- | ------- | ---- | ---------- | ----------- | -------------- |
|
|
197
|
+
| `actor` | | | | | |
|
|
198
|
+
| `snapshot` | | | | | |
|
|
199
|
+
| `machine` | | | `machine` | | |
|
|
200
|
+
| `options` | | | `options` | | |
|
|
201
|
+
| `callback` | | | `callback` | | |
|
|
202
|
+
| `currentSnapshot` | | | | | |
|
|
171
203
|
|
|
172
204
|
##### Methods
|
|
173
205
|
|
|
174
|
-
| Name | Privacy | Description | Parameters
|
|
175
|
-
| ------------------ | ------- | ----------- |
|
|
176
|
-
| `send` | | | `ev:
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
206
|
+
| Name | Privacy | Description | Parameters | Return | Inherited From |
|
|
207
|
+
| ------------------ | ------- | ----------- | --------------------------------------------- | ------ | -------------- |
|
|
208
|
+
| `send` | | | `ev: EventFrom<typeof this.machine>` | | |
|
|
209
|
+
| `unsubscribe` | | | | | |
|
|
210
|
+
| `onNext` | | | `snapshot: SnapshotFrom<typeof this.machine>` | | |
|
|
211
|
+
| `startService` | | | | | |
|
|
212
|
+
| `stopService` | | | | | |
|
|
213
|
+
| `hostConnected` | | | | | |
|
|
214
|
+
| `hostDisconnected` | | | | | |
|
|
179
215
|
|
|
180
216
|
<hr/>
|
|
181
217
|
|
|
182
218
|
#### Exports
|
|
183
219
|
|
|
184
|
-
| Kind | Name | Declaration
|
|
185
|
-
| ---- | ---------------------------- |
|
|
186
|
-
| `js` | `BlockquoteControllerXstate` |
|
|
220
|
+
| Kind | Name | Declaration | Module | Package |
|
|
221
|
+
| ---- | ---------------------------- | ----------- | --------------------------------- | ------- |
|
|
222
|
+
| `js` | `BlockquoteControllerXstate` | UseMachine | src/BlockquoteControllerXstate.js | |
|
|
187
223
|
|
|
188
224
|
### `index.js`:
|
|
189
225
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockquote-web-components/blockquote-controller-xstate",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "This controller allows you to subscribe to an XState actor, updating a specified reactive property whenever the state machine transitions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lit",
|
|
@@ -132,16 +132,16 @@
|
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
"dependencies": {
|
|
135
|
-
"lit": "^3.1.
|
|
136
|
-
"xstate": "^5.
|
|
135
|
+
"lit": "^3.1.2",
|
|
136
|
+
"xstate": "^5.9.1"
|
|
137
137
|
},
|
|
138
138
|
"devDependencies": {
|
|
139
|
-
"@blockquote-web-components/blockquote-base-common-dev-dependencies": "^1.
|
|
140
|
-
"@blockquote-web-components/blockquote-base-embedded-webview": "^1.
|
|
139
|
+
"@blockquote-web-components/blockquote-base-common-dev-dependencies": "^1.8.0",
|
|
140
|
+
"@blockquote-web-components/blockquote-base-embedded-webview": "^1.9.0"
|
|
141
141
|
},
|
|
142
142
|
"publishConfig": {
|
|
143
143
|
"access": "public"
|
|
144
144
|
},
|
|
145
145
|
"customElements": "custom-elements.json",
|
|
146
|
-
"gitHead": "
|
|
146
|
+
"gitHead": "c19026ccc69693273fe4b13b4786672ee6c7172a"
|
|
147
147
|
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { createActor } from 'xstate';
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* # BlockquoteControllerXstate
|
|
5
4
|
*
|
|
6
5
|
* 
|
|
7
6
|
*
|
|
8
|
-
* ### Connect XState machines with Lit
|
|
9
|
-
* The
|
|
10
|
-
* This controller allows you to subscribe to an XState actor, updating a specified reactive property whenever the state machine transitions.
|
|
7
|
+
* ### Connect XState machines with Lit
|
|
8
|
+
* The BlockquoteControllerXstate is a Lit Reactive Controller that is specifically designed to facilitate a integration with XState. This controller provides the capability to subscribe to an XState actor. It also provides a callback function to handle the state changes.
|
|
11
9
|
*
|
|
12
10
|
* - [xstate v5](https://stately.ai/docs/installation)
|
|
13
11
|
* - [xstate v5 - examples](https://stately.ai/docs/examples)
|
|
14
|
-
* - [Original idea](https://codesandbox.io/s/z3o0s?file=/src/toggleMachine.ts)
|
|
15
12
|
*
|
|
16
13
|
* <hr>
|
|
17
14
|
*
|
|
@@ -96,12 +93,15 @@ import { createActor } from 'xstate';
|
|
|
96
93
|
* );
|
|
97
94
|
* ```
|
|
98
95
|
*
|
|
99
|
-
*
|
|
96
|
+
* **`new BlockquoteControllerXstate(this, {machine, options?, callback?})`**
|
|
97
|
+
*
|
|
98
|
+
* ***Usage***
|
|
100
99
|
*
|
|
101
100
|
* ```javascript
|
|
102
101
|
* import { html, LitElement } from 'lit';
|
|
103
|
-
* import { BlockquoteControllerXstate } from '
|
|
102
|
+
* import { BlockquoteControllerXstate } from '../index.js';
|
|
104
103
|
* import { counterMachine } from './counterMachine.js';
|
|
104
|
+
* import { styles } from './styles/xstate-counter-styles.css.js';
|
|
105
105
|
*
|
|
106
106
|
* export class XstateCounter extends LitElement {
|
|
107
107
|
* static properties = {
|
|
@@ -111,10 +111,31 @@ import { createActor } from 'xstate';
|
|
|
111
111
|
* },
|
|
112
112
|
* };
|
|
113
113
|
*
|
|
114
|
+
* static styles = [styles];
|
|
115
|
+
*
|
|
114
116
|
* constructor() {
|
|
115
117
|
* super();
|
|
116
118
|
* this._xstate = {};
|
|
117
|
-
* this.
|
|
119
|
+
* this._inspectEvents = this._inspectEvents.bind(this);
|
|
120
|
+
* this._callbackCounterController = this._callbackCounterController.bind(this);
|
|
121
|
+
*
|
|
122
|
+
* this.counterController = new BlockquoteControllerXstate(this, {
|
|
123
|
+
* machine: counterMachine,
|
|
124
|
+
* options: {
|
|
125
|
+
* inspect: this._inspectEvents,
|
|
126
|
+
* },
|
|
127
|
+
* callback: this._callbackCounterController,
|
|
128
|
+
* });
|
|
129
|
+
* }
|
|
130
|
+
*
|
|
131
|
+
* _callbackCounterController(snapshot) {
|
|
132
|
+
* this._xstate = snapshot;
|
|
133
|
+
* }
|
|
134
|
+
*
|
|
135
|
+
* _inspectEvents(inspEvent) {
|
|
136
|
+
* if (inspEvent.type === '@xstate.snapshot' && inspEvent.event.type === 'xstate.stop') {
|
|
137
|
+
* this._xstate = {};
|
|
138
|
+
* }
|
|
118
139
|
* }
|
|
119
140
|
*
|
|
120
141
|
* updated(props) {
|
|
@@ -129,70 +150,111 @@ import { createActor } from 'xstate';
|
|
|
129
150
|
* }
|
|
130
151
|
* }
|
|
131
152
|
*
|
|
132
|
-
* // ...
|
|
133
|
-
*
|
|
134
153
|
* get #disabled() {
|
|
135
|
-
* return this.counterController.
|
|
154
|
+
* return this.counterController.snapshot.matches('disabled');
|
|
136
155
|
* }
|
|
137
156
|
*
|
|
138
157
|
* render() {
|
|
139
158
|
* return html`
|
|
140
|
-
* <
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
159
|
+
* <slot></slot>
|
|
160
|
+
* <div aria-disabled="${this.#disabled}">
|
|
161
|
+
* <span>
|
|
162
|
+
* <button
|
|
163
|
+
* ?disabled="${this.#disabled}"
|
|
164
|
+
* data-counter="increment"
|
|
165
|
+
* \@click=${() => this.counterController.send({ type: 'INC' })}
|
|
166
|
+
* >
|
|
167
|
+
* Increment
|
|
168
|
+
* </button>
|
|
169
|
+
* <button
|
|
170
|
+
* ?disabled="${this.#disabled}"
|
|
171
|
+
* data-counter="decrement"
|
|
172
|
+
* \@click=${() => this.counterController.send({ type: 'DEC' })}
|
|
173
|
+
* >
|
|
174
|
+
* Decrement
|
|
175
|
+
* </button>
|
|
176
|
+
* </span>
|
|
177
|
+
* <p>${this.counterController.snapshot.context.counter}</p>
|
|
178
|
+
* </div>
|
|
179
|
+
* <div>
|
|
180
|
+
* <button \@click=${() => this.counterController.send({ type: 'TOGGLE' })}>
|
|
181
|
+
* ${this.#disabled ? 'Enabled counter' : 'Disabled counter'}
|
|
182
|
+
* </button>
|
|
183
|
+
* </div>
|
|
154
184
|
* `;
|
|
155
185
|
* }
|
|
156
|
-
*
|
|
157
|
-
* // ...
|
|
158
186
|
* }
|
|
159
187
|
* ```
|
|
160
188
|
* <hr>
|
|
161
189
|
*/
|
|
162
|
-
|
|
190
|
+
class UseMachine {
|
|
163
191
|
/**
|
|
164
|
-
* @param {import('lit').ReactiveElement} host
|
|
165
|
-
* @param {
|
|
166
|
-
*
|
|
192
|
+
* @param {import('lit').ReactiveElement} host - The host object.
|
|
193
|
+
* @param {{
|
|
194
|
+
* machine: import('xstate').StateMachine,
|
|
195
|
+
* options?: import('xstate').ActorOptions,
|
|
196
|
+
* callback?: Function
|
|
197
|
+
* }} arg - The arguments for the constructor.
|
|
167
198
|
*/
|
|
168
|
-
constructor(host, machine,
|
|
199
|
+
constructor(host, { machine, options, callback }) {
|
|
200
|
+
this.machine = machine;
|
|
201
|
+
this.options = options;
|
|
202
|
+
this.callback = callback;
|
|
203
|
+
this.currentSnapshot = this.snapshot;
|
|
204
|
+
this.onNext = this.onNext.bind(this);
|
|
205
|
+
|
|
169
206
|
(this.host = host).addController(this);
|
|
170
|
-
this.service = createActor(machine).start();
|
|
171
|
-
this.propKey = propKey;
|
|
172
207
|
}
|
|
173
208
|
|
|
174
|
-
get
|
|
175
|
-
return this.
|
|
209
|
+
get actor() {
|
|
210
|
+
return this.actorRef;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get snapshot() {
|
|
214
|
+
return this.actorRef?.getSnapshot?.();
|
|
176
215
|
}
|
|
177
216
|
|
|
178
217
|
/**
|
|
179
|
-
* @param {import('xstate').
|
|
218
|
+
* @param {import('xstate').EventFrom<typeof this.machine>} ev
|
|
180
219
|
*/
|
|
181
220
|
send(ev) {
|
|
182
|
-
this.
|
|
221
|
+
this.actorRef?.send(ev);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
unsubscribe() {
|
|
225
|
+
this.subs?.unsubscribe();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @param {import('xstate').SnapshotFrom<typeof this.machine>} snapshot
|
|
230
|
+
*/
|
|
231
|
+
onNext(snapshot) {
|
|
232
|
+
if (this.currentSnapshot !== snapshot) {
|
|
233
|
+
this.currentSnapshot = snapshot;
|
|
234
|
+
this.callback?.(snapshot);
|
|
235
|
+
this.host.requestUpdate();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
startService() {
|
|
240
|
+
this.actorRef = createActor(this.machine, this.options);
|
|
241
|
+
if (this.actorRef) {
|
|
242
|
+
this.subs = this.actorRef.subscribe(this.onNext);
|
|
243
|
+
}
|
|
244
|
+
this.actorRef?.start();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
stopService() {
|
|
248
|
+
this.actorRef?.stop();
|
|
183
249
|
}
|
|
184
250
|
|
|
185
251
|
hostConnected() {
|
|
186
|
-
|
|
187
|
-
* https://stately.ai/docs/context#updating-context-with-assign
|
|
188
|
-
* https://lit.dev/docs/components/properties/#mutating-properties
|
|
189
|
-
*/
|
|
190
|
-
this.service.subscribe(state => {
|
|
191
|
-
this.propKey in this.host && (this.host[this.propKey] = state);
|
|
192
|
-
});
|
|
252
|
+
this.startService();
|
|
193
253
|
}
|
|
194
254
|
|
|
195
255
|
hostDisconnected() {
|
|
196
|
-
this.
|
|
256
|
+
this.stopService();
|
|
197
257
|
}
|
|
198
258
|
}
|
|
259
|
+
|
|
260
|
+
export { UseMachine as BlockquoteControllerXstate };
|