@zhencai/vue-focus-scope 1.0.3 → 1.0.5
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/index.d.ts +8 -0
- package/index.js +3 -5
- package/package.json +1 -1
- package/readme.md +107 -0
package/index.d.ts
ADDED
package/index.js
CHANGED
|
@@ -9,23 +9,21 @@ var s = {
|
|
|
9
9
|
setup(r) {
|
|
10
10
|
let c = o("wrapper");
|
|
11
11
|
function l(e) {
|
|
12
|
-
|
|
13
|
-
let t = Array.from(e.querySelectorAll("a[href]:not([tabindex=\"-1\"]):not(.sentinel-start):not(.sentinel-end), button:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), input:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), select:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), textarea:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), [tabindex]:not([tabindex=\"-1\"]):not(.sentinel-start):not(.sentinel-end)")).filter((e) => {
|
|
12
|
+
return e ? Array.from(e.querySelectorAll("a[href]:not([tabindex=\"-1\"]):not(.sentinel-start):not(.sentinel-end), button:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), input:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), select:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), textarea:not([tabindex=\"-1\"]):not([disabled]):not(.sentinel-start):not(.sentinel-end), [tabindex]:not([tabindex=\"-1\"]):not(.sentinel-start):not(.sentinel-end)")).filter((e) => {
|
|
14
13
|
let t = e.parentElement;
|
|
15
14
|
for (; t;) {
|
|
16
15
|
if (t.tagName === "FIELDSET" && t.disabled) return !1;
|
|
17
16
|
t = t.parentElement;
|
|
18
17
|
}
|
|
19
18
|
return !0;
|
|
20
|
-
});
|
|
21
|
-
return console.log("当前可聚焦元素:", t), t;
|
|
19
|
+
}) : [];
|
|
22
20
|
}
|
|
23
21
|
function u() {
|
|
24
22
|
try {
|
|
25
23
|
if (c.value) {
|
|
26
24
|
let e = l(c.value);
|
|
27
25
|
if (e.length === 0) return;
|
|
28
|
-
|
|
26
|
+
e[0].focus();
|
|
29
27
|
}
|
|
30
28
|
} catch (e) {
|
|
31
29
|
console.error("focusFirst error:", e);
|
package/package.json
CHANGED
package/readme.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# 焦点管理组件
|
|
2
|
+
|
|
3
|
+
一个基于 Vue 3 的焦点循环管理组件。它通过"哨兵节点"(Sentinel Nodes)技术,在指定的 DOM 作用域内实现键盘 Tab 键的无缝循环导航。
|
|
4
|
+
|
|
5
|
+
适用于模态框(Modal)、下拉菜单、自定义表单或任何需要限制用户焦点范围的场景,符合 WAI-ARIA 无障碍最佳实践。
|
|
6
|
+
|
|
7
|
+
## 功能特性
|
|
8
|
+
|
|
9
|
+
- **自动循环导航**
|
|
10
|
+
- 按 `Tab` 到达最后一个元素时,自动跳回第一个
|
|
11
|
+
- 按 `Shift + Tab` 到达第一个元素时,自动跳到最后一个
|
|
12
|
+
|
|
13
|
+
- **智能作用域限制**
|
|
14
|
+
- 自动过滤不可聚焦元素(`tabindex="-1"`, `disabled`)
|
|
15
|
+
- 自动忽略被 `<fieldset disabled>` 包裹的元素
|
|
16
|
+
- 排除内部哨兵节点,防止干扰正常流程
|
|
17
|
+
|
|
18
|
+
- **双模式支持**
|
|
19
|
+
- **`loop` (默认)**:强循环模式。焦点被严格限制在组件内部,无法通过 Tab 键移出
|
|
20
|
+
- **`soft-loop`**:软循环模式。在首尾之间提供缓冲,允许特定的交互逻辑(防止单元素死循环)
|
|
21
|
+
|
|
22
|
+
- **无障碍友好**:原生支持键盘导航,无需额外配置
|
|
23
|
+
|
|
24
|
+
## 安装与使用
|
|
25
|
+
|
|
26
|
+
```vue
|
|
27
|
+
npm install @zhencai/vue-focus-scope
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 基础用法
|
|
31
|
+
|
|
32
|
+
```vue
|
|
33
|
+
<template>
|
|
34
|
+
<FocusScope>
|
|
35
|
+
<h3>用户登录</h3>
|
|
36
|
+
<input type="text" placeholder="用户名" />
|
|
37
|
+
<input type="password" placeholder="密码" />
|
|
38
|
+
<button @click="submit">登录</button>
|
|
39
|
+
<a href="/forgot">忘记密码?</a>
|
|
40
|
+
</FocusScope>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<script setup lang="ts">
|
|
44
|
+
import FocusScope from "@zhencai/vue-focus-scope";
|
|
45
|
+
</script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 模式切换
|
|
49
|
+
|
|
50
|
+
通过 tabMode 属性控制循环行为:
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<template>
|
|
54
|
+
<!-- 强循环:焦点永远无法逃出此区域 -->
|
|
55
|
+
<FocusScope tab-mode="loop">
|
|
56
|
+
<!-- 内容 -->
|
|
57
|
+
</FocusScope>
|
|
58
|
+
|
|
59
|
+
<!-- 软循环:适合只有一个输入框或需要特殊逃逸逻辑的场景 -->
|
|
60
|
+
<FocusScope tab-mode="soft-loop">
|
|
61
|
+
<input type="text" placeholder="唯一输入框" />
|
|
62
|
+
</FocusScope>
|
|
63
|
+
</template>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API 文档
|
|
67
|
+
|
|
68
|
+
### Props
|
|
69
|
+
|
|
70
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
71
|
+
| :-------- | :---------------------- | :------- | :-------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `tabMode` | `'loop' \| 'soft-loop'` | `'loop'` | 循环模式:<br>- `loop`: 严格限制焦点在插槽内<br>- `soft-loop`: 允许一定程度的外部交互,主要用于防止单元素死循环 |
|
|
73
|
+
|
|
74
|
+
### Slots
|
|
75
|
+
|
|
76
|
+
| 名称 | 说明 |
|
|
77
|
+
| :-------- | :------------------------------------------------------- |
|
|
78
|
+
| `default` | 放置需要被管理焦点的子元素(如 input, button, a 标签等) |
|
|
79
|
+
|
|
80
|
+
## 工作原理
|
|
81
|
+
|
|
82
|
+
该组件利用 **Focus Sentinel(焦点哨兵)** 技术:
|
|
83
|
+
|
|
84
|
+
### 起始哨兵 (sentinel-start)
|
|
85
|
+
|
|
86
|
+
- 位于插槽内容之前
|
|
87
|
+
- 当用户按 `Shift + Tab` 从第一个元素回退时,焦点会落在此处
|
|
88
|
+
- 组件拦截该事件,立即将焦点重定向到最后一个可聚焦元素
|
|
89
|
+
|
|
90
|
+
### 结束哨兵 (sentinel-end)
|
|
91
|
+
|
|
92
|
+
- 位于插槽内容之后
|
|
93
|
+
- 当用户按 `Tab` 从最后一个元素前进时,焦点会落在此处
|
|
94
|
+
- 组件拦截该事件,立即将焦点重定向到第一个可聚焦元素
|
|
95
|
+
|
|
96
|
+
### 可聚焦元素检测
|
|
97
|
+
|
|
98
|
+
- 内部使用 `querySelectorAll` 动态计算当前作用域内的有效焦点元素
|
|
99
|
+
- 自动排除 `tabindex="-1"`、`disabled` 状态以及被禁用的 `fieldset`
|
|
100
|
+
|
|
101
|
+
## 注意事项
|
|
102
|
+
|
|
103
|
+
- **子元素要求**:确保插槽内的元素是原生的可聚焦元素(如 `<input>`, `<button>`, `<a href>`)或具有有效的 `tabindex`
|
|
104
|
+
|
|
105
|
+
## 许可证
|
|
106
|
+
|
|
107
|
+
MIT
|