@uistate/core 1.0.0 → 2.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/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # @uistate/core
2
2
 
3
- High-performance UI state management using CSS custom properties. 44% faster than traditional state management solutions with 12.5% lower memory usage.
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 a number of other novel ideas. Focused heavily on DX and performance, in that order.
4
7
 
5
8
  ## Features
6
9
 
7
- - 🚀 44% faster state updates than Redux
8
- - 📉 12.5% lower memory usage
10
+ - ~~🚀 44% faster state updates than Redux~~ Potentially O(1) state updates!
11
+ - ~~📉 12.5% lower memory usage~~ Large memory usage savings (perf pending...)
9
12
  - 🎯 Zero configuration
10
13
  - 🔄 Automatic reactivity
11
14
  - 🎨 Framework agnostic
12
15
  - 📦 Tiny bundle size (~1KB)
13
- - 💪 Full TypeScript support
14
- - 📊 Optional performance monitoring
16
+ - ~~💪 Full TypeScript support~~ Sorry, JS-only for a while...
17
+ - ~~📊 Optional performance monitoring~~ In progress (perf pending...)
15
18
 
16
19
  ## Installation
17
20
 
@@ -123,6 +126,73 @@ The performance tracker monitors:
123
126
  - Safari 10.1+
124
127
  - Edge 79+
125
128
 
129
+ # Explanation of the Core Ideas Behind the UI State Management System
130
+
131
+ A TypeScript-based UI state management system that leverages CSS custom properties (CSS variables) as the storage mechanism.
132
+
133
+ ## Key Components
134
+
135
+ ### Type Definitions
136
+
137
+ ```typescript
138
+ type StateObserver<T> = (value: T) => void;
139
+ interface UIStateType { ... }
140
+ ```
141
+
142
+ - Defines a type for state change observers (callbacks)
143
+ - Defines the interface for the state management system
144
+
145
+ ## Core Functionality
146
+
147
+ The `UIState` object provides several key methods:
148
+
149
+ ### `init()`
150
+ - Creates a style element in the document head
151
+ - Initializes a CSS stylesheet for managing custom properties
152
+ - Returns the UIState instance for chaining
153
+
154
+ ### `setState<T>(key: string, value: T)`
155
+ - Stores values as CSS custom properties (--key)
156
+ - Converts non-string values to JSON strings
157
+ - Notifies observers when values change
158
+
159
+ ### `getState<T>(key: string)`
160
+ - Retrieves values from CSS custom properties
161
+ - Attempts to parse JSON values back to their original type
162
+ - Falls back to raw string if parsing fails
163
+
164
+ ### `observe<T>(key: string, callback)`
165
+ - Implements an observer pattern for state changes
166
+ - Returns a cleanup function to remove the observer
167
+ - Allows multiple observers per state key
168
+
169
+ ## Internal Mechanisms
170
+
171
+ ```typescript
172
+ _sheet: CSSStyleSheet | null // Stores the stylesheet reference
173
+ _observers: Map<string, Set<StateObserver>> // Stores observers per key
174
+ ```
175
+
176
+ ## Key Features
177
+
178
+ 1. Uses CSS custom properties as a storage mechanism, making state changes automatically trigger UI updates
179
+ 2. Provides type safety through TypeScript generics
180
+ 3. Implements the observer pattern for reactive updates
181
+ 4. Handles serialization/deserialization of complex data types through JSON
182
+
183
+ ## Example Usage
184
+
185
+ ```typescript
186
+ UIState.init();
187
+ UIState.setState('theme', 'dark');
188
+ UIState.observe('theme', (newValue) => {
189
+ console.log('Theme changed to:', newValue);
190
+ });
191
+ const currentTheme = UIState.getState<string>('theme');
192
+ ```
193
+
194
+ This implementation is particularly useful for managing global UI state like themes, layout preferences, or any other state that might affect multiple components simultaneously.
195
+
126
196
  ## Contributing
127
197
 
128
198
  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": "1.0.0",
4
- "description": "High-performance UI state management using CSS custom properties",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
3
+ "version": "2.0.0",
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 Ajdin",
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
  }
@@ -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
@@ -1,2 +0,0 @@
1
- export { default as UIState } from './UIState';
2
- export * from './react';
@@ -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
- }