@uistate/core 5.1.1 → 5.3.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.
Files changed (139) hide show
  1. package/eventStateNew.js +8 -2
  2. package/package.json +5 -5
  3. package/queryClient.js +55 -0
  4. package/examples/001-counter/README.md +0 -44
  5. package/examples/001-counter/index.html +0 -33
  6. package/examples/002-counter-improved/README.md +0 -44
  7. package/examples/002-counter-improved/index.html +0 -47
  8. package/examples/003-input-reactive/README.md +0 -44
  9. package/examples/003-input-reactive/index.html +0 -33
  10. package/examples/004-computed-state/README.md +0 -45
  11. package/examples/004-computed-state/index.html +0 -65
  12. package/examples/005-conditional-rendering/README.md +0 -42
  13. package/examples/005-conditional-rendering/index.html +0 -39
  14. package/examples/006-list-rendering/README.md +0 -49
  15. package/examples/006-list-rendering/index.html +0 -63
  16. package/examples/007-form-validation/README.md +0 -52
  17. package/examples/007-form-validation/index.html +0 -102
  18. package/examples/008-undo-redo/README.md +0 -70
  19. package/examples/008-undo-redo/index.html +0 -108
  20. package/examples/009-localStorage-side-effects/README.md +0 -72
  21. package/examples/009-localStorage-side-effects/index.html +0 -57
  22. package/examples/010-decoupled-components/README.md +0 -74
  23. package/examples/010-decoupled-components/index.html +0 -93
  24. package/examples/011-async-patterns/README.md +0 -98
  25. package/examples/011-async-patterns/index.html +0 -132
  26. package/examples/028-counter-improved-eventTest/LICENSE +0 -55
  27. package/examples/028-counter-improved-eventTest/README.md +0 -131
  28. package/examples/028-counter-improved-eventTest/app/store.js +0 -9
  29. package/examples/028-counter-improved-eventTest/index.html +0 -49
  30. package/examples/028-counter-improved-eventTest/runtime/core/behaviors.runtime.js +0 -282
  31. package/examples/028-counter-improved-eventTest/runtime/core/eventState.js +0 -100
  32. package/examples/028-counter-improved-eventTest/runtime/core/eventStateNew.js +0 -149
  33. package/examples/028-counter-improved-eventTest/runtime/core/helpers.js +0 -212
  34. package/examples/028-counter-improved-eventTest/runtime/core/router.js +0 -271
  35. package/examples/028-counter-improved-eventTest/store.d.ts +0 -8
  36. package/examples/028-counter-improved-eventTest/style.css +0 -170
  37. package/examples/028-counter-improved-eventTest/tests/README.md +0 -208
  38. package/examples/028-counter-improved-eventTest/tests/counter.test.js +0 -116
  39. package/examples/028-counter-improved-eventTest/tests/eventTest.js +0 -176
  40. package/examples/028-counter-improved-eventTest/tests/generateTypes.js +0 -168
  41. package/examples/028-counter-improved-eventTest/tests/run.js +0 -20
  42. package/examples/030-todo-app-with-eventTest/LICENSE +0 -55
  43. package/examples/030-todo-app-with-eventTest/README.md +0 -121
  44. package/examples/030-todo-app-with-eventTest/app/router.js +0 -25
  45. package/examples/030-todo-app-with-eventTest/app/store.js +0 -16
  46. package/examples/030-todo-app-with-eventTest/app/views/home.js +0 -11
  47. package/examples/030-todo-app-with-eventTest/app/views/todoDemo.js +0 -88
  48. package/examples/030-todo-app-with-eventTest/index.html +0 -65
  49. package/examples/030-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  50. package/examples/030-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  51. package/examples/030-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  52. package/examples/030-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  53. package/examples/030-todo-app-with-eventTest/runtime/core/router.js +0 -271
  54. package/examples/030-todo-app-with-eventTest/store.d.ts +0 -18
  55. package/examples/030-todo-app-with-eventTest/style.css +0 -170
  56. package/examples/030-todo-app-with-eventTest/tests/README.md +0 -208
  57. package/examples/030-todo-app-with-eventTest/tests/eventTest.js +0 -176
  58. package/examples/030-todo-app-with-eventTest/tests/generateTypes.js +0 -189
  59. package/examples/030-todo-app-with-eventTest/tests/run.js +0 -20
  60. package/examples/030-todo-app-with-eventTest/tests/todos.test.js +0 -167
  61. package/examples/031-todo-app-with-eventTest/LICENSE +0 -55
  62. package/examples/031-todo-app-with-eventTest/README.md +0 -54
  63. package/examples/031-todo-app-with-eventTest/TUTORIAL.md +0 -390
  64. package/examples/031-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  65. package/examples/031-todo-app-with-eventTest/app/bridges.js +0 -113
  66. package/examples/031-todo-app-with-eventTest/app/router.js +0 -26
  67. package/examples/031-todo-app-with-eventTest/app/store.js +0 -15
  68. package/examples/031-todo-app-with-eventTest/app/views/home.js +0 -46
  69. package/examples/031-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  70. package/examples/031-todo-app-with-eventTest/devtools/dock.js +0 -41
  71. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  72. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  73. package/examples/031-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  74. package/examples/031-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  75. package/examples/031-todo-app-with-eventTest/index.html +0 -103
  76. package/examples/031-todo-app-with-eventTest/package-lock.json +0 -2184
  77. package/examples/031-todo-app-with-eventTest/package.json +0 -24
  78. package/examples/031-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  79. package/examples/031-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  80. package/examples/031-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  81. package/examples/031-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  82. package/examples/031-todo-app-with-eventTest/runtime/core/router.js +0 -271
  83. package/examples/031-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  84. package/examples/031-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  85. package/examples/031-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  86. package/examples/031-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  87. package/examples/031-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  88. package/examples/031-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  89. package/examples/031-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  90. package/examples/031-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  91. package/examples/031-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  92. package/examples/031-todo-app-with-eventTest/store.d.ts +0 -23
  93. package/examples/031-todo-app-with-eventTest/style.css +0 -170
  94. package/examples/031-todo-app-with-eventTest/tests/README.md +0 -208
  95. package/examples/031-todo-app-with-eventTest/tests/eventTest.js +0 -176
  96. package/examples/031-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  97. package/examples/031-todo-app-with-eventTest/tests/run.js +0 -20
  98. package/examples/031-todo-app-with-eventTest/tests/todos.test.js +0 -192
  99. package/examples/032-todo-app-with-eventTest/LICENSE +0 -55
  100. package/examples/032-todo-app-with-eventTest/README.md +0 -54
  101. package/examples/032-todo-app-with-eventTest/TUTORIAL.md +0 -390
  102. package/examples/032-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  103. package/examples/032-todo-app-with-eventTest/app/actions/index.js +0 -153
  104. package/examples/032-todo-app-with-eventTest/app/bridges.js +0 -113
  105. package/examples/032-todo-app-with-eventTest/app/router.js +0 -26
  106. package/examples/032-todo-app-with-eventTest/app/store.js +0 -15
  107. package/examples/032-todo-app-with-eventTest/app/views/home.js +0 -46
  108. package/examples/032-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  109. package/examples/032-todo-app-with-eventTest/devtools/dock.js +0 -41
  110. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  111. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  112. package/examples/032-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  113. package/examples/032-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  114. package/examples/032-todo-app-with-eventTest/index.html +0 -87
  115. package/examples/032-todo-app-with-eventTest/package-lock.json +0 -2184
  116. package/examples/032-todo-app-with-eventTest/package.json +0 -24
  117. package/examples/032-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  118. package/examples/032-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  119. package/examples/032-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  120. package/examples/032-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  121. package/examples/032-todo-app-with-eventTest/runtime/core/router.js +0 -271
  122. package/examples/032-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  123. package/examples/032-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  124. package/examples/032-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  125. package/examples/032-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  126. package/examples/032-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  127. package/examples/032-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  128. package/examples/032-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  129. package/examples/032-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  130. package/examples/032-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  131. package/examples/032-todo-app-with-eventTest/store.d.ts +0 -23
  132. package/examples/032-todo-app-with-eventTest/style.css +0 -170
  133. package/examples/032-todo-app-with-eventTest/tests/README.md +0 -208
  134. package/examples/032-todo-app-with-eventTest/tests/eventTest.js +0 -176
  135. package/examples/032-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  136. package/examples/032-todo-app-with-eventTest/tests/run.js +0 -20
  137. package/examples/032-todo-app-with-eventTest/tests/todos.test.js +0 -192
  138. package/playground/exercise001.html +0 -38
  139. package/playground/exercise002.html +0 -49
package/eventStateNew.js CHANGED
@@ -61,7 +61,13 @@ export function createEventState(initial = {}) {
61
61
  get(path) {
62
62
  if (destroyed) throw new Error('Cannot get from destroyed store');
63
63
  if (!path) return state;
64
- return path.split(".").reduce((obj, key) => obj?.[key], state);
64
+ const parts = path.split('.');
65
+ let cur = state;
66
+ for (const p of parts) {
67
+ if (cur == null) return undefined;
68
+ cur = cur[p];
69
+ }
70
+ return cur;
65
71
  },
66
72
 
67
73
  /**
@@ -140,7 +146,7 @@ export function createEventState(initial = {}) {
140
146
 
141
147
  if (destroyed) throw new Error('Cannot setAsync on destroyed store');
142
148
 
143
- this.set(path, data);
149
+ this.set(`${path}.data`, data);
144
150
  this.set(`${path}.status`, 'success');
145
151
  return data;
146
152
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uistate/core",
3
- "version": "5.1.1",
3
+ "version": "5.3.0",
4
4
  "description": "Lightweight event-driven state management with slot orchestration and experimental event-sequence testing (eventTest.js available under dual license)",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -10,7 +10,8 @@
10
10
  "./eventStateNew": "./eventStateNew.js",
11
11
  "./cssState": "./cssState.js",
12
12
  "./stateSerializer": "./stateSerializer.js",
13
- "./templateManager": "./templateManager.js"
13
+ "./templateManager": "./templateManager.js",
14
+ "./query": "./queryClient.js"
14
15
  },
15
16
  "files": [
16
17
  "index.js",
@@ -19,11 +20,10 @@
19
20
  "cssState.js",
20
21
  "stateSerializer.js",
21
22
  "templateManager.js",
23
+ "queryClient.js",
22
24
  "eventTest.js",
23
25
  "LICENSE",
24
- "LICENSE-eventTest.md",
25
- "examples/",
26
- "playground/"
26
+ "LICENSE-eventTest.md"
27
27
  ],
28
28
  "keywords": [
29
29
  "state-management",
package/queryClient.js ADDED
@@ -0,0 +1,55 @@
1
+ // Mini QueryClient wrapper for EventState
2
+ // Thin async layer with standard query patterns
3
+
4
+ export const createQueryClient = (store) => {
5
+ return {
6
+ // Run a query with async state handling
7
+ async query(key, fetcher) {
8
+ return await store.setAsync(`query.${key}`, fetcher);
9
+ },
10
+
11
+ // Subscribe to query data
12
+ subscribe(key, cb) {
13
+ return store.subscribe(`query.${key}.data`, cb);
14
+ },
15
+
16
+ // Subscribe to query status
17
+ subscribeToStatus(key, cb) {
18
+ return store.subscribe(`query.${key}.status`, cb);
19
+ },
20
+
21
+ // Subscribe to query errors
22
+
23
+ subscribeToError(key, cb) {
24
+ return store.subscribe(`query.${key}.error`, cb);
25
+ },
26
+
27
+ // Read current query data
28
+ getData(key) {
29
+ return store.get(`query.${key}.data`);
30
+ },
31
+
32
+ // Read current query status
33
+ getStatus(key) {
34
+ return store.get(`query.${key}.status`);
35
+ },
36
+
37
+ // Read current query error
38
+ getError(key) {
39
+ return store.get(`query.${key}.error`);
40
+ },
41
+
42
+ // Cancel active query
43
+ cancel(key) {
44
+ store.cancel(`query.${key}`);
45
+ },
46
+
47
+ // Reset query to idle
48
+ invalidate(key) {
49
+ const p = `query.${key}`;
50
+ store.set(`${p}.data`, null);
51
+ store.set(`${p}.status`, "idle");
52
+ store.set(`${p}.error`, null);
53
+ },
54
+ };
55
+ };
@@ -1,44 +0,0 @@
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
@@ -1,33 +0,0 @@
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>
@@ -1,44 +0,0 @@
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
@@ -1,47 +0,0 @@
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>
@@ -1,44 +0,0 @@
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
@@ -1,33 +0,0 @@
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>
@@ -1,45 +0,0 @@
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.
@@ -1,65 +0,0 @@
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>
@@ -1,42 +0,0 @@
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.
@@ -1,39 +0,0 @@
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>
@@ -1,49 +0,0 @@
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).
@@ -1,63 +0,0 @@
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>