vaderjs 1.0.7 → 1.0.9
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 +1 -1
- package/readme.md +2 -2
- package/vader.js +116 -48
- package/vaderRouter.js +17 -8
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -20,7 +20,7 @@ router.start();
|
|
|
20
20
|
### State Management
|
|
21
21
|
|
|
22
22
|
```javascript
|
|
23
|
-
const [state, setState] = useState(initialState);
|
|
23
|
+
const [state, setState] = useState('stateName', initialState);
|
|
24
24
|
function increment(){
|
|
25
25
|
setState(state + 1)
|
|
26
26
|
}
|
|
@@ -80,7 +80,7 @@ import { vhtml, component, rf } from './script.js'
|
|
|
80
80
|
|
|
81
81
|
const app = component('app', {
|
|
82
82
|
render: (states) => {
|
|
83
|
-
let [count, setCount] = useState( 0);
|
|
83
|
+
let [count, setCount] = useState('count', 0);
|
|
84
84
|
useEffect(() => {
|
|
85
85
|
console.log(states)
|
|
86
86
|
console.log('App component mounted');
|
package/vader.js
CHANGED
|
@@ -73,10 +73,12 @@ export function vhtml(strings, ...args) {
|
|
|
73
73
|
***/
|
|
74
74
|
|
|
75
75
|
export function component(name, options) {
|
|
76
|
-
|
|
76
|
+
let states = {}
|
|
77
77
|
const effects = {};
|
|
78
78
|
const executedEffects = {};
|
|
79
79
|
let storedProps = {};
|
|
80
|
+
let componentMounted = false;
|
|
81
|
+
let hasMounted = false;
|
|
80
82
|
/**
|
|
81
83
|
* @function setState
|
|
82
84
|
* @param {*} key
|
|
@@ -86,6 +88,7 @@ export function component(name, options) {
|
|
|
86
88
|
*/
|
|
87
89
|
const setState = (key, value) => {
|
|
88
90
|
states[key] = value;
|
|
91
|
+
window.props[key] = value;
|
|
89
92
|
updateComponent();
|
|
90
93
|
};
|
|
91
94
|
|
|
@@ -96,18 +99,23 @@ export function component(name, options) {
|
|
|
96
99
|
* @returns {Array} [state, setState]
|
|
97
100
|
* @description Allows you to bind state to component
|
|
98
101
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
const useState = (key, initialValue) => {
|
|
107
|
+
if (!states[key]) {
|
|
108
|
+
states[key] = initialValue;
|
|
109
|
+
}
|
|
110
|
+
return [
|
|
111
|
+
states[key],
|
|
112
|
+
(value) => {
|
|
113
|
+
states[key] = value;
|
|
114
|
+
window.props[key] = value;
|
|
115
|
+
updateComponent();
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
};
|
|
111
119
|
/**
|
|
112
120
|
* @function useEffect
|
|
113
121
|
* @param {*} effectFn
|
|
@@ -119,13 +127,41 @@ export function component(name, options) {
|
|
|
119
127
|
if (!effects[name]) {
|
|
120
128
|
effects[name] = [];
|
|
121
129
|
}
|
|
122
|
-
if (dependencies.length > 1) {
|
|
123
|
-
runEffects();
|
|
124
|
-
}
|
|
125
130
|
effects[name].push(effectFn);
|
|
126
|
-
|
|
131
|
+
|
|
132
|
+
if (dependencies.length > 0) {
|
|
133
|
+
const oldState = states[name];
|
|
134
|
+
const newState = dependencies.map((dependency) => {
|
|
135
|
+
return states[dependency];
|
|
136
|
+
});
|
|
137
|
+
if (oldState) {
|
|
138
|
+
const hasChanged = newState.some((state) => {
|
|
139
|
+
return state !== oldState;
|
|
140
|
+
});
|
|
141
|
+
if (hasChanged) {
|
|
142
|
+
effectFn();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}else if (!hasMounted) {
|
|
146
|
+
effectFn();
|
|
147
|
+
hasMounted = true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
effects[name] = effects[name].filter((fn) => fn !== effectFn);
|
|
152
|
+
};
|
|
127
153
|
};
|
|
128
154
|
|
|
155
|
+
const useReducer = (key, reducer, initialState) => {
|
|
156
|
+
const [state, setState] = useState(key, initialState);
|
|
157
|
+
|
|
158
|
+
const dispatch = (action) => {
|
|
159
|
+
const newState = reducer(state, action);
|
|
160
|
+
setState(newState);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return [state, dispatch];
|
|
164
|
+
};
|
|
129
165
|
/**
|
|
130
166
|
* @function useSyncStore
|
|
131
167
|
* @param {*} storeName
|
|
@@ -134,8 +170,10 @@ export function component(name, options) {
|
|
|
134
170
|
* @description Allows you to manage state in local storage
|
|
135
171
|
*/
|
|
136
172
|
const useSyncStore = (storeName, initialState) => {
|
|
137
|
-
|
|
138
|
-
|
|
173
|
+
// Load state from local storage or use initial state
|
|
174
|
+
const storedState = JSON.parse(localStorage.getItem(storeName)) || initialState;
|
|
175
|
+
|
|
176
|
+
// Create a store object
|
|
139
177
|
const store = createStore(storedState);
|
|
140
178
|
|
|
141
179
|
/**
|
|
@@ -145,7 +183,7 @@ export function component(name, options) {
|
|
|
145
183
|
* @returns {*} The value of the specified field.
|
|
146
184
|
*/
|
|
147
185
|
const getField = (fieldName) => {
|
|
148
|
-
|
|
186
|
+
return store.state[fieldName];
|
|
149
187
|
};
|
|
150
188
|
|
|
151
189
|
/**
|
|
@@ -155,8 +193,11 @@ export function component(name, options) {
|
|
|
155
193
|
* @param {*} value - The new value to set for the field.
|
|
156
194
|
*/
|
|
157
195
|
const setField = (fieldName, value) => {
|
|
158
|
-
|
|
159
|
-
|
|
196
|
+
// Create a new state object with the updated field
|
|
197
|
+
const newState = { ...store.state, [fieldName]: value };
|
|
198
|
+
// Update the store's state and save it to local storage
|
|
199
|
+
store.setState(newState);
|
|
200
|
+
saveStateToLocalStorage(storeName, newState);
|
|
160
201
|
};
|
|
161
202
|
|
|
162
203
|
/**
|
|
@@ -166,23 +207,39 @@ export function component(name, options) {
|
|
|
166
207
|
* @returns {Function} A function to unsubscribe the subscriber.
|
|
167
208
|
*/
|
|
168
209
|
const subscribe = (subscriber) => {
|
|
169
|
-
|
|
210
|
+
return store.subscribe(subscriber);
|
|
170
211
|
};
|
|
171
212
|
|
|
172
213
|
/**
|
|
173
214
|
* Clear the stored state from local storage.
|
|
174
215
|
*/
|
|
175
216
|
const clear = () => {
|
|
176
|
-
|
|
217
|
+
localStorage.removeItem(storeName);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Save the state to local storage.
|
|
222
|
+
*
|
|
223
|
+
* @param {string} key - The key under which to store the state.
|
|
224
|
+
* @param {*} state - The state to be stored.
|
|
225
|
+
*/
|
|
226
|
+
const saveStateToLocalStorage = (key, state) => {
|
|
227
|
+
try {
|
|
228
|
+
localStorage.setItem(key, JSON.stringify(state));
|
|
229
|
+
} catch (error) {
|
|
230
|
+
// Handle errors when saving to local storage
|
|
231
|
+
console.error('Error saving state to local storage:', error);
|
|
232
|
+
}
|
|
177
233
|
};
|
|
178
234
|
|
|
179
235
|
return {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
236
|
+
getField,
|
|
237
|
+
setField,
|
|
238
|
+
subscribe,
|
|
239
|
+
clear,
|
|
184
240
|
};
|
|
185
|
-
|
|
241
|
+
};
|
|
242
|
+
|
|
186
243
|
/**
|
|
187
244
|
* @function useAuth
|
|
188
245
|
* @param {*} rulesets
|
|
@@ -308,20 +365,39 @@ export function component(name, options) {
|
|
|
308
365
|
window.useEffect = useEffect;
|
|
309
366
|
window.useAuth = useAuth;
|
|
310
367
|
window.useSyncStore = useSyncStore;
|
|
368
|
+
window.useReducer = useReducer;
|
|
311
369
|
|
|
312
370
|
const updateComponent = async () => {
|
|
371
|
+
|
|
372
|
+
|
|
313
373
|
const componentContainer = document.querySelector(
|
|
314
374
|
`[data-component="${name}"]`
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
|
|
375
|
+
);
|
|
376
|
+
const newHtml = await options.render(states, storedProps);
|
|
377
|
+
if (componentContainer && newHtml !== componentContainer.innerHTML) {
|
|
378
|
+
|
|
379
|
+
// only update the chunk of DOM that has changed
|
|
380
|
+
let newDom = new DOMParser().parseFromString(newHtml, 'text/html')
|
|
381
|
+
let oldDom = new DOMParser().parseFromString(componentContainer.innerHTML, 'text/html')
|
|
382
|
+
let html = newDom.body.firstChild
|
|
383
|
+
let oldHtml = oldDom.body.firstChild
|
|
384
|
+
if(!html.isEqualNode(oldHtml)){
|
|
385
|
+
// only update the chunk of DOM that has changed
|
|
386
|
+
componentContainer.innerHTML = newHtml
|
|
387
|
+
|
|
388
|
+
}
|
|
389
|
+
if (!componentMounted) {
|
|
390
|
+
componentMounted = true;
|
|
391
|
+
|
|
318
392
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
393
|
+
// Execute the "component did mount" code here
|
|
394
|
+
if (options.componentDidMount && typeof options.componentDidMount === 'function') {
|
|
395
|
+
options.componentUpdate ? options.componentDidMount() : null
|
|
396
|
+
}
|
|
397
|
+
}
|
|
323
398
|
}
|
|
324
|
-
|
|
399
|
+
};
|
|
400
|
+
|
|
325
401
|
/**
|
|
326
402
|
* @function render
|
|
327
403
|
* @param {*} states
|
|
@@ -332,16 +408,9 @@ export function component(name, options) {
|
|
|
332
408
|
*/
|
|
333
409
|
|
|
334
410
|
const render = async (props) => {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
if (componentContainer) {
|
|
341
|
-
runEffects();
|
|
342
|
-
|
|
343
|
-
componentContainer.innerHTML = await options.render( states, props);
|
|
344
|
-
} else {
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
options.componentDidMount ? options.componentDidMount() : null
|
|
345
414
|
return vhtml`
|
|
346
415
|
<div data-component="${name}">
|
|
347
416
|
${await options.render(
|
|
@@ -351,7 +420,6 @@ export function component(name, options) {
|
|
|
351
420
|
</div>
|
|
352
421
|
`;
|
|
353
422
|
}
|
|
354
|
-
};
|
|
355
423
|
|
|
356
424
|
return {
|
|
357
425
|
render,
|
package/vaderRouter.js
CHANGED
|
@@ -63,6 +63,7 @@ class VaderRouter {
|
|
|
63
63
|
* Starts the router.
|
|
64
64
|
*/
|
|
65
65
|
start() {
|
|
66
|
+
|
|
66
67
|
if (!this.routes[window.location.hash.substring(1)]) {
|
|
67
68
|
window.location.hash = this.starturl;
|
|
68
69
|
}
|
|
@@ -72,7 +73,12 @@ class VaderRouter {
|
|
|
72
73
|
: window.location.hash.substring(1);
|
|
73
74
|
// remove '' from array
|
|
74
75
|
hash = hash.filter((item) => item !== "");
|
|
75
|
-
|
|
76
|
+
let basePath = "";
|
|
77
|
+
if (hash.length > 1) {
|
|
78
|
+
basePath = hash[0] + "/" + hash[1];
|
|
79
|
+
} else {
|
|
80
|
+
basePath = hash[0];
|
|
81
|
+
}
|
|
76
82
|
|
|
77
83
|
if (!this.routes[basePath] && !this.customerror) {
|
|
78
84
|
window.location.hash = this.starturl;
|
|
@@ -313,19 +319,22 @@ class VaderRouter {
|
|
|
313
319
|
.join("/");
|
|
314
320
|
const regex = new RegExp("^" + parsedPath + "(\\?(.*))?$");
|
|
315
321
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
let hash = window.location.hash.split("#")[1]
|
|
323
|
+
? window.location.hash.split("#")[1]
|
|
324
|
+
: window.location.hash;
|
|
325
|
+
|
|
326
|
+
let basePath = "";
|
|
327
|
+
if (hash.length > 1) {
|
|
328
|
+
basePath = hash.split("/")[0] + "/" + hash.split("/")[1];
|
|
321
329
|
} else {
|
|
322
|
-
|
|
330
|
+
basePath = hash[0];
|
|
323
331
|
}
|
|
332
|
+
const route = basePath;
|
|
324
333
|
|
|
325
334
|
window.$CURRENT_URL = route;
|
|
326
335
|
window.$URL_PARAMS = {};
|
|
327
336
|
if (
|
|
328
|
-
|
|
337
|
+
window.location.hash.substring(1).match(regex) &&
|
|
329
338
|
this.routes[$CURRENT_URL]
|
|
330
339
|
) {
|
|
331
340
|
this.storedroutes.push(window.location.hash.substring(1));
|