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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "A Reactive Framework for Single-Page Applications (SPA)",
5
5
  "main": "vader.js",
6
6
  "scripts": {
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
- const states = {};
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
- const useState = ( initialValue) => {
100
- let state = states[name];
101
- if (!state) {
102
- state = initialValue;
103
- states[name] = state;
104
- }
105
- const setState = (value) => {
106
- states[name] = value;
107
- updateComponent();
108
- }
109
- return [state, setState];
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
- runEffects();
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
- const storedState =
138
- JSON.parse(localStorage.getItem(storeName)) || initialState;
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
- return store.state[fieldName];
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
- const newState = { ...store.state, [fieldName]: value };
159
- store.setState(newState);
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
- return store.subscribe(subscriber);
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
- localStorage.setItem(storeName, "");
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
- getField,
181
- setField,
182
- subscribe,
183
- clear,
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
- if (componentContainer) {
317
- runEffects;
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
- componentContainer.innerHTML = await options.render(
320
- states,
321
- (storedProps = null)
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
- storedProps = props;
336
- const componentContainer = document.querySelector(
337
- `[data-component="${name}"]`
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
- const basePath = "/" + hash[0];
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
- this.currentUrl = path;
317
- // replace params if preset
318
- let route = "";
319
- if (path.includes(":")) {
320
- route = path.split(":")[0].replace(/\/$/, "");
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
- route = path.replace(/\/$/, "");
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
- window.location.hash.substring(1).match(regex) &&
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));