@esportsplus/template 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,8 +20,7 @@ function schedule() {
20
20
 
21
21
  function task() {
22
22
  try {
23
- let fns,
24
- fn,
23
+ let fns, fn,
25
24
  n = cleanup.length;
26
25
 
27
26
  while ((fns = cleanup.next()) && n--) {
@@ -3,21 +3,22 @@ import { STATE_HYDRATING, STATE_NONE } from '~/constants';
3
3
  import { Element, SlotGroup } from '~/types';
4
4
  import { firstChild, lastChild, nodeValue } from '~/utilities/node'
5
5
  import { raf } from '~/utilities/queue'
6
- import { ondisconnect } from '~/slot/cleanup';
7
6
  import { remove } from './cleanup';
8
7
  import text from '~/utilities/text';
9
8
  import render from './render';
10
9
 
11
10
 
12
- function update(this: { group?: SlotGroup, textnode?: Element }, anchor: Element, value: unknown) {
13
- let type = typeof value;
14
-
11
+ function update(this: { group?: SlotGroup, textnode?: Node }, anchor: Element, value: unknown) {
15
12
  if (this.group) {
16
13
  remove([this.group]);
17
14
  this.group = undefined;
18
15
  }
19
16
 
20
- if (value == null || type !== 'object') {
17
+ if (value == null || value === false) {
18
+ value = '';
19
+ }
20
+
21
+ if (typeof value !== 'object') {
21
22
  let textnode = this.textnode;
22
23
 
23
24
  if (textnode) {
@@ -32,18 +33,17 @@ function update(this: { group?: SlotGroup, textnode?: Element }, anchor: Element
32
33
  }
33
34
  }
34
35
  else {
35
- let fragment = render(anchor, value);
36
-
37
- if (!fragment) {
38
- return;
39
- }
36
+ let fragment = render(anchor, value),
37
+ head = firstChild.call(fragment);
40
38
 
41
- this.group = {
42
- head: firstChild.call(fragment),
43
- tail: lastChild.call(fragment)
44
- };
39
+ if (head) {
40
+ this.group = {
41
+ head,
42
+ tail: lastChild.call(fragment)
43
+ };
45
44
 
46
- anchor.after(fragment);
45
+ anchor.after(fragment);
46
+ }
47
47
  }
48
48
  }
49
49
 
@@ -51,24 +51,21 @@ function update(this: { group?: SlotGroup, textnode?: Element }, anchor: Element
51
51
  export default (anchor: Element, fn: Function) => {
52
52
  let context = {
53
53
  group: undefined as SlotGroup | undefined,
54
- textnode: undefined as Element | undefined
54
+ textnode: undefined as Node | undefined
55
55
  },
56
56
  state = STATE_HYDRATING;
57
57
 
58
- ondisconnect(
59
- anchor,
60
- effect(() => {
61
- let value = fn();
58
+ effect(() => {
59
+ let value = fn();
62
60
 
63
- if (state === STATE_HYDRATING) {
61
+ if (state === STATE_HYDRATING) {
62
+ update.call(context, anchor, value);
63
+ state = STATE_NONE;
64
+ }
65
+ else if (state === STATE_NONE) {
66
+ raf.add(() => {
64
67
  update.call(context, anchor, value);
65
- state = STATE_NONE;
66
- }
67
- else if (state === STATE_NONE) {
68
- raf.add(() => {
69
- update.call(context, anchor, value);
70
- });
71
- }
72
- })
73
- );
68
+ });
69
+ }
70
+ });
74
71
  };
package/src/slot/index.ts CHANGED
@@ -5,8 +5,9 @@ import render from './render';
5
5
 
6
6
  export default (anchor: Element, value: unknown): void => {
7
7
  if (typeof value === 'function') {
8
- return effect(anchor, value as Function);
8
+ effect(anchor, value as Function);
9
+ }
10
+ else {
11
+ anchor.after( render(anchor, value) );
9
12
  }
10
-
11
- anchor.after(render(anchor, value));
12
13
  };
@@ -1,14 +1,14 @@
1
1
  import { root, ReactiveArray } from '@esportsplus/reactivity';
2
2
  import { EMPTY_FRAGMENT } from '~/constants';
3
- import { Fragment, RenderableReactive, SlotGroup } from '~/types';
3
+ import { RenderableReactive, SlotGroup } from '~/types';
4
4
  import { append } from '~/utilities/fragment';
5
5
  import { cloneNode, firstChild, lastChild } from '~/utilities/node';
6
- import { remove } from './cleanup';
6
+ import { ondisconnect, remove } from './cleanup';
7
7
 
8
8
 
9
9
  class ReactiveArraySlot<T> {
10
10
  array: ReactiveArray<T[]>;
11
- fragment = cloneNode.call(EMPTY_FRAGMENT) as Fragment;
11
+ fragment: Node;
12
12
  marker: Element;
13
13
  nodes: SlotGroup[] = [];
14
14
  template: (
@@ -18,45 +18,45 @@ class ReactiveArraySlot<T> {
18
18
 
19
19
 
20
20
  constructor(anchor: Element, array: ReactiveArray<T[]>, template: RenderableReactive['template']) {
21
- let fragment = this.fragment;
21
+ let fragment = this.fragment = cloneNode.call(EMPTY_FRAGMENT);
22
22
 
23
23
  this.array = array;
24
24
  this.marker = anchor;
25
25
  this.template = function (data, i) {
26
- let frag = template.call(this, data, i).fragment,
26
+ let dispose: VoidFunction,
27
+ frag = root((d) => {
28
+ dispose = d;
29
+ return template.call(this, data, i);
30
+ }),
27
31
  group = {
28
32
  head: firstChild.call(frag),
29
33
  tail: lastChild.call(frag)
30
34
  };
31
35
 
32
36
  append.call(fragment, frag);
37
+ ondisconnect(group.head, dispose!);
33
38
 
34
39
  return group;
35
40
  };
36
41
 
37
-
38
- let render = () => {
39
- root(() => this.render());
40
- };
41
-
42
42
  array.on('clear', () => this.clear());
43
- array.on('reverse', render);
43
+ array.on('reverse', () => {
44
+ root(() => this.render());
45
+ });
44
46
  array.on('pop', () => this.pop());
45
47
  array.on('push', ({ items }) => {
46
48
  root(() => this.push(items));
47
49
  });
48
50
  array.on('shift', () => this.shift());
49
- array.on('sort', render);
51
+ array.on('sort', () => {
52
+ root(() => this.render());
53
+ });
50
54
  array.on('splice', ({ deleteCount, items, start }) => {
51
55
  root(() => this.splice(start, deleteCount, ...items));
52
56
  });
53
57
  array.on('unshift', ({ items }) => {
54
58
  root(() => this.unshift(items));
55
59
  });
56
-
57
- if (array.length) {
58
- render();
59
- }
60
60
  }
61
61
 
62
62
 
@@ -1,117 +1,50 @@
1
1
  import { isArray } from '@esportsplus/utilities';
2
- import {
3
- EMPTY_FRAGMENT,
4
- RENDERABLE,
5
- RENDERABLE_ARRAY, RENDERABLE_FRAGMENT, RENDERABLE_HTML_FRAGMENT, RENDERABLE_HTML_REACTIVE_ARRAY,
6
- RENDERABLE_NODE, RENDERABLE_NODE_LIST, RENDERABLE_TEXT, RENDERABLE_VOID
7
- } from '~/constants';
8
- import { Element, Fragment, RenderableReactive, RenderableTemplate } from '~/types';
9
- import { cloneNode } from '~/utilities/node';
2
+ import { EMPTY_FRAGMENT, RENDERABLE } from '~/constants';
3
+ import { Element, RenderableReactive } from '~/types';
4
+ import { cloneNode, lastChild } from '~/utilities/node';
10
5
  import { append } from '~/utilities/fragment';
11
6
  import text from '~/utilities/text';
12
7
  import reactive from './reactive';
13
8
 
14
9
 
15
- function type(input: unknown) {
16
- if (input === false || input == null || input === '') {
17
- return RENDERABLE_VOID;
10
+ export default function render(anchor: Element, input: unknown): Node {
11
+ if (input == null || input === false || input === '') {
12
+ return EMPTY_FRAGMENT;
18
13
  }
19
14
 
20
15
  if (typeof input !== 'object') {
21
- return RENDERABLE_TEXT;
16
+ return text(input as any);
22
17
  }
23
18
 
24
- if (RENDERABLE in input) {
25
- return input[RENDERABLE];
19
+ if ('nodeType' in input) {
20
+ return input as Node;
26
21
  }
27
22
 
28
- if (isArray(input)) {
29
- return RENDERABLE_ARRAY;
23
+ if (RENDERABLE in input) {
24
+ return reactive(anchor, input as RenderableReactive);
30
25
  }
31
26
 
32
- let nodeType = (input as any).nodeType;
27
+ if (isArray(input)) {
28
+ let fragment = cloneNode.call(EMPTY_FRAGMENT);
33
29
 
34
- // Document Fragment
35
- if (nodeType === 11) {
36
- return RENDERABLE_FRAGMENT;
37
- }
30
+ for (let i = 0, n = (input as unknown[]).length; i < n; i++) {
31
+ append.call(fragment, render(anchor, (input as unknown[])[i]));
32
+ anchor = lastChild.call(fragment);
33
+ }
38
34
 
39
- if (nodeType !== undefined) {
40
- return RENDERABLE_NODE;
35
+ return fragment;
41
36
  }
42
37
 
43
38
  if (input instanceof NodeList) {
44
- return RENDERABLE_NODE_LIST;
45
- }
46
-
47
- return RENDERABLE_TEXT;
48
- }
49
-
50
- function loop(fragment: Node, input: unknown) {
51
- let t = type(input);
52
-
53
- switch (t) {
54
- case RENDERABLE_HTML_REACTIVE_ARRAY:
55
- throw new Error('@esportsplus/template: reactive arrays cannot be defined within an slot array value');
56
-
57
- case RENDERABLE_VOID:
58
- return;
59
-
60
- case RENDERABLE_ARRAY:
61
- for (let i = 0, n = (input as unknown[]).length; i < n; i++) {
62
- loop(fragment, (input as unknown[])[i])
63
- }
64
- return;
65
-
66
- case RENDERABLE_NODE_LIST:
67
- append.call(fragment, ...input as Element[]);
68
- return;
69
-
70
- default:
71
- append.call(fragment, input as Element)
72
- return;
73
- }
74
- }
75
-
76
- let scratchpad = cloneNode.call(EMPTY_FRAGMENT);
77
-
78
-
79
- export default function render(anchor: Element, input: unknown): Node {
80
- let fragment = scratchpad,
81
- t = type(input);
82
-
83
- switch (t) {
84
- case RENDERABLE_VOID:
85
- break;
86
-
87
- case RENDERABLE_TEXT:
88
- append.call(fragment, text(input as string));
89
- break;
90
-
91
- case RENDERABLE_HTML_FRAGMENT:
92
- return (input as RenderableTemplate).fragment;
93
-
94
- case RENDERABLE_HTML_REACTIVE_ARRAY:
95
- return reactive(anchor, input as RenderableReactive);
96
-
97
- case RENDERABLE_ARRAY:
98
- for (let i = 0, n = (input as unknown[]).length; i < n; i++) {
99
- loop(fragment, (input as unknown[])[i]);
100
- }
101
-
102
- break;
103
-
104
- case RENDERABLE_FRAGMENT:
105
- return input as Fragment;
39
+ let fragment = cloneNode.call(EMPTY_FRAGMENT),
40
+ nodes = Array.from(input as NodeList);
106
41
 
107
- case RENDERABLE_NODE:
108
- append.call(fragment, input as Element);
109
- break;
42
+ for (let i = 0, n = nodes.length; i < n; i++) {
43
+ append.call(fragment, nodes[i]);
44
+ }
110
45
 
111
- case RENDERABLE_NODE_LIST:
112
- append.call(fragment, ...input as Element[]);
113
- break;
46
+ return fragment;
114
47
  }
115
48
 
116
- return fragment;
49
+ return text(input as any);
117
50
  };
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactiveArray } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_HTML_FRAGMENT, RENDERABLE_HTML_REACTIVE_ARRAY } from './constants';
2
+ import { RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY } from './constants';
3
3
  import { firstChild } from './utilities/node';
4
4
  import attributes from './attributes';
5
5
  import slot from './slot';
@@ -29,15 +29,11 @@ type EffectResponse<T> = T extends [] ? (Primitive | Renderable)[] : Primitive |
29
29
 
30
30
  type Element = HTMLElement & Attributes & Record<PropertyKey, unknown>;
31
31
 
32
- type Elements = Element[];
33
-
34
- type Fragment = (DocumentFragment | Node) & Record<PropertyKey, unknown>;
35
-
36
32
  // Copied from '@esportsplus/utilities'
37
33
  // - Importing from ^ causes 'cannot be named without a reference to...' error
38
34
  type Primitive = bigint | boolean | null | number | string | undefined;
39
35
 
40
- type Renderable = Fragment | Primitive | RenderableReactive | RenderableTemplate;
36
+ type Renderable = DocumentFragment | Node | NodeList | Primitive | RenderableReactive | Renderable[];
41
37
 
42
38
  type RenderableReactive = Readonly<{
43
39
  [RENDERABLE]: typeof RENDERABLE_HTML_REACTIVE_ARRAY;
@@ -48,15 +44,7 @@ type RenderableReactive = Readonly<{
48
44
  ) => ReturnType<typeof html>;
49
45
  }>;
50
46
 
51
- type RenderableTemplate = {
52
- [RENDERABLE]: typeof RENDERABLE_HTML_FRAGMENT;
53
- fragment: Fragment;
54
- literals: TemplateStringsArray;
55
- };
56
-
57
- type RenderableValue<T = unknown> = Attributes | Readonly<Attributes> | Readonly<Attributes[]> | Effect<T> | Fragment | Primitive | RenderableReactive;
58
-
59
- type RenderableValues = RenderableValue | RenderableValue[];
47
+ type RenderableValue<T = unknown> = Attributes | Readonly<Attributes> | Readonly<Attributes[]> | Effect<T> | Primitive | RenderableReactive;
60
48
 
61
49
  type SlotGroup = {
62
50
  head: Element;
@@ -81,9 +69,8 @@ type Template = {
81
69
 
82
70
  export type {
83
71
  Attributes,
84
- Effect, Element, Elements,
85
- Fragment,
86
- Renderable, RenderableReactive, RenderableTemplate, RenderableValue, RenderableValues,
72
+ Effect, Element,
73
+ Renderable, RenderableReactive, RenderableValue,
87
74
  SlotGroup,
88
75
  Template
89
76
  };
@@ -2,18 +2,17 @@ import { innerHTML } from './element';
2
2
  import { cloneNode } from './node';
3
3
 
4
4
 
5
- let prototype = DocumentFragment.prototype,
6
- template = document.createElement('template');
5
+ let scratchpad = document.createElement('template');
7
6
 
8
7
 
9
- const append = prototype.append;
8
+ const append = DocumentFragment.prototype.append;
10
9
 
11
- const fragment = (html: string) => {
12
- innerHTML.call(template, html);
10
+ const fragment = (html: string): DocumentFragment => {
11
+ innerHTML.call(scratchpad, html);
13
12
 
14
- let content = template.content;
13
+ let content = scratchpad.content;
15
14
 
16
- template = cloneNode.call(template) as HTMLTemplateElement;
15
+ scratchpad = cloneNode.call(scratchpad) as HTMLTemplateElement;
17
16
 
18
17
  return content;
19
18
  };
@@ -2,6 +2,8 @@ let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
2
2
  prototype = Node.prototype;
3
3
 
4
4
 
5
+ const appendChild = prototype.appendChild;
6
+
5
7
  const cloneNode = prototype.cloneNode;
6
8
 
7
9
  const firstChild = getOwnPropertyDescriptor(prototype, 'firstChild')!.get!;
@@ -18,6 +20,7 @@ const previousSibling = getOwnPropertyDescriptor(prototype, 'previousSibling')!.
18
20
 
19
21
 
20
22
  export {
23
+ appendChild,
21
24
  cloneNode,
22
25
  firstChild,
23
26
  lastChild,
@@ -1,5 +1,4 @@
1
1
  import { cloneNode, nodeValue } from './node';
2
- import { Element } from '~/types';
3
2
 
4
3
 
5
4
  let text = document.createTextNode('');
@@ -12,5 +11,5 @@ export default (value: string) => {
12
11
  nodeValue.call(element, value);
13
12
  }
14
13
 
15
- return element as Element;
14
+ return element;
16
15
  };
@@ -1,3 +0,0 @@
1
- import { Fragment, RenderableValues, Template } from '../types.js';
2
- declare const _default: ({ fragment, slots }: Template, values: RenderableValues[]) => Fragment;
3
- export default _default;
@@ -1,34 +0,0 @@
1
- import { cloneNode } from '../utilities/node.js';
2
- export default ({ fragment, slots }, values) => {
3
- let clone = cloneNode.call(fragment, true);
4
- if (slots === null) {
5
- return clone;
6
- }
7
- let node, nodePath, parent, parentPath;
8
- for (let i = 0, n = slots.length; i < n; i++) {
9
- let { fn, path, slot } = slots[i], pp = path.parent, pr = path.relative;
10
- if (pp !== parentPath) {
11
- if (pp === nodePath) {
12
- parent = node;
13
- parentPath = nodePath;
14
- nodePath = undefined;
15
- }
16
- else {
17
- parent = clone;
18
- parentPath = pp;
19
- for (let i = 0, n = pp.length; i < n; i++) {
20
- parent = pp[i].call(parent);
21
- }
22
- }
23
- }
24
- if (pr !== nodePath) {
25
- node = parent;
26
- nodePath = path.absolute;
27
- for (let i = 0, n = pr.length; i < n; i++) {
28
- node = pr[i].call(node);
29
- }
30
- }
31
- fn(node, values[slot]);
32
- }
33
- return clone;
34
- };
@@ -1,53 +0,0 @@
1
- import { Fragment, RenderableValues, Template } from '~/types';
2
- import { cloneNode } from '~/utilities/node';
3
-
4
-
5
- export default ({ fragment, slots }: Template, values: RenderableValues[]): Fragment => {
6
- let clone = cloneNode.call(fragment, true);
7
-
8
- if (slots === null) {
9
- return clone as Fragment;
10
- }
11
-
12
- let node,
13
- nodePath,
14
- parent,
15
- parentPath;
16
-
17
- for (let i = 0, n = slots.length; i < n; i++) {
18
- let { fn, path, slot } = slots[i],
19
- pp = path.parent,
20
- pr = path.relative;
21
-
22
- if (pp !== parentPath) {
23
- if (pp === nodePath) {
24
- parent = node;
25
- parentPath = nodePath;
26
-
27
- nodePath = undefined;
28
- }
29
- else {
30
- parent = clone;
31
- parentPath = pp;
32
-
33
- for (let i = 0, n = pp.length; i < n; i++) {
34
- parent = pp[i].call(parent);
35
- }
36
- }
37
- }
38
-
39
- if (pr !== nodePath) {
40
- node = parent;
41
- nodePath = path.absolute;
42
-
43
- for (let i = 0, n = pr.length; i < n; i++) {
44
- node = pr[i].call(node);
45
- }
46
- }
47
-
48
- // @ts-ignore
49
- fn(node, values[slot]);
50
- }
51
-
52
- return clone as Fragment;
53
- }