@uistate/core 5.2.0 → 5.4.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 (142) hide show
  1. package/index.js +2 -9
  2. package/package.json +4 -10
  3. package/queryClient.js +55 -0
  4. package/cssState.js +0 -212
  5. package/examples/001-counter/README.md +0 -44
  6. package/examples/001-counter/index.html +0 -33
  7. package/examples/002-counter-improved/README.md +0 -44
  8. package/examples/002-counter-improved/index.html +0 -47
  9. package/examples/003-input-reactive/README.md +0 -44
  10. package/examples/003-input-reactive/index.html +0 -33
  11. package/examples/004-computed-state/README.md +0 -45
  12. package/examples/004-computed-state/index.html +0 -65
  13. package/examples/005-conditional-rendering/README.md +0 -42
  14. package/examples/005-conditional-rendering/index.html +0 -39
  15. package/examples/006-list-rendering/README.md +0 -49
  16. package/examples/006-list-rendering/index.html +0 -63
  17. package/examples/007-form-validation/README.md +0 -52
  18. package/examples/007-form-validation/index.html +0 -102
  19. package/examples/008-undo-redo/README.md +0 -70
  20. package/examples/008-undo-redo/index.html +0 -108
  21. package/examples/009-localStorage-side-effects/README.md +0 -72
  22. package/examples/009-localStorage-side-effects/index.html +0 -57
  23. package/examples/010-decoupled-components/README.md +0 -74
  24. package/examples/010-decoupled-components/index.html +0 -93
  25. package/examples/011-async-patterns/README.md +0 -98
  26. package/examples/011-async-patterns/index.html +0 -132
  27. package/examples/028-counter-improved-eventTest/LICENSE +0 -55
  28. package/examples/028-counter-improved-eventTest/README.md +0 -131
  29. package/examples/028-counter-improved-eventTest/app/store.js +0 -9
  30. package/examples/028-counter-improved-eventTest/index.html +0 -49
  31. package/examples/028-counter-improved-eventTest/runtime/core/behaviors.runtime.js +0 -282
  32. package/examples/028-counter-improved-eventTest/runtime/core/eventState.js +0 -100
  33. package/examples/028-counter-improved-eventTest/runtime/core/eventStateNew.js +0 -149
  34. package/examples/028-counter-improved-eventTest/runtime/core/helpers.js +0 -212
  35. package/examples/028-counter-improved-eventTest/runtime/core/router.js +0 -271
  36. package/examples/028-counter-improved-eventTest/store.d.ts +0 -8
  37. package/examples/028-counter-improved-eventTest/style.css +0 -170
  38. package/examples/028-counter-improved-eventTest/tests/README.md +0 -208
  39. package/examples/028-counter-improved-eventTest/tests/counter.test.js +0 -116
  40. package/examples/028-counter-improved-eventTest/tests/eventTest.js +0 -176
  41. package/examples/028-counter-improved-eventTest/tests/generateTypes.js +0 -168
  42. package/examples/028-counter-improved-eventTest/tests/run.js +0 -20
  43. package/examples/030-todo-app-with-eventTest/LICENSE +0 -55
  44. package/examples/030-todo-app-with-eventTest/README.md +0 -121
  45. package/examples/030-todo-app-with-eventTest/app/router.js +0 -25
  46. package/examples/030-todo-app-with-eventTest/app/store.js +0 -16
  47. package/examples/030-todo-app-with-eventTest/app/views/home.js +0 -11
  48. package/examples/030-todo-app-with-eventTest/app/views/todoDemo.js +0 -88
  49. package/examples/030-todo-app-with-eventTest/index.html +0 -65
  50. package/examples/030-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  51. package/examples/030-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  52. package/examples/030-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  53. package/examples/030-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  54. package/examples/030-todo-app-with-eventTest/runtime/core/router.js +0 -271
  55. package/examples/030-todo-app-with-eventTest/store.d.ts +0 -18
  56. package/examples/030-todo-app-with-eventTest/style.css +0 -170
  57. package/examples/030-todo-app-with-eventTest/tests/README.md +0 -208
  58. package/examples/030-todo-app-with-eventTest/tests/eventTest.js +0 -176
  59. package/examples/030-todo-app-with-eventTest/tests/generateTypes.js +0 -189
  60. package/examples/030-todo-app-with-eventTest/tests/run.js +0 -20
  61. package/examples/030-todo-app-with-eventTest/tests/todos.test.js +0 -167
  62. package/examples/031-todo-app-with-eventTest/LICENSE +0 -55
  63. package/examples/031-todo-app-with-eventTest/README.md +0 -54
  64. package/examples/031-todo-app-with-eventTest/TUTORIAL.md +0 -390
  65. package/examples/031-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  66. package/examples/031-todo-app-with-eventTest/app/bridges.js +0 -113
  67. package/examples/031-todo-app-with-eventTest/app/router.js +0 -26
  68. package/examples/031-todo-app-with-eventTest/app/store.js +0 -15
  69. package/examples/031-todo-app-with-eventTest/app/views/home.js +0 -46
  70. package/examples/031-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  71. package/examples/031-todo-app-with-eventTest/devtools/dock.js +0 -41
  72. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  73. package/examples/031-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  74. package/examples/031-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  75. package/examples/031-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  76. package/examples/031-todo-app-with-eventTest/index.html +0 -103
  77. package/examples/031-todo-app-with-eventTest/package-lock.json +0 -2184
  78. package/examples/031-todo-app-with-eventTest/package.json +0 -24
  79. package/examples/031-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  80. package/examples/031-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  81. package/examples/031-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  82. package/examples/031-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  83. package/examples/031-todo-app-with-eventTest/runtime/core/router.js +0 -271
  84. package/examples/031-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  85. package/examples/031-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  86. package/examples/031-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  87. package/examples/031-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  88. package/examples/031-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  89. package/examples/031-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  90. package/examples/031-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  91. package/examples/031-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  92. package/examples/031-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  93. package/examples/031-todo-app-with-eventTest/store.d.ts +0 -23
  94. package/examples/031-todo-app-with-eventTest/style.css +0 -170
  95. package/examples/031-todo-app-with-eventTest/tests/README.md +0 -208
  96. package/examples/031-todo-app-with-eventTest/tests/eventTest.js +0 -176
  97. package/examples/031-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  98. package/examples/031-todo-app-with-eventTest/tests/run.js +0 -20
  99. package/examples/031-todo-app-with-eventTest/tests/todos.test.js +0 -192
  100. package/examples/032-todo-app-with-eventTest/LICENSE +0 -55
  101. package/examples/032-todo-app-with-eventTest/README.md +0 -54
  102. package/examples/032-todo-app-with-eventTest/TUTORIAL.md +0 -390
  103. package/examples/032-todo-app-with-eventTest/WHY_EVENTSTATE.md +0 -777
  104. package/examples/032-todo-app-with-eventTest/app/actions/index.js +0 -153
  105. package/examples/032-todo-app-with-eventTest/app/bridges.js +0 -113
  106. package/examples/032-todo-app-with-eventTest/app/router.js +0 -26
  107. package/examples/032-todo-app-with-eventTest/app/store.js +0 -15
  108. package/examples/032-todo-app-with-eventTest/app/views/home.js +0 -46
  109. package/examples/032-todo-app-with-eventTest/app/views/todoDemo.js +0 -69
  110. package/examples/032-todo-app-with-eventTest/devtools/dock.js +0 -41
  111. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.dock.js +0 -10
  112. package/examples/032-todo-app-with-eventTest/devtools/stateTracker.js +0 -246
  113. package/examples/032-todo-app-with-eventTest/devtools/telemetry.js +0 -104
  114. package/examples/032-todo-app-with-eventTest/devtools/typeGenerator.js +0 -339
  115. package/examples/032-todo-app-with-eventTest/index.html +0 -87
  116. package/examples/032-todo-app-with-eventTest/package-lock.json +0 -2184
  117. package/examples/032-todo-app-with-eventTest/package.json +0 -24
  118. package/examples/032-todo-app-with-eventTest/runtime/core/behaviors.runtime.js +0 -282
  119. package/examples/032-todo-app-with-eventTest/runtime/core/eventState.js +0 -100
  120. package/examples/032-todo-app-with-eventTest/runtime/core/eventStateNew.js +0 -149
  121. package/examples/032-todo-app-with-eventTest/runtime/core/helpers.js +0 -212
  122. package/examples/032-todo-app-with-eventTest/runtime/core/router.js +0 -271
  123. package/examples/032-todo-app-with-eventTest/runtime/extensions/boundary.js +0 -36
  124. package/examples/032-todo-app-with-eventTest/runtime/extensions/converge.js +0 -63
  125. package/examples/032-todo-app-with-eventTest/runtime/extensions/eventState.plus.js +0 -210
  126. package/examples/032-todo-app-with-eventTest/runtime/extensions/hydrate.js +0 -157
  127. package/examples/032-todo-app-with-eventTest/runtime/extensions/queryBinding.js +0 -69
  128. package/examples/032-todo-app-with-eventTest/runtime/forms/computed.js +0 -78
  129. package/examples/032-todo-app-with-eventTest/runtime/forms/meta.js +0 -51
  130. package/examples/032-todo-app-with-eventTest/runtime/forms/submitWithBoundary.js +0 -28
  131. package/examples/032-todo-app-with-eventTest/runtime/forms/validators.js +0 -55
  132. package/examples/032-todo-app-with-eventTest/store.d.ts +0 -23
  133. package/examples/032-todo-app-with-eventTest/style.css +0 -170
  134. package/examples/032-todo-app-with-eventTest/tests/README.md +0 -208
  135. package/examples/032-todo-app-with-eventTest/tests/eventTest.js +0 -176
  136. package/examples/032-todo-app-with-eventTest/tests/generateTypes.js +0 -191
  137. package/examples/032-todo-app-with-eventTest/tests/run.js +0 -20
  138. package/examples/032-todo-app-with-eventTest/tests/todos.test.js +0 -192
  139. package/playground/exercise001.html +0 -38
  140. package/playground/exercise002.html +0 -49
  141. package/stateSerializer.js +0 -267
  142. package/templateManager.js +0 -216
@@ -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>