@uistate/core 3.1.2 → 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.
- package/package.json +6 -15
- package/src/index.js +14 -349
package/package.json
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uistate/core",
|
|
3
|
-
"version": "
|
|
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 -
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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';
|