@oscarpalmer/toretto 0.41.0 → 0.43.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/dist/attribute/{get.d.mts → get.attribute.d.mts} +3 -2
- package/dist/attribute/{get.mjs → get.attribute.mjs} +4 -3
- package/dist/attribute/index.d.mts +3 -16
- package/dist/attribute/index.mjs +4 -7
- package/dist/attribute/{set.d.mts → set.attribute.d.mts} +4 -4
- package/dist/attribute/{set.mjs → set.attribute.mjs} +2 -2
- package/dist/create.d.mts +25 -0
- package/dist/create.mjs +17 -0
- package/dist/data.mjs +7 -7
- package/dist/event/delegation.mjs +8 -1
- package/dist/html/index.d.mts +23 -26
- package/dist/html/index.mjs +85 -18
- package/dist/html/sanitize.mjs +6 -5
- package/dist/index.d.mts +117 -54
- package/dist/index.mjs +541 -380
- package/dist/internal/attribute.d.mts +4 -3
- package/dist/internal/attribute.mjs +13 -23
- package/dist/internal/element-value.d.mts +2 -2
- package/dist/internal/element-value.mjs +12 -6
- package/dist/internal/get-value.mjs +3 -1
- package/dist/internal/property.d.mts +4 -0
- package/dist/internal/property.mjs +24 -0
- package/dist/property/get.property.d.mts +20 -0
- package/dist/property/get.property.mjs +35 -0
- package/dist/property/index.d.mts +3 -0
- package/dist/property/index.mjs +3 -0
- package/dist/property/set.property.d.mts +32 -0
- package/dist/property/set.property.mjs +32 -0
- package/dist/style.d.mts +16 -9
- package/dist/style.mjs +22 -21
- package/package.json +14 -6
- package/src/attribute/{get.ts → get.attribute.ts} +14 -3
- package/src/attribute/index.ts +10 -22
- package/src/attribute/{set.ts → set.attribute.ts} +9 -5
- package/src/create.ts +81 -0
- package/src/data.ts +16 -8
- package/src/event/delegation.ts +24 -3
- package/src/event/index.ts +9 -3
- package/src/find/index.ts +11 -3
- package/src/find/relative.ts +4 -0
- package/src/focusable.ts +10 -2
- package/src/html/index.ts +166 -58
- package/src/html/sanitize.ts +14 -11
- package/src/index.ts +2 -1
- package/src/internal/attribute.ts +23 -42
- package/src/internal/element-value.ts +25 -6
- package/src/internal/get-value.ts +14 -0
- package/src/internal/is.ts +4 -0
- package/src/internal/property.ts +42 -0
- package/src/is.ts +10 -2
- package/src/property/get.property.ts +73 -0
- package/src/property/index.ts +2 -0
- package/src/property/set.property.ts +103 -0
- package/src/style.ts +81 -36
- package/src/touch.ts +14 -2
package/src/html/sanitize.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import {setAttribute} from '../attribute/set';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
_isInvalidBooleanAttribute,
|
|
6
|
-
} from '../internal/attribute';
|
|
1
|
+
import {setAttribute} from '../attribute/set.attribute';
|
|
2
|
+
import {_isBadAttribute, _isInvalidBooleanAttribute} from '../internal/attribute';
|
|
3
|
+
|
|
4
|
+
// #region Functions
|
|
7
5
|
|
|
8
6
|
function handleElement(element: Element, depth: number): void {
|
|
9
7
|
if (depth === 0) {
|
|
10
|
-
const removable = element.querySelectorAll(REMOVE_SELECTOR);
|
|
8
|
+
const removable = [...element.querySelectorAll(REMOVE_SELECTOR)];
|
|
9
|
+
const {length} = removable;
|
|
11
10
|
|
|
12
|
-
for (
|
|
13
|
-
|
|
11
|
+
for (let index = 0; index < length; index += 1) {
|
|
12
|
+
removable[index].remove();
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
15
|
|
|
@@ -49,7 +48,7 @@ export function sanitizeAttributes(element: Element, attributes: Attr[]): void {
|
|
|
49
48
|
for (let index = 0; index < length; index += 1) {
|
|
50
49
|
const {name, value} = attributes[index];
|
|
51
50
|
|
|
52
|
-
if (_isBadAttribute(name, value, false)
|
|
51
|
+
if (_isBadAttribute(name, value, false)) {
|
|
53
52
|
element.removeAttribute(name);
|
|
54
53
|
} else if (_isInvalidBooleanAttribute(name, value, false)) {
|
|
55
54
|
setAttribute(element, name, true);
|
|
@@ -103,8 +102,12 @@ export function sanitizeNodes(nodes: Node[], depth: number): Node[] {
|
|
|
103
102
|
return nodes;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
//
|
|
105
|
+
// #endregion
|
|
106
|
+
|
|
107
|
+
// #region Variables
|
|
107
108
|
|
|
108
109
|
const COMMENT_HARMFUL = /<[/\w]/g;
|
|
109
110
|
|
|
110
111
|
const REMOVE_SELECTOR = 'script, toretto-temporary';
|
|
112
|
+
|
|
113
|
+
// #endregion
|
package/src/index.ts
CHANGED
|
@@ -6,11 +6,11 @@ export {
|
|
|
6
6
|
getAttributes,
|
|
7
7
|
isBadAttribute,
|
|
8
8
|
isBooleanAttribute,
|
|
9
|
-
isEmptyNonBooleanAttribute,
|
|
10
9
|
isInvalidBooleanAttribute,
|
|
11
10
|
setAttribute,
|
|
12
11
|
setAttributes,
|
|
13
12
|
} from './attribute/index';
|
|
13
|
+
export * from './create';
|
|
14
14
|
export * from './data';
|
|
15
15
|
export * from './event/index';
|
|
16
16
|
export * from './find/index';
|
|
@@ -18,6 +18,7 @@ export * from './focusable';
|
|
|
18
18
|
export * from './html/index';
|
|
19
19
|
export * from './is';
|
|
20
20
|
export * from './models';
|
|
21
|
+
export * from './property/index';
|
|
21
22
|
export * from './style';
|
|
22
23
|
|
|
23
24
|
export {supportsTouch};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
3
|
+
import {kebabCase} from '@oscarpalmer/atoms/string/case';
|
|
3
4
|
import type {Attribute} from '../models';
|
|
4
5
|
import {updateElementValue} from './element-value';
|
|
6
|
+
import {updateProperty} from './property';
|
|
7
|
+
|
|
8
|
+
// #region Functions
|
|
5
9
|
|
|
6
10
|
function badAttributeHandler(name?: string, value?: string): boolean {
|
|
7
11
|
if (typeof name !== 'string' || name.trim().length === 0 || typeof value !== 'string') {
|
|
@@ -67,6 +71,10 @@ function handleAttribute(
|
|
|
67
71
|
value = second;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
if (name != null) {
|
|
75
|
+
name = kebabCase(name);
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
if (decode && value != null) {
|
|
71
79
|
value = decodeAttribute(value);
|
|
72
80
|
}
|
|
@@ -96,20 +104,6 @@ export function _isBooleanAttribute(first: unknown, decode: boolean): boolean {
|
|
|
96
104
|
);
|
|
97
105
|
}
|
|
98
106
|
|
|
99
|
-
export function _isEmptyNonBooleanAttribute(
|
|
100
|
-
first: unknown,
|
|
101
|
-
second: unknown,
|
|
102
|
-
decode: boolean,
|
|
103
|
-
): boolean {
|
|
104
|
-
return handleAttribute(
|
|
105
|
-
(name, value) =>
|
|
106
|
-
name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0,
|
|
107
|
-
decode,
|
|
108
|
-
first,
|
|
109
|
-
second,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
107
|
export function _isInvalidBooleanAttribute(
|
|
114
108
|
first: unknown,
|
|
115
109
|
second: unknown,
|
|
@@ -126,49 +120,39 @@ export function updateAttribute(
|
|
|
126
120
|
element: Element,
|
|
127
121
|
name: string,
|
|
128
122
|
value: unknown,
|
|
129
|
-
dispatch
|
|
123
|
+
dispatch: boolean,
|
|
130
124
|
): void {
|
|
131
|
-
const
|
|
125
|
+
const lowerCaseName = name.toLowerCase();
|
|
132
126
|
|
|
133
|
-
const isBoolean = booleanAttributesSet.has(
|
|
127
|
+
const isBoolean = booleanAttributesSet.has(lowerCaseName);
|
|
134
128
|
|
|
135
129
|
const next = isBoolean
|
|
136
130
|
? value === true ||
|
|
137
|
-
(typeof value === 'string' && (value === '' || value.toLowerCase() ===
|
|
131
|
+
(typeof value === 'string' && (value === '' || value.toLowerCase() === lowerCaseName))
|
|
138
132
|
: value == null
|
|
139
133
|
? ''
|
|
140
134
|
: value;
|
|
141
135
|
|
|
142
|
-
if (
|
|
143
|
-
updateProperty(element,
|
|
136
|
+
if (isBoolean || dispatchedAttributes.has(name)) {
|
|
137
|
+
updateProperty(element, name, next, dispatch);
|
|
144
138
|
}
|
|
145
139
|
|
|
146
140
|
updateElementValue(
|
|
147
141
|
element,
|
|
148
142
|
name,
|
|
149
143
|
isBoolean ? (next ? '' : null) : value,
|
|
144
|
+
// oxlint-disable-next-line typescript/unbound-method: using `.call` in `updateElementValue`
|
|
150
145
|
element.setAttribute,
|
|
146
|
+
// oxlint-disable-next-line typescript/unbound-method: using `.call` in `updateElementValue`
|
|
151
147
|
element.removeAttribute,
|
|
152
148
|
isBoolean,
|
|
153
149
|
false,
|
|
154
150
|
);
|
|
155
151
|
}
|
|
156
152
|
|
|
157
|
-
|
|
158
|
-
if (Object.is((element as unknown as PlainObject)[name], value)) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
(element as unknown as PlainObject)[name] = value;
|
|
153
|
+
// #endregion
|
|
163
154
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (typeof event === 'string') {
|
|
167
|
-
element.dispatchEvent(new Event(event, {bubbles: true}));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
//
|
|
155
|
+
// #region Variables
|
|
172
156
|
|
|
173
157
|
const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
|
|
174
158
|
|
|
@@ -220,15 +204,12 @@ export const booleanAttributes: readonly string[] = Object.freeze([
|
|
|
220
204
|
'selected',
|
|
221
205
|
]);
|
|
222
206
|
|
|
223
|
-
const booleanAttributesSet = new Set(booleanAttributes);
|
|
207
|
+
export const booleanAttributesSet = new Set(booleanAttributes);
|
|
224
208
|
|
|
225
|
-
const
|
|
226
|
-
DETAILS: {open: 'toggle'},
|
|
227
|
-
INPUT: {checked: 'change', value: 'input'},
|
|
228
|
-
SELECT: {value: 'change'},
|
|
229
|
-
TEXTAREA: {value: 'input'},
|
|
230
|
-
};
|
|
209
|
+
export const dispatchedAttributes = new Set(['checked', 'open', 'value']);
|
|
231
210
|
|
|
232
211
|
const formElement = document.createElement('form');
|
|
233
212
|
|
|
234
213
|
let textArea: HTMLTextAreaElement;
|
|
214
|
+
|
|
215
|
+
// #endregion
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import {kebabCase} from '@oscarpalmer/atoms/string/case';
|
|
2
3
|
import {isHTMLOrSVGElement} from '../is';
|
|
3
4
|
import {isAttribute} from './attribute';
|
|
4
5
|
|
|
6
|
+
// #region Functions
|
|
7
|
+
|
|
8
|
+
function normalizeKey(key: string, style?: boolean): string {
|
|
9
|
+
return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
export function setElementValue(
|
|
6
13
|
element: Element,
|
|
7
14
|
first: unknown,
|
|
8
15
|
second: unknown,
|
|
9
16
|
third: unknown,
|
|
10
|
-
callback: (element: Element, key: string, value: unknown) => void,
|
|
17
|
+
callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void,
|
|
18
|
+
style?: boolean,
|
|
11
19
|
): void {
|
|
12
20
|
if (!isHTMLOrSVGElement(element)) {
|
|
13
21
|
return;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
if (typeof first === 'string') {
|
|
17
|
-
setElementValues(element, first, second, third, callback);
|
|
25
|
+
setElementValues(element, first, second, third, callback, style);
|
|
18
26
|
} else if (isAttribute(first)) {
|
|
19
|
-
setElementValues(element, first.name, first.value, third, callback);
|
|
27
|
+
setElementValues(element, first.name, first.value, third, callback, style);
|
|
20
28
|
}
|
|
21
29
|
}
|
|
22
30
|
|
|
@@ -25,14 +33,17 @@ export function setElementValues(
|
|
|
25
33
|
first: unknown,
|
|
26
34
|
second: unknown,
|
|
27
35
|
third: unknown,
|
|
28
|
-
callback: (element: Element, key: string, value: unknown, dispatch
|
|
36
|
+
callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void,
|
|
37
|
+
style?: boolean,
|
|
29
38
|
): void {
|
|
30
39
|
if (!isHTMLOrSVGElement(element)) {
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
const dispatch = third !== false;
|
|
44
|
+
|
|
34
45
|
if (typeof first === 'string') {
|
|
35
|
-
callback(element, first, second,
|
|
46
|
+
callback(element, normalizeKey(first, style), second, dispatch);
|
|
36
47
|
|
|
37
48
|
return;
|
|
38
49
|
}
|
|
@@ -50,7 +61,7 @@ export function setElementValues(
|
|
|
50
61
|
const entry = entries[index];
|
|
51
62
|
|
|
52
63
|
if (typeof entry === 'object' && typeof entry?.name === 'string') {
|
|
53
|
-
callback(element, entry.name, entry.value,
|
|
64
|
+
callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
}
|
|
@@ -70,3 +81,11 @@ export function updateElementValue(
|
|
|
70
81
|
set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
71
82
|
}
|
|
72
83
|
}
|
|
84
|
+
|
|
85
|
+
// #endregion Functions
|
|
86
|
+
|
|
87
|
+
// #region Variables
|
|
88
|
+
|
|
89
|
+
const CSS_VARIABLE_PREFIX = '--';
|
|
90
|
+
|
|
91
|
+
// #endregion
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {parse} from '@oscarpalmer/atoms/string';
|
|
2
2
|
import {camelCase, kebabCase} from '@oscarpalmer/atoms/string/case';
|
|
3
3
|
|
|
4
|
+
// #region Functions
|
|
5
|
+
|
|
4
6
|
export function getBoolean(value: unknown, defaultValue?: boolean): boolean {
|
|
5
7
|
return typeof value === 'boolean' ? value : (defaultValue ?? false);
|
|
6
8
|
}
|
|
@@ -20,6 +22,10 @@ export function getStyleValue(
|
|
|
20
22
|
property: string,
|
|
21
23
|
computed: boolean,
|
|
22
24
|
): string | undefined {
|
|
25
|
+
if (property.startsWith(CSS_VARIABLE_PREFIX)) {
|
|
26
|
+
return (element as HTMLElement).style.getPropertyValue(property);
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
const name = camelCase(property);
|
|
24
30
|
|
|
25
31
|
return computed
|
|
@@ -27,4 +33,12 @@ export function getStyleValue(
|
|
|
27
33
|
: (element as HTMLElement).style[name as never];
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
// #endregion
|
|
37
|
+
|
|
38
|
+
// #region Variables
|
|
39
|
+
|
|
30
40
|
export const EXPRESSION_DATA_PREFIX = /^data-/i;
|
|
41
|
+
|
|
42
|
+
const CSS_VARIABLE_PREFIX = '--';
|
|
43
|
+
|
|
44
|
+
// #endregion
|
package/src/internal/is.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// #region Functions
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Is the value an event target?
|
|
3
5
|
* @param value Value to check
|
|
@@ -21,3 +23,5 @@ export function isEventTarget(value: unknown): value is EventTarget {
|
|
|
21
23
|
export function isHTMLOrSVGElement(value: unknown): value is HTMLElement | SVGElement {
|
|
22
24
|
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
// #endregion
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {camelCase} from '@oscarpalmer/atoms/string/case';
|
|
3
|
+
|
|
4
|
+
// #region Functions
|
|
5
|
+
|
|
6
|
+
export function updateProperty(
|
|
7
|
+
element: Element,
|
|
8
|
+
name: string,
|
|
9
|
+
value: unknown,
|
|
10
|
+
dispatch: boolean,
|
|
11
|
+
): void {
|
|
12
|
+
let property = name;
|
|
13
|
+
|
|
14
|
+
if (!(property in element)) {
|
|
15
|
+
property = camelCase(name);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!(property in element) || Object.is((element as unknown as PlainObject)[property], value)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
(element as unknown as PlainObject)[property] = value;
|
|
23
|
+
|
|
24
|
+
const event = dispatch && elementEvents[element.tagName]?.[property];
|
|
25
|
+
|
|
26
|
+
if (typeof event === 'string') {
|
|
27
|
+
element.dispatchEvent(new Event(event, {bubbles: true, cancelable: true}));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// #endregion
|
|
32
|
+
|
|
33
|
+
// #region Variables
|
|
34
|
+
|
|
35
|
+
const elementEvents: Record<string, Record<string, string>> = {
|
|
36
|
+
DETAILS: {open: 'toggle'},
|
|
37
|
+
INPUT: {checked: 'change', value: 'input'},
|
|
38
|
+
SELECT: {value: 'change'},
|
|
39
|
+
TEXTAREA: {value: 'input'},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// #endregion
|
package/src/is.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// #region Functions
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Is the value a child node?
|
|
3
5
|
* @param value Value to check
|
|
@@ -36,7 +38,9 @@ export function isInDocument(node: Node, doc?: Document): boolean {
|
|
|
36
38
|
: node.ownerDocument === doc && doc.contains(node);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
//
|
|
41
|
+
// #endregion
|
|
42
|
+
|
|
43
|
+
// #region Variables
|
|
40
44
|
|
|
41
45
|
const CHILD_NODE_TYPES: Set<number> = new Set([
|
|
42
46
|
Node.ELEMENT_NODE,
|
|
@@ -46,6 +50,10 @@ const CHILD_NODE_TYPES: Set<number> = new Set([
|
|
|
46
50
|
Node.DOCUMENT_TYPE_NODE,
|
|
47
51
|
]);
|
|
48
52
|
|
|
49
|
-
//
|
|
53
|
+
// #endregion
|
|
54
|
+
|
|
55
|
+
// #region Exports
|
|
50
56
|
|
|
51
57
|
export {isEventTarget, isHTMLOrSVGElement} from './internal/is';
|
|
58
|
+
|
|
59
|
+
// #endregion
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type {Primitive} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {camelCase} from '@oscarpalmer/atoms/string/case';
|
|
3
|
+
import {isHTMLOrSVGElement} from '../internal/is';
|
|
4
|
+
|
|
5
|
+
// #region Types
|
|
6
|
+
|
|
7
|
+
type GetProperties<Target extends Element> = {
|
|
8
|
+
[Property in keyof Target as Target[Property] extends Primitive
|
|
9
|
+
? Property
|
|
10
|
+
: never]?: Target[Property];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// #endregion
|
|
14
|
+
|
|
15
|
+
// #region Functions
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the values of one or more properties on an element
|
|
19
|
+
* @param target Target element
|
|
20
|
+
* @param properties Properties to get
|
|
21
|
+
* @returns Property values
|
|
22
|
+
*/
|
|
23
|
+
export function getProperties<Target extends Element, Property extends keyof GetProperties<Target>>(
|
|
24
|
+
target: Target,
|
|
25
|
+
properties: Property[],
|
|
26
|
+
): Pick<GetProperties<Target>, Property> {
|
|
27
|
+
const values: Partial<GetProperties<Target>> = {};
|
|
28
|
+
|
|
29
|
+
if (!isHTMLOrSVGElement(target) || !Array.isArray(properties)) {
|
|
30
|
+
return values as Pick<GetProperties<Target>, Property>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {length} = properties;
|
|
34
|
+
|
|
35
|
+
for (let index = 0; index < length; index += 1) {
|
|
36
|
+
const property = properties[index];
|
|
37
|
+
|
|
38
|
+
if (typeof property === 'string') {
|
|
39
|
+
values[property] = getPropertyValue(target, property) as GetProperties<Target>[Property];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return values as Pick<GetProperties<Target>, Property>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the value of a property on an element
|
|
48
|
+
* @param target Target element
|
|
49
|
+
* @param property Property to get
|
|
50
|
+
* @returns Property value
|
|
51
|
+
*/
|
|
52
|
+
export function getProperty<Target extends Element, Property extends keyof GetProperties<Target>>(
|
|
53
|
+
target: Target,
|
|
54
|
+
property: Property,
|
|
55
|
+
): GetProperties<Target>[Property] {
|
|
56
|
+
if (isHTMLOrSVGElement(target) && typeof property === 'string') {
|
|
57
|
+
return getPropertyValue(target, property) as GetProperties<Target>[Property];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getPropertyValue(element: HTMLElement, property: string): unknown {
|
|
62
|
+
let actual = property;
|
|
63
|
+
|
|
64
|
+
if (!(actual in element)) {
|
|
65
|
+
actual = camelCase(actual);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (actual in element) {
|
|
69
|
+
return element[actual as keyof HTMLElement];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// #endregion
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import type {PlainObject, Primitive} from '@oscarpalmer/atoms/models';
|
|
3
|
+
import {setAttribute} from '../attribute';
|
|
4
|
+
import type {DispatchedAttributeName} from '../attribute/set.attribute';
|
|
5
|
+
import {booleanAttributesSet, dispatchedAttributes} from '../internal/attribute';
|
|
6
|
+
import {updateProperty} from '../internal/property';
|
|
7
|
+
import {isHTMLOrSVGElement} from '../is';
|
|
8
|
+
|
|
9
|
+
// #region Types
|
|
10
|
+
|
|
11
|
+
type DispatchedPropertyValue<
|
|
12
|
+
Target extends Element,
|
|
13
|
+
Property extends DispatchedAttributeName,
|
|
14
|
+
> = Property extends keyof SetProperties<Target> ? SetProperties<Target>[Property] : never;
|
|
15
|
+
|
|
16
|
+
type SetProperties<Target extends Element> = {
|
|
17
|
+
[Property in keyof Target as Target[Property] extends Primitive
|
|
18
|
+
? Property
|
|
19
|
+
: never]?: Target[Property];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// #endregion
|
|
23
|
+
|
|
24
|
+
// #region Functions
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set the values of one or more properties on an element
|
|
28
|
+
*
|
|
29
|
+
* Also updates attributes for boolean/dispatchable properties, and if `dispatch` is `true`, will dispatch events for dispatchable properties
|
|
30
|
+
* @param target Target element
|
|
31
|
+
* @param properties Properties to set
|
|
32
|
+
* @param dispatch Dispatch events for properties? _(defaults to `true`)_
|
|
33
|
+
*/
|
|
34
|
+
export function setProperties<Target extends Element>(
|
|
35
|
+
target: Target,
|
|
36
|
+
properties: SetProperties<Target>,
|
|
37
|
+
dispatch?: boolean,
|
|
38
|
+
): void {
|
|
39
|
+
if (!isHTMLOrSVGElement(target) || !isPlainObject(properties)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const shouldDispatch = dispatch !== false;
|
|
44
|
+
|
|
45
|
+
const keys = Object.keys(properties);
|
|
46
|
+
const {length} = keys;
|
|
47
|
+
|
|
48
|
+
for (let index = 0; index < length; index += 1) {
|
|
49
|
+
setPropertyValue(target, keys[index], (properties as PlainObject)[keys[index]], shouldDispatch);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set the value for a dispatchable property on an element
|
|
55
|
+
* @param target Target element
|
|
56
|
+
* @param property Property to set
|
|
57
|
+
* @param value Value to set
|
|
58
|
+
* @param dispatch Dispatch event for property? _(defaults to `true`)_
|
|
59
|
+
*/
|
|
60
|
+
export function setProperty<Target extends Element, Property extends DispatchedAttributeName>(
|
|
61
|
+
target: Target,
|
|
62
|
+
property: Property,
|
|
63
|
+
value: DispatchedPropertyValue<Target, Property>,
|
|
64
|
+
dispatch?: boolean,
|
|
65
|
+
): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Set the value for a property on an element
|
|
69
|
+
* @param target Target element
|
|
70
|
+
* @param property Property to set
|
|
71
|
+
* @param value Value to set
|
|
72
|
+
*/
|
|
73
|
+
export function setProperty<Target extends Element, Property extends keyof SetProperties<Target>>(
|
|
74
|
+
target: Target,
|
|
75
|
+
property: Property,
|
|
76
|
+
value: SetProperties<Target>[Property],
|
|
77
|
+
): void;
|
|
78
|
+
|
|
79
|
+
export function setProperty<Target extends Element, Property extends keyof SetProperties<Target>>(
|
|
80
|
+
target: Target,
|
|
81
|
+
property: Property,
|
|
82
|
+
value: SetProperties<Target>[Property],
|
|
83
|
+
dispatch?: boolean,
|
|
84
|
+
): void {
|
|
85
|
+
if (isHTMLOrSVGElement(target) && typeof property === 'string') {
|
|
86
|
+
setPropertyValue(target, property, value, dispatch !== false);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setPropertyValue(
|
|
91
|
+
element: Element,
|
|
92
|
+
property: string,
|
|
93
|
+
value: unknown,
|
|
94
|
+
dispatch: boolean,
|
|
95
|
+
): void {
|
|
96
|
+
if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) {
|
|
97
|
+
setAttribute(element, property as never, value, dispatch);
|
|
98
|
+
} else {
|
|
99
|
+
updateProperty(element, property, value, dispatch);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// #endregion
|