lume-js 0.2.1 → 0.4.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 +623 -24
- package/package.json +47 -4
- package/src/addons/computed.js +126 -28
- package/src/addons/watch.js +4 -3
- package/src/core/bindDom.js +94 -16
- package/src/core/effect.js +104 -0
- package/src/core/state.js +115 -13
- package/src/core/utils.js +33 -6
- package/src/index.d.ts +115 -0
- package/src/index.js +3 -1
package/src/core/utils.js
CHANGED
|
@@ -1,14 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for Lume.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Resolve a nested path in an object.
|
|
3
|
-
* Example:
|
|
7
|
+
* Example: resolvePath(obj, ['user', 'address']) returns obj.user.address
|
|
4
8
|
*
|
|
5
9
|
* @param {object} obj - The root object
|
|
6
10
|
* @param {string[]} pathArr - Array of keys
|
|
7
11
|
* @returns {object} Last object in the path
|
|
12
|
+
* @throws {Error} If path is invalid or doesn't exist
|
|
8
13
|
*/
|
|
9
14
|
export function resolvePath(obj, pathArr) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
+
// If no path, return the object itself
|
|
16
|
+
if (!pathArr || pathArr.length === 0) {
|
|
17
|
+
return obj;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let current = obj;
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < pathArr.length; i++) {
|
|
23
|
+
const key = pathArr[i];
|
|
24
|
+
|
|
25
|
+
if (current === null || current === undefined) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Cannot access property "${key}" of ${current} at path: ${pathArr.slice(0, i + 1).join('.')}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!(key in current)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Property "${key}" does not exist at path: ${pathArr.slice(0, i + 1).join('.')}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
current = current[key];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return current;
|
|
41
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lume.js TypeScript Definitions
|
|
3
|
+
*
|
|
4
|
+
* Provides type safety for reactive state management
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Unsubscribe function returned by $subscribe
|
|
9
|
+
*/
|
|
10
|
+
export type Unsubscribe = () => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Subscriber callback function
|
|
14
|
+
*/
|
|
15
|
+
export type Subscriber<T> = (value: T) => void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reactive state object with $subscribe method
|
|
19
|
+
*/
|
|
20
|
+
export type ReactiveState<T extends object> = T & {
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to changes on a specific property key
|
|
23
|
+
* @param key - Property key to watch
|
|
24
|
+
* @param callback - Function called when property changes
|
|
25
|
+
* @returns Unsubscribe function for cleanup
|
|
26
|
+
*/
|
|
27
|
+
$subscribe<K extends keyof T>(
|
|
28
|
+
key: K,
|
|
29
|
+
callback: Subscriber<T[K]>
|
|
30
|
+
): Unsubscribe;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a reactive state object
|
|
35
|
+
*
|
|
36
|
+
* @param obj - Plain object to make reactive
|
|
37
|
+
* @returns Reactive proxy with $subscribe method
|
|
38
|
+
* @throws {Error} If obj is not a plain object
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const store = state({
|
|
43
|
+
* count: 0,
|
|
44
|
+
* user: state({
|
|
45
|
+
* name: 'Alice'
|
|
46
|
+
* })
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* store.count++; // Triggers reactivity
|
|
50
|
+
*
|
|
51
|
+
* const unsub = store.$subscribe('count', (val) => {
|
|
52
|
+
* console.log('Count:', val);
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Cleanup
|
|
56
|
+
* unsub();
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function state<T extends object>(obj: T): ReactiveState<T>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Bind reactive state to DOM elements
|
|
63
|
+
*
|
|
64
|
+
* @param root - Root element to scan for [data-bind] attributes
|
|
65
|
+
* @param store - Reactive state object
|
|
66
|
+
* @returns Cleanup function to remove all bindings
|
|
67
|
+
* @throws {Error} If root is not an HTMLElement
|
|
68
|
+
* @throws {Error} If store is not a reactive state object
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const store = state({ count: 0 });
|
|
73
|
+
* const cleanup = bindDom(document.body, store);
|
|
74
|
+
*
|
|
75
|
+
* // Later: cleanup all bindings
|
|
76
|
+
* cleanup();
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* HTML:
|
|
80
|
+
* ```html
|
|
81
|
+
* <span data-bind="count"></span>
|
|
82
|
+
* <input data-bind="name">
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function bindDom(
|
|
86
|
+
root: HTMLElement,
|
|
87
|
+
store: ReactiveState<any>
|
|
88
|
+
): Unsubscribe;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create an effect that automatically tracks dependencies
|
|
92
|
+
*
|
|
93
|
+
* The effect runs immediately and re-runs when any accessed state properties change.
|
|
94
|
+
* Only tracks properties that are actually accessed during execution.
|
|
95
|
+
*
|
|
96
|
+
* @param fn - Function to run reactively
|
|
97
|
+
* @returns Cleanup function to stop the effect
|
|
98
|
+
* @throws {Error} If fn is not a function
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const store = state({ count: 0, name: 'Alice' });
|
|
103
|
+
*
|
|
104
|
+
* const cleanup = effect(() => {
|
|
105
|
+
* // Only tracks 'count' (name not accessed)
|
|
106
|
+
* console.log(`Count: ${store.count}`);
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* store.count = 5; // Effect re-runs
|
|
110
|
+
* store.name = 'Bob'; // Effect does NOT re-run
|
|
111
|
+
*
|
|
112
|
+
* cleanup(); // Stop the effect
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function effect(fn: () => void): Unsubscribe;
|
package/src/index.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Exposes:
|
|
5
5
|
* - state(): create reactive state
|
|
6
6
|
* - bindDom(): zero-runtime DOM binding
|
|
7
|
+
* - effect(): reactive effect with automatic dependency tracking
|
|
7
8
|
*
|
|
8
9
|
* Usage:
|
|
9
|
-
* import { state, bindDom } from "lume-js";
|
|
10
|
+
* import { state, bindDom, effect } from "lume-js";
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
export { state } from "./core/state.js";
|
|
13
14
|
export { bindDom } from "./core/bindDom.js";
|
|
15
|
+
export { effect } from "./core/effect.js";
|