@esportsplus/template 0.13.0 → 0.14.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.

Potentially problematic release.


This version of @esportsplus/template might be problematic. Click here for more details.

@@ -9,7 +9,8 @@ declare const RENDERABLE: unique symbol;
9
9
  declare const RENDERABLE_REACTIVE: unique symbol;
10
10
  declare const RENDERABLE_TEMPLATE: unique symbol;
11
11
  declare const SLOT: unique symbol;
12
+ declare const SLOT_CLEANUP: unique symbol;
12
13
  declare const SLOT_HTML = "<!--$-->";
13
14
  declare const SLOT_MARKER = "{{$}}";
14
15
  declare const SLOT_MARKER_LENGTH: number;
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, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
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 };
@@ -29,7 +29,8 @@ const RENDERABLE = Symbol();
29
29
  const RENDERABLE_REACTIVE = Symbol();
30
30
  const RENDERABLE_TEMPLATE = Symbol();
31
31
  const SLOT = Symbol();
32
+ const SLOT_CLEANUP = Symbol();
32
33
  const SLOT_HTML = '<!--$-->';
33
34
  const SLOT_MARKER = '{{$}}';
34
35
  const SLOT_MARKER_LENGTH = 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, SLOT_HTML, SLOT_MARKER, 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 };
package/build/event.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { Element } from './types.js';
2
- declare const _default: (element: Element, event: string, listener: Function) => void;
2
+ declare const _default: (element: Element, event: `on${string}`, listener: Function) => any;
3
3
  export default _default;
package/build/event.js CHANGED
@@ -1,33 +1,15 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
+ import { SLOT_CLEANUP } from './constants.js';
2
3
  import { addEventListener, defineProperty, parentElement } from './utilities.js';
3
- let capture = new Set(['blur', 'focus', 'scroll']), keys = {}, passive = new Set([
4
- 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mousewheel',
5
- 'scroll',
6
- 'touchcancel', 'touchend', 'touchleave', 'touchmove', 'touchstart',
7
- 'wheel'
4
+ let capture = new Set(['onblur', 'onfocus', 'onscroll']), controllers = new Map(), keys = {}, passive = new Set([
5
+ 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
6
+ 'onscroll',
7
+ 'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart',
8
+ 'onwheel'
8
9
  ]);
9
- function register(event) {
10
- let key = keys[event] = Symbol(), type = event.slice(2);
11
- addEventListener.call(window.document, type, (e) => {
12
- let node = e.target;
13
- defineProperty(e, 'currentTarget', {
14
- configurable: true,
15
- get() {
16
- return node || window.document;
17
- }
18
- });
19
- while (node) {
20
- if (key in node) {
21
- return node[key].call(node, e);
22
- }
23
- node = parentElement.call(node);
24
- }
25
- }, {
26
- capture: capture.has(type),
27
- passive: passive.has(type)
28
- });
29
- return key;
30
- }
10
+ ['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'].map(event => {
11
+ controllers.set(event, null);
12
+ });
31
13
  export default (element, event, listener) => {
32
14
  if (event === 'onmount') {
33
15
  let interval = setInterval(() => {
@@ -45,5 +27,48 @@ export default (element, event, listener) => {
45
27
  else if (event === 'onrender') {
46
28
  return root(() => listener(element));
47
29
  }
48
- element[keys[event] || register(event)] = listener;
30
+ let controller = controllers.get(event), signal;
31
+ if (controller === null) {
32
+ let { abort, signal } = new AbortController();
33
+ controllers.set(event, controller = {
34
+ abort,
35
+ signal,
36
+ listeners: 0,
37
+ });
38
+ }
39
+ if (controller) {
40
+ controller.listeners++;
41
+ (element[SLOT_CLEANUP] ??= []).push(() => {
42
+ if (--controller.listeners) {
43
+ return;
44
+ }
45
+ controller.abort();
46
+ controllers.set(event, null);
47
+ });
48
+ signal = controller.signal;
49
+ }
50
+ let key = keys[event];
51
+ if (!key) {
52
+ key = keys[event] = Symbol();
53
+ addEventListener.call(window.document, event.slice(2), (e) => {
54
+ let node = e.target;
55
+ defineProperty(e, 'currentTarget', {
56
+ configurable: true,
57
+ get() {
58
+ return node || window.document;
59
+ }
60
+ });
61
+ while (node) {
62
+ if (key in node) {
63
+ return node[key].call(node, e);
64
+ }
65
+ node = parentElement.call(node);
66
+ }
67
+ }, {
68
+ capture: capture.has(event),
69
+ passive: passive.has(event),
70
+ signal
71
+ });
72
+ }
73
+ element[key] = listener;
49
74
  };
package/build/slot.d.ts CHANGED
@@ -10,10 +10,10 @@ declare class Slot {
10
10
  set length(n: number);
11
11
  anchor(index?: number): Element;
12
12
  clear(): void;
13
- pop(): Elements | undefined;
13
+ pop(): Elements[] | undefined;
14
14
  push(...groups: Elements[]): number;
15
15
  render(input: unknown): this;
16
- shift(): Elements | undefined;
16
+ shift(): Elements[] | undefined;
17
17
  splice(start: number, stop?: number, ...groups: Elements[]): Elements[];
18
18
  unshift(...groups: Elements[]): number;
19
19
  }
package/build/slot.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { effect, root, DIRTY } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, SLOT } from './constants.js';
2
+ import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants.js';
3
3
  import { hydrate } from './html/index.js';
4
4
  import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities.js';
5
- let cleanup = [], key = Symbol(), scheduled = false;
6
- function afterGroups(anchor, groups) {
5
+ let cleanup = [], scheduled = false;
6
+ function after(anchor, groups) {
7
7
  for (let i = 0, n = groups.length; i < n; i++) {
8
8
  let group = groups[i];
9
9
  if (group.length) {
@@ -13,29 +13,13 @@ function afterGroups(anchor, groups) {
13
13
  }
14
14
  return groups;
15
15
  }
16
- function removeGroup(group) {
17
- if (group === undefined) {
18
- return group;
19
- }
20
- for (let i = 0, n = group.length; i < n; i++) {
21
- let item = group[i];
22
- if (key in item) {
23
- cleanup.push(item[key]);
24
- }
25
- item.remove();
26
- }
27
- if (!scheduled && cleanup.length) {
28
- schedule();
29
- }
30
- return group;
31
- }
32
- function removeGroups(groups) {
16
+ function remove(...groups) {
33
17
  for (let i = 0, n = groups.length; i < n; i++) {
34
18
  let group = groups[i];
35
19
  for (let j = 0, o = group.length; j < o; j++) {
36
20
  let item = group[j];
37
- if (key in item) {
38
- cleanup.push(item[key]);
21
+ if (item[SLOT_CLEANUP]) {
22
+ cleanup.push(...item[SLOT_CLEANUP]);
39
23
  }
40
24
  item.remove();
41
25
  }
@@ -55,12 +39,12 @@ function render(anchor, input, slot) {
55
39
  for (let i = 0, n = input.length; i < n; i++) {
56
40
  groups.push(render(null, input[i]));
57
41
  }
58
- return anchor ? afterGroups(anchor, groups) : groups;
42
+ return anchor ? after(anchor, groups) : groups;
59
43
  }
60
44
  let nodes = [];
61
45
  if (RENDERABLE in input) {
62
46
  if (input[RENDERABLE] === RENDERABLE_REACTIVE) {
63
- return afterGroups(anchor, hydrate.reactive(input, slot));
47
+ return after(anchor, hydrate.reactive(input, slot));
64
48
  }
65
49
  else {
66
50
  nodes = hydrate.static(input);
@@ -98,13 +82,16 @@ function schedule() {
98
82
  scheduled = true;
99
83
  raf.add(() => {
100
84
  try {
101
- let slot;
102
- while (slot = cleanup.pop()) {
103
- slot.clear();
85
+ let fn;
86
+ while (fn = cleanup.pop()) {
87
+ fn();
104
88
  }
105
89
  }
106
90
  catch (e) { }
107
91
  scheduled = false;
92
+ if (cleanup.length) {
93
+ schedule();
94
+ }
108
95
  });
109
96
  }
110
97
  class Slot {
@@ -113,7 +100,9 @@ class Slot {
113
100
  nodes;
114
101
  text = null;
115
102
  constructor(marker) {
116
- marker[key] = this;
103
+ (marker[SLOT_CLEANUP] ??= []).push(() => {
104
+ this.clear();
105
+ });
117
106
  this.marker = marker;
118
107
  this.nodes = [];
119
108
  }
@@ -131,14 +120,18 @@ class Slot {
131
120
  return node || this.marker;
132
121
  }
133
122
  clear() {
134
- removeGroups(this.nodes);
123
+ remove(...this.nodes);
135
124
  this.text = null;
136
125
  }
137
126
  pop() {
138
- return removeGroup(this.nodes.pop());
127
+ let group = this.nodes.pop();
128
+ if (!group) {
129
+ return undefined;
130
+ }
131
+ return remove(group);
139
132
  }
140
133
  push(...groups) {
141
- afterGroups(this.anchor(), groups);
134
+ after(this.anchor(), groups);
142
135
  for (let i = 0, n = groups.length; i < n; i++) {
143
136
  this.nodes.push(groups[i]);
144
137
  }
@@ -182,13 +175,17 @@ class Slot {
182
175
  return this;
183
176
  }
184
177
  shift() {
185
- return removeGroup(this.nodes.shift());
178
+ let group = this.nodes.shift();
179
+ if (!group) {
180
+ return undefined;
181
+ }
182
+ return remove(group);
186
183
  }
187
184
  splice(start, stop = this.nodes.length, ...groups) {
188
- return removeGroups(this.nodes.splice(start, stop, ...afterGroups(this.anchor(start), groups)));
185
+ return remove(...this.nodes.splice(start, stop, ...after(this.anchor(start), groups)));
189
186
  }
190
187
  unshift(...groups) {
191
- return this.nodes.unshift(...afterGroups(this.marker, groups));
188
+ return this.nodes.unshift(...after(this.marker, groups));
192
189
  }
193
190
  }
194
191
  export default (marker, value) => {
package/build/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactiveArray } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE } from './constants.js';
2
+ import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP } from './constants.js';
3
3
  import { firstChild } from './utilities.js';
4
4
  import attributes from './attributes.js';
5
5
  import event from './event.js';
@@ -17,7 +17,9 @@ type Attributes = {
17
17
  } & Record<PropertyKey, unknown>;
18
18
  type Effect<T> = () => EffectResponse<T>;
19
19
  type EffectResponse<T> = T extends [] ? EffectResponse<T[number]>[] : Primitive | Renderable<T>;
20
- type Element = HTMLElement & Attributes & Record<PropertyKey, unknown>;
20
+ type Element = HTMLElement & Attributes & {
21
+ [SLOT_CLEANUP]?: VoidFunction[];
22
+ } & Record<PropertyKey, unknown>;
21
23
  type Elements = Element[];
22
24
  type Primitive = bigint | boolean | null | number | string | undefined;
23
25
  type Renderable<T = unknown> = RenderableReactive<T> | RenderableTemplate<T>;
package/build/types.js CHANGED
@@ -1 +1 @@
1
- import { RENDERABLE } from './constants.js';
1
+ import { RENDERABLE, SLOT_CLEANUP } from './constants.js';
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "private": false,
14
14
  "type": "module",
15
15
  "types": "./build/index.d.ts",
16
- "version": "0.13.0",
16
+ "version": "0.14.0",
17
17
  "scripts": {
18
18
  "build": "tsc && tsc-alias",
19
19
  "-": "-"
package/src/attributes.ts CHANGED
@@ -67,7 +67,7 @@ function set(element: Element, value: unknown, name: string) {
67
67
  }
68
68
  else if (typeof value === 'function') {
69
69
  if (name.startsWith('on')) {
70
- event(element, name, value);
70
+ event(element, name as `on${string}`, value);
71
71
  }
72
72
  else {
73
73
  reactive(element, ('e' + store(element)[key]++), name, value, true);
@@ -197,5 +197,6 @@ const spread = function (element: Element, attributes: Attributes | Attributes[]
197
197
  }
198
198
  };
199
199
 
200
+
200
201
  export default { apply, spread };
201
202
  export { apply, spread }
package/src/constants.ts CHANGED
@@ -45,6 +45,8 @@ const RENDERABLE_TEMPLATE = Symbol();
45
45
 
46
46
  const SLOT = Symbol();
47
47
 
48
+ const SLOT_CLEANUP = Symbol();
49
+
48
50
  const SLOT_HTML = '<!--$-->';
49
51
 
50
52
  const SLOT_MARKER = '{{$}}';
@@ -64,6 +66,7 @@ export {
64
66
  RENDERABLE_REACTIVE,
65
67
  RENDERABLE_TEMPLATE,
66
68
  SLOT,
69
+ SLOT_CLEANUP,
67
70
  SLOT_HTML,
68
71
  SLOT_MARKER,
69
72
  SLOT_MARKER_LENGTH
package/src/event.ts CHANGED
@@ -1,51 +1,31 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
+ import { SLOT_CLEANUP } from './constants';
2
3
  import { Element } from './types';
3
4
  import { addEventListener, defineProperty, parentElement } from './utilities';
4
5
 
5
6
 
6
- let capture = new Set(['blur', 'focus', 'scroll']),
7
+ let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
8
+ controllers = new Map<
9
+ `on${string}`,
10
+ (AbortController & { listeners: number }) | null
11
+ >(),
7
12
  keys: Record<string, symbol> = {},
8
- passive = new Set([
9
- 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mousewheel',
10
- 'scroll',
11
- 'touchcancel', 'touchend', 'touchleave', 'touchmove', 'touchstart',
12
- 'wheel'
13
+ passive = new Set<`on${string}`>([
14
+ 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
15
+ 'onscroll',
16
+ 'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart',
17
+ 'onwheel'
13
18
  ]);
14
19
 
15
20
 
16
- function register(event: string) {
17
- let key = keys[event] = Symbol(),
18
- type = event.slice(2);
21
+ (['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'] as `on${string}`[]).map(event => {
22
+ controllers.set(event, null);
23
+ });
19
24
 
20
- addEventListener.call(window.document, type, (e) => {
21
- let node = e.target as Element | null;
22
25
 
23
- defineProperty(e, 'currentTarget', {
24
- configurable: true,
25
- get() {
26
- return node || window.document;
27
- }
28
- });
29
-
30
- while (node) {
31
- if (key in node) {
32
- return (node[key] as Function).call(node, e);
33
- }
34
-
35
- node = parentElement.call(node);
36
- }
37
- }, {
38
- capture: capture.has(type),
39
- passive: passive.has(type)
40
- });
41
-
42
- return key;
43
- }
44
-
45
-
46
- export default (element: Element, event: string, listener: Function): void => {
26
+ export default (element: Element, event: `on${string}`, listener: Function) => {
47
27
  if (event === 'onmount') {
48
- let interval: ReturnType<typeof setInterval> = setInterval(() => {
28
+ let interval = setInterval(() => {
49
29
  retry--;
50
30
 
51
31
  if (element.isConnected) {
@@ -66,5 +46,64 @@ export default (element: Element, event: string, listener: Function): void => {
66
46
  return root(() => listener(element));
67
47
  }
68
48
 
69
- element[keys[event] || register(event)] = listener;
49
+ let controller = controllers.get(event),
50
+ signal: AbortController['signal'] | undefined;
51
+
52
+ if (controller === null) {
53
+ let { abort, signal } = new AbortController();
54
+
55
+ controllers.set(
56
+ event,
57
+ controller = {
58
+ abort,
59
+ signal,
60
+ listeners: 0,
61
+ }
62
+ );
63
+ }
64
+
65
+ if (controller) {
66
+ controller.listeners++;
67
+
68
+ ( element[SLOT_CLEANUP] ??= [] ).push(() => {
69
+ if (--controller.listeners) {
70
+ return;
71
+ }
72
+
73
+ controller.abort();
74
+ controllers.set(event, null);
75
+ })
76
+ signal = controller.signal;
77
+ }
78
+
79
+ let key = keys[event];
80
+
81
+ if (!key) {
82
+ key = keys[event] = Symbol();
83
+
84
+ addEventListener.call(window.document, event.slice(2), (e) => {
85
+ let node = e.target as Element | null;
86
+
87
+ defineProperty(e, 'currentTarget', {
88
+ configurable: true,
89
+ get() {
90
+ return node || window.document;
91
+ }
92
+ });
93
+
94
+ while (node) {
95
+ if (key in node) {
96
+ return (node[key] as Function).call(node, e);
97
+ }
98
+
99
+ node = parentElement.call(node);
100
+ }
101
+ }, {
102
+ capture: capture.has(event),
103
+ passive: passive.has(event),
104
+ signal
105
+ });
106
+ }
107
+
108
+ element[key] = listener;
70
109
  };
package/src/slot.ts CHANGED
@@ -1,17 +1,15 @@
1
1
  import { effect, root, DIRTY } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, SLOT } from './constants';
2
+ import { RENDERABLE, RENDERABLE_REACTIVE, SLOT, SLOT_CLEANUP } from './constants';
3
3
  import { hydrate } from './html';
4
4
  import { Element, Elements, RenderableReactive, RenderableTemplate } from './types';
5
5
  import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities'
6
6
 
7
7
 
8
- let cleanup: Slot[] = [],
9
- // Using a private symbol since 'SLOT' is used as a different flag in 'render.ts'
10
- key = Symbol(),
8
+ let cleanup: VoidFunction[] = [],
11
9
  scheduled = false;
12
10
 
13
11
 
14
- function afterGroups(anchor: Element, groups: Elements[]) {
12
+ function after(anchor: Element, groups: Elements[]) {
15
13
  for (let i = 0, n = groups.length; i < n; i++) {
16
14
  let group = groups[i];
17
15
 
@@ -24,37 +22,15 @@ function afterGroups(anchor: Element, groups: Elements[]) {
24
22
  return groups;
25
23
  }
26
24
 
27
- function removeGroup(group?: Elements) {
28
- if (group === undefined) {
29
- return group;
30
- }
31
-
32
- for (let i = 0, n = group.length; i < n; i++) {
33
- let item = group[i];
34
-
35
- if (key in item) {
36
- cleanup.push(item[key] as Slot);
37
- }
38
-
39
- item.remove();
40
- }
41
-
42
- if (!scheduled && cleanup.length) {
43
- schedule();
44
- }
45
-
46
- return group;
47
- }
48
-
49
- function removeGroups(groups: Elements[]) {
25
+ function remove(...groups: Elements[]) {
50
26
  for (let i = 0, n = groups.length; i < n; i++) {
51
27
  let group = groups[i];
52
28
 
53
29
  for (let j = 0, o = group.length; j < o; j++) {
54
30
  let item = group[j];
55
31
 
56
- if (key in item) {
57
- cleanup.push(item[key] as Slot);
32
+ if (item[SLOT_CLEANUP]) {
33
+ cleanup.push(...item[SLOT_CLEANUP]);
58
34
  }
59
35
 
60
36
  item.remove();
@@ -80,14 +56,14 @@ function render(anchor: Element | null, input: unknown, slot?: Slot): Elements |
80
56
  groups.push( render(null, input[i]) as Elements );
81
57
  }
82
58
 
83
- return anchor ? afterGroups(anchor, groups) : groups;
59
+ return anchor ? after(anchor, groups) : groups;
84
60
  }
85
61
 
86
62
  let nodes: Elements = [];
87
63
 
88
64
  if (RENDERABLE in input) {
89
65
  if (input[RENDERABLE] === RENDERABLE_REACTIVE) {
90
- return afterGroups(anchor!, hydrate.reactive(input as RenderableReactive, slot!));
66
+ return after(anchor!, hydrate.reactive(input as RenderableReactive, slot!));
91
67
  }
92
68
  else {
93
69
  nodes = hydrate.static(input as RenderableTemplate<unknown>);
@@ -135,15 +111,19 @@ function schedule() {
135
111
 
136
112
  raf.add(() => {
137
113
  try {
138
- let slot;
114
+ let fn;
139
115
 
140
- while (slot = cleanup.pop()) {
141
- slot.clear();
116
+ while (fn = cleanup.pop()) {
117
+ fn();
142
118
  }
143
119
  }
144
- catch(e) {}
120
+ catch(e) { }
145
121
 
146
122
  scheduled = false;
123
+
124
+ if (cleanup.length) {
125
+ schedule();
126
+ }
147
127
  });
148
128
  }
149
129
 
@@ -157,7 +137,9 @@ class Slot {
157
137
 
158
138
 
159
139
  constructor(marker: Element) {
160
- marker[key] = this;
140
+ ( marker[SLOT_CLEANUP] ??= [] ).push(() => {
141
+ this.clear();
142
+ });
161
143
 
162
144
  this.marker = marker;
163
145
  this.nodes = [];
@@ -185,16 +167,22 @@ class Slot {
185
167
  }
186
168
 
187
169
  clear() {
188
- removeGroups(this.nodes);
170
+ remove(...this.nodes);
189
171
  this.text = null;
190
172
  }
191
173
 
192
174
  pop() {
193
- return removeGroup(this.nodes.pop());
175
+ let group = this.nodes.pop();
176
+
177
+ if (!group) {
178
+ return undefined;
179
+ }
180
+
181
+ return remove(group);
194
182
  }
195
183
 
196
184
  push(...groups: Elements[]) {
197
- afterGroups(this.anchor(), groups);
185
+ after(this.anchor(), groups);
198
186
 
199
187
  for (let i = 0, n = groups.length; i < n; i++) {
200
188
  this.nodes.push(groups[i]);
@@ -252,17 +240,23 @@ class Slot {
252
240
  }
253
241
 
254
242
  shift() {
255
- return removeGroup(this.nodes.shift());
243
+ let group = this.nodes.shift();
244
+
245
+ if (!group) {
246
+ return undefined;
247
+ }
248
+
249
+ return remove(group);
256
250
  }
257
251
 
258
252
  splice(start: number, stop: number = this.nodes.length, ...groups: Elements[]) {
259
- return removeGroups(
260
- this.nodes.splice(start, stop, ...afterGroups(this.anchor(start), groups))
253
+ return remove(
254
+ ...this.nodes.splice(start, stop, ...after(this.anchor(start), groups))
261
255
  );
262
256
  }
263
257
 
264
258
  unshift(...groups: Elements[]) {
265
- return this.nodes.unshift(...afterGroups(this.marker, groups));
259
+ return this.nodes.unshift(...after(this.marker, groups));
266
260
  }
267
261
  }
268
262
 
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactiveArray } from '@esportsplus/reactivity';
2
- import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE } from './constants';
2
+ import { RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT_CLEANUP } from './constants';
3
3
  import { firstChild } from './utilities';
4
4
  import attributes from './attributes';
5
5
  import event from './event';
@@ -26,7 +26,7 @@ type Effect<T> = () => EffectResponse<T>;
26
26
 
27
27
  type EffectResponse<T> = T extends [] ? EffectResponse<T[number]>[] : Primitive | Renderable<T>;
28
28
 
29
- type Element = HTMLElement & Attributes & Record<PropertyKey, unknown>;
29
+ type Element = HTMLElement & Attributes & { [SLOT_CLEANUP]?: VoidFunction[] } & Record<PropertyKey, unknown>;
30
30
 
31
31
  type Elements = Element[];
32
32