@koordinates/xstate-tree 2.0.6 → 2.0.8

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.
Files changed (2) hide show
  1. package/README.md +111 -6
  2. package/package.json +3 -2
package/README.md CHANGED
@@ -1,13 +1,117 @@
1
1
  # xstate-tree
2
2
 
3
- xstate-tree is a React state management framework built using XState. It allows you to create a tree of XState machines and map them to a tree of React views representing them as a UI.
3
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/koordinates/xstate-tree/blob/master/LICENSE)
4
+ [![npm version](https://img.shields.io/npm/v/@koordinates/xstate-tree.svg)](https://www.npmjs.com/package/@koordinates/xstate-tree)
5
+ [![Downloads](https://img.shields.io/npm/dm/@koordinates/xstate-tree.svg)](https://www.npmjs.com/package/@koordinates/xstate-tree)
6
+ [![Build Status](https://github.com/koordinates/xstate-tree/workflows/xstate-tree/badge.svg)](https://github.com/koordinates/xstate-tree/actions?query=workflow%3Axstate-tree)
7
+
8
+ xstate-tree was born as an answer to the question "What would a UI framework that uses [Actors](https://en.wikipedia.org/wiki/Actor_model) as the building block look like?". Inspired by Thomas Weber's [Master Thesis](https://links-lang.org/papers/mscs/Master_Thesis_Thomas_Weber_1450761.pdf) on the topic. [XState](https://xstate.js.org/) was chosen to power the actors with [React](https://reactjs.org) powering the UI derived from the actors.
9
+
10
+ xstate-tree was designed to enable modeling large applications as a single tree of xstate machines, with each machine being responsible for smaller and smaller sub sections of the UI. This allows modeling the entire UI structure in state machines, but without having to worry about the complexity of managing the state of the entire application in a single large machine.
11
+
12
+ Each machine has an associated, but loosely coupled, React view associated with it. The loose coupling allows the view to have no knowledge of the state machine for ease of testing and re-use in tools like [Storybook](https://storybook.js.org). Actors views are composed together via "slots", which can be rendered in the view to provide child actors a place to render their views in the parent's view.
13
+
14
+ While xstate-tree manages your application state, it does not have a mechanism for providing global application state accessible by multiple actors, this must be provided with another library like [Redux](https://redux.js.org/) or [GraphQL](https://graphql.org/). It does provide a routing solution, outlined [here](https://github.com/koordinates/xstate-tree/blob/master/src/routing/README.md).
15
+
16
+ At Koordinates we use xstate-tree for all new UI development. Our desktop application, built on top of [Kart](https://kartproject.org/) our Geospatial version control system, is built entirely with xstate-tree using GraphQL for global state.
17
+
18
+ A minimal example of a single machine tree
19
+
20
+ ```tsx
21
+ import React from "react";
22
+ import { createRoot } from "react-dom/client";
23
+ import { createMachine } from "xstate";
24
+ import { assign } from "@xstate/immer";
25
+ import { buildSelectors, buildActions, buildView, buildXstateTreeMachine, buildRootComponent } from "@koordinates/xstate-tree";
26
+
27
+ type Events = { type: "SWITCH_CLICKED" } | { type: "INCREMENT", amount: number };
28
+ type Context = { incremented: number };
29
+
30
+ // If this tree had more than a single machine the slots to render child machines into would be defined here
31
+ const slots = [];
32
+
33
+ // A standard xstate machine, nothing extra is needed for xstate-tree
34
+ const machine = createMachine<Context, Events>({
35
+ id: "root",
36
+ initial: "inactive",
37
+ context: {
38
+ incremented: 0,
39
+ },
40
+ states: {
41
+ inactive: {
42
+ on: {
43
+ SWITCH_CLICKED: "active",
44
+ },
45
+ },
46
+ active: {
47
+ on: {
48
+ SWITCH_CLICKED: "idle",
49
+ INCREMENT: { actions: "increment" },
50
+ },
51
+ },
52
+ },
53
+ }, {
54
+ actions: {
55
+ increment: assign((context, event) => {
56
+ context.incremented += event.amount;
57
+ }),
58
+ },
59
+ });
60
+
61
+ // Selectors to transform the machines state into a representation useful for the view
62
+ const selectors = buildSelectors(machine, (ctx, canHandleEvent) => ({
63
+ canIncrement: canHandleEvent({type: "INCREMENT", count: 1 }),
64
+ showSecret: ctx.incremented > 10,
65
+ count: ctx.incremented,
66
+ }));
67
+
68
+ // Actions to abstract away the details of sending events to the machine
69
+ const actions = buildActions(machine, actions, (send, selectors) => ({
70
+ increment(amount: number) {
71
+ send({ type: "INCREMENT", amount: selectors.count > 4 ? amount * 2 : amount });
72
+ },
73
+ switch() {
74
+ send({ type: "SWITCH_CLICKED" });
75
+ },
76
+ }));
77
+
78
+ // A view to bring it all together
79
+ // the return value is a plain React view that can be rendered anywhere by passing in the needed props
80
+ // the view has no knowledge of the machine it's bound to
81
+ const view = buildView(machine, actions, selectors, slots, ({ actions, selectors, inState }) => {
82
+ return (
83
+ <div>
84
+ <button onClick={() => actions.switch()}>{inState("active") ? "Deactivate" : "Activate"}</button>
85
+ <p>Count: {selectors.count}</p>
86
+ <button onClick={() => actions.increment(1)} disabled={!selectors.canIncrement}>Increment</button>
87
+ {selectors.showSecret && <p>The secret password is hunter2</p>}
88
+ </div>
89
+ );
90
+ });
91
+
92
+ // Stapling the machine, selectors, actions, view, and slots together
93
+ const RootMachine = buildXstateTreeMachine(machine, {
94
+ selectors,
95
+ actions,
96
+ view,
97
+ slots
98
+ });
99
+
100
+ // Build the React host for the tree
101
+ const XstateTreeRoot = buildRootComponent(RootMachine);
102
+
103
+
104
+ // Rendering it with React
105
+ const ReactRoot = createRoot(document.getElementById("root"));
106
+ ReactRoot.render(<XstateTreeRoot />);
107
+ ```
4
108
 
5
109
  ## Overview
6
110
 
7
111
  Each machine that forms the tree representing your UI has an associated set of selector, action, view functions, and "slots"
8
112
  - Selector functions are provided with the current context of the machine, a function to determine if it can handle a given event and a function to determine if it is in a given state, and expose the returned result to the view.
9
113
  - Action functions are provided with the `send` method bound to the machines interpreter and the result of calling the selector function
10
- - Slots are how children of the machine are exposed to the view. They can be either single slot, a single machine, or multi slot for when you have a list of actors.
114
+ - Slots are how children of the machine are exposed to the view. They can be either single slot for a single actor, or multi slot for when you have a list of actors.
11
115
  - View functions are React views provided with the output of the selector and action functions, a function to determine if the machine is in a given state, and the currently active slots
12
116
 
13
117
  ## API
@@ -19,6 +123,8 @@ To assist in making xstate-tree easy to use with TypeScript there are "builder"
19
123
  * `buildView`, first argument is the machine we are creating a view for, second argument is the selector factory, third argument is the actions factory, fourth argument is the array of slots and the fifth argument is the view function itself which gets supplied the selectors, actions, slots and `inState` method as props. It wraps the view in a React.memo
20
124
  * `buildXStateTreeMachine` takes the results of `buildSelectors`, `buildActions`, `buildView` and the list of slots and returns an xstate-tree compatible machine
21
125
 
126
+ Full API docs coming soon, see [#20](https://github.com/koordinates/xstate-tree/issues/20)
127
+
22
128
  ### Slots
23
129
 
24
130
  Slots are how invoked/spawned children of the machine are supplied to the Machines view. The child machines get wrapped into a React component responsible for rendering the machine itself. Since the view is provided with these components it is responsible for determining where in the view output they show up. This leaves the view in complete control of where the child views are placed.
@@ -58,10 +164,9 @@ These events can be added anywhere, either next to a component for component spe
58
164
  2. If they are tied to a component they need to be in the index.ts file that imports the view/selectors/actions etc and calls `buildXstateTreeMachine`. If they are in the file containing those functions the index.d.ts file will not end up importing them.
59
165
 
60
166
 
61
- ### Storybook
167
+ ### [Storybook](https://storybook.js.org)
62
168
 
63
- It's relatively uncomplicated to display xstate-tree views directly in Storybook. Since the views are plain React components
64
- that accept selectors/actions/slots/inState as props you can just import the view and render it in a Story
169
+ It is relatively simple to display xstate-tree views directly in Storybook. Since the views are plain React components that accept selectors/actions/slots/inState as props you can just import the view and render it in a Story
65
170
 
66
171
  There are a few utilities in xstate-tree to make this easier
67
172
 
@@ -87,4 +192,4 @@ are planning on rendering the view using the xstate-tree machine itself, or test
87
192
  via the view.
88
193
 
89
194
  It's a simple function that takes a name argument and returns a basic xstate-tree machine that you
90
- can replace slot services with. It just renders a div containing the name supplied
195
+ can replace slot services with. It just renders a div containing the name supplied
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@koordinates/xstate-tree",
3
3
  "main": "lib/index.js",
4
4
  "types": "lib/xstate-tree.d.ts",
5
- "version": "2.0.6",
5
+ "version": "2.0.8",
6
6
  "license": "MIT",
7
7
  "description": "Build UIs with Actors using xstate and React",
8
8
  "keywords": [
@@ -73,7 +73,8 @@
73
73
  "build": "rimraf dist && tsc -p tsconfig.build.json",
74
74
  "build:watch": "tsc -p tsconfig.build.json -w",
75
75
  "api-extractor": "api-extractor run",
76
- "release": "semantic-release"
76
+ "release": "semantic-release",
77
+ "commitlint": "commitlint --edit"
77
78
  },
78
79
  "files": [
79
80
  "lib/**/*.js",