~repos /atoms-element

#js

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
Files changed (1) hide show
  1. index.js +161 -113
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 / 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
- hydrate(child);
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(ssrAttributes) {
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
- this.initState();
863
+ this.initState();
807
- this.initAttrs();
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 = isBrowser ? this.getAttribute(key.toLowerCase()) : this.ssrAttributes[key.toLowerCase()];
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
- if (isBrowser) {
899
+ this.initAttrs();
900
+ this.initState();
843
- this.update();
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 missingClassList = template.values
971
+ const classList = getClassList(template);
914
- .filter((item) => {
915
- if (typeof item === 'string') {
916
- const list = item.split(' ');
917
- return list.filter((cls) => classLookup[cls] && !this.prevClassList.includes(cls)).length > 0;
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,