vaderjs 1.0.1 → 1.0.2

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/config.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "port": 3001,
3
+ "type": "http",
4
+ "origin": "http://localhost:3000",
5
+ "path": "/api"
6
+
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A Reactive Framework for Single-Page Applications (SPA)",
5
5
  "main": "vader.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # VaderJS: A Reactive Framework for SPAs
2
2
 
3
- [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE)
3
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/vaderjs.svg?style=flat)](https://www.npmjs.com/package/vaderjs)
4
4
 
5
5
  VaderJS is a powerful reactive framework for building Single-Page Applications (SPAs), inspired by React.js.
6
6
 
@@ -24,7 +24,7 @@ const [state, setState] = useState("count", initialState);
24
24
  function increment(){
25
25
  setState(state + 1)
26
26
  }
27
- registerFunction('increment', increment)
27
+ rf('increment', increment)
28
28
  useEffect((state)=>{
29
29
  console.log('New State for count' + state)
30
30
  }[state])
@@ -34,7 +34,7 @@ useEffect((state)=>{
34
34
  ### Function Binding
35
35
 
36
36
  ```javascript
37
- registerFunction('login', login);
37
+ rf('login', login);
38
38
  return html`<button onclick="login()">Login</button>`;
39
39
  ```
40
40
 
@@ -60,23 +60,53 @@ const { state, setState, subscribe } = store;
60
60
  ### Simplified Component Creation
61
61
 
62
62
  ```javascript
63
- async function myComponent(props){
63
+ // id is a unique component key in which allows vader to update the component state!
64
+ const myComponent = (id) = component(id, {
65
+ render: (states, props) => {
64
66
  return vhtml`
65
67
  <div>${props.message}</div>
66
68
  `
67
69
  }
68
-
69
- async function app(props){
70
- let html = await createComponent(myComponent, {
71
- message: 'Hello Vader'
72
- })
73
- return vhtml `
74
- <div>
75
- ${html}
76
- </div>
77
- `
78
- }
79
-
70
+ })
71
+
72
+ // then call
73
+
74
+ myComponent(key).render({props})
75
+
76
+ //example
77
+
78
+ import VaderRouter from "./router.js";
79
+ import { vhtml, component, rf } from './script.js'
80
+
81
+ const app = component('app', {
82
+ render: (states) => {
83
+ let [count, setCount] = useState('count', 0);
84
+ useEffect(() => {
85
+ console.log(states)
86
+ console.log('App component mounted');
87
+ }, [count]);
88
+
89
+ function incrementHandler() {
90
+ setCount(count + 1);
91
+ }
92
+ rf('incrementHandler', incrementHandler);
93
+
94
+
95
+ return vhtml`
96
+
97
+ <div>
98
+ <button className="btn" onclick="incrementHandler()"
99
+
100
+ >Count: ${count}</button>
101
+ ${
102
+ count > 10 ? '<h1 style="color:lightBlue">Greater Than 10</h1>' : 'Less than 10'
103
+ }
104
+ </div>
105
+ `;
106
+ },
107
+ })
108
+
109
+ document.body.innerHTML = app.render()
80
110
  ```
81
111
 
82
112
  ## Get Started
@@ -98,4 +128,4 @@ VaderJS is released under the MIT License. See the [LICENSE](https://github.com/
98
128
 
99
129
  ## Join the Community
100
130
 
101
- Connect with the VaderJS community on [GitHub](https://github.com/Postr-Inc/Vader.js). Contribute, share feedback, and improve VaderJS for SPA development.
131
+ Connect with the VaderJS community on [GitHub](https://github.com/Postr-Inc/Vader.js). Contribute, share feedback, and improve VaderJS for SPA development.
package/vader.js CHANGED
@@ -1,867 +1,346 @@
1
- const templates = [];
2
- const cache = {};
3
1
  /**
4
- * @file Vader.js
5
- * @version 1.0.0
6
- * @license MIT
7
- * @description A simple ReactLike - framework for building web applications.
8
- */
9
- /**
10
- *
2
+ * @function vhtml
11
3
  * @param {*} strings
12
- * @param {...any} values
13
- * @returns {string}
14
- * @description Creates a template literal and returns it as a string.
4
+ * @param {...any} args
5
+ * @returns modified string
6
+ *
15
7
  */
16
- const vhtml = (strings, ...values) => {
8
+ window.props = {}
9
+ export function vhtml(strings, ...args) {
17
10
  let result = "";
11
+
18
12
  for (let i = 0; i < strings.length; i++) {
19
13
  result += strings[i];
20
- if (i < values.length) {
21
- result += values[i];
14
+ if (i < args.length) {
15
+ result += args[i];
22
16
  }
23
17
  }
24
- let dom = new DOMParser().parseFromString(result, "text/html");
25
-
26
- let eventTypes = [
27
- "click",
28
- "dblclick",
29
- "mousedown",
30
- "mouseup",
31
- "mouseenter",
32
- "mouseleave",
33
- "mousemove",
34
- "keydown",
35
- "keyup",
36
- "focus",
37
- "blur",
38
- ];
39
-
40
- dom.querySelectorAll("[data-on]").forEach((element) => {
41
- eventTypes.forEach((eventType) => {
42
- const attributeName = `data-on-${eventType}`;
43
- if (element.hasAttribute(attributeName)) {
44
- awaitElement(element.tagName).then((el) => {
45
- el.addEventListener(eventType, () => {
46
- const eventCode = el.getAttribute(attributeName);
47
- let eventFunction = new Function(eventCode);
48
- eventFunction();
49
- });
50
- });
51
- }
52
- });
53
- });
54
18
 
55
- dom.querySelectorAll("[id]").forEach((element) => {
56
- const id = element.getAttribute("id");
57
- window[id] = element;
58
- });
59
-
60
- return dom.body.innerHTML;
61
- };
62
-
63
- let init = false;
64
- /**
65
- * @param {string} selector
66
- * @param {function} rt
67
- * @returns {void}
68
- * @description Registers a template to be rendered.
69
- */
70
- const render = async (template) => {
71
- if (!document.querySelector(template.selector)) {
72
- throw new Error(`No element found with selector ${template.selector}`);
73
- } else {
74
- const content = await template.rt();
75
-
76
- document.querySelector(template.selector).innerHTML = content;
77
- window["currentRender"] = template;
78
- if (!init) {
79
- init = true;
80
- hydrate();
81
- }
19
+ let dom = new DOMParser().parseFromString(result, 'text/html')
20
+ if(dom.body.firstChild.nodeName.toLowerCase() !== 'div'){
21
+ throw new Error(`Ensure that you have a parent div for all component elements`)
82
22
  }
83
- return {
84
- register: () => {
85
- register(template);
86
- },
87
- };
88
- };
89
- /**
90
- * @alias form
91
- *
92
- * @param {*} config
93
- * @returns form component
94
- * @description Creates a form component based on the config object.
95
- * @example
96
- * const form = form({
97
- * name: 'myForm',
98
- * fields: {
99
- * name: {
100
- * value: 'John Doe',
101
- * type: 'text',
102
- * placeholder: 'Enter your name'
103
- * },
104
- * email: {
105
- * value: ''
106
- * }
107
- * },
108
- * onSubmit: (e) => {
109
- * console.log(e)
110
- * }
111
- *
112
- *
113
- * })
114
- */
115
- const form = (config) => {
116
- const { name, fields, onSubmit, inputs, button, onReset, onChange, rules } =
117
- config;
118
- const formData = {};
119
-
120
- const componentInstance = {
121
- state: {},
122
- props: {},
123
- reset: () => {
124
- for (const fieldName in fields) {
125
- formData[fieldName] = fields[fieldName].value || "";
126
- }
127
- document.forms[name].reset();
128
- },
129
- componentDidMount: (form) => {
130
- form.setAttribute("onsubmit", "return false;");
131
-
132
- for (const fieldName in fields) {
133
- formData[fieldName] = fields[fieldName].value || "";
134
- const fieldElement = form[fieldName];
135
-
136
- fieldElement.addEventListener("input", (event) => {
137
- formData[fieldName] = event.target.value;
138
- });
139
- }
140
-
141
- if (onSubmit) {
142
- document.onsubmit = (ev) => {
143
- if (ev.target.name === name) {
144
- let event = formData;
145
-
146
- event["reset"] = componentInstance.reset;
147
- onSubmit(event);
148
- }
149
- };
150
- } else if (onReset) {
151
- document.onreset = (ev) => {
152
- if (ev.target.name === name) {
153
- for (const fieldName in fields) {
154
- formData[fieldName] = fields[fieldName].value || "";
155
- }
156
- onReset();
157
- }
158
- };
159
- } else if (onChange) {
160
- document.onchange = (ev) => {
161
- if (formData[ev.target.name]) {
162
- let event = formData;
163
-
164
- event["reset"] = componentInstance.reset;
165
- onChange(event);
166
- }
167
- };
168
- }
169
- },
170
- render: async () => {
171
- const formElement = document.createElement("form");
172
- formElement.name = name;
173
-
174
- for (const fieldName in fields) {
175
- const fieldConfig = fields[fieldName];
176
- const fieldElement = document.createElement("input");
177
- fieldElement.name = fieldName;
178
- fieldElement.value = fieldConfig.value || "";
179
- fieldElement.type = fieldConfig.type || "text";
180
- fieldElement.placeholder = fieldConfig.placeholder || "";
181
- if (inputs && inputs[fieldName]) {
182
- const fieldStyles = inputs[fieldName];
183
- for (const key in fieldStyles) {
184
- fieldElement.style[key] = fieldStyles[key];
185
- }
186
- }
187
-
188
- if (rules) {
189
- Object.keys(rules).forEach((rule) => {
190
- if (rule === fieldName) {
191
- const rulesobj = rules[rule];
192
- // set all attributes to fieldElement
193
- for (const key in rulesobj) {
194
- fieldElement.setAttribute(key, rulesobj[key]);
195
- }
196
- }
197
- });
198
- }
199
-
200
- document.oninput = (ev) => {
201
- let fieldName = ev.target.name;
202
- let fieldValue = ev.target.value;
203
- formData[fieldName] = fieldValue;
204
- };
205
-
206
- // Add more attributes or properties to the fieldElement if needed
207
- formElement.appendChild(fieldElement);
208
- }
209
- const submitButton = document.createElement("button");
210
- submitButton.type = "submit";
211
- submitButton.textContent = button.text || "Submit";
212
- if (button.styles) {
213
- for (const key in button.styles) {
214
- submitButton.style[key] = button.styles[key];
215
- }
216
- }
217
- formElement.appendChild(submitButton);
218
-
219
- // Call componentDidMount
220
- componentInstance.componentDidMount(formElement);
221
-
222
- return formElement.outerHTML;
223
- },
224
- };
225
- window.currentRender = componentInstance;
226
-
227
- return componentInstance;
228
- };
229
-
230
- /**
231
- * Component Lifecycle Hooks
232
- */
233
- const componentLifecycle = {
234
- componentWillMount: [],
235
- componentDidMount: [],
236
- componentWillUnmount: [],
237
- };
238
-
239
- /**
240
- * Register a lifecycle hook for a component
241
- * @param {string} hookName - Name of the lifecycle hook
242
- * @param {Function} hookFunction - Function to be called at the lifecycle stage
243
- */
244
- const registerLifecycleHook = (hookName, hookFunction) => {
245
- componentLifecycle[hookName].push(hookFunction);
246
- };
247
-
248
- /**
249
- * Call all registered lifecycle hooks for a component
250
- * @param {string} hookName - Name of the lifecycle hook
251
- * @param {Object} component - Component object
252
- */
253
- const callLifecycleHooks = (hookName, component) => {
254
- componentLifecycle[hookName].forEach((hookFunction) => {
255
- hookFunction(component);
256
- });
257
- };
258
23
 
259
- /**
260
- * Component wrapper function
261
- * @param {Function} componentFn - Component function
262
- * @param {Object} props - Component props
263
- * @returns {Promise} - Promise with the component content
264
- */
265
- const component = async (componentFn, props) => {
266
- let isMounted = true;
267
-
268
- const componentInstance = {
269
- state: {},
270
- props,
271
- setState: (newState) => {
272
- if (isMounted) {
273
- componentInstance.state = { ...componentInstance.state, ...newState };
274
- render(template); // Assuming 'template' is defined somewhere
275
- }
276
- },
277
- };
278
-
279
- // Call componentDidMount lifecycle hooks
280
- callLifecycleHooks("componentWillMount", componentInstance);
281
-
282
- const content = await componentFn(props);
283
-
284
- // Call componentDidMount lifecycle hooks
285
- callLifecycleHooks("componentDidMount", componentInstance);
286
-
287
- const unmount = () => {
288
- isMounted = false;
289
- // Call componentWillUnmount lifecycle hooks
290
- callLifecycleHooks("componentWillUnmount", componentInstance);
291
- };
292
-
293
- return {
294
- ...content,
295
- unmount,
296
- };
297
- };
298
-
299
- /**
300
- * Create a new component instance
301
- * @param {Function} componentFn - Component function
302
- * @param {Object} props - Component props
303
- * @returns {Promise<ComponentInstance>} - Promise with the component instance
304
- * @typedef {Object} ComponentInstance - Component instance
305
- * @property {Object} state - Component state
306
- * @property {Object} props - Component props
307
- * @property {Function} setState - Function to update the component state
308
- */
309
- const createComponent = async (componentFn, props) => {
310
- const componentInstance = {
311
- state: {},
312
- props,
313
- setState: (newState) => {
314
- componentInstance.state = { ...componentInstance.state, ...newState };
315
- },
316
- };
317
- /** @type {Function} */
318
- return await componentFn(props);
319
- };
320
-
321
- // ... (other functions)
322
-
323
- // Exported functions
324
- export {
325
- render,
326
- vhtml,
327
- component,
328
- form,
329
- useAuth,
330
- createComponent,
331
- useExternalStore,
332
- useState,
333
- useEffect,
334
- useReduce,
335
- useSyncStore,
336
- require,
337
- $s,
338
- registerFunction,
339
- };
340
-
341
- // ... (remaining code)
342
-
343
- function hydrate() {
344
- templates.forEach(async (template) => {
345
- if (template === window["currentRender"]) {
346
- render(template);
347
- return;
348
- }
349
- const content = await template.rt();
350
- const element = document.querySelector(template.selector);
351
-
352
- if (element) {
353
- if (template.renderedContent !== content) {
354
- element.innerHTML = content;
355
- template.renderedContent = content;
356
- }
357
- }
358
- });
24
+ dom.body.querySelectorAll('[className]').forEach((el)=>{
25
+ el.setAttribute('class', el.getAttribute('classname'))
26
+ el.removeAttribute('classname')
27
+ })
28
+ return dom.body.innerHTML
359
29
  }
360
30
  /**
361
- * Register a function to be available in the component scope for vhtml elements.
362
- *
363
- * @param {string} name - The name to assign to the function in the global scope.
364
- * @param {Function} fn - The function to register.
365
- * @throws {Error} Throws an error if the name is not a string or if the function is not a valid function.
366
- * @throws {Error} Throws an error if the name is already used in the global scope.
367
- * @param {string} name @type {string}
368
- * @example
369
- * function login() {
370
- * setState({ loggedIn: true });
371
- * }
372
- * registerFunction('login', login);
373
- * return html`<button onclick="login()">Login</button>`;
374
- */
375
- const registerFunction = (name, fn) => {
376
- if (typeof name !== "string") {
377
- throw new Error("The name parameter must be a string.");
378
- }
379
-
380
- if (typeof fn !== "function") {
381
- throw new Error("The fn parameter must be a function.");
382
- }
383
-
384
-
385
-
31
+ * @function component
32
+ * @param {*} name
33
+ * @param {*} options
34
+ * @returns
35
+ * @param {*} states
36
+ * @param {*} setState
37
+ * @param {*} useState
38
+ * @param {*} useEffect
39
+ * @param {*} useAuth
40
+ * @param {*} render
41
+ *
42
+ * @example
43
+ *
44
+ * const app = component('app', {
45
+ render: (states, props) => {
46
+
47
+
48
+ let [count, setCount] = useState('count', 0);
49
+
50
+ useEffect(() => {
51
+ console.log('App component mounted');
52
+ });
53
+
54
+ function incrementHandler() {
55
+ setCount(count + 1);
56
+ }
57
+ rf('incrementHandler', incrementHandler);
58
+
59
+
60
+ return vhtml`
61
+ <div>
62
+ <button onclick="incrementHandler()" >Click me</button>
63
+ <p>You clicked ${count} times</p>
64
+ </div>
65
+ `;
66
+ },
67
+ });
68
+ ***/
69
+
70
+ export function component(name, options) {
71
+ const states = {};
72
+ const effects = {};
73
+ const executedEffects = {};
74
+ let storedProps = {};
386
75
  /**
387
- * @global
388
- * @function
389
- * @name {Function}
76
+ * @function setState
77
+ * @param {*} key
78
+ * @param {*} value
79
+ * @returns {null}
80
+ * @description Allows you to change state of component and re-render it
390
81
  */
391
- window[name] = fn;
392
- };
393
-
394
- function doxMethods(element) {
395
- element.on = function (event, callback) {
396
- element.addEventListener(event, callback);
82
+ const setState = (key, value) => {
83
+ states[key] = value;
84
+ updateComponent();
397
85
  };
398
- element.query = function (selector) {
399
- return element.querySelector(selector);
400
- };
401
-
402
- return element;
403
- }
404
-
405
- const awaitElement = (selector) => {
406
- return new Promise((resolve, reject) => {
407
- const interval = setInterval(() => {
408
- if (document.querySelector(selector)) {
409
- clearInterval(interval);
410
- resolve(document.querySelector(selector));
411
- }
412
- }, 100);
413
- });
414
- };
415
-
416
- /**
417
- * Create a new state variable with optional initial value.
418
- *
419
- * @param {string} StateName - The name of the state variable.
420
- * @param {*} initialState - The initial state value.
421
- * @returns {Array} An array containing the current state value and a function to update the state.
422
- * @typedef {Array} StateHook
423
- * @property {*} 0 - The current state value.
424
- * @property {Function} 1 - A function to update the state.
425
- */
426
- const useState = (StateName, initialState) => {
427
- let currentstate;
428
-
429
- // Attempt to retrieve state from sessionStorage
430
- if (sessionStorage.getItem(StateName)) {
431
- currentstate = JSON.parse(sessionStorage.getItem(StateName));
432
- } else {
433
- // If state is not found in sessionStorage, use initial state
434
- currentstate = initialState;
435
- sessionStorage.setItem(StateName, JSON.stringify(initialState));
436
- }
437
86
 
438
87
  /**
439
- * Update the state with a new value.
440
- *
441
- * @param {*} newState - The new state value.
442
- * @returns {*} The new state value.
88
+ * @function useState
89
+ * @param {*} key
90
+ * @param {*} initialValue
91
+ * @returns {Array} [state, setState]
92
+ * @description Allows you to bind state to component
443
93
  */
444
- const setState = (newState) => {
445
- currentstate = newState;
446
- sessionStorage.setItem(StateName, JSON.stringify(newState));
447
- let clonedState = JSON.parse(JSON.stringify(newState));
448
- window.postMessage({ state: clonedState }, "*");
449
- hydrate();
450
- window[StateName] = newState;
451
- return newState;
452
- };
453
-
454
- return [currentstate, setState];
455
- };
456
-
457
- /**
458
- * Update a state variable in sessionStorage and the global scope.
459
- *
460
- * @param {string} statename - The name of the state variable.
461
- * @param {*} newState - The new state value.
462
- */
463
- const setState = (statename, newState) => {
464
- window[statename] = sessionStorage.setItem(
465
- statename,
466
- JSON.stringify(newState)
467
- );
468
- window.postMessage({ state: newState }, "*");
469
- hydrate();
470
- };
471
-
472
- /**
473
- * Create a store with state management functionality.
474
- *
475
- * @param {Object} initialState - The initial state of the store.
476
- * @returns {Object} An object containing state management functions.
477
- */
478
- const createStore = (initialState) => {
479
- let state = initialState;
480
- const subscribers = new Set();
94
+ const useState = (key, initialValue) => {
95
+ if (!(key in states)) {
96
+ states[key] = initialValue;
97
+ window.props[key] = initialValue;
98
+ }
481
99
 
100
+ return [states[key], (newValue) => setState(key, newValue)];
101
+ };
482
102
  /**
483
- * Update the store's state and notify subscribers.
484
- *
485
- * @param {Object} newState - The new state to set.
103
+ * @function useEffect
104
+ * @param {*} effectFn
105
+ * @returns {null}
106
+ * @description Allows you to run side effects
486
107
  */
487
- const setState = (newState) => {
488
- state = newState;
489
- subscribers.forEach((subscriber) => subscriber(state));
490
108
 
491
- // Update local storage with the new state
492
- if (localStorage.getItem("store")) {
493
- let store = JSON.parse(localStorage.getItem("store"));
494
- store = { ...store, ...state };
495
- localStorage.setItem("store", JSON.stringify(store));
496
- } else {
497
- localStorage.setItem("store", JSON.stringify(state));
109
+ const useEffect = (effectFn, dependencies) => {
110
+ if (!effects[name]) {
111
+ effects[name] = [];
498
112
  }
499
-
500
- // Notify other windows/frames about the state change
501
- window.postMessage({ state: state }, "*");
502
- hydrate();
113
+ if (dependencies.length > 1) {
114
+ runEffects();
115
+ }
116
+ effects[name].push(effectFn);
117
+ runEffects();
503
118
  };
504
119
 
505
120
  /**
506
- * Subscribe a function to be notified of state changes.
507
- *
508
- * @param {Function} subscriber - The function to call when the state changes.
509
- * @returns {Function} A function to unsubscribe the subscriber.
121
+ * @function useAuth
122
+ * @param {*} rulesets
123
+ * @param {*} user
124
+ * @returns {Object} {canAccess, grantAccess, revokeAccess}
125
+ * @description Allows you to manage access to resources through rulesets
510
126
  */
511
- const subscribe = (subscriber) => {
512
- subscribers.add(subscriber);
513
- return () => {
514
- subscribers.delete(subscriber);
515
- };
516
- };
517
-
518
- return { state, setState, subscribe };
519
- };
520
-
521
- /**
522
- * Create an authentication object with utility methods for managing user permissions and roles.
523
- *
524
- * @param {Object} options - The options object.
525
- * @param {Array} options.rulesets - An array of rulesets defining user permissions.
526
- * @param {Object} options.user - The user object containing roles and other information.
527
- * @returns {Object} The authentication object with utility methods.
528
- */
529
- function useAuth(options) {
530
- if (!options.rulesets) {
531
- throw new Error("No rulesets provided");
532
- }
127
+ const useSyncStore = (storeName, initialState) => {
128
+ const storedState =
129
+ JSON.parse(localStorage.getItem(storeName)) || initialState;
130
+ const store = createStore(storedState);
533
131
 
534
- let rules = options.rulesets;
535
- let user = options.user;
536
-
537
- const auth = {
538
- /**
539
- * Check if the user can perform a specific action.
540
- *
541
- * @param {string} action - The action to check.
542
- * @returns {boolean} True if the user can perform the action, false otherwise.
543
- */
544
- can: (action) => {
545
- let can = false;
546
- rules.forEach((rule) => {
547
- if (rule.action === action) {
548
- if (rule.condition(user)) {
549
- can = true;
550
- }
551
- }
552
- });
553
- return can;
554
- },
555
- /**
556
- * Check if the user has a specific role.
557
- *
558
- * @param {string} role - The role to check.
559
- * @returns {boolean} True if the user has the role, false otherwise.
560
- */
561
- hasRole: (role) => {
562
- return user.roles && user.roles.includes(role);
563
- },
564
- /**
565
- * Check if the user can perform a specific action with a specific role.
566
- *
567
- * @param {string} action - The action to check.
568
- * @param {string} role - The role to check.
569
- * @returns {boolean} True if the user can perform the action with the role, false otherwise.
570
- */
571
- canWithRole: (action, role) => {
572
- return auth.can(action) && auth.hasRole(role);
573
- },
574
- /**
575
- * Assign a new rule to the rulesets.
576
- *
577
- * @param {Object} rule - The rule to assign.
578
- */
579
- assignRule: (rule) => {
580
- if (!rules.some((existingRule) => existingRule.action === rule.action)) {
581
- rules.push(rule);
582
- }
583
- },
584
132
  /**
585
- * Revoke a rule from the rulesets.
133
+ * Get the value of a specific field from the store's state.
586
134
  *
587
- * @param {string} action - The action of the rule to revoke.
135
+ * @param {string} fieldName - The name of the field.
136
+ * @returns {*} The value of the specified field.
588
137
  */
589
- revokeRule: (action) => {
590
- rules = rules.filter((rule) => rule.action !== action);
591
- },
592
- /**
593
- * Check if the user can perform any of the specified actions.
594
- *
595
- * @param {Array} actions - An array of actions to check.
596
- * @returns {boolean} True if the user can perform any of the actions, false otherwise.
597
- */
598
- canAnyOf: (actions) => {
599
- return actions.some((action) => auth.can(action));
600
- },
138
+ const getField = (fieldName) => {
139
+ return store.state[fieldName];
140
+ };
141
+
601
142
  /**
602
- * Check if the user can perform all of the specified actions.
143
+ * Set the value of a specific field in the store's state.
603
144
  *
604
- * @param {Array} actions - An array of actions to check.
605
- * @returns {boolean} True if the user can perform all of the actions, false otherwise.
145
+ * @param {string} fieldName - The name of the field.
146
+ * @param {*} value - The new value to set for the field.
606
147
  */
607
- canAllOf: (actions) => {
608
- return actions.every((action) => auth.can(action));
609
- },
148
+ const setField = (fieldName, value) => {
149
+ const newState = { ...store.state, [fieldName]: value };
150
+ store.setState(newState);
151
+ };
152
+
610
153
  /**
611
- * Check if the user can perform a group of actions based on a logical operator.
154
+ * Subscribe a function to be notified of state changes.
612
155
  *
613
- * @param {Array} actions - An array of actions to check.
614
- * @param {string} logicalOperator - The logical operator to use ('any' or 'all').
615
- * @returns {boolean} True if the user can perform the actions based on the logical operator, false otherwise.
156
+ * @param {Function} subscriber - The function to call when the state changes.
157
+ * @returns {Function} A function to unsubscribe the subscriber.
616
158
  */
617
- canGroup: (actions, logicalOperator = "any") => {
618
- return logicalOperator === "any"
619
- ? auth.canAnyOf(actions)
620
- : auth.canAllOf(actions);
621
- },
622
- };
623
-
624
- return auth;
625
- }
626
-
627
- /**
628
- * Create a synchronized store with field-level state management.
629
- *
630
- * @param {string} storeName - The name of the store.
631
- * @param {Object} initialState - The initial state of the store.
632
- * @returns {Object} An object containing field management functions.
633
- */
634
- const useSyncStore = (storeName, initialState) => {
635
- const storedState = JSON.parse(localStorage.getItem("store")) || initialState;
636
- const store = createStore(storedState);
637
-
638
- /**
639
- * Get the value of a specific field from the store's state.
640
- *
641
- * @param {string} fieldName - The name of the field.
642
- * @returns {*} The value of the specified field.
643
- */
644
- const getField = (fieldName) => {
645
- return store.state[fieldName];
646
- };
647
-
648
- /**
649
- * Set the value of a specific field in the store's state.
650
- *
651
- * @param {string} fieldName - The name of the field.
652
- * @param {*} value - The new value to set for the field.
653
- */
654
- const setField = (fieldName, value) => {
655
- const newState = { ...store.state, [fieldName]: value };
656
- store.setState(newState);
657
- };
658
-
659
- /**
660
- * Subscribe a function to be notified of state changes.
661
- *
662
- * @param {Function} subscriber - The function to call when the state changes.
663
- * @returns {Function} A function to unsubscribe the subscriber.
664
- */
665
- const subscribe = (subscriber) => {
666
- return store.subscribe(subscriber);
667
- };
668
-
669
- /**
670
- * Clear the stored state from local storage.
671
- */
672
- const clear = () => {
673
- localStorage.setItem("store", "");
674
- };
675
-
676
- return {
677
- getField,
678
- setField,
679
- subscribe,
680
- clear,
681
- };
682
- };
683
-
684
- /**
685
- * Create an external store with API integration and local storage caching.
686
- *
687
- * @param {Object} options - Configuration options for the external store.
688
- * @param {Function} options.api - The API method to fetch data.
689
- * @param {string} options.storeKey - The key for storing data in local storage.
690
- * @param {Object} initialState - The initial state of the store.
691
- * @returns {Object} An object containing store management functions.
692
- */
693
- const useExternalStore = (options, initialState) => {
694
- const store = createStore(initialState);
695
- const { subscribe, setState } = store;
696
-
697
- let consecutiveFetches = 0;
698
- let lastFetchedData = null;
699
- let debounceTimer = null;
700
-
701
- if (options.api) {
702
- const apiMethod = options.api;
159
+ const subscribe = (subscriber) => {
160
+ return store.subscribe(subscriber);
161
+ };
703
162
 
704
163
  /**
705
- * Update the store with fetched data from the API.
706
- *
707
- * @param {...*} args - Arguments to pass to the API method.
164
+ * Clear the stored state from local storage.
708
165
  */
709
- store.update = async (...args) => {
710
- try {
711
- const fetchedData = await apiMethod(...args);
166
+ const clear = () => {
167
+ localStorage.setItem(storeName, "");
168
+ };
712
169
 
713
- if (JSON.stringify(fetchedData) === JSON.stringify(lastFetchedData)) {
714
- consecutiveFetches++;
715
- } else {
716
- consecutiveFetches = 1;
717
- lastFetchedData = fetchedData;
170
+ return {
171
+ getField,
172
+ setField,
173
+ subscribe,
174
+ clear,
175
+ };
176
+ };
718
177
 
719
- // Save the fetched data to localStorage
720
- localStorage.setItem(options.storeKey, JSON.stringify(fetchedData));
721
- }
178
+ function useAuth(options) {
179
+ if (!options.rulesets) {
180
+ throw new Error("No rulesets provided");
181
+ }
722
182
 
723
- if (consecutiveFetches === 3) {
724
- clearTimeout(debounceTimer); // Clear previous timer
725
- debounceTimer = setTimeout(() => {
726
- console.log("Updating state with fetched data:", fetchedData);
727
- setState(fetchedData);
728
- consecutiveFetches = 0;
729
- }, 500); // Adjust the delay time as needed
183
+ let rules = options.rulesets;
184
+ let user = options.user;
185
+
186
+ const auth = {
187
+ /**
188
+ * Check if the user can perform a specific action.
189
+ *
190
+ * @param {string} action - The action to check.
191
+ * @returns {boolean} True if the user can perform the action, false otherwise.
192
+ */
193
+ can: (action) => {
194
+ let can = false;
195
+ rules.forEach((rule) => {
196
+ if (rule.action === action) {
197
+ if (rule.condition(user)) {
198
+ can = true;
199
+ }
200
+ }
201
+ });
202
+ return can;
203
+ },
204
+ /**
205
+ * Check if the user has a specific role.
206
+ *
207
+ * @param {string} role - The role to check.
208
+ * @returns {boolean} True if the user has the role, false otherwise.
209
+ */
210
+ hasRole: (role) => {
211
+ return user.role && user.role.includes(role);
212
+ },
213
+ /**
214
+ * Check if the user can perform a specific action with a specific role.
215
+ *
216
+ * @param {string} action - The action to check.
217
+ * @param {string} role - The role to check.
218
+ * @returns {boolean} True if the user can perform the action with the role, false otherwise.
219
+ */
220
+ canWithRole: (action, role) => {
221
+ return auth.can(action) && auth.hasRole(role);
222
+ },
223
+ /**
224
+ * Assign a new rule to the rulesets.
225
+ *
226
+ * @param {Object} rule - The rule to assign.
227
+ */
228
+ assignRule: (rule) => {
229
+ if (
230
+ !rules.some((existingRule) => existingRule.action === rule.action)
231
+ ) {
232
+ rules.push(rule);
730
233
  }
731
- } catch (error) {
732
- console.error("Error fetching data:", error);
733
- }
234
+ },
235
+ /**
236
+ * Revoke a rule from the rulesets.
237
+ *
238
+ * @param {string} action - The action of the rule to revoke.
239
+ */
240
+ revokeRule: (action) => {
241
+ rules = rules.filter((rule) => rule.action !== action);
242
+ },
243
+ /**
244
+ * Check if the user can perform any of the specified actions.
245
+ *
246
+ * @param {Array} actions - An array of actions to check.
247
+ * @returns {boolean} True if the user can perform any of the actions, false otherwise.
248
+ */
249
+ canAnyOf: (actions) => {
250
+ return actions.some((action) => auth.can(action));
251
+ },
252
+ /**
253
+ * Check if the user can perform all of the specified actions.
254
+ *
255
+ * @param {Array} actions - An array of actions to check.
256
+ * @returns {boolean} True if the user can perform all of the actions, false otherwise.
257
+ */
258
+ canAllOf: (actions) => {
259
+ return actions.every((action) => auth.can(action));
260
+ },
261
+ /**
262
+ * Check if the user can perform a group of actions based on a logical operator.
263
+ *
264
+ * @param {Array} actions - An array of actions to check.
265
+ * @param {string} logicalOperator - The logical operator to use ('any' or 'all').
266
+ * @returns {boolean} True if the user can perform the actions based on the logical operator, false otherwise.
267
+ */
268
+ canGroup: (actions, logicalOperator = "any") => {
269
+ return logicalOperator === "any"
270
+ ? auth.canAnyOf(actions)
271
+ : auth.canAllOf(actions);
272
+ },
734
273
  };
735
274
 
736
- // Load initial state from localStorage
737
- const storedData = localStorage.getItem(options.storeKey);
738
- if (storedData) {
739
- localStorage.setItem(options.storeKey, storedData);
740
- if (!isntHydrated) {
741
- hydrate(isntHydrated);
742
- isntHydrated = true;
743
- }
744
- }
275
+ return auth;
745
276
  }
746
277
 
747
- return {
748
- ...store,
749
- subscribe,
750
- delete: () => {
751
- localStorage.removeItem(options.storeKey);
752
- },
753
- clear: () => {
754
- localStorage.setItem(options.storeKey, "");
755
- },
756
- return: () => {
757
- isntHydrated = false;
758
- },
759
- };
760
- };
761
-
762
- /**
763
- * Create a state management hook that uses a reducer function to update state.
764
- *
765
- * @template S, A
766
- * @param {function(S, A): S} reducer - The reducer function that updates the state.
767
- * @param {S} initialState - The initial state value.
768
- * @returns {[S, function(A): void]} An array containing the current state and a dispatch function.
769
- */
770
- const useReduce = (reducer, initialState) => {
771
- /**
772
- * The current state managed by the reducer.
773
- * @type {S}
774
- */
775
- const [state, setState] = useState(initialState + "state", initialState);
776
-
777
278
  /**
778
- * Dispatches an action to the reducer to update the state.
779
- *
780
- * @param {A} action - The action to be dispatched.
279
+ * @function runEffects
280
+ * @returns {null}
281
+ * @description Allows you to run side effects
781
282
  */
782
- const dispatch = (action) => {
783
- const newState = reducer(state, action);
784
- setState(newState);
283
+ const runEffects = () => {
284
+ if (!executedEffects[name] && effects[name]) {
285
+ effects[name].forEach((effectFn) => effectFn());
286
+ executedEffects[name] = true;
287
+ }
785
288
  };
786
-
787
- return [state, dispatch];
788
- };
789
-
790
- const getState = (statename) => {
791
- const storedState = sessionStorage.getItem(statename);
792
- return storedState ? JSON.parse(storedState) : null;
793
- };
794
- window["getState"] = getState;
795
- window["setState"] = setState;
796
-
797
- /**
798
- *
799
- * @param {Function} callback
800
- * @param {Array} dependencies
801
- * @example
802
- * useEffect(() => {
803
- * console.log('state changed');
804
- * }, ['state']);
805
- */
806
- const useEffect = (callback, dependencies) => {
807
- callback();
808
- window.addEventListener("message", (event) => {
809
- if (dependencies.includes(event.data.state)) {
810
- callback(event.data.state);
811
- return;
289
+ window.useState = useState;
290
+ window.setState = setState;
291
+ window.useEffect = useEffect;
292
+ window.useAuth = useAuth;
293
+ window.useSyncStore = useSyncStore;
294
+ const updateComponent = () => {
295
+ const componentContainer = document.querySelector(
296
+ `[data-component="${name}"]`
297
+ );
298
+ if (componentContainer) {
299
+ runEffects;
300
+
301
+ componentContainer.innerHTML = options.render(
302
+ states,
303
+ (storedProps = null)
304
+ );
812
305
  }
813
- });
814
- };
815
-
816
- const register = (template) => {
817
- templates.push(template);
818
- };
819
- /**
820
- *
821
- * @param {string} path
822
- * @param {object} props
823
- * @returns {Promise} - Promise with the obstructed html.
824
- * @example
825
- * const header = await require('./components/header.html');
826
- */
306
+ };
827
307
 
828
- const require = (path, props) => {
829
- const promise = new Promise((resolve, reject) => {
830
- if (cache[path]) {
831
- resolve(cache[path]);
832
- }
833
- if (!path.endsWith(".html")) {
834
- reject("Only html files are supported");
308
+ const render = (props) => {
309
+ storedProps = props;
310
+ const componentContainer = document.querySelector(
311
+ `[data-component="${name}"]`
312
+ );
313
+ if (componentContainer) {
314
+ runEffects();
315
+
316
+ componentContainer.innerHTML = options.render(
317
+ states,
318
+ (storedProps = null)
319
+ );
320
+ } else {
321
+ return vhtml`
322
+ <div data-component="${name}">
323
+ ${options.render(
324
+ states,
325
+ props
326
+ )}
327
+ </div>
328
+ `;
835
329
  }
836
- fetch(path)
837
- .then((response) => {
838
- return response.text();
839
- })
840
- .then((code) => {
841
- cache[path] = new Function("props", "return " + "`" + code + "`");
842
- hydrate();
843
- resolve(cache[path]);
844
- });
845
- });
846
- return promise;
847
- };
848
- window["require"] = require;
330
+ };
849
331
 
850
- /**
851
- * @typedef {Object} $s
852
- * @property {object} styles - Object with css properties.
853
- * @returns {string} - String with css properties.
854
- * @example
855
- * $s({ color: 'red' })
856
- */
857
- function $s(styles = {}) {
858
- let result = "";
859
- for (let key in styles) {
860
- result += `${key}: ${styles[key]};`;
861
- }
862
- return `style="${result}"`;
332
+ return {
333
+ render,
334
+ };
863
335
  }
864
336
 
865
- window.onbeforeunload = function () {
866
- sessionStorage.clear();
337
+ /**
338
+ * @function rf
339
+ * @param {*} name
340
+ * @param {*} fn
341
+ * @returns {null}
342
+ * @description Allows you to register function in global scope
343
+ */
344
+ export const rf = (name, fn) => {
345
+ window[name] = fn;
867
346
  };
package/vaderRouter.js CHANGED
@@ -206,6 +206,9 @@ class VaderRouter {
206
206
  return: function (data) {
207
207
  this.hooked = false;
208
208
  },
209
+ render: function (selector, data) {
210
+ document.querySelector(selector).innerHTML = data;
211
+ },
209
212
  };
210
213
 
211
214
  callback(req, res);
@@ -351,6 +354,14 @@ class VaderRouter {
351
354
  url: window.location.hash.substring(1),
352
355
  method: "POST",
353
356
  };
357
+ const res = {
358
+ return: function (data) {
359
+ this.hooked = false;
360
+ },
361
+ render: function (selector, data) {
362
+ document.querySelector(selector).innerHTML = data;
363
+ },
364
+ };
354
365
  window.$URL_QUERY = query;
355
366
  window.$URL_PARAMS = params;
356
367
 
@@ -362,7 +373,7 @@ class VaderRouter {
362
373
  * @memberof VaderRouter
363
374
  * @description Allows you to perform actions when the currentRoute changes.
364
375
  */
365
- callback(req);
376
+ callback(req, res);
366
377
  }
367
378
  });
368
379
  }