@esportsplus/template 0.15.4 → 0.15.6

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.
@@ -1,6 +1,7 @@
1
- import { computed, dispose, root } from '@esportsplus/reactivity';
2
- import { oncleanup } from './slot.js';
3
- import { className, isArray, isObject, raf, removeAttribute, setAttribute } from './utilities.js';
1
+ import { effect } from '@esportsplus/reactivity';
2
+ import { isArray, isFunction, isObject, isString } from '@esportsplus/utilities';
3
+ import { onRemove } from './slot.js';
4
+ import { className, raf, removeAttribute, setAttribute } from './utilities.js';
4
5
  import event from './event.js';
5
6
  let attributes = {}, delimiters = {
6
7
  class: ' ',
@@ -23,52 +24,35 @@ function attribute(element, name, value) {
23
24
  element[name] = value;
24
25
  }
25
26
  }
26
- function reactive(element, id, name, value, wait = false) {
27
- if (typeof value === 'function') {
28
- let instance = computed(() => {
29
- let v = value(element);
30
- if (typeof v === 'function') {
31
- root(() => {
32
- reactive(element, id, name, v(element), wait);
33
- });
34
- }
35
- else if (isArray(v) || isObject(v)) {
36
- spread(element, v);
37
- }
38
- else {
39
- raf.add(() => {
40
- update(element, id, name, v, wait);
41
- });
42
- }
43
- });
44
- oncleanup(element, () => dispose(instance));
45
- wait = false;
46
- }
47
- else {
48
- update(element, id, name, value, wait);
49
- }
50
- }
51
- function set(element, value, name) {
52
- if (name === 'style' && isObject(value)) {
53
- for (let key in value) {
54
- set(element, value[key], name);
55
- }
56
- }
57
- else if (isArray(value)) {
27
+ function set(element, value, name, wait = false) {
28
+ if (isArray(value)) {
58
29
  for (let i = 0, n = value.length; i < n; i++) {
59
- set(element, value[i], name);
30
+ set(element, value[i], name, wait);
60
31
  }
61
32
  }
62
- else if (typeof value === 'function') {
33
+ else if (isFunction(value)) {
63
34
  if (name.startsWith('on')) {
64
35
  event(element, name, value);
65
36
  }
66
37
  else {
67
- reactive(element, ('e' + store(element)[key]++), name, value, true);
38
+ let id = ('e' + store(element)[key]++);
39
+ onRemove(element, effect(() => {
40
+ let v = value(element);
41
+ if (isArray(v)) {
42
+ let last = v.length - 1;
43
+ for (let i = 0, n = v.length; i < n; i++) {
44
+ update(element, id, name, v[i], wait || i !== last);
45
+ }
46
+ }
47
+ else {
48
+ update(element, id, name, v, wait);
49
+ }
50
+ }));
51
+ wait = false;
68
52
  }
69
53
  }
70
54
  else {
71
- update(element, null, name, value, true);
55
+ update(element, null, name, value, wait);
72
56
  }
73
57
  }
74
58
  function store(element) {
@@ -87,13 +71,13 @@ function update(element, id, name, value, wait = false) {
87
71
  data[name] = dynamic = {};
88
72
  }
89
73
  if (id === null) {
90
- if (value && typeof value === 'string') {
74
+ if (value && isString(value)) {
91
75
  data[cache] += (data[cache] ? delimiter : '') + value;
92
76
  }
93
77
  }
94
78
  else {
95
79
  let hot = {};
96
- if (typeof value === 'string') {
80
+ if (isString(value)) {
97
81
  let part, parts = value.split(delimiter);
98
82
  for (let i = 0, n = parts.length; i < n; i++) {
99
83
  part = parts[i].trim();
@@ -120,17 +104,21 @@ function update(element, id, name, value, wait = false) {
120
104
  value += (value ? delimiter : '') + key;
121
105
  }
122
106
  }
123
- else if (typeof id === 'string') {
107
+ else if (isString(id)) {
124
108
  if (data[name] === value) {
125
109
  return;
126
110
  }
127
111
  data[name] = value;
128
112
  }
129
113
  if (wait) {
130
- attributes[name] = value;
114
+ if (id === null) {
115
+ attributes[name] = value;
116
+ }
131
117
  }
132
118
  else {
133
- attribute(element, name, value);
119
+ raf.add(() => {
120
+ attribute(element, name, value);
121
+ });
134
122
  }
135
123
  }
136
124
  const apply = (element) => {
@@ -142,7 +130,7 @@ const apply = (element) => {
142
130
  const spread = function (element, attributes) {
143
131
  if (isObject(attributes)) {
144
132
  for (let name in attributes) {
145
- set(element, attributes[name], name);
133
+ set(element, attributes[name], name, true);
146
134
  }
147
135
  }
148
136
  else if (isArray(attributes)) {
@@ -152,7 +140,7 @@ const spread = function (element, attributes) {
152
140
  throw new Error('@esportsplus/template: attributes must be of type `Attributes` or `Attributes[]`; Received ' + JSON.stringify(attributes));
153
141
  }
154
142
  for (let name in attrs) {
155
- set(element, attrs[name], name);
143
+ set(element, attrs[name], name, true);
156
144
  }
157
145
  }
158
146
  }
@@ -8,9 +8,8 @@ declare const REGEX_SLOT_NODES: RegExp;
8
8
  declare const RENDERABLE: unique symbol;
9
9
  declare const RENDERABLE_REACTIVE: unique symbol;
10
10
  declare const RENDERABLE_TEMPLATE: unique symbol;
11
- declare const SLOT: unique symbol;
12
11
  declare const SLOT_CLEANUP: unique symbol;
13
12
  declare const SLOT_HTML = "<!--$-->";
14
13
  declare const SLOT_MARKER = "{{$}}";
15
14
  declare const SLOT_MARKER_LENGTH: number;
16
- export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
15
+ export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
@@ -28,9 +28,8 @@ const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{
28
28
  const RENDERABLE = Symbol();
29
29
  const RENDERABLE_REACTIVE = Symbol();
30
30
  const RENDERABLE_TEMPLATE = Symbol();
31
- const SLOT = Symbol();
32
31
  const SLOT_CLEANUP = Symbol();
33
32
  const SLOT_HTML = '<!--$-->';
34
33
  const SLOT_MARKER = '{{$}}';
35
34
  const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
36
- export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
35
+ export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
package/build/event.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { Element } from './types.js';
2
- declare const _default: (element: Element, event: `on${string}`, listener: Function) => any;
2
+ declare const _default: (element: Element, event: `on${string}`, listener: Function) => void;
3
3
  export default _default;
package/build/event.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
- import { oncleanup } from './slot.js';
3
- import { addEventListener, defineProperty, parentElement } from './utilities.js';
2
+ import { defineProperty } from '@esportsplus/utilities';
3
+ import { onRemove } from './slot.js';
4
+ import { addEventListener, parentElement } from './utilities.js';
4
5
  let capture = new Set(['onblur', 'onfocus', 'onscroll']), controllers = new Map(), keys = {}, passive = new Set([
5
6
  'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
6
7
  'onscroll',
@@ -18,18 +19,19 @@ export default (element, event, listener) => {
18
19
  retry = 0;
19
20
  root(() => listener(element));
20
21
  }
21
- if (retry === 0) {
22
+ if (!retry) {
22
23
  clearInterval(interval);
23
24
  }
24
25
  }, 1000 / 60), retry = 60;
25
26
  return;
26
27
  }
27
- else if (event === 'oncleanup') {
28
- oncleanup(element, () => listener(element));
28
+ else if (event === 'onremove') {
29
+ onRemove(element, () => listener(element));
29
30
  return;
30
31
  }
31
32
  else if (event === 'onrender') {
32
- return root(() => listener(element));
33
+ root(() => listener(element));
34
+ return;
33
35
  }
34
36
  let controller = controllers.get(event), signal;
35
37
  if (controller === null) {
@@ -42,7 +44,7 @@ export default (element, event, listener) => {
42
44
  }
43
45
  if (controller) {
44
46
  controller.listeners++;
45
- oncleanup(element, () => {
47
+ onRemove(element, () => {
46
48
  if (--controller.listeners) {
47
49
  return;
48
50
  }
@@ -35,9 +35,7 @@ function render(renderable, template) {
35
35
  }
36
36
  fn(node, values[slot], name);
37
37
  }
38
- if (node) {
39
- apply(node);
40
- }
38
+ apply(node);
41
39
  }
42
40
  for (let element = firstChild.call(fragment); element; element = nextSibling.call(element)) {
43
41
  elements.push(element);
package/build/render.js CHANGED
@@ -1,9 +1,10 @@
1
- import { SLOT, SLOT_HTML } from './constants.js';
2
- import slot from './slot.js';
1
+ import { isInstanceOf } from '@esportsplus/utilities';
2
+ import { SLOT_HTML } from './constants.js';
3
+ import slot, { Slot } from './slot.js';
3
4
  import { firstChild, fragment, nodeValue, prepend } from './utilities.js';
4
5
  let marker = firstChild.call(fragment(SLOT_HTML)), node;
5
6
  export default (renderable, parent) => {
6
- if (SLOT in parent) {
7
+ if (isInstanceOf(parent, Slot)) {
7
8
  return parent.render(renderable);
8
9
  }
9
10
  nodeValue.call(parent, '');
package/build/slot.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- import { SLOT } from './constants.js';
2
1
  import { Element, Elements } from './types.js';
3
2
  declare class Slot {
4
- [SLOT]: null;
5
3
  marker: Element;
6
4
  nodes: Elements[];
7
5
  text: Element | null;
@@ -17,7 +15,7 @@ declare class Slot {
17
15
  splice(start: number, stop?: number, ...groups: Elements[]): Elements[];
18
16
  unshift(...groups: Elements[]): number;
19
17
  }
20
- declare const oncleanup: (element: Element, fn: VoidFunction) => void;
18
+ declare const onRemove: (element: Element, fn: VoidFunction) => void;
21
19
  declare const _default: (marker: Element, value: unknown) => Slot;
22
20
  export default _default;
23
- export { oncleanup, Slot };
21
+ export { onRemove, Slot };
package/build/slot.js CHANGED
@@ -1,8 +1,8 @@
1
- import { computed, dispose, root } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants.js';
1
+ import { effect, root } from '@esportsplus/reactivity';
2
+ import { isArray, isFunction, isInstanceOf, isObject } from '@esportsplus/utilities';
3
+ import { RENDERABLE, RENDERABLE_REACTIVE, SLOT_CLEANUP } from './constants.js';
3
4
  import { hydrate } from './html/index.js';
4
- import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities.js';
5
- import { isFunction } from '@esportsplus/utilities';
5
+ import { firstChild, nextSibling, nodeValue, raf, text } from './utilities.js';
6
6
  let cleanup = [], scheduled = false;
7
7
  function after(anchor, groups) {
8
8
  for (let i = 0, n = groups.length; i < n; i++) {
@@ -34,7 +34,7 @@ function render(anchor, input, slot) {
34
34
  if (input === false || input == null) {
35
35
  input = '';
36
36
  }
37
- else if (typeof input === 'object') {
37
+ else if (isObject(input)) {
38
38
  if (isArray(input)) {
39
39
  let groups = [];
40
40
  for (let i = 0, n = input.length; i < n; i++) {
@@ -51,12 +51,12 @@ function render(anchor, input, slot) {
51
51
  nodes = hydrate.static(input);
52
52
  }
53
53
  }
54
- else if (input instanceof NodeList) {
54
+ else if (isInstanceOf(input, NodeList)) {
55
55
  for (let node = firstChild.call(input); node; node = nextSibling.call(node)) {
56
56
  nodes.push(node);
57
57
  }
58
58
  }
59
- else if (input instanceof Node) {
59
+ else if (isInstanceOf(input, Node)) {
60
60
  nodes = [input];
61
61
  }
62
62
  if (anchor) {
@@ -96,12 +96,11 @@ function schedule() {
96
96
  });
97
97
  }
98
98
  class Slot {
99
- [SLOT] = null;
100
99
  marker;
101
100
  nodes;
102
101
  text = null;
103
102
  constructor(marker) {
104
- oncleanup(marker, () => this.clear());
103
+ onRemove(marker, () => this.clear());
105
104
  this.marker = marker;
106
105
  this.nodes = [];
107
106
  }
@@ -138,7 +137,7 @@ class Slot {
138
137
  }
139
138
  render(input) {
140
139
  if (isFunction(input)) {
141
- let instance = computed(() => {
140
+ onRemove(this.marker, effect(() => {
142
141
  let v = input();
143
142
  if (isFunction(v)) {
144
143
  root(() => this.render(v()));
@@ -148,8 +147,7 @@ class Slot {
148
147
  this.render(v);
149
148
  });
150
149
  }
151
- });
152
- oncleanup(this.marker, () => dispose(instance));
150
+ }));
153
151
  return this;
154
152
  }
155
153
  if (this.text) {
@@ -183,10 +181,10 @@ class Slot {
183
181
  return this.nodes.unshift(...after(this.marker, groups));
184
182
  }
185
183
  }
186
- const oncleanup = (element, fn) => {
184
+ const onRemove = (element, fn) => {
187
185
  (element[SLOT_CLEANUP] ??= []).push(fn);
188
186
  };
189
187
  export default (marker, value) => {
190
188
  return new Slot(marker).render(value);
191
189
  };
192
- export { oncleanup, Slot };
190
+ export { onRemove, Slot };
package/build/types.d.ts CHANGED
@@ -5,15 +5,15 @@ import attributes from './attributes.js';
5
5
  import event from './event.js';
6
6
  import slot from './slot.js';
7
7
  type Attributes = {
8
- class?: string | Effect<Primitive> | (string | Effect<Primitive>)[];
9
- style?: string | Effect<Primitive> | (string | Effect<Primitive>)[];
8
+ class?: string | Effect<Primitive | Primitive[]> | (string | Effect<Primitive | Primitive[]>)[];
9
+ style?: string | Effect<Primitive | Primitive[]> | (string | Effect<Primitive | Primitive[]>)[];
10
10
  } & {
11
11
  [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: Element, event: GlobalEventHandlersEventMap[K]) => void;
12
12
  } & {
13
13
  [key: `aria-${string}`]: string | number | boolean | undefined;
14
14
  [key: `data-${string}`]: string | undefined;
15
15
  onconnected?: (element: Element) => void;
16
- oncleanup?: (element: Element) => void;
16
+ onremove?: (element: Element) => void;
17
17
  onrender?: (element: Element) => void;
18
18
  } & Record<PropertyKey, unknown>;
19
19
  type Effect<T> = () => EffectResponse<T>;
@@ -1,4 +1,3 @@
1
- import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
2
1
  import { Element as E } from './types.js';
3
2
  declare const addEventListener: {
4
3
  <K extends keyof ElementEventMap>(type: K, listener: (this: Element, ev: ElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
@@ -24,4 +23,4 @@ declare const parentNode: () => any;
24
23
  declare const fragment: (html: string) => DocumentFragment;
25
24
  declare const raf: import("@esportsplus/tasks/build/factory").Scheduler;
26
25
  declare const text: (value: string) => E;
27
- export { addEventListener, className, cloneNode, defineProperty, firstChild, firstElementChild, fragment, innerHTML, isArray, isObject, nextElementSibling, nextSibling, nodeValue, parentElement, parentNode, prepend, raf, removeAttribute, removeEventListener, setAttribute, text };
26
+ export { addEventListener, className, cloneNode, firstChild, firstElementChild, fragment, innerHTML, nextElementSibling, nextSibling, nodeValue, parentElement, parentNode, prepend, raf, removeAttribute, removeEventListener, setAttribute, text };
@@ -1,5 +1,4 @@
1
1
  import { raf as tasks } from '@esportsplus/tasks';
2
- import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
3
2
  let prototype, template = document.createElement('template'), t = document.createTextNode('');
4
3
  prototype = Element.prototype;
5
4
  const addEventListener = prototype.addEventListener;
@@ -32,4 +31,4 @@ const text = (value) => {
32
31
  }
33
32
  return element;
34
33
  };
35
- export { addEventListener, className, cloneNode, defineProperty, firstChild, firstElementChild, fragment, innerHTML, isArray, isObject, nextElementSibling, nextSibling, nodeValue, parentElement, parentNode, prepend, raf, removeAttribute, removeEventListener, setAttribute, text };
34
+ export { addEventListener, className, cloneNode, firstChild, firstElementChild, fragment, innerHTML, nextElementSibling, nextSibling, nodeValue, parentElement, parentNode, prepend, raf, removeAttribute, removeEventListener, setAttribute, text };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": "ICJR",
3
3
  "dependencies": {
4
- "@esportsplus/reactivity": "^0.8.0",
4
+ "@esportsplus/reactivity": "^0.10.1",
5
5
  "@esportsplus/tasks": "^0.1.11",
6
6
  "@esportsplus/utilities": "^0.21.0"
7
7
  },
@@ -13,7 +13,7 @@
13
13
  "private": false,
14
14
  "type": "module",
15
15
  "types": "./build/index.d.ts",
16
- "version": "0.15.4",
16
+ "version": "0.15.6",
17
17
  "scripts": {
18
18
  "build": "tsc && tsc-alias",
19
19
  "-": "-"
package/src/attributes.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { computed, dispose, root } from '@esportsplus/reactivity';
2
- import { oncleanup } from './slot';
1
+ import { effect } from '@esportsplus/reactivity';
2
+ import { isArray, isFunction, isObject, isString } from '@esportsplus/utilities';
3
+ import { onRemove } from './slot';
3
4
  import { Attributes, Element } from './types';
4
- import { className, isArray, isObject, raf, removeAttribute, setAttribute } from './utilities';
5
+ import { className, raf, removeAttribute, setAttribute } from './utilities';
5
6
  import event from './event';
6
7
 
7
8
 
@@ -32,56 +33,42 @@ function attribute(element: Element, name: string, value: unknown) {
32
33
  }
33
34
  }
34
35
 
35
- function reactive(element: Element, id: string, name: string, value: unknown, wait = false) {
36
- if (typeof value === 'function') {
37
- let instance = computed(() => {
38
- let v = (value as Function)(element);
39
-
40
- if (typeof v === 'function') {
41
- root(() => {
42
- reactive(element, id, name, v(element), wait);
43
- });
44
- }
45
- else if (isArray(v) || isObject(v)) {
46
- spread(element, v as Attributes | Attributes[]);
47
- }
48
- else {
49
- raf.add(() => {
50
- update(element, id, name, v, wait);
51
- });
52
- }
53
- });
54
-
55
- oncleanup(element, () => dispose(instance));
56
-
57
- wait = false;
58
- }
59
- else {
60
- update(element, id, name, value, wait);
61
- }
62
- }
63
-
64
- function set(element: Element, value: unknown, name: string) {
65
- if (name === 'style' && isObject(value)) {
66
- for (let key in value) {
67
- set(element, value[key], name);
68
- }
69
- }
70
- else if (isArray(value)) {
36
+ function set(element: Element, value: unknown, name: string, wait = false) {
37
+ if (isArray(value)) {
71
38
  for (let i = 0, n = value.length; i < n; i++) {
72
- set(element, value[i], name);
39
+ set(element, value[i], name, wait);
73
40
  }
74
41
  }
75
- else if (typeof value === 'function') {
42
+ else if (isFunction(value)) {
76
43
  if (name.startsWith('on')) {
77
44
  event(element, name as `on${string}`, value);
78
45
  }
79
46
  else {
80
- reactive(element, ('e' + store(element)[key]++), name, value, true);
47
+ let id = ('e' + store(element)[key]++);
48
+
49
+ onRemove(
50
+ element,
51
+ effect(() => {
52
+ let v = (value as Function)(element);
53
+
54
+ if (isArray(v)) {
55
+ let last = v.length - 1;
56
+
57
+ for (let i = 0, n = v.length; i < n; i++) {
58
+ update(element, id, name, v[i], wait || i !== last);
59
+ }
60
+ }
61
+ else {
62
+ update(element, id, name, v, wait);
63
+ }
64
+ })
65
+ );
66
+
67
+ wait = false;
81
68
  }
82
69
  }
83
70
  else {
84
- update(element, null, name, value, true);
71
+ update(element, null, name, value, wait);
85
72
  }
86
73
  }
87
74
 
@@ -111,14 +98,14 @@ function update(element: Element, id: null | string, name: string, value: unknow
111
98
  }
112
99
 
113
100
  if (id === null) {
114
- if (value && typeof value === 'string') {
101
+ if (value && isString(value)) {
115
102
  data[cache] += (data[cache] ? delimiter : '') + value;
116
103
  }
117
104
  }
118
105
  else {
119
106
  let hot: Attributes = {};
120
107
 
121
- if (typeof value === 'string') {
108
+ if (isString(value)) {
122
109
  let part: string,
123
110
  parts = value.split(delimiter);
124
111
 
@@ -155,7 +142,7 @@ function update(element: Element, id: null | string, name: string, value: unknow
155
142
  value += (value ? delimiter : '') + key;
156
143
  }
157
144
  }
158
- else if (typeof id === 'string') {
145
+ else if (isString(id)) {
159
146
  if (data[name] === value) {
160
147
  return;
161
148
  }
@@ -164,10 +151,14 @@ function update(element: Element, id: null | string, name: string, value: unknow
164
151
  }
165
152
 
166
153
  if (wait) {
167
- attributes[name] = value;
154
+ if (id === null) {
155
+ attributes[name] = value;
156
+ }
168
157
  }
169
158
  else {
170
- attribute(element, name, value);
159
+ raf.add(() => {
160
+ attribute(element, name, value);
161
+ });
171
162
  }
172
163
  }
173
164
 
@@ -183,7 +174,7 @@ const apply = (element: Element) => {
183
174
  const spread = function (element: Element, attributes: Attributes | Attributes[]) {
184
175
  if (isObject(attributes)) {
185
176
  for (let name in attributes) {
186
- set(element, attributes[name], name);
177
+ set(element, attributes[name], name, true);
187
178
  }
188
179
  }
189
180
  else if (isArray(attributes)) {
@@ -195,7 +186,7 @@ const spread = function (element: Element, attributes: Attributes | Attributes[]
195
186
  }
196
187
 
197
188
  for (let name in attrs) {
198
- set(element, attrs[name], name);
189
+ set(element, attrs[name], name, true);
199
190
  }
200
191
  }
201
192
  }
package/src/constants.ts CHANGED
@@ -43,8 +43,6 @@ const RENDERABLE_REACTIVE = Symbol();
43
43
  const RENDERABLE_TEMPLATE = Symbol();
44
44
 
45
45
 
46
- const SLOT = Symbol();
47
-
48
46
  const SLOT_CLEANUP = Symbol();
49
47
 
50
48
  const SLOT_HTML = '<!--$-->';
@@ -65,7 +63,6 @@ export {
65
63
  RENDERABLE,
66
64
  RENDERABLE_REACTIVE,
67
65
  RENDERABLE_TEMPLATE,
68
- SLOT,
69
66
  SLOT_CLEANUP,
70
67
  SLOT_HTML,
71
68
  SLOT_MARKER,
package/src/event.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
- import { oncleanup } from './slot';
2
+ import { defineProperty } from '@esportsplus/utilities';
3
+ import { onRemove } from './slot';
3
4
  import { Element } from './types';
4
- import { addEventListener, defineProperty, parentElement } from './utilities';
5
+ import { addEventListener, parentElement } from './utilities';
5
6
 
6
7
 
7
8
  let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
@@ -23,7 +24,7 @@ let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
23
24
  });
24
25
 
25
26
 
26
- export default (element: Element, event: `on${string}`, listener: Function) => {
27
+ export default (element: Element, event: `on${string}`, listener: Function): void => {
27
28
  if (event === 'onconnected') {
28
29
  let interval = setInterval(() => {
29
30
  retry--;
@@ -33,7 +34,7 @@ export default (element: Element, event: `on${string}`, listener: Function) => {
33
34
  root(() => listener(element));
34
35
  }
35
36
 
36
- if (retry === 0) {
37
+ if (!retry) {
37
38
  clearInterval(interval);
38
39
  }
39
40
  }, 1000 / 60),
@@ -41,12 +42,13 @@ export default (element: Element, event: `on${string}`, listener: Function) => {
41
42
 
42
43
  return;
43
44
  }
44
- else if (event === 'oncleanup') {
45
- oncleanup(element, () => listener(element));
45
+ else if (event === 'onremove') {
46
+ onRemove(element, () => listener(element));
46
47
  return;
47
48
  }
48
49
  else if (event === 'onrender') {
49
- return root(() => listener(element));
50
+ root(() => listener(element));
51
+ return;
50
52
  }
51
53
 
52
54
  let controller = controllers.get(event),
@@ -68,7 +70,7 @@ export default (element: Element, event: `on${string}`, listener: Function) => {
68
70
  if (controller) {
69
71
  controller.listeners++;
70
72
 
71
- oncleanup(element, () => {
73
+ onRemove(element, () => {
72
74
  if (--controller.listeners) {
73
75
  return;
74
76
  }
package/src/html/cache.ts CHANGED
@@ -88,6 +88,8 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
88
88
 
89
89
  if (type === NODE_CLOSING) {
90
90
  level--;
91
+
92
+ // TODO: support multiple root nodes in template literals
91
93
  }
92
94
  else {
93
95
  parent.children++;
@@ -58,9 +58,7 @@ function render<T>(renderable: Renderable<T>, template: Template) {
58
58
  fn(node, values[slot], name);
59
59
  }
60
60
 
61
- if (node) {
62
- apply(node);
63
- }
61
+ apply(node);
64
62
  }
65
63
 
66
64
  for (let element = firstChild.call(fragment as Element); element; element = nextSibling.call(element)) {
package/src/render.ts CHANGED
@@ -1,15 +1,16 @@
1
- import { SLOT, SLOT_HTML } from './constants';
1
+ import { isInstanceOf } from '@esportsplus/utilities';
2
+ import { SLOT_HTML } from './constants';
2
3
  import slot, { Slot } from './slot';
3
4
  import { Renderable } from './types';
4
5
  import { firstChild, fragment, nodeValue, prepend } from './utilities';
5
6
 
6
7
 
7
- let marker = firstChild.call(fragment(SLOT_HTML)),
8
+ let marker = firstChild.call( fragment(SLOT_HTML) ),
8
9
  node;
9
10
 
10
11
 
11
12
  export default (renderable: Renderable, parent: HTMLElement | Slot) => {
12
- if (SLOT in parent) {
13
+ if (isInstanceOf(parent, Slot)) {
13
14
  return parent.render(renderable);
14
15
  }
15
16
 
package/src/slot.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { computed, dispose, root } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants';
1
+ import { effect, root } from '@esportsplus/reactivity';
2
+ import { isArray, isFunction, isInstanceOf, isObject } from '@esportsplus/utilities';
3
+ import { RENDERABLE, RENDERABLE_REACTIVE, SLOT_CLEANUP } from './constants';
3
4
  import { hydrate } from './html';
4
5
  import { Element, Elements, RenderableReactive, RenderableTemplate } from './types';
5
- import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities'
6
- import { isFunction } from '@esportsplus/utilities';
6
+ import { firstChild, nextSibling, nodeValue, raf, text } from './utilities'
7
7
 
8
8
 
9
9
  let cleanup: VoidFunction[] = [],
@@ -49,7 +49,7 @@ function render(anchor: Element | null, input: unknown, slot?: Slot): Elements |
49
49
  if (input === false || input == null) {
50
50
  input = '';
51
51
  }
52
- else if (typeof input === 'object') {
52
+ else if (isObject(input)) {
53
53
  if (isArray(input)) {
54
54
  let groups: Elements[] = [];
55
55
 
@@ -70,12 +70,12 @@ function render(anchor: Element | null, input: unknown, slot?: Slot): Elements |
70
70
  nodes = hydrate.static(input as RenderableTemplate<unknown>);
71
71
  }
72
72
  }
73
- else if (input instanceof NodeList) {
74
- for (let node = firstChild.call(input as any as Element); node; node = nextSibling.call(node)) {
73
+ else if (isInstanceOf(input, NodeList)) {
74
+ for (let node = firstChild.call(input); node; node = nextSibling.call(node)) {
75
75
  nodes.push(node);
76
76
  }
77
77
  }
78
- else if (input instanceof Node) {
78
+ else if (isInstanceOf(input, Node)) {
79
79
  nodes = [input] as Elements;
80
80
  }
81
81
 
@@ -130,15 +130,13 @@ function schedule() {
130
130
 
131
131
 
132
132
  class Slot {
133
- [SLOT] = null;
134
-
135
133
  marker: Element;
136
134
  nodes: Elements[];
137
135
  text: Element | null = null;
138
136
 
139
137
 
140
138
  constructor(marker: Element) {
141
- oncleanup(marker, () => this.clear());
139
+ onRemove(marker, () => this.clear());
142
140
 
143
141
  this.marker = marker;
144
142
  this.nodes = [];
@@ -192,7 +190,9 @@ class Slot {
192
190
 
193
191
  render(input: unknown) {
194
192
  if (isFunction(input)) {
195
- let instance = computed(() => {
193
+ onRemove(
194
+ this.marker,
195
+ effect(() => {
196
196
  let v = (input as Function)();
197
197
 
198
198
  if (isFunction(v)) {
@@ -203,9 +203,8 @@ class Slot {
203
203
  this.render(v);
204
204
  });
205
205
  }
206
- });
207
-
208
- oncleanup(this.marker, () => dispose(instance));
206
+ })
207
+ );
209
208
 
210
209
  return this;
211
210
  }
@@ -257,7 +256,7 @@ class Slot {
257
256
  }
258
257
 
259
258
 
260
- const oncleanup = (element: Element, fn: VoidFunction) => {
259
+ const onRemove = (element: Element, fn: VoidFunction) => {
261
260
  ( element[SLOT_CLEANUP] ??= [] ).push(fn);
262
261
  };
263
262
 
@@ -265,4 +264,4 @@ const oncleanup = (element: Element, fn: VoidFunction) => {
265
264
  export default (marker: Element, value: unknown) => {
266
265
  return new Slot(marker).render(value);
267
266
  };
268
- export { oncleanup, Slot };
267
+ export { onRemove, Slot };
package/src/types.ts CHANGED
@@ -7,15 +7,15 @@ import slot from './slot';
7
7
 
8
8
 
9
9
  type Attributes = {
10
- class?: string | Effect<Primitive> | (string | Effect<Primitive>)[];
11
- style?: string | Effect<Primitive> | (string | Effect<Primitive>)[];
10
+ class?: string | Effect<Primitive | Primitive[]> | (string | Effect<Primitive | Primitive[]>)[];
11
+ style?: string | Effect<Primitive | Primitive[]> | (string | Effect<Primitive | Primitive[]>)[];
12
12
  } & {
13
13
  [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: Element, event: GlobalEventHandlersEventMap[K]) => void;
14
14
  } & {
15
15
  [key: `aria-${string}`]: string | number | boolean | undefined;
16
16
  [key: `data-${string}`]: string | undefined;
17
17
  onconnected?: (element: Element) => void;
18
- oncleanup?: (element: Element) => void;
18
+ onremove?: (element: Element) => void;
19
19
  // Rendered in fragment
20
20
  // - Used to retrieve reference to the element
21
21
  onrender?: (element: Element) => void;
package/src/utilities.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { raf as tasks } from '@esportsplus/tasks';
2
- import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
3
2
  import { Element as E } from './types';
4
3
 
5
4
 
@@ -70,24 +69,12 @@ const text = (value: string) => {
70
69
 
71
70
  export {
72
71
  addEventListener,
73
- className,
74
- cloneNode,
75
- defineProperty,
76
- firstChild,
77
- firstElementChild,
78
- fragment,
72
+ className, cloneNode,
73
+ firstChild, firstElementChild, fragment,
79
74
  innerHTML,
80
- isArray,
81
- isObject,
82
- nextElementSibling,
83
- nextSibling,
84
- nodeValue,
85
- parentElement,
86
- parentNode,
87
- prepend,
88
- raf,
89
- removeAttribute,
90
- removeEventListener,
75
+ nextElementSibling, nextSibling, nodeValue,
76
+ parentElement, parentNode, prepend,
77
+ raf, removeAttribute, removeEventListener,
91
78
  setAttribute,
92
79
  text
93
80
  };