@rc-component/util
Advanced tools
+11
-7
| export declare function getFocusNodeList(node: HTMLElement, includePositive?: boolean): HTMLElement[]; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function saveLastFocusNode(): void; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function clearLastFocusNode(): void; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function backLastFocusNode(): void; | ||
| export declare function limitTabRange(node: HTMLElement, e: KeyboardEvent): void; | ||
| export interface InputFocusOptions extends FocusOptions { | ||
@@ -16,1 +9,12 @@ cursor?: 'start' | 'end' | 'all'; | ||
| export declare function triggerFocus(element?: HTMLElement, option?: InputFocusOptions): void; | ||
| /** | ||
| * Lock focus in the element. | ||
| * It will force back to the first focusable element when focus leaves the element. | ||
| */ | ||
| export declare function lockFocus(element: HTMLElement): VoidFunction; | ||
| /** | ||
| * Lock focus within an element. | ||
| * When locked, focus will be restricted to focusable elements within the specified element. | ||
| * If multiple elements are locked, only the last locked element will be effective. | ||
| */ | ||
| export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): void; |
+89
-38
@@ -0,1 +1,2 @@ | ||
| import { useEffect } from 'react'; | ||
| import isVisible from "./isVisible"; | ||
@@ -42,40 +43,2 @@ function focusable(node, includePositive = false) { | ||
| } | ||
| let lastFocusElement = null; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export function saveLastFocusNode() { | ||
| lastFocusElement = document.activeElement; | ||
| } | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export function clearLastFocusNode() { | ||
| lastFocusElement = null; | ||
| } | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export function backLastFocusNode() { | ||
| if (lastFocusElement) { | ||
| try { | ||
| // 元素可能已经被移动了 | ||
| lastFocusElement.focus(); | ||
| /* eslint-disable no-empty */ | ||
| } catch (e) { | ||
| // empty | ||
| } | ||
| /* eslint-enable no-empty */ | ||
| } | ||
| } | ||
| export function limitTabRange(node, e) { | ||
| if (e.keyCode === 9) { | ||
| const tabNodeList = getFocusNodeList(node); | ||
| const lastTabNode = tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1]; | ||
| const leavingTab = lastTabNode === document.activeElement || node === document.activeElement; | ||
| if (leavingTab) { | ||
| const target = tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0]; | ||
| target.focus(); | ||
| e.preventDefault(); | ||
| } | ||
| } | ||
| } | ||
| // Used for `rc-input` `rc-textarea` `rc-input-number` | ||
@@ -106,2 +69,90 @@ /** | ||
| } | ||
| } | ||
| // ====================================================== | ||
| // == Lock Focus == | ||
| // ====================================================== | ||
| let lastFocusElement = null; | ||
| let focusElements = []; | ||
| function getLastElement() { | ||
| return focusElements[focusElements.length - 1]; | ||
| } | ||
| function hasFocus(element) { | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| return element === activeElement || element.contains(activeElement); | ||
| } | ||
| function syncFocus() { | ||
| const lastElement = getLastElement(); | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| if (lastElement && !hasFocus(lastElement)) { | ||
| const focusableList = getFocusNodeList(lastElement); | ||
| const matchElement = focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0]; | ||
| matchElement?.focus(); | ||
| } else { | ||
| lastFocusElement = activeElement; | ||
| } | ||
| } | ||
| function onWindowKeyDown(e) { | ||
| if (e.key === 'Tab') { | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| const lastElement = getLastElement(); | ||
| const focusableList = getFocusNodeList(lastElement); | ||
| const last = focusableList[focusableList.length - 1]; | ||
| if (e.shiftKey && activeElement === focusableList[0]) { | ||
| // Tab backward on first focusable element | ||
| lastFocusElement = last; | ||
| } else if (!e.shiftKey && activeElement === last) { | ||
| // Tab forward on last focusable element | ||
| lastFocusElement = focusableList[0]; | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Lock focus in the element. | ||
| * It will force back to the first focusable element when focus leaves the element. | ||
| */ | ||
| export function lockFocus(element) { | ||
| if (element) { | ||
| // Refresh focus elements | ||
| focusElements = focusElements.filter(ele => ele !== element); | ||
| focusElements.push(element); | ||
| // Just add event since it will de-duplicate | ||
| window.addEventListener('focusin', syncFocus); | ||
| window.addEventListener('keydown', onWindowKeyDown, true); | ||
| syncFocus(); | ||
| } | ||
| // Always return unregister function | ||
| return () => { | ||
| lastFocusElement = null; | ||
| focusElements = focusElements.filter(ele => ele !== element); | ||
| if (focusElements.length === 0) { | ||
| window.removeEventListener('focusin', syncFocus); | ||
| window.removeEventListener('keydown', onWindowKeyDown, true); | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Lock focus within an element. | ||
| * When locked, focus will be restricted to focusable elements within the specified element. | ||
| * If multiple elements are locked, only the last locked element will be effective. | ||
| */ | ||
| export function useLockFocus(lock, getElement) { | ||
| useEffect(() => { | ||
| if (lock) { | ||
| const element = getElement(); | ||
| if (element) { | ||
| return lockFocus(element); | ||
| } | ||
| } | ||
| }, [lock]); | ||
| } |
@@ -1,7 +0,5 @@ | ||
| type ScrollBarSize = { | ||
| export default function getScrollBarSize(fresh?: boolean): number; | ||
| export declare function getTargetScrollBarSize(target: HTMLElement): { | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| export default function getScrollBarSize(fresh?: boolean): number; | ||
| export declare function getTargetScrollBarSize(target: HTMLElement): ScrollBarSize; | ||
| export {}; |
+11
-7
| export declare function getFocusNodeList(node: HTMLElement, includePositive?: boolean): HTMLElement[]; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function saveLastFocusNode(): void; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function clearLastFocusNode(): void; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| export declare function backLastFocusNode(): void; | ||
| export declare function limitTabRange(node: HTMLElement, e: KeyboardEvent): void; | ||
| export interface InputFocusOptions extends FocusOptions { | ||
@@ -16,1 +9,12 @@ cursor?: 'start' | 'end' | 'all'; | ||
| export declare function triggerFocus(element?: HTMLElement, option?: InputFocusOptions): void; | ||
| /** | ||
| * Lock focus in the element. | ||
| * It will force back to the first focusable element when focus leaves the element. | ||
| */ | ||
| export declare function lockFocus(element: HTMLElement): VoidFunction; | ||
| /** | ||
| * Lock focus within an element. | ||
| * When locked, focus will be restricted to focusable elements within the specified element. | ||
| * If multiple elements are locked, only the last locked element will be effective. | ||
| */ | ||
| export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): void; |
+91
-42
@@ -6,8 +6,7 @@ "use strict"; | ||
| }); | ||
| exports.backLastFocusNode = backLastFocusNode; | ||
| exports.clearLastFocusNode = clearLastFocusNode; | ||
| exports.getFocusNodeList = getFocusNodeList; | ||
| exports.limitTabRange = limitTabRange; | ||
| exports.saveLastFocusNode = saveLastFocusNode; | ||
| exports.lockFocus = lockFocus; | ||
| exports.triggerFocus = triggerFocus; | ||
| exports.useLockFocus = useLockFocus; | ||
| var _react = require("react"); | ||
| var _isVisible = _interopRequireDefault(require("./isVisible")); | ||
@@ -55,40 +54,2 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| } | ||
| let lastFocusElement = null; | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| function saveLastFocusNode() { | ||
| lastFocusElement = document.activeElement; | ||
| } | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| function clearLastFocusNode() { | ||
| lastFocusElement = null; | ||
| } | ||
| /** @deprecated Do not use since this may failed when used in async */ | ||
| function backLastFocusNode() { | ||
| if (lastFocusElement) { | ||
| try { | ||
| // 元素可能已经被移动了 | ||
| lastFocusElement.focus(); | ||
| /* eslint-disable no-empty */ | ||
| } catch (e) { | ||
| // empty | ||
| } | ||
| /* eslint-enable no-empty */ | ||
| } | ||
| } | ||
| function limitTabRange(node, e) { | ||
| if (e.keyCode === 9) { | ||
| const tabNodeList = getFocusNodeList(node); | ||
| const lastTabNode = tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1]; | ||
| const leavingTab = lastTabNode === document.activeElement || node === document.activeElement; | ||
| if (leavingTab) { | ||
| const target = tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0]; | ||
| target.focus(); | ||
| e.preventDefault(); | ||
| } | ||
| } | ||
| } | ||
| // Used for `rc-input` `rc-textarea` `rc-input-number` | ||
@@ -119,2 +80,90 @@ /** | ||
| } | ||
| } | ||
| // ====================================================== | ||
| // == Lock Focus == | ||
| // ====================================================== | ||
| let lastFocusElement = null; | ||
| let focusElements = []; | ||
| function getLastElement() { | ||
| return focusElements[focusElements.length - 1]; | ||
| } | ||
| function hasFocus(element) { | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| return element === activeElement || element.contains(activeElement); | ||
| } | ||
| function syncFocus() { | ||
| const lastElement = getLastElement(); | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| if (lastElement && !hasFocus(lastElement)) { | ||
| const focusableList = getFocusNodeList(lastElement); | ||
| const matchElement = focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0]; | ||
| matchElement?.focus(); | ||
| } else { | ||
| lastFocusElement = activeElement; | ||
| } | ||
| } | ||
| function onWindowKeyDown(e) { | ||
| if (e.key === 'Tab') { | ||
| const { | ||
| activeElement | ||
| } = document; | ||
| const lastElement = getLastElement(); | ||
| const focusableList = getFocusNodeList(lastElement); | ||
| const last = focusableList[focusableList.length - 1]; | ||
| if (e.shiftKey && activeElement === focusableList[0]) { | ||
| // Tab backward on first focusable element | ||
| lastFocusElement = last; | ||
| } else if (!e.shiftKey && activeElement === last) { | ||
| // Tab forward on last focusable element | ||
| lastFocusElement = focusableList[0]; | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Lock focus in the element. | ||
| * It will force back to the first focusable element when focus leaves the element. | ||
| */ | ||
| function lockFocus(element) { | ||
| if (element) { | ||
| // Refresh focus elements | ||
| focusElements = focusElements.filter(ele => ele !== element); | ||
| focusElements.push(element); | ||
| // Just add event since it will de-duplicate | ||
| window.addEventListener('focusin', syncFocus); | ||
| window.addEventListener('keydown', onWindowKeyDown, true); | ||
| syncFocus(); | ||
| } | ||
| // Always return unregister function | ||
| return () => { | ||
| lastFocusElement = null; | ||
| focusElements = focusElements.filter(ele => ele !== element); | ||
| if (focusElements.length === 0) { | ||
| window.removeEventListener('focusin', syncFocus); | ||
| window.removeEventListener('keydown', onWindowKeyDown, true); | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Lock focus within an element. | ||
| * When locked, focus will be restricted to focusable elements within the specified element. | ||
| * If multiple elements are locked, only the last locked element will be effective. | ||
| */ | ||
| function useLockFocus(lock, getElement) { | ||
| (0, _react.useEffect)(() => { | ||
| if (lock) { | ||
| const element = getElement(); | ||
| if (element) { | ||
| return lockFocus(element); | ||
| } | ||
| } | ||
| }, [lock]); | ||
| } |
@@ -1,7 +0,5 @@ | ||
| type ScrollBarSize = { | ||
| export default function getScrollBarSize(fresh?: boolean): number; | ||
| export declare function getTargetScrollBarSize(target: HTMLElement): { | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| export default function getScrollBarSize(fresh?: boolean): number; | ||
| export declare function getTargetScrollBarSize(target: HTMLElement): ScrollBarSize; | ||
| export {}; |
+1
-1
| { | ||
| "name": "@rc-component/util", | ||
| "version": "1.5.0", | ||
| "version": "1.6.0", | ||
| "description": "Common Utils For React Component", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
187135
1.69%6207
1.67%