~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.
897b1286
—
Peter John 4 years ago
improve page
- element.js +109 -66
- page.d.ts +7 -9
- page.test.js +11 -9
element.js
CHANGED
|
@@ -5,7 +5,7 @@ export { html, isBrowser };
|
|
|
5
5
|
|
|
6
6
|
const constructionToken = Symbol();
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
class CSSResult {
|
|
9
9
|
constructor(cssText, safeToken) {
|
|
10
10
|
if (safeToken !== constructionToken) {
|
|
11
11
|
throw new Error('CSSResult is not constructable. Use `unsafeCSS` or `css` instead.');
|
|
@@ -53,6 +53,23 @@ export const css = (strings, ...values) => {
|
|
|
53
53
|
return new CSSResult(cssText, constructionToken);
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
// const cssSymbol = Symbol();
|
|
57
|
+
// const hasCSSSymbol = (value: unknown): value is HasCSSSymbol => {
|
|
58
|
+
// return value && (value as HasCSSSymbol)[cssSymbol] != null;
|
|
59
|
+
// };
|
|
60
|
+
// const resolve = (value: unknown): string => {
|
|
61
|
+
// if (typeof value === "number") return String(value);
|
|
62
|
+
// if (hasCSSSymbol(value)) return value[cssSymbol];
|
|
63
|
+
// throw new TypeError(`${value} is not supported type.`);
|
|
64
|
+
// };
|
|
65
|
+
// export const css = (strings: readonly string[], ...values: unknown[]) => ({
|
|
66
|
+
// [cssSymbol]: strings
|
|
67
|
+
// .slice(1)
|
|
68
|
+
// .reduce((acc, s, i) => acc + resolve(values[i]) + s, strings[0]),
|
|
69
|
+
// });
|
|
70
|
+
// export const unsafeCSS = (css: string) => ({ [cssSymbol]: css });
|
|
71
|
+
// root.appendChild(document.createElement("style")).textContent = cssStyle;
|
|
72
|
+
|
|
56
73
|
const lastAttributeNameRegex =
|
|
57
74
|
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
58
75
|
|
|
@@ -275,63 +292,7 @@ export const array = checkComplex('array', (innerType, context, data) => {
|
|
|
275
292
|
});
|
|
276
293
|
export const func = checkComplex('function', (innerType, context, data) => {});
|
|
277
294
|
|
|
278
|
-
// export const useReducer = (reducer, initialState) =>
|
|
279
|
-
// hooks({
|
|
280
|
-
// oncreate: (h, c, i) => [
|
|
281
|
-
// initialState,
|
|
282
|
-
// function dispatch(action) {
|
|
283
|
-
// const state = h.values[i][0];
|
|
284
|
-
// const nextState = reducer(state, action);
|
|
285
|
-
// if (!Object.is(state, nextState)) {
|
|
286
|
-
// h.values[i][0] = nextState;
|
|
287
|
-
// c.update();
|
|
288
|
-
// }
|
|
289
|
-
// },
|
|
290
|
-
// ],
|
|
291
|
-
// });
|
|
292
295
|
const depsChanged = (prev, next) => prev == null || next.some((f, i) => !Object.is(f, prev[i]));
|
|
293
|
-
export const useEffect = (handler, deps) =>
|
|
294
|
-
hooks({
|
|
295
|
-
onupdate(h, _, i) {
|
|
296
|
-
if (!deps || depsChanged(h.deps[i], deps)) {
|
|
297
|
-
h.deps[i] = deps || [];
|
|
298
|
-
h.effects[i] = handler;
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// createHooks(config) {
|
|
304
|
-
// const index = this.currentCursor++;
|
|
305
|
-
// if (this.hooks.values.length <= index && config.oncreate) {
|
|
306
|
-
// this.hooks.values[index] = config.oncreate(this.hooks, this, index);
|
|
307
|
-
// }
|
|
308
|
-
// if (config.onupdate) {
|
|
309
|
-
// this.hooks.values[index] = config.onupdate(this.hooks, this, index);
|
|
310
|
-
// }
|
|
311
|
-
// return this.hooks.values[index];
|
|
312
|
-
// }
|
|
313
|
-
//
|
|
314
|
-
// export const useLayoutEffect = (handler, deps) =>
|
|
315
|
-
// hooks({
|
|
316
|
-
// onupdate(h, _, i) {
|
|
317
|
-
// if (!deps || depsChanged(h.deps[i], deps)) {
|
|
318
|
-
// h.deps[i] = deps || [];
|
|
319
|
-
// h.layoutEffects[i] = handler;
|
|
320
|
-
// }
|
|
321
|
-
// },
|
|
322
|
-
// });
|
|
323
|
-
// export const useMemo = (fn, deps) =>
|
|
324
|
-
// hooks({
|
|
325
|
-
// onupdate(h, _, i) {
|
|
326
|
-
// let value = h.values[i];
|
|
327
|
-
// if (!deps || depsChanged(h.deps[i], deps)) {
|
|
328
|
-
// h.deps[i] = deps || [];
|
|
329
|
-
// value = fn();
|
|
330
|
-
// }
|
|
331
|
-
// return value;
|
|
332
|
-
// },
|
|
333
|
-
// });
|
|
334
|
-
// export const useCallback = (callback, deps) => useMemo(() => callback, deps);
|
|
335
296
|
|
|
336
297
|
const batch = (runner, pick, callback) => {
|
|
337
298
|
const q = [];
|
|
@@ -457,6 +418,17 @@ export default class AtomsElement extends BaseElement {
|
|
|
457
418
|
}
|
|
458
419
|
}
|
|
459
420
|
|
|
421
|
+
get attrs() {
|
|
422
|
+
return Object.keys(this.constructor.attrTypes).reduceRight((acc, key) => {
|
|
423
|
+
const attrType = this.constructor.attrTypes[key];
|
|
424
|
+
const newValue = isBrowser ? this.getAttribute(key.toLowerCase()) : this.ssrAttributes.find((item) => item.name === key.toLowerCase())?.value;
|
|
425
|
+
const data = attrType.parse(newValue);
|
|
426
|
+
attrType.validate(`<${this.constructor.name}> ${key}`, data);
|
|
427
|
+
acc[key] = data;
|
|
428
|
+
return acc;
|
|
429
|
+
}, {});
|
|
430
|
+
}
|
|
431
|
+
|
|
460
432
|
useState(initialState) {
|
|
461
433
|
const index = this.hooks.currentCursor++;
|
|
462
434
|
if (this.hooks.values.length <= index) {
|
|
@@ -477,14 +449,85 @@ export default class AtomsElement extends BaseElement {
|
|
|
477
449
|
return this.hooks.values[index];
|
|
478
450
|
}
|
|
479
451
|
|
|
452
|
+
useReducer(reducer, initialState) {
|
|
453
|
+
const index = this.hooks.currentCursor++;
|
|
454
|
+
if (this.hooks.values.length <= index) {
|
|
455
|
+
this.hooks.values[index] = [
|
|
456
|
+
initialState,
|
|
480
|
-
|
|
457
|
+
(action) => {
|
|
481
|
-
return Object.keys(this.constructor.attrTypes).reduceRight((acc, key) => {
|
|
482
|
-
|
|
458
|
+
const state = this.hooks.values[index][0];
|
|
483
|
-
const newValue = isBrowser ? this.getAttribute(key.toLowerCase()) : this.ssrAttributes.find((item) => item.name === key.toLowerCase())?.value;
|
|
484
|
-
|
|
459
|
+
const nextState = reducer(state, action);
|
|
460
|
+
if (!Object.is(state, nextState)) {
|
|
485
|
-
|
|
461
|
+
this.hooks.values[index][0] = nextState;
|
|
486
|
-
|
|
462
|
+
this.update();
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
];
|
|
466
|
+
}
|
|
487
|
-
|
|
467
|
+
return this.hooks.values[index];
|
|
488
|
-
}, {});
|
|
489
468
|
}
|
|
469
|
+
|
|
470
|
+
useEffect(handler, deps) {
|
|
471
|
+
const index = this.hooks.currentCursor++;
|
|
472
|
+
if (!deps || depsChanged(this.hooks.deps[index], deps)) {
|
|
473
|
+
this.hooks.deps[index] = deps || [];
|
|
474
|
+
this.hooks.effects[index] = handler;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
useLayoutEffect(handler, deps) {
|
|
479
|
+
const index = this.hooks.currentCursor++;
|
|
480
|
+
if (!deps || depsChanged(this.hooks.deps[index], deps)) {
|
|
481
|
+
this.hooks.deps[index] = deps || [];
|
|
482
|
+
this.hooks.layoutEffects[index] = handler;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
useMemo(fn, deps) {
|
|
487
|
+
const index = this.hooks.currentCursor++;
|
|
488
|
+
if (!deps || depsChanged(this.hooks.deps[index], deps)) {
|
|
489
|
+
this.hooks.deps[index] = deps || [];
|
|
490
|
+
this.hooks.values[i] = fn();
|
|
491
|
+
}
|
|
492
|
+
return this.hooks.values[i];
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
useCallback(callback, deps) {
|
|
496
|
+
return this.useMemo(() => callback, deps);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// export const useDispatchEvent = <T>(name: string, eventInit: EventInit = {}) =>
|
|
500
|
+
// hooks<(detail: T) => void>({
|
|
501
|
+
// _onmount: (_, c) => (detail: T) =>
|
|
502
|
+
// c._dispatch(name, { ...eventInit, detail }),
|
|
503
|
+
// });
|
|
504
|
+
|
|
505
|
+
// export const useListenEvent = <T extends Event>(
|
|
506
|
+
// name: string,
|
|
507
|
+
// listener: Listener<T>
|
|
508
|
+
// ) =>
|
|
509
|
+
// hooks<void>({
|
|
510
|
+
// _onmount(h, c, i) {
|
|
511
|
+
// h._cleanup[i] = c._listen(name, listener);
|
|
512
|
+
// },
|
|
513
|
+
// });
|
|
514
|
+
|
|
515
|
+
// useErrorBoundary() {
|
|
516
|
+
// const [error, setError] = this.useState(null);
|
|
517
|
+
// useListenEvent(errorType, (e) => {
|
|
518
|
+
// setError(e.detail);
|
|
519
|
+
// });
|
|
520
|
+
// return [error, () => setError(null)];
|
|
521
|
+
// }
|
|
522
|
+
|
|
523
|
+
// public _dispatch<T>(name: string, init: CustomEventInit<T>) {
|
|
524
|
+
// this.dispatchEvent(new CustomEvent<T>(name, init));
|
|
525
|
+
// }
|
|
526
|
+
|
|
527
|
+
// public _listen<T extends Event>(name: string, listener: Listener<T>) {
|
|
528
|
+
// this.addEventListener(name, listener as EventListener);
|
|
529
|
+
// return () => {
|
|
530
|
+
// this.removeEventListener(name, listener as EventListener);
|
|
531
|
+
// };
|
|
532
|
+
// }
|
|
490
533
|
}
|
page.d.ts
CHANGED
|
@@ -13,16 +13,14 @@ export declare type Config = {
|
|
|
13
13
|
themes: {[key: string]: any}
|
|
14
14
|
}
|
|
15
15
|
export declare type Data = any;
|
|
16
|
-
export declare type
|
|
16
|
+
export declare type Item = any;
|
|
17
17
|
|
|
18
|
-
export
|
|
18
|
+
export class Page {
|
|
19
19
|
config: Config;
|
|
20
20
|
data: Data;
|
|
21
|
-
item:
|
|
21
|
+
item: Item;
|
|
22
|
-
}
|
|
23
|
-
export class Page {
|
|
24
|
-
route: (
|
|
22
|
+
route: () => string;
|
|
25
|
-
styles: (
|
|
23
|
+
styles: () => string;
|
|
26
|
-
head: (
|
|
24
|
+
head: () => string;
|
|
27
|
-
body: (
|
|
25
|
+
body: () => string;
|
|
28
26
|
}
|
page.test.js
CHANGED
|
@@ -4,12 +4,12 @@ import Page from './page.js';
|
|
|
4
4
|
|
|
5
5
|
test('Page', () => {
|
|
6
6
|
class MainPage extends Page {
|
|
7
|
-
route(
|
|
7
|
+
route() {
|
|
8
|
-
const langPart = config.lang === 'en' ? '' : `/${config.lang}`;
|
|
8
|
+
const langPart = this.config.lang === 'en' ? '' : `/${this.config.lang}`;
|
|
9
9
|
return `${langPart}`;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
styles(
|
|
12
|
+
styles() {
|
|
13
13
|
return css`
|
|
14
14
|
div {
|
|
15
15
|
color: red;
|
|
@@ -17,20 +17,22 @@ test('Page', () => {
|
|
|
17
17
|
`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
head(
|
|
20
|
+
head() {
|
|
21
|
+
const { title } = this.config;
|
|
21
22
|
return html`
|
|
22
|
-
<title>${
|
|
23
|
+
<title>${title}</title>
|
|
23
|
-
<meta name="title" content=${
|
|
24
|
+
<meta name="title" content=${title} />
|
|
24
|
-
<meta name="description" content=${
|
|
25
|
+
<meta name="description" content=${title} />
|
|
25
26
|
`;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
body(
|
|
29
|
+
body() {
|
|
30
|
+
const { title } = this.config;
|
|
29
31
|
return html`
|
|
30
32
|
<div>
|
|
31
33
|
<app-header></app-header>
|
|
32
34
|
<main class="flex flex-1 flex-col mt-20 items-center">
|
|
33
|
-
<h1 class="text-5xl">${
|
|
35
|
+
<h1 class="text-5xl">${title}</h1>
|
|
34
36
|
</main>
|
|
35
37
|
</div>
|
|
36
38
|
`;
|