vaderjs 1.3.3-alpha-107 → 1.3.3-alpha-109
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/client/index.js +18 -0
- package/package.json +1 -1
- package/runtime/vader.js +656 -1
package/client/index.js
CHANGED
|
@@ -417,10 +417,28 @@ export const useRef = (initialState) => {
|
|
|
417
417
|
};
|
|
418
418
|
};
|
|
419
419
|
|
|
420
|
+
/**
|
|
421
|
+
* @method Link
|
|
422
|
+
* @param {string} href
|
|
423
|
+
* @param {string} children
|
|
424
|
+
* @returns {string} - The rendered content.
|
|
425
|
+
* @description This method allows you to create passive links without reloading the page
|
|
426
|
+
*/
|
|
427
|
+
class Link extends Component{
|
|
428
|
+
constructor(props){
|
|
429
|
+
super(props)
|
|
430
|
+
this.props = props
|
|
431
|
+
}
|
|
432
|
+
render(){
|
|
433
|
+
return
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
420
437
|
export default {
|
|
421
438
|
Component,
|
|
422
439
|
useRef,
|
|
423
440
|
useReducer,
|
|
424
441
|
useState,
|
|
425
442
|
strictMount,
|
|
443
|
+
Link
|
|
426
444
|
}
|
package/package.json
CHANGED
package/runtime/vader.js
CHANGED
|
@@ -1 +1,656 @@
|
|
|
1
|
-
window.Vader
|
|
1
|
+
window.Vader = {
|
|
2
|
+
version: "1.3.3",
|
|
3
|
+
};
|
|
4
|
+
window.componentRegistry = {};
|
|
5
|
+
let errors = {
|
|
6
|
+
"SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let mounts = [];
|
|
10
|
+
let hasRan = []
|
|
11
|
+
/**
|
|
12
|
+
* @method strictMount
|
|
13
|
+
* @description This method allows you to await until the component is mounted before running a callback
|
|
14
|
+
* @param {*} key
|
|
15
|
+
* @param {*} callback
|
|
16
|
+
*/
|
|
17
|
+
export const strictMount = (key, callback) => {
|
|
18
|
+
let timer = setInterval(() => {
|
|
19
|
+
if (document.querySelector(`[key="${key}"]`) && !hasRan.includes(key)) {
|
|
20
|
+
clearInterval(timer);
|
|
21
|
+
callback();
|
|
22
|
+
hasRan.push(key)
|
|
23
|
+
}
|
|
24
|
+
}, 120);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Represents a component in the Vader framework.
|
|
31
|
+
*/
|
|
32
|
+
export class Component {
|
|
33
|
+
/**
|
|
34
|
+
* Creates an instance of Component.
|
|
35
|
+
*/
|
|
36
|
+
constructor() {
|
|
37
|
+
this.state = {};
|
|
38
|
+
this.key = null;
|
|
39
|
+
this.components = {};
|
|
40
|
+
this.mounted = false;
|
|
41
|
+
this.checkIFMounted();
|
|
42
|
+
this.memoizes = []
|
|
43
|
+
this.functions = []
|
|
44
|
+
this.children = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Parent of the current component.
|
|
49
|
+
* @type {Component}
|
|
50
|
+
*/
|
|
51
|
+
this.parentNode = {}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Request object.
|
|
55
|
+
*/
|
|
56
|
+
this.request = {
|
|
57
|
+
/**
|
|
58
|
+
* @type {string}
|
|
59
|
+
* @description The headers for the current route
|
|
60
|
+
*/
|
|
61
|
+
headers:{},
|
|
62
|
+
/**
|
|
63
|
+
* @type {string}
|
|
64
|
+
* @description The method for the current route
|
|
65
|
+
*/
|
|
66
|
+
method: "GET",
|
|
67
|
+
/**
|
|
68
|
+
* @type {string}
|
|
69
|
+
* @description params for the given route /:id/:name etc
|
|
70
|
+
*/
|
|
71
|
+
params: {},
|
|
72
|
+
/**
|
|
73
|
+
* @type {string}
|
|
74
|
+
* @description path: current route path
|
|
75
|
+
*/
|
|
76
|
+
path: "",
|
|
77
|
+
/**
|
|
78
|
+
* @type {string}
|
|
79
|
+
* @description query: query object for the current route ?name=hello -> {name: 'hello'}
|
|
80
|
+
*/
|
|
81
|
+
query: {},
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* @type {string}
|
|
85
|
+
* @description The response object for the current route
|
|
86
|
+
*/
|
|
87
|
+
this.response = {
|
|
88
|
+
/**
|
|
89
|
+
* @method json
|
|
90
|
+
* @description This method allows you to send json data to the client
|
|
91
|
+
* @param {*} data
|
|
92
|
+
*/
|
|
93
|
+
json: (data) => {},
|
|
94
|
+
/**
|
|
95
|
+
* @method send
|
|
96
|
+
* @description This method allows you to send text data to the client
|
|
97
|
+
* @param {*} data
|
|
98
|
+
*/
|
|
99
|
+
send: (data) => {},
|
|
100
|
+
/**
|
|
101
|
+
* @method redirect
|
|
102
|
+
* @description This method allows you to redirect the client to a new route
|
|
103
|
+
* @param {*} path
|
|
104
|
+
*/
|
|
105
|
+
redirect: (path) => {},
|
|
106
|
+
/**
|
|
107
|
+
* @method render
|
|
108
|
+
* @description render a new component to the client
|
|
109
|
+
* @param {*} Component
|
|
110
|
+
*/
|
|
111
|
+
render: async (Component) => {},
|
|
112
|
+
/**
|
|
113
|
+
* @method log
|
|
114
|
+
* @description This method is used to log the request and response
|
|
115
|
+
* @param {String} type
|
|
116
|
+
*/
|
|
117
|
+
log: (type) => {},
|
|
118
|
+
/**
|
|
119
|
+
* @method setQuery
|
|
120
|
+
* @description This method is used to set the query object for the current route
|
|
121
|
+
*/
|
|
122
|
+
setQuery: (query) => {},
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @method router
|
|
127
|
+
* @description use router methods directly from the parent component
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
this.router = {
|
|
131
|
+
/**
|
|
132
|
+
* @method use
|
|
133
|
+
* @description add a middleware to the current route
|
|
134
|
+
* @param {Function} middleware
|
|
135
|
+
* @returns {void}
|
|
136
|
+
*/
|
|
137
|
+
use: (/**@type {Function} */ middleware) => {},
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
createComponent(/**@type {Component}**/component, props, children) {
|
|
142
|
+
|
|
143
|
+
function isClass(funcOrClass) {
|
|
144
|
+
return typeof funcOrClass === 'function' &&
|
|
145
|
+
/^class\s/.test(Function.prototype.toString.call(funcOrClass));
|
|
146
|
+
}
|
|
147
|
+
let comp = !isClass(component) ? null : new component(props);
|
|
148
|
+
if (!component) {
|
|
149
|
+
throw new Error("Component must be defined");
|
|
150
|
+
}
|
|
151
|
+
let c = new Component(props);
|
|
152
|
+
if(!isClass(component)){
|
|
153
|
+
let render = component.toString();
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
c.key = component.toString().split('key="')[1] ? component.toString().split('key="')[1].split('"')[0] : null;
|
|
159
|
+
|
|
160
|
+
let comp = {
|
|
161
|
+
key: c.key,
|
|
162
|
+
render: () => {
|
|
163
|
+
return component.apply(c, [props])
|
|
164
|
+
},
|
|
165
|
+
request: this.request,
|
|
166
|
+
isChild: true,
|
|
167
|
+
response: this.response,
|
|
168
|
+
params: this.request.params,
|
|
169
|
+
queryParams: this.request.query,
|
|
170
|
+
reset: c.reset.bind(c),
|
|
171
|
+
onMount: c.onMount.bind(c),
|
|
172
|
+
useState: null,
|
|
173
|
+
router: {
|
|
174
|
+
use: c.router.use.bind(c),
|
|
175
|
+
},
|
|
176
|
+
bindMount: c.bindMount.bind(c),
|
|
177
|
+
memoize: c.memoize.bind(c),
|
|
178
|
+
createComponent: c.createComponent.bind(c),
|
|
179
|
+
isChild: false,
|
|
180
|
+
useState: c.useState.bind(c),
|
|
181
|
+
parseStyle: c.parseStyle.bind(c),
|
|
182
|
+
bind: c.bind.bind(c),
|
|
183
|
+
useRef: c.useRef.bind(c),
|
|
184
|
+
request: this.request,
|
|
185
|
+
response: this.response,
|
|
186
|
+
useReducer: c.useReducer.bind(c),
|
|
187
|
+
hydrate: c.hydrate.bind(c),
|
|
188
|
+
onUnmount: c.onUnmount.bind(c),
|
|
189
|
+
parentNoe: this,
|
|
190
|
+
}
|
|
191
|
+
c.render = comp.render;
|
|
192
|
+
c = comp;
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
} else{
|
|
196
|
+
comp['props'] = props;
|
|
197
|
+
comp.children = children;
|
|
198
|
+
comp.props.children = children.join('')
|
|
199
|
+
comp.parentNode = this;
|
|
200
|
+
comp.request = this.request;
|
|
201
|
+
comp.response = this.response;
|
|
202
|
+
comp.key = props.key || null;
|
|
203
|
+
c = comp;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if(!this.components[props.key]){
|
|
210
|
+
this.components[props.key] = c
|
|
211
|
+
}
|
|
212
|
+
!this.children.includes(c) ? this.children.push(c) : null
|
|
213
|
+
return this.components[props.key]
|
|
214
|
+
}
|
|
215
|
+
reset(){
|
|
216
|
+
Object.keys(this.components).forEach((key) => {
|
|
217
|
+
this.components[key].onUnmount()
|
|
218
|
+
delete this.components[key]
|
|
219
|
+
})
|
|
220
|
+
this.state = {}
|
|
221
|
+
this.children = []
|
|
222
|
+
}
|
|
223
|
+
memoize(/**@type {Component}**/component){
|
|
224
|
+
|
|
225
|
+
switch(true){
|
|
226
|
+
case !this.memoizes.includes(component.key):
|
|
227
|
+
this.memoizes.push(component.key)
|
|
228
|
+
this.components[component.key] = component;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let comp = this.components[component.key];
|
|
233
|
+
comp.bindMount();
|
|
234
|
+
comp.parentNode = this;
|
|
235
|
+
comp.props = component.props;
|
|
236
|
+
comp.request = this.request;
|
|
237
|
+
comp.response = this.response;
|
|
238
|
+
comp.onMount = component.onMount.bind(component);
|
|
239
|
+
comp.onUnmount = component.onUnmount.bind(component);
|
|
240
|
+
let h = comp.render()
|
|
241
|
+
|
|
242
|
+
if(h && h.split('>,').length > 1){
|
|
243
|
+
h = h.replaceAll('>,', '>')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return `<span key="${component.key}" >${h}</span>`
|
|
247
|
+
}
|
|
248
|
+
parseStyle(styles){
|
|
249
|
+
let css = ''
|
|
250
|
+
Object.keys(styles).forEach((key) => {
|
|
251
|
+
let value = styles[key]
|
|
252
|
+
key = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
|
|
253
|
+
css += `${key}:${value};`
|
|
254
|
+
})
|
|
255
|
+
return css
|
|
256
|
+
}
|
|
257
|
+
bindMount(){
|
|
258
|
+
mounts.push(this)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Hydrates the component by updating the HTML content if it has changed.
|
|
263
|
+
* @private
|
|
264
|
+
*/
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Hydrates the component by updating the HTML content if it has changed.
|
|
268
|
+
* @private
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
domDifference(oldDom, newDom) {
|
|
272
|
+
let diff = [];
|
|
273
|
+
|
|
274
|
+
for (let i = 0; i < oldDom.length; i++) {
|
|
275
|
+
let oldEl = oldDom[i];
|
|
276
|
+
let newEl = newDom[i];
|
|
277
|
+
|
|
278
|
+
if (oldEl && newEl && !oldEl.isEqualNode(newEl)) {
|
|
279
|
+
diff.push({
|
|
280
|
+
type: 'replace',
|
|
281
|
+
old: oldEl,
|
|
282
|
+
new: newEl.cloneNode(true),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return diff;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
updateChangedElements(diff) {
|
|
291
|
+
diff.forEach((change) => {
|
|
292
|
+
switch (change.type) {
|
|
293
|
+
case 'replace':
|
|
294
|
+
change.old.parentNode.replaceChild(change.new, change.old);
|
|
295
|
+
break;
|
|
296
|
+
case 'remove':
|
|
297
|
+
change.old.remove();
|
|
298
|
+
break;
|
|
299
|
+
case 'add':
|
|
300
|
+
change.old.appendChild(change.new.cloneNode(true));
|
|
301
|
+
break;
|
|
302
|
+
default:
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
hydrate(hook) {
|
|
308
|
+
|
|
309
|
+
if (hook) {
|
|
310
|
+
let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body.querySelector(`[ref="${hook}"]`);
|
|
311
|
+
let oldDom = document.querySelector(`[ref="${hook}"]`);
|
|
312
|
+
|
|
313
|
+
} else {
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
let targetElement = this.key ? document.querySelector(`[key="${this.key}"]`) : null;
|
|
317
|
+
|
|
318
|
+
let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body
|
|
319
|
+
newDom = document.createElement('div').appendChild(newDom)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
!targetElement ? targetElement = document.querySelector(`[key="${newDom.attributes?.key?.value || null}"]`) : null
|
|
323
|
+
if(!targetElement){
|
|
324
|
+
console.error('Hydration failed, component not found got ensure you have set key="a value" on the component or this.key inside of function or render method body')
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
newDom.querySelectorAll('*').forEach((el) => {
|
|
329
|
+
|
|
330
|
+
if(el.hasAttribute('key') && el.innerHTML !== document.querySelector(`[key="${el.attributes.key.value}"]`).innerHTML){
|
|
331
|
+
|
|
332
|
+
document.querySelector(`[key="${el.attributes.key.value}"]`).replaceWith(el)
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
patch(oldElements, newElements) {
|
|
345
|
+
const diff = this.domDifference(oldElements, newElements);
|
|
346
|
+
|
|
347
|
+
this.updateChangedElements(diff);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Handles an object by parsing it as JSON and evaluating it.
|
|
356
|
+
* @param {string} obj - The object to handle.
|
|
357
|
+
* @returns {*} - The evaluated object.
|
|
358
|
+
* @prvate
|
|
359
|
+
*/
|
|
360
|
+
handleObject(obj) {
|
|
361
|
+
try {
|
|
362
|
+
obj = JSON.parse(obj);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
// Handle JSON parsing error if needed
|
|
365
|
+
}
|
|
366
|
+
return eval(obj);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Binds a function to the component.
|
|
373
|
+
* @param {string} funcData - The function data.
|
|
374
|
+
* @param {string} p - The parameter.
|
|
375
|
+
* @param {string} ref - The reference.
|
|
376
|
+
* @returns {string} - A valid inline JS function call.
|
|
377
|
+
*/
|
|
378
|
+
bind(funcTion, jsx,ref, paramNames, ...params) {
|
|
379
|
+
ref = ref + this.key || 2022
|
|
380
|
+
|
|
381
|
+
let paramObject = {};
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
paramNames = paramNames.replace(/,,/g, ',');
|
|
385
|
+
let newparamnames = paramNames.replaceAll(',,', ',')
|
|
386
|
+
|
|
387
|
+
for(var i in params){
|
|
388
|
+
let param = params[i]
|
|
389
|
+
let paramname = newparamnames.split(',')[i]
|
|
390
|
+
paramObject[paramname] = param
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
paramNames = paramNames.replace(',,', ',');
|
|
396
|
+
let func = null
|
|
397
|
+
// at the end of spaces or new lines add a ;
|
|
398
|
+
funcTion = funcTion.split('\n').join(';')
|
|
399
|
+
try{
|
|
400
|
+
func = new Function(`event, ${paramNames}`, `
|
|
401
|
+
return (async (event, ${paramNames}) => {
|
|
402
|
+
${funcTion.toString()}
|
|
403
|
+
})(event, ${Object.keys(paramObject).join(',')})
|
|
404
|
+
`);
|
|
405
|
+
}catch(e){
|
|
406
|
+
let { message } = e;
|
|
407
|
+
console.error(`Error in function ${ref} ${message}`)
|
|
408
|
+
}
|
|
409
|
+
func = func.bind(this)
|
|
410
|
+
|
|
411
|
+
if(!this.functions.find((f) => f.ref === ref)){
|
|
412
|
+
document.addEventListener(`$dispatch_#id=${ref}`, (e) => {
|
|
413
|
+
let { name, event } = e.detail;
|
|
414
|
+
if (name === ref) {
|
|
415
|
+
let params = this.functions.find((f) => f.ref === ref).params
|
|
416
|
+
Object.keys(params).forEach((key) => {
|
|
417
|
+
if(params[key] instanceof CustomEvent){
|
|
418
|
+
delete params[key]
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
params[key] === undefined ? delete params[key] : params[key]
|
|
422
|
+
})
|
|
423
|
+
func(event, ...Object.values(params))
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
window.callFunction = (name, event) => {
|
|
430
|
+
document.dispatchEvent(new CustomEvent(`$dispatch_#id=${name}`, { detail: { name: name, params: null, event: event } }));
|
|
431
|
+
}
|
|
432
|
+
!this.functions.find((f) => f.ref === ref) ? this.functions.push({ref: ref, params: paramObject}) : null
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
return jsx ? funcTion : `((event)=>{event.target.ev = event; callFunction('${ref}', event.target.ev)})(event)`;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* useState hook.
|
|
442
|
+
*
|
|
443
|
+
* @template T
|
|
444
|
+
* @param {string} key - The key for the state property.
|
|
445
|
+
* @param {T} initialState - The initial state value.
|
|
446
|
+
* @returns {[() => T, (newValue: T, hook: Function) => void]} - A tuple with getter and setter functions.
|
|
447
|
+
*/
|
|
448
|
+
useState(key, initialState) {
|
|
449
|
+
if (!this.state[key]) {
|
|
450
|
+
this.state[key] = initialState;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Get the current state value.
|
|
455
|
+
*
|
|
456
|
+
* @returns {T} The current state value.
|
|
457
|
+
*/
|
|
458
|
+
let updatedValue = () => this.state[key];
|
|
459
|
+
|
|
460
|
+
let getValue = updatedValue();
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Set a new value for the state.
|
|
464
|
+
*
|
|
465
|
+
* @param {T} newValue - The new value to set.
|
|
466
|
+
* @param {Function} hook - The hook to hydrate after setting the value.
|
|
467
|
+
*/
|
|
468
|
+
const set = (newValue, hook) => {
|
|
469
|
+
this.state[key] = newValue;
|
|
470
|
+
|
|
471
|
+
this.hydrate(hook);
|
|
472
|
+
getValue = updatedValue();
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
return [getValue, set];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
useRef(key = null, initialState) {
|
|
483
|
+
if (!this.state[key]) {
|
|
484
|
+
this.state[key] = initialState;
|
|
485
|
+
}
|
|
486
|
+
const getValue = () => document.querySelector(`[ref="${key + this.key}"]`) || initialState;
|
|
487
|
+
const set = (newValue) => {
|
|
488
|
+
this.state[key] = newValue;
|
|
489
|
+
|
|
490
|
+
this.hydrate();
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
bind: key + this.key,
|
|
496
|
+
current: getValue(),
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
useReducer(key = null, initialState, func = null) {
|
|
501
|
+
|
|
502
|
+
if (!this.state[key]) {
|
|
503
|
+
this.state[key] = initialState;
|
|
504
|
+
}
|
|
505
|
+
const getValue = () => this.state[key];
|
|
506
|
+
let value = getValue();
|
|
507
|
+
const set = (newValue, hook) => {
|
|
508
|
+
const nextState = func(value, newValue) ?? newValue;
|
|
509
|
+
this.state[key] = nextState;
|
|
510
|
+
this.hydrate(hook);
|
|
511
|
+
value = getValue();
|
|
512
|
+
};
|
|
513
|
+
return [getValue(), set];
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Placeholder for content to be rendered.
|
|
519
|
+
* @method render
|
|
520
|
+
*/
|
|
521
|
+
render() {}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Checks if the component is mounted and triggers the onMount method.
|
|
525
|
+
* @private
|
|
526
|
+
*/
|
|
527
|
+
checkIFMounted() {
|
|
528
|
+
let observer = new MutationObserver((mutations) => {
|
|
529
|
+
mutations.forEach((mutation) => {
|
|
530
|
+
|
|
531
|
+
if (mutation.target.querySelector(`[key="${this.key}"]`) && !this.mounted) {
|
|
532
|
+
this.onMount();
|
|
533
|
+
this.mounted = true;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if(Array.from(mutation.removedNodes).find((node) => node.attributes && node.attributes.key && node.attributes.key.value === this.key)){
|
|
537
|
+
this.onUnmount();
|
|
538
|
+
this.reset();
|
|
539
|
+
}
|
|
540
|
+
})
|
|
541
|
+
})
|
|
542
|
+
observer.observe(document.body, {
|
|
543
|
+
childList: true,
|
|
544
|
+
subtree: true,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Method that is called when the component is mounted.
|
|
550
|
+
* @method onMount
|
|
551
|
+
*/
|
|
552
|
+
onMount() {}
|
|
553
|
+
/**
|
|
554
|
+
* Method that is called when the component is unmounted.
|
|
555
|
+
* @method onUnmount
|
|
556
|
+
*/
|
|
557
|
+
onUnmount() {}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* useState hook.
|
|
564
|
+
*
|
|
565
|
+
* @param {string} key - The key for the state property.
|
|
566
|
+
* @param {*} initialState - The initial state value.
|
|
567
|
+
* @returns {[*]} - A tuple with the current state value and a setter function.
|
|
568
|
+
*/
|
|
569
|
+
export const useState = (key, initialState) => {
|
|
570
|
+
if (!states[key]) {
|
|
571
|
+
states[key] = initialState;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Get the current state value.
|
|
576
|
+
*
|
|
577
|
+
* @returns {*} The current state value.
|
|
578
|
+
*/
|
|
579
|
+
let updatedValue = () => states[key];
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Set a new value for the state.
|
|
583
|
+
*
|
|
584
|
+
* @param {*} newValue - The new value to set.
|
|
585
|
+
* @param {Function} hook - The hook to hydrate after setting the value.
|
|
586
|
+
*/
|
|
587
|
+
const set = (newValue, hook) => {
|
|
588
|
+
states[key] = newValue;
|
|
589
|
+
this.hydrate(hook);
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
return [states[key], set];
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* @method useReducer
|
|
599
|
+
* @param {*} initialState
|
|
600
|
+
* @param {*} reducer
|
|
601
|
+
* @returns {Array} [value, set]
|
|
602
|
+
*/
|
|
603
|
+
export const useReducer = (/**@type {*}**/initialState, /**@type {function}**/reducer) => {
|
|
604
|
+
return [initialState, (newValue) => {}];
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* useRef hook.
|
|
610
|
+
*
|
|
611
|
+
* @param {*} initialState - The initial state value for the ref.
|
|
612
|
+
* @returns {{ current: *, bind: string }} - An object containing the current value and a bind string.
|
|
613
|
+
*/
|
|
614
|
+
export const useRef = (initialState) => {
|
|
615
|
+
return {
|
|
616
|
+
/**
|
|
617
|
+
* @description The current value of the ref.
|
|
618
|
+
@type {*}
|
|
619
|
+
*/
|
|
620
|
+
current: initialState,
|
|
621
|
+
/**
|
|
622
|
+
* @description A unique string that can be used to bind the ref to an element.
|
|
623
|
+
* @type {HTMLElement|string}
|
|
624
|
+
*/
|
|
625
|
+
bind: '',
|
|
626
|
+
};
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
export class Link extends Component{
|
|
630
|
+
constructor(props){
|
|
631
|
+
super(props)
|
|
632
|
+
this.props = props
|
|
633
|
+
this.link = document.createElement('a')
|
|
634
|
+
}
|
|
635
|
+
render(){
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
this.link.innerHTML = this.props.children
|
|
639
|
+
this.link.setAttribute('id', this.props?.href)
|
|
640
|
+
this.link.style = this.props?.style
|
|
641
|
+
|
|
642
|
+
this.link.setAttribute('class', this.props?.class)
|
|
643
|
+
this.link.setAttribute('onclick', `window.history.pushState({}, '', '${this.props?.href}'); window.dispatchEvent(new Event('popstate'));`)
|
|
644
|
+
return this.link.outerHTML
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export default {
|
|
650
|
+
Component,
|
|
651
|
+
useRef,
|
|
652
|
+
useReducer,
|
|
653
|
+
useState,
|
|
654
|
+
strictMount,
|
|
655
|
+
Link
|
|
656
|
+
}
|