~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.
42ef01f2
—
Peter John 4 years ago
improve element
- element.js +27 -30
- element.test.js +9 -7
- example/app-counter.js +10 -0
- example/index.html +10 -0
- example/index.js +4 -0
- package.json +1 -0
element.js
CHANGED
|
@@ -240,6 +240,15 @@ const validator = (type, validate) => (innerType) => {
|
|
|
240
240
|
common.__default = fnOrValue;
|
|
241
241
|
return common;
|
|
242
242
|
};
|
|
243
|
+
common.compute = (...args) => {
|
|
244
|
+
const fn = args[args.length - 1];
|
|
245
|
+
const deps = args.slice(0, args.length - 1);
|
|
246
|
+
common.__compute = {
|
|
247
|
+
fn,
|
|
248
|
+
deps,
|
|
249
|
+
};
|
|
250
|
+
return common;
|
|
251
|
+
};
|
|
243
252
|
return common;
|
|
244
253
|
};
|
|
245
254
|
|
|
@@ -266,15 +275,6 @@ export const array = validator('array', (innerType, context, data) => {
|
|
|
266
275
|
}
|
|
267
276
|
});
|
|
268
277
|
|
|
269
|
-
// const depsChanged = (prev, next) => prev == null || next.some((f, i) => !Object.is(f, prev[i]));
|
|
270
|
-
// useEffect(handler, deps) {
|
|
271
|
-
// const index = this.hooks.currentCursor++;
|
|
272
|
-
// if (!deps || depsChanged(this.hooks.deps[index], deps)) {
|
|
273
|
-
// this.hooks.deps[index] = deps || [];
|
|
274
|
-
// this.hooks.effects[index] = handler;
|
|
275
|
-
// }
|
|
276
|
-
// }
|
|
277
|
-
|
|
278
278
|
const normalizeCss = `html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}`;
|
|
279
279
|
const fifo = (q) => q.shift();
|
|
280
280
|
const filo = (q) => q.pop();
|
|
@@ -320,7 +320,6 @@ export default class AtomsElement extends BaseElement {
|
|
|
320
320
|
this._dirty = false;
|
|
321
321
|
this._connected = false;
|
|
322
322
|
this._state = {};
|
|
323
|
-
this._effects = {};
|
|
324
323
|
this.ssrAttributes = ssrAttributes;
|
|
325
324
|
this.config = isBrowser ? window.config : global.config;
|
|
326
325
|
this.location = isBrowser ? window.location : global.location;
|
|
@@ -335,10 +334,6 @@ export default class AtomsElement extends BaseElement {
|
|
|
335
334
|
}
|
|
336
335
|
disconnectedCallback() {
|
|
337
336
|
this._connected = false;
|
|
338
|
-
// let cleanup;
|
|
339
|
-
// while ((cleanup = this.hooks.cleanup.shift())) {
|
|
340
|
-
// cleanup();
|
|
341
|
-
// }
|
|
342
337
|
}
|
|
343
338
|
|
|
344
339
|
attributeChangedCallback(key, oldValue, newValue) {
|
|
@@ -360,7 +355,6 @@ export default class AtomsElement extends BaseElement {
|
|
|
360
355
|
return;
|
|
361
356
|
}
|
|
362
357
|
this.renderTemplate();
|
|
363
|
-
this.enqueueEffects();
|
|
364
358
|
this._dirty = false;
|
|
365
359
|
}
|
|
366
360
|
|
|
@@ -374,23 +368,10 @@ export default class AtomsElement extends BaseElement {
|
|
|
374
368
|
q.push(this) === 1 && run();
|
|
375
369
|
}
|
|
376
370
|
|
|
377
|
-
enqueueEffects() {
|
|
378
|
-
this.batch(task, filo, () => this._flushEffects());
|
|
379
|
-
}
|
|
380
|
-
|
|
381
371
|
enqueueUpdate() {
|
|
382
372
|
this.batch(microtask, fifo, () => this._performUpdate());
|
|
383
373
|
}
|
|
384
374
|
|
|
385
|
-
_flushEffects() {
|
|
386
|
-
Object.keys(this.constructor.effects).forEach((key) => {
|
|
387
|
-
const effect = this.constructor.effects[key];
|
|
388
|
-
// if (!effect.deps || depsChanged(, effect.deps)) {
|
|
389
|
-
// }
|
|
390
|
-
effect.callback(this.attrs, this.state);
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
375
|
get attrs() {
|
|
395
376
|
return Object.keys(this.constructor.attrTypes).reduceRight((acc, key) => {
|
|
396
377
|
const attrType = this.constructor.attrTypes[key];
|
|
@@ -409,14 +390,30 @@ export default class AtomsElement extends BaseElement {
|
|
|
409
390
|
this._state[key] = typeof stateType.__default === 'function' ? stateType.__default(this.attrs, this._state) : stateType.__default;
|
|
410
391
|
}
|
|
411
392
|
acc[key] = this._state[key];
|
|
412
|
-
acc[`set${key[0].toUpperCase()}${key.slice(1)}`] = (v) =>
|
|
393
|
+
acc[`set${key[0].toUpperCase()}${key.slice(1)}`] = (v) => {
|
|
413
|
-
|
|
394
|
+
// TODO: check type on set
|
|
395
|
+
this._state[key] = typeof v === 'function' ? v(this._state[key]) : v;
|
|
414
396
|
this.update();
|
|
415
397
|
};
|
|
416
398
|
return acc;
|
|
417
399
|
}, {});
|
|
418
400
|
}
|
|
419
401
|
|
|
402
|
+
get computed() {
|
|
403
|
+
return Object.keys(this.constructor.computedTypes).reduceRight((acc, key) => {
|
|
404
|
+
const type = this.constructor.computedTypes[key];
|
|
405
|
+
const state = this.state;
|
|
406
|
+
const values = type.__compute.deps.reduce((acc, key) => {
|
|
407
|
+
if (typeof state[key] !== undefined) {
|
|
408
|
+
acc.push(state[key]);
|
|
409
|
+
}
|
|
410
|
+
return acc;
|
|
411
|
+
}, []);
|
|
412
|
+
acc[key] = type.__compute.fn(...values);
|
|
413
|
+
return acc;
|
|
414
|
+
}, {});
|
|
415
|
+
}
|
|
416
|
+
|
|
420
417
|
renderTemplate() {
|
|
421
418
|
const template = this.render();
|
|
422
419
|
const result = render(template, this);
|
element.test.js
CHANGED
|
@@ -250,13 +250,12 @@ test('AtomsElement', async () => {
|
|
|
250
250
|
count: number().required().default(0),
|
|
251
251
|
};
|
|
252
252
|
|
|
253
|
-
static
|
|
253
|
+
static computedTypes = {
|
|
254
|
-
|
|
254
|
+
sum: number()
|
|
255
|
+
.required()
|
|
255
|
-
|
|
256
|
+
.compute('count', (count) => {
|
|
256
|
-
|
|
257
|
+
return count + 10;
|
|
257
|
-
},
|
|
258
|
+
}),
|
|
258
|
-
deps: ['attrs.perPage', 'state.count'],
|
|
259
|
-
},
|
|
260
259
|
};
|
|
261
260
|
|
|
262
261
|
static styles = css`
|
|
@@ -271,10 +270,12 @@ test('AtomsElement', async () => {
|
|
|
271
270
|
address: { street },
|
|
272
271
|
} = this.attrs;
|
|
273
272
|
const { count, setCount } = this.state;
|
|
273
|
+
const { sum } = this.computed;
|
|
274
274
|
return html`
|
|
275
275
|
<div perPage=${perPage}>
|
|
276
276
|
<p>street: ${street}</p>
|
|
277
277
|
<p>count: ${count}</p>
|
|
278
|
+
<p>sum: ${sum}</p>
|
|
278
279
|
${this.renderItem()}
|
|
279
280
|
</div>
|
|
280
281
|
`;
|
|
@@ -295,6 +296,7 @@ test('AtomsElement', async () => {
|
|
|
295
296
|
<div perPage="1">
|
|
296
297
|
<p>street: 123</p>
|
|
297
298
|
<p>count: 0</p>
|
|
299
|
+
<p>sum: 10</p>
|
|
298
300
|
<div><p>render item 1</p></div>
|
|
299
301
|
</div>
|
|
300
302
|
|
example/app-counter.js
CHANGED
|
@@ -16,6 +16,14 @@ class Counter extends AtomsElement {
|
|
|
16
16
|
.default((attrs) => attrs.meta?.start || 0),
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
static computedTypes = {
|
|
20
|
+
sum: number()
|
|
21
|
+
.required()
|
|
22
|
+
.compute('count', (count) => {
|
|
23
|
+
return count + 10;
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
|
|
19
27
|
static styles = css`
|
|
20
28
|
.container {
|
|
21
29
|
}
|
|
@@ -24,6 +32,7 @@ class Counter extends AtomsElement {
|
|
|
24
32
|
render() {
|
|
25
33
|
const { name } = this.attrs;
|
|
26
34
|
const { count, setCount } = this.state;
|
|
35
|
+
const { sum } = this.computed;
|
|
27
36
|
|
|
28
37
|
return html`
|
|
29
38
|
<div>
|
|
@@ -32,6 +41,7 @@ class Counter extends AtomsElement {
|
|
|
32
41
|
<button @click=${() => setCount((v) => v - 1)}>-</button>
|
|
33
42
|
<div class="mx-20">
|
|
34
43
|
<h1 class="text-1xl">${count}</h1>
|
|
44
|
+
<h1 class="text-1xl">${sum}</h1>
|
|
35
45
|
</div>
|
|
36
46
|
<button @click=${() => setCount((v) => v + 1)}>+</button>
|
|
37
47
|
</div>
|
example/index.html
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<body>
|
|
3
|
+
<div>
|
|
4
|
+
<app-counter name="1"></app-counter>
|
|
5
|
+
</div>
|
|
6
|
+
<script type="module">
|
|
7
|
+
import './app-counter.js';
|
|
8
|
+
</script>
|
|
9
|
+
</body>
|
|
10
|
+
</html>
|
example/index.js
CHANGED
|
@@ -7,6 +7,10 @@ class CounterPage extends Page {
|
|
|
7
7
|
return '/counter';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
datapaths() {
|
|
11
|
+
return '/data/items/**';
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
styles() {
|
|
11
15
|
return css``;
|
|
12
16
|
}
|
package.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"node": ">=14.0.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
|
+
"example": "npx serve ./",
|
|
36
37
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|