~repos /atoms-element
git clone https://pyrossh.dev/repos/atoms-element.git
A simple web component library for defining your custom elements. It works on both client and server.
5e153bcc
—
Peter John 4 years ago
fix vars issues
index.js
CHANGED
|
@@ -4,6 +4,32 @@ const isBrowser = typeof window !== 'undefined';
|
|
|
4
4
|
export { html, isBrowser };
|
|
5
5
|
|
|
6
6
|
const hyphenate = (s) => s.replace(/[A-Z]|^ms/g, '-$&').toLowerCase();
|
|
7
|
+
const percent = (v) => (v * 100).toFixed(2) + '%';
|
|
8
|
+
const createStyle = (...kvs) => {
|
|
9
|
+
const style = {};
|
|
10
|
+
for (let i = 0; i < kvs.length; i += 2) {
|
|
11
|
+
style[hyphenate(kvs[i])] = kvs[i + 1];
|
|
12
|
+
}
|
|
13
|
+
return style;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const mapApply = (obj) =>
|
|
17
|
+
Object.keys(obj.keys).reduce((acc, key) => {
|
|
18
|
+
Object.keys(obj.values).map((vkey) => {
|
|
19
|
+
const suffix = vkey ? '-' + vkey : '';
|
|
20
|
+
const className = `${key}${suffix}`;
|
|
21
|
+
if (Array.isArray(obj.keys[key])) {
|
|
22
|
+
const args = [];
|
|
23
|
+
obj.keys[key].forEach((kk) => {
|
|
24
|
+
args.push(kk, obj.values[vkey]);
|
|
25
|
+
});
|
|
26
|
+
acc[className] = createStyle(...args);
|
|
27
|
+
} else {
|
|
28
|
+
acc[className] = createStyle(obj.keys[key], obj.values[vkey]);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return acc;
|
|
32
|
+
}, {});
|
|
7
33
|
|
|
8
34
|
export const css = (obj, isChild = false, indent = '') => {
|
|
9
35
|
const cssText = Object.keys(obj).reduce((acc, key) => {
|
|
@@ -20,6 +46,47 @@ export const css = (obj, isChild = false, indent = '') => {
|
|
|
20
46
|
return cssText;
|
|
21
47
|
};
|
|
22
48
|
|
|
49
|
+
export const getClassList = (template) => {
|
|
50
|
+
const classes = template.strings
|
|
51
|
+
.reduce((acc, item) => {
|
|
52
|
+
const matches = item.match(/class=(?:["']\W+\s*(?:\w+)\()?["']([^'"]+)['"]/gim);
|
|
53
|
+
if (matches) {
|
|
54
|
+
matches.forEach((matched) => {
|
|
55
|
+
acc += matched.replace('class="', '').replace('"', '') + ' ';
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return acc;
|
|
59
|
+
}, '')
|
|
60
|
+
.split(' ')
|
|
61
|
+
.filter((it) => it !== '');
|
|
62
|
+
template.values.forEach((item) => {
|
|
63
|
+
if (typeof item === 'string') {
|
|
64
|
+
const list = item.split(' ');
|
|
65
|
+
return classes.push(...list.filter((cls) => classLookup[cls]));
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
});
|
|
69
|
+
return classes;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const getStyleSheet = (classList) => {
|
|
73
|
+
let styleSheet = ``;
|
|
74
|
+
classList.forEach((cls) => {
|
|
75
|
+
const item = classLookup[cls];
|
|
76
|
+
if (item) {
|
|
77
|
+
const className = cls.replace(`/`, `\\/`);
|
|
78
|
+
styleSheet += `
|
|
79
|
+
.${className} {
|
|
80
|
+
${Object.keys(item)
|
|
81
|
+
.map((key) => `${key}: ${item[key]};`)
|
|
82
|
+
.join('\n')}
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return styleSheet;
|
|
88
|
+
};
|
|
89
|
+
|
|
23
90
|
const colors = {
|
|
24
91
|
keys: {
|
|
25
92
|
bg: 'backgroundColor',
|
|
@@ -227,59 +294,72 @@ const sizes = {
|
|
|
227
294
|
maxw: 'maxWidth',
|
|
228
295
|
},
|
|
229
296
|
values: {
|
|
297
|
+
0: '0px',
|
|
298
|
+
px: '1px',
|
|
299
|
+
0.5: '0.125rem',
|
|
300
|
+
1: '0.25rem',
|
|
301
|
+
1.5: '0.375rem',
|
|
302
|
+
2: '0.5rem',
|
|
303
|
+
2.5: '0.625rem',
|
|
304
|
+
3: '0.75rem',
|
|
305
|
+
3.5: '0.875rem',
|
|
306
|
+
4: '1rem',
|
|
307
|
+
5: '1.25rem',
|
|
308
|
+
6: '1.5rem',
|
|
309
|
+
7: '1.75rem',
|
|
310
|
+
8: '2rem',
|
|
311
|
+
9: '2.25rem',
|
|
312
|
+
10: '2.5rem',
|
|
313
|
+
11: '2.75rem',
|
|
314
|
+
12: '3rem',
|
|
315
|
+
14: '3.5rem',
|
|
316
|
+
16: '4rem',
|
|
317
|
+
20: '5rem',
|
|
318
|
+
24: '6rem',
|
|
319
|
+
28: '7rem',
|
|
320
|
+
32: '8rem',
|
|
321
|
+
36: '9rem',
|
|
322
|
+
40: '10rem',
|
|
323
|
+
44: '11rem',
|
|
324
|
+
48: '12rem',
|
|
325
|
+
52: '13rem',
|
|
326
|
+
56: '14rem',
|
|
327
|
+
60: '15rem',
|
|
328
|
+
64: '16rem',
|
|
329
|
+
72: '18rem',
|
|
330
|
+
80: '20rem',
|
|
331
|
+
96: '24rem',
|
|
332
|
+
auto: 'auto',
|
|
333
|
+
min: 'min-content',
|
|
334
|
+
max: 'max-content',
|
|
230
|
-
'1/2': 1 /
|
|
335
|
+
'1/2': percent(1 / 2),
|
|
231
|
-
'1/4': 1 / 4,
|
|
336
|
+
'1/4': percent(1 / 4),
|
|
232
|
-
'2/4': 2 / 4,
|
|
337
|
+
'2/4': percent(2 / 4),
|
|
233
|
-
'3/4': 3 / 4,
|
|
338
|
+
'3/4': percent(3 / 4),
|
|
234
|
-
'1/5': 1 / 5,
|
|
339
|
+
'1/5': percent(1 / 5),
|
|
235
|
-
'2/5': 2 / 5,
|
|
340
|
+
'2/5': percent(2 / 5),
|
|
236
|
-
'3/5': 3 / 5,
|
|
341
|
+
'3/5': percent(3 / 5),
|
|
237
|
-
'4/5': 4 / 5,
|
|
342
|
+
'4/5': percent(4 / 5),
|
|
238
|
-
'1/6': 1 / 6,
|
|
343
|
+
'1/6': percent(1 / 6),
|
|
239
|
-
'2/6': 2 / 6,
|
|
344
|
+
'2/6': percent(2 / 6),
|
|
240
|
-
'3/6': 3 / 6,
|
|
345
|
+
'3/6': percent(3 / 6),
|
|
241
|
-
'4/6': 4 / 6,
|
|
346
|
+
'4/6': percent(4 / 6),
|
|
242
|
-
'5/6': 5 / 6,
|
|
347
|
+
'5/6': percent(5 / 6),
|
|
243
|
-
'1/12': 1 / 12,
|
|
348
|
+
'1/12': percent(1 / 12),
|
|
244
|
-
'2/12': 2 / 12,
|
|
349
|
+
'2/12': percent(2 / 12),
|
|
245
|
-
'3/12': 3 / 12,
|
|
350
|
+
'3/12': percent(3 / 12),
|
|
246
|
-
'4/12': 4 / 12,
|
|
351
|
+
'4/12': percent(4 / 12),
|
|
247
|
-
'5/12': 5 / 12,
|
|
352
|
+
'5/12': percent(5 / 12),
|
|
248
|
-
'6/12': 6 / 12,
|
|
353
|
+
'6/12': percent(6 / 12),
|
|
249
|
-
'7/12': 7 / 12,
|
|
354
|
+
'7/12': percent(7 / 12),
|
|
250
|
-
'8/12': 8 / 12,
|
|
355
|
+
'8/12': percent(8 / 12),
|
|
251
|
-
'9/12': 9 / 12,
|
|
356
|
+
'9/12': percent(9 / 12),
|
|
252
|
-
'10/12': 10 / 12,
|
|
357
|
+
'10/12': percent(10 / 12),
|
|
253
|
-
'11/12': 11 / 12,
|
|
358
|
+
'11/12': percent(11 / 12),
|
|
254
|
-
full: 1,
|
|
359
|
+
full: percent(1),
|
|
255
360
|
},
|
|
256
361
|
};
|
|
257
362
|
|
|
258
|
-
const createStyle = (...kvs) => {
|
|
259
|
-
const style = {};
|
|
260
|
-
for (let i = 0; i < kvs.length; i += 2) {
|
|
261
|
-
style[hyphenate(kvs[i])] = kvs[i + 1];
|
|
262
|
-
}
|
|
263
|
-
return style;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const mapApply = (obj) =>
|
|
267
|
-
Object.keys(obj.keys).reduce((acc, key) => {
|
|
268
|
-
Object.keys(obj.values).map((vkey) => {
|
|
269
|
-
const suffix = vkey ? '-' + vkey : '';
|
|
270
|
-
if (Array.isArray(obj.keys[key])) {
|
|
271
|
-
const args = [];
|
|
272
|
-
obj.keys[key].forEach((kk) => {
|
|
273
|
-
args.push(kk, obj.values[vkey]);
|
|
274
|
-
});
|
|
275
|
-
acc[key + suffix] = createStyle(...args);
|
|
276
|
-
} else {
|
|
277
|
-
acc[key + suffix] = createStyle(obj.keys[key], obj.values[vkey]);
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
return acc;
|
|
281
|
-
}, {});
|
|
282
|
-
|
|
283
363
|
const classLookup = {
|
|
284
364
|
flex: createStyle('display', 'flex'),
|
|
285
365
|
block: createStyle('display', 'block'),
|
|
@@ -358,6 +438,8 @@ const classLookup = {
|
|
|
358
438
|
'select-text': createStyle('user-select', 'text'),
|
|
359
439
|
'select-all': createStyle('user-select', 'all'),
|
|
360
440
|
'select-auto': createStyle('user-select', 'auto'),
|
|
441
|
+
'w-screen': '100vw',
|
|
442
|
+
'h-screen': '100vh',
|
|
361
443
|
...mapApply(sizes),
|
|
362
444
|
...mapApply(spacing),
|
|
363
445
|
...mapApply(colors),
|
|
@@ -365,38 +447,6 @@ const classLookup = {
|
|
|
365
447
|
...mapApply(radius),
|
|
366
448
|
};
|
|
367
449
|
|
|
368
|
-
export const getClassList = (template) => {
|
|
369
|
-
return template.strings
|
|
370
|
-
.reduce((acc, item) => {
|
|
371
|
-
const matches = item.match(/class=(?:["']\W+\s*(?:\w+)\()?["']([^'"]+)['"]/gim);
|
|
372
|
-
if (matches) {
|
|
373
|
-
matches.forEach((matched) => {
|
|
374
|
-
acc += matched.replace('class="', '').replace('"', '') + ' ';
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return acc;
|
|
378
|
-
}, '')
|
|
379
|
-
.split(' ')
|
|
380
|
-
.filter((it) => it !== '');
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
export const getStyleSheet = (classList) => {
|
|
384
|
-
let styleSheet = ``;
|
|
385
|
-
classList.forEach((k) => {
|
|
386
|
-
const item = classLookup[k];
|
|
387
|
-
if (item) {
|
|
388
|
-
styleSheet += `
|
|
389
|
-
.${k} {
|
|
390
|
-
${Object.keys(item)
|
|
391
|
-
.map((key) => `${key}: ${item[key]};`)
|
|
392
|
-
.join('\n')}
|
|
393
|
-
}
|
|
394
|
-
`;
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
return styleSheet;
|
|
398
|
-
};
|
|
399
|
-
|
|
400
450
|
const lastAttributeNameRegex =
|
|
401
451
|
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
402
452
|
const tagRE = /<[a-zA-Z0-9\-\!\/](?:"[^"]*"|'[^']*'|[^'">])*>/g;
|
|
@@ -587,17 +637,24 @@ const stringifyHtml = (buff, doc) => {
|
|
|
587
637
|
};
|
|
588
638
|
|
|
589
639
|
const hydrate = (node) => {
|
|
640
|
+
const Clazz = AtomsElement.getElement(node.name);
|
|
641
|
+
if (Clazz) {
|
|
642
|
+
const newAttrs = {};
|
|
643
|
+
Object.keys(node.attrs).forEach((key) => {
|
|
644
|
+
const attrType = Clazz.attrTypes[key];
|
|
645
|
+
if (attrType) {
|
|
646
|
+
newAttrs[key] = attrType.parse(node.attrs[key]);
|
|
647
|
+
} else {
|
|
648
|
+
newAttrs[key] = node.attrs[key];
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
const instance = new Clazz(newAttrs);
|
|
652
|
+
const res = instance.renderTemplate();
|
|
653
|
+
node.children = parseHtml(res);
|
|
654
|
+
}
|
|
590
655
|
if (node.children) {
|
|
591
656
|
for (const child of node.children) {
|
|
592
|
-
const Clazz = AtomsElement.getElement(child.name);
|
|
593
|
-
if (Clazz) {
|
|
594
|
-
const instance = new Clazz(child.attrs);
|
|
595
|
-
const res = instance.renderTemplate();
|
|
596
|
-
child.children.push(...parseHtml(res));
|
|
597
|
-
}
|
|
598
|
-
if (child.children) {
|
|
599
|
-
|
|
657
|
+
hydrate(child);
|
|
600
|
-
}
|
|
601
658
|
}
|
|
602
659
|
}
|
|
603
660
|
};
|
|
@@ -793,24 +850,24 @@ export class AtomsElement extends BaseElement {
|
|
|
793
850
|
return Object.keys(this.attrTypes).map((k) => k.toLowerCase());
|
|
794
851
|
}
|
|
795
852
|
|
|
796
|
-
constructor(
|
|
853
|
+
constructor(attrs) {
|
|
797
854
|
super();
|
|
798
|
-
this.ssrAttributes = ssrAttributes;
|
|
799
855
|
this._dirty = false;
|
|
800
856
|
this._connected = false;
|
|
801
|
-
this.attrs = {};
|
|
857
|
+
this.attrs = attrs || {};
|
|
802
858
|
this.state = {};
|
|
803
859
|
this.config = isBrowser ? window.config : global.config;
|
|
804
860
|
this.location = isBrowser ? window.location : global.location;
|
|
805
861
|
this.prevClassList = [];
|
|
862
|
+
if (!isBrowser) {
|
|
806
|
-
|
|
863
|
+
this.initState();
|
|
807
|
-
|
|
864
|
+
}
|
|
808
865
|
}
|
|
809
866
|
|
|
810
867
|
initAttrs() {
|
|
811
868
|
Object.keys(this.constructor.attrTypes).forEach((key) => {
|
|
812
869
|
const attrType = this.constructor.attrTypes[key];
|
|
813
|
-
const newValue =
|
|
870
|
+
const newValue = this.getAttribute(key.toLowerCase());
|
|
814
871
|
const data = attrType.parse(newValue);
|
|
815
872
|
attrType.validate(`<${this.constructor.name}> ${key}`, data);
|
|
816
873
|
this.attrs[key] = data;
|
|
@@ -839,9 +896,9 @@ export class AtomsElement extends BaseElement {
|
|
|
839
896
|
|
|
840
897
|
connectedCallback() {
|
|
841
898
|
this._connected = true;
|
|
842
|
-
|
|
899
|
+
this.initAttrs();
|
|
900
|
+
this.initState();
|
|
843
|
-
|
|
901
|
+
this.update();
|
|
844
|
-
}
|
|
845
902
|
}
|
|
846
903
|
disconnectedCallback() {
|
|
847
904
|
this._connected = false;
|
|
@@ -900,6 +957,7 @@ export class AtomsElement extends BaseElement {
|
|
|
900
957
|
}
|
|
901
958
|
|
|
902
959
|
renderTemplate() {
|
|
960
|
+
console.log('render');
|
|
903
961
|
const template = this.render();
|
|
904
962
|
if (isBrowser) {
|
|
905
963
|
if (!this.styleElement) {
|
|
@@ -910,15 +968,8 @@ export class AtomsElement extends BaseElement {
|
|
|
910
968
|
this.styleElement = document.createElement('style');
|
|
911
969
|
this.appendChild(this.styleElement).textContent = styleSheet;
|
|
912
970
|
} else {
|
|
913
|
-
const
|
|
971
|
+
const classList = getClassList(template);
|
|
914
|
-
.filter((item) => {
|
|
915
|
-
if (typeof item === 'string') {
|
|
916
|
-
const list = item.split(' ');
|
|
917
|
-
|
|
972
|
+
const missingClassList = classList.filter((cls) => !this.prevClassList.includes(cls));
|
|
918
|
-
}
|
|
919
|
-
return false;
|
|
920
|
-
})
|
|
921
|
-
.reduce((acc, str) => acc.concat(str.split(' ')), []);
|
|
922
973
|
if (missingClassList.length > 0) {
|
|
923
974
|
const styleSheet = getStyleSheet(missingClassList);
|
|
924
975
|
this.styleElement.textContent += '\n' + styleSheet;
|
|
@@ -939,7 +990,7 @@ export class AtomsElement extends BaseElement {
|
|
|
939
990
|
}
|
|
940
991
|
}
|
|
941
992
|
}
|
|
942
|
-
export const getConfig = () => (isBrowser ? window.props.config : global.config);
|
|
993
|
+
export const getConfig = () => (isBrowser ? window.props.config : global.props.config);
|
|
943
994
|
export const getLocation = () => (isBrowser ? window.location : global.location);
|
|
944
995
|
|
|
945
996
|
export const createElement = ({ name, attrTypes, stateTypes, computedTypes, render }) => {
|
|
@@ -952,9 +1003,6 @@ export const createElement = ({ name, attrTypes, stateTypes, computedTypes, rend
|
|
|
952
1003
|
|
|
953
1004
|
static computedTypes = computedTypes ? computedTypes() : {};
|
|
954
1005
|
|
|
955
|
-
constructor(ssrAttributes) {
|
|
956
|
-
super(ssrAttributes);
|
|
957
|
-
}
|
|
958
1006
|
render() {
|
|
959
1007
|
return render({
|
|
960
1008
|
attrs: this.attrs,
|