@uistate/core 3.1.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +11 -2
  2. package/package.json +6 -15
  3. package/src/index.js +14 -349
package/README.md CHANGED
@@ -1,10 +1,19 @@
1
- # @uistate/core v3.0.0
1
+ # @uistate/core v3.1.2
2
2
 
3
3
  **author**: Ajdin Imsirovic <ajdika@live.com> (GitHub)
4
4
  **maintainer**: uistate <ajdika.i@gmail.com> (npm)
5
5
 
6
6
  High-performance UI state management using CSS custom properties and ADSI (Attribute-Driven State Inheritance). Focused heavily on DX and performance with a fully declarative approach.
7
7
 
8
+ ## What's New in v3.1.1
9
+
10
+ - 🛠️ Added `setStates()` method for batch state updates
11
+ - 🧩 Added `registerSpecialHandler()` and `registerEventBinding()` for extensibility
12
+ - 🏷️ New `setupObservers()` for automatic state-to-DOM binding
13
+ - ⚡ Improved `setupStateActions()` with customizable event handling
14
+ - 💾 Default event handlers for clicks and inputs with declarative binding
15
+ - 🗡️ Enhanced attribute serialization with specialized handlers
16
+
8
17
  ## What's New in v3.0.0
9
18
 
10
19
  - 🔄 Fully declarative state management approach
@@ -62,7 +71,7 @@ npm install @uistate/core
62
71
  │ ├── stateInspector.js # State inspection tools
63
72
  │ └── stateSerializer.js # State serialization
64
73
  └── examples/ # Example applications
65
- └── 001-slider-and-cards/ # Advanced slider example
74
+ └── 001-.../ # Progressive examples from simpler to more complex
66
75
  ```
67
76
 
68
77
  ## Browser Support
package/package.json CHANGED
@@ -1,22 +1,21 @@
1
1
  {
2
2
  "name": "@uistate/core",
3
- "version": "3.1.1",
3
+ "version": "4.0.0",
4
+ "type": "module",
4
5
  "description": "High-performance UI state management using CSS custom properties and ADSI (Attribute-Driven State Inheritance)",
5
6
  "main": "src/index.js",
6
7
  "module": "src/index.js",
7
8
  "files": [
8
9
  "src"
9
10
  ],
10
- "scripts": {
11
- "test": "jest",
12
- "lint": "eslint src"
13
- },
11
+ "scripts": {},
14
12
  "keywords": [
15
13
  "state-management",
16
14
  "ui",
17
- "react",
18
15
  "css",
19
- "performance"
16
+ "performance",
17
+ "framework-agnostic",
18
+ "vanilla-js"
20
19
  ],
21
20
  "author": "Ajdin Imsirovic <ajdika@live.com> (GitHub)",
22
21
  "contributors": [
@@ -31,13 +30,5 @@
31
30
  "url": "https://github.com/ImsirovicAjdin/uistate/issues"
32
31
  },
33
32
  "homepage": "https://github.com/ImsirovicAjdin/uistate#readme",
34
- "peerDependencies": {
35
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
36
- },
37
- "devDependencies": {
38
- "eslint": "^8.0.0",
39
- "jest": "^29.0.0",
40
- "jest-environment-jsdom": "^29.0.0"
41
- },
42
33
  "sideEffects": false
43
34
  }
package/src/index.js CHANGED
@@ -1,353 +1,18 @@
1
1
  /**
2
- * UIstate - Declarative state management for the web
3
- *
4
- * A unified system that combines CSS and Event-based state management with templating
5
- * Integrates CSS variables, event-based state, and template management for optimal performance
6
- * Provides a simple, declarative API for state management
7
- * Uses the DOM as the source of truth for state
8
- * Supports plugins through a clean composition system
2
+ * UIstate - Simple barrel file for core modules
3
+ *
4
+ * Exports the four core UIstate modules:
5
+ * - cssState: CSS custom properties state management
6
+ * - eventState: Event-based state management
7
+ * - stateSerializer: State serialization utilities
8
+ * - templateManager: Declarative template management
9
9
  */
10
- import createCssState from './cssState.js';
11
- import { createEventState } from './eventState.js';
12
- import { createTemplateManager } from './templateManager.js';
13
10
 
14
- /**
15
- * Utility function to convert hierarchical path notation to CSS variable notation
16
- * Converts dot notation (e.g., 'user.profile.name') to dash notation (e.g., 'user-profile-name')
17
- * @param {string} path - Hierarchical path with dot notation
18
- * @returns {string} - CSS-compatible path with dash notation
19
- */
20
- function convertPathToCssPath(path) {
21
- return path.replace(/\./g, "-");
22
- }
23
-
24
- const createUnifiedState = (initialState = {}) => {
25
- // Create the CSS state manager with integrated serialization
26
- const cssState = createCssState(initialState);
27
-
28
- // Create the event state manager with the same initial state
29
- const eventState = createEventState(initialState);
30
-
31
- // Plugin system
32
- const plugins = new Map();
33
- const middlewares = [];
34
- const lifecycleHooks = {
35
- beforeStateChange: [],
36
- afterStateChange: [],
37
- onInit: [],
38
- onDestroy: []
39
- };
40
-
41
- // Create a unified API
42
- const unifiedState = {
43
- _isNotifying: false,
44
- _cssState: cssState, // Reference to cssState for direct access to advanced features
45
-
46
- // Get state with hierarchical path support
47
- getState(path) {
48
- // Always use eventState as the source of truth
49
- // This ensures consistency with the DOM state
50
- const value = eventState.get(path);
51
-
52
- // If path is undefined or value not found in eventState
53
- if (path && value === undefined) {
54
- // Fall back to CSS variable (for values set outside this API)
55
- const cssPath = convertPathToCssPath(path);
56
- return cssState.getState(cssPath);
57
- }
58
-
59
- return value;
60
- },
61
-
62
- // Set state with hierarchical path support
63
- setState(path, value) {
64
- if (!path) return this;
65
-
66
- // Run before state change middlewares
67
- let finalValue = value;
68
- let shouldContinue = true;
69
-
70
- // Apply middlewares
71
- for (const middleware of middlewares) {
72
- const result = middleware(path, finalValue, this.getState.bind(this));
73
- if (result === false) {
74
- shouldContinue = false;
75
- break;
76
- } else if (result !== undefined) {
77
- finalValue = result;
78
- }
79
- }
80
-
81
- // Run lifecycle hooks
82
- for (const hook of lifecycleHooks.beforeStateChange) {
83
- hook(path, finalValue, this.getState.bind(this));
84
- }
85
-
86
- if (!shouldContinue) return this;
87
-
88
- // Update event state (for JS access and pub/sub)
89
- eventState.set(path, finalValue);
90
-
91
- // Update CSS state (for styling and DOM representation)
92
- const cssPath = convertPathToCssPath(path);
93
- cssState.setState(cssPath, finalValue);
94
-
95
- // Run after state change hooks
96
- for (const hook of lifecycleHooks.afterStateChange) {
97
- hook(path, finalValue, this.getState.bind(this));
98
- }
99
-
100
- return this;
101
- },
102
-
103
- // Plugin system methods
104
- use(pluginName, plugin) {
105
- if (plugins.has(pluginName)) {
106
- console.warn(`Plugin '${pluginName}' is already registered. It will be replaced.`);
107
- }
108
-
109
- // Register the plugin
110
- plugins.set(pluginName, plugin);
111
-
112
- // Initialize the plugin
113
- if (typeof plugin.init === 'function') {
114
- // Pass the API to the plugin
115
- plugin.init(this);
116
- }
117
-
118
- // Register middlewares
119
- if (Array.isArray(plugin.middlewares)) {
120
- middlewares.push(...plugin.middlewares);
121
- }
122
-
123
- // Register lifecycle hooks
124
- if (plugin.hooks) {
125
- Object.entries(plugin.hooks).forEach(([hookName, hookFn]) => {
126
- if (Array.isArray(lifecycleHooks[hookName])) {
127
- lifecycleHooks[hookName].push(hookFn);
128
- }
129
- });
130
- }
131
-
132
- // Add plugin methods to the API
133
- if (plugin.methods) {
134
- Object.entries(plugin.methods).forEach(([methodName, method]) => {
135
- if (typeof method === 'function') {
136
- this[methodName] = method.bind(plugin);
137
- }
138
- });
139
- }
140
-
141
- return this;
142
- },
143
-
144
- getPlugin(pluginName) {
145
- return plugins.get(pluginName);
146
- },
147
-
148
- hasPlugin(pluginName) {
149
- return plugins.has(pluginName);
150
- },
151
-
152
- removePlugin(pluginName) {
153
- const plugin = plugins.get(pluginName);
154
- if (!plugin) return false;
155
-
156
- // Run cleanup if available
157
- if (typeof plugin.destroy === 'function') {
158
- plugin.destroy();
159
- }
160
-
161
- // Remove plugin methods
162
- if (plugin.methods) {
163
- Object.keys(plugin.methods).forEach(methodName => {
164
- delete this[methodName];
165
- });
166
- }
167
-
168
- // Remove middlewares
169
- if (Array.isArray(plugin.middlewares)) {
170
- plugin.middlewares.forEach(middleware => {
171
- const index = middlewares.indexOf(middleware);
172
- if (index !== -1) {
173
- middlewares.splice(index, 1);
174
- }
175
- });
176
- }
177
-
178
- // Remove lifecycle hooks
179
- if (plugin.hooks) {
180
- Object.entries(plugin.hooks).forEach(([hookName, hookFn]) => {
181
- if (Array.isArray(lifecycleHooks[hookName])) {
182
- const index = lifecycleHooks[hookName].indexOf(hookFn);
183
- if (index !== -1) {
184
- lifecycleHooks[hookName].splice(index, 1);
185
- }
186
- }
187
- });
188
- }
189
-
190
- // Remove the plugin
191
- plugins.delete(pluginName);
192
- return true;
193
- },
194
-
195
- // Add middleware
196
- addMiddleware(middleware) {
197
- if (typeof middleware === 'function') {
198
- middlewares.push(middleware);
199
- return true;
200
- }
201
- return false;
202
- },
203
-
204
- // Add lifecycle hook
205
- addHook(hookName, hookFn) {
206
- if (typeof hookFn === 'function' && Array.isArray(lifecycleHooks[hookName])) {
207
- lifecycleHooks[hookName].push(hookFn);
208
- return true;
209
- }
210
- return false;
211
- },
212
-
213
- // Subscribe to state changes with support for wildcards
214
- subscribe(path, callback) {
215
- return eventState.subscribe(path, callback);
216
- },
217
-
218
- // Observe CSS state changes (simpler API, no wildcards)
219
- observe(key, callback) {
220
- return cssState.observe(key, callback);
221
- },
222
-
223
- // Configure serialization options
224
- configureSerializer(config) {
225
- cssState.configureSerializer(config);
226
- return this;
227
- },
228
-
229
- // Get current serializer configuration
230
- getSerializerConfig() {
231
- return cssState._serializer?.config || null;
232
- },
233
-
234
- // Set serialization mode ('hybrid', 'json', or 'escape')
235
- setSerializationMode(mode) {
236
- return this.configureSerializer({ mode });
237
- },
238
-
239
- // Clean up resources
240
- destroy() {
241
- // Run onDestroy hooks
242
- for (const hook of lifecycleHooks.onDestroy) {
243
- hook();
244
- }
245
-
246
- // Destroy all plugins
247
- for (const [pluginName, plugin] of plugins.entries()) {
248
- if (typeof plugin.destroy === 'function') {
249
- plugin.destroy();
250
- }
251
- }
252
-
253
- // Clear plugins and middlewares
254
- plugins.clear();
255
- middlewares.length = 0;
256
- Object.keys(lifecycleHooks).forEach(key => {
257
- lifecycleHooks[key].length = 0;
258
- });
259
-
260
- // Destroy core state management
261
- cssState.destroy();
262
- eventState.destroy();
263
- }
264
- };
265
-
266
- return unifiedState;
267
- };
268
-
269
- // Create a singleton instance of the unified state
270
- const UnifiedState = createUnifiedState();
271
-
272
- // Create a template manager plugin
273
- const templateManagerPlugin = {
274
- name: 'templateManager',
275
- instance: null,
276
-
277
- init(api) {
278
- this.instance = createTemplateManager(api);
279
- return this;
280
- },
281
-
282
- destroy() {
283
- // Any cleanup needed for the template manager
284
- },
285
-
286
- // Plugin methods that will be added to the UIstate API
287
- methods: {
288
- onAction(action, handler) {
289
- return this.instance.onAction(action, handler);
290
- },
291
-
292
- registerActions(actionsMap) {
293
- return this.instance.registerActions(actionsMap);
294
- },
295
-
296
- attachDelegation(root = document.body) {
297
- return this.instance.attachDelegation(root);
298
- },
299
-
300
- mount(componentName, container) {
301
- return this.instance.mount(componentName, container);
302
- },
303
-
304
- renderTemplateFromCss(templateName, data) {
305
- return this.instance.renderTemplateFromCss(templateName, data);
306
- },
307
-
308
- createComponent(name, renderFn, stateKeys) {
309
- return this.instance.createComponent(name, renderFn, stateKeys);
310
- },
311
-
312
- applyClassesFromState(element, stateKey, options) {
313
- return this.instance.applyClassesFromState(element, stateKey, options);
314
- }
315
- },
316
-
317
- // Expose handlers property
318
- get handlers() {
319
- return this.instance.handlers;
320
- }
321
- };
322
-
323
- // Create a combined API with plugin support
324
- const UIstate = {
325
- ...UnifiedState,
326
-
327
- // Initialize the system with plugins
328
- init() {
329
- // Register the template manager plugin
330
- this.use('templateManager', templateManagerPlugin);
331
-
332
- // Run onInit hooks
333
- for (const hook of this.getPlugin('templateManager').instance.lifecycleHooks?.onInit || []) {
334
- // for (const hook of lifecycleHooks.onInit) {
335
- hook();
336
- }
337
-
338
- // Attach event delegation to document body
339
- this.attachDelegation(document.body);
340
-
341
- return this;
342
- }
343
- };
11
+ // Export the four core modules
12
+ export { createCssState } from './cssState.js';
13
+ export { createEventState } from './eventState.js';
14
+ export { default as stateSerializer } from './stateSerializer.js';
15
+ export { createTemplateManager } from './templateManager.js';
344
16
 
345
- export default UIstate;
346
- export {
347
- createUnifiedState,
348
- createCssState,
349
- createEventState,
350
- createTemplateManager,
351
- convertPathToCssPath,
352
- templateManagerPlugin
353
- };
17
+ // For convenience, also export cssState as default
18
+ export { createCssState as default } from './cssState.js';