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 +7 -0
- package/package.json +1 -1
- package/readme.md +47 -17
- package/vader.js +287 -808
- 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,346 @@
|
|
|
1
|
-
const templates = [];
|
|
2
|
-
const cache = {};
|
|
3
1
|
/**
|
|
4
|
-
* @
|
|
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}
|
|
13
|
-
* @returns
|
|
14
|
-
*
|
|
4
|
+
* @param {...any} args
|
|
5
|
+
* @returns modified string
|
|
6
|
+
*
|
|
15
7
|
*/
|
|
16
|
-
|
|
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 <
|
|
21
|
-
result +=
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
* @
|
|
388
|
-
* @
|
|
389
|
-
* @
|
|
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
|
-
|
|
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
|
-
*
|
|
440
|
-
*
|
|
441
|
-
* @param {*}
|
|
442
|
-
* @returns
|
|
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
|
|
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
|
-
};
|
|
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
|
-
*
|
|
484
|
-
*
|
|
485
|
-
* @
|
|
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
|
-
|
|
492
|
-
if (
|
|
493
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
113
|
+
if (dependencies.length > 1) {
|
|
114
|
+
runEffects();
|
|
115
|
+
}
|
|
116
|
+
effects[name].push(effectFn);
|
|
117
|
+
runEffects();
|
|
503
118
|
};
|
|
504
119
|
|
|
505
120
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
* @param {
|
|
509
|
-
* @returns {
|
|
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
|
|
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
|
-
}
|
|
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
|
-
*
|
|
133
|
+
* Get the value of a specific field from the store's state.
|
|
586
134
|
*
|
|
587
|
-
* @param {string}
|
|
135
|
+
* @param {string} fieldName - The name of the field.
|
|
136
|
+
* @returns {*} The value of the specified field.
|
|
588
137
|
*/
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
*
|
|
143
|
+
* Set the value of a specific field in the store's state.
|
|
603
144
|
*
|
|
604
|
-
* @param {
|
|
605
|
-
* @
|
|
145
|
+
* @param {string} fieldName - The name of the field.
|
|
146
|
+
* @param {*} value - The new value to set for the field.
|
|
606
147
|
*/
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
148
|
+
const setField = (fieldName, value) => {
|
|
149
|
+
const newState = { ...store.state, [fieldName]: value };
|
|
150
|
+
store.setState(newState);
|
|
151
|
+
};
|
|
152
|
+
|
|
610
153
|
/**
|
|
611
|
-
*
|
|
154
|
+
* Subscribe a function to be notified of state changes.
|
|
612
155
|
*
|
|
613
|
-
* @param {
|
|
614
|
-
* @
|
|
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
|
-
|
|
618
|
-
return
|
|
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
|
-
};
|
|
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
|
-
*
|
|
706
|
-
*
|
|
707
|
-
* @param {...*} args - Arguments to pass to the API method.
|
|
164
|
+
* Clear the stored state from local storage.
|
|
708
165
|
*/
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
166
|
+
const clear = () => {
|
|
167
|
+
localStorage.setItem(storeName, "");
|
|
168
|
+
};
|
|
712
169
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
170
|
+
return {
|
|
171
|
+
getField,
|
|
172
|
+
setField,
|
|
173
|
+
subscribe,
|
|
174
|
+
clear,
|
|
175
|
+
};
|
|
176
|
+
};
|
|
718
177
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
178
|
+
function useAuth(options) {
|
|
179
|
+
if (!options.rulesets) {
|
|
180
|
+
throw new Error("No rulesets provided");
|
|
181
|
+
}
|
|
722
182
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
}
|
|
732
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
779
|
-
*
|
|
780
|
-
* @
|
|
279
|
+
* @function runEffects
|
|
280
|
+
* @returns {null}
|
|
281
|
+
* @description Allows you to run side effects
|
|
781
282
|
*/
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
283
|
+
const runEffects = () => {
|
|
284
|
+
if (!executedEffects[name] && effects[name]) {
|
|
285
|
+
effects[name].forEach((effectFn) => effectFn());
|
|
286
|
+
executedEffects[name] = true;
|
|
287
|
+
}
|
|
785
288
|
};
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
if (
|
|
834
|
-
|
|
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
|
-
|
|
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
|
-
|
|
852
|
-
|
|
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
|
-
|
|
866
|
-
|
|
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
|
}
|