@esportsplus/template 0.21.0 → 0.22.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.
@@ -6,13 +6,14 @@ declare const NODE_SLOT = 4;
6
6
  declare const NODE_VOID = 5;
7
7
  declare const NODE_WHITELIST: Record<string, number>;
8
8
  declare const REGEX_EMPTY_TEXT_NODES: RegExp;
9
+ declare const REGEX_EVENTS: RegExp;
10
+ declare const REGEX_SLOT_ATTRIBUTES: RegExp;
9
11
  declare const REGEX_SLOT_NODES: RegExp;
10
12
  declare const RENDERABLE: unique symbol;
11
13
  declare const RENDERABLE_HTML_REACTIVE_ARRAY = 1;
12
14
  declare const SLOT_HTML = "<!--$-->";
13
15
  declare const SLOT_MARKER = "{{$}}";
14
- declare const SLOT_MARKER_LENGTH: number;
15
16
  declare const STATE_HYDRATING = 0;
16
17
  declare const STATE_NONE = 1;
17
18
  declare const STATE_WAITING = 2;
18
- export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH, STATE_HYDRATING, STATE_NONE, STATE_WAITING };
19
+ export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY, SLOT_HTML, SLOT_MARKER, STATE_HYDRATING, STATE_NONE, STATE_WAITING };
@@ -27,13 +27,14 @@ const NODE_WHITELIST = {
27
27
  'wbr': NODE_VOID
28
28
  };
29
29
  const REGEX_EMPTY_TEXT_NODES = /(>|})\s+(<|{)/g;
30
+ const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=(?:\s*["'][^"']*["'])*)/g;
31
+ const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
30
32
  const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
31
33
  const RENDERABLE = Symbol();
32
34
  const RENDERABLE_HTML_REACTIVE_ARRAY = 1;
33
35
  const SLOT_HTML = '<!--$-->';
34
36
  const SLOT_MARKER = '{{$}}';
35
- const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
36
37
  const STATE_HYDRATING = 0;
37
38
  const STATE_NONE = 1;
38
39
  const STATE_WAITING = 2;
39
- export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH, STATE_HYDRATING, STATE_NONE, STATE_WAITING };
40
+ export { CLEANUP, EMPTY_FRAGMENT, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY, SLOT_HTML, SLOT_MARKER, STATE_HYDRATING, STATE_NONE, STATE_WAITING };
@@ -1,3 +1,4 @@
1
+ import { onCleanup } from '@esportsplus/reactivity';
1
2
  let listeners = new Map(), registered = false;
2
3
  function onresize() {
3
4
  for (let [element, fn] of listeners) {
@@ -15,6 +16,9 @@ function onresize() {
15
16
  }
16
17
  export default (element, listener) => {
17
18
  listeners.set(element, listener);
19
+ onCleanup(() => {
20
+ listeners.delete(element);
21
+ });
18
22
  if (!registered) {
19
23
  window.addEventListener('resize', onresize);
20
24
  registered = true;
@@ -1,6 +1,6 @@
1
1
  import { ReactiveArray } from '@esportsplus/reactivity';
2
- import { Attributes, Renderable, RenderableReactive } from '../types.js';
3
- type Values<T> = Attributes<any> | Renderable<T>;
2
+ import { Attribute, Attributes, Renderable, RenderableReactive } from '../types.js';
3
+ type Values<T> = Attribute | Attributes<any> | Renderable<T>;
4
4
  declare const html: {
5
5
  <T>(literals: TemplateStringsArray, ...values: (Values<T> | Values<T>[])[]): Node;
6
6
  reactive<T>(array: ReactiveArray<T>, template: RenderableReactive<T>["template"]): RenderableReactive<T>;
@@ -6,14 +6,14 @@ const html = (literals, ...values) => {
6
6
  if (slots !== null) {
7
7
  let node, nodePath;
8
8
  for (let i = slots.length - 1; i >= 0; i--) {
9
- let { fn, path, slot } = slots[i];
9
+ let { fn, path } = slots[i];
10
10
  if (nodePath !== path) {
11
11
  node = clone;
12
12
  for (let i = 0, n = path.length; i < n; i++) {
13
13
  node = path[i].call(node);
14
14
  }
15
15
  }
16
- fn(node, values[slot]);
16
+ fn(node, values[i]);
17
17
  }
18
18
  }
19
19
  return clone;
@@ -1,8 +1,8 @@
1
- import { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH } from '../constants.js';
1
+ import { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER } from '../constants.js';
2
2
  import { firstElementChild, nextElementSibling } from '../utilities/element.js';
3
3
  import { firstChild, nextSibling } from '../utilities/node.js';
4
- import { spread } from '../attributes.js';
5
4
  import { fragment } from '../utilities/fragment.js';
5
+ import a from '../attributes.js';
6
6
  import s from '../slot/index.js';
7
7
  let cache = new WeakMap();
8
8
  function build(literals) {
@@ -10,13 +10,67 @@ function build(literals) {
10
10
  if (n === 0) {
11
11
  return set(literals, literals[0]);
12
12
  }
13
- let buffer = '', html = literals.join(SLOT_MARKER)
13
+ let attributes = {}, buffer = '', events = false, html = literals.join(SLOT_MARKER)
14
14
  .replace(REGEX_EMPTY_TEXT_NODES, '$1$2')
15
15
  .trim(), index = 0, level = 0, levels = [{
16
16
  children: 0,
17
17
  elements: 0,
18
18
  path: []
19
19
  }], parsed = html.split(SLOT_MARKER), slot = 0, slots = [];
20
+ {
21
+ let attribute = '', buffer = '', char = '', quote = '';
22
+ for (let match of html.matchAll(REGEX_SLOT_ATTRIBUTES)) {
23
+ let found = match[1], metadata = attributes[found];
24
+ if (metadata) {
25
+ continue;
26
+ }
27
+ metadata = attributes[found] = [];
28
+ for (let i = 0, n = found.length; i < n; i++) {
29
+ char = found[i];
30
+ if (char === ' ') {
31
+ buffer = '';
32
+ }
33
+ else if (char === '=') {
34
+ attribute = buffer;
35
+ buffer = '';
36
+ }
37
+ else if (char === '"' || char === "'") {
38
+ if (!attribute) {
39
+ continue;
40
+ }
41
+ else if (!quote) {
42
+ quote = char;
43
+ }
44
+ else if (quote === char) {
45
+ attribute = '';
46
+ buffer = '';
47
+ quote = '';
48
+ }
49
+ }
50
+ else if (char === '{' && char !== buffer) {
51
+ buffer = char;
52
+ }
53
+ else {
54
+ buffer += char;
55
+ if (buffer === SLOT_MARKER) {
56
+ buffer = '';
57
+ if (attribute) {
58
+ metadata.push(attribute);
59
+ if (!quote) {
60
+ attribute = '';
61
+ }
62
+ }
63
+ else {
64
+ metadata.push(null);
65
+ }
66
+ }
67
+ else if (buffer === 'on') {
68
+ events = true;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
20
74
  for (let match of html.matchAll(REGEX_SLOT_NODES)) {
21
75
  let parent = levels[level], type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
22
76
  if ((match.index || 1) - 1 > index) {
@@ -27,15 +81,18 @@ function build(literals) {
27
81
  ? methods(parent.elements, parent.path, firstElementChild, nextElementSibling)
28
82
  : methods(parent.children, [], firstChild, nextSibling);
29
83
  if (attr) {
30
- let i = attr.indexOf(SLOT_MARKER);
31
- while (i !== -1) {
84
+ let metadata = attributes[attr];
85
+ if (!metadata) {
86
+ throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
87
+ }
88
+ for (let i = 0, n = metadata.length; i < n; i++) {
89
+ let name = metadata[i];
32
90
  slots.push({
33
- fn: spread,
34
- path,
35
- slot
91
+ fn: name === null ? a.spread : a.set,
92
+ name,
93
+ path
36
94
  });
37
95
  buffer += parsed[slot++];
38
- i = attr.indexOf(SLOT_MARKER, i + SLOT_MARKER_LENGTH);
39
96
  }
40
97
  }
41
98
  if (type === NODE_ELEMENT) {
@@ -48,14 +105,14 @@ function build(literals) {
48
105
  parent.elements++;
49
106
  }
50
107
  else if (type === NODE_SLOT) {
51
- buffer += parsed[slot] + SLOT_HTML;
108
+ buffer += parsed[slot++] + SLOT_HTML;
52
109
  slots.push({
53
110
  fn: s,
54
- path: methods(parent.children, parent.path, firstChild, nextSibling),
55
- slot: slot++
111
+ name: null,
112
+ path: methods(parent.children, parent.path, firstChild, nextSibling)
56
113
  });
57
114
  }
58
- if (slot === n) {
115
+ if (n === slot) {
59
116
  buffer += parsed[slot];
60
117
  break;
61
118
  }
@@ -67,6 +124,9 @@ function build(literals) {
67
124
  }
68
125
  index = (match.index || 0) + match[0].length;
69
126
  }
127
+ if (events) {
128
+ buffer = buffer.replace(REGEX_EVENTS, '');
129
+ }
70
130
  return set(literals, buffer, slots);
71
131
  }
72
132
  function methods(children, copy, first, next) {
package/build/types.d.ts CHANGED
@@ -4,7 +4,7 @@ import { firstChild } from './utilities/node.js';
4
4
  import attributes from './attributes.js';
5
5
  import slot from './slot/index.js';
6
6
  import html from './html/index.js';
7
- type Attribute = Effect<Primitive | Primitive[]> | Primitive;
7
+ type Attribute = Effect<Primitive | Primitive[]> | (<T>(...args: T[]) => void) | Primitive;
8
8
  type Attributes<T extends HTMLElement = Element> = {
9
9
  [key: `aria-${string}`]: string | number | boolean | undefined;
10
10
  [key: `data-${string}`]: string | undefined;
@@ -12,10 +12,10 @@ type Attributes<T extends HTMLElement = Element> = {
12
12
  onconnect?: (element: T) => void;
13
13
  ondisconnect?: (element: T) => void;
14
14
  onrender?: (element: T) => void;
15
- ontick?: (element: T) => void;
15
+ ontick?: (dispose: VoidFunction, element: T) => void;
16
16
  style?: Attribute | Attribute[];
17
17
  } & {
18
- [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: HTMLElement, event: GlobalEventHandlersEventMap[K]) => void;
18
+ [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: T, event: GlobalEventHandlersEventMap[K]) => void;
19
19
  } & Record<PropertyKey, unknown>;
20
20
  type Effect<T> = () => T extends [] ? Renderable<T>[] : Renderable<T>;
21
21
  type Element = HTMLElement & Attributes<any>;
@@ -35,9 +35,9 @@ type Template = {
35
35
  html: string;
36
36
  literals: TemplateStringsArray;
37
37
  slots: {
38
- fn: typeof attributes.spread | typeof slot;
38
+ fn: typeof attributes.set | typeof attributes.spread | typeof slot;
39
+ name: string | null;
39
40
  path: typeof firstChild[];
40
- slot: number;
41
41
  }[] | null;
42
42
  };
43
- export type { Attributes, Effect, Element, Renderable, RenderableReactive, SlotGroup, Template };
43
+ export type { Attribute, Attributes, Effect, Element, Renderable, RenderableReactive, SlotGroup, Template };
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "private": false,
15
15
  "type": "module",
16
16
  "types": "./build/index.d.ts",
17
- "version": "0.21.0",
17
+ "version": "0.22.0",
18
18
  "scripts": {
19
19
  "build": "tsc && tsc-alias",
20
20
  "-": "-"
package/src/constants.ts CHANGED
@@ -42,6 +42,10 @@ const NODE_WHITELIST: Record<string, number> = {
42
42
 
43
43
  const REGEX_EMPTY_TEXT_NODES = /(>|})\s+(<|{)/g;
44
44
 
45
+ const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=(?:\s*["'][^"']*["'])*)/g;
46
+
47
+ const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
48
+
45
49
  const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
46
50
 
47
51
 
@@ -54,8 +58,6 @@ const SLOT_HTML = '<!--$-->';
54
58
 
55
59
  const SLOT_MARKER = '{{$}}';
56
60
 
57
- const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
58
-
59
61
 
60
62
  const STATE_HYDRATING = 0;
61
63
 
@@ -68,8 +70,7 @@ export {
68
70
  CLEANUP,
69
71
  EMPTY_FRAGMENT,
70
72
  NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
71
- REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES,
73
+ REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES,
72
74
  RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY,
73
- SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH,
74
- STATE_HYDRATING, STATE_NONE, STATE_WAITING
75
+ SLOT_HTML, SLOT_MARKER, STATE_HYDRATING, STATE_NONE, STATE_WAITING
75
76
  };
@@ -1,3 +1,4 @@
1
+ import { onCleanup } from '@esportsplus/reactivity';
1
2
  import { Element } from '~/types';
2
3
 
3
4
 
@@ -25,6 +26,10 @@ function onresize() {
25
26
  export default (element: Element, listener: Function) => {
26
27
  listeners.set(element, listener);
27
28
 
29
+ onCleanup(() => {
30
+ listeners.delete(element);
31
+ });
32
+
28
33
  if (!registered) {
29
34
  window.addEventListener('resize', onresize);
30
35
  registered = true;
package/src/html/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { ReactiveArray } from '@esportsplus/reactivity';
2
2
  import { RENDERABLE, RENDERABLE_HTML_REACTIVE_ARRAY } from '~/constants';
3
- import { Attributes, Renderable, RenderableReactive } from '~/types';
3
+ import { Attribute, Attributes, Renderable, RenderableReactive } from '~/types';
4
4
  import { cloneNode } from '~/utilities/node';
5
5
  import parser from './parser';
6
6
 
7
7
 
8
- type Values<T> = Attributes<any> | Renderable<T>;
8
+ type Values<T> = Attribute | Attributes<any> | Renderable<T>;
9
9
 
10
10
 
11
11
  const html = <T>(literals: TemplateStringsArray, ...values: (Values<T> | Values<T>[])[]) => {
@@ -16,7 +16,7 @@ const html = <T>(literals: TemplateStringsArray, ...values: (Values<T> | Values<
16
16
  let node, nodePath;
17
17
 
18
18
  for (let i = slots.length - 1; i >= 0; i--) {
19
- let { fn, path, slot } = slots[i];
19
+ let { fn, path } = slots[i];
20
20
 
21
21
  if (nodePath !== path) {
22
22
  node = clone;
@@ -27,7 +27,7 @@ const html = <T>(literals: TemplateStringsArray, ...values: (Values<T> | Values<
27
27
  }
28
28
 
29
29
  // @ts-ignore
30
- fn(node, values[slot]);
30
+ fn(node, values[i]);
31
31
  }
32
32
  }
33
33
 
@@ -1,12 +1,13 @@
1
1
  import {
2
- NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES,
3
- REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
2
+ NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
3
+ REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES,
4
+ SLOT_HTML, SLOT_MARKER
4
5
  } from '~/constants';
5
6
  import { Template } from '~/types';
6
7
  import { firstElementChild, nextElementSibling } from '~/utilities/element';
7
8
  import { firstChild, nextSibling } from '~/utilities/node';
8
- import { spread } from '~/attributes';
9
9
  import { fragment } from '~/utilities/fragment';
10
+ import a from '~/attributes';
10
11
  import s from '~/slot';
11
12
 
12
13
 
@@ -20,7 +21,9 @@ function build(literals: TemplateStringsArray) {
20
21
  return set(literals, literals[0]);
21
22
  }
22
23
 
23
- let buffer = '',
24
+ let attributes: Record<string, (null | string)[]> = {},
25
+ buffer = '',
26
+ events = false,
24
27
  html = literals.join(SLOT_MARKER)
25
28
  .replace(REGEX_EMPTY_TEXT_NODES, '$1$2')
26
29
  .trim(),
@@ -35,6 +38,73 @@ function build(literals: TemplateStringsArray) {
35
38
  slot = 0,
36
39
  slots: Template['slots'] = [];
37
40
 
41
+ {
42
+ let attribute = '',
43
+ buffer = '',
44
+ char = '',
45
+ quote = '';
46
+
47
+ for (let match of html.matchAll(REGEX_SLOT_ATTRIBUTES)) {
48
+ let found = match[1],
49
+ metadata = attributes[found];
50
+
51
+ if (metadata) {
52
+ continue;
53
+ }
54
+
55
+ metadata = attributes[found] = [];
56
+
57
+ for (let i = 0, n = found.length; i < n; i++) {
58
+ char = found[i];
59
+
60
+ if (char === ' ') {
61
+ buffer = '';
62
+ }
63
+ else if (char === '=') {
64
+ attribute = buffer;
65
+ buffer = '';
66
+ }
67
+ else if (char === '"' || char === "'") {
68
+ if (!attribute) {
69
+ continue;
70
+ }
71
+ else if (!quote) {
72
+ quote = char;
73
+ }
74
+ else if (quote === char) {
75
+ attribute = '';
76
+ buffer = '';
77
+ quote = '';
78
+ }
79
+ }
80
+ else if (char === '{' && char !== buffer) {
81
+ buffer = char;
82
+ }
83
+ else {
84
+ buffer += char;
85
+
86
+ if (buffer === SLOT_MARKER) {
87
+ buffer = '';
88
+
89
+ if (attribute) {
90
+ metadata.push(attribute);
91
+
92
+ if (!quote) {
93
+ attribute = '';
94
+ }
95
+ }
96
+ else {
97
+ metadata.push(null);
98
+ }
99
+ }
100
+ else if (buffer === 'on') {
101
+ events = true;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+
38
108
  for (let match of html.matchAll(REGEX_SLOT_NODES)) {
39
109
  let parent = levels[level],
40
110
  type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
@@ -51,17 +121,22 @@ function build(literals: TemplateStringsArray) {
51
121
  : methods(parent.children, [], firstChild, nextSibling);
52
122
 
53
123
  if (attr) {
54
- let i = attr.indexOf(SLOT_MARKER);
124
+ let metadata = attributes[attr];
125
+
126
+ if (!metadata) {
127
+ throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
128
+ }
129
+
130
+ for (let i = 0, n = metadata.length; i < n; i++) {
131
+ let name = metadata[i];
55
132
 
56
- while (i !== -1) {
57
133
  slots.push({
58
- fn: spread,
59
- path,
60
- slot
134
+ fn: name === null ? a.spread : a.set,
135
+ name,
136
+ path
61
137
  });
62
138
 
63
139
  buffer += parsed[slot++];
64
- i = attr.indexOf(SLOT_MARKER, i + SLOT_MARKER_LENGTH);
65
140
  }
66
141
  }
67
142
 
@@ -76,15 +151,15 @@ function build(literals: TemplateStringsArray) {
76
151
  parent.elements++;
77
152
  }
78
153
  else if (type === NODE_SLOT) {
79
- buffer += parsed[slot] + SLOT_HTML;
154
+ buffer += parsed[slot++] + SLOT_HTML;
80
155
  slots.push({
81
156
  fn: s,
82
- path: methods(parent.children, parent.path, firstChild, nextSibling),
83
- slot: slot++
157
+ name: null,
158
+ path: methods(parent.children, parent.path, firstChild, nextSibling)
84
159
  });
85
160
  }
86
161
 
87
- if (slot === n) {
162
+ if (n === slot) {
88
163
  buffer += parsed[slot];
89
164
  break;
90
165
  }
@@ -99,6 +174,10 @@ function build(literals: TemplateStringsArray) {
99
174
  index = (match.index || 0) + match[0].length;
100
175
  }
101
176
 
177
+ if (events) {
178
+ buffer = buffer.replace(REGEX_EVENTS, '');
179
+ }
180
+
102
181
  return set(literals, buffer, slots);
103
182
  }
104
183
 
package/src/types.ts CHANGED
@@ -6,7 +6,7 @@ import slot from './slot';
6
6
  import html from './html';
7
7
 
8
8
 
9
- type Attribute = Effect<Primitive | Primitive[]> | Primitive;
9
+ type Attribute = Effect<Primitive | Primitive[]> | (<T>(...args: T[]) => void) | Primitive;
10
10
 
11
11
  type Attributes<T extends HTMLElement = Element> = {
12
12
  [key: `aria-${string}`]: string | number | boolean | undefined;
@@ -15,10 +15,10 @@ type Attributes<T extends HTMLElement = Element> = {
15
15
  onconnect?: (element: T) => void;
16
16
  ondisconnect?: (element: T) => void;
17
17
  onrender?: (element: T) => void;
18
- ontick?: (element: T) => void;
18
+ ontick?: (dispose: VoidFunction, element: T) => void;
19
19
  style?: Attribute | Attribute[];
20
20
  } & {
21
- [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: HTMLElement, event: GlobalEventHandlersEventMap[K]) => void;
21
+ [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: T, event: GlobalEventHandlersEventMap[K]) => void;
22
22
  } & Record<PropertyKey, unknown>;
23
23
 
24
24
  type Effect<T> = () => T extends [] ? Renderable<T>[] : Renderable<T>;
@@ -51,15 +51,15 @@ type Template = {
51
51
  html: string;
52
52
  literals: TemplateStringsArray;
53
53
  slots: {
54
- fn: typeof attributes.spread | typeof slot;
54
+ fn: typeof attributes.set | typeof attributes.spread | typeof slot;
55
+ name: string | null;
55
56
  path: typeof firstChild[];
56
- slot: number;
57
57
  }[] | null;
58
58
  };
59
59
 
60
60
 
61
61
  export type {
62
- Attributes,
62
+ Attribute, Attributes,
63
63
  Effect, Element,
64
64
  Renderable, RenderableReactive,
65
65
  SlotGroup,