@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
@@ -1,191 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * generateTypes.js - Generate TypeScript definitions from test assertions
4
- *
5
- * Usage: node tests/generateTypes.js
6
- */
7
-
8
- import { createEventTest } from './eventTest.js';
9
- import todosTests from './todos.test.js';
10
- import { writeFileSync } from 'fs';
11
- import { fileURLToPath } from 'url';
12
- import { dirname, join } from 'path';
13
-
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = dirname(__filename);
16
-
17
- // Collect all type assertions from tests
18
- const allAssertions = [];
19
-
20
- console.log('🔍 Extracting types from tests...\n');
21
-
22
- for (const [name, testFn] of Object.entries(todosTests)) {
23
- try {
24
- // Run test to collect assertions
25
- testFn();
26
- console.log(`✓ ${name}`);
27
- } catch (error) {
28
- // Test might fail, but we still want type assertions
29
- console.log(`⚠ ${name} (failed but types extracted)`);
30
- }
31
- }
32
-
33
- // Simple type generator (minimal version for Node.js)
34
- class SimpleTypeGenerator {
35
- constructor() {
36
- this.pathTypes = new Map();
37
- }
38
-
39
- parseAssertions(assertions) {
40
- for (const assertion of assertions) {
41
- const { path, type, elementShape, shape } = assertion;
42
-
43
- if (type === 'array' && elementShape) {
44
- this.pathTypes.set(path, {
45
- type: 'array',
46
- element: elementShape
47
- });
48
- } else if (type === 'object' && shape) {
49
- this.pathTypes.set(path, {
50
- type: 'object',
51
- shape: shape
52
- });
53
- } else if (type) {
54
- this.pathTypes.set(path, { type });
55
- }
56
- }
57
- }
58
-
59
- buildTree() {
60
- const tree = {};
61
-
62
- for (const [path, typeInfo] of this.pathTypes) {
63
- const parts = path.split('.');
64
- let current = tree;
65
-
66
- for (let i = 0; i < parts.length; i++) {
67
- const part = parts[i];
68
-
69
- if (i === parts.length - 1) {
70
- current[part] = { __typeInfo: typeInfo };
71
- } else {
72
- if (!current[part]) {
73
- current[part] = {};
74
- }
75
- current = current[part];
76
- }
77
- }
78
- }
79
-
80
- return tree;
81
- }
82
-
83
- formatType(typeInfo) {
84
- if (typeof typeInfo === 'string') return typeInfo;
85
-
86
- if (typeInfo.type === 'array' && typeInfo.element) {
87
- const elementType = this.formatShape(typeInfo.element);
88
- return `Array<${elementType}>`;
89
- }
90
-
91
- if (typeInfo.type === 'object' && typeInfo.shape) {
92
- return this.formatShape(typeInfo.shape);
93
- }
94
-
95
- return typeInfo.type || 'unknown';
96
- }
97
-
98
- formatShape(shape) {
99
- if (typeof shape === 'string') return shape;
100
-
101
- const props = Object.entries(shape)
102
- .map(([k, v]) => {
103
- const type = typeof v === 'string' ? v : this.formatShape(v);
104
- return `${k}: ${type}`;
105
- })
106
- .join('; ');
107
-
108
- return `{ ${props} }`;
109
- }
110
-
111
- generateInterface(obj, indent = 0) {
112
- const lines = [];
113
- const spaces = ' '.repeat(indent);
114
-
115
- for (const [key, value] of Object.entries(obj)) {
116
- if (value.__typeInfo) {
117
- const type = this.formatType(value.__typeInfo);
118
- lines.push(`${spaces}${key}: ${type};`);
119
- } else {
120
- lines.push(`${spaces}${key}: {`);
121
- lines.push(this.generateInterface(value, indent + 1));
122
- lines.push(`${spaces}};`);
123
- }
124
- }
125
-
126
- return lines.join('\n');
127
- }
128
-
129
- generateDTS() {
130
- const lines = [
131
- '// Auto-generated from test assertions',
132
- '// DO NOT EDIT - regenerate by running: node tests/generateTypes.js',
133
- '',
134
- 'export interface StoreState {',
135
- ];
136
-
137
- const tree = this.buildTree();
138
- lines.push(this.generateInterface(tree, 1));
139
- lines.push('}');
140
- lines.push('');
141
- lines.push('export default StoreState;');
142
- lines.push('');
143
-
144
- return lines.join('\n');
145
- }
146
- }
147
-
148
- // Note: We need to actually collect assertions from test execution
149
- // For now, manually define expected types based on test assertions
150
- const manualAssertions = [
151
- {
152
- path: 'domain.todos.items',
153
- type: 'array',
154
- elementShape: {
155
- id: 'number',
156
- text: 'string',
157
- done: 'boolean'
158
- }
159
- },
160
- {
161
- path: 'ui.todos.filter',
162
- type: 'string'
163
- },
164
- {
165
- path: 'intent.todo.add',
166
- type: 'object',
167
- shape: {
168
- text: 'string'
169
- }
170
- },
171
- {
172
- path: 'intent.todo.toggle',
173
- type: 'object',
174
- shape: {
175
- id: 'number'
176
- }
177
- }
178
- ];
179
-
180
- const generator = new SimpleTypeGenerator();
181
- generator.parseAssertions(manualAssertions);
182
-
183
- const dts = generator.generateDTS();
184
-
185
- // Write to file
186
- const outputPath = join(__dirname, '..', 'store.d.ts');
187
- writeFileSync(outputPath, dts, 'utf-8');
188
-
189
- console.log(`\n✅ Generated ${outputPath}`);
190
- console.log('\n📝 Type definitions:\n');
191
- console.log(dts);
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * run.js - Test runner for Node.js
4
- *
5
- * Usage: node tests/run.js
6
- */
7
-
8
- import { runTests } from './eventTest.js';
9
- import todosTests from './todos.test.js';
10
-
11
- // Combine all test suites
12
- const allTests = {
13
- ...todosTests
14
- };
15
-
16
- // Run tests
17
- const results = runTests(allTests);
18
-
19
- // Exit with appropriate code
20
- process.exit(results.failed > 0 ? 1 : 0);
@@ -1,192 +0,0 @@
1
- /**
2
- * todos.test.js - Event-sequence tests for todo functionality
3
- *
4
- * These tests define the behavior AND types of the todo domain
5
- */
6
-
7
- import { createEventTest, test, runTests } from './eventTest.js';
8
-
9
- // Helper to set up todo bridges on a test store
10
- function setupTodoBridges(store) {
11
- let nextId = 1;
12
-
13
- // Add todo
14
- store.subscribe('intent.todo.add', (detail) => {
15
- const { text } = detail;
16
- const items = store.get('domain.todos.items') || [];
17
- const todo = { id: nextId++, text: String(text || '').trim(), done: false };
18
- if (!todo.text) return;
19
- store.set('domain.todos.items', [...items, todo]);
20
- });
21
-
22
- // Toggle todo
23
- store.subscribe('intent.todo.toggle', (detail) => {
24
- const { id } = detail;
25
- const items = store.get('domain.todos.items') || [];
26
- const out = items.map(t => (String(t?.id) === String(id)) ? { ...t, done: !t.done } : t);
27
- store.set('domain.todos.items', out);
28
- });
29
-
30
- // Clear completed
31
- store.subscribe('intent.todo.clearCompleted', () => {
32
- const items = store.get('domain.todos.items') || [];
33
- store.set('domain.todos.items', items.filter(t => !t.done));
34
- });
35
-
36
- // UI filter
37
- store.subscribe('intent.ui.filter', (detail) => {
38
- const { filter } = detail;
39
- const f = (filter === 'active' || filter === 'completed') ? filter : 'all';
40
- store.set('ui.todos.filter', f);
41
- });
42
- }
43
-
44
- // Test suite
45
- const tests = {
46
- 'add todo creates correct structure': () => {
47
- const t = createEventTest({
48
- domain: { todos: { items: [] } }
49
- });
50
-
51
- // Set up bridges for this test
52
- setupTodoBridges(t.store);
53
-
54
- // Trigger intent
55
- t.trigger('intent.todo.add', { text: 'Buy milk' });
56
-
57
- // Assert array structure and element types
58
- t.assertArrayOf('domain.todos.items', {
59
- id: 'number',
60
- text: 'string',
61
- done: 'boolean'
62
- });
63
-
64
- // Assert array length
65
- t.assertArrayLength('domain.todos.items', 1);
66
-
67
- // Assert specific values
68
- const items = t.store.get('domain.todos.items');
69
- if (items[0].text !== 'Buy milk') {
70
- throw new Error('Expected first todo text to be "Buy milk"');
71
- }
72
- if (items[0].done !== false) {
73
- throw new Error('Expected first todo to not be done');
74
- }
75
- },
76
-
77
- 'toggle todo changes done state': () => {
78
- const t = createEventTest({
79
- domain: { todos: { items: [{ id: 1, text: 'Buy milk', done: false }] } }
80
- });
81
-
82
- // Set up bridges for this test
83
- setupTodoBridges(t.store);
84
-
85
- // Trigger toggle
86
- t.trigger('intent.todo.toggle', { id: 1 });
87
-
88
- // Assert structure
89
- t.assertArrayOf('domain.todos.items', {
90
- id: 'number',
91
- text: 'string',
92
- done: 'boolean'
93
- });
94
-
95
- // Assert done is now true
96
- const items = t.store.get('domain.todos.items');
97
- if (items[0].done !== true) {
98
- throw new Error('Expected todo to be done');
99
- }
100
- },
101
-
102
- 'clear completed removes done todos': () => {
103
- const t = createEventTest({
104
- domain: {
105
- todos: {
106
- items: [
107
- { id: 1, text: 'Buy milk', done: true },
108
- { id: 2, text: 'Walk dog', done: false }
109
- ]
110
- }
111
- }
112
- });
113
-
114
- // Set up bridges for this test
115
- setupTodoBridges(t.store);
116
-
117
- // Trigger clear
118
- t.trigger('intent.todo.clearCompleted');
119
-
120
- // Assert only active todo remains
121
- t.assertArrayLength('domain.todos.items', 1);
122
-
123
- const items = t.store.get('domain.todos.items');
124
- if (items[0].text !== 'Walk dog') {
125
- throw new Error('Expected only "Walk dog" to remain');
126
- }
127
- },
128
-
129
- 'filter intent updates filter state': () => {
130
- const t = createEventTest({
131
- ui: { todos: { filter: 'all' } }
132
- });
133
-
134
- // Set up bridges for this test
135
- setupTodoBridges(t.store);
136
-
137
- // Trigger filter change
138
- t.trigger('intent.ui.filter', { filter: 'active' });
139
-
140
- // Assert type
141
- t.assertType('ui.todos.filter', 'string');
142
-
143
- // Assert value
144
- t.assertPath('ui.todos.filter', 'active');
145
- },
146
-
147
- 'intent.todo.add has correct shape': () => {
148
- const t = createEventTest({});
149
-
150
- // Trigger intent
151
- t.trigger('intent.todo.add', { text: 'Buy milk' });
152
-
153
- // Assert intent shape (for type generation)
154
- t.assertShape('intent.todo.add', {
155
- text: 'string'
156
- });
157
- },
158
-
159
- 'intent.todo.toggle has correct shape': () => {
160
- const t = createEventTest({});
161
-
162
- // Trigger intent
163
- t.trigger('intent.todo.toggle', { id: 1 });
164
-
165
- // Assert intent shape
166
- t.assertShape('intent.todo.toggle', {
167
- id: 'number'
168
- });
169
- },
170
-
171
- 'empty todos array is valid': () => {
172
- const t = createEventTest({
173
- domain: { todos: { items: [] } }
174
- });
175
-
176
- // Assert empty array is still typed correctly
177
- t.assertArrayOf('domain.todos.items', {
178
- id: 'number',
179
- text: 'string',
180
- done: 'boolean'
181
- });
182
-
183
- t.assertArrayLength('domain.todos.items', 0);
184
- }
185
- };
186
-
187
- // Run tests if executed directly
188
- if (import.meta.url === `file://${process.argv[1]}`) {
189
- runTests(tests);
190
- }
191
-
192
- export default tests;
@@ -1,38 +0,0 @@
1
- <!-- playground/exercise001.html -->
2
- <!DOCTYPE html>
3
- <html>
4
- <head>
5
- <script type="importmap">
6
- { "imports": { "@uistate/core": "../index.js" } }
7
- </script>
8
- </head>
9
- <body>
10
- <h1>Counter: <span id="count">0</span></h1>
11
- <button id="increment">+</button>
12
- <button id="decrement">-</button>
13
-
14
- <script type="module">
15
- // (1) Import createEventState and create store
16
- import { createEventState } from '@uistate/core';
17
- const store = createEventState({
18
- count: 0
19
- });
20
-
21
- // (2) Increment store.count on button click
22
- document.getElementById('increment').onclick = () => {
23
- const current = store.get('count');
24
- store.set('count', current + 1);
25
- };
26
-
27
- // (3) Subscribe to count changes
28
- store.subscribe('count', (value) => {
29
- document.getElementById('count').textContent = value;
30
- });
31
-
32
- // (4) Exercise: Add decrement functionality
33
- // Hint: Copy the increment handler above and modify it
34
- // Your code here:
35
-
36
- </script>
37
- </body>
38
- </html>
@@ -1,49 +0,0 @@
1
- <!-- playground/exercise002.html -->
2
- <!DOCTYPE html>
3
- <html>
4
- <head>
5
- <script type="importmap">
6
- { "imports": { "@uistate/core": "../index.js" } }
7
- </script>
8
- <style>
9
- body { font-family: system-ui; padding: 2rem; }
10
- h1 { margin-bottom: 1rem; }
11
- </style>
12
- </head>
13
- <body>
14
- <h1 id="heading">Counter: <span id="count">0</span></h1>
15
- <button id="increment">+</button>
16
- <button id="decrement">-</button>
17
-
18
- <script type="module">
19
- // Working counter with increment and decrement
20
- import { createEventState } from '@uistate/core';
21
-
22
- const store = createEventState({
23
- count: 0
24
- });
25
-
26
- document.getElementById('increment').onclick = () => {
27
- store.set('count', store.get('count') + 1);
28
- };
29
-
30
- document.getElementById('decrement').onclick = () => {
31
- store.set('count', store.get('count') - 1);
32
- };
33
-
34
- store.subscribe('count', (value) => {
35
- document.getElementById('count').textContent = value;
36
- });
37
-
38
- // Exercise: Change the h1 color based on count value
39
- // - If count is positive: make it green
40
- // - If count is negative: make it red
41
- // - If count is zero: make it black
42
- //
43
- // Hint 1: Subscribe to 'count' changes
44
- // Hint 2: Use document.getElementById('heading').style.color = '...'
45
- // Your code here:
46
-
47
- </script>
48
- </body>
49
- </html>