UNPKG

@markuplint/ml-spec

Version:

Types and schema that specs of the Markup languages for markuplint

104 lines (103 loc) 3.61 kB
import { ariaSpecs as _ariaSpecs } from '../specs/aria-specs.js'; import { getComputedRole } from './get-computed-role.js'; export function getComputedAriaProps(specs, // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types el, version) { const ariaSpecs = _ariaSpecs(specs, version); const { role } = getComputedRole(specs, el, version); if (!role) { return {}; } const props = {}; for (const ownedProp of role.ownedProperties) { const spec = ariaSpecs.props.find(propSpec => propSpec.name === ownedProp.name); if (!spec) { continue; } if (el.hasAttribute(spec.name)) { const attrValue = el.getAttribute(spec.name); if (attrValue && isValidAriaValue(spec, role.name, attrValue, spec.enum)) { props[ownedProp.name] = { name: ownedProp.name, value: attrValue, deprecated: !!spec.deprecated, required: !!ownedProp.required, from: 'aria-attr', }; continue; } } const equivalentHtmlAttr = spec.equivalentHtmlAttrs?.[0]; if (equivalentHtmlAttr && el.hasAttribute(equivalentHtmlAttr.htmlAttrName)) { const value = equivalentHtmlAttr.value === null ? el.getAttribute(equivalentHtmlAttr.htmlAttrName) : equivalentHtmlAttr.value; if (value != null) { props[ownedProp.name] = { name: ownedProp.name, value, deprecated: !!spec.deprecated, required: !!ownedProp.required, from: 'html-attr', }; continue; } } let defaultValue = spec.defaultValue; /** * @see https://www.w3.org/TR/html-aria/#el-h1-h6 */ if (ownedProp.name === 'aria-level' && /^H[1-6]$/.test(el.nodeName)) { defaultValue = el.nodeName.replace('H', ''); } props[ownedProp.name] = { name: ownedProp.name, value: defaultValue, deprecated: !!spec.deprecated, required: !!ownedProp.required, from: 'default', }; } return props; } function isValidAriaValue(spec, role, value, enumList) { let type = spec.value; if (spec.conditionalValue) { for (const cond of spec.conditionalValue) { if (cond.role.includes(role)) { type = cond.value; } } } value = (value ?? '').trim(); switch (type) { case 'string': { return true; } case 'ID reference': case 'ID reference list': case 'URI': { return !!value; } case 'integer': case 'number': { return !Number.isNaN(Number.parseFloat(value)); } case 'token': case 'token list': { if (enumList.length === 0) { throw new Error('Need an enum list in token and token list types'); } return enumList.includes(value.toLowerCase()); } case 'tristate': { return ['true', 'false', 'mixed'].includes(value.toLowerCase()); } case 'true/false': { return ['true', 'false'].includes(value.toLowerCase()); } case 'true/false/undefined': { return ['true', 'false', 'undefined'].includes(value.toLowerCase()); } } }