create-gardener 1.0.0
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/.envrc +1 -0
- package/LICENSE +9 -0
- package/Readme.md +349 -0
- package/flake.lock +61 -0
- package/flake.nix +23 -0
- package/package.json +34 -0
- package/starter.js +73 -0
- package/template/Readme.md +349 -0
- package/template/package.json +34 -0
- package/template/src/backend/cache/w_500x500.webp +0 -0
- package/template/src/backend/controllers/gardener.controller.ts +211 -0
- package/template/src/backend/frontendtemplate.ejs +26 -0
- package/template/src/backend/libs/generateWebp.ts +26 -0
- package/template/src/backend/routes/gardener.route.ts +19 -0
- package/template/src/backend/server.ts +30 -0
- package/template/src/frontend/assets/w.webp +0 -0
- package/template/src/frontend/components/test.js +54 -0
- package/template/src/frontend/gardener.js +404 -0
- package/template/src/frontend/global.js +57 -0
- package/template/src/frontend/style.css +481 -0
- package/template/src/frontend/tailwind.css +1 -0
- package/template/src/frontend/views/_.ejs +26 -0
- package/template/src/frontend/views/partials/loader.ejs +3 -0
- package/template/tailwind.config.js +0 -0
- package/template/tsconfig.json +43 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
import { gardener } from '../gardener.js'
|
|
3
|
+
|
|
4
|
+
export default function () {
|
|
5
|
+
return gardener({
|
|
6
|
+
"t": "div",
|
|
7
|
+
"attr": {
|
|
8
|
+
"id": "body"
|
|
9
|
+
},
|
|
10
|
+
"children": [
|
|
11
|
+
{
|
|
12
|
+
"t": "div",
|
|
13
|
+
"cn": [
|
|
14
|
+
"h-screen",
|
|
15
|
+
"w-screen",
|
|
16
|
+
"bg-white",
|
|
17
|
+
"loader",
|
|
18
|
+
"absolute"
|
|
19
|
+
],
|
|
20
|
+
"attr": {
|
|
21
|
+
"style": "transition: 0.4s; opacity: 0;"
|
|
22
|
+
},
|
|
23
|
+
"txt": ""
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"t": "div",
|
|
27
|
+
"cn": [
|
|
28
|
+
"hero",
|
|
29
|
+
"flex",
|
|
30
|
+
"justify-around",
|
|
31
|
+
"items-center",
|
|
32
|
+
"p-5",
|
|
33
|
+
"h-[90vh]"
|
|
34
|
+
],
|
|
35
|
+
"children": [
|
|
36
|
+
{
|
|
37
|
+
"t": "p",
|
|
38
|
+
"cn": [
|
|
39
|
+
"p-5"
|
|
40
|
+
],
|
|
41
|
+
"txt": "Gardener is a front-end library for creating and manipulating DOM elements using a declarative JavaScript object syntax. It includes a development server with features like hot-reloading and on-the-fly component creation from existing HTML. The server also provides dynamic image resizing and caching."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"t": "img",
|
|
45
|
+
"attr": {
|
|
46
|
+
"src": "/cache/w_500x500.webp",
|
|
47
|
+
"alt": "logo"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
})
|
|
54
|
+
}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
const config = {
|
|
2
|
+
mode: 'dev',
|
|
3
|
+
componentdir: 'components',
|
|
4
|
+
hotreload: true
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let hotReloadtimeout;
|
|
8
|
+
const body = fetchElement('body');
|
|
9
|
+
let hotReload = localStorage.getItem('hotreload');
|
|
10
|
+
|
|
11
|
+
if (hotReload === null) hotReload = config.hotreload;
|
|
12
|
+
else if (hotReload === 'true') hotReload = true
|
|
13
|
+
else if (hotReload === 'false') hotReload = false
|
|
14
|
+
|
|
15
|
+
function opnPagedialog(btn = true) {
|
|
16
|
+
if (btn) {
|
|
17
|
+
const dialog = gardener({
|
|
18
|
+
t: 'form', cn: ['addpageform', 'fixed', 'left-2/5', 'bg-gray-200', 'rounded-lg', 'block', 'top-2/5', 'p-2', 'flex', 'flex-col', 'p-5', 'gap-2'], events: {
|
|
19
|
+
submit: async (e) => {
|
|
20
|
+
try {
|
|
21
|
+
e.preventDefault()
|
|
22
|
+
const data = new FormData(e.target);
|
|
23
|
+
const input = Object.fromEntries(data.entries());
|
|
24
|
+
console.log(input)
|
|
25
|
+
|
|
26
|
+
const response = await fetch('/addpage', {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": 'application/json'
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify(input)
|
|
32
|
+
}).then(res => res.json())
|
|
33
|
+
console.log(response)
|
|
34
|
+
opnPagedialog(false)
|
|
35
|
+
window.location.href = input.page
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.log(err)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
}, children: [{
|
|
43
|
+
t: 'label',
|
|
44
|
+
txt: 'ENTER PATH FOR NEW PAGE'
|
|
45
|
+
}, { t: 'input', attr: { name: 'page' }, cn: ['pathinput'] }]
|
|
46
|
+
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
console.log('test')
|
|
50
|
+
appendElement(body, dialog);
|
|
51
|
+
fetchElement('.pathinput').focus();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.log('removed')
|
|
55
|
+
fetchElement('.addpageform').remove();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (config.mode === 'dev') {
|
|
60
|
+
const addPagebtn = gardener({
|
|
61
|
+
t: 'button',
|
|
62
|
+
cn: ['pb-1.5', 'flex', 'items-center', 'justify-center', 'h-15', 'w-15', 'bg-green-300', 'fixed', 'bottom-22', 'z-100', 'right-2', 'rounded-full', 'text-5xl'],
|
|
63
|
+
children: [{ t: 'span', txt: '+' }],
|
|
64
|
+
events: {
|
|
65
|
+
click: opnPagedialog
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
appendElement(body, addPagebtn);
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
appendElement(body, gardener({
|
|
73
|
+
t: 'p',
|
|
74
|
+
cn: ['bg-gray-200', 'fixed', 'bottom-0', 'z-100', 'right-0', 'border-b-1', 'p-2', 'rounded-md'],
|
|
75
|
+
children: [
|
|
76
|
+
{
|
|
77
|
+
t: 'span',
|
|
78
|
+
txt: 'Press '
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
t: 'span',
|
|
82
|
+
cn: ['text-green-500', 'font-bold'],
|
|
83
|
+
txt: 'Ctrl+h'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
t: 'span',
|
|
87
|
+
txt: ' to toggle Hot Reload'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
t: 'form',
|
|
91
|
+
attr: {
|
|
92
|
+
id: 'hrcheckbox',
|
|
93
|
+
},
|
|
94
|
+
events: {
|
|
95
|
+
click: () => togglehotreload()
|
|
96
|
+
},
|
|
97
|
+
cn: ['p-2', 'bg-red-300'],
|
|
98
|
+
children: [{
|
|
99
|
+
t: 'label',
|
|
100
|
+
txt: 'Hot Reload ',
|
|
101
|
+
}
|
|
102
|
+
, {
|
|
103
|
+
t: 'input',
|
|
104
|
+
cn: ['hrcheckbox'],
|
|
105
|
+
attr: {
|
|
106
|
+
type: 'checkbox'
|
|
107
|
+
}
|
|
108
|
+
}]
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}))
|
|
112
|
+
|
|
113
|
+
//appendElement(body, gardener())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
togglehotreload();
|
|
117
|
+
document.addEventListener('keydown', function(e) {
|
|
118
|
+
// Detect Ctrl + H
|
|
119
|
+
if (e.ctrlKey && e.key.toLowerCase() === 'h') {
|
|
120
|
+
e.preventDefault(); // Stop browser from opening history
|
|
121
|
+
// Your logic here...
|
|
122
|
+
togglehotreload();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//if (config.mode === 'dev') {
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
function togglehotreload() {
|
|
131
|
+
const hr = hotReload;
|
|
132
|
+
const hrcheck = fetchElement('#hrcheckbox');
|
|
133
|
+
|
|
134
|
+
localStorage.setItem('hotreload', hr);
|
|
135
|
+
|
|
136
|
+
hotReload = !hotReload;
|
|
137
|
+
|
|
138
|
+
if (hr) {
|
|
139
|
+
hrcheck.style.background = '#66e666';
|
|
140
|
+
fetchElement('.hrcheckbox').checked = true;
|
|
141
|
+
localStorage.setItem('hotreload', 'true');
|
|
142
|
+
hotReloadtimeout = setTimeout(() => window.location.reload(), 1000);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
hrcheck.style.background = 'red';
|
|
146
|
+
fetchElement('.hrcheckbox').checked = false;
|
|
147
|
+
localStorage.setItem('hotreload', 'false');
|
|
148
|
+
clearTimeout(hotReloadtimeout);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//localStorage.setItem('hotreload', hotReload);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function parserWindow(text) {
|
|
155
|
+
if (config.mode !== 'dev') return;
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
const result = gardener({
|
|
159
|
+
t: 'div',
|
|
160
|
+
cn: ['fixed', 'border-2', 'border-black', 'bg-gray-500', 'text-white', 'rounded-lg', 'z-90', 'w-2/4', 'h-2/4', 'left-1/4', 'flex', 'flex-col', 'justify-between', 'top-1/4'],
|
|
161
|
+
children: [
|
|
162
|
+
{
|
|
163
|
+
t: 'div',
|
|
164
|
+
cn: ['bg-gray-200', 'h-15', 'text-black', 'rounded-t-lg', 'flex', 'items-center', 'justify-around'],
|
|
165
|
+
children: [
|
|
166
|
+
{
|
|
167
|
+
t: 'h3',
|
|
168
|
+
cn: ['font-bold'],
|
|
169
|
+
txt: 'Parser Window'
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
t: 'button',
|
|
173
|
+
cn: ['p-2', 'bg-red-300', 'rounded-lg', 'cursor-pointer'],
|
|
174
|
+
txt: 'Add Component',
|
|
175
|
+
attr: {
|
|
176
|
+
id: 'copybtn'
|
|
177
|
+
},
|
|
178
|
+
events: {
|
|
179
|
+
click: copytxt
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
t: 'p',
|
|
186
|
+
cn: ['p-5', 'overflow-scroll'],
|
|
187
|
+
txt: text
|
|
188
|
+
},
|
|
189
|
+
]
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
function copytxt() {
|
|
193
|
+
result.remove()
|
|
194
|
+
|
|
195
|
+
const compform = gardener({
|
|
196
|
+
t: 'form',
|
|
197
|
+
events: {
|
|
198
|
+
submit: (event) => {
|
|
199
|
+
event.preventDefault()
|
|
200
|
+
copyText(text, `${fetchElement('.componentInp').value}.js`)
|
|
201
|
+
compform.remove();
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
cn: ['fixed', 'left-2/5', 'bg-gray-500', 'rounded-lg', 'block', 'top-2/5', 'p-2'],
|
|
205
|
+
children: [
|
|
206
|
+
{
|
|
207
|
+
t: 'input',
|
|
208
|
+
cn: ['bg-white', 'componentInp'],
|
|
209
|
+
attr: {
|
|
210
|
+
type: 'text',
|
|
211
|
+
placeholder: 'Component Name'
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
});
|
|
216
|
+
appendElement(body, compform);
|
|
217
|
+
|
|
218
|
+
fetchElement('.componentInp').focus();
|
|
219
|
+
//setTimeout(() => result.remove(), 500);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
appendElement(body, result);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
async function copyText(txt, path) {
|
|
228
|
+
// await navigator.clipboard.writeText(txt);
|
|
229
|
+
try {
|
|
230
|
+
const res = await fetch('/addcomponent', {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: {
|
|
233
|
+
"Content-Type": 'application/json'
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({ component: txt, path: `${config.componentdir}/${path}` })
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
if (!res.ok) console.error('wrong');
|
|
239
|
+
|
|
240
|
+
const data = await res.json()
|
|
241
|
+
console.log(data);
|
|
242
|
+
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
console.error(err);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
export function fetchElement(param) {
|
|
254
|
+
return document.querySelector(param);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function appendElement(parent, child) {
|
|
258
|
+
parent.appendChild(child);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function createElement(type, classname) {
|
|
262
|
+
let element = document.createElement(type);
|
|
263
|
+
if (classname)
|
|
264
|
+
element.classList.add(...classname);
|
|
265
|
+
return element;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function insertText(element, text) {
|
|
269
|
+
element.innerText = text;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function replaceElement(original, New) {
|
|
273
|
+
original.replaceWith(New);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function gardener(Dom) {
|
|
277
|
+
|
|
278
|
+
if (Dom.nodeType === 1) return Dom;
|
|
279
|
+
// detect if this is an SVG element
|
|
280
|
+
const isSVG = [
|
|
281
|
+
'svg', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'g', 'defs', 'clipPath', 'use'
|
|
282
|
+
].includes(Dom.t);
|
|
283
|
+
|
|
284
|
+
// create element accordingly
|
|
285
|
+
let element;
|
|
286
|
+
|
|
287
|
+
if (isSVG) {
|
|
288
|
+
element = document.createElementNS('http://www.w3.org/2000/svg', Dom.t);
|
|
289
|
+
if (Dom.cn)
|
|
290
|
+
element.classList.add(...Dom.cn);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
element = createElement(Dom.t, Dom.cn);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// text content (skip for SVG like <path>)
|
|
297
|
+
if (Dom.txt) {
|
|
298
|
+
insertText(element, Dom.txt);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// apply attributes safely
|
|
302
|
+
const propertyNames = new Set([
|
|
303
|
+
'value', 'selected', 'muted', 'disabled',
|
|
304
|
+
'selectedIndex', 'volume', // etc.
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
if (Dom.attr) {
|
|
308
|
+
for (const [key, value] of Object.entries(Dom.attr)) {
|
|
309
|
+
if (isSVG || key.startsWith('data-') || key.startsWith('aria-')) {
|
|
310
|
+
element.setAttribute(key, value);
|
|
311
|
+
} else if (key in element && !propertyNames.has(key)) {
|
|
312
|
+
// Prefer property for known safe cases
|
|
313
|
+
try { element[key] = value === '' ? true : value; } catch (e) { element.setAttribute(key, value); }
|
|
314
|
+
} else {
|
|
315
|
+
element.setAttribute(key, value);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (Dom.events) {
|
|
321
|
+
Object.entries(Dom.events).forEach(([eventName, handler]) => {
|
|
322
|
+
element.addEventListener(eventName, handler);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// recursively handle children
|
|
327
|
+
if (Dom.children) {
|
|
328
|
+
Dom.children.forEach(child => appendElement(element, gardener(child)));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return element;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function parser(element, isParent = true) {
|
|
335
|
+
if (typeof element === 'string') {
|
|
336
|
+
// If user passes raw HTML string
|
|
337
|
+
const temp = document.createElement('div');
|
|
338
|
+
temp.innerHTML = element.trim();
|
|
339
|
+
element = temp.firstElementChild;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const obj = {
|
|
343
|
+
t: element.tagName.toLowerCase(),
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// add classes if present
|
|
347
|
+
if (element.classList.length) {
|
|
348
|
+
obj.cn = Array.from(element.classList);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// add attributes if present
|
|
352
|
+
const attrs = {};
|
|
353
|
+
for (const attr of element.attributes) {
|
|
354
|
+
if (attr.name !== 'class') attrs[attr.name] = attr.value;
|
|
355
|
+
}
|
|
356
|
+
if (Object.keys(attrs).length) obj.attr = attrs;
|
|
357
|
+
// add text content (only if no children)
|
|
358
|
+
if (element.childNodes.length === 1 && element.firstChild.nodeType === Node.TEXT_NODE) {
|
|
359
|
+
obj.txt = element.textContent.trim();
|
|
360
|
+
|
|
361
|
+
if (isParent) {
|
|
362
|
+
parserWindow(JSON.stringify(obj))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return obj;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// add children recursively
|
|
369
|
+
const children = [];
|
|
370
|
+
for (const child of element.children) {
|
|
371
|
+
children.push(parser(child, false));
|
|
372
|
+
}
|
|
373
|
+
if (children.length) obj.children = children;
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
if (isParent) {
|
|
377
|
+
parserWindow(JSON.stringify(obj))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return obj
|
|
381
|
+
//Let Browser do the migration from html to json and then use copy paste
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function imagePreloader(images) {
|
|
385
|
+
const body = fetchElement('body')
|
|
386
|
+
images.forEach(entry => {
|
|
387
|
+
appendElement(body, gardener({
|
|
388
|
+
t: 'img',
|
|
389
|
+
cn: ['preloaderimage'],
|
|
390
|
+
attr: {
|
|
391
|
+
src: entry,
|
|
392
|
+
alt: entry
|
|
393
|
+
}
|
|
394
|
+
}));
|
|
395
|
+
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
const images = document.querySelectorAll('.preloaderimage');
|
|
398
|
+
images.forEach(entry => { entry.style.display = 'none' });
|
|
399
|
+
}, 0)
|
|
400
|
+
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { parser, fetchElement, replaceElement, gardener, appendElement } from './gardener.js'
|
|
2
|
+
|
|
3
|
+
const body = fetchElement('#body')
|
|
4
|
+
|
|
5
|
+
nextPagehandler();
|
|
6
|
+
pageloader();
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
function nextPagehandler() {
|
|
10
|
+
const anchor = document.querySelectorAll('a')
|
|
11
|
+
anchor.forEach(link => {
|
|
12
|
+
|
|
13
|
+
link.addEventListener('click', (e) => {
|
|
14
|
+
|
|
15
|
+
//event delegation
|
|
16
|
+
const link = e.target.closest('a');
|
|
17
|
+
if (!link) return;
|
|
18
|
+
if (link.target === '_blank') return;
|
|
19
|
+
//event delegation
|
|
20
|
+
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
appendElement(body, gardener({
|
|
23
|
+
t: 'div',
|
|
24
|
+
cn: ['tempnpdiv', 'top-0', 'left-[100vw]', 'absolute', 'h-screen', 'w-screen', 'shadow-[30px_0_60px_15px_rgb(0,0,0)]'],
|
|
25
|
+
}))
|
|
26
|
+
const width = window.innerWidth
|
|
27
|
+
console.log(width)
|
|
28
|
+
body.style.transition = '.2s';
|
|
29
|
+
body.style.transform = `translateX(-${width}px)`
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
window.location.href = link.href
|
|
32
|
+
}, 200)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
window.addEventListener('pagehide', () => {
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
body.style.transform = 'translateX(0px)';
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
fetchElement('.tempnpdiv').remove()
|
|
41
|
+
|
|
42
|
+
}, 200)
|
|
43
|
+
}, 200);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function pageloader() {
|
|
49
|
+
const loader = fetchElement('.loader');
|
|
50
|
+
loader.style.transition = '.4s';
|
|
51
|
+
loader.style.opacity = '0';
|
|
52
|
+
setTimeout(() => loader.remove(), 400)
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
//console.log('hellooo');
|
|
56
|
+
//parser(fetchElement('.hero'));
|
|
57
|
+
//parser(fetchElement('nav'));
|