@uistate/core 1.0.0 → 2.0.1
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/README.md +115 -69
- package/package.json +10 -21
- package/src/cssState.js +87 -0
- package/src/eventState.js +98 -0
- package/src/index.js +111 -0
- package/src/templateManager.js +97 -0
- package/LICENSE +0 -21
- package/dist/index.d.mts +0 -16
- package/dist/index.d.ts +0 -16
- package/dist/index.js +0 -107
- package/dist/index.mjs +0 -79
- package/src/UIState.ts +0 -68
- package/src/index.ts +0 -2
- package/src/react/index.ts +0 -33
package/README.md
CHANGED
|
@@ -1,119 +1,105 @@
|
|
|
1
1
|
# @uistate/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**author**: Ajdin Imsirovic <ajdika@live.com> (GitHub)
|
|
4
|
+
**maintainer**: uistate <ajdika.i@gmail.com> (npm)
|
|
5
|
+
|
|
6
|
+
High-performance UI state management using CSS custom properties and ADSI (Attribute-Driven State Inheritance). Focused heavily on DX and performance.
|
|
4
7
|
|
|
5
8
|
## Features
|
|
6
9
|
|
|
7
|
-
- 🚀
|
|
8
|
-
- 📉
|
|
10
|
+
- 🚀 Potentially O(1) state updates using CSS custom properties
|
|
11
|
+
- 📉 Significant memory savings compared to virtual DOM approaches
|
|
9
12
|
- 🎯 Zero configuration
|
|
10
|
-
- 🔄 Automatic reactivity
|
|
13
|
+
- 🔄 Automatic reactivity through CSS cascade
|
|
11
14
|
- 🎨 Framework agnostic
|
|
12
|
-
- 📦 Tiny bundle size (~
|
|
13
|
-
-
|
|
14
|
-
- 📊 Optional performance monitoring
|
|
15
|
+
- 📦 Tiny bundle size (~2KB)
|
|
16
|
+
- 🧩 Modular architecture with dedicated modules for CSS state and events
|
|
15
17
|
|
|
16
18
|
## Installation
|
|
17
19
|
|
|
18
20
|
```bash
|
|
19
21
|
# Install the core package
|
|
20
22
|
npm install @uistate/core
|
|
21
|
-
|
|
22
|
-
# Optional: Install performance monitoring
|
|
23
|
-
npm install @uistate/performance
|
|
24
23
|
```
|
|
25
24
|
|
|
26
25
|
## Quick Start
|
|
27
26
|
|
|
28
|
-
```
|
|
29
|
-
import {
|
|
27
|
+
```javascript
|
|
28
|
+
import { cssState, eventState } from '@uistate/core';
|
|
30
29
|
|
|
31
30
|
// Initialize state
|
|
32
|
-
|
|
31
|
+
cssState.init();
|
|
33
32
|
|
|
34
|
-
// Set state
|
|
35
|
-
|
|
33
|
+
// Set state via CSS variables
|
|
34
|
+
cssState.set('--counter-value', '0');
|
|
36
35
|
|
|
37
36
|
// Get state
|
|
38
|
-
const count =
|
|
37
|
+
const count = parseInt(cssState.get('--counter-value'));
|
|
39
38
|
|
|
40
39
|
// Subscribe to changes
|
|
41
|
-
const unsubscribe =
|
|
42
|
-
console.log('
|
|
40
|
+
const unsubscribe = eventState.on('counter:change', (newValue) => {
|
|
41
|
+
console.log('Counter changed:', newValue);
|
|
43
42
|
});
|
|
44
43
|
|
|
45
|
-
//
|
|
46
|
-
|
|
44
|
+
// Set state with an attribute
|
|
45
|
+
document.documentElement.dataset.counterValue = count + 1;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### HTML Usage Example
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<button data-counter-value="0" id="counter-btn">Count: 0</button>
|
|
52
|
+
```
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<button onClick={() => setCount(count + 1)}>
|
|
52
|
-
Count: {count}
|
|
53
|
-
</button>
|
|
54
|
-
);
|
|
54
|
+
```css
|
|
55
|
+
[data-counter-value] {
|
|
56
|
+
/* Style based on state */
|
|
55
57
|
}
|
|
56
58
|
```
|
|
57
59
|
|
|
60
|
+
```javascript
|
|
61
|
+
document.getElementById('counter-btn').addEventListener('click', () => {
|
|
62
|
+
const btn = document.getElementById('counter-btn');
|
|
63
|
+
const currentValue = parseInt(btn.dataset.counterValue);
|
|
64
|
+
btn.dataset.counterValue = currentValue + 1;
|
|
65
|
+
btn.textContent = `Count: ${currentValue + 1}`;
|
|
66
|
+
eventState.emit('counter:change', currentValue + 1);
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
58
70
|
## Why @uistate/core?
|
|
59
71
|
|
|
60
72
|
### Performance
|
|
61
73
|
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
- **Minimal Overhead**: No virtual DOM diffing
|
|
74
|
+
- **CSS-Driven Updates**: Leverages browser's CSS engine for optimal performance with O(1) complexity
|
|
75
|
+
- **DOM as Source of Truth**: Efficient state storage using CSS custom properties and data attributes
|
|
76
|
+
- **Minimal Overhead**: No virtual DOM diffing or shadow DOM needed
|
|
65
77
|
|
|
66
78
|
### Developer Experience
|
|
67
79
|
|
|
68
|
-
- **Simple API**:
|
|
69
|
-
- **
|
|
70
|
-
- **Framework Agnostic**: Works with any framework
|
|
80
|
+
- **Simple API**: Modular `cssState` and `eventState` for clear separation of concerns
|
|
81
|
+
- **Framework Agnostic**: Works with any framework or vanilla JavaScript
|
|
71
82
|
- **Zero Config**: No store setup, no reducers, no actions
|
|
83
|
+
- **CSS-Native**: Leverages the power of CSS selectors and the cascade
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
The `@uistate/performance` package provides detailed performance metrics for your application:
|
|
85
|
+
### Core Concepts
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Start tracking performance
|
|
82
|
-
const tracker = PerformanceTracker.getInstance();
|
|
83
|
-
tracker.start();
|
|
84
|
-
|
|
85
|
-
// Optional: Add the performance display component to your React app
|
|
86
|
-
function App() {
|
|
87
|
-
return (
|
|
88
|
-
<div>
|
|
89
|
-
<YourApp />
|
|
90
|
-
<PerformanceDisplay />
|
|
91
|
-
</div>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
The performance tracker monitors:
|
|
97
|
-
- State update duration
|
|
98
|
-
- Component render time
|
|
99
|
-
- FPS (Frames Per Second)
|
|
100
|
-
- Memory usage
|
|
101
|
-
- Long task duration
|
|
87
|
+
- **Attribute-Driven State Inheritance (ADSI)**: State represented both as CSS variables and data attributes
|
|
88
|
+
- **Hierarchical State Machines**: Model complex UI states with nested state machines
|
|
89
|
+
- **CSS-Driven State Derivation**: Derive complex states using CSS without JavaScript
|
|
102
90
|
|
|
103
91
|
## Project Structure
|
|
104
92
|
|
|
105
93
|
```
|
|
106
94
|
@uistate/core/
|
|
107
95
|
├── src/ # Core library
|
|
108
|
-
│ ├── index.
|
|
109
|
-
│ ├──
|
|
110
|
-
│
|
|
111
|
-
│
|
|
112
|
-
├── packages/
|
|
113
|
-
│ └── performance/ # Performance monitoring package
|
|
96
|
+
│ ├── index.js # Main entry
|
|
97
|
+
│ ├── cssState.js # CSS variables management
|
|
98
|
+
│ ├── eventState.js # Event-based state transitions
|
|
99
|
+
│ └── templateManager.js # Component management
|
|
114
100
|
└── examples/ # Example applications
|
|
115
|
-
├──
|
|
116
|
-
└──
|
|
101
|
+
├── basic/ # Simple examples (range sliders, toggles, etc)
|
|
102
|
+
└── advanced/ # Advanced patterns and techniques
|
|
117
103
|
```
|
|
118
104
|
|
|
119
105
|
## Browser Support
|
|
@@ -123,6 +109,66 @@ The performance tracker monitors:
|
|
|
123
109
|
- Safari 10.1+
|
|
124
110
|
- Edge 79+
|
|
125
111
|
|
|
112
|
+
# Core Ideas Behind UIstate
|
|
113
|
+
|
|
114
|
+
UIstate is a JavaScript-based UI state management system that leverages CSS custom properties and data attributes as the storage mechanism, paired with event-based state transitions.
|
|
115
|
+
|
|
116
|
+
## Key Components
|
|
117
|
+
|
|
118
|
+
### cssState
|
|
119
|
+
|
|
120
|
+
The `cssState` module provides methods to manage state through CSS custom properties:
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// Initialize CSS state management
|
|
124
|
+
cssState.init();
|
|
125
|
+
|
|
126
|
+
// Set a CSS custom property
|
|
127
|
+
cssState.set('--theme-mode', 'dark');
|
|
128
|
+
|
|
129
|
+
// Get a CSS custom property value
|
|
130
|
+
const theme = cssState.get('--theme-mode');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### eventState
|
|
134
|
+
|
|
135
|
+
The `eventState` module provides an event system for state transitions:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Listen for state changes
|
|
139
|
+
eventState.on('theme:change', (newTheme) => {
|
|
140
|
+
console.log('Theme changed to:', newTheme);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Trigger state changes
|
|
144
|
+
eventState.emit('theme:change', 'light');
|
|
145
|
+
|
|
146
|
+
// Clean up listeners
|
|
147
|
+
eventState.off('theme:change');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### templateManager
|
|
151
|
+
|
|
152
|
+
The `templateManager` module helps with component initialization and templating:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// Initialize components from templates
|
|
156
|
+
templateManager.init();
|
|
157
|
+
|
|
158
|
+
// Create a component from a template
|
|
159
|
+
const button = templateManager.createFromTemplate('button-template');
|
|
160
|
+
document.body.appendChild(button);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Key Features
|
|
164
|
+
|
|
165
|
+
1. Uses CSS custom properties as a storage mechanism, making state changes automatically trigger UI updates
|
|
166
|
+
2. Provides a clear separation between state storage (CSS) and behavior (JavaScript)
|
|
167
|
+
3. Implements a pub/sub pattern for reactive updates
|
|
168
|
+
4. Leverages the CSS cascade for hierarchical state inheritance
|
|
169
|
+
|
|
170
|
+
This implementation is particularly useful for building UI components with clean separation of concerns and optimal performance.
|
|
171
|
+
|
|
126
172
|
## Contributing
|
|
127
173
|
|
|
128
174
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
package/package.json
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uistate/core",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "High-performance UI state management using CSS custom properties",
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "High-performance UI state management using CSS custom properties and ADSI (Attribute-Driven State Inheritance)",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"module": "src/index.js",
|
|
8
7
|
"files": [
|
|
9
|
-
"dist",
|
|
10
8
|
"src"
|
|
11
9
|
],
|
|
12
10
|
"scripts": {
|
|
13
|
-
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
14
11
|
"test": "jest",
|
|
15
|
-
"lint": "eslint src"
|
|
16
|
-
"typecheck": "tsc --noEmit",
|
|
17
|
-
"prepare": "npm run build"
|
|
12
|
+
"lint": "eslint src"
|
|
18
13
|
},
|
|
19
14
|
"keywords": [
|
|
20
15
|
"state-management",
|
|
@@ -23,7 +18,10 @@
|
|
|
23
18
|
"css",
|
|
24
19
|
"performance"
|
|
25
20
|
],
|
|
26
|
-
"author": "Imsirovic
|
|
21
|
+
"author": "Ajdin Imsirovic <ajdika@live.com> (GitHub)",
|
|
22
|
+
"contributors": [
|
|
23
|
+
"uistate <ajdika.i@gmail.com> (npm)"
|
|
24
|
+
],
|
|
27
25
|
"license": "MIT",
|
|
28
26
|
"repository": {
|
|
29
27
|
"type": "git",
|
|
@@ -37,18 +35,9 @@
|
|
|
37
35
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
38
36
|
},
|
|
39
37
|
"devDependencies": {
|
|
40
|
-
"@types/node": "^20.0.0",
|
|
41
|
-
"@types/react": "^18.0.0",
|
|
42
|
-
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
43
|
-
"@typescript-eslint/parser": "^6.0.0",
|
|
44
38
|
"eslint": "^8.0.0",
|
|
45
39
|
"jest": "^29.0.0",
|
|
46
|
-
"jest-environment-jsdom": "^29.0.0"
|
|
47
|
-
"@types/jest": "^29.0.0",
|
|
48
|
-
"ts-jest": "^29.0.0",
|
|
49
|
-
"tsup": "^8.0.0",
|
|
50
|
-
"typescript": "^5.0.0",
|
|
51
|
-
"react": "^18.0.0"
|
|
40
|
+
"jest-environment-jsdom": "^29.0.0"
|
|
52
41
|
},
|
|
53
42
|
"sideEffects": false
|
|
54
43
|
}
|
package/src/cssState.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UIstate - CSS-based state management module
|
|
3
|
+
* Part of the UIstate declarative state management system
|
|
4
|
+
* Uses CSS custom properties and data attributes for state representation
|
|
5
|
+
*/
|
|
6
|
+
const createCssState = (initialState = {}) => {
|
|
7
|
+
const state = {
|
|
8
|
+
_sheet: null,
|
|
9
|
+
_observers: new Map(),
|
|
10
|
+
|
|
11
|
+
init() {
|
|
12
|
+
if (!this._sheet) {
|
|
13
|
+
const style = document.createElement('style');
|
|
14
|
+
document.head.appendChild(style);
|
|
15
|
+
this._sheet = style.sheet;
|
|
16
|
+
this._addRule(':root {}');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Initialize with any provided state
|
|
20
|
+
if (initialState && typeof initialState === 'object') {
|
|
21
|
+
Object.entries(initialState).forEach(([key, value]) => {
|
|
22
|
+
this.setState(key, value);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
setState(key, value) {
|
|
30
|
+
const cssValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
31
|
+
document.documentElement.style.setProperty(`--${key}`, cssValue);
|
|
32
|
+
document.documentElement.setAttribute(`data-${key}`, value);
|
|
33
|
+
this._notifyObservers(key, value);
|
|
34
|
+
return value;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
getState(key) {
|
|
38
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(`--${key}`).trim();
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(value);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
observe(key, callback) {
|
|
47
|
+
if (!this._observers.has(key)) {
|
|
48
|
+
this._observers.set(key, new Set());
|
|
49
|
+
}
|
|
50
|
+
this._observers.get(key).add(callback);
|
|
51
|
+
return () => {
|
|
52
|
+
const observers = this._observers.get(key);
|
|
53
|
+
if (observers) {
|
|
54
|
+
observers.delete(callback);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
_notifyObservers(key, value) {
|
|
60
|
+
const observers = this._observers.get(key);
|
|
61
|
+
if (observers) {
|
|
62
|
+
observers.forEach(cb => cb(value));
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
_addRule(rule) {
|
|
67
|
+
if (this._sheet) {
|
|
68
|
+
this._sheet.insertRule(rule, this._sheet.cssRules.length);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// Clean up resources
|
|
73
|
+
destroy() {
|
|
74
|
+
this._observers.clear();
|
|
75
|
+
// The style element will remain in the DOM
|
|
76
|
+
// as removing it would affect the UI state
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return state.init();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Legacy singleton for backward compatibility
|
|
84
|
+
const CssState = createCssState();
|
|
85
|
+
|
|
86
|
+
export default createCssState;
|
|
87
|
+
export { createCssState, CssState };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UIstate - Event-based hierarchical state management module
|
|
3
|
+
* Part of the UIstate declarative state management system
|
|
4
|
+
* Uses DOM events for pub/sub with hierarchical path support
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const createEventState = (initial = {}) => {
|
|
8
|
+
// Clone the initial state to avoid direct mutations to the passed object
|
|
9
|
+
const store = JSON.parse(JSON.stringify(initial));
|
|
10
|
+
|
|
11
|
+
// Create a dedicated DOM element to use as an event bus
|
|
12
|
+
const bus = document.createElement("x-store");
|
|
13
|
+
|
|
14
|
+
// Optional: Keep the bus element off the actual DOM for better encapsulation
|
|
15
|
+
// but this isn't strictly necessary for functionality
|
|
16
|
+
bus.style.display = "none";
|
|
17
|
+
document.documentElement.appendChild(bus);
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
// get a value from the store by path
|
|
21
|
+
get: (path) => {
|
|
22
|
+
if (!path) return store;
|
|
23
|
+
return path
|
|
24
|
+
.split(".")
|
|
25
|
+
.reduce(
|
|
26
|
+
(obj, prop) =>
|
|
27
|
+
obj && obj[prop] !== undefined ? obj[prop] : undefined,
|
|
28
|
+
store
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// set a value in the store by path
|
|
33
|
+
set: (path, value) => {
|
|
34
|
+
if(!path) return;
|
|
35
|
+
|
|
36
|
+
// Update the store
|
|
37
|
+
let target = store;
|
|
38
|
+
const parts = path.split(".");
|
|
39
|
+
const last = parts.pop();
|
|
40
|
+
|
|
41
|
+
// Create the path if it doesn't exist
|
|
42
|
+
parts.forEach((part) => {
|
|
43
|
+
if (!target[part] || typeof target[part] !== "object") {
|
|
44
|
+
target[part] = {};
|
|
45
|
+
}
|
|
46
|
+
target = target[part];
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Set the value
|
|
50
|
+
target[last] = value;
|
|
51
|
+
|
|
52
|
+
// Notify subscribers with a DOM event
|
|
53
|
+
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
54
|
+
|
|
55
|
+
// Also dispatch events for parent paths to support wildcards
|
|
56
|
+
if (parts.length > 0) {
|
|
57
|
+
let parentPath = "";
|
|
58
|
+
for (const part of parts) {
|
|
59
|
+
parentPath = parentPath ? `${parentPath}.${part}` : part;
|
|
60
|
+
bus.dispatchEvent(
|
|
61
|
+
new CustomEvent(`${parentPath}.*`, {
|
|
62
|
+
detail: { path, value },
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Dispatch root wildcard for any state change
|
|
68
|
+
bus.dispatchEvent(
|
|
69
|
+
new CustomEvent("*", {
|
|
70
|
+
detail: { path, value},
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return value;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Subscribe to changes on a path
|
|
79
|
+
subscribe: (path, callback) => {
|
|
80
|
+
if (!path || typeof callback !== "function") return () => {};
|
|
81
|
+
|
|
82
|
+
const handler = (e) => callback(e.detail, path);
|
|
83
|
+
bus.addEventListener(path, handler);
|
|
84
|
+
|
|
85
|
+
return () => bus.removeEventListener(path, handler);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Optional method to clean up resources
|
|
89
|
+
destroy: () => {
|
|
90
|
+
if (bus.parentNode) {
|
|
91
|
+
bus.parentNode.removeChild(bus);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default createEventState;
|
|
98
|
+
export { createEventState };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
*/
|
|
7
|
+
import { createCssState } from './cssState.js';
|
|
8
|
+
import { createEventState } from './eventState.js';
|
|
9
|
+
import { createTemplateManager } from './templateManager.js';
|
|
10
|
+
|
|
11
|
+
const createUnifiedState = (initialState = {}) => {
|
|
12
|
+
// Initialize state store
|
|
13
|
+
const store = JSON.parse(JSON.stringify(initialState));
|
|
14
|
+
|
|
15
|
+
// Create the CSS state manager
|
|
16
|
+
const cssState = createCssState(initialState);
|
|
17
|
+
|
|
18
|
+
// Create the event state manager with the same initial state
|
|
19
|
+
const eventState = createEventState(initialState);
|
|
20
|
+
|
|
21
|
+
// Create a unified API
|
|
22
|
+
const unifiedState = {
|
|
23
|
+
_isNotifying: false, // Flag to prevent recursive notifications
|
|
24
|
+
|
|
25
|
+
// Get state with hierarchical path support
|
|
26
|
+
getState(path) {
|
|
27
|
+
if (!path) return store;
|
|
28
|
+
|
|
29
|
+
// Try to get from event state first (faster)
|
|
30
|
+
const value = eventState.get(path);
|
|
31
|
+
|
|
32
|
+
if (value !== undefined) return value;
|
|
33
|
+
|
|
34
|
+
// Fall back to CSS variable (for values set outside this API)
|
|
35
|
+
const cssPath = path.replace(/\./g, "-");
|
|
36
|
+
return cssState.getState(cssPath);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Set state with hierarchical path support
|
|
40
|
+
setState(path, value) {
|
|
41
|
+
// Prevent recursive notifications
|
|
42
|
+
if (this._isNotifying) return value;
|
|
43
|
+
|
|
44
|
+
this._isNotifying = true;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Update event state
|
|
48
|
+
eventState.set(path, value);
|
|
49
|
+
|
|
50
|
+
// Update CSS state (convert dots to dashes for CSS variables)
|
|
51
|
+
const cssPath = path.replace(/\./g, "-");
|
|
52
|
+
cssState.setState(cssPath, value);
|
|
53
|
+
|
|
54
|
+
return value;
|
|
55
|
+
} finally {
|
|
56
|
+
this._isNotifying = false;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Subscribe to state changes with support for wildcards
|
|
61
|
+
subscribe(path, callback) {
|
|
62
|
+
return eventState.subscribe(path, callback);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Observe CSS state changes (simpler API, no wildcards)
|
|
66
|
+
observe(key, callback) {
|
|
67
|
+
return cssState.observe(key, callback);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// Clean up resources
|
|
71
|
+
destroy() {
|
|
72
|
+
cssState.destroy();
|
|
73
|
+
eventState.destroy();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return unifiedState;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Create a singleton instance of the unified state
|
|
81
|
+
const UnifiedState = createUnifiedState();
|
|
82
|
+
|
|
83
|
+
// Create a template manager connected to the unified state
|
|
84
|
+
const TemplateManager = createTemplateManager(UnifiedState);
|
|
85
|
+
|
|
86
|
+
// Create a combined API for backward compatibility
|
|
87
|
+
const UIstate = {
|
|
88
|
+
...UnifiedState,
|
|
89
|
+
|
|
90
|
+
// Add template manager methods
|
|
91
|
+
handlers: TemplateManager.handlers,
|
|
92
|
+
onAction: TemplateManager.onAction.bind(TemplateManager),
|
|
93
|
+
attachDelegation: TemplateManager.attachDelegation.bind(TemplateManager),
|
|
94
|
+
mount: TemplateManager.mount.bind(TemplateManager),
|
|
95
|
+
|
|
96
|
+
// Initialize both systems
|
|
97
|
+
init() {
|
|
98
|
+
// Attach event delegation to document body
|
|
99
|
+
this.attachDelegation(document.body);
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default UIstate;
|
|
105
|
+
export {
|
|
106
|
+
createUnifiedState,
|
|
107
|
+
UnifiedState,
|
|
108
|
+
createTemplateManager,
|
|
109
|
+
TemplateManager,
|
|
110
|
+
UIstate
|
|
111
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateManager - Component mounting and event delegation
|
|
3
|
+
* Handles HTML templating, component mounting, and event delegation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const createTemplateManager = (stateManager) => {
|
|
7
|
+
const manager = {
|
|
8
|
+
handlers: {},
|
|
9
|
+
|
|
10
|
+
onAction(action, handler) {
|
|
11
|
+
this.handlers[action] = handler;
|
|
12
|
+
return this;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
attachDelegation(root = document.body) {
|
|
16
|
+
root.addEventListener('click', e => {
|
|
17
|
+
const target = e.target.closest('[data-action]');
|
|
18
|
+
if (!target) return;
|
|
19
|
+
|
|
20
|
+
const action = target.dataset.action;
|
|
21
|
+
if (!action) return;
|
|
22
|
+
|
|
23
|
+
const handler = this.handlers[action];
|
|
24
|
+
if (typeof handler === 'function') {
|
|
25
|
+
handler(e);
|
|
26
|
+
} else if (target.dataset.value !== undefined && stateManager) {
|
|
27
|
+
// If we have a state manager, use it to update state
|
|
28
|
+
stateManager.setState(action, target.dataset.value);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return this;
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
mount(componentName, container) {
|
|
35
|
+
const tpl = document.getElementById(`${componentName}-template`);
|
|
36
|
+
if (!tpl) throw new Error(`Template not found: ${componentName}-template`);
|
|
37
|
+
const clone = tpl.content.cloneNode(true);
|
|
38
|
+
|
|
39
|
+
function resolvePlaceholders(fragment) {
|
|
40
|
+
Array.from(fragment.querySelectorAll('*')).forEach(el => {
|
|
41
|
+
const tag = el.tagName.toLowerCase();
|
|
42
|
+
if (tag.endsWith('-placeholder')) {
|
|
43
|
+
const name = tag.replace('-placeholder','');
|
|
44
|
+
const childTpl = document.getElementById(`${name}-template`);
|
|
45
|
+
if (!childTpl) throw new Error(`Template not found: ${name}-template`);
|
|
46
|
+
const childClone = childTpl.content.cloneNode(true);
|
|
47
|
+
resolvePlaceholders(childClone);
|
|
48
|
+
el.replaceWith(childClone);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
resolvePlaceholders(clone);
|
|
54
|
+
container.appendChild(clone);
|
|
55
|
+
return clone.firstElementChild;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Helper to create a reactive component with automatic updates
|
|
59
|
+
createComponent(name, renderFn, stateKeys = []) {
|
|
60
|
+
if (!stateManager) {
|
|
61
|
+
throw new Error('State manager is required for reactive components');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create template element if it doesn't exist
|
|
65
|
+
let tpl = document.getElementById(`${name}-template`);
|
|
66
|
+
if (!tpl) {
|
|
67
|
+
tpl = document.createElement('template');
|
|
68
|
+
tpl.id = `${name}-template`;
|
|
69
|
+
document.body.appendChild(tpl);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Initial render
|
|
73
|
+
tpl.innerHTML = renderFn(stateManager);
|
|
74
|
+
|
|
75
|
+
// Set up observers for reactive updates
|
|
76
|
+
if (stateKeys.length > 0) {
|
|
77
|
+
stateKeys.forEach(key => {
|
|
78
|
+
stateManager.observe(key, () => {
|
|
79
|
+
tpl.innerHTML = renderFn(stateManager);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
mount: (container) => this.mount(name, container)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return manager;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Create a standalone instance that doesn't depend on any state manager
|
|
94
|
+
const TemplateManager = createTemplateManager();
|
|
95
|
+
|
|
96
|
+
export default createTemplateManager;
|
|
97
|
+
export { createTemplateManager, TemplateManager };
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Imsirovic Ajdin
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/index.d.mts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type StateObserver<T = any> = (value: T) => void;
|
|
2
|
-
interface UIStateType {
|
|
3
|
-
_sheet: CSSStyleSheet | null;
|
|
4
|
-
_observers: Map<string, Set<StateObserver>>;
|
|
5
|
-
init(): UIStateType;
|
|
6
|
-
setState<T>(key: string, value: T): void;
|
|
7
|
-
getState<T>(key: string): T;
|
|
8
|
-
observe<T>(key: string, callback: StateObserver<T>): () => void;
|
|
9
|
-
_notifyObservers<T>(key: string, value: T): void;
|
|
10
|
-
_addRule(rule: string): void;
|
|
11
|
-
}
|
|
12
|
-
declare const UIState: UIStateType;
|
|
13
|
-
|
|
14
|
-
declare function useUIState<T>(key: string, initialValue: T): [T, (value: T) => void];
|
|
15
|
-
|
|
16
|
-
export { UIState, useUIState };
|
package/dist/index.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
type StateObserver<T = any> = (value: T) => void;
|
|
2
|
-
interface UIStateType {
|
|
3
|
-
_sheet: CSSStyleSheet | null;
|
|
4
|
-
_observers: Map<string, Set<StateObserver>>;
|
|
5
|
-
init(): UIStateType;
|
|
6
|
-
setState<T>(key: string, value: T): void;
|
|
7
|
-
getState<T>(key: string): T;
|
|
8
|
-
observe<T>(key: string, callback: StateObserver<T>): () => void;
|
|
9
|
-
_notifyObservers<T>(key: string, value: T): void;
|
|
10
|
-
_addRule(rule: string): void;
|
|
11
|
-
}
|
|
12
|
-
declare const UIState: UIStateType;
|
|
13
|
-
|
|
14
|
-
declare function useUIState<T>(key: string, initialValue: T): [T, (value: T) => void];
|
|
15
|
-
|
|
16
|
-
export { UIState, useUIState };
|
package/dist/index.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
UIState: () => UIState_default,
|
|
24
|
-
useUIState: () => useUIState
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
|
-
// src/UIState.ts
|
|
29
|
-
var UIState = {
|
|
30
|
-
_sheet: null,
|
|
31
|
-
_observers: /* @__PURE__ */ new Map(),
|
|
32
|
-
init() {
|
|
33
|
-
if (!this._sheet) {
|
|
34
|
-
const style = document.createElement("style");
|
|
35
|
-
document.head.appendChild(style);
|
|
36
|
-
this._sheet = style.sheet;
|
|
37
|
-
this._addRule(":root {}");
|
|
38
|
-
}
|
|
39
|
-
return this;
|
|
40
|
-
},
|
|
41
|
-
setState(key, value) {
|
|
42
|
-
const cssValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
43
|
-
document.documentElement.style.setProperty(`--${key}`, cssValue);
|
|
44
|
-
this._notifyObservers(key, value);
|
|
45
|
-
},
|
|
46
|
-
getState(key) {
|
|
47
|
-
const value = getComputedStyle(document.documentElement).getPropertyValue(`--${key}`).trim();
|
|
48
|
-
try {
|
|
49
|
-
return JSON.parse(value);
|
|
50
|
-
} catch (e) {
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
observe(key, callback) {
|
|
55
|
-
var _a;
|
|
56
|
-
if (!this._observers.has(key)) {
|
|
57
|
-
this._observers.set(key, /* @__PURE__ */ new Set());
|
|
58
|
-
}
|
|
59
|
-
(_a = this._observers.get(key)) == null ? void 0 : _a.add(callback);
|
|
60
|
-
return () => {
|
|
61
|
-
var _a2;
|
|
62
|
-
(_a2 = this._observers.get(key)) == null ? void 0 : _a2.delete(callback);
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
_notifyObservers(key, value) {
|
|
66
|
-
var _a;
|
|
67
|
-
(_a = this._observers.get(key)) == null ? void 0 : _a.forEach(
|
|
68
|
-
(callback) => callback(value)
|
|
69
|
-
);
|
|
70
|
-
},
|
|
71
|
-
_addRule(rule) {
|
|
72
|
-
if (this._sheet) {
|
|
73
|
-
this._sheet.insertRule(rule, this._sheet.cssRules.length);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
var UIState_default = UIState;
|
|
78
|
-
|
|
79
|
-
// src/react/index.ts
|
|
80
|
-
var import_react = require("react");
|
|
81
|
-
function useUIState(key, initialValue) {
|
|
82
|
-
(0, import_react.useEffect)(() => {
|
|
83
|
-
UIState_default.init();
|
|
84
|
-
}, []);
|
|
85
|
-
const [state, setState] = (0, import_react.useState)(() => {
|
|
86
|
-
try {
|
|
87
|
-
const value = UIState_default.getState(key);
|
|
88
|
-
return value !== void 0 ? value : initialValue;
|
|
89
|
-
} catch (e) {
|
|
90
|
-
return initialValue;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
(0, import_react.useEffect)(() => {
|
|
94
|
-
return UIState_default.observe(key, (value) => {
|
|
95
|
-
setState(value);
|
|
96
|
-
});
|
|
97
|
-
}, [key]);
|
|
98
|
-
const setUIState = (value) => {
|
|
99
|
-
UIState_default.setState(key, value);
|
|
100
|
-
};
|
|
101
|
-
return [state, setUIState];
|
|
102
|
-
}
|
|
103
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
104
|
-
0 && (module.exports = {
|
|
105
|
-
UIState,
|
|
106
|
-
useUIState
|
|
107
|
-
});
|
package/dist/index.mjs
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// src/UIState.ts
|
|
2
|
-
var UIState = {
|
|
3
|
-
_sheet: null,
|
|
4
|
-
_observers: /* @__PURE__ */ new Map(),
|
|
5
|
-
init() {
|
|
6
|
-
if (!this._sheet) {
|
|
7
|
-
const style = document.createElement("style");
|
|
8
|
-
document.head.appendChild(style);
|
|
9
|
-
this._sheet = style.sheet;
|
|
10
|
-
this._addRule(":root {}");
|
|
11
|
-
}
|
|
12
|
-
return this;
|
|
13
|
-
},
|
|
14
|
-
setState(key, value) {
|
|
15
|
-
const cssValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
16
|
-
document.documentElement.style.setProperty(`--${key}`, cssValue);
|
|
17
|
-
this._notifyObservers(key, value);
|
|
18
|
-
},
|
|
19
|
-
getState(key) {
|
|
20
|
-
const value = getComputedStyle(document.documentElement).getPropertyValue(`--${key}`).trim();
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(value);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
observe(key, callback) {
|
|
28
|
-
var _a;
|
|
29
|
-
if (!this._observers.has(key)) {
|
|
30
|
-
this._observers.set(key, /* @__PURE__ */ new Set());
|
|
31
|
-
}
|
|
32
|
-
(_a = this._observers.get(key)) == null ? void 0 : _a.add(callback);
|
|
33
|
-
return () => {
|
|
34
|
-
var _a2;
|
|
35
|
-
(_a2 = this._observers.get(key)) == null ? void 0 : _a2.delete(callback);
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
_notifyObservers(key, value) {
|
|
39
|
-
var _a;
|
|
40
|
-
(_a = this._observers.get(key)) == null ? void 0 : _a.forEach(
|
|
41
|
-
(callback) => callback(value)
|
|
42
|
-
);
|
|
43
|
-
},
|
|
44
|
-
_addRule(rule) {
|
|
45
|
-
if (this._sheet) {
|
|
46
|
-
this._sheet.insertRule(rule, this._sheet.cssRules.length);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var UIState_default = UIState;
|
|
51
|
-
|
|
52
|
-
// src/react/index.ts
|
|
53
|
-
import { useState, useEffect } from "react";
|
|
54
|
-
function useUIState(key, initialValue) {
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
UIState_default.init();
|
|
57
|
-
}, []);
|
|
58
|
-
const [state, setState] = useState(() => {
|
|
59
|
-
try {
|
|
60
|
-
const value = UIState_default.getState(key);
|
|
61
|
-
return value !== void 0 ? value : initialValue;
|
|
62
|
-
} catch (e) {
|
|
63
|
-
return initialValue;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
return UIState_default.observe(key, (value) => {
|
|
68
|
-
setState(value);
|
|
69
|
-
});
|
|
70
|
-
}, [key]);
|
|
71
|
-
const setUIState = (value) => {
|
|
72
|
-
UIState_default.setState(key, value);
|
|
73
|
-
};
|
|
74
|
-
return [state, setUIState];
|
|
75
|
-
}
|
|
76
|
-
export {
|
|
77
|
-
UIState_default as UIState,
|
|
78
|
-
useUIState
|
|
79
|
-
};
|
package/src/UIState.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
type StateObserver<T = any> = (value: T) => void;
|
|
2
|
-
|
|
3
|
-
interface UIStateType {
|
|
4
|
-
_sheet: CSSStyleSheet | null;
|
|
5
|
-
_observers: Map<string, Set<StateObserver>>;
|
|
6
|
-
init(): UIStateType;
|
|
7
|
-
setState<T>(key: string, value: T): void;
|
|
8
|
-
getState<T>(key: string): T;
|
|
9
|
-
observe<T>(key: string, callback: StateObserver<T>): () => void;
|
|
10
|
-
_notifyObservers<T>(key: string, value: T): void;
|
|
11
|
-
_addRule(rule: string): void;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const UIState: UIStateType = {
|
|
15
|
-
_sheet: null,
|
|
16
|
-
_observers: new Map(),
|
|
17
|
-
|
|
18
|
-
init() {
|
|
19
|
-
if (!this._sheet) {
|
|
20
|
-
const style = document.createElement('style');
|
|
21
|
-
document.head.appendChild(style);
|
|
22
|
-
this._sheet = style.sheet;
|
|
23
|
-
this._addRule(':root {}');
|
|
24
|
-
}
|
|
25
|
-
return this;
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
setState<T>(key: string, value: T): void {
|
|
29
|
-
const cssValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
30
|
-
document.documentElement.style.setProperty(`--${key}`, cssValue);
|
|
31
|
-
this._notifyObservers(key, value);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
getState<T>(key: string): T {
|
|
35
|
-
const value = getComputedStyle(document.documentElement)
|
|
36
|
-
.getPropertyValue(`--${key}`).trim();
|
|
37
|
-
try {
|
|
38
|
-
return JSON.parse(value) as T;
|
|
39
|
-
} catch {
|
|
40
|
-
return value as unknown as T;
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
observe<T>(key: string, callback: StateObserver<T>): () => void {
|
|
45
|
-
if (!this._observers.has(key)) {
|
|
46
|
-
this._observers.set(key, new Set());
|
|
47
|
-
}
|
|
48
|
-
this._observers.get(key)?.add(callback as StateObserver);
|
|
49
|
-
|
|
50
|
-
return () => {
|
|
51
|
-
this._observers.get(key)?.delete(callback as StateObserver);
|
|
52
|
-
};
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
_notifyObservers<T>(key: string, value: T): void {
|
|
56
|
-
this._observers.get(key)?.forEach(callback =>
|
|
57
|
-
(callback as StateObserver<T>)(value)
|
|
58
|
-
);
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
_addRule(rule: string): void {
|
|
62
|
-
if (this._sheet) {
|
|
63
|
-
this._sheet.insertRule(rule, this._sheet.cssRules.length);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export default UIState;
|
package/src/index.ts
DELETED
package/src/react/index.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import UIState from '../UIState';
|
|
3
|
-
|
|
4
|
-
export function useUIState<T>(key: string, initialValue: T): [T, (value: T) => void] {
|
|
5
|
-
// Initialize UIState if needed
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
UIState.init();
|
|
8
|
-
}, []);
|
|
9
|
-
|
|
10
|
-
// Get initial state
|
|
11
|
-
const [state, setState] = useState<T>(() => {
|
|
12
|
-
try {
|
|
13
|
-
const value = UIState.getState<T>(key);
|
|
14
|
-
return value !== undefined ? value : initialValue;
|
|
15
|
-
} catch {
|
|
16
|
-
return initialValue;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// Set up observer
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
return UIState.observe<T>(key, (value) => {
|
|
23
|
-
setState(value);
|
|
24
|
-
});
|
|
25
|
-
}, [key]);
|
|
26
|
-
|
|
27
|
-
// Return state and setter
|
|
28
|
-
const setUIState = (value: T) => {
|
|
29
|
-
UIState.setState(key, value);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return [state, setUIState];
|
|
33
|
-
}
|