vaderjs 1.3.2 → 1.3.4
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/create-vader-app/example_proj/dist/vaderjs/index.js +5 -0
- package/create-vader-app/example_proj/dist/vaderjs/vader-min.js +1115 -0
- package/create-vader-app/example_proj/dist/vaderjs/vaderRouter-min.js +1 -0
- package/create-vader-app/example_proj/dist/vaderjs/worker.js +327 -0
- package/create-vader-app/example_proj/index.html +20 -0
- package/create-vader-app/example_proj/readme.md +2 -0
- package/create-vader-app/example_proj/src/pages/Index.js +13 -0
- package/create-vader-app/example_proj/src/views/app.html +16 -0
- package/create-vader-app/index.js +20 -0
- package/create-vader-app/node_modules/.package-lock.json +350 -0
- package/create-vader-app/node_modules/chalk/license +9 -0
- package/create-vader-app/node_modules/chalk/package.json +83 -0
- package/create-vader-app/node_modules/chalk/readme.md +325 -0
- package/create-vader-app/node_modules/chalk/source/index.d.ts +320 -0
- package/create-vader-app/node_modules/chalk/source/index.js +225 -0
- package/create-vader-app/node_modules/chalk/source/utilities.js +33 -0
- package/create-vader-app/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/create-vader-app/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/create-vader-app/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/create-vader-app/node_modules/chalk/source/vendor/supports-color/browser.js +30 -0
- package/create-vader-app/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/create-vader-app/node_modules/chalk/source/vendor/supports-color/index.js +182 -0
- package/create-vader-app/node_modules/commander/LICENSE +22 -0
- package/create-vader-app/node_modules/commander/Readme.md +1146 -0
- package/create-vader-app/node_modules/commander/esm.mjs +16 -0
- package/create-vader-app/node_modules/commander/index.js +27 -0
- package/create-vader-app/node_modules/commander/lib/argument.js +147 -0
- package/create-vader-app/node_modules/commander/lib/command.js +2196 -0
- package/create-vader-app/node_modules/commander/lib/error.js +45 -0
- package/create-vader-app/node_modules/commander/lib/help.js +464 -0
- package/create-vader-app/node_modules/commander/lib/option.js +331 -0
- package/create-vader-app/node_modules/commander/lib/suggestSimilar.js +100 -0
- package/create-vader-app/node_modules/commander/package-support.json +16 -0
- package/create-vader-app/node_modules/commander/package.json +90 -0
- package/create-vader-app/node_modules/commander/typings/esm.d.mts +3 -0
- package/create-vader-app/node_modules/commander/typings/index.d.ts +889 -0
- package/create-vader-app/node_modules/fs-extra/LICENSE +15 -0
- package/create-vader-app/node_modules/fs-extra/README.md +292 -0
- package/create-vader-app/node_modules/fs-extra/lib/copy/copy-sync.js +161 -0
- package/create-vader-app/node_modules/fs-extra/lib/copy/copy.js +238 -0
- package/create-vader-app/node_modules/fs-extra/lib/copy/index.js +7 -0
- package/create-vader-app/node_modules/fs-extra/lib/empty/index.js +39 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/file.js +69 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/index.js +23 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/link.js +64 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/symlink-paths.js +99 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/symlink-type.js +31 -0
- package/create-vader-app/node_modules/fs-extra/lib/ensure/symlink.js +82 -0
- package/create-vader-app/node_modules/fs-extra/lib/esm.mjs +68 -0
- package/create-vader-app/node_modules/fs-extra/lib/fs/index.js +140 -0
- package/create-vader-app/node_modules/fs-extra/lib/index.js +16 -0
- package/create-vader-app/node_modules/fs-extra/lib/json/index.js +16 -0
- package/create-vader-app/node_modules/fs-extra/lib/json/jsonfile.js +11 -0
- package/create-vader-app/node_modules/fs-extra/lib/json/output-json-sync.js +12 -0
- package/create-vader-app/node_modules/fs-extra/lib/json/output-json.js +12 -0
- package/create-vader-app/node_modules/fs-extra/lib/mkdirs/index.js +14 -0
- package/create-vader-app/node_modules/fs-extra/lib/mkdirs/make-dir.js +27 -0
- package/create-vader-app/node_modules/fs-extra/lib/mkdirs/utils.js +21 -0
- package/create-vader-app/node_modules/fs-extra/lib/move/index.js +7 -0
- package/create-vader-app/node_modules/fs-extra/lib/move/move-sync.js +55 -0
- package/create-vader-app/node_modules/fs-extra/lib/move/move.js +76 -0
- package/create-vader-app/node_modules/fs-extra/lib/output-file/index.js +40 -0
- package/create-vader-app/node_modules/fs-extra/lib/path-exists/index.js +12 -0
- package/create-vader-app/node_modules/fs-extra/lib/remove/index.js +17 -0
- package/create-vader-app/node_modules/fs-extra/lib/util/stat.js +154 -0
- package/create-vader-app/node_modules/fs-extra/lib/util/utimes.js +26 -0
- package/create-vader-app/node_modules/fs-extra/package.json +71 -0
- package/create-vader-app/node_modules/graceful-fs/LICENSE +15 -0
- package/create-vader-app/node_modules/graceful-fs/README.md +143 -0
- package/create-vader-app/node_modules/graceful-fs/clone.js +23 -0
- package/create-vader-app/node_modules/graceful-fs/graceful-fs.js +448 -0
- package/create-vader-app/node_modules/graceful-fs/legacy-streams.js +118 -0
- package/create-vader-app/node_modules/graceful-fs/package.json +53 -0
- package/create-vader-app/node_modules/graceful-fs/polyfills.js +355 -0
- package/create-vader-app/node_modules/inherits/LICENSE +16 -0
- package/create-vader-app/node_modules/inherits/README.md +42 -0
- package/create-vader-app/node_modules/inherits/inherits.js +7 -0
- package/create-vader-app/node_modules/inherits/inherits_browser.js +23 -0
- package/create-vader-app/node_modules/inherits/package.json +29 -0
- package/create-vader-app/node_modules/jsonfile/CHANGELOG.md +171 -0
- package/create-vader-app/node_modules/jsonfile/LICENSE +15 -0
- package/create-vader-app/node_modules/jsonfile/README.md +230 -0
- package/create-vader-app/node_modules/jsonfile/index.js +88 -0
- package/create-vader-app/node_modules/jsonfile/package.json +40 -0
- package/create-vader-app/node_modules/jsonfile/utils.js +14 -0
- package/create-vader-app/node_modules/path/LICENSE +18 -0
- package/create-vader-app/node_modules/path/README.md +15 -0
- package/create-vader-app/node_modules/path/package.json +24 -0
- package/create-vader-app/node_modules/path/path.js +628 -0
- package/create-vader-app/node_modules/process/.eslintrc +21 -0
- package/create-vader-app/node_modules/process/LICENSE +22 -0
- package/create-vader-app/node_modules/process/README.md +26 -0
- package/create-vader-app/node_modules/process/browser.js +184 -0
- package/create-vader-app/node_modules/process/index.js +2 -0
- package/create-vader-app/node_modules/process/package.json +27 -0
- package/create-vader-app/node_modules/process/test.js +199 -0
- package/create-vader-app/node_modules/universalify/LICENSE +20 -0
- package/create-vader-app/node_modules/universalify/README.md +76 -0
- package/create-vader-app/node_modules/universalify/index.js +24 -0
- package/create-vader-app/node_modules/universalify/package.json +34 -0
- package/create-vader-app/node_modules/util/LICENSE +18 -0
- package/create-vader-app/node_modules/util/README.md +15 -0
- package/create-vader-app/node_modules/util/package.json +35 -0
- package/create-vader-app/node_modules/util/support/isBuffer.js +3 -0
- package/create-vader-app/node_modules/util/support/isBufferBrowser.js +6 -0
- package/create-vader-app/node_modules/util/util.js +586 -0
- package/create-vader-app/package-lock.json +384 -0
- package/create-vader-app/package.json +18 -0
- package/images/router.png +0 -0
- package/images/state.png +0 -0
- package/package.json +1 -1
- package/ts.config.json +1 -0
- package/vader-min.js +1 -1
- package/vader.js +106 -326
- package/worker-min.js +1 -1
- package/worker.js +268 -168
|
@@ -0,0 +1,1115 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
let dom = [];
|
|
3
|
+
let states = {};
|
|
4
|
+
|
|
5
|
+
let worker = new Worker(new URL("./worker.js", import.meta.url));
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @function useRef
|
|
9
|
+
* @description Allows you to get reference to DOM element
|
|
10
|
+
* @param {String} ref
|
|
11
|
+
* @returns {void | Object} {current, update}
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const useRef = (ref) => {
|
|
15
|
+
const element = document.querySelector(`[ref="${ref}"]`);
|
|
16
|
+
const getElement = () => element;
|
|
17
|
+
|
|
18
|
+
const update = (data) => {
|
|
19
|
+
const newDom = new DOMParser().parseFromString(data, "text/html");
|
|
20
|
+
const newElement = newDom.body.firstChild;
|
|
21
|
+
|
|
22
|
+
if (element) {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const isDifferent = !newElement.isEqualNode(element);
|
|
25
|
+
if (isDifferent) {
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
element.parentNode.replaceChild(newElement, element);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
current: getElement(),
|
|
34
|
+
update
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let components = [];
|
|
39
|
+
/**
|
|
40
|
+
* @class Component
|
|
41
|
+
* @description Allows you to create a component
|
|
42
|
+
* @returns {void}
|
|
43
|
+
* @example
|
|
44
|
+
* import { Vader } from "../../dist/vader/index.js";
|
|
45
|
+
* export class Home extends Vader.Component {
|
|
46
|
+
* constructor() {
|
|
47
|
+
* super();
|
|
48
|
+
* }
|
|
49
|
+
* async render() {
|
|
50
|
+
* return this.html(`
|
|
51
|
+
* <div className="hero p-5">
|
|
52
|
+
* <h1>Home</h1>
|
|
53
|
+
* </div>
|
|
54
|
+
* `);
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
*/
|
|
58
|
+
export class Component {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.states = {};
|
|
61
|
+
//@ts-ignore
|
|
62
|
+
this.name = this.constructor.name;
|
|
63
|
+
this.executedEffects = {};
|
|
64
|
+
this.storedProps = {};
|
|
65
|
+
this.componentMounted = false;
|
|
66
|
+
this.hasMounted = false;
|
|
67
|
+
this.$_signal_subscribers = [];
|
|
68
|
+
/**
|
|
69
|
+
* @property {Array} $_signal_subscribers_ran
|
|
70
|
+
* @description Allows you to keep track of signal subscribers
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
this.$_signal_subscribers_ran = [];
|
|
74
|
+
this.effects = {};
|
|
75
|
+
this.$_useStore_subscribers = [];
|
|
76
|
+
this.init();
|
|
77
|
+
this.Componentcontent = null;
|
|
78
|
+
this.$_signal_dispatch_event = new CustomEvent("SignalDispatch", {
|
|
79
|
+
detail: {
|
|
80
|
+
hasUpdated: false,
|
|
81
|
+
state: null
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* @property {Object} $_signal_dispatch_cleanup_event
|
|
86
|
+
* @description Allows you to dispatch a signal cleanup event
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
this.$_signal_dispatch_cleanup_event = new CustomEvent(
|
|
90
|
+
"Signal_Cleanup_Dispatch",
|
|
91
|
+
{
|
|
92
|
+
detail: {
|
|
93
|
+
state: null,
|
|
94
|
+
lastState: null
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
/**
|
|
99
|
+
* @property {Array} snapshots
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
this.snapshots = [];
|
|
103
|
+
/**
|
|
104
|
+
* @property {Object} dom
|
|
105
|
+
* @description Allows you to get reference to DOM element
|
|
106
|
+
* @returns {void | HTMLElement}
|
|
107
|
+
*
|
|
108
|
+
*/
|
|
109
|
+
this.dom = [];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @property {Boolean} cfr
|
|
113
|
+
* @description Allows you to compile html code on the fly - client fly rendering
|
|
114
|
+
*
|
|
115
|
+
*/
|
|
116
|
+
this.cfr = false;
|
|
117
|
+
/**
|
|
118
|
+
* @property {Boolean} worker
|
|
119
|
+
* @description Allows you to use a web worker to compile html code on the fly - client fly rendering
|
|
120
|
+
|
|
121
|
+
*/
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @method adapter
|
|
126
|
+
* @description Allows you to create an adapter - this is used to create custom logic
|
|
127
|
+
*
|
|
128
|
+
*
|
|
129
|
+
*/
|
|
130
|
+
adapter(options) {
|
|
131
|
+
// allow you to override the compoent logic
|
|
132
|
+
}
|
|
133
|
+
init() {
|
|
134
|
+
this.registerComponent();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
registerComponent() {
|
|
138
|
+
components.push(this);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @method setState
|
|
143
|
+
* @description Allows you to set state
|
|
144
|
+
* @param {String} key
|
|
145
|
+
* @param {*} value
|
|
146
|
+
* @returns {void}
|
|
147
|
+
* @example
|
|
148
|
+
* this.setState('count', 1)
|
|
149
|
+
* */
|
|
150
|
+
setState(key, value) {
|
|
151
|
+
this.states[key] = value;
|
|
152
|
+
this.updateComponent();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @method componentUnmount
|
|
156
|
+
* @description Allows you to run code after component has unmounted
|
|
157
|
+
* @type {VoidFunction}
|
|
158
|
+
* @returns {void}
|
|
159
|
+
*/
|
|
160
|
+
unmount() {
|
|
161
|
+
this.componentMounted = false;
|
|
162
|
+
this.componentWillUnmount();
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
document.querySelector(`[data-component="${this.name}"]`).remove();
|
|
165
|
+
if (!document.querySelector(`[data-component="${this.name}"]`)) {
|
|
166
|
+
components = components.filter(
|
|
167
|
+
(component) => component.name !== this.name
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @method componentUpdate
|
|
174
|
+
* @description Allows you to run code after component has updated
|
|
175
|
+
* @param {Object} prev_state
|
|
176
|
+
* @param {Object} prev_props
|
|
177
|
+
* @param {Object} snapshot
|
|
178
|
+
* @returns {void}
|
|
179
|
+
* @example
|
|
180
|
+
* componentUpdate(prev_state, prev_props, snapshot) {
|
|
181
|
+
* console.log(prev_state, prev_props, snapshot)
|
|
182
|
+
* }
|
|
183
|
+
* */
|
|
184
|
+
componentUpdate(prev_state, prev_props, snapshot) {}
|
|
185
|
+
/**
|
|
186
|
+
* @method componentDidMount
|
|
187
|
+
* @description Allows you to run code after component has mounted
|
|
188
|
+
*/
|
|
189
|
+
componentDidMount() {}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @method componentWillUnmount
|
|
193
|
+
* @description Allows you to run code before component unmounts
|
|
194
|
+
* @type {VoidFunction}
|
|
195
|
+
* @returns {void}
|
|
196
|
+
*/
|
|
197
|
+
componentWillUnmount() {}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @method signal
|
|
201
|
+
* @description Allows you to create a signal
|
|
202
|
+
* @param {String} key
|
|
203
|
+
* @param {any} initialState
|
|
204
|
+
* @returns {Object} {subscribe, cleanup, dispatch, call, set, get}
|
|
205
|
+
* @example
|
|
206
|
+
* let signal = this.signal('count', 0);
|
|
207
|
+
* signal.subscribe((value) => {
|
|
208
|
+
* console.log(value)
|
|
209
|
+
* }, false) // false means it will run every time
|
|
210
|
+
* signal.subscribe((value) => {
|
|
211
|
+
* console.log(value)
|
|
212
|
+
* }, true) // true means it will run once
|
|
213
|
+
* signal.call() // this will call all subscribers
|
|
214
|
+
* signal.set(1) // this will set the value of the signal
|
|
215
|
+
* signal.get() // this will get the value of the signal
|
|
216
|
+
* signal.cleanup() // this will remove all subscribers
|
|
217
|
+
*/
|
|
218
|
+
signal = (key, initialState) => {
|
|
219
|
+
let hasCaller = false;
|
|
220
|
+
let [state, setState] = this.useState(key, initialState, () => {
|
|
221
|
+
if (this.$_signal_subscribers.length > 0) {
|
|
222
|
+
for (var i = 0; i < this.$_signal_subscribers.length; i++) {
|
|
223
|
+
if (!hasCaller) {
|
|
224
|
+
if (
|
|
225
|
+
this.$_signal_subscribers[i].runonce &&
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
this.$_signal_subscribers_ran.includes(
|
|
228
|
+
this.$_signal_subscribers[i]
|
|
229
|
+
)
|
|
230
|
+
) {
|
|
231
|
+
break;
|
|
232
|
+
} else {
|
|
233
|
+
this.$_signal_subscribers[i].function(state);
|
|
234
|
+
this.$_signal_subscribers_ran.push(this.$_signal_subscribers[i]);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
this.$_signal_dispatch_event.detail.hasUpdated = true;
|
|
241
|
+
this.$_signal_dispatch_event.detail.state = state;
|
|
242
|
+
window.dispatchEvent(this.$_signal_dispatch_event);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
/**
|
|
246
|
+
* @function $_signal_subscribe
|
|
247
|
+
* @description Allows you to subscribe to a signal
|
|
248
|
+
* @param {*} fn
|
|
249
|
+
* @param {*} runonce
|
|
250
|
+
* @returns {void}
|
|
251
|
+
*
|
|
252
|
+
*/
|
|
253
|
+
this.$_signal_subscribe = (fn, runonce) => {
|
|
254
|
+
this.$_signal_subscribers.push({
|
|
255
|
+
function: fn,
|
|
256
|
+
runonce: runonce
|
|
257
|
+
});
|
|
258
|
+
return fn;
|
|
259
|
+
};
|
|
260
|
+
this.$_signal_cleanup = (fn) => {
|
|
261
|
+
this.lastState = state;
|
|
262
|
+
this.$_signal_subscribers = this.$_signal_subscribers.filter(
|
|
263
|
+
(subscriber) => subscriber.function !== fn
|
|
264
|
+
);
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
this.$_signal_dispatch_cleanup_event.detail.state = this.states;
|
|
267
|
+
// @ts-ignore
|
|
268
|
+
this.$_signal_dispatch_cleanup_event.detail.lastState = this.lastState;
|
|
269
|
+
window.dispatchEvent(this.$_signal_dispatch_event);
|
|
270
|
+
};
|
|
271
|
+
this.$_signal_dispatch = () => {
|
|
272
|
+
for (var i = 0; i < this.$_signal_subscribers.length; i++) {
|
|
273
|
+
if (
|
|
274
|
+
this.$_signal_subscribers[i].runonce &&
|
|
275
|
+
// @ts-ignore
|
|
276
|
+
this.$_signal_subscribers_ran.includes(this.$_signal_subscribers[i])
|
|
277
|
+
) {
|
|
278
|
+
break;
|
|
279
|
+
} else {
|
|
280
|
+
this.$_signal_subscribers[i].function(state);
|
|
281
|
+
this.$_signal_subscribers_ran.push(this.$_signal_subscribers[i]);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
this.$_signal_get = () => {
|
|
286
|
+
return state;
|
|
287
|
+
};
|
|
288
|
+
this.$_signal_call = () => {
|
|
289
|
+
hasCaller = true;
|
|
290
|
+
// @ts-ignore
|
|
291
|
+
this.$_signal_dispatch();
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* @function $_signal_set
|
|
295
|
+
* @description Allows you to set the value of a signal
|
|
296
|
+
* @param {*} detail
|
|
297
|
+
*/
|
|
298
|
+
this.$_signal_set = (detail) => {
|
|
299
|
+
setState(detail);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
/**
|
|
304
|
+
* @function subscribe
|
|
305
|
+
* @description Allows you to subscribe to a signal
|
|
306
|
+
* @param {*} fn
|
|
307
|
+
* @param {*} runonce
|
|
308
|
+
*/
|
|
309
|
+
subscribe: this.$_signal_subscribe,
|
|
310
|
+
/**
|
|
311
|
+
* @function cleanup
|
|
312
|
+
* @description Allows you to cleanup a signal
|
|
313
|
+
* @param {*} fn
|
|
314
|
+
* @returns {null}
|
|
315
|
+
*/
|
|
316
|
+
cleanup: this.$_signal_cleanup,
|
|
317
|
+
/**
|
|
318
|
+
* @function dispatch
|
|
319
|
+
* @description Allows you to dispatch a signal
|
|
320
|
+
* @returns {null}
|
|
321
|
+
*/
|
|
322
|
+
dispatch: this.$_signal_dispatch,
|
|
323
|
+
/**
|
|
324
|
+
* @function call
|
|
325
|
+
* @description Allows you to call a signal
|
|
326
|
+
* @returns {null}
|
|
327
|
+
*/
|
|
328
|
+
call: this.$_signal_call,
|
|
329
|
+
/**
|
|
330
|
+
* @function set
|
|
331
|
+
* @description Allows you to set the value of a signal
|
|
332
|
+
* @param {*} detail
|
|
333
|
+
* @returns {null}
|
|
334
|
+
*/
|
|
335
|
+
set: this.$_signal_set,
|
|
336
|
+
/**
|
|
337
|
+
* @function get
|
|
338
|
+
* @readonly
|
|
339
|
+
* @description Allows you to get the value of a signal
|
|
340
|
+
* @returns {any}
|
|
341
|
+
*/
|
|
342
|
+
get: this.$_signal_get
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
/**
|
|
346
|
+
* @method useAuth
|
|
347
|
+
* @description Allows you to create an auth object
|
|
348
|
+
* @param {Object} options
|
|
349
|
+
* @param {Array} options.rulesets
|
|
350
|
+
* @param {Object} options.user
|
|
351
|
+
* @returns {Object} {can, hasRole, canWithRole, assignRule, revokeRule, canAnyOf, canAllOf, canGroup}
|
|
352
|
+
* @example
|
|
353
|
+
* let auth = this.useAuth({
|
|
354
|
+
* rulesets: [
|
|
355
|
+
* {
|
|
356
|
+
* action: 'create',
|
|
357
|
+
* condition: (user) => {
|
|
358
|
+
* return user.role === 'admin'
|
|
359
|
+
* }
|
|
360
|
+
* }
|
|
361
|
+
* ],
|
|
362
|
+
* user: {
|
|
363
|
+
* role: 'admin'
|
|
364
|
+
* }
|
|
365
|
+
* })
|
|
366
|
+
* auth.can('create') // true
|
|
367
|
+
*/
|
|
368
|
+
useAuth(options) {
|
|
369
|
+
/**@type {Array}**/
|
|
370
|
+
let rules = options.rulesets;
|
|
371
|
+
if (!rules) {
|
|
372
|
+
throw new Error("No rulesets provided");
|
|
373
|
+
}
|
|
374
|
+
/**@type {Object}**/
|
|
375
|
+
let user = options.user;
|
|
376
|
+
let auth = {
|
|
377
|
+
can: (action) => {
|
|
378
|
+
let can = false;
|
|
379
|
+
rules.forEach((rule) => {
|
|
380
|
+
if (rule.action === action) {
|
|
381
|
+
if (rule.condition(user)) {
|
|
382
|
+
can = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
return can;
|
|
387
|
+
},
|
|
388
|
+
/**
|
|
389
|
+
* @function hasRole
|
|
390
|
+
* @description Allows you to check if user has a role
|
|
391
|
+
* @param {String} role
|
|
392
|
+
* @returns {Boolean}
|
|
393
|
+
*/
|
|
394
|
+
hasRole: (role) => {
|
|
395
|
+
return user.role && user.role.includes(role);
|
|
396
|
+
},
|
|
397
|
+
/**
|
|
398
|
+
* @function canWithRole
|
|
399
|
+
* @param {String} action
|
|
400
|
+
* @param {String} role
|
|
401
|
+
* @returns
|
|
402
|
+
*/
|
|
403
|
+
canWithRole: (action, role) => {
|
|
404
|
+
return auth.can(action) && auth.hasRole(role);
|
|
405
|
+
},
|
|
406
|
+
assignRule: (rule) => {
|
|
407
|
+
if (
|
|
408
|
+
!rules.some((existingRule) => existingRule.action === rule.action)
|
|
409
|
+
) {
|
|
410
|
+
rules.push(rule);
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
revokeRule: (action) => {
|
|
414
|
+
rules = rules.filter((rule) => rule.action !== action);
|
|
415
|
+
},
|
|
416
|
+
canAnyOf: (actions) => {
|
|
417
|
+
return actions.some((action) => auth.can(action));
|
|
418
|
+
},
|
|
419
|
+
canAllOf: (actions) => {
|
|
420
|
+
return actions.every((action) => auth.can(action));
|
|
421
|
+
},
|
|
422
|
+
canGroup: (actions, logicalOperator = "any") => {
|
|
423
|
+
return logicalOperator === "any"
|
|
424
|
+
? auth.canAnyOf(actions)
|
|
425
|
+
: auth.canAllOf(actions);
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
return auth;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* @method useReducer
|
|
432
|
+
* @description Allows you to create a reducer
|
|
433
|
+
* @param {*} key
|
|
434
|
+
* @param {*} reducer
|
|
435
|
+
* @param {*} initialState
|
|
436
|
+
* @url - useReducer works similarly to - https://react.dev/reference/react/useReducer
|
|
437
|
+
* @returns {Array} [state, dispatch]
|
|
438
|
+
* @example
|
|
439
|
+
* let [count, setCount] = this.useReducer('count', (state, action) => {
|
|
440
|
+
* switch (action.type) {
|
|
441
|
+
* case 'increment':
|
|
442
|
+
* return state + 1
|
|
443
|
+
* case 'decrement':
|
|
444
|
+
* return state - 1
|
|
445
|
+
* default:
|
|
446
|
+
* throw new Error()
|
|
447
|
+
* }
|
|
448
|
+
* }, 0)
|
|
449
|
+
* setCount({type: 'increment'})
|
|
450
|
+
*/
|
|
451
|
+
useReducer(key, reducer, initialState) {
|
|
452
|
+
if (!this.states[key]) {
|
|
453
|
+
this.states[key] = initialState;
|
|
454
|
+
}
|
|
455
|
+
return [
|
|
456
|
+
this.states[key],
|
|
457
|
+
/**
|
|
458
|
+
* @function dispatch
|
|
459
|
+
* @description Allows you to dispatch a reducer
|
|
460
|
+
* @param {*} action
|
|
461
|
+
* @returns {void}
|
|
462
|
+
*/
|
|
463
|
+
(action) => {
|
|
464
|
+
this.states[key] = reducer(this.states[key], action);
|
|
465
|
+
this.updateComponent();
|
|
466
|
+
}
|
|
467
|
+
];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
runEffects() {
|
|
471
|
+
Object.keys(this.effects).forEach((component) => {
|
|
472
|
+
this.effects[component].forEach((effect) => {
|
|
473
|
+
if (!this.executedEffects[effect]) {
|
|
474
|
+
effect();
|
|
475
|
+
this.executedEffects[effect] = true;
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* @method useSyncStore
|
|
483
|
+
* @description Allows you to create a store
|
|
484
|
+
* @param {String} storeName
|
|
485
|
+
* @param {*} initialState
|
|
486
|
+
* @returns {Object} {getField, setField, subscribe, clear}
|
|
487
|
+
* @example
|
|
488
|
+
* let store = this.useSyncStore('store', {count: 0})
|
|
489
|
+
* store.setField('count', 1)
|
|
490
|
+
* store.getField('count') // 1
|
|
491
|
+
*
|
|
492
|
+
*/
|
|
493
|
+
useSyncStore(storeName, initialState) {
|
|
494
|
+
let [storedState, setStoredState] = this.useState(
|
|
495
|
+
storeName,
|
|
496
|
+
initialState ||
|
|
497
|
+
// @ts-ignore
|
|
498
|
+
localStorage.getItem(`$_vader_${storeName}`, (s) => {
|
|
499
|
+
localStorage.setItem(`$_vader_${storeName}`, JSON.stringify(s));
|
|
500
|
+
this.$_useStore_subscribers.forEach((subscriber) => {
|
|
501
|
+
subscriber(s);
|
|
502
|
+
});
|
|
503
|
+
}) ||
|
|
504
|
+
{}
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const getField = (fieldName) => {
|
|
508
|
+
return storedState[fieldName];
|
|
509
|
+
};
|
|
510
|
+
const setField = (fieldName, value) => {
|
|
511
|
+
const newState = { ...storedState, [fieldName]: value };
|
|
512
|
+
setStoredState(newState);
|
|
513
|
+
};
|
|
514
|
+
const subscribe = (subscriber) => {
|
|
515
|
+
return this.$_useStore_subscribers.push(subscriber);
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const clear = (fieldName) => {
|
|
519
|
+
let newState = storedState;
|
|
520
|
+
delete newState[fieldName];
|
|
521
|
+
setStoredState(newState);
|
|
522
|
+
};
|
|
523
|
+
return {
|
|
524
|
+
getField,
|
|
525
|
+
setField,
|
|
526
|
+
subscribe,
|
|
527
|
+
clear
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* @method useState
|
|
532
|
+
* @description Allows you to create a state
|
|
533
|
+
* @param {String} key
|
|
534
|
+
* @param {*} initialValue
|
|
535
|
+
* @param {*} callback
|
|
536
|
+
* @url - useState works similarly to - https://react.dev/reference/react/useState
|
|
537
|
+
* @returns {Array} [state, setState]
|
|
538
|
+
* @example
|
|
539
|
+
* let [count, setCount] = this.useState('count', 0, () => {
|
|
540
|
+
* console.log('count has been updated')
|
|
541
|
+
* })
|
|
542
|
+
*
|
|
543
|
+
* setCount(count + 1)
|
|
544
|
+
*/
|
|
545
|
+
useState(key, initialValue, callback = null) {
|
|
546
|
+
if (!this.states[key]) {
|
|
547
|
+
this.states[key] = initialValue;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return [
|
|
551
|
+
this.states[key],
|
|
552
|
+
/**
|
|
553
|
+
* @function setState
|
|
554
|
+
* @description Allows you to set state
|
|
555
|
+
* @param {*} value
|
|
556
|
+
* @returns {void}
|
|
557
|
+
*/
|
|
558
|
+
(value) => {
|
|
559
|
+
this.states[key] = value;
|
|
560
|
+
this.updateComponent();
|
|
561
|
+
// @ts-ignore
|
|
562
|
+
typeof callback === "function" ? callback() : null;
|
|
563
|
+
}
|
|
564
|
+
];
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* @method useRef
|
|
568
|
+
* @memberof Component
|
|
569
|
+
* @param {string} ref
|
|
570
|
+
* @description Allows you to get reference to DOM elements from the dom array
|
|
571
|
+
* @returns {Object} {current, update}
|
|
572
|
+
* @example
|
|
573
|
+
* let ref = this.useRef('ref')
|
|
574
|
+
* ref.current // returns the DOM element
|
|
575
|
+
|
|
576
|
+
*/
|
|
577
|
+
|
|
578
|
+
useRef(ref) {
|
|
579
|
+
// get ref from array
|
|
580
|
+
const element = this.dom[ref];
|
|
581
|
+
|
|
582
|
+
const getElement = () => element;
|
|
583
|
+
|
|
584
|
+
const update = (data) => {
|
|
585
|
+
const newDom = new DOMParser().parseFromString(data, "text/html");
|
|
586
|
+
const newElement = newDom.body.firstChild;
|
|
587
|
+
|
|
588
|
+
if (element) {
|
|
589
|
+
// @ts-ignore
|
|
590
|
+
const isDifferent = !newElement.isEqualNode(element);
|
|
591
|
+
if (isDifferent) {
|
|
592
|
+
// @ts-ignore
|
|
593
|
+
element.parentNode.replaceChild(newElement, element);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
/**@type {HTMLElement} */
|
|
600
|
+
// @ts-ignore
|
|
601
|
+
current: getElement,
|
|
602
|
+
/**@type {Function} */
|
|
603
|
+
update
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
*
|
|
609
|
+
* @function useEffect
|
|
610
|
+
* @param {*} effectFn
|
|
611
|
+
* @param {*} dependencies
|
|
612
|
+
* @description Allows you to run side effects
|
|
613
|
+
* @deprecated - this is no longer suggested please use vader signals instead
|
|
614
|
+
* @returns {Object} {cleanup}
|
|
615
|
+
*/
|
|
616
|
+
useEffect(effectFn, dependencies) {
|
|
617
|
+
if (!this.effects[this.name]) {
|
|
618
|
+
this.effects[this.name] = [];
|
|
619
|
+
}
|
|
620
|
+
this.effects[this.name].push(effectFn);
|
|
621
|
+
|
|
622
|
+
if (dependencies.length > 0) {
|
|
623
|
+
dependencies.forEach((d) => {
|
|
624
|
+
if (d.set) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
"signal found, do not use effect and signals at the same time - signals are more efficient"
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
} else if (!this.hasMounted) {
|
|
631
|
+
effectFn();
|
|
632
|
+
this.hasMounted = true;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return {
|
|
636
|
+
cleanup: () => {
|
|
637
|
+
// @ts-ignore
|
|
638
|
+
this.effects[this.name] = this.effects[this.name].filter(
|
|
639
|
+
// @ts-ignore
|
|
640
|
+
(effect) => effect !== effectFn
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* @method $Function
|
|
647
|
+
* @description Allows you to create a function in global scope
|
|
648
|
+
* @returns {Function}
|
|
649
|
+
* @example
|
|
650
|
+
* let func = this.$Function(function add(e, a){
|
|
651
|
+
* return e + a
|
|
652
|
+
* })
|
|
653
|
+
* @param {*} fn
|
|
654
|
+
*/
|
|
655
|
+
$Function(fn) {
|
|
656
|
+
// @ts-ignore
|
|
657
|
+
if (!typeof fn === "function") {
|
|
658
|
+
throw new Error("fn must be a function");
|
|
659
|
+
}
|
|
660
|
+
let name = fn.name;
|
|
661
|
+
if (!name) {
|
|
662
|
+
name = "anonymous" + Math.floor(Math.random() * 100000000000000000);
|
|
663
|
+
}
|
|
664
|
+
window[name] = fn;
|
|
665
|
+
// @ts-ignore
|
|
666
|
+
return `window.${name}()`;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Add other methods like render, useEffect, useReducer, useAuth, etc.
|
|
670
|
+
|
|
671
|
+
updateComponent() {
|
|
672
|
+
const fragment = document.createDocumentFragment();
|
|
673
|
+
Object.keys(components).forEach(async (component) => {
|
|
674
|
+
const { name } = components[component];
|
|
675
|
+
|
|
676
|
+
let componentContainer = document.querySelector(
|
|
677
|
+
`[data-component="${name}"]`
|
|
678
|
+
);
|
|
679
|
+
let time = new Date().getTime();
|
|
680
|
+
/**
|
|
681
|
+
* @property {Object} snapshot
|
|
682
|
+
* @description Allows you to keep track of component snapshots
|
|
683
|
+
* @private
|
|
684
|
+
* @returns {Object} {name, time, prev_state, prev_props, content}
|
|
685
|
+
*/
|
|
686
|
+
let snapshot = {
|
|
687
|
+
name: name,
|
|
688
|
+
time: time,
|
|
689
|
+
prev_state: this.states,
|
|
690
|
+
prev_props: this.storedProps,
|
|
691
|
+
// @ts-ignore
|
|
692
|
+
content: componentContainer.innerHTML
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
if (!componentContainer) return;
|
|
696
|
+
const newHtml = await new Function(
|
|
697
|
+
"useState",
|
|
698
|
+
"useEffect",
|
|
699
|
+
"useAuth",
|
|
700
|
+
"useReducer",
|
|
701
|
+
"useSyncStore",
|
|
702
|
+
"signal",
|
|
703
|
+
"rf",
|
|
704
|
+
"props",
|
|
705
|
+
"render",
|
|
706
|
+
"return `" + (await this.render()) + "`;"
|
|
707
|
+
)(
|
|
708
|
+
this.useState,
|
|
709
|
+
this.useEffect,
|
|
710
|
+
this.useAuth,
|
|
711
|
+
this.useReducer,
|
|
712
|
+
this.useSyncStore,
|
|
713
|
+
this.signal,
|
|
714
|
+
this.render
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
if (newHtml !== componentContainer.innerHTML) {
|
|
718
|
+
if (this.snapshots.length > 0) {
|
|
719
|
+
let lastSnapshot = this.snapshots[this.snapshots.length - 1];
|
|
720
|
+
if (lastSnapshot !== snapshot) {
|
|
721
|
+
this.snapshots.push(snapshot);
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
this.snapshots.push(snapshot);
|
|
725
|
+
}
|
|
726
|
+
this.componentUpdate(
|
|
727
|
+
snapshot.prev_state,
|
|
728
|
+
snapshot.prev_props,
|
|
729
|
+
snapshot.content
|
|
730
|
+
);
|
|
731
|
+
// batch updates
|
|
732
|
+
fragment.appendChild(
|
|
733
|
+
document.createRange().createContextualFragment(newHtml)
|
|
734
|
+
);
|
|
735
|
+
componentContainer.innerHTML = "";
|
|
736
|
+
componentContainer.appendChild(fragment);
|
|
737
|
+
this.runEffects();
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* @method validateClassName
|
|
743
|
+
* @param {String} className
|
|
744
|
+
* @private
|
|
745
|
+
* @returns {Boolean}
|
|
746
|
+
*/
|
|
747
|
+
validateClassName(className) {
|
|
748
|
+
// validate classNames ensure they are camelCase but also allow for - and _
|
|
749
|
+
return /^[a-zA-Z0-9-_]+$/.test(className);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* The `html` method generates and processes HTML content for a component, performing various validations and tasks.
|
|
754
|
+
*
|
|
755
|
+
* @param {String} strings - The HTML content to be processed.
|
|
756
|
+
* @param {...any} args - Dynamic values to be inserted into the template.
|
|
757
|
+
* @returns {string} - The processed HTML content as a string.
|
|
758
|
+
*
|
|
759
|
+
* @throws {SyntaxError} - Throws a `SyntaxError` if image-related attributes are missing or invalid.
|
|
760
|
+
* @throws {Error} - Throws an `Error` if there are issues with class names or relative paths.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* // Example usage within a component:
|
|
764
|
+
* const myComponent = new Component();
|
|
765
|
+
* const htmlContent = myComponent.html`
|
|
766
|
+
* <div>
|
|
767
|
+
* <img src="/images/example.jpg" alt="Example Image" />
|
|
768
|
+
* </div>
|
|
769
|
+
* `;
|
|
770
|
+
* document.body.innerHTML = htmlContent;
|
|
771
|
+
*
|
|
772
|
+
* @remarks
|
|
773
|
+
* The `html` method is a core function used in component rendering. It allows you to define and generate HTML content within your component while enforcing best practices and accessibility standards. The method performs several essential tasks:
|
|
774
|
+
*
|
|
775
|
+
* 1. **Image Validation**: It checks images for the presence of 'alt' attributes and their validity.
|
|
776
|
+
* - Throws a `SyntaxError` if an image is missing the 'alt' attribute.
|
|
777
|
+
* - Throws a `SyntaxError` if the 'alt' attribute is empty.
|
|
778
|
+
* - Checks for an 'aria-hidden' attribute for image elements.
|
|
779
|
+
*
|
|
780
|
+
* 2. **Class Attribute Handling**: It enforces class attribute usage and allows optional configuration via comments.
|
|
781
|
+
* - Throws an `Error` if 'class' attributes are used without permission.
|
|
782
|
+
* - Supports 'className' attributes for class definitions.
|
|
783
|
+
* - Allows or disallows class-related comments based on your configuration.
|
|
784
|
+
*
|
|
785
|
+
* 3. **Relative Path Handling**: It processes relative paths in 'href' and 'src' attributes, ensuring proper routing.
|
|
786
|
+
* - Converts relative 'href' attributes to anchor links with appropriate routing.
|
|
787
|
+
* - Converts relative 'src' attributes to absolute paths with 'public' directories.
|
|
788
|
+
*
|
|
789
|
+
* 4. **Custom Component Attributes**: It supports adding a 'data-component' attribute to the root element.
|
|
790
|
+
* - Ensures that the 'data-component' attribute is present for component identification.
|
|
791
|
+
*
|
|
792
|
+
* 5. **Lifecycle Method Invocation**: It invokes the `componentDidMount` method if called from a 'render' context.
|
|
793
|
+
* - Executes `componentDidMount` to handle component initialization once the DOM is ready.
|
|
794
|
+
*
|
|
795
|
+
* @see {@link Component}
|
|
796
|
+
* @see {@link Component#componentDidMount}
|
|
797
|
+
*/
|
|
798
|
+
|
|
799
|
+
html(strings, ...args) {
|
|
800
|
+
// @ts-ignore
|
|
801
|
+
let timer = setInterval(() => {
|
|
802
|
+
if (document.querySelector(`[data-component="${this.name}"]`)) {
|
|
803
|
+
clearInterval(timer);
|
|
804
|
+
this.componentMounted = true;
|
|
805
|
+
|
|
806
|
+
document
|
|
807
|
+
.querySelector(`[data-component="${this.name}"]`)
|
|
808
|
+
?.querySelectorAll("*")
|
|
809
|
+
.forEach((element) => {
|
|
810
|
+
if (element.hasAttribute("ref")) {
|
|
811
|
+
// @ts-ignore
|
|
812
|
+
this.dom[element.getAttribute("ref")] = element;
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
this.componentDidMount();
|
|
816
|
+
}
|
|
817
|
+
}, 100);
|
|
818
|
+
let script = document.createElement("script");
|
|
819
|
+
script.setAttribute("type", "text/javascript");
|
|
820
|
+
script.setAttribute(`data-component-script`, this.name);
|
|
821
|
+
|
|
822
|
+
worker.postMessage({
|
|
823
|
+
strings,
|
|
824
|
+
args,
|
|
825
|
+
location:
|
|
826
|
+
window.location.origin +
|
|
827
|
+
window.location.pathname.replace(/\/[^\/]*$/, "") +
|
|
828
|
+
"/public/",
|
|
829
|
+
name: this.name
|
|
830
|
+
});
|
|
831
|
+
let promise = new Promise((resolve, reject) => {
|
|
832
|
+
worker.onmessage = (e) => {
|
|
833
|
+
if (e.data.error) {
|
|
834
|
+
throw new Error(e.data.error);
|
|
835
|
+
}
|
|
836
|
+
const dom = this.dom; // Assuming this.dom is an object
|
|
837
|
+
let js = e.data.js;
|
|
838
|
+
let template = e.data.template;
|
|
839
|
+
// Bind the component's context
|
|
840
|
+
|
|
841
|
+
const useState = this.useState.bind(this); // Bind the component's context
|
|
842
|
+
const useEffect = this.useEffect.bind(this); // Bind the component's context
|
|
843
|
+
const useReducer = this.useReducer.bind(this); // Bind the component's context
|
|
844
|
+
const useAuth = this.useAuth.bind(this); // Bind the component's context
|
|
845
|
+
const useSyncStore = this.useSyncStore.bind(this); // Bind the component's context
|
|
846
|
+
const signal = this.signal.bind(this); // Bind the component's context
|
|
847
|
+
const $Function = this.$Function.bind(this); // Bind the component's context
|
|
848
|
+
let states = this.states;
|
|
849
|
+
const useRef = this.useRef.bind(this); // Bind the component's context
|
|
850
|
+
new Function(
|
|
851
|
+
"useState",
|
|
852
|
+
"useEffect",
|
|
853
|
+
"useAuth",
|
|
854
|
+
"useReducer",
|
|
855
|
+
"useSyncStore",
|
|
856
|
+
"signal",
|
|
857
|
+
"$Function",
|
|
858
|
+
"dom",
|
|
859
|
+
"render",
|
|
860
|
+
"states",
|
|
861
|
+
"useRef",
|
|
862
|
+
js
|
|
863
|
+
)(
|
|
864
|
+
useState,
|
|
865
|
+
useEffect,
|
|
866
|
+
useAuth,
|
|
867
|
+
useReducer,
|
|
868
|
+
useSyncStore,
|
|
869
|
+
signal,
|
|
870
|
+
$Function,
|
|
871
|
+
this.dom,
|
|
872
|
+
this.render,
|
|
873
|
+
this.states,
|
|
874
|
+
useRef
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
resolve(
|
|
878
|
+
handletemplate( new Function(
|
|
879
|
+
"useRef",
|
|
880
|
+
"states",
|
|
881
|
+
"signal",
|
|
882
|
+
"useState",
|
|
883
|
+
"useReducer",
|
|
884
|
+
"useAuth",
|
|
885
|
+
"useSyncStore",
|
|
886
|
+
"useRef",
|
|
887
|
+
"$Function",
|
|
888
|
+
"return" + "`" + template + "`"
|
|
889
|
+
)(
|
|
890
|
+
useRef,
|
|
891
|
+
states,
|
|
892
|
+
signal,
|
|
893
|
+
useState,
|
|
894
|
+
useReducer,
|
|
895
|
+
useAuth,
|
|
896
|
+
useSyncStore,
|
|
897
|
+
useRef,
|
|
898
|
+
$Function
|
|
899
|
+
))
|
|
900
|
+
);
|
|
901
|
+
};
|
|
902
|
+
worker.onerror = (e) => {
|
|
903
|
+
reject(e);
|
|
904
|
+
};
|
|
905
|
+
});
|
|
906
|
+
// @ts-ignore
|
|
907
|
+
return promise;
|
|
908
|
+
}
|
|
909
|
+
// write types to ensure it returns a string
|
|
910
|
+
/**
|
|
911
|
+
* @method render
|
|
912
|
+
* @description Allows you to render html
|
|
913
|
+
* @returns {Promise <any>}
|
|
914
|
+
* @example
|
|
915
|
+
* async render() {
|
|
916
|
+
* return this.html(`
|
|
917
|
+
* <div className="hero p-5">
|
|
918
|
+
* <h1>Home</h1>
|
|
919
|
+
* </div>
|
|
920
|
+
* `);
|
|
921
|
+
*/
|
|
922
|
+
async render(props) {}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* @object Vader
|
|
927
|
+
* @property {class} Component
|
|
928
|
+
* @property {function} useRef
|
|
929
|
+
* @description Allows you to create a component
|
|
930
|
+
* @example
|
|
931
|
+
* import { Vader } from "../../dist/vader/vader.js";
|
|
932
|
+
* export class Home extends Vader.Component {
|
|
933
|
+
* constructor() {
|
|
934
|
+
* super('Home');
|
|
935
|
+
* }
|
|
936
|
+
* async render() {
|
|
937
|
+
* return this.html(`
|
|
938
|
+
* <div className="hero p-5">
|
|
939
|
+
* <h1>Home</h1>
|
|
940
|
+
* </div>
|
|
941
|
+
* `);
|
|
942
|
+
* }
|
|
943
|
+
*/
|
|
944
|
+
const Vader = {
|
|
945
|
+
/**
|
|
946
|
+
* @class Component
|
|
947
|
+
* @description Allows you to create a component
|
|
948
|
+
* @returns {void}
|
|
949
|
+
* @memberof {Vader}
|
|
950
|
+
* @example
|
|
951
|
+
* import { Vader } from "../../dist/vader/index.js";
|
|
952
|
+
* export class Home extends Vader.Component {
|
|
953
|
+
* constructor() {
|
|
954
|
+
* super();
|
|
955
|
+
* }
|
|
956
|
+
* async render() {
|
|
957
|
+
* return this.html(`
|
|
958
|
+
* <div className="hero p-5">
|
|
959
|
+
* <h1>Home</h1>
|
|
960
|
+
* </div>
|
|
961
|
+
* `);
|
|
962
|
+
* }
|
|
963
|
+
* }
|
|
964
|
+
*/
|
|
965
|
+
Component: Component,
|
|
966
|
+
useRef: useRef
|
|
967
|
+
};
|
|
968
|
+
/**
|
|
969
|
+
* @function component
|
|
970
|
+
* @description Allows you to create a component
|
|
971
|
+
* @returns {Component}
|
|
972
|
+
*/
|
|
973
|
+
export const component = () => {
|
|
974
|
+
return new Component();
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* @function rf
|
|
979
|
+
* @param {*} name
|
|
980
|
+
* @param {*} fn
|
|
981
|
+
* @returns {void}
|
|
982
|
+
* @deprecated - rf has been replaced with Vader.Component.$Function
|
|
983
|
+
* @description Allows you to register function in global scope
|
|
984
|
+
*/
|
|
985
|
+
export const rf = (name, fn) => {
|
|
986
|
+
window[name] = fn;
|
|
987
|
+
};
|
|
988
|
+
let cache = {};
|
|
989
|
+
async function handletemplate(data) {
|
|
990
|
+
let dom = new DOMParser().parseFromString(data, "text/html");
|
|
991
|
+
let elements = dom.documentElement.querySelectorAll("*");
|
|
992
|
+
|
|
993
|
+
if (elements.length > 0) {
|
|
994
|
+
for (var i = 0; i < elements.length; i++) {
|
|
995
|
+
if (elements[i].nodeName === "INCLUDE") {
|
|
996
|
+
if (
|
|
997
|
+
!elements[i].getAttribute("src") ||
|
|
998
|
+
elements[i].getAttribute("src") === ""
|
|
999
|
+
) {
|
|
1000
|
+
throw new Error("Include tag must have src attribute");
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
let componentName = elements[i]
|
|
1004
|
+
.getAttribute("src")
|
|
1005
|
+
?.split("/")
|
|
1006
|
+
.pop()
|
|
1007
|
+
?.split(".")[0];
|
|
1008
|
+
// @ts-ignore
|
|
1009
|
+
let filedata = await include(elements[i].getAttribute("src"));
|
|
1010
|
+
// replace ` with \`\` to allow for template literals
|
|
1011
|
+
filedata = filedata.replace(/`/g, "\\`");
|
|
1012
|
+
cache[elements[i].getAttribute("src")] = filedata;
|
|
1013
|
+
filedata = new Function(`return \`${filedata}\`;`)();
|
|
1014
|
+
let newdom = new DOMParser().parseFromString(filedata, "text/html");
|
|
1015
|
+
|
|
1016
|
+
newdom.querySelectorAll("include").forEach((el) => {
|
|
1017
|
+
el.remove();
|
|
1018
|
+
});
|
|
1019
|
+
// @ts-ignore
|
|
1020
|
+
|
|
1021
|
+
let els = dom.querySelectorAll(componentName);
|
|
1022
|
+
|
|
1023
|
+
els.forEach((el) => {
|
|
1024
|
+
if (el.attributes.length > 0) {
|
|
1025
|
+
for (var i = 0; i < el.attributes.length; i++) {
|
|
1026
|
+
// @ts-ignore
|
|
1027
|
+
let t = "{{" + el.attributes[i].name + "}}";
|
|
1028
|
+
if (newdom.body.innerHTML.includes(t)) {
|
|
1029
|
+
// @ts-ignore
|
|
1030
|
+
newdom.body.innerHTML = newdom.body.innerHTML.replaceAll(
|
|
1031
|
+
t,
|
|
1032
|
+
el.attributes[i].value
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
if (el.children.length > 0 && newdom.body.querySelector("slot")) {
|
|
1038
|
+
for (var i = 0; i < el.children.length; i++) {
|
|
1039
|
+
let slots = newdom.body.querySelectorAll("slot");
|
|
1040
|
+
slots.forEach((slot) => {
|
|
1041
|
+
let id = slot.getAttribute("id");
|
|
1042
|
+
|
|
1043
|
+
if (
|
|
1044
|
+
(el.hasAttribute("key") && el.getAttribute("key") === id) ||
|
|
1045
|
+
(!el.hasAttribute("key") && el.nodeName === id)
|
|
1046
|
+
) {
|
|
1047
|
+
if (el.children[i].innerHTML.length > 0) {
|
|
1048
|
+
slot.outerHTML = el.children[i].innerHTML;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
dom.body.querySelectorAll("include").forEach((el) => {
|
|
1056
|
+
el.remove();
|
|
1057
|
+
});
|
|
1058
|
+
// replace ` with \`\` to allow for template literals
|
|
1059
|
+
dom.body.outerHTML = dom.body.outerHTML.replace(/`/g, "\\`");
|
|
1060
|
+
dom.body.outerHTML = dom.body.outerHTML.replace(
|
|
1061
|
+
el.outerHTML,
|
|
1062
|
+
new Function(`return \`${newdom.body.outerHTML}\`;`)()
|
|
1063
|
+
);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// replace ` with \`\` to allow for template literals
|
|
1070
|
+
dom.body.outerHTML = dom.body.outerHTML.replace(/`/g, "\\`");
|
|
1071
|
+
data = new Function(`return \`${dom.body.outerHTML}\`;`)();
|
|
1072
|
+
|
|
1073
|
+
return data;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* @function include
|
|
1077
|
+
* @description Allows you to include html file
|
|
1078
|
+
* @returns {Promise} - modified string with html content
|
|
1079
|
+
* @param {string} path
|
|
1080
|
+
*/
|
|
1081
|
+
|
|
1082
|
+
export const include = async (path) => {
|
|
1083
|
+
if (
|
|
1084
|
+
path.startsWith("/") &&
|
|
1085
|
+
!path.includes("/src/") &&
|
|
1086
|
+
!document.documentElement.outerHTML
|
|
1087
|
+
.trim()
|
|
1088
|
+
.includes("<!-- #vader-disable_relative-paths -->")
|
|
1089
|
+
) {
|
|
1090
|
+
path = "/src/" + path;
|
|
1091
|
+
}
|
|
1092
|
+
// @ts-ignore
|
|
1093
|
+
if (cache[path]) {
|
|
1094
|
+
// @ts-ignore
|
|
1095
|
+
return cache[path];
|
|
1096
|
+
} else {
|
|
1097
|
+
return fetch(`./${path}`)
|
|
1098
|
+
.then((res) => {
|
|
1099
|
+
if (res.status === 404) {
|
|
1100
|
+
throw new Error(`No file found at ${path}`);
|
|
1101
|
+
}
|
|
1102
|
+
return res.text();
|
|
1103
|
+
})
|
|
1104
|
+
.then(async (data) => {
|
|
1105
|
+
// @ts-ignore
|
|
1106
|
+
cache[path] = data;
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
return data;
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
export default Vader
|