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