@v-c/util 1.0.7 → 1.0.8

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.
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_isVisible = require("./isVisible.cjs");
3
+ let vue = require("vue");
3
4
  function focusable(node, includePositive = false) {
4
5
  if (require_isVisible.default(node)) {
5
6
  const nodeName = node.nodeName.toLowerCase();
@@ -26,27 +27,6 @@ function getFocusNodeList(node, includePositive = false) {
26
27
  if (focusable(node, includePositive)) res.unshift(node);
27
28
  return res;
28
29
  }
29
- var lastFocusElement = null;
30
- function saveLastFocusNode() {
31
- lastFocusElement = document.activeElement;
32
- }
33
- function clearLastFocusNode() {
34
- lastFocusElement = null;
35
- }
36
- function backLastFocusNode() {
37
- if (lastFocusElement) try {
38
- lastFocusElement.focus();
39
- } catch (_e) {}
40
- }
41
- function limitTabRange(node, e) {
42
- if (e.keyCode === 9) {
43
- const tabNodeList = getFocusNodeList(node);
44
- if (tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1] === document.activeElement || node === document.activeElement) {
45
- tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0].focus();
46
- e.preventDefault();
47
- }
48
- }
49
- }
50
30
  function triggerFocus(element, option) {
51
31
  if (!element) return;
52
32
  element.focus(option);
@@ -64,9 +44,58 @@ function triggerFocus(element, option) {
64
44
  }
65
45
  }
66
46
  }
67
- exports.backLastFocusNode = backLastFocusNode;
68
- exports.clearLastFocusNode = clearLastFocusNode;
47
+ var lastFocusElement = null;
48
+ var focusElements = [];
49
+ function getLastElement() {
50
+ return focusElements[focusElements.length - 1];
51
+ }
52
+ function hasFocus(element) {
53
+ const { activeElement } = document;
54
+ return element === activeElement || element.contains(activeElement);
55
+ }
56
+ function syncFocus() {
57
+ const lastElement = getLastElement();
58
+ const { activeElement } = document;
59
+ if (lastElement && !hasFocus(lastElement)) {
60
+ const focusableList = getFocusNodeList(lastElement);
61
+ (focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0])?.focus();
62
+ } else lastFocusElement = activeElement;
63
+ }
64
+ function onWindowKeyDown(e) {
65
+ if (e.key === "Tab") {
66
+ const { activeElement } = document;
67
+ const focusableList = getFocusNodeList(getLastElement());
68
+ const last = focusableList[focusableList.length - 1];
69
+ if (e.shiftKey && activeElement === focusableList[0]) lastFocusElement = last;
70
+ else if (!e.shiftKey && activeElement === last) lastFocusElement = focusableList[0];
71
+ }
72
+ }
73
+ function lockFocus(element) {
74
+ if (element) {
75
+ focusElements = focusElements.filter((ele) => ele !== element);
76
+ focusElements.push(element);
77
+ window.addEventListener("focusin", syncFocus);
78
+ window.addEventListener("keydown", onWindowKeyDown, true);
79
+ syncFocus();
80
+ }
81
+ return () => {
82
+ lastFocusElement = null;
83
+ focusElements = focusElements.filter((ele) => ele !== element);
84
+ if (focusElements.length === 0) {
85
+ window.removeEventListener("focusin", syncFocus);
86
+ window.removeEventListener("keydown", onWindowKeyDown, true);
87
+ }
88
+ };
89
+ }
90
+ function useLockFocus(lock, getElement) {
91
+ (0, vue.watch)(lock, (_, _o, onCleanup) => {
92
+ if (lock.value) {
93
+ const element = getElement();
94
+ if (element) onCleanup(lockFocus(element));
95
+ }
96
+ });
97
+ }
69
98
  exports.getFocusNodeList = getFocusNodeList;
70
- exports.limitTabRange = limitTabRange;
71
- exports.saveLastFocusNode = saveLastFocusNode;
99
+ exports.lockFocus = lockFocus;
72
100
  exports.triggerFocus = triggerFocus;
101
+ exports.useLockFocus = useLockFocus;
@@ -1,12 +1,17 @@
1
+ import { Ref } from 'vue';
1
2
  export declare function getFocusNodeList(node: HTMLElement, includePositive?: boolean): HTMLElement[];
2
- /** @deprecated Do not use since this may failed when used in async */
3
- export declare function saveLastFocusNode(): void;
4
- /** @deprecated Do not use since this may failed when used in async */
5
- export declare function clearLastFocusNode(): void;
6
- /** @deprecated Do not use since this may failed when used in async */
7
- export declare function backLastFocusNode(): void;
8
- export declare function limitTabRange(node: HTMLElement, e: KeyboardEvent): void;
9
3
  export interface InputFocusOptions extends FocusOptions {
10
4
  cursor?: 'start' | 'end' | 'all';
11
5
  }
12
6
  export declare function triggerFocus(element?: HTMLInputElement | HTMLTextAreaElement, option?: InputFocusOptions): void;
7
+ /**
8
+ * Lock focus in the element.
9
+ * It will force back to the first focusable element when focus leaves the element.
10
+ */
11
+ export declare function lockFocus(element: HTMLElement): VoidFunction;
12
+ /**
13
+ * Lock focus within an element.
14
+ * When locked, focus will be restricted to focusable elements within the specified element.
15
+ * If multiple elements are locked, only the last locked element will be effective.
16
+ */
17
+ export declare function useLockFocus(lock: Ref<boolean>, getElement: () => HTMLElement | null): void;
package/dist/Dom/focus.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import isVisible_default from "./isVisible.js";
2
+ import { watch } from "vue";
2
3
  function focusable(node, includePositive = false) {
3
4
  if (isVisible_default(node)) {
4
5
  const nodeName = node.nodeName.toLowerCase();
@@ -25,27 +26,6 @@ function getFocusNodeList(node, includePositive = false) {
25
26
  if (focusable(node, includePositive)) res.unshift(node);
26
27
  return res;
27
28
  }
28
- var lastFocusElement = null;
29
- function saveLastFocusNode() {
30
- lastFocusElement = document.activeElement;
31
- }
32
- function clearLastFocusNode() {
33
- lastFocusElement = null;
34
- }
35
- function backLastFocusNode() {
36
- if (lastFocusElement) try {
37
- lastFocusElement.focus();
38
- } catch (_e) {}
39
- }
40
- function limitTabRange(node, e) {
41
- if (e.keyCode === 9) {
42
- const tabNodeList = getFocusNodeList(node);
43
- if (tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1] === document.activeElement || node === document.activeElement) {
44
- tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0].focus();
45
- e.preventDefault();
46
- }
47
- }
48
- }
49
29
  function triggerFocus(element, option) {
50
30
  if (!element) return;
51
31
  element.focus(option);
@@ -63,4 +43,55 @@ function triggerFocus(element, option) {
63
43
  }
64
44
  }
65
45
  }
66
- export { backLastFocusNode, clearLastFocusNode, getFocusNodeList, limitTabRange, saveLastFocusNode, triggerFocus };
46
+ var lastFocusElement = null;
47
+ var focusElements = [];
48
+ function getLastElement() {
49
+ return focusElements[focusElements.length - 1];
50
+ }
51
+ function hasFocus(element) {
52
+ const { activeElement } = document;
53
+ return element === activeElement || element.contains(activeElement);
54
+ }
55
+ function syncFocus() {
56
+ const lastElement = getLastElement();
57
+ const { activeElement } = document;
58
+ if (lastElement && !hasFocus(lastElement)) {
59
+ const focusableList = getFocusNodeList(lastElement);
60
+ (focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0])?.focus();
61
+ } else lastFocusElement = activeElement;
62
+ }
63
+ function onWindowKeyDown(e) {
64
+ if (e.key === "Tab") {
65
+ const { activeElement } = document;
66
+ const focusableList = getFocusNodeList(getLastElement());
67
+ const last = focusableList[focusableList.length - 1];
68
+ if (e.shiftKey && activeElement === focusableList[0]) lastFocusElement = last;
69
+ else if (!e.shiftKey && activeElement === last) lastFocusElement = focusableList[0];
70
+ }
71
+ }
72
+ function lockFocus(element) {
73
+ if (element) {
74
+ focusElements = focusElements.filter((ele) => ele !== element);
75
+ focusElements.push(element);
76
+ window.addEventListener("focusin", syncFocus);
77
+ window.addEventListener("keydown", onWindowKeyDown, true);
78
+ syncFocus();
79
+ }
80
+ return () => {
81
+ lastFocusElement = null;
82
+ focusElements = focusElements.filter((ele) => ele !== element);
83
+ if (focusElements.length === 0) {
84
+ window.removeEventListener("focusin", syncFocus);
85
+ window.removeEventListener("keydown", onWindowKeyDown, true);
86
+ }
87
+ };
88
+ }
89
+ function useLockFocus(lock, getElement) {
90
+ watch(lock, (_, _o, onCleanup) => {
91
+ if (lock.value) {
92
+ const element = getElement();
93
+ if (element) onCleanup(lockFocus(element));
94
+ }
95
+ });
96
+ }
97
+ export { getFocusNodeList, lockFocus, triggerFocus, useLockFocus };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@v-c/util",
3
3
  "type": "module",
4
- "version": "1.0.7",
4
+ "version": "1.0.8",
5
5
  "description": "Vue3 components utils",
6
6
  "publishConfig": {
7
7
  "access": "public"