~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.


897b1286 Peter John

4 years ago
improve page
Files changed (3) hide show
  1. element.js +109 -66
  2. page.d.ts +7 -9
  3. 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
- export class CSSResult {
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
- get attrs() {
457
+ (action) => {
481
- return Object.keys(this.constructor.attrTypes).reduceRight((acc, key) => {
482
- const attrType = this.constructor.attrTypes[key];
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
- const data = attrType.parse(newValue);
459
+ const nextState = reducer(state, action);
460
+ if (!Object.is(state, nextState)) {
485
- attrType.validate(`<${this.constructor.name}> ${key}`, data);
461
+ this.hooks.values[index][0] = nextState;
486
- acc[key] = data;
462
+ this.update();
463
+ }
464
+ },
465
+ ];
466
+ }
487
- return acc;
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 Iteam = any;
16
+ export declare type Item = any;
17
17
 
18
- export type Props = {
18
+ export class Page {
19
19
  config: Config;
20
20
  data: Data;
21
- item: Data;
21
+ item: Item;
22
- }
23
- export class Page {
24
- route: (props: Props) => string;
22
+ route: () => string;
25
- styles: (props: Props) => string;
23
+ styles: () => string;
26
- head: (props: Props) => string;
24
+ head: () => string;
27
- body: (props: Props) => string;
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({ config }) {
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({ config }) {
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({ config }) {
20
+ head() {
21
+ const { title } = this.config;
21
22
  return html`
22
- <title>${config.title}</title>
23
+ <title>${title}</title>
23
- <meta name="title" content=${config.title} />
24
+ <meta name="title" content=${title} />
24
- <meta name="description" content=${config.title} />
25
+ <meta name="description" content=${title} />
25
26
  `;
26
27
  }
27
28
 
28
- body({ config }) {
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">${config.title}</h1>
35
+ <h1 class="text-5xl">${title}</h1>
34
36
  </main>
35
37
  </div>
36
38
  `;