@oscarpalmer/toretto 0.31.0 → 0.33.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.
package/package.json CHANGED
@@ -4,19 +4,19 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.115"
7
+ "@oscarpalmer/atoms": "^0.119"
8
8
  },
9
9
  "description": "A collection of badass DOM utilities.",
10
10
  "devDependencies": {
11
11
  "@types/node": "^25",
12
12
  "@vitest/coverage-istanbul": "^4",
13
- "jsdom": "^27.3",
14
- "oxfmt": "^0.19",
15
- "oxlint": "^1.34",
16
- "rolldown": "1.0.0-beta.55",
13
+ "jsdom": "^27.4",
14
+ "oxfmt": "^0.21",
15
+ "oxlint": "^1.36",
16
+ "rolldown": "1.0.0-beta.57",
17
17
  "tslib": "^2.8",
18
18
  "typescript": "^5.9",
19
- "vite": "8.0.0-beta.2",
19
+ "vite": "8.0.0-beta.5",
20
20
  "vitest": "^4"
21
21
  },
22
22
  "exports": {
@@ -93,5 +93,5 @@
93
93
  },
94
94
  "type": "module",
95
95
  "types": "types/index.d.ts",
96
- "version": "0.31.0"
96
+ "version": "0.33.0"
97
97
  }
@@ -1,5 +1,28 @@
1
- import {updateValue, updateValues} from '../internal/attribute';
2
- import type {Attribute, Property} from '../models';
1
+ import {updateAttribute} from '../internal/attribute';
2
+ import {setElementValue, setElementValues} from '../internal/element-value';
3
+ import type {Attribute} from '../models';
4
+
5
+ //
6
+
7
+ type DispatchedAttribute = 'checked' | 'open' | 'value';
8
+
9
+ //
10
+
11
+ /**
12
+ * Set an attribute on an element
13
+ *
14
+ * _(Or remove it, if value is `null` or `undefined`)_
15
+ * @param element Element for attribute
16
+ * @param name Attribute name
17
+ * @param value Attribute value
18
+ * @param dispatch Dispatch event for attribute? _(defaults to `true`)_
19
+ */
20
+ export function setAttribute<Name extends DispatchedAttribute>(
21
+ element: Element,
22
+ name: Name,
23
+ value?: unknown,
24
+ dispatch?: boolean,
25
+ ): void;
3
26
 
4
27
  /**
5
28
  * Set an attribute on an element
@@ -17,11 +40,21 @@ export function setAttribute(element: Element, name: string, value?: unknown): v
17
40
  * _(Or remove it, if value is `null` or `undefined`)_
18
41
  * @param element Element for attribute
19
42
  * @param attribute Attribute to set
43
+ * @param dispatch Dispatch event for attribute? _(defaults to `true`)_
20
44
  */
21
- export function setAttribute(element: Element, attribute: Attr | Attribute): void;
45
+ export function setAttribute(
46
+ element: Element,
47
+ attribute: Attr | Attribute,
48
+ dispatch?: boolean,
49
+ ): void;
22
50
 
23
- export function setAttribute(element: Element, first: unknown, second?: unknown): void {
24
- updateValue(element, first, second);
51
+ export function setAttribute(
52
+ element: Element,
53
+ first: unknown,
54
+ second?: unknown,
55
+ third?: unknown,
56
+ ): void {
57
+ setElementValue(element, first, second, third, updateAttribute);
25
58
  }
26
59
 
27
60
  /**
@@ -30,8 +63,13 @@ export function setAttribute(element: Element, first: unknown, second?: unknown)
30
63
  * _(Or remove them, if their value is `null` or `undefined`)_
31
64
  * @param element Element for attributes
32
65
  * @param attributes Attributes to set
66
+ * @param dispatch Dispatch events for relevant attributes? _(defaults to `true`)_
33
67
  */
34
- export function setAttributes(element: Element, attributes: Array<Attr | Attribute>): void;
68
+ export function setAttributes(
69
+ element: Element,
70
+ attributes: Array<Attr | Attribute>,
71
+ dispatch?: boolean,
72
+ ): void;
35
73
 
36
74
  /**
37
75
  * Set one or more attributes on an element
@@ -39,60 +77,18 @@ export function setAttributes(element: Element, attributes: Array<Attr | Attribu
39
77
  * _(Or remove them, if their value is `null` or `undefined`)_
40
78
  * @param element Element for attributes
41
79
  * @param attributes Attributes to set
80
+ * @param dispatch Dispatch events for relevant attributes? _(defaults to `true`)_
42
81
  */
43
- export function setAttributes(element: Element, attributes: Record<string, unknown>): void;
44
-
45
82
  export function setAttributes(
46
83
  element: Element,
47
- attributes: Attribute[] | Record<string, unknown>,
48
- ): void {
49
- updateValues(element, attributes);
50
- }
51
-
52
- /**
53
- * Set a property on an element
54
- *
55
- * _(Or remove it, if value is not an empty string or does not match the name)_
56
- * @param element Element for property
57
- * @param name Property name
58
- * @param value Property value
59
- */
60
- export function setProperty(element: Element, name: string, value: boolean | string): void;
61
-
62
- /**
63
- * Set a property on an element
64
- *
65
- * _(Or remove it, if value is not an empty string or does not match the name)_
66
- * @param element Element for property
67
- * @param property Property to set
68
- */
69
- export function setProperty(element: Element, property: Property): void;
70
-
71
- export function setProperty(element: Element, first: unknown, second?: unknown): void {
72
- updateValue(element, first, second);
73
- }
84
+ attributes: Record<string, unknown>,
85
+ dispatch?: boolean,
86
+ ): void;
74
87
 
75
- /**
76
- * Set one or more properties on an element
77
- *
78
- * _(Or remove them, if their value is not an empty string or does not match the name)_
79
- * @param element Element for properties
80
- * @param properties Properties to set
81
- */
82
- export function setProperties(element: Element, properties: Property[]): void;
83
-
84
- /**
85
- * Set one or more properties on an element
86
- *
87
- * _(Or remove them, if their value is not an empty string or does not match the name)_
88
- * @param element Element for properties
89
- * @param properties Properties to set
90
- */
91
- export function setProperties(element: Element, properties: Record<string, unknown>): void;
92
-
93
- export function setProperties(
88
+ export function setAttributes(
94
89
  element: Element,
95
- properties: Property[] | Record<string, unknown>,
90
+ attributes: Attribute[] | Record<string, unknown>,
91
+ dispatch?: boolean,
96
92
  ): void {
97
- updateValues(element, properties);
93
+ setElementValues(element, attributes, null, dispatch, updateAttribute);
98
94
  }
package/src/data.ts CHANGED
@@ -81,7 +81,7 @@ export function setData(element: Element, data: PlainObject): void;
81
81
  export function setData(element: Element, key: string, value: unknown): void;
82
82
 
83
83
  export function setData(element: Element, first: PlainObject | string, second?: unknown): void {
84
- setElementValues(element, first, second, updateDataAttribute);
84
+ setElementValues(element, first, second, null, updateDataAttribute);
85
85
  }
86
86
 
87
87
  function updateDataAttribute(element: Element, key: string, value: unknown): void {
@@ -91,6 +91,7 @@ function updateDataAttribute(element: Element, key: string, value: unknown): voi
91
91
  value,
92
92
  element.setAttribute,
93
93
  element.removeAttribute,
94
+ false,
94
95
  true,
95
96
  );
96
97
  }
@@ -3,31 +3,17 @@ import type {CustomEventListener, RemovableEventListener} from '../models';
3
3
 
4
4
  //
5
5
 
6
- type DocumentWithListenerCounts = Document &
7
- Partial<{
8
- [key: string]: number;
9
- }>;
10
-
11
6
  export type EventTargetWithListeners = EventTarget &
12
7
  Partial<{
13
8
  [key: string]: Set<EventListener | CustomEventListener>;
14
9
  }>;
15
10
 
16
- function addDelegatedHandler(
17
- doc: DocumentWithListenerCounts,
18
- type: string,
19
- name: string,
20
- passive: boolean,
21
- ): void {
22
- const count = `${name}${COUNT_SUFFIX}`;
23
-
24
- if (doc[count] != null) {
25
- (doc[count] as number) += 1;
26
-
11
+ function addDelegatedHandler(doc: Document, type: string, name: string, passive: boolean): void {
12
+ if (DELEGATED.has(name)) {
27
13
  return;
28
14
  }
29
15
 
30
- doc[count] = 1;
16
+ DELEGATED.add(name);
31
17
 
32
18
  doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, {
33
19
  passive,
@@ -43,12 +29,12 @@ export function addDelegatedListener(
43
29
  ): RemovableEventListener {
44
30
  target[name] ??= new Set();
45
31
 
46
- target[name]?.add(listener);
32
+ target[name].add(listener);
47
33
 
48
- addDelegatedHandler(document as DocumentWithListenerCounts, type, name, passive);
34
+ addDelegatedHandler(document, type, name, passive);
49
35
 
50
36
  return () => {
51
- removeDelegatedListener(target, type, name, listener, passive);
37
+ removeDelegatedListener(target, name, listener);
52
38
  };
53
39
  }
54
40
 
@@ -58,9 +44,19 @@ function delegatedEventHandler(this: boolean, event: Event): void {
58
44
  const items = event.composedPath();
59
45
  const {length} = items;
60
46
 
61
- Object.defineProperty(event, 'target', {
62
- configurable: true,
63
- value: items[0],
47
+ let target = items[0];
48
+
49
+ Object.defineProperties(event, {
50
+ currentTarget: {
51
+ configurable: true,
52
+ get() {
53
+ return target;
54
+ },
55
+ },
56
+ target: {
57
+ configurable: true,
58
+ value: target,
59
+ },
64
60
  });
65
61
 
66
62
  for (let index = 0; index < length; index += 1) {
@@ -71,10 +67,7 @@ function delegatedEventHandler(this: boolean, event: Event): void {
71
67
  continue;
72
68
  }
73
69
 
74
- Object.defineProperty(event, 'currentTarget', {
75
- configurable: true,
76
- value: item,
77
- });
70
+ target = item;
78
71
 
79
72
  for (const listener of listeners) {
80
73
  (listener as EventListener).call(item, event);
@@ -102,29 +95,10 @@ export function getDelegatedName(
102
95
  }
103
96
  }
104
97
 
105
- function removeDelegatedHandler(
106
- doc: DocumentWithListenerCounts,
107
- type: string,
108
- name: string,
109
- passive: boolean,
110
- ): void {
111
- const count = `${name}${COUNT_SUFFIX}`;
112
-
113
- (doc[count] as number) -= 1;
114
-
115
- if ((doc[count] as number) < 1) {
116
- doc[count] = undefined;
117
-
118
- doc.removeEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE);
119
- }
120
- }
121
-
122
98
  export function removeDelegatedListener(
123
99
  target: EventTargetWithListeners,
124
- type: string,
125
100
  name: string,
126
101
  listener: EventListener | CustomEventListener,
127
- passive: boolean,
128
102
  ): boolean {
129
103
  const handlers = target[name];
130
104
 
@@ -138,14 +112,12 @@ export function removeDelegatedListener(
138
112
  target[name] = undefined;
139
113
  }
140
114
 
141
- removeDelegatedHandler(document as DocumentWithListenerCounts, type, name, passive);
142
-
143
115
  return true;
144
116
  }
145
117
 
146
118
  //
147
119
 
148
- const COUNT_SUFFIX = '.count';
120
+ const DELEGATED = new Set<string>();
149
121
 
150
122
  const EVENT_PREFIX = '@';
151
123
 
@@ -178,6 +150,6 @@ const EVENT_TYPES: Set<string> = new Set([
178
150
  'touchstart',
179
151
  ]);
180
152
 
181
- const HANDLER_ACTIVE: EventListener = delegatedEventHandler.bind(false);
153
+ const HANDLER_ACTIVE = delegatedEventHandler.bind(false);
182
154
 
183
- const HANDLER_PASSIVE: EventListener = delegatedEventHandler.bind(true);
155
+ const HANDLER_PASSIVE = delegatedEventHandler.bind(true);
@@ -151,13 +151,7 @@ export function off(
151
151
 
152
152
  if (
153
153
  delegated == null ||
154
- !removeDelegatedListener(
155
- target as EventTargetWithListeners,
156
- type,
157
- delegated,
158
- listener,
159
- extended.passive,
160
- )
154
+ !removeDelegatedListener(target as EventTargetWithListeners, delegated, listener)
161
155
  ) {
162
156
  target.removeEventListener(type, listener as EventListener, extended);
163
157
  }
package/src/find/index.ts CHANGED
@@ -171,4 +171,4 @@ const TAG_HEAD = 'HEAD';
171
171
  //
172
172
 
173
173
  export {findElement as $, findElements as $$};
174
- export {findAncestor, findRelatives} from './relative';
174
+ export {findAncestor, findRelatives, getDistance} from './relative';
@@ -1,30 +1,3 @@
1
- /**
2
- * - Get the distance between two elements _(i.e., the amount of nodes of between them)_
3
- * - If the distance cannot be calculated, `-1` is returned
4
- */
5
- function getDistanceBetweenElements(origin: Element, target: Element): number | undefined {
6
- if (origin === target) {
7
- return 0;
8
- }
9
-
10
- if (origin.parentElement === target) {
11
- return 1;
12
- }
13
-
14
- const children = [...(origin.parentElement?.children ?? [])];
15
-
16
- if (children.includes(target)) {
17
- return Math.abs(children.indexOf(origin) - children.indexOf(target));
18
- }
19
-
20
- const comparison = origin.compareDocumentPosition(target);
21
- const beforeOrInside = Boolean(comparison & 2 || comparison & 8);
22
-
23
- if (beforeOrInside || Boolean(comparison & 4 || comparison & 16)) {
24
- return traverse(beforeOrInside ? origin : target, beforeOrInside ? target : origin) ?? -1;
25
- }
26
- }
27
-
28
1
  /**
29
2
  * Find the closest ancestor element that matches the selector _(string or callback)_
30
3
  *
@@ -74,8 +47,7 @@ export function findAncestor(
74
47
  /**
75
48
  * Finds the closest elements to the origin element that matches the selector
76
49
  *
77
- * - Traverses up, down, and sideways in the _DOM_-tree
78
- * - _(If you only want to traverse up, use {@link findAncestor})_
50
+ * Traverses up, down, and sideways in the _DOM_-tree. _(If you only want to traverse up, use {@link findAncestor})_
79
51
  * @param origin Element to start from
80
52
  * @param selector Selector to match
81
53
  * @param context Context to search within
@@ -109,9 +81,9 @@ export function findRelatives(
109
81
 
110
82
  for (let index = 0; index < length; index += 1) {
111
83
  const element = elements[index];
112
- const distance = getDistanceBetweenElements(origin, element);
84
+ const distance = getDistance(origin, element);
113
85
 
114
- if (distance != null && distance > 0) {
86
+ if (distance > 0) {
115
87
  if (minimum == null || distance < minimum) {
116
88
  minimum = distance;
117
89
  }
@@ -124,17 +96,43 @@ export function findRelatives(
124
96
  }
125
97
 
126
98
  return distances
127
- .filter(found => found.distance === minimum && found.element !== origin)
99
+ .filter(found => found.distance === minimum)
128
100
  .map(found => found.element);
129
101
  }
130
102
 
131
- function traverse(from: Element, to: Element): number | undefined {
132
- let index = [...to.children].indexOf(from);
103
+ /**
104
+ * Get the distance between two elements _(i.e., the amount of nodes of between them)_
105
+ * @param origin Origin element
106
+ * @param target Target element
107
+ * @returns Distance between elements, or `-1` if distance cannot be calculated
108
+ */
109
+ export function getDistance(origin: Element, target: Element): number {
110
+ if (origin === target) {
111
+ return 0;
112
+ }
133
113
 
134
- if (index > -1) {
114
+ if (origin.parentElement === target || target.parentElement === origin) {
135
115
  return 1;
136
116
  }
137
117
 
118
+ if (origin.parentElement != null && origin.parentElement === target.parentElement) {
119
+ const children = [...origin.parentElement.children];
120
+
121
+ return Math.abs(children.indexOf(origin) - children.indexOf(target));
122
+ }
123
+
124
+ const comparison = origin.compareDocumentPosition(target);
125
+
126
+ if (comparison & Node.DOCUMENT_POSITION_DISCONNECTED) {
127
+ return -1;
128
+ }
129
+
130
+ const preceding = comparison & Node.DOCUMENT_POSITION_PRECEDING;
131
+
132
+ return traverse(preceding ? origin : target, preceding ? target : origin) ?? -1;
133
+ }
134
+
135
+ function traverse(from: Element, to: Element): number | undefined {
138
136
  let current = from;
139
137
  let distance = 0;
140
138
  let parent: Element | null = from.parentElement;
@@ -146,11 +144,11 @@ function traverse(from: Element, to: Element): number | undefined {
146
144
 
147
145
  const children = [...parent.children];
148
146
 
149
- if (children.includes(to)) {
147
+ if (to.parentElement === parent) {
150
148
  return distance + Math.abs(children.indexOf(current) - children.indexOf(to));
151
149
  }
152
150
 
153
- index = children.findIndex(child => child.contains(to));
151
+ const index = children.findIndex(child => child.contains(to));
154
152
 
155
153
  if (index > -1) {
156
154
  const traversed = traverse(current, children[index]);
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@ export {
5
5
  isBooleanAttribute,
6
6
  isEmptyNonBooleanAttribute,
7
7
  isInvalidBooleanAttribute,
8
- } from './attribute';
8
+ } from './attribute/index';
9
9
  export * from './data';
10
10
  export * from './event/index';
11
11
  export * from './find/index';
@@ -1,11 +1,10 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms';
2
2
  import {isPlainObject} from '@oscarpalmer/atoms/is';
3
- import {getString} from '@oscarpalmer/atoms/string';
4
- import type {Attribute, Property} from '../models';
5
- import {isHTMLOrSVGElement} from './is';
3
+ import type {Attribute} from '../models';
4
+ import {updateElementValue} from './element-value';
6
5
 
7
6
  function badAttributeHandler(name?: string, value?: string): boolean {
8
- if (name == null || value == null) {
7
+ if (typeof name !== 'string' || name.trim().length === 0 || typeof value !== 'string') {
9
8
  return true;
10
9
  }
11
10
 
@@ -28,17 +27,19 @@ function badAttributeHandler(name?: string, value?: string): boolean {
28
27
  }
29
28
 
30
29
  function booleanAttributeHandler(name?: string, value?: string): boolean {
31
- if (name == null || value == null) {
30
+ if (typeof name !== 'string' || name.trim().length === 0 || typeof value !== 'string') {
32
31
  return true;
33
32
  }
34
33
 
35
- if (!booleanAttributesSet.has(name)) {
34
+ const normalizedName = name.toLowerCase();
35
+
36
+ if (!booleanAttributesSet.has(normalizedName)) {
36
37
  return false;
37
38
  }
38
39
 
39
- const normalized = value.toLowerCase().trim();
40
+ const normalized = value.toLowerCase();
40
41
 
41
- return !(normalized.length === 0 || normalized === name);
42
+ return !(normalized.length === 0 || normalized === normalizedName);
42
43
  }
43
44
 
44
45
  function decodeAttribute(value: string): string {
@@ -73,12 +74,12 @@ function handleAttribute(
73
74
  return callback(name, value?.replace(EXPRESSION_WHITESPACE, ''));
74
75
  }
75
76
 
76
- function isAttribute(value: unknown): value is Attr | Attribute {
77
+ export function isAttribute(value: unknown): value is Attr | Attribute {
77
78
  return (
78
79
  value instanceof Attr ||
79
80
  (isPlainObject(value) &&
80
81
  typeof (value as PlainObject).name === 'string' &&
81
- typeof (value as PlainObject).value === 'string')
82
+ 'value' in (value as PlainObject))
82
83
  );
83
84
  }
84
85
 
@@ -117,67 +118,53 @@ export function _isInvalidBooleanAttribute(
117
118
  return handleAttribute(booleanAttributeHandler, decode, first, second);
118
119
  }
119
120
 
120
- export function isProperty(value: unknown): value is Property {
121
- return isPlainObject(value) && typeof (value as PlainObject).name === 'string';
122
- }
123
-
124
121
  function isValidSourceAttribute(name: string, value: string): boolean {
125
122
  return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
126
123
  }
127
124
 
128
- function updateAttribute(element: Element, name: string, value: unknown): void {
129
- const isBoolean = booleanAttributesSet.has(name.toLowerCase());
130
-
131
- if (isBoolean) {
132
- updateProperty(element, name, value);
133
- }
125
+ export function updateAttribute(
126
+ element: Element,
127
+ name: string,
128
+ value: unknown,
129
+ dispatch?: unknown,
130
+ ): void {
131
+ const normalizedName = name.toLowerCase();
134
132
 
135
- if (isBoolean ? value !== true : value == null) {
136
- element.removeAttribute(name);
137
- } else {
138
- element.setAttribute(name, isBoolean ? '' : getString(value));
139
- }
140
- }
133
+ const isBoolean = booleanAttributesSet.has(normalizedName);
141
134
 
142
- function updateProperty(element: Element, name: string, value: unknown): void {
143
- const actual = name.toLowerCase();
135
+ const next = isBoolean
136
+ ? value === true ||
137
+ (typeof value === 'string' && (value === '' || value.toLowerCase() === normalizedName))
138
+ : value == null
139
+ ? ''
140
+ : value;
144
141
 
145
- (element as unknown as PlainObject)[actual] =
146
- value === '' || (typeof value === 'string' && value.toLowerCase() === actual) || value === true;
147
- }
148
-
149
- export function updateValue(element: Element, first: unknown, second: unknown): void {
150
- if (!isHTMLOrSVGElement(element)) {
151
- return;
142
+ if (name in element) {
143
+ updateProperty(element, normalizedName, next, dispatch);
152
144
  }
153
145
 
154
- if (isProperty(first)) {
155
- updateAttribute(element, (first as Attribute).name, (first as Attribute).value);
156
- } else if (typeof first === 'string') {
157
- updateAttribute(element, first as string, second);
158
- }
146
+ updateElementValue(
147
+ element,
148
+ name,
149
+ isBoolean ? (next ? '' : null) : value,
150
+ element.setAttribute,
151
+ element.removeAttribute,
152
+ isBoolean,
153
+ false,
154
+ );
159
155
  }
160
156
 
161
- export function updateValues(
162
- element: Element,
163
- values: Attribute<unknown>[] | Record<string, unknown>,
164
- ): void {
165
- if (!isHTMLOrSVGElement(element)) {
157
+ function updateProperty(element: Element, name: string, value: unknown, dispatch?: unknown): void {
158
+ if (Object.is((element as unknown as PlainObject)[name], value)) {
166
159
  return;
167
160
  }
168
161
 
169
- const isArray = Array.isArray(values);
170
- const entries = Object.entries(values);
171
- const {length} = entries;
162
+ (element as unknown as PlainObject)[name] = value;
172
163
 
173
- for (let index = 0; index < length; index += 1) {
174
- const entry = entries[index];
164
+ const event = dispatch !== false && elementEvents[element.tagName]?.[name];
175
165
 
176
- if (isArray) {
177
- updateAttribute(element, (entry[1] as Attribute).name, (entry[1] as Attribute).value);
178
- } else {
179
- updateAttribute(element, entry[0], entry[1]);
180
- }
166
+ if (typeof event === 'string') {
167
+ element.dispatchEvent(new Event(event, {bubbles: true}));
181
168
  }
182
169
  }
183
170
 
@@ -201,6 +188,8 @@ const EXPRESSION_URI_VALUE =
201
188
  // oxlint-disable-next-line no-control-regex
202
189
  const EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
203
190
 
191
+ //
192
+
204
193
  /**
205
194
  * List of boolean attributes
206
195
  */
@@ -233,6 +222,13 @@ export const booleanAttributes: readonly string[] = Object.freeze([
233
222
 
234
223
  const booleanAttributesSet = new Set(booleanAttributes);
235
224
 
225
+ const elementEvents: Record<string, Record<string, string>> = {
226
+ DETAILS: {open: 'toggle'},
227
+ INPUT: {checked: 'change', value: 'input'},
228
+ SELECT: {value: 'change'},
229
+ TEXTAREA: {value: 'input'},
230
+ };
231
+
236
232
  const formElement = document.createElement('form');
237
233
 
238
234
  let textArea: HTMLTextAreaElement;