Skip to content

Commit 8fd5d48

Browse files
committed
Improve README
1 parent c850121 commit 8fd5d48

File tree

1 file changed

+113
-34
lines changed

1 file changed

+113
-34
lines changed

README.md

+113-34
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,137 @@
1-
# create-svelte
1+
# tiny-svelte-fsm
22

3-
Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
3+
A minimalistic finite state machine library for Svelte 5, heavily inspired by [kenkunz/svelte-fsm](https://github.com/kenkunz/svelte-fsm) — but strongly-typed, and powered by Svelte 5 runes.
44

5-
Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging).
5+
FSMs are ideal for representing many different kinds of systems and interaction patterns. Stately's [xstate](https://github.com/statelyai/xstate) is an incredibly powerful library with more functionality. This is a much smaller and simpler library, and hopefully it's easy to understand.
66

7-
## Creating a project
87

9-
If you're seeing this, you've probably already done this step. Congrats!
8+
## Usage
109

11-
```bash
12-
# create a new project in the current directory
13-
npm create svelte@latest
10+
### Installation
1411

15-
# create a new project in my-app
16-
npm create svelte@latest my-app
17-
```
12+
> [!WARNING]
13+
> It's not yet published to npm.
1814
19-
## Developing
15+
### Introduction
2016

21-
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
17+
Every state machine is defined as a collection of _states_ and _events_. To define a state machine, create a list of the valid states and events:
2218

23-
```bash
24-
npm run dev
19+
```ts
20+
type myStates = 'on' | 'off';
21+
type myEvents = 'toggle';
22+
```
2523

26-
# or start the server and open the app in a new browser tab
27-
npm run dev -- --open
24+
Next, create the actual state machine:
25+
26+
```ts
27+
const f = fsm<myStates, myEvents>('off', {
28+
off: {
29+
toggle: 'on'
30+
},
31+
on: {
32+
toggle: 'off'
33+
}
34+
});
2835
```
2936

30-
Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
37+
The first argument is the initial state. The second argument is an object with one key for each state. Each state then describes which events are valid for that state, and which state that event should lead to.
38+
39+
In the above example of a simple switch, there are two states (`on` and `off`). The `toggle` event in either state leads to the other state.
40+
41+
You send events to the fsm using `f.send`. To send the `toggle` event, invoke `f.send('toggle')`.
42+
43+
### Actions
44+
45+
Maybe you want fancier logic for an event handler, or you want to conditionally transition into another state:
46+
47+
```ts
48+
type myStates = 'on' | 'off' | 'cooldown';
49+
50+
const f = fsm<myStates, myEvents>('off', {
51+
off: {
52+
toggle: () => {
53+
// You can prevent state transitions from happening by returning nothing.
54+
if (isTuesday) {
55+
// switch can only turn on during Tuesdays
56+
return 'on'
57+
}
58+
}
59+
},
60+
on: {
61+
toggle: (heldMillis: number) => {
62+
// You can also dynamically return the next state
63+
// only turn off if switch is depressed for 3 seconds
64+
// otherwise enter the `cooldown` state
65+
return heldMillis > 3000 ? 'off' : 'cooldown'
66+
}
67+
}
68+
});
69+
```
3170

32-
## Building
71+
### Lifecycle methods
72+
73+
You can define special handlers that are invoked whenever a state is entered or exited:
74+
75+
```ts
76+
const f = fsm<myStates, myEvents>('off', {
77+
off: {
78+
toggle: 'on'
79+
_enter: () => { console.log('switch is off')}
80+
_exit: () => { console.log('switch is no longer off')}
81+
},
82+
on: {
83+
toggle: 'off'
84+
_enter: () => { console.log('switch is on')}
85+
_exit: () => { console.log('switch is no longer on')}
86+
}
87+
});
88+
```
3389

34-
To build your library:
90+
The lifecycle methods are invoked with an argument containing useful metadata:
3591

36-
```bash
37-
npm run package
38-
```
92+
- `from`: the name of the event that is being exited
93+
- `to`: the name of the event that is being entered
94+
- `event`: the name of the event which has triggered the transition
95+
- `args`: (optional) you may pass additional metadata when invoking an action with `f.send('theAction', additional, params, as, args)`
3996

40-
To create a production version of your showcase app:
97+
The `_enter` handler for the initial state is called upon creation of the FSM. It is invoked with both the `from` and `event` fields set to `null`.
4198

42-
```bash
43-
npm run build
44-
```
99+
### Wildcard handlers
45100

46-
You can preview the production build with `npm run preview`.
101+
There is one special state used as a fallback: `*`. If you attempt to `send()` an event that is not handled by the current state, it will try to find a handler for that event on the `*` state:
47102

48-
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
103+
```ts
104+
const f = fsm<myStates, myEvents>('off', {
105+
off: {
106+
toggle: 'on'
107+
},
108+
on: {
109+
toggle: 'off'
110+
}
111+
'*': {
112+
emergency: 'off'
113+
}
114+
});
49115

50-
## Publishing
116+
// will always result in the switch turning off.
117+
f.send('emergency');
118+
```
51119

52-
Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
120+
### Debouncing
53121

54-
To publish your library to [npm](https://www.npmjs.com):
122+
Frequently, you want to transition to another state after some time has elapsed. To do this, use the `debounce` method:
55123

56-
```bash
57-
npm publish
124+
```ts
125+
f.send('toggle'); // turn on immediately
126+
f.debounce(5000, 'toggle'); // turn off in 5000 milliseconds
58127
```
128+
129+
If you re-invoke debounce with the same event, it will cancel the existing timer and start the countdown over:
130+
131+
```ts
132+
// schedule a toggle in five seconds
133+
f.debounce(5000, 'toggle');
134+
135+
// Cancels the original timer, and starts a fresh one:
136+
f.debounce(5000, 'toggle');
137+
```

0 commit comments

Comments
 (0)