@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.
Files changed (55) hide show
  1. package/dist/attribute/{get.d.mts → get.attribute.d.mts} +3 -2
  2. package/dist/attribute/{get.mjs → get.attribute.mjs} +4 -3
  3. package/dist/attribute/index.d.mts +3 -16
  4. package/dist/attribute/index.mjs +4 -7
  5. package/dist/attribute/{set.d.mts → set.attribute.d.mts} +4 -4
  6. package/dist/attribute/{set.mjs → set.attribute.mjs} +2 -2
  7. package/dist/create.d.mts +25 -0
  8. package/dist/create.mjs +17 -0
  9. package/dist/data.mjs +7 -7
  10. package/dist/event/delegation.mjs +8 -1
  11. package/dist/html/index.d.mts +23 -26
  12. package/dist/html/index.mjs +85 -18
  13. package/dist/html/sanitize.mjs +6 -5
  14. package/dist/index.d.mts +117 -54
  15. package/dist/index.mjs +541 -380
  16. package/dist/internal/attribute.d.mts +4 -3
  17. package/dist/internal/attribute.mjs +13 -23
  18. package/dist/internal/element-value.d.mts +2 -2
  19. package/dist/internal/element-value.mjs +12 -6
  20. package/dist/internal/get-value.mjs +3 -1
  21. package/dist/internal/property.d.mts +4 -0
  22. package/dist/internal/property.mjs +24 -0
  23. package/dist/property/get.property.d.mts +20 -0
  24. package/dist/property/get.property.mjs +35 -0
  25. package/dist/property/index.d.mts +3 -0
  26. package/dist/property/index.mjs +3 -0
  27. package/dist/property/set.property.d.mts +32 -0
  28. package/dist/property/set.property.mjs +32 -0
  29. package/dist/style.d.mts +16 -9
  30. package/dist/style.mjs +22 -21
  31. package/package.json +14 -6
  32. package/src/attribute/{get.ts → get.attribute.ts} +14 -3
  33. package/src/attribute/index.ts +10 -22
  34. package/src/attribute/{set.ts → set.attribute.ts} +9 -5
  35. package/src/create.ts +81 -0
  36. package/src/data.ts +16 -8
  37. package/src/event/delegation.ts +24 -3
  38. package/src/event/index.ts +9 -3
  39. package/src/find/index.ts +11 -3
  40. package/src/find/relative.ts +4 -0
  41. package/src/focusable.ts +10 -2
  42. package/src/html/index.ts +166 -58
  43. package/src/html/sanitize.ts +14 -11
  44. package/src/index.ts +2 -1
  45. package/src/internal/attribute.ts +23 -42
  46. package/src/internal/element-value.ts +25 -6
  47. package/src/internal/get-value.ts +14 -0
  48. package/src/internal/is.ts +4 -0
  49. package/src/internal/property.ts +42 -0
  50. package/src/is.ts +10 -2
  51. package/src/property/get.property.ts +73 -0
  52. package/src/property/index.ts +2 -0
  53. package/src/property/set.property.ts +103 -0
  54. package/src/style.ts +81 -36
  55. package/src/touch.ts +14 -2
@@ -4,12 +4,13 @@ import { Attribute } from "../models.mjs";
4
4
  declare function isAttribute(value: unknown): value is Attr | Attribute;
5
5
  declare function _isBadAttribute(first: unknown, second: unknown, decode: boolean): boolean;
6
6
  declare function _isBooleanAttribute(first: unknown, decode: boolean): boolean;
7
- declare function _isEmptyNonBooleanAttribute(first: unknown, second: unknown, decode: boolean): boolean;
8
7
  declare function _isInvalidBooleanAttribute(first: unknown, second: unknown, decode: boolean): boolean;
9
- declare function updateAttribute(element: Element, name: string, value: unknown, dispatch?: unknown): void;
8
+ declare function updateAttribute(element: Element, name: string, value: unknown, dispatch: boolean): void;
10
9
  /**
11
10
  * List of boolean attributes
12
11
  */
13
12
  declare const booleanAttributes: readonly string[];
13
+ declare const booleanAttributesSet: Set<string>;
14
+ declare const dispatchedAttributes: Set<string>;
14
15
  //#endregion
15
- export { _isBadAttribute, _isBooleanAttribute, _isEmptyNonBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes, isAttribute, updateAttribute };
16
+ export { _isBadAttribute, _isBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes, booleanAttributesSet, dispatchedAttributes, isAttribute, updateAttribute };
@@ -1,5 +1,7 @@
1
1
  import { updateElementValue } from "./element-value.mjs";
2
+ import { updateProperty } from "./property.mjs";
2
3
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
+ import { kebabCase } from "@oscarpalmer/atoms/string/case";
3
5
  //#region src/internal/attribute.ts
4
6
  function badAttributeHandler(name, value) {
5
7
  if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
@@ -29,6 +31,7 @@ function handleAttribute(callback, decode, first, second) {
29
31
  name = first;
30
32
  value = second;
31
33
  }
34
+ if (name != null) name = kebabCase(name);
32
35
  if (decode && value != null) value = decodeAttribute(value);
33
36
  return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
34
37
  }
@@ -41,9 +44,6 @@ function _isBadAttribute(first, second, decode) {
41
44
  function _isBooleanAttribute(first, decode) {
42
45
  return handleAttribute((name) => booleanAttributesSet.has(name?.toLowerCase()), decode, first, "");
43
46
  }
44
- function _isEmptyNonBooleanAttribute(first, second, decode) {
45
- return handleAttribute((name, value) => name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0, decode, first, second);
46
- }
47
47
  function _isInvalidBooleanAttribute(first, second, decode) {
48
48
  return handleAttribute(booleanAttributeHandler, decode, first, second);
49
49
  }
@@ -51,18 +51,12 @@ function isValidSourceAttribute(name, value) {
51
51
  return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
52
52
  }
53
53
  function updateAttribute(element, name, value, dispatch) {
54
- const normalizedName = name.toLowerCase();
55
- const isBoolean = booleanAttributesSet.has(normalizedName);
56
- const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === normalizedName) : value == null ? "" : value;
57
- if (name in element) updateProperty(element, normalizedName, next, dispatch);
54
+ const lowerCaseName = name.toLowerCase();
55
+ const isBoolean = booleanAttributesSet.has(lowerCaseName);
56
+ const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === lowerCaseName) : value == null ? "" : value;
57
+ if (isBoolean || dispatchedAttributes.has(name)) updateProperty(element, name, next, dispatch);
58
58
  updateElementValue(element, name, isBoolean ? next ? "" : null : value, element.setAttribute, element.removeAttribute, isBoolean, false);
59
59
  }
60
- function updateProperty(element, name, value, dispatch) {
61
- if (Object.is(element[name], value)) return;
62
- element[name] = value;
63
- const event = dispatch !== false && elementEvents[element.tagName]?.[name];
64
- if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
65
- }
66
60
  const EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
67
61
  const EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
68
62
  const EXPRESSION_EVENT_NAME = /^on/i;
@@ -101,16 +95,12 @@ const booleanAttributes = Object.freeze([
101
95
  "selected"
102
96
  ]);
103
97
  const booleanAttributesSet = new Set(booleanAttributes);
104
- const elementEvents = {
105
- DETAILS: { open: "toggle" },
106
- INPUT: {
107
- checked: "change",
108
- value: "input"
109
- },
110
- SELECT: { value: "change" },
111
- TEXTAREA: { value: "input" }
112
- };
98
+ const dispatchedAttributes = new Set([
99
+ "checked",
100
+ "open",
101
+ "value"
102
+ ]);
113
103
  const formElement = document.createElement("form");
114
104
  let textArea;
115
105
  //#endregion
116
- export { _isBadAttribute, _isBooleanAttribute, _isEmptyNonBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes, isAttribute, updateAttribute };
106
+ export { _isBadAttribute, _isBooleanAttribute, _isInvalidBooleanAttribute, booleanAttributes, booleanAttributesSet, dispatchedAttributes, isAttribute, updateAttribute };
@@ -1,6 +1,6 @@
1
1
  //#region src/internal/element-value.d.ts
2
- declare function setElementValue(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown) => void): void;
3
- declare function setElementValues(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown, dispatch?: unknown) => void): void;
2
+ declare function setElementValue(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void, style?: boolean): void;
3
+ declare function setElementValues(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void, style?: boolean): void;
4
4
  declare function updateElementValue(element: Element, key: string, value: unknown, set: (key: string, value: string) => void, remove: (key: string) => void, isBoolean: boolean, json: boolean): void;
5
5
  //#endregion
6
6
  export { setElementValue, setElementValues, updateElementValue };
@@ -2,16 +2,21 @@ import { isHTMLOrSVGElement } from "./is.mjs";
2
2
  import "../is.mjs";
3
3
  import { isAttribute } from "./attribute.mjs";
4
4
  import { isNullableOrWhitespace } from "@oscarpalmer/atoms/is";
5
+ import { kebabCase } from "@oscarpalmer/atoms/string/case";
5
6
  //#region src/internal/element-value.ts
6
- function setElementValue(element, first, second, third, callback) {
7
+ function normalizeKey(key, style) {
8
+ return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
9
+ }
10
+ function setElementValue(element, first, second, third, callback, style) {
7
11
  if (!isHTMLOrSVGElement(element)) return;
8
- if (typeof first === "string") setElementValues(element, first, second, third, callback);
9
- else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
12
+ if (typeof first === "string") setElementValues(element, first, second, third, callback, style);
13
+ else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback, style);
10
14
  }
11
- function setElementValues(element, first, second, third, callback) {
15
+ function setElementValues(element, first, second, third, callback, style) {
12
16
  if (!isHTMLOrSVGElement(element)) return;
17
+ const dispatch = third !== false;
13
18
  if (typeof first === "string") {
14
- callback(element, first, second, third);
19
+ callback(element, normalizeKey(first, style), second, dispatch);
15
20
  return;
16
21
  }
17
22
  const isArray = Array.isArray(first);
@@ -23,12 +28,13 @@ function setElementValues(element, first, second, third, callback) {
23
28
  const { length } = entries;
24
29
  for (let index = 0; index < length; index += 1) {
25
30
  const entry = entries[index];
26
- if (typeof entry === "object" && typeof entry?.name === "string") callback(element, entry.name, entry.value, third);
31
+ if (typeof entry === "object" && typeof entry?.name === "string") callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
27
32
  }
28
33
  }
29
34
  function updateElementValue(element, key, value, set, remove, isBoolean, json) {
30
35
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
31
36
  else set.call(element, key, json ? JSON.stringify(value) : String(value));
32
37
  }
38
+ const CSS_VARIABLE_PREFIX = "--";
33
39
  //#endregion
34
40
  export { setElementValue, setElementValues, updateElementValue };
@@ -1,5 +1,5 @@
1
- import { parse } from "@oscarpalmer/atoms/string";
2
1
  import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
2
+ import { parse } from "@oscarpalmer/atoms/string";
3
3
  //#region src/internal/get-value.ts
4
4
  function getBoolean(value, defaultValue) {
5
5
  return typeof value === "boolean" ? value : defaultValue ?? false;
@@ -11,9 +11,11 @@ function getAttributeValue(element, name, parseValue) {
11
11
  return EXPRESSION_DATA_PREFIX.test(normalized) && typeof value === "string" && parseValue ? parse(value) ?? value : value;
12
12
  }
13
13
  function getStyleValue(element, property, computed) {
14
+ if (property.startsWith(CSS_VARIABLE_PREFIX)) return element.style.getPropertyValue(property);
14
15
  const name = camelCase(property);
15
16
  return computed ? getComputedStyle(element)[name] : element.style[name];
16
17
  }
17
18
  const EXPRESSION_DATA_PREFIX = /^data-/i;
19
+ const CSS_VARIABLE_PREFIX = "--";
18
20
  //#endregion
19
21
  export { EXPRESSION_DATA_PREFIX, getAttributeValue, getBoolean, getStyleValue };
@@ -0,0 +1,4 @@
1
+ //#region src/internal/property.d.ts
2
+ declare function updateProperty(element: Element, name: string, value: unknown, dispatch: boolean): void;
3
+ //#endregion
4
+ export { updateProperty };
@@ -0,0 +1,24 @@
1
+ import { camelCase } from "@oscarpalmer/atoms/string/case";
2
+ //#region src/internal/property.ts
3
+ function updateProperty(element, name, value, dispatch) {
4
+ let property = name;
5
+ if (!(property in element)) property = camelCase(name);
6
+ if (!(property in element) || Object.is(element[property], value)) return;
7
+ element[property] = value;
8
+ const event = dispatch && elementEvents[element.tagName]?.[property];
9
+ if (typeof event === "string") element.dispatchEvent(new Event(event, {
10
+ bubbles: true,
11
+ cancelable: true
12
+ }));
13
+ }
14
+ const elementEvents = {
15
+ DETAILS: { open: "toggle" },
16
+ INPUT: {
17
+ checked: "change",
18
+ value: "input"
19
+ },
20
+ SELECT: { value: "change" },
21
+ TEXTAREA: { value: "input" }
22
+ };
23
+ //#endregion
24
+ export { updateProperty };
@@ -0,0 +1,20 @@
1
+ import { Primitive } from "@oscarpalmer/atoms/models";
2
+
3
+ //#region src/property/get.property.d.ts
4
+ type GetProperties<Target extends Element> = { [Property in keyof Target as Target[Property] extends Primitive ? Property : never]?: Target[Property] };
5
+ /**
6
+ * Get the values of one or more properties on an element
7
+ * @param target Target element
8
+ * @param properties Properties to get
9
+ * @returns Property values
10
+ */
11
+ declare function getProperties<Target extends Element, Property extends keyof GetProperties<Target>>(target: Target, properties: Property[]): Pick<GetProperties<Target>, Property>;
12
+ /**
13
+ * Get the value of a property on an element
14
+ * @param target Target element
15
+ * @param property Property to get
16
+ * @returns Property value
17
+ */
18
+ declare function getProperty<Target extends Element, Property extends keyof GetProperties<Target>>(target: Target, property: Property): GetProperties<Target>[Property];
19
+ //#endregion
20
+ export { getProperties, getProperty };
@@ -0,0 +1,35 @@
1
+ import { isHTMLOrSVGElement } from "../internal/is.mjs";
2
+ import { camelCase } from "@oscarpalmer/atoms/string/case";
3
+ //#region src/property/get.property.ts
4
+ /**
5
+ * Get the values of one or more properties on an element
6
+ * @param target Target element
7
+ * @param properties Properties to get
8
+ * @returns Property values
9
+ */
10
+ function getProperties(target, properties) {
11
+ const values = {};
12
+ if (!isHTMLOrSVGElement(target) || !Array.isArray(properties)) return values;
13
+ const { length } = properties;
14
+ for (let index = 0; index < length; index += 1) {
15
+ const property = properties[index];
16
+ if (typeof property === "string") values[property] = getPropertyValue(target, property);
17
+ }
18
+ return values;
19
+ }
20
+ /**
21
+ * Get the value of a property on an element
22
+ * @param target Target element
23
+ * @param property Property to get
24
+ * @returns Property value
25
+ */
26
+ function getProperty(target, property) {
27
+ if (isHTMLOrSVGElement(target) && typeof property === "string") return getPropertyValue(target, property);
28
+ }
29
+ function getPropertyValue(element, property) {
30
+ let actual = property;
31
+ if (!(actual in element)) actual = camelCase(actual);
32
+ if (actual in element) return element[actual];
33
+ }
34
+ //#endregion
35
+ export { getProperties, getProperty };
@@ -0,0 +1,3 @@
1
+ import { getProperties, getProperty } from "./get.property.mjs";
2
+ import { setProperties, setProperty } from "./set.property.mjs";
3
+ export { getProperties, getProperty, setProperties, setProperty };
@@ -0,0 +1,3 @@
1
+ import { getProperties, getProperty } from "./get.property.mjs";
2
+ import { setProperties, setProperty } from "./set.property.mjs";
3
+ export { getProperties, getProperty, setProperties, setProperty };
@@ -0,0 +1,32 @@
1
+ import { DispatchedAttributeName } from "../attribute/set.attribute.mjs";
2
+ import { Primitive } from "@oscarpalmer/atoms/models";
3
+
4
+ //#region src/property/set.property.d.ts
5
+ type DispatchedPropertyValue<Target extends Element, Property extends DispatchedAttributeName> = Property extends keyof SetProperties<Target> ? SetProperties<Target>[Property] : never;
6
+ type SetProperties<Target extends Element> = { [Property in keyof Target as Target[Property] extends Primitive ? Property : never]?: Target[Property] };
7
+ /**
8
+ * Set the values of one or more properties on an element
9
+ *
10
+ * Also updates attributes for boolean/dispatchable properties, and if `dispatch` is `true`, will dispatch events for dispatchable properties
11
+ * @param target Target element
12
+ * @param properties Properties to set
13
+ * @param dispatch Dispatch events for properties? _(defaults to `true`)_
14
+ */
15
+ declare function setProperties<Target extends Element>(target: Target, properties: SetProperties<Target>, dispatch?: boolean): void;
16
+ /**
17
+ * Set the value for a dispatchable property on an element
18
+ * @param target Target element
19
+ * @param property Property to set
20
+ * @param value Value to set
21
+ * @param dispatch Dispatch event for property? _(defaults to `true`)_
22
+ */
23
+ declare function setProperty<Target extends Element, Property extends DispatchedAttributeName>(target: Target, property: Property, value: DispatchedPropertyValue<Target, Property>, dispatch?: boolean): void;
24
+ /**
25
+ * Set the value for a property on an element
26
+ * @param target Target element
27
+ * @param property Property to set
28
+ * @param value Value to set
29
+ */
30
+ declare function setProperty<Target extends Element, Property extends keyof SetProperties<Target>>(target: Target, property: Property, value: SetProperties<Target>[Property]): void;
31
+ //#endregion
32
+ export { setProperties, setProperty };
@@ -0,0 +1,32 @@
1
+ import { isHTMLOrSVGElement } from "../internal/is.mjs";
2
+ import "../is.mjs";
3
+ import { updateProperty } from "../internal/property.mjs";
4
+ import { booleanAttributesSet, dispatchedAttributes } from "../internal/attribute.mjs";
5
+ import { setAttribute } from "../attribute/set.attribute.mjs";
6
+ import "../attribute/index.mjs";
7
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
8
+ //#region src/property/set.property.ts
9
+ /**
10
+ * Set the values of one or more properties on an element
11
+ *
12
+ * Also updates attributes for boolean/dispatchable properties, and if `dispatch` is `true`, will dispatch events for dispatchable properties
13
+ * @param target Target element
14
+ * @param properties Properties to set
15
+ * @param dispatch Dispatch events for properties? _(defaults to `true`)_
16
+ */
17
+ function setProperties(target, properties, dispatch) {
18
+ if (!isHTMLOrSVGElement(target) || !isPlainObject(properties)) return;
19
+ const shouldDispatch = dispatch !== false;
20
+ const keys = Object.keys(properties);
21
+ const { length } = keys;
22
+ for (let index = 0; index < length; index += 1) setPropertyValue(target, keys[index], properties[keys[index]], shouldDispatch);
23
+ }
24
+ function setProperty(target, property, value, dispatch) {
25
+ if (isHTMLOrSVGElement(target) && typeof property === "string") setPropertyValue(target, property, value, dispatch !== false);
26
+ }
27
+ function setPropertyValue(element, property, value, dispatch) {
28
+ if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) setAttribute(element, property, value, dispatch);
29
+ else updateProperty(element, property, value, dispatch);
30
+ }
31
+ //#endregion
32
+ export { setProperties, setProperty };
package/dist/style.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { TextDirection } from "./models.mjs";
2
2
 
3
3
  //#region src/style.d.ts
4
+ type CSSStyleValues = Variables & CSSStyleDeclaration;
4
5
  type StyleToggler = {
5
6
  /**
6
7
  * Set the provided styles on the element
@@ -11,6 +12,8 @@ type StyleToggler = {
11
12
  */
12
13
  remove(): void;
13
14
  };
15
+ type Styles = Partial<Record<keyof CSSStyleValues, unknown>>;
16
+ type Variables<Value extends Record<string, string | undefined> = Record<string, string | undefined>> = { [property in keyof Value as `--${string & property}`]?: string | undefined };
14
17
  /**
15
18
  * Get a style from an element
16
19
  * @param element Element to get the style from
@@ -18,7 +21,7 @@ type StyleToggler = {
18
21
  * @param computed Get the computed style? _(defaults to `false`)_
19
22
  * @returns Style value
20
23
  */
21
- declare function getStyle(element: Element, property: keyof CSSStyleDeclaration, computed?: boolean): string | undefined;
24
+ declare function getStyle(element: Element, property: keyof CSSStyleValues, computed?: boolean): string | undefined;
22
25
  /**
23
26
  * Get styles from an element
24
27
  * @param element Element to get the styles from
@@ -26,33 +29,37 @@ declare function getStyle(element: Element, property: keyof CSSStyleDeclaration,
26
29
  * @param computed Get the computed styles? _(defaults to `false`)_
27
30
  * @returns Style values
28
31
  */
29
- declare function getStyles<Property extends keyof CSSStyleDeclaration>(element: Element, properties: Property[], computed?: boolean): Record<Property, string | undefined>;
32
+ declare function getStyles<Property extends keyof CSSStyleValues>(element: Element, properties: Property[], computed?: boolean): Record<Property, string | undefined>;
30
33
  /**
31
- * Get the text direction of an element
32
- * @param element Element to get the text direction from
33
- * @param computed Get the computed text direction? _(defaults to `false`)_
34
+ * Get the text direction of a node or element _(or document, if element is invalid)_
35
+ * @param node Node or element to get the text direction from
34
36
  * @returns Text direction
35
37
  */
36
- declare function getTextDirection(element: Element, computed?: boolean): TextDirection;
38
+ declare function getTextDirection(node: Element | Node): TextDirection;
39
+ /**
40
+ * Get the text direction of the document
41
+ * @returns Text direction
42
+ */
43
+ declare function getTextDirection(): TextDirection;
37
44
  /**
38
45
  * Set a style on an element
39
46
  * @param element Element to set the style on
40
47
  * @param property Style name
41
48
  * @param value Style value
42
49
  */
43
- declare function setStyle(element: Element, property: keyof CSSStyleDeclaration, value?: string): void;
50
+ declare function setStyle(element: Element, property: keyof CSSStyleValues, value?: unknown): void;
44
51
  /**
45
52
  * Set styles on an element
46
53
  * @param element Element to set the styles on
47
54
  * @param styles Styles to set
48
55
  */
49
- declare function setStyles(element: Element, styles: Partial<CSSStyleDeclaration>): void;
56
+ declare function setStyles(element: Element, styles: Styles): void;
50
57
  /**
51
58
  * Toggle styles for an element
52
59
  * @param element Element to style
53
60
  * @param styles Styles to be set or removed
54
61
  * @returns Style toggler
55
62
  */
56
- declare function toggleStyles(element: Element, styles: Partial<CSSStyleDeclaration>): StyleToggler;
63
+ declare function toggleStyles(element: Element, styles: Styles): StyleToggler;
57
64
  //#endregion
58
65
  export { StyleToggler, getStyle, getStyles, getTextDirection, setStyle, setStyles, toggleStyles };
package/dist/style.mjs CHANGED
@@ -10,8 +10,7 @@ import { getStyleValue } from "./internal/get-value.mjs";
10
10
  * @returns Style value
11
11
  */
12
12
  function getStyle(element, property, computed) {
13
- if (!isHTMLOrSVGElement(element) || typeof property !== "string") return;
14
- return getStyleValue(element, property, computed === true);
13
+ if (isHTMLOrSVGElement(element) && typeof property === "string") return getStyleValue(element, property, computed === true);
15
14
  }
16
15
  /**
17
16
  * Get styles from an element
@@ -30,18 +29,13 @@ function getStyles(element, properties, computed) {
30
29
  }
31
30
  return styles;
32
31
  }
33
- /**
34
- * Get the text direction of an element
35
- * @param element Element to get the text direction from
36
- * @param computed Get the computed text direction? _(defaults to `false`)_
37
- * @returns Text direction
38
- */
39
- function getTextDirection(element, computed) {
40
- if (!(element instanceof Element)) return;
41
- const direction = element.getAttribute(ATTRIBUTE_DIRECTION);
42
- if (direction != null && EXPRESSION_DIRECTION.test(direction)) return direction.toLowerCase();
43
- const value = getStyleValue(element, "direction", computed === true);
44
- return value === "rtl" ? value : "ltr";
32
+ function getTextDirection(node) {
33
+ let target;
34
+ if (isHTMLOrSVGElement(node)) target = node;
35
+ else target = node instanceof Node ? node.ownerDocument?.documentElement ?? document.documentElement : document.documentElement;
36
+ let { direction } = target.style;
37
+ if (direction === "") direction = getStyleValue(target, PROPERTY_DIRECTION, true);
38
+ return direction === DIRECTION_RTL ? DIRECTION_RTL : DIRECTION_LTR;
45
39
  }
46
40
  /**
47
41
  * Set a style on an element
@@ -50,7 +44,7 @@ function getTextDirection(element, computed) {
50
44
  * @param value Style value
51
45
  */
52
46
  function setStyle(element, property, value) {
53
- setElementValues(element, property, value, null, updateStyleProperty);
47
+ setElementValues(element, property, value, null, updateStyleProperty, true);
54
48
  }
55
49
  /**
56
50
  * Set styles on an element
@@ -58,7 +52,7 @@ function setStyle(element, property, value) {
58
52
  * @param styles Styles to set
59
53
  */
60
54
  function setStyles(element, styles) {
61
- setElementValues(element, styles, null, null, updateStyleProperty);
55
+ setElementValues(element, styles, null, null, updateStyleProperty, true);
62
56
  }
63
57
  /**
64
58
  * Toggle styles for an element
@@ -76,11 +70,12 @@ function toggleStyles(element, styles) {
76
70
  } else {
77
71
  next = { ...values };
78
72
  values = {};
79
- for (const key of keys) values[key] = void 0;
73
+ for (let index = 0; index < length; index += 1) values[keys[index]] = void 0;
80
74
  }
81
75
  setStyles(element, next);
82
76
  }
83
77
  const keys = Object.keys(styles);
78
+ const { length } = keys;
84
79
  let hasSet = false;
85
80
  let values = {};
86
81
  return {
@@ -94,12 +89,18 @@ function toggleStyles(element, styles) {
94
89
  }
95
90
  function updateStyleProperty(element, key, value) {
96
91
  updateElementValue(element, key, value, function(property, style) {
97
- this.style[property] = style;
92
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, String(style));
93
+ else this.style[property] = String(style);
98
94
  }, function(property) {
99
- this.style[property] = "";
95
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
96
+ else this.style[property] = "";
97
+ if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
100
98
  }, false, false);
101
99
  }
102
- const ATTRIBUTE_DIRECTION = "dir";
103
- const EXPRESSION_DIRECTION = /^(ltr|rtl)$/i;
100
+ const ATTRIBUTE_STYLE = "style";
101
+ const DIRECTION_LTR = "ltr";
102
+ const DIRECTION_RTL = "rtl";
103
+ const PROPERTY_DIRECTION = "direction";
104
+ const VARIABLE_PREFIX = "--";
104
105
  //#endregion
105
106
  export { getStyle, getStyles, getTextDirection, setStyle, setStyles, toggleStyles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/toretto",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "description": "A collection of badass DOM utilities.",
5
5
  "keywords": [
6
6
  "dom",
@@ -34,6 +34,10 @@
34
34
  "types": "./dist/attribute/index.d.mts",
35
35
  "default": "./dist/attribute/index.mjs"
36
36
  },
37
+ "./create": {
38
+ "types": "./dist/create.d.mts",
39
+ "default": "./dist/create.mjs"
40
+ },
37
41
  "./data": {
38
42
  "types": "./dist/data.d.mts",
39
43
  "default": "./dist/data.mjs"
@@ -61,6 +65,10 @@
61
65
  "./models": {
62
66
  "types": "./dist/models.d.mts"
63
67
  },
68
+ "./property": {
69
+ "types": "./dist/property/index.d.mts",
70
+ "default": "./dist/property/index.mjs"
71
+ },
64
72
  "./style": {
65
73
  "types": "./dist/style.d.mts",
66
74
  "default": "./dist/style.mjs"
@@ -75,16 +83,16 @@
75
83
  "tsdown:build": "npx tsdown -c ./tsdown.config.ts",
76
84
  "tsdown:watch": "npx tsdown -c ./tsdown.config.ts --watch",
77
85
  "test": "npx vp test run --coverage",
78
- "test:leak": "npx vp test run --detect-async-leaks --coverage",
79
- "watch": "npx vite build --watch"
86
+ "test:leak": "npx vp test run --detect-async-leaks --coverage"
80
87
  },
81
88
  "dependencies": {
82
- "@oscarpalmer/atoms": "^0.165"
89
+ "@oscarpalmer/atoms": "^0.185"
83
90
  },
84
91
  "devDependencies": {
85
- "@types/node": "^25.5",
92
+ "@oxlint/plugins": "^1.62",
93
+ "@types/node": "^25.6",
86
94
  "@vitest/coverage-istanbul": "^4.1",
87
- "jsdom": "^28.1",
95
+ "jsdom": "^29.1",
88
96
  "tsdown": "^0.21",
89
97
  "typescript": "^5.9",
90
98
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
@@ -1,6 +1,15 @@
1
+ import {kebabCase} from '@oscarpalmer/atoms/string/case';
1
2
  import {getAttributeValue} from '../internal/get-value';
2
3
  import {isHTMLOrSVGElement} from '../internal/is';
3
4
 
5
+ // #region Types
6
+
7
+ type DataPrefixedName = `data-${string}`;
8
+
9
+ // #endregion
10
+
11
+ // #region Functions
12
+
4
13
  /**
5
14
  * Get the value of a specific attribute from an element
6
15
  * @param element Element to get attribute from
@@ -8,7 +17,7 @@ import {isHTMLOrSVGElement} from '../internal/is';
8
17
  * @param parse Parse value? _(defaults to `true`)_
9
18
  * @returns Attribute value _(or `undefined`)_
10
19
  */
11
- export function getAttribute(element: Element, name: `data-${string}`, parse?: boolean): unknown;
20
+ export function getAttribute(element: Element, name: DataPrefixedName, parse?: boolean): unknown;
12
21
 
13
22
  /**
14
23
  * Get the value of a specific attribute from an element
@@ -20,7 +29,7 @@ export function getAttribute(element: Element, name: string): unknown;
20
29
 
21
30
  export function getAttribute(element: Element, name: string, parseValues?: boolean): unknown {
22
31
  if (isHTMLOrSVGElement(element) && typeof name === 'string') {
23
- return getAttributeValue(element, name, parseValues !== false);
32
+ return getAttributeValue(element, kebabCase(name), parseValues !== false);
24
33
  }
25
34
  }
26
35
 
@@ -50,9 +59,11 @@ export function getAttributes<Key extends string>(
50
59
  const name = names[index];
51
60
 
52
61
  if (typeof name === 'string') {
53
- attributes[name] = getAttributeValue(element, name, shouldParse);
62
+ attributes[name] = getAttributeValue(element, kebabCase(name), shouldParse);
54
63
  }
55
64
  }
56
65
 
57
66
  return attributes;
58
67
  }
68
+
69
+ // #endregion
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  _isBadAttribute,
3
3
  _isBooleanAttribute,
4
- _isEmptyNonBooleanAttribute,
5
4
  _isInvalidBooleanAttribute,
6
5
  } from '../internal/attribute';
7
6
  import type {Attribute} from '../models';
8
7
 
8
+ // #region Functions
9
+
9
10
  /**
10
11
  * Is the attribute considered bad and potentially harmful?
11
12
  * @param attribute Attribute to check
@@ -43,25 +44,6 @@ export function isBooleanAttribute(first: unknown): boolean {
43
44
  return _isBooleanAttribute(first, true);
44
45
  }
45
46
 
46
- /**
47
- * Is the attribute empty and not a boolean attribute?
48
- * @param attribute Attribute to check
49
- * @returns `true` if attribute is empty and not a boolean attribute
50
- */
51
- export function isEmptyNonBooleanAttribute(attribute: Attr | Attribute): boolean;
52
-
53
- /**
54
- * Is the attribute empty and not a boolean attribute?
55
- * @param name Attribute name
56
- * @param value Attribute value
57
- * @returns `true` if attribute is empty and not a boolean attribute
58
- */
59
- export function isEmptyNonBooleanAttribute(name: string, value: string): boolean;
60
-
61
- export function isEmptyNonBooleanAttribute(first: unknown, second?: unknown): boolean {
62
- return _isEmptyNonBooleanAttribute(first, second, true);
63
- }
64
-
65
47
  /**
66
48
  * Is the attribute an invalid boolean attribute?
67
49
  *
@@ -85,6 +67,12 @@ export function isInvalidBooleanAttribute(first: unknown, second?: unknown): boo
85
67
  return _isInvalidBooleanAttribute(first, second, true);
86
68
  }
87
69
 
70
+ // #endregion
71
+
72
+ // #region Exports
73
+
88
74
  export {booleanAttributes} from '../internal/attribute';
89
- export * from './get';
90
- export * from './set';
75
+ export * from './get.attribute';
76
+ export {setAttribute, setAttributes} from './set.attribute';
77
+
78
+ // #endregion