vaderjs 1.0.1 → 1.0.3
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 +7 -0
- package/package.json +1 -1
- package/readme.md +47 -17
- package/vader.js +308 -802
- package/vaderRouter.js +12 -1
package/config.json
ADDED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# VaderJS: A Reactive Framework for SPAs
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE)
|
|
3
|
+
[](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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,373 @@
|
|
|
1
|
-
const templates = [];
|
|
2
|
-
const cache = {};
|
|
3
1
|
/**
|
|
4
|
-
* @
|
|
5
|
-
* @
|
|
6
|
-
* @
|
|
7
|
-
* @description A simple ReactLike - framework for building web applications.
|
|
2
|
+
* @Object window
|
|
3
|
+
* @property {Object} props
|
|
4
|
+
* @description Allows you to store props for component
|
|
8
5
|
*/
|
|
6
|
+
window.props = {}
|
|
9
7
|
/**
|
|
8
|
+
* @function vhtml
|
|
9
|
+
* @param {String} strings
|
|
10
|
+
* @param {...any} args
|
|
11
|
+
* @returns modified string
|
|
10
12
|
*
|
|
11
|
-
* @param {*} strings
|
|
12
|
-
* @param {...any} values
|
|
13
|
-
* @returns {string}
|
|
14
|
-
* @description Creates a template literal and returns it as a string.
|
|
15
13
|
*/
|
|
16
|
-
|
|
14
|
+
export function vhtml(strings, ...args) {
|
|
17
15
|
let result = "";
|
|
16
|
+
|
|
18
17
|
for (let i = 0; i < strings.length; i++) {
|
|
19
18
|
result += strings[i];
|
|
20
|
-
if (i <
|
|
21
|
-
result +=
|
|
19
|
+
if (i < args.length) {
|
|
20
|
+
result += args[i];
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
|
-
let dom = new DOMParser().parseFromString(result, "text/html");
|
|
25
23
|
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
24
|
+
let dom = new DOMParser().parseFromString(result, 'text/html')
|
|
25
|
+
if(dom.body.firstChild.nodeName.toLowerCase() !== 'div'){
|
|
26
|
+
throw new Error(`Ensure that you have a parent div for all component elements`)
|
|
82
27
|
}
|
|
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
|
-
|
|
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
28
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
});
|
|
29
|
+
dom.body.querySelectorAll('[className]').forEach((el)=>{
|
|
30
|
+
el.setAttribute('class', el.getAttribute('classname'))
|
|
31
|
+
el.removeAttribute('classname')
|
|
32
|
+
})
|
|
33
|
+
return dom.body.innerHTML
|
|
359
34
|
}
|
|
360
35
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* @param {
|
|
364
|
-
* @
|
|
365
|
-
*
|
|
366
|
-
* @
|
|
367
|
-
* @param {
|
|
368
|
-
* @
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
36
|
+
* @function component
|
|
37
|
+
* @param {*} name
|
|
38
|
+
* @param {*} options
|
|
39
|
+
* @returns
|
|
40
|
+
* @param {*} states
|
|
41
|
+
* @param {*} setState
|
|
42
|
+
* @param {*} useState
|
|
43
|
+
* @param {*} useEffect
|
|
44
|
+
* @param {*} useAuth
|
|
45
|
+
* @param {*} render
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
*
|
|
49
|
+
* const app = component('app', {
|
|
50
|
+
render: (states, props) => {
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
let [count, setCount] = useState('count', 0);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
console.log('App component mounted');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function incrementHandler() {
|
|
60
|
+
setCount(count + 1);
|
|
61
|
+
}
|
|
62
|
+
rf('incrementHandler', incrementHandler);
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
return vhtml`
|
|
66
|
+
<div>
|
|
67
|
+
<button onclick="incrementHandler()" >Click me</button>
|
|
68
|
+
<p>You clicked ${count} times</p>
|
|
69
|
+
</div>
|
|
70
|
+
`;
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
***/
|
|
74
|
+
|
|
75
|
+
export function component(name, options) {
|
|
76
|
+
const states = {};
|
|
77
|
+
const effects = {};
|
|
78
|
+
const executedEffects = {};
|
|
79
|
+
let storedProps = {};
|
|
386
80
|
/**
|
|
387
|
-
* @
|
|
388
|
-
* @
|
|
389
|
-
* @
|
|
81
|
+
* @function setState
|
|
82
|
+
* @param {*} key
|
|
83
|
+
* @param {*} value
|
|
84
|
+
* @returns {null}
|
|
85
|
+
* @description Allows you to change state of component and re-render it
|
|
390
86
|
*/
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
function doxMethods(element) {
|
|
395
|
-
element.on = function (event, callback) {
|
|
396
|
-
element.addEventListener(event, callback);
|
|
397
|
-
};
|
|
398
|
-
element.query = function (selector) {
|
|
399
|
-
return element.querySelector(selector);
|
|
87
|
+
const setState = (key, value) => {
|
|
88
|
+
states[key] = value;
|
|
89
|
+
updateComponent();
|
|
400
90
|
};
|
|
401
91
|
|
|
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
|
-
|
|
438
92
|
/**
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
* @param {*}
|
|
442
|
-
* @returns
|
|
93
|
+
* @function useState
|
|
94
|
+
* @param {*} key
|
|
95
|
+
* @param {*} initialValue
|
|
96
|
+
* @returns {Array} [state, setState]
|
|
97
|
+
* @description Allows you to bind state to component
|
|
443
98
|
*/
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
};
|
|
99
|
+
const useState = (key, initialValue) => {
|
|
100
|
+
if (!(key in states)) {
|
|
101
|
+
states[key] = initialValue;
|
|
102
|
+
window.props[key] = initialValue;
|
|
103
|
+
}
|
|
471
104
|
|
|
472
|
-
/**
|
|
473
|
-
|
|
474
|
-
*
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
*/
|
|
478
|
-
const createStore = (initialState) => {
|
|
479
|
-
let state = initialState;
|
|
480
|
-
const subscribers = new Set();
|
|
105
|
+
/**
|
|
106
|
+
* @Array state
|
|
107
|
+
* @param {*} states
|
|
108
|
+
* @description Allows you to get state of component
|
|
109
|
+
*/
|
|
481
110
|
|
|
111
|
+
return [states[key], (newValue) => setState(key, newValue)];
|
|
112
|
+
};
|
|
482
113
|
/**
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
* @
|
|
114
|
+
* @function useEffect
|
|
115
|
+
* @param {*} effectFn
|
|
116
|
+
* @returns {null}
|
|
117
|
+
* @description Allows you to run side effects
|
|
486
118
|
*/
|
|
487
|
-
const setState = (newState) => {
|
|
488
|
-
state = newState;
|
|
489
|
-
subscribers.forEach((subscriber) => subscriber(state));
|
|
490
119
|
|
|
491
|
-
|
|
492
|
-
if (
|
|
493
|
-
|
|
494
|
-
store = { ...store, ...state };
|
|
495
|
-
localStorage.setItem("store", JSON.stringify(store));
|
|
496
|
-
} else {
|
|
497
|
-
localStorage.setItem("store", JSON.stringify(state));
|
|
120
|
+
const useEffect = (effectFn, dependencies) => {
|
|
121
|
+
if (!effects[name]) {
|
|
122
|
+
effects[name] = [];
|
|
498
123
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
124
|
+
if (dependencies.length > 1) {
|
|
125
|
+
runEffects();
|
|
126
|
+
}
|
|
127
|
+
effects[name].push(effectFn);
|
|
128
|
+
runEffects();
|
|
503
129
|
};
|
|
504
130
|
|
|
505
131
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
* @param {
|
|
509
|
-
* @returns {
|
|
132
|
+
* @function useSyncStore
|
|
133
|
+
* @param {*} storeName
|
|
134
|
+
* @param {*} initialState
|
|
135
|
+
* @returns {Object} {getField, setField, subscribe, clear}
|
|
136
|
+
* @description Allows you to manage state in local storage
|
|
510
137
|
*/
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
}
|
|
533
|
-
|
|
534
|
-
let rules = options.rulesets;
|
|
535
|
-
let user = options.user;
|
|
138
|
+
const useSyncStore = (storeName, initialState) => {
|
|
139
|
+
const storedState =
|
|
140
|
+
JSON.parse(localStorage.getItem(storeName)) || initialState;
|
|
141
|
+
const store = createStore(storedState);
|
|
536
142
|
|
|
537
|
-
const auth = {
|
|
538
143
|
/**
|
|
539
|
-
*
|
|
144
|
+
* Get the value of a specific field from the store's state.
|
|
540
145
|
*
|
|
541
|
-
* @param {string}
|
|
542
|
-
* @returns {
|
|
146
|
+
* @param {string} fieldName - The name of the field.
|
|
147
|
+
* @returns {*} The value of the specified field.
|
|
543
148
|
*/
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
/**
|
|
585
|
-
* Revoke a rule from the rulesets.
|
|
586
|
-
*
|
|
587
|
-
* @param {string} action - The action of the rule to revoke.
|
|
588
|
-
*/
|
|
589
|
-
revokeRule: (action) => {
|
|
590
|
-
rules = rules.filter((rule) => rule.action !== action);
|
|
591
|
-
},
|
|
149
|
+
const getField = (fieldName) => {
|
|
150
|
+
return store.state[fieldName];
|
|
151
|
+
};
|
|
152
|
+
|
|
592
153
|
/**
|
|
593
|
-
*
|
|
154
|
+
* Set the value of a specific field in the store's state.
|
|
594
155
|
*
|
|
595
|
-
* @param {
|
|
596
|
-
* @
|
|
156
|
+
* @param {string} fieldName - The name of the field.
|
|
157
|
+
* @param {*} value - The new value to set for the field.
|
|
597
158
|
*/
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
159
|
+
const setField = (fieldName, value) => {
|
|
160
|
+
const newState = { ...store.state, [fieldName]: value };
|
|
161
|
+
store.setState(newState);
|
|
162
|
+
};
|
|
163
|
+
|
|
601
164
|
/**
|
|
602
|
-
*
|
|
165
|
+
* Subscribe a function to be notified of state changes.
|
|
603
166
|
*
|
|
604
|
-
* @param {
|
|
605
|
-
* @returns {
|
|
167
|
+
* @param {Function} subscriber - The function to call when the state changes.
|
|
168
|
+
* @returns {Function} A function to unsubscribe the subscriber.
|
|
606
169
|
*/
|
|
607
|
-
|
|
608
|
-
return
|
|
609
|
-
}
|
|
170
|
+
const subscribe = (subscriber) => {
|
|
171
|
+
return store.subscribe(subscriber);
|
|
172
|
+
};
|
|
173
|
+
|
|
610
174
|
/**
|
|
611
|
-
*
|
|
612
|
-
*
|
|
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.
|
|
175
|
+
* Clear the stored state from local storage.
|
|
616
176
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
};
|
|
177
|
+
const clear = () => {
|
|
178
|
+
localStorage.setItem(storeName, "");
|
|
179
|
+
};
|
|
658
180
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const subscribe = (subscriber) => {
|
|
666
|
-
return store.subscribe(subscriber);
|
|
181
|
+
return {
|
|
182
|
+
getField,
|
|
183
|
+
setField,
|
|
184
|
+
subscribe,
|
|
185
|
+
clear,
|
|
186
|
+
};
|
|
667
187
|
};
|
|
668
|
-
|
|
669
188
|
/**
|
|
670
|
-
*
|
|
189
|
+
* @function useAuth
|
|
190
|
+
* @param {*} rulesets
|
|
191
|
+
* @param {*} options
|
|
192
|
+
* @returns {Object} {canAccess, grantAccess, revokeAccess}
|
|
193
|
+
* @description Allows you to manage access to resources through rulesets
|
|
194
|
+
* @returns
|
|
671
195
|
*/
|
|
672
|
-
const clear = () => {
|
|
673
|
-
localStorage.setItem("store", "");
|
|
674
|
-
};
|
|
675
196
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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;
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Update the store with fetched data from the API.
|
|
706
|
-
*
|
|
707
|
-
* @param {...*} args - Arguments to pass to the API method.
|
|
708
|
-
*/
|
|
709
|
-
store.update = async (...args) => {
|
|
710
|
-
try {
|
|
711
|
-
const fetchedData = await apiMethod(...args);
|
|
712
|
-
|
|
713
|
-
if (JSON.stringify(fetchedData) === JSON.stringify(lastFetchedData)) {
|
|
714
|
-
consecutiveFetches++;
|
|
715
|
-
} else {
|
|
716
|
-
consecutiveFetches = 1;
|
|
717
|
-
lastFetchedData = fetchedData;
|
|
718
|
-
|
|
719
|
-
// Save the fetched data to localStorage
|
|
720
|
-
localStorage.setItem(options.storeKey, JSON.stringify(fetchedData));
|
|
721
|
-
}
|
|
197
|
+
function useAuth(options) {
|
|
198
|
+
if (!options.rulesets) {
|
|
199
|
+
throw new Error("No rulesets provided");
|
|
200
|
+
}
|
|
722
201
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
202
|
+
let rules = options.rulesets;
|
|
203
|
+
let user = options.user;
|
|
204
|
+
|
|
205
|
+
const auth = {
|
|
206
|
+
/**
|
|
207
|
+
* Check if the user can perform a specific action.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} action - The action to check.
|
|
210
|
+
* @returns {boolean} True if the user can perform the action, false otherwise.
|
|
211
|
+
*/
|
|
212
|
+
can: (action) => {
|
|
213
|
+
let can = false;
|
|
214
|
+
rules.forEach((rule) => {
|
|
215
|
+
if (rule.action === action) {
|
|
216
|
+
if (rule.condition(user)) {
|
|
217
|
+
can = true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
return can;
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Check if the user has a specific role.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} role - The role to check.
|
|
227
|
+
* @returns {boolean} True if the user has the role, false otherwise.
|
|
228
|
+
*/
|
|
229
|
+
hasRole: (role) => {
|
|
230
|
+
return user.role && user.role.includes(role);
|
|
231
|
+
},
|
|
232
|
+
/**
|
|
233
|
+
* Check if the user can perform a specific action with a specific role.
|
|
234
|
+
*
|
|
235
|
+
* @param {string} action - The action to check.
|
|
236
|
+
* @param {string} role - The role to check.
|
|
237
|
+
* @returns {boolean} True if the user can perform the action with the role, false otherwise.
|
|
238
|
+
*/
|
|
239
|
+
canWithRole: (action, role) => {
|
|
240
|
+
return auth.can(action) && auth.hasRole(role);
|
|
241
|
+
},
|
|
242
|
+
/**
|
|
243
|
+
* Assign a new rule to the rulesets.
|
|
244
|
+
*
|
|
245
|
+
* @param {Object} rule - The rule to assign.
|
|
246
|
+
*/
|
|
247
|
+
assignRule: (rule) => {
|
|
248
|
+
if (
|
|
249
|
+
!rules.some((existingRule) => existingRule.action === rule.action)
|
|
250
|
+
) {
|
|
251
|
+
rules.push(rule);
|
|
730
252
|
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* Revoke a rule from the rulesets.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} action - The action of the rule to revoke.
|
|
258
|
+
*/
|
|
259
|
+
revokeRule: (action) => {
|
|
260
|
+
rules = rules.filter((rule) => rule.action !== action);
|
|
261
|
+
},
|
|
262
|
+
/**
|
|
263
|
+
* Check if the user can perform any of the specified actions.
|
|
264
|
+
*
|
|
265
|
+
* @param {Array} actions - An array of actions to check.
|
|
266
|
+
* @returns {boolean} True if the user can perform any of the actions, false otherwise.
|
|
267
|
+
*/
|
|
268
|
+
canAnyOf: (actions) => {
|
|
269
|
+
return actions.some((action) => auth.can(action));
|
|
270
|
+
},
|
|
271
|
+
/**
|
|
272
|
+
* Check if the user can perform all of the specified actions.
|
|
273
|
+
*
|
|
274
|
+
* @param {Array} actions - An array of actions to check.
|
|
275
|
+
* @returns {boolean} True if the user can perform all of the actions, false otherwise.
|
|
276
|
+
*/
|
|
277
|
+
canAllOf: (actions) => {
|
|
278
|
+
return actions.every((action) => auth.can(action));
|
|
279
|
+
},
|
|
280
|
+
/**
|
|
281
|
+
* Check if the user can perform a group of actions based on a logical operator.
|
|
282
|
+
*
|
|
283
|
+
* @param {Array} actions - An array of actions to check.
|
|
284
|
+
* @param {string} logicalOperator - The logical operator to use ('any' or 'all').
|
|
285
|
+
* @returns {boolean} True if the user can perform the actions based on the logical operator, false otherwise.
|
|
286
|
+
*/
|
|
287
|
+
canGroup: (actions, logicalOperator = "any") => {
|
|
288
|
+
return logicalOperator === "any"
|
|
289
|
+
? auth.canAnyOf(actions)
|
|
290
|
+
: auth.canAllOf(actions);
|
|
291
|
+
},
|
|
734
292
|
};
|
|
735
293
|
|
|
736
|
-
|
|
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
|
-
}
|
|
294
|
+
return auth;
|
|
745
295
|
}
|
|
746
296
|
|
|
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
297
|
/**
|
|
772
|
-
*
|
|
773
|
-
* @
|
|
298
|
+
* @function runEffects
|
|
299
|
+
* @returns {null}
|
|
300
|
+
* @description Allows you to run side effects
|
|
774
301
|
*/
|
|
775
|
-
const
|
|
776
|
-
|
|
302
|
+
const runEffects = () => {
|
|
303
|
+
if (!executedEffects[name] && effects[name]) {
|
|
304
|
+
effects[name].forEach((effectFn) => effectFn());
|
|
305
|
+
executedEffects[name] = true;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
window.useState = useState;
|
|
309
|
+
window.setState = setState;
|
|
310
|
+
window.useEffect = useEffect;
|
|
311
|
+
window.useAuth = useAuth;
|
|
312
|
+
window.useSyncStore = useSyncStore;
|
|
313
|
+
const updateComponent = () => {
|
|
314
|
+
const componentContainer = document.querySelector(
|
|
315
|
+
`[data-component="${name}"]`
|
|
316
|
+
);
|
|
317
|
+
if (componentContainer) {
|
|
318
|
+
runEffects;
|
|
319
|
+
|
|
320
|
+
componentContainer.innerHTML = options.render(
|
|
321
|
+
states,
|
|
322
|
+
(storedProps = null)
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
777
326
|
/**
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
* @param {
|
|
327
|
+
* @function render
|
|
328
|
+
* @param {*} states
|
|
329
|
+
* @param {*} props
|
|
330
|
+
* @description Allows you to render component to DOM
|
|
331
|
+
* @returns {HTMLcContent}
|
|
332
|
+
* @returns
|
|
781
333
|
*/
|
|
782
|
-
const dispatch = (action) => {
|
|
783
|
-
const newState = reducer(state, action);
|
|
784
|
-
setState(newState);
|
|
785
|
-
};
|
|
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
334
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
335
|
+
const render = (props) => {
|
|
336
|
+
storedProps = props;
|
|
337
|
+
const componentContainer = document.querySelector(
|
|
338
|
+
`[data-component="${name}"]`
|
|
339
|
+
);
|
|
340
|
+
if (componentContainer) {
|
|
341
|
+
runEffects();
|
|
342
|
+
|
|
343
|
+
componentContainer.innerHTML = options.render(
|
|
344
|
+
states,
|
|
345
|
+
(storedProps = null)
|
|
346
|
+
);
|
|
347
|
+
} else {
|
|
348
|
+
return vhtml`
|
|
349
|
+
<div data-component="${name}">
|
|
350
|
+
${options.render(
|
|
351
|
+
states,
|
|
352
|
+
props
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
`;
|
|
812
356
|
}
|
|
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
|
-
*/
|
|
357
|
+
};
|
|
827
358
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
}
|
|
833
|
-
if (!path.endsWith(".html")) {
|
|
834
|
-
reject("Only html files are supported");
|
|
835
|
-
}
|
|
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;
|
|
359
|
+
return {
|
|
360
|
+
render,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
849
363
|
|
|
850
364
|
/**
|
|
851
|
-
* @
|
|
852
|
-
* @
|
|
853
|
-
* @
|
|
854
|
-
* @
|
|
855
|
-
*
|
|
365
|
+
* @function rf
|
|
366
|
+
* @param {*} name
|
|
367
|
+
* @param {*} fn
|
|
368
|
+
* @returns {null}
|
|
369
|
+
* @description Allows you to register function in global scope
|
|
856
370
|
*/
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
for (let key in styles) {
|
|
860
|
-
result += `${key}: ${styles[key]};`;
|
|
861
|
-
}
|
|
862
|
-
return `style="${result}"`;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
window.onbeforeunload = function () {
|
|
866
|
-
sessionStorage.clear();
|
|
371
|
+
export const rf = (name, fn) => {
|
|
372
|
+
window[name] = fn;
|
|
867
373
|
};
|
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
|
}
|