@oscarpalmer/toretto 0.32.0 → 0.34.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/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;
@@ -1,28 +1,57 @@
1
- import type {PlainObject} from '@oscarpalmer/atoms';
2
- import {isNullableOrWhitespace, isPlainObject} from '@oscarpalmer/atoms/is';
1
+ import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
3
2
  import {isHTMLOrSVGElement} from '../is';
3
+ import {isAttribute} from './attribute';
4
4
 
5
- export function setElementValues(
5
+ export function setElementValue(
6
6
  element: Element,
7
- first: PlainObject | string,
7
+ first: unknown,
8
8
  second: unknown,
9
+ third: unknown,
9
10
  callback: (element: Element, key: string, value: unknown) => void,
10
11
  ): void {
11
12
  if (!isHTMLOrSVGElement(element)) {
12
13
  return;
13
14
  }
14
15
 
15
- if (isPlainObject(first)) {
16
- const entries = Object.entries(first);
17
- const {length} = entries;
16
+ if (typeof first === 'string') {
17
+ setElementValues(element, first, second, third, callback);
18
+ } else if (isAttribute(first)) {
19
+ setElementValues(element, first.name, first.value, third, callback);
20
+ }
21
+ }
22
+
23
+ export function setElementValues(
24
+ element: Element,
25
+ first: unknown,
26
+ second: unknown,
27
+ third: unknown,
28
+ callback: (element: Element, key: string, value: unknown, dispatch?: unknown) => void,
29
+ ): void {
30
+ if (!isHTMLOrSVGElement(element)) {
31
+ return;
32
+ }
33
+
34
+ if (typeof first === 'string') {
35
+ callback(element, first, second, third);
36
+
37
+ return;
38
+ }
39
+
40
+ const isArray = Array.isArray(first);
41
+
42
+ if (!isArray && !(typeof first === 'object' && first !== null)) {
43
+ return;
44
+ }
45
+
46
+ const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({name, value}));
47
+ const {length} = entries;
18
48
 
19
- for (let index = 0; index < length; index += 1) {
20
- const [key, value] = entries[index];
49
+ for (let index = 0; index < length; index += 1) {
50
+ const entry = entries[index];
21
51
 
22
- callback(element, key, value);
52
+ if (typeof entry === 'object' && typeof entry?.name === 'string') {
53
+ callback(element, entry.name, entry.value, third);
23
54
  }
24
- } else if (typeof first === 'string') {
25
- callback(element, first, second);
26
55
  }
27
56
  }
28
57
 
@@ -32,9 +61,10 @@ export function updateElementValue(
32
61
  value: unknown,
33
62
  set: (key: string, value: string) => void,
34
63
  remove: (key: string) => void,
64
+ isBoolean: boolean,
35
65
  json: boolean,
36
66
  ): void {
37
- if (isNullableOrWhitespace(value)) {
67
+ if (isBoolean ? value == null : isNullableOrWhitespace(value)) {
38
68
  remove.call(element, key);
39
69
  } else {
40
70
  set.call(element, key, json ? JSON.stringify(value) : String(value));
package/src/models.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Attribute for an element
3
3
  */
4
- export type Attribute<Value = unknown> = {
4
+ export type Attribute = {
5
5
  name: string;
6
- value: Value;
6
+ value: unknown;
7
7
  };
8
8
 
9
9
  /**
@@ -19,11 +19,6 @@ export type EventPosition = {
19
19
  y: number;
20
20
  };
21
21
 
22
- /**
23
- * Property for an element
24
- */
25
- export type Property = Attribute<unknown>;
26
-
27
22
  /**
28
23
  * Event listener that can be removed
29
24
  */
package/src/style.ts CHANGED
@@ -97,7 +97,7 @@ export function setStyle(
97
97
  property: keyof CSSStyleDeclaration,
98
98
  value?: string,
99
99
  ): void {
100
- setElementValues(element, property as string, value, updateStyleProperty);
100
+ setElementValues(element, property as string, value, null, updateStyleProperty);
101
101
  }
102
102
 
103
103
  /**
@@ -106,7 +106,7 @@ export function setStyle(
106
106
  * @param styles Styles to set
107
107
  */
108
108
  export function setStyles(element: Element, styles: Partial<CSSStyleDeclaration>): void {
109
- setElementValues(element, styles as never, null, updateStyleProperty);
109
+ setElementValues(element, styles as never, null, null, updateStyleProperty);
110
110
  }
111
111
 
112
112
  /**
@@ -169,6 +169,7 @@ function updateStyleProperty(element: Element, key: string, value: unknown): voi
169
169
  (this as HTMLElement).style[property as never] = '';
170
170
  },
171
171
  false,
172
+ false,
172
173
  );
173
174
  }
174
175
 
@@ -1,4 +1,15 @@
1
- import type { Attribute, Property } from '../models';
1
+ import type { Attribute } from '../models';
2
+ type DispatchedAttribute = 'checked' | 'open' | 'value';
3
+ /**
4
+ * Set an attribute on an element
5
+ *
6
+ * _(Or remove it, if value is `null` or `undefined`)_
7
+ * @param element Element for attribute
8
+ * @param name Attribute name
9
+ * @param value Attribute value
10
+ * @param dispatch Dispatch event for attribute? _(defaults to `true`)_
11
+ */
12
+ export declare function setAttribute<Name extends DispatchedAttribute>(element: Element, name: Name, value?: unknown, dispatch?: boolean): void;
2
13
  /**
3
14
  * Set an attribute on an element
4
15
  *
@@ -14,54 +25,25 @@ export declare function setAttribute(element: Element, name: string, value?: unk
14
25
  * _(Or remove it, if value is `null` or `undefined`)_
15
26
  * @param element Element for attribute
16
27
  * @param attribute Attribute to set
28
+ * @param dispatch Dispatch event for attribute? _(defaults to `true`)_
17
29
  */
18
- export declare function setAttribute(element: Element, attribute: Attr | Attribute): void;
30
+ export declare function setAttribute(element: Element, attribute: Attr | Attribute, dispatch?: boolean): void;
19
31
  /**
20
32
  * Set one or more attributes on an element
21
33
  *
22
34
  * _(Or remove them, if their value is `null` or `undefined`)_
23
35
  * @param element Element for attributes
24
36
  * @param attributes Attributes to set
37
+ * @param dispatch Dispatch events for relevant attributes? _(defaults to `true`)_
25
38
  */
26
- export declare function setAttributes(element: Element, attributes: Array<Attr | Attribute>): void;
39
+ export declare function setAttributes(element: Element, attributes: Array<Attr | Attribute>, dispatch?: boolean): void;
27
40
  /**
28
41
  * Set one or more attributes on an element
29
42
  *
30
43
  * _(Or remove them, if their value is `null` or `undefined`)_
31
44
  * @param element Element for attributes
32
45
  * @param attributes Attributes to set
46
+ * @param dispatch Dispatch events for relevant attributes? _(defaults to `true`)_
33
47
  */
34
- export declare function setAttributes(element: Element, attributes: Record<string, unknown>): void;
35
- /**
36
- * Set a property on an element
37
- *
38
- * _(Or remove it, if value is not an empty string or does not match the name)_
39
- * @param element Element for property
40
- * @param name Property name
41
- * @param value Property value
42
- */
43
- export declare function setProperty(element: Element, name: string, value: boolean | string): void;
44
- /**
45
- * Set a property on an element
46
- *
47
- * _(Or remove it, if value is not an empty string or does not match the name)_
48
- * @param element Element for property
49
- * @param property Property to set
50
- */
51
- export declare function setProperty(element: Element, property: Property): void;
52
- /**
53
- * Set one or more properties on an element
54
- *
55
- * _(Or remove them, if their value is not an empty string or does not match the name)_
56
- * @param element Element for properties
57
- * @param properties Properties to set
58
- */
59
- export declare function setProperties(element: Element, properties: Property[]): void;
60
- /**
61
- * Set one or more properties on an element
62
- *
63
- * _(Or remove them, if their value is not an empty string or does not match the name)_
64
- * @param element Element for properties
65
- * @param properties Properties to set
66
- */
67
- export declare function setProperties(element: Element, properties: Record<string, unknown>): void;
48
+ export declare function setAttributes(element: Element, attributes: Record<string, unknown>, dispatch?: boolean): void;
49
+ export {};
package/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import supportsTouch from './touch';
2
- export { isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, } from './attribute';
2
+ export { isBadAttribute, isBooleanAttribute, isEmptyNonBooleanAttribute, isInvalidBooleanAttribute, } from './attribute/index';
3
3
  export * from './data';
4
4
  export * from './event/index';
5
5
  export * from './find/index';
@@ -1,11 +1,10 @@
1
- import type { Attribute, Property } from '../models';
1
+ import type { Attribute } from '../models';
2
+ export declare function isAttribute(value: unknown): value is Attr | Attribute;
2
3
  export declare function _isBadAttribute(first: unknown, second: unknown, decode: boolean): boolean;
3
4
  export declare function _isBooleanAttribute(first: unknown, decode: boolean): boolean;
4
5
  export declare function _isEmptyNonBooleanAttribute(first: unknown, second: unknown, decode: boolean): boolean;
5
6
  export declare function _isInvalidBooleanAttribute(first: unknown, second: unknown, decode: boolean): boolean;
6
- export declare function isProperty(value: unknown): value is Property;
7
- export declare function updateValue(element: Element, first: unknown, second: unknown): void;
8
- export declare function updateValues(element: Element, values: Attribute<unknown>[] | Record<string, unknown>): void;
7
+ export declare function updateAttribute(element: Element, name: string, value: unknown, dispatch?: unknown): void;
9
8
  /**
10
9
  * List of boolean attributes
11
10
  */
@@ -1,3 +1,3 @@
1
- import type { PlainObject } from '@oscarpalmer/atoms';
2
- export declare function setElementValues(element: Element, first: PlainObject | string, second: unknown, callback: (element: Element, key: string, value: unknown) => void): void;
3
- export declare function updateElementValue(element: Element, key: string, value: unknown, set: (key: string, value: string) => void, remove: (key: string) => void, json: boolean): void;
1
+ export declare function setElementValue(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown) => void): void;
2
+ export declare function setElementValues(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown, dispatch?: unknown) => void): void;
3
+ export declare function updateElementValue(element: Element, key: string, value: unknown, set: (key: string, value: string) => void, remove: (key: string) => void, isBoolean: boolean, json: boolean): void;
package/types/models.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Attribute for an element
3
3
  */
4
- export type Attribute<Value = unknown> = {
4
+ export type Attribute = {
5
5
  name: string;
6
- value: Value;
6
+ value: unknown;
7
7
  };
8
8
  /**
9
9
  * Event listener for custom events
@@ -16,10 +16,6 @@ export type EventPosition = {
16
16
  x: number;
17
17
  y: number;
18
18
  };
19
- /**
20
- * Property for an element
21
- */
22
- export type Property = Attribute<unknown>;
23
19
  /**
24
20
  * Event listener that can be removed
25
21
  */