@zhencai/vue-focus-scope 1.0.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/index.d.ts +29 -0
- package/index.js +107 -0
- package/package.json +22 -0
- package/vue-focus-scope.css +5 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// index.d.ts
|
|
2
|
+
import { DefineComponent } from "vue";
|
|
3
|
+
|
|
4
|
+
type TabMode = "loop" | "loop-sentinel";
|
|
5
|
+
|
|
6
|
+
interface FocusScopeProps {
|
|
7
|
+
autoFocus?: "first" | "last" | false;
|
|
8
|
+
tabMode?: TabMode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FocusScopeExpose {
|
|
12
|
+
activate: () => void;
|
|
13
|
+
deactivate: () => void;
|
|
14
|
+
focusFirst: () => void;
|
|
15
|
+
focusLast: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare const FocusScope: DefineComponent<
|
|
19
|
+
FocusScopeProps,
|
|
20
|
+
{},
|
|
21
|
+
{},
|
|
22
|
+
{},
|
|
23
|
+
{},
|
|
24
|
+
{},
|
|
25
|
+
{},
|
|
26
|
+
FocusScopeExpose
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
export default FocusScope;
|
package/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createElementBlock, defineComponent, onMounted, onUnmounted, openBlock, ref, renderSlot } from "vue";
|
|
2
|
+
//#region src/components/FocusScope.vue?vue&type=script&setup=true&lang.ts
|
|
3
|
+
var selector = "a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex='-1']),[contenteditable='true']";
|
|
4
|
+
var FocusScope_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
5
|
+
__name: "FocusScope",
|
|
6
|
+
props: {
|
|
7
|
+
autoFocus: {
|
|
8
|
+
type: [String, Boolean],
|
|
9
|
+
default: "first"
|
|
10
|
+
},
|
|
11
|
+
tabMode: { default: "loop" }
|
|
12
|
+
},
|
|
13
|
+
setup(__props) {
|
|
14
|
+
const props = __props;
|
|
15
|
+
const scopeRef = ref(null);
|
|
16
|
+
let active = false;
|
|
17
|
+
let previous = null;
|
|
18
|
+
const getFocusable = () => {
|
|
19
|
+
if (!scopeRef.value) return [];
|
|
20
|
+
return [...scopeRef.value.querySelectorAll(selector)];
|
|
21
|
+
};
|
|
22
|
+
const focusFirst = () => {
|
|
23
|
+
getFocusable()[0]?.focus();
|
|
24
|
+
};
|
|
25
|
+
const focusLast = () => {
|
|
26
|
+
const list = getFocusable();
|
|
27
|
+
list[list.length - 1]?.focus();
|
|
28
|
+
};
|
|
29
|
+
const handleMouseDown = () => {
|
|
30
|
+
scopeRef.value?.focus();
|
|
31
|
+
};
|
|
32
|
+
const handleKeydown = (e) => {
|
|
33
|
+
if (!active) return;
|
|
34
|
+
if (e.key !== "Tab") return;
|
|
35
|
+
if (!scopeRef.value) return;
|
|
36
|
+
const list = getFocusable();
|
|
37
|
+
if (!list.length) return;
|
|
38
|
+
const first = list[0];
|
|
39
|
+
const last = list[list.length - 1];
|
|
40
|
+
const current = document.activeElement;
|
|
41
|
+
if (e.shiftKey) {
|
|
42
|
+
if (current === first) {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
if (props.tabMode === "loop") last.focus();
|
|
45
|
+
else scopeRef.value.focus();
|
|
46
|
+
} else if (current === scopeRef.value) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
last.focus();
|
|
49
|
+
}
|
|
50
|
+
} else if (current === last) {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
if (props.tabMode === "loop") first.focus();
|
|
53
|
+
else scopeRef.value.focus();
|
|
54
|
+
} else if (current === scopeRef.value) {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
first.focus();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const activate = () => {
|
|
60
|
+
if (!scopeRef.value) return;
|
|
61
|
+
active = true;
|
|
62
|
+
previous = document.activeElement;
|
|
63
|
+
if (props.autoFocus === "first") focusFirst();
|
|
64
|
+
if (props.autoFocus === "last") focusLast();
|
|
65
|
+
};
|
|
66
|
+
const deactivate = () => {
|
|
67
|
+
active = false;
|
|
68
|
+
if (previous && document.body.contains(previous)) previous.focus();
|
|
69
|
+
previous = null;
|
|
70
|
+
};
|
|
71
|
+
onMounted(() => {
|
|
72
|
+
document.addEventListener("keydown", handleKeydown);
|
|
73
|
+
activate();
|
|
74
|
+
});
|
|
75
|
+
onUnmounted(() => {
|
|
76
|
+
document.removeEventListener("keydown", handleKeydown);
|
|
77
|
+
deactivate();
|
|
78
|
+
});
|
|
79
|
+
return (_ctx, _cache) => {
|
|
80
|
+
return openBlock(), createElementBlock("div", {
|
|
81
|
+
ref_key: "scopeRef",
|
|
82
|
+
ref: scopeRef,
|
|
83
|
+
tabindex: "-1",
|
|
84
|
+
onMousedown: handleMouseDown,
|
|
85
|
+
"data-focus-scope": ""
|
|
86
|
+
}, [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 544);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region \0plugin-vue:export-helper
|
|
92
|
+
var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
93
|
+
const target = sfc.__vccOpts || sfc;
|
|
94
|
+
for (const [key, val] of props) target[key] = val;
|
|
95
|
+
return target;
|
|
96
|
+
};
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/components/FocusScope.vue
|
|
99
|
+
var FocusScope_default = /* @__PURE__ */ _plugin_vue_export_helper_default(FocusScope_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-5ffda61b"]]);
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/index.ts
|
|
102
|
+
var src_default = FocusScope_default;
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { FocusScope_default as FocusScope, src_default as default };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhencai/vue-focus-scope",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"types": "index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"index.d.ts",
|
|
9
|
+
"vue-focus-scope.css"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"vue",
|
|
13
|
+
"focus",
|
|
14
|
+
"ui",
|
|
15
|
+
"component"
|
|
16
|
+
],
|
|
17
|
+
"author": "严硕",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"vue": "^3.2.0"
|
|
21
|
+
}
|
|
22
|
+
}
|