@oscarpalmer/toretto 0.48.0 → 0.49.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/index.mjs CHANGED
@@ -627,7 +627,10 @@ function getAria(element, value) {
627
627
  return arias;
628
628
  }
629
629
  function getAriaValue(element, attribute) {
630
- return element.getAttribute(getName$1(attribute)) ?? void 0;
630
+ const name = getName$1(attribute);
631
+ const value = element.getAttribute(name) ?? void 0;
632
+ if (ariaBooleanAttributesSet.has(name) && typeof value === "string" && EXPRESSION_BOOLEAN.test(value)) return value.toLowerCase() === "true";
633
+ return value;
631
634
  }
632
635
  function getName$1(value) {
633
636
  return EXPRESSION_ARIA_PREFIX.test(value) ? value : `${ATTRIBUTE_ARIA_PREFIX}${value}`;
@@ -656,10 +659,39 @@ function setRole(element, role) {
656
659
  else element.removeAttribute("role");
657
660
  }
658
661
  function updateAriaAttribute(element, key, value) {
659
- updateElementValue(element, getName$1(key), value, element.setAttribute, element.removeAttribute, false, false);
662
+ const name = getName$1(key);
663
+ let actual = value;
664
+ if (ariaBooleanAttributesSet.has(name) && typeof value === "string" && EXPRESSION_BOOLEAN.test(value)) actual = value.toLowerCase() === "true";
665
+ updateElementValue(element, name, actual, element.setAttribute, element.removeAttribute, false, false);
660
666
  }
661
667
  const ATTRIBUTE_ARIA_PREFIX = "aria-";
662
668
  const EXPRESSION_ARIA_PREFIX = /^aria-/i;
669
+ const EXPRESSION_BOOLEAN = /^(true|false)$/i;
670
+ /**
671
+ * List of _ARIA_ attributes that can be treated as boolean values
672
+ */
673
+ const ariaBooleanAttributes = Object.freeze([
674
+ "aria-atomic",
675
+ "aria-busy",
676
+ "aria-checked",
677
+ "aria-current",
678
+ "aria-disabled",
679
+ "aria-expanded",
680
+ "aria-haspopup",
681
+ "aria-hidden",
682
+ "aria-invalid",
683
+ "aria-modal",
684
+ "aria-multiline",
685
+ "aria-multiselectable",
686
+ "aria-pressed",
687
+ "aria-readonly",
688
+ "aria-required",
689
+ "aria-selected"
690
+ ]);
691
+ /**
692
+ * Set of _ARIA_ attributes that can be treated as boolean values
693
+ */
694
+ const ariaBooleanAttributesSet = new Set(ariaBooleanAttributes);
663
695
  //#endregion
664
696
  //#region src/internal/get-value.ts
665
697
  function getBoolean(value, defaultValue) {
@@ -722,6 +754,36 @@ function isInvalidBooleanAttribute(first, second) {
722
754
  return _isInvalidBooleanAttribute(first, second, true);
723
755
  }
724
756
  //#endregion
757
+ //#region src/data.ts
758
+ function getData(element, keys, parseValues) {
759
+ if (!(element instanceof Element)) return;
760
+ const noParse = parseValues === false;
761
+ if (typeof keys === "string") {
762
+ const value = element.dataset[camelCase(keys)];
763
+ if (value === void 0) return;
764
+ return noParse ? value : parse(value);
765
+ }
766
+ const { length } = keys;
767
+ const data = {};
768
+ for (let index = 0; index < length; index += 1) {
769
+ const key = keys[index];
770
+ const value = element.dataset[camelCase(key)];
771
+ if (value == null) data[key] = void 0;
772
+ else data[key] = noParse ? value : parse(value);
773
+ }
774
+ return data;
775
+ }
776
+ function getName(original) {
777
+ return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original.replace(EXPRESSION_DATA_PREFIX, ""))}`;
778
+ }
779
+ function setData(element, first, second) {
780
+ setElementValues(element, first, second, null, updateDataAttribute);
781
+ }
782
+ function updateDataAttribute(element, key, value) {
783
+ updateElementValue(element, getName(key), value, element.setAttribute, element.removeAttribute, false, true);
784
+ }
785
+ const ATTRIBUTE_DATA_PREFIX = "data-";
786
+ //#endregion
725
787
  //#region src/property/get.property.ts
726
788
  /**
727
789
  * Get the values of one or more properties on an element
@@ -786,28 +848,28 @@ function setPropertyValue(element, property, value, dispatch) {
786
848
  * Get a style from an element
787
849
  *
788
850
  * @param element Element to get the style from
789
- * @param property Style name
851
+ * @param name Style name
790
852
  * @param computed Get the computed style? _(defaults to `false`)_
791
853
  * @returns Style value
792
854
  */
793
- function getStyle(element, property, computed) {
794
- if (element instanceof Element && typeof property === "string") return getStyleValue(element, property, computed === true);
855
+ function getStyle(element, name, computed) {
856
+ if (element instanceof Element && typeof name === "string") return getStyleValue(element, name, computed === true);
795
857
  }
796
858
  /**
797
859
  * Get styles from an element
798
860
  *
799
861
  * @param element Element to get the styles from
800
- * @param properties Styles to get
862
+ * @param names Styles to get
801
863
  * @param computed Get the computed styles? _(defaults to `false`)_
802
864
  * @returns Style values
803
865
  */
804
- function getStyles(element, properties, computed) {
866
+ function getStyles(element, names, computed) {
805
867
  const styles = {};
806
- if (!(element instanceof Element && Array.isArray(properties))) return styles;
807
- const { length } = properties;
868
+ if (!(element instanceof Element && Array.isArray(names))) return styles;
869
+ const { length } = names;
808
870
  for (let index = 0; index < length; index += 1) {
809
- const property = properties[index];
810
- if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
871
+ const name = names[index];
872
+ if (typeof name === "string") styles[name] = getStyleValue(element, name, computed === true);
811
873
  }
812
874
  return styles;
813
875
  }
@@ -823,11 +885,11 @@ function getTextDirection(node) {
823
885
  * Set a style on an element
824
886
  *
825
887
  * @param element Element to set the style on
826
- * @param property Style name
888
+ * @param name Style name
827
889
  * @param value Style value
828
890
  */
829
- function setStyle(element, property, value) {
830
- setElementValues(element, property, value, null, updateStyleProperty, true);
891
+ function setStyle(element, name, value) {
892
+ setElementValues(element, name, value, null, updateStyleProperty, true);
831
893
  }
832
894
  /**
833
895
  * Set styles on an element
@@ -873,12 +935,12 @@ function toggleStyles(element, styles) {
873
935
  };
874
936
  }
875
937
  function updateStyleProperty(element, key, value) {
876
- updateElementValue(element, key, value, function(property, style) {
877
- if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
878
- else this.style[property] = getString(style);
879
- }, function(property) {
880
- if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
881
- else this.style[property] = "";
938
+ updateElementValue(element, key, value, function(name, style) {
939
+ if (name.startsWith(VARIABLE_PREFIX)) this.style.setProperty(name, getString(style));
940
+ else this.style[name] = getString(style);
941
+ }, function(name) {
942
+ if (name.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(name);
943
+ else this.style[name] = "";
882
944
  if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
883
945
  }, false, false);
884
946
  }
@@ -889,45 +951,28 @@ const PROPERTY_DIRECTION = "direction";
889
951
  const VARIABLE_PREFIX = "--";
890
952
  //#endregion
891
953
  //#region src/create.ts
892
- function createElement(tag, properties, attributes, styles) {
954
+ function createElement(tag, values) {
893
955
  if (typeof tag !== "string") throw new TypeError(MESSAGE);
894
956
  const element = document.createElement(tag);
895
- if (properties != null) setProperties(element, properties);
896
- if (attributes != null) setAttributes(element, attributes);
897
- if (styles != null) setStyles(element, styles);
957
+ const { aria, attribute, data, property, style } = getElementValues(values);
958
+ setAria(element, aria ?? {});
959
+ setAttributes(element, attribute ?? {});
960
+ setData(element, data ?? {});
961
+ setProperties(element, property ?? {});
962
+ setStyles(element, style ?? {});
898
963
  return element;
899
964
  }
900
- const MESSAGE = "Tag name must be a string";
901
- //#endregion
902
- //#region src/data.ts
903
- function getData(element, keys, parseValues) {
904
- if (!(element instanceof Element)) return;
905
- const noParse = parseValues === false;
906
- if (typeof keys === "string") {
907
- const value = element.dataset[camelCase(keys)];
908
- if (value === void 0) return;
909
- return noParse ? value : parse(value);
910
- }
911
- const { length } = keys;
912
- const data = {};
913
- for (let index = 0; index < length; index += 1) {
914
- const key = keys[index];
915
- const value = element.dataset[camelCase(key)];
916
- if (value == null) data[key] = void 0;
917
- else data[key] = noParse ? value : parse(value);
918
- }
919
- return data;
920
- }
921
- function getName(original) {
922
- return `${ATTRIBUTE_DATA_PREFIX}${kebabCase(original.replace(EXPRESSION_DATA_PREFIX, ""))}`;
923
- }
924
- function setData(element, first, second) {
925
- setElementValues(element, first, second, null, updateDataAttribute);
926
- }
927
- function updateDataAttribute(element, key, value) {
928
- updateElementValue(element, getName(key), value, element.setAttribute, element.removeAttribute, false, true);
965
+ function getElementValues(input) {
966
+ if (!isPlainObject(input)) return {};
967
+ return {
968
+ aria: isPlainObject(input.aria) ? input.aria : void 0,
969
+ attribute: isPlainObject(input.attribute) ? input.attribute : void 0,
970
+ data: isPlainObject(input.data) ? input.data : void 0,
971
+ property: isPlainObject(input.property) ? input.property : void 0,
972
+ style: isPlainObject(input.style) ? input.style : void 0
973
+ };
929
974
  }
930
- const ATTRIBUTE_DATA_PREFIX = "data-";
975
+ const MESSAGE = "Tag name must be a string";
931
976
  //#endregion
932
977
  //#region node_modules/@oscarpalmer/atoms/dist/internal/function/misc.mjs
933
978
  /**
@@ -1673,4 +1718,4 @@ const CHILD_NODE_TYPES = /* @__PURE__ */ new Set([
1673
1718
  Node.DOCUMENT_TYPE_NODE
1674
1719
  ]);
1675
1720
  //#endregion
1676
- export { findElement as $, findElement, findElements as $$, findElements, booleanAttributes, createElement, dispatch, findAncestor, findRelatives, getAria, getAttribute, getAttributes, getData, getDistance, getElementFromPosition, getElementUnderPointer, getFocusable, getPosition, getProperties, getProperty, getRole, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEventPosition, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInputElement, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setAria, setAttribute, setAttributes, setData, setProperties, setProperty, setRole, setStyle, setStyles, supportsTouch, toggleStyles };
1721
+ export { findElement as $, findElement, findElements as $$, findElements, ariaBooleanAttributes, ariaBooleanAttributesSet, booleanAttributes, createElement, dispatch, findAncestor, findRelatives, getAria, getAttribute, getAttributes, getData, getDistance, getElementFromPosition, getElementUnderPointer, getFocusable, getPosition, getProperties, getProperty, getRole, getStyle, getStyles, getTabbable, getTextDirection, html, isBadAttribute, isBooleanAttribute, isChildNode, isEventPosition, isEventTarget, isFocusable, isHTMLOrSVGElement, isInDocument, isInputElement, isInvalidBooleanAttribute, isTabbable, off, on, sanitize, setAria, setAttribute, setAttributes, setData, setProperties, setProperty, setRole, setStyle, setStyles, supportsTouch, toggleStyles };
package/dist/models.d.mts CHANGED
@@ -1,4 +1,12 @@
1
1
  //#region src/models.d.ts
2
+ /**
3
+ * Any _ARIA_ attribute for an element _(both prefixed and unprefixed)_
4
+ */
5
+ type AnyAriaAttribute = AriaAttribute | AriaAttributeUnprefixed;
6
+ /**
7
+ * Any _ARIA_ attribute for an element that can be set to a boolean value _(both prefixed and unprefixed)_
8
+ */
9
+ type AnyAriaBooleanAttribute = AriaBooleanAttribute | AriaBooleanAttributeUnprefixed;
2
10
  /**
3
11
  * _ARIA_ attribute for an element
4
12
  *
@@ -10,8 +18,16 @@ type AriaAttribute = keyof AriaAttributes;
10
18
  *
11
19
  * _(https://www.w3.org/TR/wai-aria-1.3/#aria-attributes)_
12
20
  */
13
- type AriaAttributeUnprefixed = keyof { [Key in AriaAttribute as Key extends `aria-${infer Name}` ? Name : never]: string | null; };
14
- type AriaAttributes = { [Key in keyof ARIAMixin as NormalizedName<Key>]: string | null; };
21
+ type AriaAttributeUnprefixed = keyof { [Key in AriaAttribute as Key extends `aria-${infer Name}` ? Name : never]: unknown; };
22
+ type AriaAttributes = { [Key in keyof ARIAMixin as NormalizedName<Key>]: unknown; };
23
+ /**
24
+ * _ARIA_ attribute for an element that can be set to a boolean value
25
+ */
26
+ type AriaBooleanAttribute = 'aria-atomic' | 'aria-busy' | 'aria-checked' | 'aria-current' | 'aria-disabled' | 'aria-expanded' | 'aria-haspopup' | 'aria-hidden' | 'aria-invalid' | 'aria-modal' | 'aria-multiline' | 'aria-multiselectable' | 'aria-pressed' | 'aria-readonly' | 'aria-required' | 'aria-selected';
27
+ /**
28
+ * _ARIA_ attribute for an element that can be set to a boolean value, without the `aria-` prefix
29
+ */
30
+ type AriaBooleanAttributeUnprefixed = keyof { [Key in AriaBooleanAttribute as Key extends `aria-${infer Name}` ? Name : never]: string | null; };
15
31
  type NormalizedName<Key extends string> = Key extends `aria${infer Name}` ? Name extends `${infer Part}Element` ? `aria-${Lowercase<Part>}` : Name extends `${infer Part}Elements` ? `aria-${Lowercase<Part>}` : `aria-${Lowercase<Name>}` : never;
16
32
  /**
17
33
  * _ARIA_ role for an element
@@ -26,6 +42,18 @@ type Attribute = {
26
42
  name: string;
27
43
  value: unknown;
28
44
  };
45
+ /**
46
+ * CSS styles for an element
47
+ */
48
+ type CSSStyles = Record<keyof CSSStyleDeclaration, unknown>;
49
+ /**
50
+ * CSSS values for an element _(both styles and variables)_
51
+ */
52
+ type CSSValues = CSSVariables & CSSStyles;
53
+ /**
54
+ * CSS variables for an element
55
+ */
56
+ type CSSVariables<Value extends Record<string, unknown> = Record<string, unknown>> = { [Property in keyof Value as `--${string & Property}`]?: unknown; };
29
57
  /**
30
58
  * Event listener for custom events
31
59
  */
@@ -43,4 +71,4 @@ type Selector = string | Node | Node[] | NodeList;
43
71
  */
44
72
  type TextDirection = 'ltr' | 'rtl';
45
73
  //#endregion
46
- export { AriaAttribute, AriaAttributeUnprefixed, AriaRole, Attribute, CustomEventListener, RemovableEventListener, Selector, TextDirection };
74
+ export { AnyAriaAttribute, AnyAriaBooleanAttribute, AriaAttribute, AriaAttributeUnprefixed, AriaBooleanAttribute, AriaBooleanAttributeUnprefixed, AriaRole, Attribute, CSSStyles, CSSValues, CSSVariables, CustomEventListener, RemovableEventListener, Selector, TextDirection };
package/dist/style.d.mts CHANGED
@@ -1,6 +1,5 @@
1
- import { TextDirection } from "./models.mjs";
1
+ import { CSSValues, TextDirection } from "./models.mjs";
2
2
  //#region src/style.d.ts
3
- type CSSStyleValues = Variables & CSSStyleDeclaration;
4
3
  type StyleToggler = {
5
4
  /**
6
5
  * Set the provided styles on the element
@@ -11,26 +10,24 @@ type StyleToggler = {
11
10
  */
12
11
  remove(): void;
13
12
  };
14
- type Styles = Partial<Record<keyof CSSStyleValues, unknown>>;
15
- type Variables<Value extends Record<string, string | undefined> = Record<string, string | undefined>> = { [property in keyof Value as `--${string & property}`]?: string | undefined; };
16
13
  /**
17
14
  * Get a style from an element
18
15
  *
19
16
  * @param element Element to get the style from
20
- * @param property Style name
17
+ * @param name Style name
21
18
  * @param computed Get the computed style? _(defaults to `false`)_
22
19
  * @returns Style value
23
20
  */
24
- declare function getStyle(element: Element, property: keyof CSSStyleValues, computed?: boolean): string | undefined;
21
+ declare function getStyle(element: Element, name: keyof CSSValues, computed?: boolean): string | undefined;
25
22
  /**
26
23
  * Get styles from an element
27
24
  *
28
25
  * @param element Element to get the styles from
29
- * @param properties Styles to get
26
+ * @param names Styles to get
30
27
  * @param computed Get the computed styles? _(defaults to `false`)_
31
28
  * @returns Style values
32
29
  */
33
- declare function getStyles<Property extends keyof CSSStyleValues>(element: Element, properties: Property[], computed?: boolean): Record<Property, string | undefined>;
30
+ declare function getStyles<Name extends keyof CSSValues>(element: Element, names: Name[], computed?: boolean): Record<Name, string | undefined>;
34
31
  /**
35
32
  * Get the text direction of a node or element _(or document, if element is invalid)_
36
33
  *
@@ -48,17 +45,17 @@ declare function getTextDirection(): TextDirection;
48
45
  * Set a style on an element
49
46
  *
50
47
  * @param element Element to set the style on
51
- * @param property Style name
48
+ * @param name Style name
52
49
  * @param value Style value
53
50
  */
54
- declare function setStyle(element: Element, property: keyof CSSStyleValues, value?: unknown): void;
51
+ declare function setStyle(element: Element, name: keyof CSSValues, value?: unknown): void;
55
52
  /**
56
53
  * Set styles on an element
57
54
  *
58
55
  * @param element Element to set the styles on
59
56
  * @param styles Styles to set
60
57
  */
61
- declare function setStyles(element: Element, styles: Styles): void;
58
+ declare function setStyles(element: Element, styles: Partial<CSSValues>): void;
62
59
  /**
63
60
  * Toggle styles for an element
64
61
  *
@@ -66,6 +63,6 @@ declare function setStyles(element: Element, styles: Styles): void;
66
63
  * @param styles Styles to be set or removed
67
64
  * @returns Style toggler
68
65
  */
69
- declare function toggleStyles(element: Element, styles: Styles): StyleToggler;
66
+ declare function toggleStyles(element: Element, styles: Partial<CSSValues>): StyleToggler;
70
67
  //#endregion
71
68
  export { StyleToggler, getStyle, getStyles, getTextDirection, setStyle, setStyles, toggleStyles };
package/dist/style.mjs CHANGED
@@ -6,28 +6,28 @@ import { getString } from "@oscarpalmer/atoms/string";
6
6
  * Get a style from an element
7
7
  *
8
8
  * @param element Element to get the style from
9
- * @param property Style name
9
+ * @param name Style name
10
10
  * @param computed Get the computed style? _(defaults to `false`)_
11
11
  * @returns Style value
12
12
  */
13
- function getStyle(element, property, computed) {
14
- if (element instanceof Element && typeof property === "string") return getStyleValue(element, property, computed === true);
13
+ function getStyle(element, name, computed) {
14
+ if (element instanceof Element && typeof name === "string") return getStyleValue(element, name, computed === true);
15
15
  }
16
16
  /**
17
17
  * Get styles from an element
18
18
  *
19
19
  * @param element Element to get the styles from
20
- * @param properties Styles to get
20
+ * @param names Styles to get
21
21
  * @param computed Get the computed styles? _(defaults to `false`)_
22
22
  * @returns Style values
23
23
  */
24
- function getStyles(element, properties, computed) {
24
+ function getStyles(element, names, computed) {
25
25
  const styles = {};
26
- if (!(element instanceof Element && Array.isArray(properties))) return styles;
27
- const { length } = properties;
26
+ if (!(element instanceof Element && Array.isArray(names))) return styles;
27
+ const { length } = names;
28
28
  for (let index = 0; index < length; index += 1) {
29
- const property = properties[index];
30
- if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
29
+ const name = names[index];
30
+ if (typeof name === "string") styles[name] = getStyleValue(element, name, computed === true);
31
31
  }
32
32
  return styles;
33
33
  }
@@ -43,11 +43,11 @@ function getTextDirection(node) {
43
43
  * Set a style on an element
44
44
  *
45
45
  * @param element Element to set the style on
46
- * @param property Style name
46
+ * @param name Style name
47
47
  * @param value Style value
48
48
  */
49
- function setStyle(element, property, value) {
50
- setElementValues(element, property, value, null, updateStyleProperty, true);
49
+ function setStyle(element, name, value) {
50
+ setElementValues(element, name, value, null, updateStyleProperty, true);
51
51
  }
52
52
  /**
53
53
  * Set styles on an element
@@ -93,12 +93,12 @@ function toggleStyles(element, styles) {
93
93
  };
94
94
  }
95
95
  function updateStyleProperty(element, key, value) {
96
- updateElementValue(element, key, value, function(property, style) {
97
- if (property.startsWith(VARIABLE_PREFIX)) this.style.setProperty(property, getString(style));
98
- else this.style[property] = getString(style);
99
- }, function(property) {
100
- if (property.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(property);
101
- else this.style[property] = "";
96
+ updateElementValue(element, key, value, function(name, style) {
97
+ if (name.startsWith(VARIABLE_PREFIX)) this.style.setProperty(name, getString(style));
98
+ else this.style[name] = getString(style);
99
+ }, function(name) {
100
+ if (name.startsWith(VARIABLE_PREFIX)) this.style.removeProperty(name);
101
+ else this.style[name] = "";
102
102
  if (this.getAttribute(ATTRIBUTE_STYLE) === "") this.removeAttribute(ATTRIBUTE_STYLE);
103
103
  }, false, false);
104
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/toretto",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "A collection of badass DOM utilities.",
5
5
  "keywords": [
6
6
  "dom",
@@ -93,10 +93,10 @@
93
93
  "@oscarpalmer/atoms": "^0.188.1"
94
94
  },
95
95
  "devDependencies": {
96
- "@oxlint/plugins": "^1.74",
96
+ "@oxlint/plugins": "^1.76",
97
97
  "@types/node": "^26.1",
98
98
  "@vitest/coverage-istanbul": "^4.1",
99
- "jsdom": "^29.1",
99
+ "jsdom": "^30",
100
100
  "tsdown": "^0.22",
101
101
  "typescript": "^6",
102
102
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
package/src/aria.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import {setElementValues, updateElementValue} from './internal/element-value';
2
- import type {AriaAttribute, AriaAttributeUnprefixed, AriaRole} from './models';
2
+ import type {AnyAriaAttribute, AnyAriaBooleanAttribute, AriaRole} from './models';
3
3
 
4
4
  // #region Types
5
5
 
6
- type AnyAriaAttribute = AriaAttribute | AriaAttributeUnprefixed;
7
-
8
- type AriaAttributeItem = {
9
- name: AnyAriaAttribute;
10
- value?: string;
6
+ type AriaAttributeItem<Name extends AnyAriaAttribute = AnyAriaAttribute> = {
7
+ name: Name;
8
+ value?: Name extends AnyAriaBooleanAttribute ? boolean | string : string;
11
9
  };
12
10
 
13
11
  // #endregion
@@ -18,10 +16,22 @@ type AriaAttributeItem = {
18
16
  * Get the value of a specific _ARIA_ attribute from an element
19
17
  *
20
18
  * @param element Element to get _ARIA_ attribute from
21
- * @param name _ARIA_ name
19
+ * @param name _ARIA_ attribute name
20
+ * @returns _ARIA_ value _(or `undefined`)_
21
+ */
22
+ export function getAria(
23
+ element: Element,
24
+ name: AnyAriaBooleanAttribute,
25
+ ): boolean | string | undefined;
26
+
27
+ /**
28
+ * Get the value of a specific _ARIA_ attribute from an element
29
+ *
30
+ * @param element Element to get _ARIA_ attribute from
31
+ * @param name _ARIA_ attribute name
22
32
  * @returns _ARIA_ value _(or `undefined`)_
23
33
  */
24
- export function getAria(element: Element, attribute: AnyAriaAttribute): string | undefined;
34
+ export function getAria(element: Element, name: AnyAriaAttribute): string | undefined;
25
35
 
26
36
  /**
27
37
  * Get specific _ARIA_ attributes from an element
@@ -32,8 +42,12 @@ export function getAria(element: Element, attribute: AnyAriaAttribute): string |
32
42
  */
33
43
  export function getAria<Attribute extends AnyAriaAttribute>(
34
44
  element: Element,
35
- attributes: Attribute[],
36
- ): Record<Attribute, string | undefined>;
45
+ names: Attribute[],
46
+ ): {
47
+ [Key in Attribute as Key extends `aria-${infer Name}`
48
+ ? Name
49
+ : Key]: Key extends AnyAriaBooleanAttribute ? boolean | string | undefined : string | undefined;
50
+ };
37
51
 
38
52
  export function getAria(element: Element, value: string | string[]): unknown {
39
53
  if (!(element instanceof Element)) {
@@ -44,7 +58,7 @@ export function getAria(element: Element, value: string | string[]): unknown {
44
58
  return typeof value === 'string' ? getAriaValue(element, value) : undefined;
45
59
  }
46
60
 
47
- const arias = {} as Record<string, string | undefined>;
61
+ const arias = {} as Record<string, unknown>;
48
62
 
49
63
  const {length} = value;
50
64
 
@@ -59,8 +73,20 @@ export function getAria(element: Element, value: string | string[]): unknown {
59
73
  return arias;
60
74
  }
61
75
 
62
- function getAriaValue(element: Element, attribute: string): string | undefined {
63
- return element.getAttribute(getName(attribute)) ?? undefined;
76
+ function getAriaValue(element: Element, attribute: string): unknown {
77
+ const name = getName(attribute);
78
+
79
+ const value = element.getAttribute(name) ?? undefined;
80
+
81
+ if (
82
+ ariaBooleanAttributesSet.has(name as never) &&
83
+ typeof value === 'string' &&
84
+ EXPRESSION_BOOLEAN.test(value)
85
+ ) {
86
+ return value.toLowerCase() === 'true';
87
+ }
88
+
89
+ return value;
64
90
  }
65
91
 
66
92
  function getName(value: string): string {
@@ -88,7 +114,22 @@ export function getRole(element: Element): string | undefined {
88
114
  * @param attribute _ARIA_ attribute to set
89
115
  * @param value _ARIA_ attribute value
90
116
  */
91
- export function setAria(element: Element, attribute: AnyAriaAttribute, value?: unknown): void;
117
+ export function setAria(
118
+ element: Element,
119
+ attribute: AnyAriaBooleanAttribute,
120
+ value?: boolean | string,
121
+ ): void;
122
+
123
+ /**
124
+ * Set an _ARIA_ attribute on an element
125
+ *
126
+ * _(Or remove it, if value is `null` or `undefined`)_
127
+ *
128
+ * @param element Element for _ARIA_ attribute
129
+ * @param attribute _ARIA_ attribute to set
130
+ * @param value _ARIA_ attribute value
131
+ */
132
+ export function setAria(element: Element, attribute: AnyAriaAttribute, value?: string): void;
92
133
 
93
134
  /**
94
135
  * Set one or more _ARIA_ attributes on an element
@@ -110,14 +151,12 @@ export function setAria(element: Element, attributes: AriaAttributeItem[]): void
110
151
  */
111
152
  export function setAria(
112
153
  element: Element,
113
- attributes: Partial<Record<AnyAriaAttribute, unknown>>,
154
+ attributes: {
155
+ [Key in AnyAriaAttribute]?: Key extends AnyAriaBooleanAttribute ? boolean | string : string;
156
+ },
114
157
  ): void;
115
158
 
116
- export function setAria(
117
- element: Element,
118
- first: AnyAriaAttribute | AriaAttributeItem[] | Partial<Record<AnyAriaAttribute, unknown>>,
119
- second?: unknown,
120
- ): void {
159
+ export function setAria(element: Element, first: unknown, second?: unknown): void {
121
160
  setElementValues(element, first, second, null, updateAriaAttribute);
122
161
  }
123
162
 
@@ -140,10 +179,22 @@ export function setRole(element: Element, role?: AriaRole): void {
140
179
  }
141
180
 
142
181
  function updateAriaAttribute(element: Element, key: string, value: unknown): void {
182
+ const name = getName(key);
183
+
184
+ let actual = value;
185
+
186
+ if (
187
+ ariaBooleanAttributesSet.has(name as never) &&
188
+ typeof value === 'string' &&
189
+ EXPRESSION_BOOLEAN.test(value)
190
+ ) {
191
+ actual = value.toLowerCase() === 'true';
192
+ }
193
+
143
194
  updateElementValue(
144
195
  element,
145
- getName(key),
146
- value,
196
+ name,
197
+ actual,
147
198
  // Using `.call` in `updateElementValue`
148
199
  // oxlint-disable-next-line typescript/unbound-method
149
200
  element.setAttribute,
@@ -163,4 +214,33 @@ const ATTRIBUTE_ARIA_PREFIX = 'aria-';
163
214
 
164
215
  const EXPRESSION_ARIA_PREFIX = /^aria-/i;
165
216
 
217
+ const EXPRESSION_BOOLEAN = /^(true|false)$/i;
218
+
219
+ /**
220
+ * List of _ARIA_ attributes that can be treated as boolean values
221
+ */
222
+ export const ariaBooleanAttributes: readonly AnyAriaBooleanAttribute[] = Object.freeze([
223
+ 'aria-atomic',
224
+ 'aria-busy',
225
+ 'aria-checked',
226
+ 'aria-current',
227
+ 'aria-disabled',
228
+ 'aria-expanded',
229
+ 'aria-haspopup',
230
+ 'aria-hidden',
231
+ 'aria-invalid',
232
+ 'aria-modal',
233
+ 'aria-multiline',
234
+ 'aria-multiselectable',
235
+ 'aria-pressed',
236
+ 'aria-readonly',
237
+ 'aria-required',
238
+ 'aria-selected',
239
+ ]);
240
+
241
+ /**
242
+ * Set of _ARIA_ attributes that can be treated as boolean values
243
+ */
244
+ export const ariaBooleanAttributesSet = new Set(ariaBooleanAttributes);
245
+
166
246
  // #endregion