@uistate/examples 1.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 +40 -0
- package/cssState/.gitkeep +0 -0
- package/eventState/001-counter/README.md +44 -0
- package/eventState/001-counter/index.html +33 -0
- package/eventState/002-counter-improved/README.md +44 -0
- package/eventState/002-counter-improved/index.html +47 -0
- package/eventState/003-input-reactive/README.md +44 -0
- package/eventState/003-input-reactive/index.html +33 -0
- package/eventState/004-computed-state/README.md +45 -0
- package/eventState/004-computed-state/index.html +65 -0
- package/eventState/005-conditional-rendering/README.md +42 -0
- package/eventState/005-conditional-rendering/index.html +39 -0
- package/eventState/006-list-rendering/README.md +49 -0
- package/eventState/006-list-rendering/index.html +63 -0
- package/eventState/007-form-validation/README.md +52 -0
- package/eventState/007-form-validation/index.html +102 -0
- package/eventState/008-undo-redo/README.md +70 -0
- package/eventState/008-undo-redo/index.html +108 -0
- package/eventState/009-localStorage-side-effects/README.md +72 -0
- package/eventState/009-localStorage-side-effects/index.html +57 -0
- package/eventState/010-decoupled-components/README.md +74 -0
- package/eventState/010-decoupled-components/index.html +93 -0
- package/eventState/011-async-patterns/README.md +98 -0
- package/eventState/011-async-patterns/index.html +132 -0
- package/eventState/028-counter-improved-eventTest/LICENSE +55 -0
- package/eventState/028-counter-improved-eventTest/README.md +131 -0
- package/eventState/028-counter-improved-eventTest/app/store.js +9 -0
- package/eventState/028-counter-improved-eventTest/index.html +49 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/028-counter-improved-eventTest/runtime/core/router.js +271 -0
- package/eventState/028-counter-improved-eventTest/store.d.ts +8 -0
- package/eventState/028-counter-improved-eventTest/style.css +170 -0
- package/eventState/028-counter-improved-eventTest/tests/README.md +208 -0
- package/eventState/028-counter-improved-eventTest/tests/counter.test.js +116 -0
- package/eventState/028-counter-improved-eventTest/tests/eventTest.js +176 -0
- package/eventState/028-counter-improved-eventTest/tests/generateTypes.js +168 -0
- package/eventState/028-counter-improved-eventTest/tests/run.js +20 -0
- package/eventState/030-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/030-todo-app-with-eventTest/README.md +121 -0
- package/eventState/030-todo-app-with-eventTest/app/router.js +25 -0
- package/eventState/030-todo-app-with-eventTest/app/store.js +16 -0
- package/eventState/030-todo-app-with-eventTest/app/views/home.js +11 -0
- package/eventState/030-todo-app-with-eventTest/app/views/todoDemo.js +88 -0
- package/eventState/030-todo-app-with-eventTest/index.html +65 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/030-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/030-todo-app-with-eventTest/store.d.ts +18 -0
- package/eventState/030-todo-app-with-eventTest/style.css +170 -0
- package/eventState/030-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/030-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/030-todo-app-with-eventTest/tests/generateTypes.js +189 -0
- package/eventState/030-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/030-todo-app-with-eventTest/tests/todos.test.js +167 -0
- package/eventState/031-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/031-todo-app-with-eventTest/README.md +54 -0
- package/eventState/031-todo-app-with-eventTest/TUTORIAL.md +390 -0
- package/eventState/031-todo-app-with-eventTest/WHY_EVENTSTATE.md +777 -0
- package/eventState/031-todo-app-with-eventTest/app/bridges.js +113 -0
- package/eventState/031-todo-app-with-eventTest/app/router.js +26 -0
- package/eventState/031-todo-app-with-eventTest/app/store.js +15 -0
- package/eventState/031-todo-app-with-eventTest/app/views/home.js +46 -0
- package/eventState/031-todo-app-with-eventTest/app/views/todoDemo.js +69 -0
- package/eventState/031-todo-app-with-eventTest/devtools/dock.js +41 -0
- package/eventState/031-todo-app-with-eventTest/devtools/stateTracker.dock.js +10 -0
- package/eventState/031-todo-app-with-eventTest/devtools/stateTracker.js +246 -0
- package/eventState/031-todo-app-with-eventTest/devtools/telemetry.js +104 -0
- package/eventState/031-todo-app-with-eventTest/devtools/typeGenerator.js +339 -0
- package/eventState/031-todo-app-with-eventTest/index.html +103 -0
- package/eventState/031-todo-app-with-eventTest/package-lock.json +2184 -0
- package/eventState/031-todo-app-with-eventTest/package.json +24 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/031-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/boundary.js +36 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/converge.js +63 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +210 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/hydrate.js +157 -0
- package/eventState/031-todo-app-with-eventTest/runtime/extensions/queryBinding.js +69 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/computed.js +78 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/meta.js +51 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +28 -0
- package/eventState/031-todo-app-with-eventTest/runtime/forms/validators.js +55 -0
- package/eventState/031-todo-app-with-eventTest/store.d.ts +23 -0
- package/eventState/031-todo-app-with-eventTest/style.css +170 -0
- package/eventState/031-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/031-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/031-todo-app-with-eventTest/tests/generateTypes.js +191 -0
- package/eventState/031-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/031-todo-app-with-eventTest/tests/todos.test.js +192 -0
- package/eventState/032-todo-app-with-eventTest/LICENSE +55 -0
- package/eventState/032-todo-app-with-eventTest/README.md +54 -0
- package/eventState/032-todo-app-with-eventTest/TUTORIAL.md +390 -0
- package/eventState/032-todo-app-with-eventTest/WHY_EVENTSTATE.md +777 -0
- package/eventState/032-todo-app-with-eventTest/app/actions/index.js +153 -0
- package/eventState/032-todo-app-with-eventTest/app/bridges.js +113 -0
- package/eventState/032-todo-app-with-eventTest/app/router.js +26 -0
- package/eventState/032-todo-app-with-eventTest/app/store.js +15 -0
- package/eventState/032-todo-app-with-eventTest/app/views/home.js +46 -0
- package/eventState/032-todo-app-with-eventTest/app/views/todoDemo.js +69 -0
- package/eventState/032-todo-app-with-eventTest/devtools/dock.js +41 -0
- package/eventState/032-todo-app-with-eventTest/devtools/stateTracker.dock.js +10 -0
- package/eventState/032-todo-app-with-eventTest/devtools/stateTracker.js +246 -0
- package/eventState/032-todo-app-with-eventTest/devtools/telemetry.js +104 -0
- package/eventState/032-todo-app-with-eventTest/devtools/typeGenerator.js +339 -0
- package/eventState/032-todo-app-with-eventTest/index.html +87 -0
- package/eventState/032-todo-app-with-eventTest/package-lock.json +2184 -0
- package/eventState/032-todo-app-with-eventTest/package.json +24 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +282 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/eventState.js +100 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/eventStateNew.js +149 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/helpers.js +212 -0
- package/eventState/032-todo-app-with-eventTest/runtime/core/router.js +271 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/boundary.js +36 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/converge.js +63 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +210 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/hydrate.js +157 -0
- package/eventState/032-todo-app-with-eventTest/runtime/extensions/queryBinding.js +69 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/computed.js +78 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/meta.js +51 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +28 -0
- package/eventState/032-todo-app-with-eventTest/runtime/forms/validators.js +55 -0
- package/eventState/032-todo-app-with-eventTest/store.d.ts +23 -0
- package/eventState/032-todo-app-with-eventTest/style.css +170 -0
- package/eventState/032-todo-app-with-eventTest/tests/README.md +208 -0
- package/eventState/032-todo-app-with-eventTest/tests/eventTest.js +176 -0
- package/eventState/032-todo-app-with-eventTest/tests/generateTypes.js +191 -0
- package/eventState/032-todo-app-with-eventTest/tests/run.js +20 -0
- package/eventState/032-todo-app-with-eventTest/tests/todos.test.js +192 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @uistate/examples
|
|
2
|
+
|
|
3
|
+
Example applications and patterns for [@uistate/core](https://www.npmjs.com/package/@uistate/core).
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
eventState/ # EventState examples
|
|
9
|
+
001-counter/
|
|
10
|
+
002-counter-improved/
|
|
11
|
+
003-input-reactive/
|
|
12
|
+
004-computed-state/
|
|
13
|
+
005-conditional-rendering/
|
|
14
|
+
006-list-rendering/
|
|
15
|
+
007-form-validation/
|
|
16
|
+
008-undo-redo/
|
|
17
|
+
009-localStorage-side-effects/
|
|
18
|
+
010-decoupled-components/
|
|
19
|
+
011-async-patterns/
|
|
20
|
+
028-counter-improved-eventTest/
|
|
21
|
+
030-todo-app-with-eventTest/
|
|
22
|
+
031-todo-app-with-eventTest/
|
|
23
|
+
032-todo-app-with-eventTest/
|
|
24
|
+
|
|
25
|
+
cssState/ # CssState examples (coming soon)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Each example is a standalone HTML file that can be opened directly in the browser. Examples import `@uistate/core` via CDN or local reference.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @uistate/examples
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
MIT
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 001 Counter - Minimal EventState Example
|
|
2
|
+
|
|
3
|
+
The simplest possible EventState example: a counter with one button.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- `eventState.js` - ~60 lines of reactive state management
|
|
8
|
+
- `index.html` - Counter UI with subscription
|
|
9
|
+
|
|
10
|
+
## How It Works
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
// 1. Create store
|
|
14
|
+
const store = createEventState({ count: 0 });
|
|
15
|
+
|
|
16
|
+
// 2. Subscribe to changes
|
|
17
|
+
store.subscribe('count', ({ value }) => {
|
|
18
|
+
document.getElementById('count').textContent = value;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 3. Update state
|
|
22
|
+
store.set('count', store.get('count') + 1);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. No framework, no build step, just reactive data.
|
|
26
|
+
|
|
27
|
+
## Run It
|
|
28
|
+
|
|
29
|
+
Open `index.html` in a browser. Click the `+` button to increment.
|
|
30
|
+
|
|
31
|
+
## Key Concepts
|
|
32
|
+
|
|
33
|
+
- **`createEventState(initial)`** - Creates a reactive store
|
|
34
|
+
- **`store.get(path)`** - Read state at dot-path
|
|
35
|
+
- **`store.set(path, value)`** - Write state and notify subscribers
|
|
36
|
+
- **`store.subscribe(path, handler)`** - React to changes
|
|
37
|
+
|
|
38
|
+
## Next Steps
|
|
39
|
+
|
|
40
|
+
See `examples/009-todo-app-with-eventTest/` for a full application with:
|
|
41
|
+
- Router
|
|
42
|
+
- Bridges (intent → domain pattern)
|
|
43
|
+
- Tests with type generation
|
|
44
|
+
- Dev tools
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>001 Counter - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Counter: <span id="count">0</span></h1>
|
|
13
|
+
<button id="increment">+</button>
|
|
14
|
+
|
|
15
|
+
<script type="module">
|
|
16
|
+
import { createEventState } from '@uistate/core';
|
|
17
|
+
|
|
18
|
+
// Create store
|
|
19
|
+
const store = createEventState({ count: 0 });
|
|
20
|
+
|
|
21
|
+
// Subscribe to count changes
|
|
22
|
+
store.subscribe('count', ( value ) => {
|
|
23
|
+
document.getElementById('count').textContent = value;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Increment on button click
|
|
27
|
+
document.getElementById('increment').onclick = () => {
|
|
28
|
+
const current = store.get('count');
|
|
29
|
+
store.set('count', current + 1);
|
|
30
|
+
};
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 001 Counter - Minimal EventState Example
|
|
2
|
+
|
|
3
|
+
The simplest possible EventState example: a counter with one button.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- `eventState.js` - ~60 lines of reactive state management
|
|
8
|
+
- `index.html` - Counter UI with subscription
|
|
9
|
+
|
|
10
|
+
## How It Works
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
// 1. Create store
|
|
14
|
+
const store = createEventState({ count: 0 });
|
|
15
|
+
|
|
16
|
+
// 2. Subscribe to changes
|
|
17
|
+
store.subscribe('count', ({ value }) => {
|
|
18
|
+
document.getElementById('count').textContent = value;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 3. Update state
|
|
22
|
+
store.set('count', store.get('count') + 1);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. No framework, no build step, just reactive data.
|
|
26
|
+
|
|
27
|
+
## Run It
|
|
28
|
+
|
|
29
|
+
Open `index.html` in a browser. Click the `+` button to increment.
|
|
30
|
+
|
|
31
|
+
## Key Concepts
|
|
32
|
+
|
|
33
|
+
- **`createEventState(initial)`** - Creates a reactive store
|
|
34
|
+
- **`store.get(path)`** - Read state at dot-path
|
|
35
|
+
- **`store.set(path, value)`** - Write state and notify subscribers
|
|
36
|
+
- **`store.subscribe(path, handler)`** - React to changes
|
|
37
|
+
|
|
38
|
+
## Next Steps
|
|
39
|
+
|
|
40
|
+
See `examples/009-todo-app-with-eventTest/` for a full application with:
|
|
41
|
+
- Router
|
|
42
|
+
- Bridges (intent → domain pattern)
|
|
43
|
+
- Tests with type generation
|
|
44
|
+
- Dev tools
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>002 Counter Improved - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Counter: <span id="count">0</span></h1>
|
|
13
|
+
<button id="increment">+</button>
|
|
14
|
+
<button id="decrement">-</button>
|
|
15
|
+
<button id="double">x2</button>
|
|
16
|
+
|
|
17
|
+
<script type="module">
|
|
18
|
+
import { createEventState } from '@uistate/core';
|
|
19
|
+
|
|
20
|
+
// Create store
|
|
21
|
+
const store = createEventState({ count: 0 });
|
|
22
|
+
|
|
23
|
+
// Subscribe to count changes
|
|
24
|
+
store.subscribe('count', ( value ) => {
|
|
25
|
+
document.getElementById('count').textContent = value;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Increment on button click
|
|
29
|
+
document.getElementById('increment').onclick = () => {
|
|
30
|
+
const current = store.get('count');
|
|
31
|
+
store.set('count', current + 1);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Decrement on button click
|
|
35
|
+
document.getElementById('decrement').onclick = () => {
|
|
36
|
+
const current = store.get('count');
|
|
37
|
+
store.set('count', current - 1);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Decrement on button click
|
|
41
|
+
document.getElementById('double').onclick = () => {
|
|
42
|
+
const current = store.get('count');
|
|
43
|
+
store.set('count', current * 2);
|
|
44
|
+
};
|
|
45
|
+
</script>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 001 Counter - Minimal EventState Example
|
|
2
|
+
|
|
3
|
+
The simplest possible EventState example: a counter with one button.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- `eventState.js` - ~60 lines of reactive state management
|
|
8
|
+
- `index.html` - Counter UI with subscription
|
|
9
|
+
|
|
10
|
+
## How It Works
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
// 1. Create store
|
|
14
|
+
const store = createEventState({ count: 0 });
|
|
15
|
+
|
|
16
|
+
// 2. Subscribe to changes
|
|
17
|
+
store.subscribe('count', ({ value }) => {
|
|
18
|
+
document.getElementById('count').textContent = value;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// 3. Update state
|
|
22
|
+
store.set('count', store.get('count') + 1);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. No framework, no build step, just reactive data.
|
|
26
|
+
|
|
27
|
+
## Run It
|
|
28
|
+
|
|
29
|
+
Open `index.html` in a browser. Click the `+` button to increment.
|
|
30
|
+
|
|
31
|
+
## Key Concepts
|
|
32
|
+
|
|
33
|
+
- **`createEventState(initial)`** - Creates a reactive store
|
|
34
|
+
- **`store.get(path)`** - Read state at dot-path
|
|
35
|
+
- **`store.set(path, value)`** - Write state and notify subscribers
|
|
36
|
+
- **`store.subscribe(path, handler)`** - React to changes
|
|
37
|
+
|
|
38
|
+
## Next Steps
|
|
39
|
+
|
|
40
|
+
See `examples/009-todo-app-with-eventTest/` for a full application with:
|
|
41
|
+
- Router
|
|
42
|
+
- Bridges (intent → domain pattern)
|
|
43
|
+
- Tests with type generation
|
|
44
|
+
- Dev tools
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>003 Input Reactive - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Type something:</h1>
|
|
13
|
+
<input type="text" id="textInput" placeholder="Start typing...">
|
|
14
|
+
<p id="output"></p>
|
|
15
|
+
|
|
16
|
+
<script type="module">
|
|
17
|
+
import { createEventState } from '@uistate/core';
|
|
18
|
+
|
|
19
|
+
// Create store
|
|
20
|
+
const store = createEventState({ text: '' });
|
|
21
|
+
|
|
22
|
+
// Subscribe to text changes
|
|
23
|
+
store.subscribe('text', (value) => {
|
|
24
|
+
document.getElementById('output').textContent = value;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Update state on input
|
|
28
|
+
document.getElementById('textInput').oninput = (e) => {
|
|
29
|
+
store.set('text', e.target.value);
|
|
30
|
+
};
|
|
31
|
+
</script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 004 Computed State - Derived Values
|
|
2
|
+
|
|
3
|
+
Shows how trivial it is to create computed/derived state in EventState.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- Two inputs: `firstName` and `lastName`
|
|
8
|
+
- Two computed values: `fullName` and `charCount`
|
|
9
|
+
- **Zero framework magic** - just functions
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// 1. Define a function that computes derived state
|
|
15
|
+
const updateFullName = () => {
|
|
16
|
+
const first = store.get('firstName');
|
|
17
|
+
const last = store.get('lastName');
|
|
18
|
+
const fullName = `${first} ${last}`.trim();
|
|
19
|
+
document.getElementById('fullName').textContent = fullName;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// 2. Subscribe to wildcard to catch all changes
|
|
23
|
+
store.subscribe('*', () => {
|
|
24
|
+
updateFullName();
|
|
25
|
+
updateCharCount();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// 3. That's it. No special API needed.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Key Insight
|
|
32
|
+
|
|
33
|
+
**Computed state is just a function.**
|
|
34
|
+
|
|
35
|
+
Other frameworks need:
|
|
36
|
+
- Vue: `computed()`
|
|
37
|
+
- React: `useMemo()`
|
|
38
|
+
- Svelte: `$:` reactive declarations
|
|
39
|
+
- MobX: `@computed` decorators
|
|
40
|
+
|
|
41
|
+
**EventState:** Just write a function and call it when state changes.
|
|
42
|
+
|
|
43
|
+
## Run It
|
|
44
|
+
|
|
45
|
+
Open `index.html` in a browser. Type in the inputs and watch the computed values update.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>004 Computed State - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Computed State</h1>
|
|
13
|
+
|
|
14
|
+
<label>First Name: <input type="text" id="firstName" placeholder="Abraham"></label><br>
|
|
15
|
+
<label>Last Name: <input type="text" id="lastName" placeholder="Lincoln"></label><br>
|
|
16
|
+
|
|
17
|
+
<p><strong>Full Name:</strong> <span id="fullName"></span></p>
|
|
18
|
+
<p><strong>Character Count:</strong> <span id="charCount">0</span></p>
|
|
19
|
+
|
|
20
|
+
<script type="module">
|
|
21
|
+
import { createEventState } from '@uistate/core';
|
|
22
|
+
|
|
23
|
+
// Create store
|
|
24
|
+
const store = createEventState({
|
|
25
|
+
firstName: '',
|
|
26
|
+
lastName: ''
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Computed: full name (derived from firstName + lastName)
|
|
30
|
+
const updateFullName = () => {
|
|
31
|
+
const first = store.get('firstName');
|
|
32
|
+
const last = store.get('lastName');
|
|
33
|
+
const fullName = `${first} ${last}`.trim();
|
|
34
|
+
document.getElementById('fullName').textContent = fullName || '(empty)';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Computed: character count
|
|
38
|
+
const updateCharCount = () => {
|
|
39
|
+
const first = store.get('firstName');
|
|
40
|
+
const last = store.get('lastName');
|
|
41
|
+
const total = (first + last).length;
|
|
42
|
+
document.getElementById('charCount').textContent = total;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Subscribe to any state change and recompute
|
|
46
|
+
store.subscribe('*', () => {
|
|
47
|
+
updateFullName();
|
|
48
|
+
updateCharCount();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Wire up inputs
|
|
52
|
+
document.getElementById('firstName').oninput = (e) => {
|
|
53
|
+
store.set('firstName', e.target.value);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
document.getElementById('lastName').oninput = (e) => {
|
|
57
|
+
store.set('lastName', e.target.value);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Initial render
|
|
61
|
+
updateFullName();
|
|
62
|
+
updateCharCount();
|
|
63
|
+
</script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 005 Conditional Rendering - Show/Hide Elements
|
|
2
|
+
|
|
3
|
+
Demonstrates how to conditionally render elements based on state.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- Checkbox to toggle visibility
|
|
8
|
+
- Message that shows/hides based on state
|
|
9
|
+
- **No `v-if` or `{#if}` needed** - just DOM manipulation
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// 1. Store boolean state
|
|
15
|
+
const store = createEventState({ isVisible: false });
|
|
16
|
+
|
|
17
|
+
// 2. Subscribe and toggle display
|
|
18
|
+
store.subscribe('isVisible', (value) => {
|
|
19
|
+
const message = document.getElementById('message');
|
|
20
|
+
message.style.display = value ? 'block' : 'none';
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 3. Update state on checkbox change
|
|
24
|
+
document.getElementById('toggle').onchange = (e) => {
|
|
25
|
+
store.set('isVisible', e.target.checked);
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Key Insight
|
|
30
|
+
|
|
31
|
+
**Conditional rendering is just DOM manipulation.**
|
|
32
|
+
|
|
33
|
+
Other frameworks need:
|
|
34
|
+
- React: `{isVisible && <p>Message</p>}`
|
|
35
|
+
- Vue: `v-if="isVisible"`
|
|
36
|
+
- Svelte: `{#if isVisible}`
|
|
37
|
+
|
|
38
|
+
**EventState:** Just set `style.display` in a subscriber. That's it.
|
|
39
|
+
|
|
40
|
+
## Run It
|
|
41
|
+
|
|
42
|
+
Open `index.html` in a browser. Check the box to reveal the message.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>005 Conditional Rendering - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Conditional Rendering</h1>
|
|
13
|
+
|
|
14
|
+
<label>
|
|
15
|
+
<input type="checkbox" id="toggle">
|
|
16
|
+
Show secret message
|
|
17
|
+
</label>
|
|
18
|
+
|
|
19
|
+
<p id="message" style="display: none;">You found the secret message!</p>
|
|
20
|
+
|
|
21
|
+
<script type="module">
|
|
22
|
+
import { createEventState } from '@uistate/core';
|
|
23
|
+
|
|
24
|
+
// Create store
|
|
25
|
+
const store = createEventState({ isVisible: false });
|
|
26
|
+
|
|
27
|
+
// Subscribe to visibility changes
|
|
28
|
+
store.subscribe('isVisible', (value) => {
|
|
29
|
+
const message = document.getElementById('message');
|
|
30
|
+
message.style.display = value ? 'block' : 'none';
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Toggle on checkbox change
|
|
34
|
+
document.getElementById('toggle').onchange = (e) => {
|
|
35
|
+
store.set('isVisible', e.target.checked);
|
|
36
|
+
};
|
|
37
|
+
</script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 006 List Rendering - Dynamic Arrays
|
|
2
|
+
|
|
3
|
+
Demonstrates how to render lists from array state.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- Input to add items
|
|
8
|
+
- Dynamic list that updates on state change
|
|
9
|
+
- **No `v-for` or `.map()` in JSX** - just `innerHTML`
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// 1. Store array in state
|
|
15
|
+
const store = createEventState({ items: [] });
|
|
16
|
+
|
|
17
|
+
// 2. Render function
|
|
18
|
+
const renderList = () => {
|
|
19
|
+
const items = store.get('items');
|
|
20
|
+
const list = document.getElementById('itemList');
|
|
21
|
+
list.innerHTML = items.map(item => `<li>${item}</li>`).join('');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// 3. Subscribe to array changes
|
|
25
|
+
store.subscribe('items', renderList);
|
|
26
|
+
|
|
27
|
+
// 4. Add items by creating new array
|
|
28
|
+
const items = store.get('items');
|
|
29
|
+
store.set('items', [...items, newItem]);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Key Insight
|
|
33
|
+
|
|
34
|
+
**List rendering is just string concatenation.**
|
|
35
|
+
|
|
36
|
+
Other frameworks need:
|
|
37
|
+
- React: `{items.map(item => <li>{item}</li>)}`
|
|
38
|
+
- Vue: `v-for="item in items"`
|
|
39
|
+
- Svelte: `{#each items as item}`
|
|
40
|
+
|
|
41
|
+
**EventState:** Just use `.map()` and `innerHTML`. That's it.
|
|
42
|
+
|
|
43
|
+
## Important
|
|
44
|
+
|
|
45
|
+
We create a **new array** with `[...items, newItem]` instead of mutating. This ensures the subscription fires (new reference = change detected).
|
|
46
|
+
|
|
47
|
+
## Run It
|
|
48
|
+
|
|
49
|
+
Open `index.html` in a browser. Type items and click Add (or press Enter).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>006 List Rendering - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<h1>Shopping List</h1>
|
|
13
|
+
|
|
14
|
+
<input type="text" id="itemInput" placeholder="Add item...">
|
|
15
|
+
<button id="addBtn">Add</button>
|
|
16
|
+
|
|
17
|
+
<ul id="itemList"></ul>
|
|
18
|
+
|
|
19
|
+
<script type="module">
|
|
20
|
+
import { createEventState } from '@uistate/core';
|
|
21
|
+
|
|
22
|
+
// Create store
|
|
23
|
+
const store = createEventState({ items: [] });
|
|
24
|
+
|
|
25
|
+
// Render list
|
|
26
|
+
const renderList = () => {
|
|
27
|
+
const items = store.get('items');
|
|
28
|
+
const list = document.getElementById('itemList');
|
|
29
|
+
|
|
30
|
+
if (items.length === 0) {
|
|
31
|
+
list.innerHTML = '<li><em>No items yet</em></li>';
|
|
32
|
+
} else {
|
|
33
|
+
list.innerHTML = items.map(item => `<li>${item}</li>`).join('');
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Subscribe to items changes
|
|
38
|
+
store.subscribe('items', renderList);
|
|
39
|
+
|
|
40
|
+
// Add item
|
|
41
|
+
document.getElementById('addBtn').onclick = () => {
|
|
42
|
+
const input = document.getElementById('itemInput');
|
|
43
|
+
const value = input.value.trim();
|
|
44
|
+
|
|
45
|
+
if (value) {
|
|
46
|
+
const items = store.get('items');
|
|
47
|
+
store.set('items', [...items, value]);
|
|
48
|
+
input.value = '';
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Add on Enter key
|
|
53
|
+
document.getElementById('itemInput').onkeypress = (e) => {
|
|
54
|
+
if (e.key === 'Enter') {
|
|
55
|
+
document.getElementById('addBtn').click();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Initial render
|
|
60
|
+
renderList();
|
|
61
|
+
</script>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 007 Form Validation - Real-time Feedback
|
|
2
|
+
|
|
3
|
+
Demonstrates form validation with multiple state paths and computed validity.
|
|
4
|
+
|
|
5
|
+
## What's Here
|
|
6
|
+
|
|
7
|
+
- Email and password inputs
|
|
8
|
+
- Real-time validation messages
|
|
9
|
+
- Submit button enabled only when form is valid
|
|
10
|
+
- **No form library needed** - just state and functions
|
|
11
|
+
|
|
12
|
+
## How It Works
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
// 1. Store input values AND validation state
|
|
16
|
+
const store = createEventState({
|
|
17
|
+
email: '',
|
|
18
|
+
password: '',
|
|
19
|
+
emailValid: false,
|
|
20
|
+
passwordValid: false
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 2. Validate on input change
|
|
24
|
+
store.subscribe('email', (value) => {
|
|
25
|
+
const isValid = validateEmail(value);
|
|
26
|
+
store.set('emailValid', isValid);
|
|
27
|
+
// Show error message
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 3. Enable submit when all fields valid
|
|
31
|
+
store.subscribe('emailValid', updateSubmitButton);
|
|
32
|
+
store.subscribe('passwordValid', updateSubmitButton);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Key Insight
|
|
36
|
+
|
|
37
|
+
**Validation is just derived state.**
|
|
38
|
+
|
|
39
|
+
- Input values are **source state**
|
|
40
|
+
- Validation flags are **derived state**
|
|
41
|
+
- Submit button state is **computed from derived state**
|
|
42
|
+
|
|
43
|
+
Other frameworks need:
|
|
44
|
+
- React: `useForm` hooks, validation libraries
|
|
45
|
+
- Vue: `v-model` + validation plugins
|
|
46
|
+
- Svelte: Stores + validation actions
|
|
47
|
+
|
|
48
|
+
**EventState:** Just subscribe to inputs, validate, and update state. That's it.
|
|
49
|
+
|
|
50
|
+
## Run It
|
|
51
|
+
|
|
52
|
+
Open `index.html` in a browser. Type invalid data to see error messages. Submit button enables when both fields are valid.
|