vaderjs 1.3.3-alpha-106 → 1.3.3-alpha-108

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 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
@@ -2,7 +2,7 @@
2
2
  "name": "vaderjs",
3
3
  "description": "A Reactive library aimed to helping you build reactive applications inspired by react.js",
4
4
  "module": "vader.js",
5
- "version": "1.3.3-alpha-106",
5
+ "version": "1.3.3-alpha-108",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
package/runtime/vader.js CHANGED
@@ -1 +1,655 @@
1
- window.Vader={version:"1.3.3"},window.componentRegistry={};let errors={"SyntaxError: Unexpected token '<'":"You forgot to enclose tags in a fragment <></>"},mounts=[],hasRan=[];export const strictMount=(e,t)=>{let n=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(n),t(),hasRan.push(e))}),120)};export class Component{constructor(){this.state={},this.key=null,this.components={},this.mounted=!1,this.checkIFMounted(),this.memoizes=[],this.functions=[],this.children=[],this.parentNode={},this.request={headers:{},method:"GET",params:{},path:"",query:{}},this.response={json:e=>{},send:e=>{},redirect:e=>{},render:async e=>{},log:e=>{},setQuery:e=>{}},this.router={use:e=>{}}}createComponent(e,t,n){function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let s=isClass(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let r=new Component(t);if(isClass(e))s.props=t,s.children=n,s.props.children=n.join(""),s.parentNode=this,s.request=this.request,s.response=this.response,s.key=t.key||null,r=s;else{e.toString();r.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let n={key:r.key,render:()=>e.apply(r,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:r.reset.bind(r),onMount:r.onMount.bind(r),useState:null,router:{use:r.router.use.bind(r)},bindMount:r.bindMount.bind(r),memoize:r.memoize.bind(r),createComponent:r.createComponent.bind(r),isChild:!1,useState:r.useState.bind(r),parseStyle:r.parseStyle.bind(r),bind:r.bind.bind(r),useRef:r.useRef.bind(r),request:this.request,response:this.response,useReducer:r.useReducer.bind(r),hydrate:r.hydrate.bind(r),onUnmount:r.onUnmount.bind(r),parentNoe:this};r.render=n.render,r=n}return this.components[t.key]||(this.components[t.key]=r),!this.children.includes(r)&&this.children.push(r),this.components[t.key]}reset(){Object.keys(this.components).forEach((e=>{this.components[e].onUnmount(),delete this.components[e]})),this.state={},this.children=[]}memoize(e){if(!0==!this.memoizes.includes(e.key))this.memoizes.push(e.key),this.components[e.key]=e;let t=this.components[e.key];t.bindMount(),t.parentNode=this,t.props=e.props,t.request=this.request,t.response=this.response,t.onMount=e.onMount.bind(e),t.onUnmount=e.onUnmount.bind(e);let n=t.render();return n&&n.split(">,").length>1&&(n=n.replaceAll(">,",">")),`<span key="${e.key}" >${n}</span>`}parseStyle(e){let t="";return Object.keys(e).forEach((n=>{let s=e[n];n=n.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${n}:${s};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=t[s];r&&o&&!r.isEqualNode(o)&&n.push({type:"replace",old:r,new:o.cloneNode(!0)})}return n}updateChangedElements(e){e.forEach((e=>{switch(e.type){case"replace":e.old.parentNode.replaceChild(e.new,e.old);break;case"remove":e.old.remove();break;case"add":e.old.appendChild(e.new.cloneNode(!0))}}))}hydrate(e){if(e){(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`),document.querySelector(`[ref="${e}"]`)}else{let e=this.key?document.querySelector(`[key="${this.key}"]`):null,t=(new DOMParser).parseFromString(this.render(),"text/html").body;if(t=document.createElement("div").appendChild(t),!e&&(e=document.querySelector(`[key="${t.attributes?.key?.value||null}"]`)),!e)return void 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');t.querySelectorAll("*").forEach((e=>{e.hasAttribute("key")&&e.innerHTML!==document.querySelector(`[key="${e.attributes.key.value}"]`).innerHTML&&document.querySelector(`[key="${e.attributes.key.value}"]`).replaceWith(e)}))}}patch(e,t){const n=this.domDifference(e,t);this.updateChangedElements(n)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,n,s,...r){n=n+this.key||2022;let o={},i=(s=s.replace(/,,/g,",")).replaceAll(",,",",");for(var u in r){let e=r[u];o[i.split(",")[u]]=e}s=s.replace(",,",",");let a=null;e=e.split("\n").join(";");try{a=new Function(`event, ${s}`,` \n return (async (event, ${s}) => { \n ${e.toString()}\n })(event, ${Object.keys(o).join(",")}) \n `)}catch(e){let{message:t}=e;console.error(`Error in function ${n} ${t}`)}return a=a.bind(this),this.functions.find((e=>e.ref===n))||document.addEventListener(`$dispatch_#id=${n}`,(e=>{let{name:t,event:s}=e.detail;if(t===n){let e=this.functions.find((e=>e.ref===n)).params;Object.keys(e).forEach((t=>{e[t]instanceof CustomEvent&&delete e[t],void 0===e[t]?delete e[t]:e[t]})),a(s,...Object.values(e))}})),window.callFunction=(e,t)=>{document.dispatchEvent(new CustomEvent(`$dispatch_#id=${e}`,{detail:{name:e,params:null,event:t}}))},!this.functions.find((e=>e.ref===n))&&this.functions.push({ref:n,params:o}),t?e:`((event)=>{event.target.ev = event; callFunction('${n}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);let updatedValue=()=>this.state[e],n=updatedValue();return[n,(t,s)=>{this.state[e]=t,this.hydrate(s),n=updatedValue()}]}useRef(e=null,t){this.state[e]||(this.state[e]=t);return{bind:e+this.key,current:(()=>document.querySelector(`[ref="${e+this.key}"]`)||t)()}}useReducer(e=null,t,n=null){this.state[e]||(this.state[e]=t);const getValue=()=>this.state[e];let s=getValue();return[getValue(),(t,r)=>{const o=n(s,t)??t;this.state[e]=o,this.hydrate(r),s=getValue()}]}render(){}checkIFMounted(){new MutationObserver((e=>{e.forEach((e=>{e.target.querySelector(`[key="${this.key}"]`)&&!this.mounted&&(this.onMount(),this.mounted=!0),Array.from(e.removedNodes).find((e=>e.attributes&&e.attributes.key&&e.attributes.key.value===this.key))&&(this.onUnmount(),this.reset())}))})).observe(document.body,{childList:!0,subtree:!0})}onMount(){}onUnmount(){}}export const useState=(e,t)=>{states[e]||(states[e]=t);return[states[e],(t,n)=>{states[e]=t,this.hydrate(n)}]};export const useReducer=(e,t)=>[e,e=>{}];export const useRef=e=>({current:e,bind:""});export class Link extends Component{constructor(e){super(e),this.props=e,this.link=document.createElement("a")}render(){return this.link.innerHTML=this.props.children,this.link.setAttribute("id",this.props?.href),this.link.style=this.props?.style,this.link.setAttribute("class",this.props?.class),this.link.setAttribute("onclick",`window.history.pushState({}, '', '${this.props?.href}'); window.dispatchEvent(new Event('popstate'));`),this.link.outerHTML}}export default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount};
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
+ }