@lytjs/common-a11y 6.5.0 → 6.6.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.cjs +2 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.mjs +2 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +550 -508
package/dist/index.cjs
CHANGED
|
@@ -13,7 +13,7 @@ function getButtonA11yProps(props) {
|
|
|
13
13
|
"aria-labelledby": props.ariaLabelledBy,
|
|
14
14
|
"aria-disabled": props.ariaDisabled ?? props.disabled,
|
|
15
15
|
"aria-pressed": props.ariaPressed,
|
|
16
|
-
|
|
16
|
+
tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),
|
|
17
17
|
id: props.id
|
|
18
18
|
};
|
|
19
19
|
}
|
|
@@ -195,9 +195,7 @@ function isFocusable(element) {
|
|
|
195
195
|
}
|
|
196
196
|
function getFocusableElements(container) {
|
|
197
197
|
const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
198
|
-
return elements.filter(
|
|
199
|
-
(el) => el instanceof HTMLElement && isFocusable(el)
|
|
200
|
-
);
|
|
198
|
+
return elements.filter((el) => el instanceof HTMLElement && isFocusable(el));
|
|
201
199
|
}
|
|
202
200
|
function focusTrap(container, options) {
|
|
203
201
|
const { initialFocus, escapeDeactivates = true } = options || {};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-a11y\r\n * 轻量级无障碍访问工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface FocusTrapOptions {\r\n initialFocus?: HTMLElement;\r\n escapeDeactivates?: boolean;\r\n}\r\n\r\n/**\r\n * 通用无障碍属性接口\r\n */\r\nexport interface A11yProps {\r\n id?: string;\r\n ariaLabel?: string;\r\n ariaDescribedBy?: string;\r\n ariaLabelledBy?: string;\r\n ariaRequired?: boolean;\r\n ariaInvalid?: boolean;\r\n ariaDisabled?: boolean;\r\n ariaHidden?: boolean;\r\n ariaExpanded?: boolean;\r\n ariaChecked?: boolean | 'mixed';\r\n ariaSelected?: boolean;\r\n ariaPressed?: boolean;\r\n ariaHasPopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';\r\n ariaControls?: string;\r\n ariaOwns?: string;\r\n ariaLive?: 'off' | 'polite' | 'assertive';\r\n ariaValuenow?: number | string;\r\n ariaValuemax?: number;\r\n ariaValuemin?: number;\r\n ariaModal?: boolean;\r\n tabIndex?: number;\r\n role?: string;\r\n}\r\n\r\n/**\r\n * 生成 tabindex 属性值\r\n */\r\nexport function getTabIndex(disabled: boolean, customTabIndex?: number): number | undefined {\r\n if (customTabIndex !== undefined) return customTabIndex;\r\n return disabled ? -1 : 0;\r\n}\r\n\r\n/**\r\n * 为按钮组件生成 a11y 属性\r\n */\r\nexport function getButtonA11yProps(props: A11yProps & { disabled?: boolean }): Record<string, any> {\r\n return {\r\n role: 'button',\r\n 'aria-label': props.ariaLabel,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-labelledby': props.ariaLabelledBy,\r\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\r\n 'aria-pressed': props.ariaPressed,\r\n 'tabindex': getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为表单控件生成 a11y 属性\r\n */\r\nexport function getFormControlA11yProps(props: A11yProps & { disabled?: boolean; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n 'aria-label': props.ariaLabel,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-labelledby': props.ariaLabelledBy,\r\n 'aria-required': props.ariaRequired ?? props.required,\r\n 'aria-invalid': props.ariaInvalid ?? props.invalid,\r\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\r\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为复选框/单选框生成 a11y 属性\r\n */\r\nexport function getInputControlA11yProps(props: A11yProps & { disabled?: boolean; checked?: boolean | 'mixed'; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n ...getFormControlA11yProps(props),\r\n 'aria-checked': props.checked,\r\n };\r\n}\r\n\r\n/**\r\n * 为开关组件生成 a11y 属性\r\n */\r\nexport function getSwitchA11yProps(props: A11yProps & { disabled?: boolean; checked?: boolean; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n role: 'switch',\r\n 'aria-checked': props.checked,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为下拉选择组件生成 a11y 属性\r\n */\r\nexport function getComboboxA11yProps(props: A11yProps & { disabled?: boolean; expanded?: boolean; controls?: string; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n role: 'combobox',\r\n 'aria-expanded': props.expanded,\r\n 'aria-controls': props.ariaControls ?? props.controls,\r\n 'aria-haspopup': 'listbox',\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为列表框选项生成 a11y 属性\r\n */\r\nexport function getOptionA11yProps(props: A11yProps & { selected?: boolean; disabled?: boolean }): Record<string, any> {\r\n return {\r\n role: 'option',\r\n 'aria-selected': props.selected,\r\n 'aria-disabled': props.disabled,\r\n tabindex: props.disabled ? -1 : 0,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为滑块组件生成 a11y 属性\r\n */\r\nexport function getSliderA11yProps(props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number }): Record<string, any> {\r\n return {\r\n role: 'slider',\r\n 'aria-valuenow': props.value,\r\n 'aria-valuemin': props.min,\r\n 'aria-valuemax': props.max,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为数字输入组件生成 a11y 属性\r\n */\r\nexport function getSpinbuttonA11yProps(props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number }): Record<string, any> {\r\n return {\r\n role: 'spinbutton',\r\n 'aria-valuenow': props.value,\r\n 'aria-valuemin': props.min,\r\n 'aria-valuemax': props.max,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为标签页列表生成 a11y 属性\r\n */\r\nexport function getTablistA11yProps(props: A11yProps & { label?: string }): Record<string, any> {\r\n return {\r\n role: 'tablist',\r\n 'aria-label': props.ariaLabel ?? props.label,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为单个标签页生成 a11y 属性\r\n */\r\nexport function getTabA11yProps(props: A11yProps & { selected?: boolean; disabled?: boolean; controls?: string }): Record<string, any> {\r\n return {\r\n role: 'tab',\r\n 'aria-selected': props.selected,\r\n 'aria-disabled': props.disabled,\r\n 'aria-controls': props.ariaControls ?? props.controls,\r\n tabindex: props.selected ? 0 : -1,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为标签面板生成 a11y 属性\r\n */\r\nexport function getTabpanelA11yProps(props: A11yProps & { labelledBy?: string; hidden?: boolean }): Record<string, any> {\r\n return {\r\n role: 'tabpanel',\r\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\r\n 'aria-hidden': props.hidden,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为对话框/模态框生成 a11y 属性\r\n */\r\nexport function getDialogA11yProps(props: A11yProps & { labelledBy?: string; describedBy?: string; modal?: boolean }): Record<string, any> {\r\n return {\r\n role: 'dialog',\r\n 'aria-modal': props.ariaModal ?? props.modal ?? true,\r\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\r\n 'aria-describedby': props.ariaDescribedBy ?? props.describedBy,\r\n 'aria-label': props.ariaLabel,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性\r\n */\r\nexport function getGroupA11yProps(props: A11yProps & { role?: 'radiogroup' | 'group' | 'listbox'; required?: boolean; label?: string }): Record<string, any> {\r\n return {\r\n role: props.role ?? 'group',\r\n 'aria-label': props.ariaLabel ?? props.label,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-required': props.ariaRequired ?? props.required,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 合并多个 a11y 属性对象\r\n */\r\nexport function mergeA11yProps(...propsList: Array<Record<string, any>>): Record<string, any> {\r\n const result: Record<string, any> = {};\r\n for (const props of propsList) {\r\n for (const [key, value] of Object.entries(props)) {\r\n if (value !== undefined && value !== null) {\r\n result[key] = value;\r\n }\r\n }\r\n }\r\n // 过滤 undefined 值\r\n return Object.fromEntries(\r\n Object.entries(result).filter(([_, v]) => v !== undefined && v !== null)\r\n );\r\n}\r\n\r\n/** ARIA 角色到必需属性的映射 */\r\nexport const ARIA_ROLES: Record<string, string[]> = {\r\n alert: ['aria-live'],\r\n alertdialog: ['aria-labelledby', 'aria-describedby'],\r\n button: [],\r\n checkbox: ['aria-checked'],\r\n combobox: ['aria-expanded', 'aria-controls'],\r\n dialog: ['aria-labelledby', 'aria-describedby'],\r\n grid: [],\r\n gridcell: [],\r\n link: [],\r\n listbox: ['aria-label'],\r\n menu: ['aria-label'],\r\n menubar: [],\r\n menuitem: [],\r\n option: ['aria-selected'],\r\n progressbar: ['aria-valuenow'],\r\n radio: ['aria-checked'],\r\n radiogroup: ['aria-label'],\r\n slider: ['aria-valuenow'],\r\n spinbutton: ['aria-valuenow'],\r\n tab: ['aria-selected'],\r\n tablist: [],\r\n tabpanel: ['aria-labelledby'],\r\n textbox: [],\r\n tree: ['aria-label'],\r\n treeitem: ['aria-selected'],\r\n};\r\n\r\n/** 可聚焦元素选择器 */\r\nconst FOCUSABLE_SELECTOR = [\r\n 'a[href]',\r\n 'area[href]',\r\n 'button:not([disabled])',\r\n 'input:not([disabled])',\r\n 'select:not([disabled])',\r\n 'textarea:not([disabled])',\r\n '[tabindex]:not([tabindex=\"-1\"])',\r\n '[contenteditable=\"true\"]',\r\n].join(', ');\r\n\r\n/**\r\n * 检查元素是否可聚焦\r\n */\r\nexport function isFocusable(element: Element): boolean {\r\n if (!(element instanceof HTMLElement)) return false;\r\n if ('disabled' in element && (element as { disabled: boolean }).disabled) return false;\r\n if (element.getAttribute('tabindex') === '-1') return false;\r\n if (element.getAttribute('aria-hidden') === 'true') return false;\r\n\r\n const tag = element.tagName.toLowerCase();\r\n const focusableTags = new Set([\r\n 'a', 'button', 'input', 'select', 'textarea',\r\n 'details', 'summary',\r\n ]);\r\n\r\n if (focusableTags.has(tag)) return true;\r\n if (element.getAttribute('tabindex') !== null) return true;\r\n if (element.isContentEditable || element.getAttribute('contenteditable') === 'true') return true;\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 获取容器内所有可聚焦元素\r\n */\r\nexport function getFocusableElements(container: Element): HTMLElement[] {\r\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));\r\n return elements.filter(\r\n (el): el is HTMLElement => el instanceof HTMLElement && isFocusable(el),\r\n );\r\n}\r\n\r\n/**\r\n * 在容器内创建焦点陷阱\r\n *\r\n * @param container - 陷阱容器\r\n * @param options - 配置选项\r\n * @returns 清理函数\r\n */\r\nexport function focusTrap(\r\n container: HTMLElement,\r\n options?: FocusTrapOptions,\r\n): () => void {\r\n const { initialFocus, escapeDeactivates = true } = options || {};\r\n\r\n const focusableElements = getFocusableElements(container);\r\n const firstElement = focusableElements[0] || container;\r\n const lastElement = focusableElements[focusableElements.length - 1] || container;\r\n\r\n // 设置初始焦点\r\n if (initialFocus) {\r\n initialFocus.focus();\r\n } else {\r\n firstElement.focus();\r\n }\r\n\r\n const handleKeyDown = (event: KeyboardEvent) => {\r\n if (event.key === 'Escape' && escapeDeactivates) {\r\n cleanup();\r\n return;\r\n }\r\n\r\n if (event.key !== 'Tab') return;\r\n\r\n if (focusableElements.length === 0) {\r\n event.preventDefault();\r\n return;\r\n }\r\n\r\n if (event.shiftKey) {\r\n if (document.activeElement === firstElement) {\r\n event.preventDefault();\r\n lastElement.focus();\r\n }\r\n } else {\r\n if (document.activeElement === lastElement) {\r\n event.preventDefault();\r\n firstElement.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener('keydown', handleKeyDown);\r\n\r\n const cleanup = () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n };\r\n\r\n return cleanup;\r\n}\r\n\r\n/**\r\n * 管理焦点:保存之前的焦点,将焦点移入容器,返回恢复函数\r\n *\r\n * @param container - 目标容器\r\n * @param triggerEl - 触发元素,恢复焦点时优先回到此元素\r\n * @returns 恢复函数\r\n */\r\nexport function manageFocus(\r\n container: HTMLElement,\r\n triggerEl?: HTMLElement,\r\n): () => void {\r\n const previousFocus = document.activeElement as HTMLElement | null;\r\n\r\n const focusableElements = getFocusableElements(container);\r\n if (focusableElements.length > 0) {\r\n focusableElements[0]!.focus();\r\n } else {\r\n container.setAttribute('tabindex', '-1');\r\n container.focus();\r\n }\r\n\r\n return () => {\r\n const target = triggerEl || previousFocus;\r\n if (target && typeof target.focus === 'function') {\r\n target.focus();\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * 获取元素上所有 aria-* 属性\r\n */\r\nexport function getAriaProps(element: Element): Record<string, string> {\r\n const result: Record<string, string> = {};\r\n const attrs = element.attributes;\r\n for (let i = 0; i < attrs.length; i++) {\r\n const attr = attrs[i]!;\r\n if (attr.name.startsWith('aria-')) {\r\n result[attr.name] = attr.value!;\r\n }\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * 批量设置 aria-* 属性\r\n */\r\nexport function setAriaProps(\r\n element: Element,\r\n props: Record<string, string>,\r\n): void {\r\n for (const key of Object.keys(props)) {\r\n if (key.startsWith('aria-')) {\r\n element.setAttribute(key, props[key]!);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查给定元素是否是当前活动元素\r\n */\r\nexport function assertActiveElement(element: Element): boolean {\r\n return document.activeElement === element;\r\n}\r\n\r\n/**\r\n * 键盘导航辅助函数 - 在启用的选项间循环\r\n */\r\nexport function getNextEnabledIndex(\r\n currentIndex: number,\r\n totalItems: number,\r\n isEnabled: (index: number) => boolean,\r\n direction: 'forward' | 'backward' = 'forward'\r\n): number {\r\n const step = direction === 'forward' ? 1 : -1;\r\n let nextIndex = (currentIndex + step + totalItems) % totalItems;\r\n \r\n for (let i = 0; i < totalItems; i++) {\r\n if (isEnabled(nextIndex)) {\r\n return nextIndex;\r\n }\r\n nextIndex = (nextIndex + step + totalItems) % totalItems;\r\n }\r\n \r\n return currentIndex;\r\n}\r\n\r\n/**\r\n * 处理列表组件的键盘导航\r\n */\r\nexport function handleListKeydown(\r\n event: KeyboardEvent,\r\n currentIndex: number,\r\n totalItems: number,\r\n isEnabled: (index: number) => boolean,\r\n onSelect: (index: number) => void,\r\n onClose?: () => void\r\n): void {\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n case 'ArrowRight':\r\n event.preventDefault();\r\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'forward'));\r\n break;\r\n case 'ArrowUp':\r\n case 'ArrowLeft':\r\n event.preventDefault();\r\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'backward'));\r\n break;\r\n case 'Home':\r\n event.preventDefault();\r\n for (let i = 0; i < totalItems; i++) {\r\n if (isEnabled(i)) {\r\n onSelect(i);\r\n break;\r\n }\r\n }\r\n break;\r\n case 'End':\r\n event.preventDefault();\r\n for (let i = totalItems - 1; i >= 0; i--) {\r\n if (isEnabled(i)) {\r\n onSelect(i);\r\n break;\r\n }\r\n }\r\n break;\r\n case 'Enter':\r\n case ' ':\r\n event.preventDefault();\r\n if (isEnabled(currentIndex)) {\r\n onSelect(currentIndex);\r\n }\r\n break;\r\n case 'Escape':\r\n event.preventDefault();\r\n onClose?.();\r\n break;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lytjs/common-a11y\n * 轻量级无障碍访问工具\n */\n\ndeclare const __DEV__: boolean;\n\nexport interface FocusTrapOptions {\n initialFocus?: HTMLElement;\n escapeDeactivates?: boolean;\n}\n\n/**\n * 通用无障碍属性接口\n */\nexport interface A11yProps {\n id?: string;\n ariaLabel?: string;\n ariaDescribedBy?: string;\n ariaLabelledBy?: string;\n ariaRequired?: boolean;\n ariaInvalid?: boolean;\n ariaDisabled?: boolean;\n ariaHidden?: boolean;\n ariaExpanded?: boolean;\n ariaChecked?: boolean | 'mixed';\n ariaSelected?: boolean;\n ariaPressed?: boolean;\n ariaHasPopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';\n ariaControls?: string;\n ariaOwns?: string;\n ariaLive?: 'off' | 'polite' | 'assertive';\n ariaValuenow?: number | string;\n ariaValuemax?: number;\n ariaValuemin?: number;\n ariaModal?: boolean;\n tabIndex?: number;\n role?: string;\n}\n\n/**\n * 生成 tabindex 属性值\n */\nexport function getTabIndex(disabled: boolean, customTabIndex?: number): number | undefined {\n if (customTabIndex !== undefined) return customTabIndex;\n return disabled ? -1 : 0;\n}\n\n/**\n * 为按钮组件生成 a11y 属性\n */\nexport function getButtonA11yProps(\n props: A11yProps & { disabled?: boolean },\n): Record<string, unknown> {\n return {\n role: 'button',\n 'aria-label': props.ariaLabel,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-labelledby': props.ariaLabelledBy,\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\n 'aria-pressed': props.ariaPressed,\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\n id: props.id,\n };\n}\n\n/**\n * 为表单控件生成 a11y 属性\n */\nexport function getFormControlA11yProps(\n props: A11yProps & { disabled?: boolean; required?: boolean; invalid?: boolean },\n): Record<string, unknown> {\n return {\n 'aria-label': props.ariaLabel,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-labelledby': props.ariaLabelledBy,\n 'aria-required': props.ariaRequired ?? props.required,\n 'aria-invalid': props.ariaInvalid ?? props.invalid,\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\n id: props.id,\n };\n}\n\n/**\n * 为复选框/单选框生成 a11y 属性\n */\nexport function getInputControlA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n checked?: boolean | 'mixed';\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n ...getFormControlA11yProps(props),\n 'aria-checked': props.checked,\n };\n}\n\n/**\n * 为开关组件生成 a11y 属性\n */\nexport function getSwitchA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n checked?: boolean;\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n role: 'switch',\n 'aria-checked': props.checked,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为下拉选择组件生成 a11y 属性\n */\nexport function getComboboxA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n expanded?: boolean;\n controls?: string;\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n role: 'combobox',\n 'aria-expanded': props.expanded,\n 'aria-controls': props.ariaControls ?? props.controls,\n 'aria-haspopup': 'listbox',\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为列表框选项生成 a11y 属性\n */\nexport function getOptionA11yProps(\n props: A11yProps & { selected?: boolean; disabled?: boolean },\n): Record<string, unknown> {\n return {\n role: 'option',\n 'aria-selected': props.selected,\n 'aria-disabled': props.disabled,\n tabindex: props.disabled ? -1 : 0,\n id: props.id,\n };\n}\n\n/**\n * 为滑块组件生成 a11y 属性\n */\nexport function getSliderA11yProps(\n props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number },\n): Record<string, unknown> {\n return {\n role: 'slider',\n 'aria-valuenow': props.value,\n 'aria-valuemin': props.min,\n 'aria-valuemax': props.max,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为数字输入组件生成 a11y 属性\n */\nexport function getSpinbuttonA11yProps(\n props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number },\n): Record<string, unknown> {\n return {\n role: 'spinbutton',\n 'aria-valuenow': props.value,\n 'aria-valuemin': props.min,\n 'aria-valuemax': props.max,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为标签页列表生成 a11y 属性\n */\nexport function getTablistA11yProps(\n props: A11yProps & { label?: string },\n): Record<string, unknown> {\n return {\n role: 'tablist',\n 'aria-label': props.ariaLabel ?? props.label,\n 'aria-describedby': props.ariaDescribedBy,\n id: props.id,\n };\n}\n\n/**\n * 为单个标签页生成 a11y 属性\n */\nexport function getTabA11yProps(\n props: A11yProps & { selected?: boolean; disabled?: boolean; controls?: string },\n): Record<string, unknown> {\n return {\n role: 'tab',\n 'aria-selected': props.selected,\n 'aria-disabled': props.disabled,\n 'aria-controls': props.ariaControls ?? props.controls,\n tabindex: props.selected ? 0 : -1,\n id: props.id,\n };\n}\n\n/**\n * 为标签面板生成 a11y 属性\n */\nexport function getTabpanelA11yProps(\n props: A11yProps & { labelledBy?: string; hidden?: boolean },\n): Record<string, unknown> {\n return {\n role: 'tabpanel',\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\n 'aria-hidden': props.hidden,\n id: props.id,\n };\n}\n\n/**\n * 为对话框/模态框生成 a11y 属性\n */\nexport function getDialogA11yProps(\n props: A11yProps & { labelledBy?: string; describedBy?: string; modal?: boolean },\n): Record<string, unknown> {\n return {\n role: 'dialog',\n 'aria-modal': props.ariaModal ?? props.modal ?? true,\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\n 'aria-describedby': props.ariaDescribedBy ?? props.describedBy,\n 'aria-label': props.ariaLabel,\n id: props.id,\n };\n}\n\n/**\n * 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性\n */\nexport function getGroupA11yProps(\n props: A11yProps & {\n role?: 'radiogroup' | 'group' | 'listbox';\n required?: boolean;\n label?: string;\n },\n): Record<string, unknown> {\n return {\n role: props.role ?? 'group',\n 'aria-label': props.ariaLabel ?? props.label,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-required': props.ariaRequired ?? props.required,\n id: props.id,\n };\n}\n\n/**\n * 合并多个 a11y 属性对象\n */\nexport function mergeA11yProps(\n ...propsList: Array<Record<string, unknown>>\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const props of propsList) {\n for (const [key, value] of Object.entries(props)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n }\n // 过滤 undefined 值\n return Object.fromEntries(\n Object.entries(result).filter(([_, v]) => v !== undefined && v !== null),\n );\n}\n\n/** ARIA 角色到必需属性的映射 */\nexport const ARIA_ROLES: Record<string, string[]> = {\n alert: ['aria-live'],\n alertdialog: ['aria-labelledby', 'aria-describedby'],\n button: [],\n checkbox: ['aria-checked'],\n combobox: ['aria-expanded', 'aria-controls'],\n dialog: ['aria-labelledby', 'aria-describedby'],\n grid: [],\n gridcell: [],\n link: [],\n listbox: ['aria-label'],\n menu: ['aria-label'],\n menubar: [],\n menuitem: [],\n option: ['aria-selected'],\n progressbar: ['aria-valuenow'],\n radio: ['aria-checked'],\n radiogroup: ['aria-label'],\n slider: ['aria-valuenow'],\n spinbutton: ['aria-valuenow'],\n tab: ['aria-selected'],\n tablist: [],\n tabpanel: ['aria-labelledby'],\n textbox: [],\n tree: ['aria-label'],\n treeitem: ['aria-selected'],\n};\n\n/** 可聚焦元素选择器 */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\n/**\n * 检查元素是否可聚焦\n */\nexport function isFocusable(element: Element): boolean {\n if (!(element instanceof HTMLElement)) return false;\n if ('disabled' in element && (element as { disabled: boolean }).disabled) return false;\n if (element.getAttribute('tabindex') === '-1') return false;\n if (element.getAttribute('aria-hidden') === 'true') return false;\n\n const tag = element.tagName.toLowerCase();\n const focusableTags = new Set([\n 'a',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'details',\n 'summary',\n ]);\n\n if (focusableTags.has(tag)) return true;\n if (element.getAttribute('tabindex') !== null) return true;\n if (element.isContentEditable || element.getAttribute('contenteditable') === 'true') return true;\n\n return false;\n}\n\n/**\n * 获取容器内所有可聚焦元素\n */\nexport function getFocusableElements(container: Element): HTMLElement[] {\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));\n return elements.filter((el): el is HTMLElement => el instanceof HTMLElement && isFocusable(el));\n}\n\n/**\n * 在容器内创建焦点陷阱\n *\n * @param container - 陷阱容器\n * @param options - 配置选项\n * @returns 清理函数\n */\nexport function focusTrap(container: HTMLElement, options?: FocusTrapOptions): () => void {\n const { initialFocus, escapeDeactivates = true } = options || {};\n\n const focusableElements = getFocusableElements(container);\n const firstElement = focusableElements[0] || container;\n const lastElement = focusableElements[focusableElements.length - 1] || container;\n\n // 设置初始焦点\n if (initialFocus) {\n initialFocus.focus();\n } else {\n firstElement.focus();\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && escapeDeactivates) {\n cleanup();\n return;\n }\n\n if (event.key !== 'Tab') return;\n\n if (focusableElements.length === 0) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey) {\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n\n const cleanup = () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n\n return cleanup;\n}\n\n/**\n * 管理焦点:保存之前的焦点,将焦点移入容器,返回恢复函数\n *\n * @param container - 目标容器\n * @param triggerEl - 触发元素,恢复焦点时优先回到此元素\n * @returns 恢复函数\n */\nexport function manageFocus(container: HTMLElement, triggerEl?: HTMLElement): () => void {\n const previousFocus = document.activeElement as HTMLElement | null;\n\n const focusableElements = getFocusableElements(container);\n if (focusableElements.length > 0) {\n focusableElements[0]!.focus();\n } else {\n container.setAttribute('tabindex', '-1');\n container.focus();\n }\n\n return () => {\n const target = triggerEl || previousFocus;\n if (target && typeof target.focus === 'function') {\n target.focus();\n }\n };\n}\n\n/**\n * 获取元素上所有 aria-* 属性\n */\nexport function getAriaProps(element: Element): Record<string, string> {\n const result: Record<string, string> = {};\n const attrs = element.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i]!;\n if (attr.name.startsWith('aria-')) {\n result[attr.name] = attr.value!;\n }\n }\n return result;\n}\n\n/**\n * 批量设置 aria-* 属性\n */\nexport function setAriaProps(element: Element, props: Record<string, string>): void {\n for (const key of Object.keys(props)) {\n if (key.startsWith('aria-')) {\n element.setAttribute(key, props[key]!);\n }\n }\n}\n\n/**\n * 检查给定元素是否是当前活动元素\n */\nexport function assertActiveElement(element: Element): boolean {\n return document.activeElement === element;\n}\n\n/**\n * 键盘导航辅助函数 - 在启用的选项间循环\n */\nexport function getNextEnabledIndex(\n currentIndex: number,\n totalItems: number,\n isEnabled: (index: number) => boolean,\n direction: 'forward' | 'backward' = 'forward',\n): number {\n const step = direction === 'forward' ? 1 : -1;\n let nextIndex = (currentIndex + step + totalItems) % totalItems;\n\n for (let i = 0; i < totalItems; i++) {\n if (isEnabled(nextIndex)) {\n return nextIndex;\n }\n nextIndex = (nextIndex + step + totalItems) % totalItems;\n }\n\n return currentIndex;\n}\n\n/**\n * 处理列表组件的键盘导航\n */\nexport function handleListKeydown(\n event: KeyboardEvent,\n currentIndex: number,\n totalItems: number,\n isEnabled: (index: number) => boolean,\n onSelect: (index: number) => void,\n onClose?: () => void,\n): void {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n event.preventDefault();\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'forward'));\n break;\n case 'ArrowUp':\n case 'ArrowLeft':\n event.preventDefault();\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'backward'));\n break;\n case 'Home':\n event.preventDefault();\n for (let i = 0; i < totalItems; i++) {\n if (isEnabled(i)) {\n onSelect(i);\n break;\n }\n }\n break;\n case 'End':\n event.preventDefault();\n for (let i = totalItems - 1; i >= 0; i--) {\n if (isEnabled(i)) {\n onSelect(i);\n break;\n }\n }\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (isEnabled(currentIndex)) {\n onSelect(currentIndex);\n }\n break;\n case 'Escape':\n event.preventDefault();\n onClose?.();\n break;\n }\n}\n"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -42,7 +42,7 @@ declare function getTabIndex(disabled: boolean, customTabIndex?: number): number
|
|
|
42
42
|
*/
|
|
43
43
|
declare function getButtonA11yProps(props: A11yProps & {
|
|
44
44
|
disabled?: boolean;
|
|
45
|
-
}): Record<string,
|
|
45
|
+
}): Record<string, unknown>;
|
|
46
46
|
/**
|
|
47
47
|
* 为表单控件生成 a11y 属性
|
|
48
48
|
*/
|
|
@@ -50,7 +50,7 @@ declare function getFormControlA11yProps(props: A11yProps & {
|
|
|
50
50
|
disabled?: boolean;
|
|
51
51
|
required?: boolean;
|
|
52
52
|
invalid?: boolean;
|
|
53
|
-
}): Record<string,
|
|
53
|
+
}): Record<string, unknown>;
|
|
54
54
|
/**
|
|
55
55
|
* 为复选框/单选框生成 a11y 属性
|
|
56
56
|
*/
|
|
@@ -59,7 +59,7 @@ declare function getInputControlA11yProps(props: A11yProps & {
|
|
|
59
59
|
checked?: boolean | 'mixed';
|
|
60
60
|
required?: boolean;
|
|
61
61
|
invalid?: boolean;
|
|
62
|
-
}): Record<string,
|
|
62
|
+
}): Record<string, unknown>;
|
|
63
63
|
/**
|
|
64
64
|
* 为开关组件生成 a11y 属性
|
|
65
65
|
*/
|
|
@@ -68,7 +68,7 @@ declare function getSwitchA11yProps(props: A11yProps & {
|
|
|
68
68
|
checked?: boolean;
|
|
69
69
|
required?: boolean;
|
|
70
70
|
invalid?: boolean;
|
|
71
|
-
}): Record<string,
|
|
71
|
+
}): Record<string, unknown>;
|
|
72
72
|
/**
|
|
73
73
|
* 为下拉选择组件生成 a11y 属性
|
|
74
74
|
*/
|
|
@@ -78,14 +78,14 @@ declare function getComboboxA11yProps(props: A11yProps & {
|
|
|
78
78
|
controls?: string;
|
|
79
79
|
required?: boolean;
|
|
80
80
|
invalid?: boolean;
|
|
81
|
-
}): Record<string,
|
|
81
|
+
}): Record<string, unknown>;
|
|
82
82
|
/**
|
|
83
83
|
* 为列表框选项生成 a11y 属性
|
|
84
84
|
*/
|
|
85
85
|
declare function getOptionA11yProps(props: A11yProps & {
|
|
86
86
|
selected?: boolean;
|
|
87
87
|
disabled?: boolean;
|
|
88
|
-
}): Record<string,
|
|
88
|
+
}): Record<string, unknown>;
|
|
89
89
|
/**
|
|
90
90
|
* 为滑块组件生成 a11y 属性
|
|
91
91
|
*/
|
|
@@ -94,7 +94,7 @@ declare function getSliderA11yProps(props: A11yProps & {
|
|
|
94
94
|
value?: number | string;
|
|
95
95
|
min?: number;
|
|
96
96
|
max?: number;
|
|
97
|
-
}): Record<string,
|
|
97
|
+
}): Record<string, unknown>;
|
|
98
98
|
/**
|
|
99
99
|
* 为数字输入组件生成 a11y 属性
|
|
100
100
|
*/
|
|
@@ -103,13 +103,13 @@ declare function getSpinbuttonA11yProps(props: A11yProps & {
|
|
|
103
103
|
value?: number | string;
|
|
104
104
|
min?: number;
|
|
105
105
|
max?: number;
|
|
106
|
-
}): Record<string,
|
|
106
|
+
}): Record<string, unknown>;
|
|
107
107
|
/**
|
|
108
108
|
* 为标签页列表生成 a11y 属性
|
|
109
109
|
*/
|
|
110
110
|
declare function getTablistA11yProps(props: A11yProps & {
|
|
111
111
|
label?: string;
|
|
112
|
-
}): Record<string,
|
|
112
|
+
}): Record<string, unknown>;
|
|
113
113
|
/**
|
|
114
114
|
* 为单个标签页生成 a11y 属性
|
|
115
115
|
*/
|
|
@@ -117,14 +117,14 @@ declare function getTabA11yProps(props: A11yProps & {
|
|
|
117
117
|
selected?: boolean;
|
|
118
118
|
disabled?: boolean;
|
|
119
119
|
controls?: string;
|
|
120
|
-
}): Record<string,
|
|
120
|
+
}): Record<string, unknown>;
|
|
121
121
|
/**
|
|
122
122
|
* 为标签面板生成 a11y 属性
|
|
123
123
|
*/
|
|
124
124
|
declare function getTabpanelA11yProps(props: A11yProps & {
|
|
125
125
|
labelledBy?: string;
|
|
126
126
|
hidden?: boolean;
|
|
127
|
-
}): Record<string,
|
|
127
|
+
}): Record<string, unknown>;
|
|
128
128
|
/**
|
|
129
129
|
* 为对话框/模态框生成 a11y 属性
|
|
130
130
|
*/
|
|
@@ -132,7 +132,7 @@ declare function getDialogA11yProps(props: A11yProps & {
|
|
|
132
132
|
labelledBy?: string;
|
|
133
133
|
describedBy?: string;
|
|
134
134
|
modal?: boolean;
|
|
135
|
-
}): Record<string,
|
|
135
|
+
}): Record<string, unknown>;
|
|
136
136
|
/**
|
|
137
137
|
* 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性
|
|
138
138
|
*/
|
|
@@ -140,11 +140,11 @@ declare function getGroupA11yProps(props: A11yProps & {
|
|
|
140
140
|
role?: 'radiogroup' | 'group' | 'listbox';
|
|
141
141
|
required?: boolean;
|
|
142
142
|
label?: string;
|
|
143
|
-
}): Record<string,
|
|
143
|
+
}): Record<string, unknown>;
|
|
144
144
|
/**
|
|
145
145
|
* 合并多个 a11y 属性对象
|
|
146
146
|
*/
|
|
147
|
-
declare function mergeA11yProps(...propsList: Array<Record<string,
|
|
147
|
+
declare function mergeA11yProps(...propsList: Array<Record<string, unknown>>): Record<string, unknown>;
|
|
148
148
|
/** ARIA 角色到必需属性的映射 */
|
|
149
149
|
declare const ARIA_ROLES: Record<string, string[]>;
|
|
150
150
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ declare function getTabIndex(disabled: boolean, customTabIndex?: number): number
|
|
|
42
42
|
*/
|
|
43
43
|
declare function getButtonA11yProps(props: A11yProps & {
|
|
44
44
|
disabled?: boolean;
|
|
45
|
-
}): Record<string,
|
|
45
|
+
}): Record<string, unknown>;
|
|
46
46
|
/**
|
|
47
47
|
* 为表单控件生成 a11y 属性
|
|
48
48
|
*/
|
|
@@ -50,7 +50,7 @@ declare function getFormControlA11yProps(props: A11yProps & {
|
|
|
50
50
|
disabled?: boolean;
|
|
51
51
|
required?: boolean;
|
|
52
52
|
invalid?: boolean;
|
|
53
|
-
}): Record<string,
|
|
53
|
+
}): Record<string, unknown>;
|
|
54
54
|
/**
|
|
55
55
|
* 为复选框/单选框生成 a11y 属性
|
|
56
56
|
*/
|
|
@@ -59,7 +59,7 @@ declare function getInputControlA11yProps(props: A11yProps & {
|
|
|
59
59
|
checked?: boolean | 'mixed';
|
|
60
60
|
required?: boolean;
|
|
61
61
|
invalid?: boolean;
|
|
62
|
-
}): Record<string,
|
|
62
|
+
}): Record<string, unknown>;
|
|
63
63
|
/**
|
|
64
64
|
* 为开关组件生成 a11y 属性
|
|
65
65
|
*/
|
|
@@ -68,7 +68,7 @@ declare function getSwitchA11yProps(props: A11yProps & {
|
|
|
68
68
|
checked?: boolean;
|
|
69
69
|
required?: boolean;
|
|
70
70
|
invalid?: boolean;
|
|
71
|
-
}): Record<string,
|
|
71
|
+
}): Record<string, unknown>;
|
|
72
72
|
/**
|
|
73
73
|
* 为下拉选择组件生成 a11y 属性
|
|
74
74
|
*/
|
|
@@ -78,14 +78,14 @@ declare function getComboboxA11yProps(props: A11yProps & {
|
|
|
78
78
|
controls?: string;
|
|
79
79
|
required?: boolean;
|
|
80
80
|
invalid?: boolean;
|
|
81
|
-
}): Record<string,
|
|
81
|
+
}): Record<string, unknown>;
|
|
82
82
|
/**
|
|
83
83
|
* 为列表框选项生成 a11y 属性
|
|
84
84
|
*/
|
|
85
85
|
declare function getOptionA11yProps(props: A11yProps & {
|
|
86
86
|
selected?: boolean;
|
|
87
87
|
disabled?: boolean;
|
|
88
|
-
}): Record<string,
|
|
88
|
+
}): Record<string, unknown>;
|
|
89
89
|
/**
|
|
90
90
|
* 为滑块组件生成 a11y 属性
|
|
91
91
|
*/
|
|
@@ -94,7 +94,7 @@ declare function getSliderA11yProps(props: A11yProps & {
|
|
|
94
94
|
value?: number | string;
|
|
95
95
|
min?: number;
|
|
96
96
|
max?: number;
|
|
97
|
-
}): Record<string,
|
|
97
|
+
}): Record<string, unknown>;
|
|
98
98
|
/**
|
|
99
99
|
* 为数字输入组件生成 a11y 属性
|
|
100
100
|
*/
|
|
@@ -103,13 +103,13 @@ declare function getSpinbuttonA11yProps(props: A11yProps & {
|
|
|
103
103
|
value?: number | string;
|
|
104
104
|
min?: number;
|
|
105
105
|
max?: number;
|
|
106
|
-
}): Record<string,
|
|
106
|
+
}): Record<string, unknown>;
|
|
107
107
|
/**
|
|
108
108
|
* 为标签页列表生成 a11y 属性
|
|
109
109
|
*/
|
|
110
110
|
declare function getTablistA11yProps(props: A11yProps & {
|
|
111
111
|
label?: string;
|
|
112
|
-
}): Record<string,
|
|
112
|
+
}): Record<string, unknown>;
|
|
113
113
|
/**
|
|
114
114
|
* 为单个标签页生成 a11y 属性
|
|
115
115
|
*/
|
|
@@ -117,14 +117,14 @@ declare function getTabA11yProps(props: A11yProps & {
|
|
|
117
117
|
selected?: boolean;
|
|
118
118
|
disabled?: boolean;
|
|
119
119
|
controls?: string;
|
|
120
|
-
}): Record<string,
|
|
120
|
+
}): Record<string, unknown>;
|
|
121
121
|
/**
|
|
122
122
|
* 为标签面板生成 a11y 属性
|
|
123
123
|
*/
|
|
124
124
|
declare function getTabpanelA11yProps(props: A11yProps & {
|
|
125
125
|
labelledBy?: string;
|
|
126
126
|
hidden?: boolean;
|
|
127
|
-
}): Record<string,
|
|
127
|
+
}): Record<string, unknown>;
|
|
128
128
|
/**
|
|
129
129
|
* 为对话框/模态框生成 a11y 属性
|
|
130
130
|
*/
|
|
@@ -132,7 +132,7 @@ declare function getDialogA11yProps(props: A11yProps & {
|
|
|
132
132
|
labelledBy?: string;
|
|
133
133
|
describedBy?: string;
|
|
134
134
|
modal?: boolean;
|
|
135
|
-
}): Record<string,
|
|
135
|
+
}): Record<string, unknown>;
|
|
136
136
|
/**
|
|
137
137
|
* 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性
|
|
138
138
|
*/
|
|
@@ -140,11 +140,11 @@ declare function getGroupA11yProps(props: A11yProps & {
|
|
|
140
140
|
role?: 'radiogroup' | 'group' | 'listbox';
|
|
141
141
|
required?: boolean;
|
|
142
142
|
label?: string;
|
|
143
|
-
}): Record<string,
|
|
143
|
+
}): Record<string, unknown>;
|
|
144
144
|
/**
|
|
145
145
|
* 合并多个 a11y 属性对象
|
|
146
146
|
*/
|
|
147
|
-
declare function mergeA11yProps(...propsList: Array<Record<string,
|
|
147
|
+
declare function mergeA11yProps(...propsList: Array<Record<string, unknown>>): Record<string, unknown>;
|
|
148
148
|
/** ARIA 角色到必需属性的映射 */
|
|
149
149
|
declare const ARIA_ROLES: Record<string, string[]>;
|
|
150
150
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ function getButtonA11yProps(props) {
|
|
|
11
11
|
"aria-labelledby": props.ariaLabelledBy,
|
|
12
12
|
"aria-disabled": props.ariaDisabled ?? props.disabled,
|
|
13
13
|
"aria-pressed": props.ariaPressed,
|
|
14
|
-
|
|
14
|
+
tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),
|
|
15
15
|
id: props.id
|
|
16
16
|
};
|
|
17
17
|
}
|
|
@@ -193,9 +193,7 @@ function isFocusable(element) {
|
|
|
193
193
|
}
|
|
194
194
|
function getFocusableElements(container) {
|
|
195
195
|
const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
196
|
-
return elements.filter(
|
|
197
|
-
(el) => el instanceof HTMLElement && isFocusable(el)
|
|
198
|
-
);
|
|
196
|
+
return elements.filter((el) => el instanceof HTMLElement && isFocusable(el));
|
|
199
197
|
}
|
|
200
198
|
function focusTrap(container, options) {
|
|
201
199
|
const { initialFocus, escapeDeactivates = true } = options || {};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-a11y\r\n * 轻量级无障碍访问工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface FocusTrapOptions {\r\n initialFocus?: HTMLElement;\r\n escapeDeactivates?: boolean;\r\n}\r\n\r\n/**\r\n * 通用无障碍属性接口\r\n */\r\nexport interface A11yProps {\r\n id?: string;\r\n ariaLabel?: string;\r\n ariaDescribedBy?: string;\r\n ariaLabelledBy?: string;\r\n ariaRequired?: boolean;\r\n ariaInvalid?: boolean;\r\n ariaDisabled?: boolean;\r\n ariaHidden?: boolean;\r\n ariaExpanded?: boolean;\r\n ariaChecked?: boolean | 'mixed';\r\n ariaSelected?: boolean;\r\n ariaPressed?: boolean;\r\n ariaHasPopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';\r\n ariaControls?: string;\r\n ariaOwns?: string;\r\n ariaLive?: 'off' | 'polite' | 'assertive';\r\n ariaValuenow?: number | string;\r\n ariaValuemax?: number;\r\n ariaValuemin?: number;\r\n ariaModal?: boolean;\r\n tabIndex?: number;\r\n role?: string;\r\n}\r\n\r\n/**\r\n * 生成 tabindex 属性值\r\n */\r\nexport function getTabIndex(disabled: boolean, customTabIndex?: number): number | undefined {\r\n if (customTabIndex !== undefined) return customTabIndex;\r\n return disabled ? -1 : 0;\r\n}\r\n\r\n/**\r\n * 为按钮组件生成 a11y 属性\r\n */\r\nexport function getButtonA11yProps(props: A11yProps & { disabled?: boolean }): Record<string, any> {\r\n return {\r\n role: 'button',\r\n 'aria-label': props.ariaLabel,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-labelledby': props.ariaLabelledBy,\r\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\r\n 'aria-pressed': props.ariaPressed,\r\n 'tabindex': getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为表单控件生成 a11y 属性\r\n */\r\nexport function getFormControlA11yProps(props: A11yProps & { disabled?: boolean; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n 'aria-label': props.ariaLabel,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-labelledby': props.ariaLabelledBy,\r\n 'aria-required': props.ariaRequired ?? props.required,\r\n 'aria-invalid': props.ariaInvalid ?? props.invalid,\r\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\r\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为复选框/单选框生成 a11y 属性\r\n */\r\nexport function getInputControlA11yProps(props: A11yProps & { disabled?: boolean; checked?: boolean | 'mixed'; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n ...getFormControlA11yProps(props),\r\n 'aria-checked': props.checked,\r\n };\r\n}\r\n\r\n/**\r\n * 为开关组件生成 a11y 属性\r\n */\r\nexport function getSwitchA11yProps(props: A11yProps & { disabled?: boolean; checked?: boolean; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n role: 'switch',\r\n 'aria-checked': props.checked,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为下拉选择组件生成 a11y 属性\r\n */\r\nexport function getComboboxA11yProps(props: A11yProps & { disabled?: boolean; expanded?: boolean; controls?: string; required?: boolean; invalid?: boolean }): Record<string, any> {\r\n return {\r\n role: 'combobox',\r\n 'aria-expanded': props.expanded,\r\n 'aria-controls': props.ariaControls ?? props.controls,\r\n 'aria-haspopup': 'listbox',\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为列表框选项生成 a11y 属性\r\n */\r\nexport function getOptionA11yProps(props: A11yProps & { selected?: boolean; disabled?: boolean }): Record<string, any> {\r\n return {\r\n role: 'option',\r\n 'aria-selected': props.selected,\r\n 'aria-disabled': props.disabled,\r\n tabindex: props.disabled ? -1 : 0,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为滑块组件生成 a11y 属性\r\n */\r\nexport function getSliderA11yProps(props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number }): Record<string, any> {\r\n return {\r\n role: 'slider',\r\n 'aria-valuenow': props.value,\r\n 'aria-valuemin': props.min,\r\n 'aria-valuemax': props.max,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为数字输入组件生成 a11y 属性\r\n */\r\nexport function getSpinbuttonA11yProps(props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number }): Record<string, any> {\r\n return {\r\n role: 'spinbutton',\r\n 'aria-valuenow': props.value,\r\n 'aria-valuemin': props.min,\r\n 'aria-valuemax': props.max,\r\n ...getFormControlA11yProps(props),\r\n };\r\n}\r\n\r\n/**\r\n * 为标签页列表生成 a11y 属性\r\n */\r\nexport function getTablistA11yProps(props: A11yProps & { label?: string }): Record<string, any> {\r\n return {\r\n role: 'tablist',\r\n 'aria-label': props.ariaLabel ?? props.label,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为单个标签页生成 a11y 属性\r\n */\r\nexport function getTabA11yProps(props: A11yProps & { selected?: boolean; disabled?: boolean; controls?: string }): Record<string, any> {\r\n return {\r\n role: 'tab',\r\n 'aria-selected': props.selected,\r\n 'aria-disabled': props.disabled,\r\n 'aria-controls': props.ariaControls ?? props.controls,\r\n tabindex: props.selected ? 0 : -1,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为标签面板生成 a11y 属性\r\n */\r\nexport function getTabpanelA11yProps(props: A11yProps & { labelledBy?: string; hidden?: boolean }): Record<string, any> {\r\n return {\r\n role: 'tabpanel',\r\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\r\n 'aria-hidden': props.hidden,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为对话框/模态框生成 a11y 属性\r\n */\r\nexport function getDialogA11yProps(props: A11yProps & { labelledBy?: string; describedBy?: string; modal?: boolean }): Record<string, any> {\r\n return {\r\n role: 'dialog',\r\n 'aria-modal': props.ariaModal ?? props.modal ?? true,\r\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\r\n 'aria-describedby': props.ariaDescribedBy ?? props.describedBy,\r\n 'aria-label': props.ariaLabel,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性\r\n */\r\nexport function getGroupA11yProps(props: A11yProps & { role?: 'radiogroup' | 'group' | 'listbox'; required?: boolean; label?: string }): Record<string, any> {\r\n return {\r\n role: props.role ?? 'group',\r\n 'aria-label': props.ariaLabel ?? props.label,\r\n 'aria-describedby': props.ariaDescribedBy,\r\n 'aria-required': props.ariaRequired ?? props.required,\r\n id: props.id,\r\n };\r\n}\r\n\r\n/**\r\n * 合并多个 a11y 属性对象\r\n */\r\nexport function mergeA11yProps(...propsList: Array<Record<string, any>>): Record<string, any> {\r\n const result: Record<string, any> = {};\r\n for (const props of propsList) {\r\n for (const [key, value] of Object.entries(props)) {\r\n if (value !== undefined && value !== null) {\r\n result[key] = value;\r\n }\r\n }\r\n }\r\n // 过滤 undefined 值\r\n return Object.fromEntries(\r\n Object.entries(result).filter(([_, v]) => v !== undefined && v !== null)\r\n );\r\n}\r\n\r\n/** ARIA 角色到必需属性的映射 */\r\nexport const ARIA_ROLES: Record<string, string[]> = {\r\n alert: ['aria-live'],\r\n alertdialog: ['aria-labelledby', 'aria-describedby'],\r\n button: [],\r\n checkbox: ['aria-checked'],\r\n combobox: ['aria-expanded', 'aria-controls'],\r\n dialog: ['aria-labelledby', 'aria-describedby'],\r\n grid: [],\r\n gridcell: [],\r\n link: [],\r\n listbox: ['aria-label'],\r\n menu: ['aria-label'],\r\n menubar: [],\r\n menuitem: [],\r\n option: ['aria-selected'],\r\n progressbar: ['aria-valuenow'],\r\n radio: ['aria-checked'],\r\n radiogroup: ['aria-label'],\r\n slider: ['aria-valuenow'],\r\n spinbutton: ['aria-valuenow'],\r\n tab: ['aria-selected'],\r\n tablist: [],\r\n tabpanel: ['aria-labelledby'],\r\n textbox: [],\r\n tree: ['aria-label'],\r\n treeitem: ['aria-selected'],\r\n};\r\n\r\n/** 可聚焦元素选择器 */\r\nconst FOCUSABLE_SELECTOR = [\r\n 'a[href]',\r\n 'area[href]',\r\n 'button:not([disabled])',\r\n 'input:not([disabled])',\r\n 'select:not([disabled])',\r\n 'textarea:not([disabled])',\r\n '[tabindex]:not([tabindex=\"-1\"])',\r\n '[contenteditable=\"true\"]',\r\n].join(', ');\r\n\r\n/**\r\n * 检查元素是否可聚焦\r\n */\r\nexport function isFocusable(element: Element): boolean {\r\n if (!(element instanceof HTMLElement)) return false;\r\n if ('disabled' in element && (element as { disabled: boolean }).disabled) return false;\r\n if (element.getAttribute('tabindex') === '-1') return false;\r\n if (element.getAttribute('aria-hidden') === 'true') return false;\r\n\r\n const tag = element.tagName.toLowerCase();\r\n const focusableTags = new Set([\r\n 'a', 'button', 'input', 'select', 'textarea',\r\n 'details', 'summary',\r\n ]);\r\n\r\n if (focusableTags.has(tag)) return true;\r\n if (element.getAttribute('tabindex') !== null) return true;\r\n if (element.isContentEditable || element.getAttribute('contenteditable') === 'true') return true;\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 获取容器内所有可聚焦元素\r\n */\r\nexport function getFocusableElements(container: Element): HTMLElement[] {\r\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));\r\n return elements.filter(\r\n (el): el is HTMLElement => el instanceof HTMLElement && isFocusable(el),\r\n );\r\n}\r\n\r\n/**\r\n * 在容器内创建焦点陷阱\r\n *\r\n * @param container - 陷阱容器\r\n * @param options - 配置选项\r\n * @returns 清理函数\r\n */\r\nexport function focusTrap(\r\n container: HTMLElement,\r\n options?: FocusTrapOptions,\r\n): () => void {\r\n const { initialFocus, escapeDeactivates = true } = options || {};\r\n\r\n const focusableElements = getFocusableElements(container);\r\n const firstElement = focusableElements[0] || container;\r\n const lastElement = focusableElements[focusableElements.length - 1] || container;\r\n\r\n // 设置初始焦点\r\n if (initialFocus) {\r\n initialFocus.focus();\r\n } else {\r\n firstElement.focus();\r\n }\r\n\r\n const handleKeyDown = (event: KeyboardEvent) => {\r\n if (event.key === 'Escape' && escapeDeactivates) {\r\n cleanup();\r\n return;\r\n }\r\n\r\n if (event.key !== 'Tab') return;\r\n\r\n if (focusableElements.length === 0) {\r\n event.preventDefault();\r\n return;\r\n }\r\n\r\n if (event.shiftKey) {\r\n if (document.activeElement === firstElement) {\r\n event.preventDefault();\r\n lastElement.focus();\r\n }\r\n } else {\r\n if (document.activeElement === lastElement) {\r\n event.preventDefault();\r\n firstElement.focus();\r\n }\r\n }\r\n };\r\n\r\n document.addEventListener('keydown', handleKeyDown);\r\n\r\n const cleanup = () => {\r\n document.removeEventListener('keydown', handleKeyDown);\r\n };\r\n\r\n return cleanup;\r\n}\r\n\r\n/**\r\n * 管理焦点:保存之前的焦点,将焦点移入容器,返回恢复函数\r\n *\r\n * @param container - 目标容器\r\n * @param triggerEl - 触发元素,恢复焦点时优先回到此元素\r\n * @returns 恢复函数\r\n */\r\nexport function manageFocus(\r\n container: HTMLElement,\r\n triggerEl?: HTMLElement,\r\n): () => void {\r\n const previousFocus = document.activeElement as HTMLElement | null;\r\n\r\n const focusableElements = getFocusableElements(container);\r\n if (focusableElements.length > 0) {\r\n focusableElements[0]!.focus();\r\n } else {\r\n container.setAttribute('tabindex', '-1');\r\n container.focus();\r\n }\r\n\r\n return () => {\r\n const target = triggerEl || previousFocus;\r\n if (target && typeof target.focus === 'function') {\r\n target.focus();\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * 获取元素上所有 aria-* 属性\r\n */\r\nexport function getAriaProps(element: Element): Record<string, string> {\r\n const result: Record<string, string> = {};\r\n const attrs = element.attributes;\r\n for (let i = 0; i < attrs.length; i++) {\r\n const attr = attrs[i]!;\r\n if (attr.name.startsWith('aria-')) {\r\n result[attr.name] = attr.value!;\r\n }\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * 批量设置 aria-* 属性\r\n */\r\nexport function setAriaProps(\r\n element: Element,\r\n props: Record<string, string>,\r\n): void {\r\n for (const key of Object.keys(props)) {\r\n if (key.startsWith('aria-')) {\r\n element.setAttribute(key, props[key]!);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查给定元素是否是当前活动元素\r\n */\r\nexport function assertActiveElement(element: Element): boolean {\r\n return document.activeElement === element;\r\n}\r\n\r\n/**\r\n * 键盘导航辅助函数 - 在启用的选项间循环\r\n */\r\nexport function getNextEnabledIndex(\r\n currentIndex: number,\r\n totalItems: number,\r\n isEnabled: (index: number) => boolean,\r\n direction: 'forward' | 'backward' = 'forward'\r\n): number {\r\n const step = direction === 'forward' ? 1 : -1;\r\n let nextIndex = (currentIndex + step + totalItems) % totalItems;\r\n \r\n for (let i = 0; i < totalItems; i++) {\r\n if (isEnabled(nextIndex)) {\r\n return nextIndex;\r\n }\r\n nextIndex = (nextIndex + step + totalItems) % totalItems;\r\n }\r\n \r\n return currentIndex;\r\n}\r\n\r\n/**\r\n * 处理列表组件的键盘导航\r\n */\r\nexport function handleListKeydown(\r\n event: KeyboardEvent,\r\n currentIndex: number,\r\n totalItems: number,\r\n isEnabled: (index: number) => boolean,\r\n onSelect: (index: number) => void,\r\n onClose?: () => void\r\n): void {\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n case 'ArrowRight':\r\n event.preventDefault();\r\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'forward'));\r\n break;\r\n case 'ArrowUp':\r\n case 'ArrowLeft':\r\n event.preventDefault();\r\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'backward'));\r\n break;\r\n case 'Home':\r\n event.preventDefault();\r\n for (let i = 0; i < totalItems; i++) {\r\n if (isEnabled(i)) {\r\n onSelect(i);\r\n break;\r\n }\r\n }\r\n break;\r\n case 'End':\r\n event.preventDefault();\r\n for (let i = totalItems - 1; i >= 0; i--) {\r\n if (isEnabled(i)) {\r\n onSelect(i);\r\n break;\r\n }\r\n }\r\n break;\r\n case 'Enter':\r\n case ' ':\r\n event.preventDefault();\r\n if (isEnabled(currentIndex)) {\r\n onSelect(currentIndex);\r\n }\r\n break;\r\n case 'Escape':\r\n event.preventDefault();\r\n onClose?.();\r\n break;\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @lytjs/common-a11y\n * 轻量级无障碍访问工具\n */\n\ndeclare const __DEV__: boolean;\n\nexport interface FocusTrapOptions {\n initialFocus?: HTMLElement;\n escapeDeactivates?: boolean;\n}\n\n/**\n * 通用无障碍属性接口\n */\nexport interface A11yProps {\n id?: string;\n ariaLabel?: string;\n ariaDescribedBy?: string;\n ariaLabelledBy?: string;\n ariaRequired?: boolean;\n ariaInvalid?: boolean;\n ariaDisabled?: boolean;\n ariaHidden?: boolean;\n ariaExpanded?: boolean;\n ariaChecked?: boolean | 'mixed';\n ariaSelected?: boolean;\n ariaPressed?: boolean;\n ariaHasPopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog';\n ariaControls?: string;\n ariaOwns?: string;\n ariaLive?: 'off' | 'polite' | 'assertive';\n ariaValuenow?: number | string;\n ariaValuemax?: number;\n ariaValuemin?: number;\n ariaModal?: boolean;\n tabIndex?: number;\n role?: string;\n}\n\n/**\n * 生成 tabindex 属性值\n */\nexport function getTabIndex(disabled: boolean, customTabIndex?: number): number | undefined {\n if (customTabIndex !== undefined) return customTabIndex;\n return disabled ? -1 : 0;\n}\n\n/**\n * 为按钮组件生成 a11y 属性\n */\nexport function getButtonA11yProps(\n props: A11yProps & { disabled?: boolean },\n): Record<string, unknown> {\n return {\n role: 'button',\n 'aria-label': props.ariaLabel,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-labelledby': props.ariaLabelledBy,\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\n 'aria-pressed': props.ariaPressed,\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\n id: props.id,\n };\n}\n\n/**\n * 为表单控件生成 a11y 属性\n */\nexport function getFormControlA11yProps(\n props: A11yProps & { disabled?: boolean; required?: boolean; invalid?: boolean },\n): Record<string, unknown> {\n return {\n 'aria-label': props.ariaLabel,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-labelledby': props.ariaLabelledBy,\n 'aria-required': props.ariaRequired ?? props.required,\n 'aria-invalid': props.ariaInvalid ?? props.invalid,\n 'aria-disabled': props.ariaDisabled ?? props.disabled,\n tabindex: getTabIndex(props.disabled ?? !!props.ariaDisabled, props.tabIndex),\n id: props.id,\n };\n}\n\n/**\n * 为复选框/单选框生成 a11y 属性\n */\nexport function getInputControlA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n checked?: boolean | 'mixed';\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n ...getFormControlA11yProps(props),\n 'aria-checked': props.checked,\n };\n}\n\n/**\n * 为开关组件生成 a11y 属性\n */\nexport function getSwitchA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n checked?: boolean;\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n role: 'switch',\n 'aria-checked': props.checked,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为下拉选择组件生成 a11y 属性\n */\nexport function getComboboxA11yProps(\n props: A11yProps & {\n disabled?: boolean;\n expanded?: boolean;\n controls?: string;\n required?: boolean;\n invalid?: boolean;\n },\n): Record<string, unknown> {\n return {\n role: 'combobox',\n 'aria-expanded': props.expanded,\n 'aria-controls': props.ariaControls ?? props.controls,\n 'aria-haspopup': 'listbox',\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为列表框选项生成 a11y 属性\n */\nexport function getOptionA11yProps(\n props: A11yProps & { selected?: boolean; disabled?: boolean },\n): Record<string, unknown> {\n return {\n role: 'option',\n 'aria-selected': props.selected,\n 'aria-disabled': props.disabled,\n tabindex: props.disabled ? -1 : 0,\n id: props.id,\n };\n}\n\n/**\n * 为滑块组件生成 a11y 属性\n */\nexport function getSliderA11yProps(\n props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number },\n): Record<string, unknown> {\n return {\n role: 'slider',\n 'aria-valuenow': props.value,\n 'aria-valuemin': props.min,\n 'aria-valuemax': props.max,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为数字输入组件生成 a11y 属性\n */\nexport function getSpinbuttonA11yProps(\n props: A11yProps & { disabled?: boolean; value?: number | string; min?: number; max?: number },\n): Record<string, unknown> {\n return {\n role: 'spinbutton',\n 'aria-valuenow': props.value,\n 'aria-valuemin': props.min,\n 'aria-valuemax': props.max,\n ...getFormControlA11yProps(props),\n };\n}\n\n/**\n * 为标签页列表生成 a11y 属性\n */\nexport function getTablistA11yProps(\n props: A11yProps & { label?: string },\n): Record<string, unknown> {\n return {\n role: 'tablist',\n 'aria-label': props.ariaLabel ?? props.label,\n 'aria-describedby': props.ariaDescribedBy,\n id: props.id,\n };\n}\n\n/**\n * 为单个标签页生成 a11y 属性\n */\nexport function getTabA11yProps(\n props: A11yProps & { selected?: boolean; disabled?: boolean; controls?: string },\n): Record<string, unknown> {\n return {\n role: 'tab',\n 'aria-selected': props.selected,\n 'aria-disabled': props.disabled,\n 'aria-controls': props.ariaControls ?? props.controls,\n tabindex: props.selected ? 0 : -1,\n id: props.id,\n };\n}\n\n/**\n * 为标签面板生成 a11y 属性\n */\nexport function getTabpanelA11yProps(\n props: A11yProps & { labelledBy?: string; hidden?: boolean },\n): Record<string, unknown> {\n return {\n role: 'tabpanel',\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\n 'aria-hidden': props.hidden,\n id: props.id,\n };\n}\n\n/**\n * 为对话框/模态框生成 a11y 属性\n */\nexport function getDialogA11yProps(\n props: A11yProps & { labelledBy?: string; describedBy?: string; modal?: boolean },\n): Record<string, unknown> {\n return {\n role: 'dialog',\n 'aria-modal': props.ariaModal ?? props.modal ?? true,\n 'aria-labelledby': props.ariaLabelledBy ?? props.labelledBy,\n 'aria-describedby': props.ariaDescribedBy ?? props.describedBy,\n 'aria-label': props.ariaLabel,\n id: props.id,\n };\n}\n\n/**\n * 为分组组件(checkboxGroup/radioGroup)生成 a11y 属性\n */\nexport function getGroupA11yProps(\n props: A11yProps & {\n role?: 'radiogroup' | 'group' | 'listbox';\n required?: boolean;\n label?: string;\n },\n): Record<string, unknown> {\n return {\n role: props.role ?? 'group',\n 'aria-label': props.ariaLabel ?? props.label,\n 'aria-describedby': props.ariaDescribedBy,\n 'aria-required': props.ariaRequired ?? props.required,\n id: props.id,\n };\n}\n\n/**\n * 合并多个 a11y 属性对象\n */\nexport function mergeA11yProps(\n ...propsList: Array<Record<string, unknown>>\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const props of propsList) {\n for (const [key, value] of Object.entries(props)) {\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n }\n // 过滤 undefined 值\n return Object.fromEntries(\n Object.entries(result).filter(([_, v]) => v !== undefined && v !== null),\n );\n}\n\n/** ARIA 角色到必需属性的映射 */\nexport const ARIA_ROLES: Record<string, string[]> = {\n alert: ['aria-live'],\n alertdialog: ['aria-labelledby', 'aria-describedby'],\n button: [],\n checkbox: ['aria-checked'],\n combobox: ['aria-expanded', 'aria-controls'],\n dialog: ['aria-labelledby', 'aria-describedby'],\n grid: [],\n gridcell: [],\n link: [],\n listbox: ['aria-label'],\n menu: ['aria-label'],\n menubar: [],\n menuitem: [],\n option: ['aria-selected'],\n progressbar: ['aria-valuenow'],\n radio: ['aria-checked'],\n radiogroup: ['aria-label'],\n slider: ['aria-valuenow'],\n spinbutton: ['aria-valuenow'],\n tab: ['aria-selected'],\n tablist: [],\n tabpanel: ['aria-labelledby'],\n textbox: [],\n tree: ['aria-label'],\n treeitem: ['aria-selected'],\n};\n\n/** 可聚焦元素选择器 */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\n/**\n * 检查元素是否可聚焦\n */\nexport function isFocusable(element: Element): boolean {\n if (!(element instanceof HTMLElement)) return false;\n if ('disabled' in element && (element as { disabled: boolean }).disabled) return false;\n if (element.getAttribute('tabindex') === '-1') return false;\n if (element.getAttribute('aria-hidden') === 'true') return false;\n\n const tag = element.tagName.toLowerCase();\n const focusableTags = new Set([\n 'a',\n 'button',\n 'input',\n 'select',\n 'textarea',\n 'details',\n 'summary',\n ]);\n\n if (focusableTags.has(tag)) return true;\n if (element.getAttribute('tabindex') !== null) return true;\n if (element.isContentEditable || element.getAttribute('contenteditable') === 'true') return true;\n\n return false;\n}\n\n/**\n * 获取容器内所有可聚焦元素\n */\nexport function getFocusableElements(container: Element): HTMLElement[] {\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));\n return elements.filter((el): el is HTMLElement => el instanceof HTMLElement && isFocusable(el));\n}\n\n/**\n * 在容器内创建焦点陷阱\n *\n * @param container - 陷阱容器\n * @param options - 配置选项\n * @returns 清理函数\n */\nexport function focusTrap(container: HTMLElement, options?: FocusTrapOptions): () => void {\n const { initialFocus, escapeDeactivates = true } = options || {};\n\n const focusableElements = getFocusableElements(container);\n const firstElement = focusableElements[0] || container;\n const lastElement = focusableElements[focusableElements.length - 1] || container;\n\n // 设置初始焦点\n if (initialFocus) {\n initialFocus.focus();\n } else {\n firstElement.focus();\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && escapeDeactivates) {\n cleanup();\n return;\n }\n\n if (event.key !== 'Tab') return;\n\n if (focusableElements.length === 0) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey) {\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n\n const cleanup = () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n\n return cleanup;\n}\n\n/**\n * 管理焦点:保存之前的焦点,将焦点移入容器,返回恢复函数\n *\n * @param container - 目标容器\n * @param triggerEl - 触发元素,恢复焦点时优先回到此元素\n * @returns 恢复函数\n */\nexport function manageFocus(container: HTMLElement, triggerEl?: HTMLElement): () => void {\n const previousFocus = document.activeElement as HTMLElement | null;\n\n const focusableElements = getFocusableElements(container);\n if (focusableElements.length > 0) {\n focusableElements[0]!.focus();\n } else {\n container.setAttribute('tabindex', '-1');\n container.focus();\n }\n\n return () => {\n const target = triggerEl || previousFocus;\n if (target && typeof target.focus === 'function') {\n target.focus();\n }\n };\n}\n\n/**\n * 获取元素上所有 aria-* 属性\n */\nexport function getAriaProps(element: Element): Record<string, string> {\n const result: Record<string, string> = {};\n const attrs = element.attributes;\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i]!;\n if (attr.name.startsWith('aria-')) {\n result[attr.name] = attr.value!;\n }\n }\n return result;\n}\n\n/**\n * 批量设置 aria-* 属性\n */\nexport function setAriaProps(element: Element, props: Record<string, string>): void {\n for (const key of Object.keys(props)) {\n if (key.startsWith('aria-')) {\n element.setAttribute(key, props[key]!);\n }\n }\n}\n\n/**\n * 检查给定元素是否是当前活动元素\n */\nexport function assertActiveElement(element: Element): boolean {\n return document.activeElement === element;\n}\n\n/**\n * 键盘导航辅助函数 - 在启用的选项间循环\n */\nexport function getNextEnabledIndex(\n currentIndex: number,\n totalItems: number,\n isEnabled: (index: number) => boolean,\n direction: 'forward' | 'backward' = 'forward',\n): number {\n const step = direction === 'forward' ? 1 : -1;\n let nextIndex = (currentIndex + step + totalItems) % totalItems;\n\n for (let i = 0; i < totalItems; i++) {\n if (isEnabled(nextIndex)) {\n return nextIndex;\n }\n nextIndex = (nextIndex + step + totalItems) % totalItems;\n }\n\n return currentIndex;\n}\n\n/**\n * 处理列表组件的键盘导航\n */\nexport function handleListKeydown(\n event: KeyboardEvent,\n currentIndex: number,\n totalItems: number,\n isEnabled: (index: number) => boolean,\n onSelect: (index: number) => void,\n onClose?: () => void,\n): void {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight':\n event.preventDefault();\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'forward'));\n break;\n case 'ArrowUp':\n case 'ArrowLeft':\n event.preventDefault();\n onSelect(getNextEnabledIndex(currentIndex, totalItems, isEnabled, 'backward'));\n break;\n case 'Home':\n event.preventDefault();\n for (let i = 0; i < totalItems; i++) {\n if (isEnabled(i)) {\n onSelect(i);\n break;\n }\n }\n break;\n case 'End':\n event.preventDefault();\n for (let i = totalItems - 1; i >= 0; i--) {\n if (isEnabled(i)) {\n onSelect(i);\n break;\n }\n }\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (isEnabled(currentIndex)) {\n onSelect(currentIndex);\n }\n break;\n case 'Escape':\n event.preventDefault();\n onClose?.();\n break;\n }\n}\n"]}
|