@oscarpalmer/toretto 0.42.0 → 0.44.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/data.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { isHTMLOrSVGElement } from "./internal/is.mjs";
2
2
  import { setElementValues, updateElementValue } from "./internal/element-value.mjs";
3
3
  import { EXPRESSION_DATA_PREFIX } from "./internal/get-value.mjs";
4
- import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
5
4
  import { parse } from "@oscarpalmer/atoms/string";
5
+ import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
6
6
  //#region src/data.ts
7
7
  function getData(element, keys, parseValues) {
8
8
  if (!isHTMLOrSVGElement(element)) return;
package/dist/index.d.mts CHANGED
@@ -530,6 +530,7 @@ declare function setProperty<Target extends Element, Property extends Dispatched
530
530
  declare function setProperty<Target extends Element, Property extends keyof SetProperties<Target>>(target: Target, property: Property, value: SetProperties<Target>[Property]): void;
531
531
  //#endregion
532
532
  //#region src/style.d.ts
533
+ type CSSStyleValues = Variables & CSSStyleDeclaration;
533
534
  type StyleToggler = {
534
535
  /**
535
536
  * Set the provided styles on the element
@@ -540,7 +541,8 @@ type StyleToggler = {
540
541
  */
541
542
  remove(): void;
542
543
  };
543
- type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
544
+ type Styles = Partial<Record<keyof CSSStyleValues, unknown>>;
545
+ type Variables<Value extends Record<string, string | undefined> = Record<string, string | undefined>> = { [property in keyof Value as `--${string & property}`]?: string | undefined };
544
546
  /**
545
547
  * Get a style from an element
546
548
  * @param element Element to get the style from
@@ -548,7 +550,7 @@ type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
548
550
  * @param computed Get the computed style? _(defaults to `false`)_
549
551
  * @returns Style value
550
552
  */
551
- declare function getStyle(element: Element, property: keyof CSSStyleDeclaration, computed?: boolean): string | undefined;
553
+ declare function getStyle(element: Element, property: keyof CSSStyleValues, computed?: boolean): string | undefined;
552
554
  /**
553
555
  * Get styles from an element
554
556
  * @param element Element to get the styles from
@@ -556,7 +558,7 @@ declare function getStyle(element: Element, property: keyof CSSStyleDeclaration,
556
558
  * @param computed Get the computed styles? _(defaults to `false`)_
557
559
  * @returns Style values
558
560
  */
559
- declare function getStyles<Property extends keyof CSSStyleDeclaration>(element: Element, properties: Property[], computed?: boolean): Record<Property, string | undefined>;
561
+ declare function getStyles<Property extends keyof CSSStyleValues>(element: Element, properties: Property[], computed?: boolean): Record<Property, string | undefined>;
560
562
  /**
561
563
  * Get the text direction of a node or element _(or document, if element is invalid)_
562
564
  * @param node Node or element to get the text direction from
@@ -574,7 +576,7 @@ declare function getTextDirection(): TextDirection;
574
576
  * @param property Style name
575
577
  * @param value Style value
576
578
  */
577
- declare function setStyle(element: Element, property: keyof CSSStyleDeclaration, value?: unknown): void;
579
+ declare function setStyle(element: Element, property: keyof CSSStyleValues, value?: unknown): void;
578
580
  /**
579
581
  * Set styles on an element
580
582
  * @param element Element to set the styles on
package/dist/index.mjs CHANGED
@@ -51,19 +51,6 @@ function isPlainObject(value) {
51
51
  return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
52
52
  }
53
53
  //#endregion
54
- //#region node_modules/@oscarpalmer/atoms/dist/internal/array/compact.mjs
55
- function compact(array, strict) {
56
- if (!Array.isArray(array)) return [];
57
- if (strict === true) return array.filter(Boolean);
58
- const { length } = array;
59
- const compacted = [];
60
- for (let index = 0; index < length; index += 1) {
61
- const item = array[index];
62
- if (item != null) compacted.push(item);
63
- }
64
- return compacted;
65
- }
66
- //#endregion
67
54
  //#region node_modules/@oscarpalmer/atoms/dist/internal/string.mjs
68
55
  /**
69
56
  * Get the string value from any value
@@ -79,13 +66,23 @@ function getString(value) {
79
66
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
80
67
  }
81
68
  /**
82
- * Join an array of values into a string
83
- * @param value Array of values
69
+ * Join an array of values into a string _(while ignoring empty values)_
70
+ *
71
+ * _(`null`, `undefined`, and any values that become whitespace-only strings are considered empty)_
72
+ * @param array Array of values
84
73
  * @param delimiter Delimiter to use between values
85
74
  * @returns Joined string
86
75
  */
87
- function join(value, delimiter) {
88
- return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
76
+ function join(array, delimiter) {
77
+ if (!Array.isArray(array)) return "";
78
+ const { length } = array;
79
+ if (length === 0) return "";
80
+ const values = [];
81
+ for (let index = 0; index < length; index += 1) {
82
+ const item = getString(array[index]);
83
+ if (item.trim().length > 0) values.push(item);
84
+ }
85
+ return values.join(typeof delimiter === "string" ? delimiter : "");
89
86
  }
90
87
  /**
91
88
  * Split a string into words _(and other readable parts)_
@@ -99,15 +96,30 @@ const EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
99
96
  //#endregion
100
97
  //#region node_modules/@oscarpalmer/atoms/dist/is.mjs
101
98
  /**
102
- * Is the value `undefined`, `null`, or a whitespace-only string?
99
+ * Is the value `undefined`, `null`, or stringified as a whitespace-only string?
103
100
  * @param value Value to check
104
- * @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
101
+ * @returns `true` if the value is nullable or matches a whitespace-only string, otherwise `false`
105
102
  */
106
103
  function isNullableOrWhitespace(value) {
107
104
  return value == null || EXPRESSION_WHITESPACE$1.test(getString(value));
108
105
  }
109
106
  const EXPRESSION_WHITESPACE$1 = /^\s*$/;
110
107
  //#endregion
108
+ //#region node_modules/@oscarpalmer/atoms/dist/string/index.mjs
109
+ /**
110
+ * Parse a JSON string into its proper value _(or `undefined` if it fails)_
111
+ * @param value JSON string to parse
112
+ * @param reviver Reviver function to transform the parsed values
113
+ * @returns Parsed value or `undefined` if parsing fails
114
+ */
115
+ function parse(value, reviver) {
116
+ try {
117
+ return JSON.parse(value, reviver);
118
+ } catch {
119
+ return;
120
+ }
121
+ }
122
+ //#endregion
111
123
  //#region node_modules/@oscarpalmer/atoms/dist/internal/number.mjs
112
124
  /**
113
125
  * Clamp a number between a minimum and maximum value
@@ -192,6 +204,9 @@ var SizedMap = class extends Map {
192
204
  };
193
205
  //#endregion
194
206
  //#region node_modules/@oscarpalmer/atoms/dist/function/memoize.mjs
207
+ /**
208
+ * A memoized function, caching and retrieving results based on the its parameters _(or a custom cache key)_
209
+ */
195
210
  var Memoized = class {
196
211
  #state;
197
212
  /**
@@ -406,16 +421,23 @@ const CHILD_NODE_TYPES = new Set([
406
421
  ]);
407
422
  //#endregion
408
423
  //#region src/internal/element-value.ts
409
- function setElementValue(element, first, second, third, callback) {
424
+ function ignoreSetAttribute(element, name) {
425
+ if (element instanceof HTMLTextAreaElement && name === "value") return true;
426
+ return false;
427
+ }
428
+ function normalizeKey(key, style) {
429
+ return style && key.startsWith(CSS_VARIABLE_PREFIX$1) ? key : kebabCase(key);
430
+ }
431
+ function setElementValue(element, first, second, third, callback, style) {
410
432
  if (!isHTMLOrSVGElement(element)) return;
411
- if (typeof first === "string") setElementValues(element, first, second, third, callback);
412
- else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
433
+ if (typeof first === "string") setElementValues(element, first, second, third, callback, style);
434
+ else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback, style);
413
435
  }
414
- function setElementValues(element, first, second, third, callback) {
436
+ function setElementValues(element, first, second, third, callback, style) {
415
437
  if (!isHTMLOrSVGElement(element)) return;
416
438
  const dispatch = third !== false;
417
439
  if (typeof first === "string") {
418
- callback(element, kebabCase(first), second, dispatch);
440
+ callback(element, normalizeKey(first, style), second, dispatch);
419
441
  return;
420
442
  }
421
443
  const isArray = Array.isArray(first);
@@ -427,13 +449,14 @@ function setElementValues(element, first, second, third, callback) {
427
449
  const { length } = entries;
428
450
  for (let index = 0; index < length; index += 1) {
429
451
  const entry = entries[index];
430
- if (typeof entry === "object" && typeof entry?.name === "string") callback(element, kebabCase(entry.name), entry.value, dispatch);
452
+ if (typeof entry === "object" && typeof entry?.name === "string") callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
431
453
  }
432
454
  }
433
455
  function updateElementValue(element, key, value, set, remove, isBoolean, json) {
434
456
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
435
- else set.call(element, key, json ? JSON.stringify(value) : String(value));
457
+ else if (!ignoreSetAttribute(element, key)) set.call(element, key, json ? JSON.stringify(value) : getString(value));
436
458
  }
459
+ const CSS_VARIABLE_PREFIX$1 = "--";
437
460
  //#endregion
438
461
  //#region src/internal/property.ts
439
462
  function updateProperty(element, name, value, dispatch) {
@@ -442,7 +465,10 @@ function updateProperty(element, name, value, dispatch) {
442
465
  if (!(property in element) || Object.is(element[property], value)) return;
443
466
  element[property] = value;
444
467
  const event = dispatch && elementEvents[element.tagName]?.[property];
445
- if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
468
+ if (typeof event === "string") element.dispatchEvent(new Event(event, {
469
+ bubbles: true,
470
+ cancelable: true
471
+ }));
446
472
  }
447
473
  const elementEvents = {
448
474
  DETAILS: { open: "toggle" },
@@ -478,7 +504,7 @@ function handleAttribute(callback, decode, first, second) {
478
504
  let value;
479
505
  if (isAttribute(first)) {
480
506
  name = first.name;
481
- value = String(first.value);
507
+ value = getString(first.value);
482
508
  } else if (typeof first === "string" && typeof second === "string") {
483
509
  name = first;
484
510
  value = second;
@@ -555,21 +581,6 @@ const dispatchedAttributes = new Set([
555
581
  const formElement = document.createElement("form");
556
582
  let textArea;
557
583
  //#endregion
558
- //#region node_modules/@oscarpalmer/atoms/dist/string/index.mjs
559
- /**
560
- * Parse a JSON string into its proper value _(or `undefined` if it fails)_
561
- * @param value JSON string to parse
562
- * @param reviver Reviver function to transform the parsed values
563
- * @returns Parsed value or `undefined` if parsing fails
564
- */
565
- function parse(value, reviver) {
566
- try {
567
- return JSON.parse(value, reviver);
568
- } catch {
569
- return;
570
- }
571
- }
572
- //#endregion
573
584
  //#region src/internal/get-value.ts
574
585
  function getBoolean(value, defaultValue) {
575
586
  return typeof value === "boolean" ? value : defaultValue ?? false;
@@ -581,10 +592,12 @@ function getAttributeValue(element, name, parseValue) {
581
592
  return EXPRESSION_DATA_PREFIX.test(normalized) && typeof value === "string" && parseValue ? parse(value) ?? value : value;
582
593
  }
583
594
  function getStyleValue(element, property, computed) {
595
+ if (property.startsWith(CSS_VARIABLE_PREFIX)) return element.style.getPropertyValue(property);
584
596
  const name = camelCase(property);
585
597
  return computed ? getComputedStyle(element)[name] : element.style[name];
586
598
  }
587
599
  const EXPRESSION_DATA_PREFIX = /^data-/i;
600
+ const CSS_VARIABLE_PREFIX = "--";
588
601
  //#endregion
589
602
  //#region src/attribute/get.attribute.ts
590
603
  function getAttribute(element, name, parseValues) {
@@ -661,7 +674,7 @@ function getTextDirection(node) {
661
674
  if (isHTMLOrSVGElement(node)) target = node;
662
675
  else target = node instanceof Node ? node.ownerDocument?.documentElement ?? document.documentElement : document.documentElement;
663
676
  let { direction } = target.style;
664
- if (direction === "") direction = getStyleValue(target, PROPETY_DIRECTION, true);
677
+ if (direction === "") direction = getStyleValue(target, PROPERTY_DIRECTION, true);
665
678
  return direction === DIRECTION_RTL ? DIRECTION_RTL : DIRECTION_LTR;
666
679
  }
667
680
  /**
@@ -671,7 +684,7 @@ function getTextDirection(node) {
671
684
  * @param value Style value
672
685
  */
673
686
  function setStyle(element, property, value) {
674
- setElementValues(element, property, value, null, updateStyleProperty);
687
+ setElementValues(element, property, value, null, updateStyleProperty, true);
675
688
  }
676
689
  /**
677
690
  * Set styles on an element
@@ -679,7 +692,7 @@ function setStyle(element, property, value) {
679
692
  * @param styles Styles to set
680
693
  */
681
694
  function setStyles(element, styles) {
682
- setElementValues(element, styles, null, null, updateStyleProperty);
695
+ setElementValues(element, styles, null, null, updateStyleProperty, true);
683
696
  }
684
697
  /**
685
698
  * Toggle styles for an element
@@ -716,14 +729,19 @@ function toggleStyles(element, styles) {
716
729
  }
717
730
  function updateStyleProperty(element, key, value) {
718
731
  updateElementValue(element, key, value, function(property, style) {
719
- this.style[property] = String(style);
732
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
733
+ else this.style[property] = getString(style);
720
734
  }, function(property) {
721
- this.style[property] = "";
735
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
736
+ else this.style[property] = "";
737
+ if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
722
738
  }, false, false);
723
739
  }
740
+ const ATTRIBUTE_STYLE = "style";
724
741
  const DIRECTION_LTR = "ltr";
725
742
  const DIRECTION_RTL = "rtl";
726
- const PROPETY_DIRECTION = "direction";
743
+ const PROPERTY_DIRECTION = "direction";
744
+ const VARIABLE_PREFIX = "--";
727
745
  //#endregion
728
746
  //#region src/property/get.property.ts
729
747
  /**
@@ -771,16 +789,14 @@ function setProperties(target, properties, dispatch) {
771
789
  const shouldDispatch = dispatch !== false;
772
790
  const keys = Object.keys(properties);
773
791
  const { length } = keys;
774
- for (let index = 0; index < length; index += 1) {
775
- const key = keys[index];
776
- if (booleanAttributesSet.has(key.toLowerCase()) || dispatchedAttributes.has(key)) setAttribute(target, key, properties[key], shouldDispatch);
777
- else updateProperty(target, key, properties[key], shouldDispatch);
778
- }
792
+ for (let index = 0; index < length; index += 1) setPropertyValue(target, keys[index], properties[keys[index]], shouldDispatch);
779
793
  }
780
794
  function setProperty(target, property, value, dispatch) {
781
- if (!isHTMLOrSVGElement(target) || typeof property !== "string") return;
782
- if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) setAttribute(target, property, value, dispatch !== false);
783
- else updateProperty(target, property, value, dispatch !== false);
795
+ if (isHTMLOrSVGElement(target) && typeof property === "string") setPropertyValue(target, property, value, dispatch !== false);
796
+ }
797
+ function setPropertyValue(element, property, value, dispatch) {
798
+ if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) setAttribute(element, property, value, dispatch);
799
+ else updateProperty(element, property, value, dispatch);
784
800
  }
785
801
  //#endregion
786
802
  //#region src/create.ts
@@ -1,6 +1,7 @@
1
1
  import { updateElementValue } from "./element-value.mjs";
2
2
  import { updateProperty } from "./property.mjs";
3
3
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
+ import { getString } from "@oscarpalmer/atoms/string";
4
5
  import { kebabCase } from "@oscarpalmer/atoms/string/case";
5
6
  //#region src/internal/attribute.ts
6
7
  function badAttributeHandler(name, value) {
@@ -26,7 +27,7 @@ function handleAttribute(callback, decode, first, second) {
26
27
  let value;
27
28
  if (isAttribute(first)) {
28
29
  name = first.name;
29
- value = String(first.value);
30
+ value = getString(first.value);
30
31
  } else if (typeof first === "string" && typeof second === "string") {
31
32
  name = first;
32
33
  value = second;
@@ -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, dispatch: boolean) => void): void;
3
- declare function setElementValues(element: Element, first: unknown, second: unknown, third: unknown, callback: (element: Element, key: string, value: unknown, dispatch: boolean) => 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,18 +2,26 @@ 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 { getString } from "@oscarpalmer/atoms/string";
5
6
  import { kebabCase } from "@oscarpalmer/atoms/string/case";
6
7
  //#region src/internal/element-value.ts
7
- function setElementValue(element, first, second, third, callback) {
8
+ function ignoreSetAttribute(element, name) {
9
+ if (element instanceof HTMLTextAreaElement && name === "value") return true;
10
+ return false;
11
+ }
12
+ function normalizeKey(key, style) {
13
+ return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
14
+ }
15
+ function setElementValue(element, first, second, third, callback, style) {
8
16
  if (!isHTMLOrSVGElement(element)) return;
9
- if (typeof first === "string") setElementValues(element, first, second, third, callback);
10
- else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
17
+ if (typeof first === "string") setElementValues(element, first, second, third, callback, style);
18
+ else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback, style);
11
19
  }
12
- function setElementValues(element, first, second, third, callback) {
20
+ function setElementValues(element, first, second, third, callback, style) {
13
21
  if (!isHTMLOrSVGElement(element)) return;
14
22
  const dispatch = third !== false;
15
23
  if (typeof first === "string") {
16
- callback(element, kebabCase(first), second, dispatch);
24
+ callback(element, normalizeKey(first, style), second, dispatch);
17
25
  return;
18
26
  }
19
27
  const isArray = Array.isArray(first);
@@ -25,12 +33,13 @@ function setElementValues(element, first, second, third, callback) {
25
33
  const { length } = entries;
26
34
  for (let index = 0; index < length; index += 1) {
27
35
  const entry = entries[index];
28
- if (typeof entry === "object" && typeof entry?.name === "string") callback(element, kebabCase(entry.name), entry.value, dispatch);
36
+ if (typeof entry === "object" && typeof entry?.name === "string") callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
29
37
  }
30
38
  }
31
39
  function updateElementValue(element, key, value, set, remove, isBoolean, json) {
32
40
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
33
- else set.call(element, key, json ? JSON.stringify(value) : String(value));
41
+ else if (!ignoreSetAttribute(element, key)) set.call(element, key, json ? JSON.stringify(value) : getString(value));
34
42
  }
43
+ const CSS_VARIABLE_PREFIX = "--";
35
44
  //#endregion
36
45
  export { setElementValue, setElementValues, updateElementValue };
@@ -1,5 +1,5 @@
1
- import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
2
1
  import { parse } from "@oscarpalmer/atoms/string";
2
+ import { camelCase, kebabCase } from "@oscarpalmer/atoms/string/case";
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 };
@@ -6,7 +6,10 @@ function updateProperty(element, name, value, dispatch) {
6
6
  if (!(property in element) || Object.is(element[property], value)) return;
7
7
  element[property] = value;
8
8
  const event = dispatch && elementEvents[element.tagName]?.[property];
9
- if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
9
+ if (typeof event === "string") element.dispatchEvent(new Event(event, {
10
+ bubbles: true,
11
+ cancelable: true
12
+ }));
10
13
  }
11
14
  const elementEvents = {
12
15
  DETAILS: { open: "toggle" },
@@ -19,16 +19,14 @@ function setProperties(target, properties, dispatch) {
19
19
  const shouldDispatch = dispatch !== false;
20
20
  const keys = Object.keys(properties);
21
21
  const { length } = keys;
22
- for (let index = 0; index < length; index += 1) {
23
- const key = keys[index];
24
- if (booleanAttributesSet.has(key.toLowerCase()) || dispatchedAttributes.has(key)) setAttribute(target, key, properties[key], shouldDispatch);
25
- else updateProperty(target, key, properties[key], shouldDispatch);
26
- }
22
+ for (let index = 0; index < length; index += 1) setPropertyValue(target, keys[index], properties[keys[index]], shouldDispatch);
27
23
  }
28
24
  function setProperty(target, property, value, dispatch) {
29
- if (!isHTMLOrSVGElement(target) || typeof property !== "string") return;
30
- if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) setAttribute(target, property, value, dispatch !== false);
31
- else updateProperty(target, property, value, dispatch !== false);
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);
32
30
  }
33
31
  //#endregion
34
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,7 +12,8 @@ type StyleToggler = {
11
12
  */
12
13
  remove(): void;
13
14
  };
14
- type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
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 };
15
17
  /**
16
18
  * Get a style from an element
17
19
  * @param element Element to get the style from
@@ -19,7 +21,7 @@ type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
19
21
  * @param computed Get the computed style? _(defaults to `false`)_
20
22
  * @returns Style value
21
23
  */
22
- 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;
23
25
  /**
24
26
  * Get styles from an element
25
27
  * @param element Element to get the styles from
@@ -27,7 +29,7 @@ declare function getStyle(element: Element, property: keyof CSSStyleDeclaration,
27
29
  * @param computed Get the computed styles? _(defaults to `false`)_
28
30
  * @returns Style values
29
31
  */
30
- 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>;
31
33
  /**
32
34
  * Get the text direction of a node or element _(or document, if element is invalid)_
33
35
  * @param node Node or element to get the text direction from
@@ -45,7 +47,7 @@ declare function getTextDirection(): TextDirection;
45
47
  * @param property Style name
46
48
  * @param value Style value
47
49
  */
48
- declare function setStyle(element: Element, property: keyof CSSStyleDeclaration, value?: unknown): void;
50
+ declare function setStyle(element: Element, property: keyof CSSStyleValues, value?: unknown): void;
49
51
  /**
50
52
  * Set styles on an element
51
53
  * @param element Element to set the styles on
package/dist/style.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { isHTMLOrSVGElement } from "./internal/is.mjs";
2
2
  import { setElementValues, updateElementValue } from "./internal/element-value.mjs";
3
3
  import { getStyleValue } from "./internal/get-value.mjs";
4
+ import { getString } from "@oscarpalmer/atoms/string";
4
5
  //#region src/style.ts
5
6
  /**
6
7
  * Get a style from an element
@@ -34,7 +35,7 @@ function getTextDirection(node) {
34
35
  if (isHTMLOrSVGElement(node)) target = node;
35
36
  else target = node instanceof Node ? node.ownerDocument?.documentElement ?? document.documentElement : document.documentElement;
36
37
  let { direction } = target.style;
37
- if (direction === "") direction = getStyleValue(target, PROPETY_DIRECTION, true);
38
+ if (direction === "") direction = getStyleValue(target, PROPERTY_DIRECTION, true);
38
39
  return direction === DIRECTION_RTL ? DIRECTION_RTL : DIRECTION_LTR;
39
40
  }
40
41
  /**
@@ -44,7 +45,7 @@ function getTextDirection(node) {
44
45
  * @param value Style value
45
46
  */
46
47
  function setStyle(element, property, value) {
47
- setElementValues(element, property, value, null, updateStyleProperty);
48
+ setElementValues(element, property, value, null, updateStyleProperty, true);
48
49
  }
49
50
  /**
50
51
  * Set styles on an element
@@ -52,7 +53,7 @@ function setStyle(element, property, value) {
52
53
  * @param styles Styles to set
53
54
  */
54
55
  function setStyles(element, styles) {
55
- setElementValues(element, styles, null, null, updateStyleProperty);
56
+ setElementValues(element, styles, null, null, updateStyleProperty, true);
56
57
  }
57
58
  /**
58
59
  * Toggle styles for an element
@@ -89,13 +90,18 @@ function toggleStyles(element, styles) {
89
90
  }
90
91
  function updateStyleProperty(element, key, value) {
91
92
  updateElementValue(element, key, value, function(property, style) {
92
- this.style[property] = String(style);
93
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
94
+ else this.style[property] = getString(style);
93
95
  }, function(property) {
94
- this.style[property] = "";
96
+ if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
97
+ else this.style[property] = "";
98
+ if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
95
99
  }, false, false);
96
100
  }
101
+ const ATTRIBUTE_STYLE = "style";
97
102
  const DIRECTION_LTR = "ltr";
98
103
  const DIRECTION_RTL = "rtl";
99
- const PROPETY_DIRECTION = "direction";
104
+ const PROPERTY_DIRECTION = "direction";
105
+ const VARIABLE_PREFIX = "--";
100
106
  //#endregion
101
107
  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.42.0",
3
+ "version": "0.44.0",
4
4
  "description": "A collection of badass DOM utilities.",
5
5
  "keywords": [
6
6
  "dom",
@@ -86,12 +86,13 @@
86
86
  "test:leak": "npx vp test run --detect-async-leaks --coverage"
87
87
  },
88
88
  "dependencies": {
89
- "@oscarpalmer/atoms": "^0.172"
89
+ "@oscarpalmer/atoms": "^0.185"
90
90
  },
91
91
  "devDependencies": {
92
- "@types/node": "^25.5",
92
+ "@oxlint/plugins": "^1.62",
93
+ "@types/node": "^25.6",
93
94
  "@vitest/coverage-istanbul": "^4.1",
94
- "jsdom": "^29",
95
+ "jsdom": "^29.1",
95
96
  "tsdown": "^0.21",
96
97
  "typescript": "^5.9",
97
98
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
@@ -1,5 +1,6 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {PlainObject} from '@oscarpalmer/atoms/models';
3
+ import {getString} from '@oscarpalmer/atoms/string';
3
4
  import {kebabCase} from '@oscarpalmer/atoms/string/case';
4
5
  import type {Attribute} from '../models';
5
6
  import {updateElementValue} from './element-value';
@@ -65,7 +66,7 @@ function handleAttribute(
65
66
 
66
67
  if (isAttribute(first)) {
67
68
  name = first.name;
68
- value = String(first.value);
69
+ value = getString(first.value);
69
70
  } else if (typeof first === 'string' && typeof second === 'string') {
70
71
  name = first;
71
72
  value = second;
@@ -1,25 +1,39 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
+ import {getString} from '@oscarpalmer/atoms/string';
2
3
  import {kebabCase} from '@oscarpalmer/atoms/string/case';
3
4
  import {isHTMLOrSVGElement} from '../is';
4
5
  import {isAttribute} from './attribute';
5
6
 
6
7
  // #region Functions
7
8
 
9
+ function ignoreSetAttribute(element: Element, name: string): boolean {
10
+ if (element instanceof HTMLTextAreaElement && name === 'value') {
11
+ return true;
12
+ }
13
+
14
+ return false;
15
+ }
16
+
17
+ function normalizeKey(key: string, style?: boolean): string {
18
+ return style && key.startsWith(CSS_VARIABLE_PREFIX) ? key : kebabCase(key);
19
+ }
20
+
8
21
  export function setElementValue(
9
22
  element: Element,
10
23
  first: unknown,
11
24
  second: unknown,
12
25
  third: unknown,
13
26
  callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void,
27
+ style?: boolean,
14
28
  ): void {
15
29
  if (!isHTMLOrSVGElement(element)) {
16
30
  return;
17
31
  }
18
32
 
19
33
  if (typeof first === 'string') {
20
- setElementValues(element, first, second, third, callback);
34
+ setElementValues(element, first, second, third, callback, style);
21
35
  } else if (isAttribute(first)) {
22
- setElementValues(element, first.name, first.value, third, callback);
36
+ setElementValues(element, first.name, first.value, third, callback, style);
23
37
  }
24
38
  }
25
39
 
@@ -29,6 +43,7 @@ export function setElementValues(
29
43
  second: unknown,
30
44
  third: unknown,
31
45
  callback: (element: Element, key: string, value: unknown, dispatch: boolean) => void,
46
+ style?: boolean,
32
47
  ): void {
33
48
  if (!isHTMLOrSVGElement(element)) {
34
49
  return;
@@ -37,7 +52,7 @@ export function setElementValues(
37
52
  const dispatch = third !== false;
38
53
 
39
54
  if (typeof first === 'string') {
40
- callback(element, kebabCase(first), second, dispatch);
55
+ callback(element, normalizeKey(first, style), second, dispatch);
41
56
 
42
57
  return;
43
58
  }
@@ -55,7 +70,7 @@ export function setElementValues(
55
70
  const entry = entries[index];
56
71
 
57
72
  if (typeof entry === 'object' && typeof entry?.name === 'string') {
58
- callback(element, kebabCase(entry.name), entry.value, dispatch);
73
+ callback(element, normalizeKey(entry.name, style), entry.value, dispatch);
59
74
  }
60
75
  }
61
76
  }
@@ -71,9 +86,15 @@ export function updateElementValue(
71
86
  ): void {
72
87
  if (isBoolean ? value == null : isNullableOrWhitespace(value)) {
73
88
  remove.call(element, key);
74
- } else {
75
- set.call(element, key, json ? JSON.stringify(value) : String(value));
89
+ } else if (!ignoreSetAttribute(element, key)) {
90
+ set.call(element, key, json ? JSON.stringify(value) : getString(value));
76
91
  }
77
92
  }
78
93
 
79
94
  // #endregion Functions
95
+
96
+ // #region Variables
97
+
98
+ const CSS_VARIABLE_PREFIX = '--';
99
+
100
+ // #endregion
@@ -22,6 +22,10 @@ export function getStyleValue(
22
22
  property: string,
23
23
  computed: boolean,
24
24
  ): string | undefined {
25
+ if (property.startsWith(CSS_VARIABLE_PREFIX)) {
26
+ return (element as HTMLElement).style.getPropertyValue(property);
27
+ }
28
+
25
29
  const name = camelCase(property);
26
30
 
27
31
  return computed
@@ -35,4 +39,6 @@ export function getStyleValue(
35
39
 
36
40
  export const EXPRESSION_DATA_PREFIX = /^data-/i;
37
41
 
42
+ const CSS_VARIABLE_PREFIX = '--';
43
+
38
44
  // #endregion
@@ -24,7 +24,7 @@ export function updateProperty(
24
24
  const event = dispatch && elementEvents[element.tagName]?.[property];
25
25
 
26
26
  if (typeof event === 'string') {
27
- element.dispatchEvent(new Event(event, {bubbles: true}));
27
+ element.dispatchEvent(new Event(event, {bubbles: true, cancelable: true}));
28
28
  }
29
29
  }
30
30
 
@@ -46,13 +46,7 @@ export function setProperties<Target extends Element>(
46
46
  const {length} = keys;
47
47
 
48
48
  for (let index = 0; index < length; index += 1) {
49
- const key = keys[index];
50
-
51
- if (booleanAttributesSet.has(key.toLowerCase()) || dispatchedAttributes.has(key)) {
52
- setAttribute(target, key as never, (properties as PlainObject)[key], shouldDispatch);
53
- } else {
54
- updateProperty(target, key, (properties as PlainObject)[key], shouldDispatch);
55
- }
49
+ setPropertyValue(target, keys[index], (properties as PlainObject)[keys[index]], shouldDispatch);
56
50
  }
57
51
  }
58
52
 
@@ -88,14 +82,21 @@ export function setProperty<Target extends Element, Property extends keyof SetPr
88
82
  value: SetProperties<Target>[Property],
89
83
  dispatch?: boolean,
90
84
  ): void {
91
- if (!isHTMLOrSVGElement(target) || typeof property !== 'string') {
92
- return;
85
+ if (isHTMLOrSVGElement(target) && typeof property === 'string') {
86
+ setPropertyValue(target, property, value, dispatch !== false);
93
87
  }
88
+ }
94
89
 
90
+ function setPropertyValue(
91
+ element: Element,
92
+ property: string,
93
+ value: unknown,
94
+ dispatch: boolean,
95
+ ): void {
95
96
  if (booleanAttributesSet.has(property.toLowerCase()) || dispatchedAttributes.has(property)) {
96
- setAttribute(target, property as never, value, dispatch !== false);
97
+ setAttribute(element, property as never, value, dispatch);
97
98
  } else {
98
- updateProperty(target, property, value, dispatch !== false);
99
+ updateProperty(element, property, value, dispatch);
99
100
  }
100
101
  }
101
102
 
package/src/style.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import {getString} from '@oscarpalmer/atoms/string';
1
2
  import {setElementValues, updateElementValue} from './internal/element-value';
2
3
  import {getStyleValue} from './internal/get-value';
3
4
  import {isHTMLOrSVGElement} from './internal/is';
@@ -5,6 +6,8 @@ import type {TextDirection} from './models';
5
6
 
6
7
  // #region Types
7
8
 
9
+ type CSSStyleValues = Variables & CSSStyleDeclaration;
10
+
8
11
  export type StyleToggler = {
9
12
  /**
10
13
  * Set the provided styles on the element
@@ -16,7 +19,13 @@ export type StyleToggler = {
16
19
  remove(): void;
17
20
  };
18
21
 
19
- type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
22
+ type Styles = Partial<Record<keyof CSSStyleValues, unknown>>;
23
+
24
+ type Variables<
25
+ Value extends Record<string, string | undefined> = Record<string, string | undefined>,
26
+ > = {
27
+ [property in keyof Value as `--${string & property}`]?: string | undefined;
28
+ };
20
29
 
21
30
  // #endregion
22
31
 
@@ -31,7 +40,7 @@ type Styles = Partial<Record<keyof CSSStyleDeclaration, unknown>>;
31
40
  */
32
41
  export function getStyle(
33
42
  element: Element,
34
- property: keyof CSSStyleDeclaration,
43
+ property: keyof CSSStyleValues,
35
44
  computed?: boolean,
36
45
  ): string | undefined {
37
46
  if (isHTMLOrSVGElement(element) && typeof property === 'string') {
@@ -46,7 +55,7 @@ export function getStyle(
46
55
  * @param computed Get the computed styles? _(defaults to `false`)_
47
56
  * @returns Style values
48
57
  */
49
- export function getStyles<Property extends keyof CSSStyleDeclaration>(
58
+ export function getStyles<Property extends keyof CSSStyleValues>(
50
59
  element: Element,
51
60
  properties: Property[],
52
61
  computed?: boolean,
@@ -98,7 +107,7 @@ export function getTextDirection(node?: Element | Node): TextDirection {
98
107
  let {direction} = target.style;
99
108
 
100
109
  if (direction === '') {
101
- direction = getStyleValue(target, PROPETY_DIRECTION, true)!;
110
+ direction = getStyleValue(target, PROPERTY_DIRECTION, true)!;
102
111
  }
103
112
 
104
113
  return direction === DIRECTION_RTL ? DIRECTION_RTL : DIRECTION_LTR;
@@ -110,12 +119,8 @@ export function getTextDirection(node?: Element | Node): TextDirection {
110
119
  * @param property Style name
111
120
  * @param value Style value
112
121
  */
113
- export function setStyle(
114
- element: Element,
115
- property: keyof CSSStyleDeclaration,
116
- value?: unknown,
117
- ): void {
118
- setElementValues(element, property as string, value, null, updateStyleProperty);
122
+ export function setStyle(element: Element, property: keyof CSSStyleValues, value?: unknown): void {
123
+ setElementValues(element, property as string, value, null, updateStyleProperty, true);
119
124
  }
120
125
 
121
126
  /**
@@ -124,7 +129,7 @@ export function setStyle(
124
129
  * @param styles Styles to set
125
130
  */
126
131
  export function setStyles(element: Element, styles: Styles): void {
127
- setElementValues(element, styles as never, null, null, updateStyleProperty);
132
+ setElementValues(element, styles as never, null, null, updateStyleProperty, true);
128
133
  }
129
134
 
130
135
  /**
@@ -182,10 +187,22 @@ function updateStyleProperty(element: Element, key: string, value: unknown): voi
182
187
  key,
183
188
  value,
184
189
  function (this: Element, property: string, style: unknown) {
185
- (this as HTMLElement).style[property as never] = String(style);
190
+ if (property.startsWith(VARIABLE_PREFIX)) {
191
+ (this as HTMLElement).style.setProperty(property, getString(style));
192
+ } else {
193
+ (this as HTMLElement).style[property as never] = getString(style);
194
+ }
186
195
  },
187
196
  function (this: Element, property: string) {
188
- (this as HTMLElement).style[property as never] = '';
197
+ if (property.startsWith(VARIABLE_PREFIX)) {
198
+ (this as HTMLElement).style.removeProperty(property);
199
+ } else {
200
+ (this as HTMLElement).style[property as never] = '';
201
+ }
202
+
203
+ if ((this as HTMLElement).getAttribute(ATTRIBUTE_STYLE) === '') {
204
+ (this as HTMLElement).removeAttribute(ATTRIBUTE_STYLE);
205
+ }
189
206
  },
190
207
  false,
191
208
  false,
@@ -196,10 +213,14 @@ function updateStyleProperty(element: Element, key: string, value: unknown): voi
196
213
 
197
214
  // #region Variables
198
215
 
216
+ const ATTRIBUTE_STYLE = 'style';
217
+
199
218
  const DIRECTION_LTR = 'ltr';
200
219
 
201
220
  const DIRECTION_RTL = 'rtl';
202
221
 
203
- const PROPETY_DIRECTION = 'direction';
222
+ const PROPERTY_DIRECTION = 'direction';
223
+
224
+ const VARIABLE_PREFIX = '--';
204
225
 
205
226
  // #endregion