1
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
export default function canUseDom(): boolean;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export default function canUseDom() {
|
||||
return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export default function contains(root: Node | null | undefined, n?: Node): boolean;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
export default function contains(root, n) {
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use native if support
|
||||
if (root.contains) {
|
||||
return root.contains(n);
|
||||
}
|
||||
|
||||
// `document.contains` not support with IE11
|
||||
let node = n;
|
||||
while (node) {
|
||||
if (node === root) {
|
||||
return true;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
export type ContainerType = Element | ShadowRoot;
|
||||
export type Prepend = boolean | 'queue';
|
||||
export type AppendType = 'prependQueue' | 'append' | 'prepend';
|
||||
interface Options {
|
||||
attachTo?: ContainerType;
|
||||
csp?: {
|
||||
nonce?: string;
|
||||
};
|
||||
prepend?: Prepend;
|
||||
/**
|
||||
* Config the `priority` of `prependQueue`. Default is `0`.
|
||||
* It's useful if you need to insert style before other style.
|
||||
*/
|
||||
priority?: number;
|
||||
mark?: string;
|
||||
styles?: HTMLElement[];
|
||||
}
|
||||
export declare function injectCSS(css: string, option?: Options): HTMLStyleElement;
|
||||
export declare function removeCSS(key: string, option?: Options): void;
|
||||
/**
|
||||
* manually clear container cache to avoid global cache in unit testes
|
||||
*/
|
||||
export declare function clearContainerCache(): void;
|
||||
export declare function updateCSS(css: string, key: string, originOption?: Options): HTMLElement;
|
||||
export {};
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import canUseDom from "./canUseDom";
|
||||
import contains from "./contains";
|
||||
const APPEND_ORDER = 'data-rc-order';
|
||||
const APPEND_PRIORITY = 'data-rc-priority';
|
||||
const MARK_KEY = `rc-util-key`;
|
||||
const containerCache = new Map();
|
||||
function getMark({
|
||||
mark
|
||||
} = {}) {
|
||||
if (mark) {
|
||||
return mark.startsWith('data-') ? mark : `data-${mark}`;
|
||||
}
|
||||
return MARK_KEY;
|
||||
}
|
||||
function getContainer(option) {
|
||||
if (option.attachTo) {
|
||||
return option.attachTo;
|
||||
}
|
||||
const head = document.querySelector('head');
|
||||
return head || document.body;
|
||||
}
|
||||
function getOrder(prepend) {
|
||||
if (prepend === 'queue') {
|
||||
return 'prependQueue';
|
||||
}
|
||||
return prepend ? 'prepend' : 'append';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find style which inject by rc-util
|
||||
*/
|
||||
function findStyles(container) {
|
||||
return Array.from((containerCache.get(container) || container).children).filter(node => node.tagName === 'STYLE');
|
||||
}
|
||||
export function injectCSS(css, option = {}) {
|
||||
if (!canUseDom()) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
csp,
|
||||
prepend,
|
||||
priority = 0
|
||||
} = option;
|
||||
const mergedOrder = getOrder(prepend);
|
||||
const isPrependQueue = mergedOrder === 'prependQueue';
|
||||
const styleNode = document.createElement('style');
|
||||
styleNode.setAttribute(APPEND_ORDER, mergedOrder);
|
||||
if (isPrependQueue && priority) {
|
||||
styleNode.setAttribute(APPEND_PRIORITY, `${priority}`);
|
||||
}
|
||||
if (csp?.nonce) {
|
||||
styleNode.nonce = csp?.nonce;
|
||||
}
|
||||
styleNode.innerHTML = css;
|
||||
const container = getContainer(option);
|
||||
const {
|
||||
firstChild
|
||||
} = container;
|
||||
if (prepend) {
|
||||
// If is queue `prepend`, it will prepend first style and then append rest style
|
||||
if (isPrependQueue) {
|
||||
const existStyle = (option.styles || findStyles(container)).filter(node => {
|
||||
// Ignore style which not injected by rc-util with prepend
|
||||
if (!['prepend', 'prependQueue'].includes(node.getAttribute(APPEND_ORDER))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore style which priority less then new style
|
||||
const nodePriority = Number(node.getAttribute(APPEND_PRIORITY) || 0);
|
||||
return priority >= nodePriority;
|
||||
});
|
||||
if (existStyle.length) {
|
||||
container.insertBefore(styleNode, existStyle[existStyle.length - 1].nextSibling);
|
||||
return styleNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Use `insertBefore` as `prepend`
|
||||
container.insertBefore(styleNode, firstChild);
|
||||
} else {
|
||||
container.appendChild(styleNode);
|
||||
}
|
||||
return styleNode;
|
||||
}
|
||||
function findExistNode(key, option = {}) {
|
||||
let {
|
||||
styles
|
||||
} = option;
|
||||
styles ||= findStyles(getContainer(option));
|
||||
return styles.find(node => node.getAttribute(getMark(option)) === key);
|
||||
}
|
||||
export function removeCSS(key, option = {}) {
|
||||
const existNode = findExistNode(key, option);
|
||||
if (existNode) {
|
||||
const container = getContainer(option);
|
||||
container.removeChild(existNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qiankun will inject `appendChild` to insert into other
|
||||
*/
|
||||
function syncRealContainer(container, option) {
|
||||
const cachedRealContainer = containerCache.get(container);
|
||||
|
||||
// Find real container when not cached or cached container removed
|
||||
if (!cachedRealContainer || !contains(document, cachedRealContainer)) {
|
||||
const placeholderStyle = injectCSS('', option);
|
||||
const {
|
||||
parentNode
|
||||
} = placeholderStyle;
|
||||
containerCache.set(container, parentNode);
|
||||
container.removeChild(placeholderStyle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* manually clear container cache to avoid global cache in unit testes
|
||||
*/
|
||||
export function clearContainerCache() {
|
||||
containerCache.clear();
|
||||
}
|
||||
export function updateCSS(css, key, originOption = {}) {
|
||||
const container = getContainer(originOption);
|
||||
const styles = findStyles(container);
|
||||
const option = {
|
||||
...originOption,
|
||||
styles
|
||||
};
|
||||
|
||||
// Sync real parent
|
||||
syncRealContainer(container, option);
|
||||
const existNode = findExistNode(key, option);
|
||||
if (existNode) {
|
||||
if (option.csp?.nonce && existNode.nonce !== option.csp?.nonce) {
|
||||
existNode.nonce = option.csp?.nonce;
|
||||
}
|
||||
if (existNode.innerHTML !== css) {
|
||||
existNode.innerHTML = css;
|
||||
}
|
||||
return existNode;
|
||||
}
|
||||
const newNode = injectCSS(css, option);
|
||||
newNode.setAttribute(getMark(option), key);
|
||||
return newNode;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
export declare function isDOM(node: any): node is HTMLElement | SVGElement;
|
||||
/**
|
||||
* Retrieves a DOM node via a ref, and does not invoke `findDOMNode`.
|
||||
*/
|
||||
export declare function getDOM(node: any): HTMLElement | SVGElement | null;
|
||||
/**
|
||||
* Return if a node is a DOM node. Else will return by `findDOMNode`
|
||||
*/
|
||||
export default function findDOMNode<T = Element | Text>(node: React.ReactInstance | HTMLElement | SVGElement | {
|
||||
nativeElement: T;
|
||||
} | {
|
||||
current: T;
|
||||
}): T | null;
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
export function isDOM(node) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element
|
||||
// Since XULElement is also subclass of Element, we only need HTMLElement and SVGElement
|
||||
return node instanceof HTMLElement || node instanceof SVGElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a DOM node via a ref, and does not invoke `findDOMNode`.
|
||||
*/
|
||||
export function getDOM(node) {
|
||||
if (node && typeof node === 'object' && isDOM(node.nativeElement)) {
|
||||
return node.nativeElement;
|
||||
}
|
||||
if (isDOM(node)) {
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if a node is a DOM node. Else will return by `findDOMNode`
|
||||
*/
|
||||
export default function findDOMNode(node) {
|
||||
const domNode = getDOM(node);
|
||||
if (domNode) {
|
||||
return domNode;
|
||||
}
|
||||
if (node && typeof node === 'object' && 'current' in node) {
|
||||
const refDomNode = getDOM(node.current);
|
||||
if (refDomNode) {
|
||||
return refDomNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
export declare function getFocusNodeList(node: HTMLElement, includePositive?: boolean): HTMLElement[];
|
||||
export interface InputFocusOptions extends FocusOptions {
|
||||
cursor?: 'start' | 'end' | 'all';
|
||||
}
|
||||
/**
|
||||
* Focus element and set cursor position for input/textarea elements.
|
||||
*/
|
||||
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.
|
||||
* @param id - A stable ID for this lock instance
|
||||
*/
|
||||
export declare function lockFocus(element: HTMLElement, id: string): 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.
|
||||
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
||||
*/
|
||||
export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): [ignoreElement: (ele: HTMLElement) => void];
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import isVisible from "./isVisible";
|
||||
import useId from "../hooks/useId";
|
||||
function focusable(node, includePositive = false) {
|
||||
if (isVisible(node)) {
|
||||
const nodeName = node.nodeName.toLowerCase();
|
||||
const isFocusableElement =
|
||||
// Focusable element
|
||||
['input', 'select', 'textarea', 'button'].includes(nodeName) ||
|
||||
// Editable element
|
||||
node.isContentEditable ||
|
||||
// Anchor with href element
|
||||
nodeName === 'a' && !!node.getAttribute('href');
|
||||
|
||||
// Get tabIndex
|
||||
const tabIndexAttr = node.getAttribute('tabindex');
|
||||
const tabIndexNum = Number(tabIndexAttr);
|
||||
|
||||
// Parse as number if validate
|
||||
let tabIndex = null;
|
||||
if (tabIndexAttr && !Number.isNaN(tabIndexNum)) {
|
||||
tabIndex = tabIndexNum;
|
||||
} else if (isFocusableElement && tabIndex === null) {
|
||||
tabIndex = 0;
|
||||
}
|
||||
|
||||
// Block focusable if disabled
|
||||
if (isFocusableElement && node.disabled) {
|
||||
tabIndex = null;
|
||||
}
|
||||
return tabIndex !== null && (tabIndex >= 0 || includePositive && tabIndex < 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export function getFocusNodeList(node, includePositive = false) {
|
||||
const res = [...node.querySelectorAll('*')].filter(child => {
|
||||
return focusable(child, includePositive);
|
||||
});
|
||||
if (focusable(node, includePositive)) {
|
||||
res.unshift(node);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// Used for `rc-input` `rc-textarea` `rc-input-number`
|
||||
/**
|
||||
* Focus element and set cursor position for input/textarea elements.
|
||||
*/
|
||||
export function triggerFocus(element, option) {
|
||||
if (!element) return;
|
||||
element.focus(option);
|
||||
|
||||
// Selection content
|
||||
const {
|
||||
cursor
|
||||
} = option || {};
|
||||
if (cursor && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement)) {
|
||||
const len = element.value.length;
|
||||
switch (cursor) {
|
||||
case 'start':
|
||||
element.setSelectionRange(0, 0);
|
||||
break;
|
||||
case 'end':
|
||||
element.setSelectionRange(len, len);
|
||||
break;
|
||||
default:
|
||||
element.setSelectionRange(0, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// == Lock Focus ==
|
||||
// ======================================================
|
||||
let lastFocusElement = null;
|
||||
let focusElements = [];
|
||||
// Map stable ID to lock element
|
||||
const idToElementMap = new Map();
|
||||
// Map stable ID to ignored element
|
||||
const ignoredElementMap = new Map();
|
||||
function getLastElement() {
|
||||
return focusElements[focusElements.length - 1];
|
||||
}
|
||||
function isIgnoredElement(element) {
|
||||
const lastElement = getLastElement();
|
||||
if (element && lastElement) {
|
||||
// Find the ID that maps to the last element
|
||||
let lockId;
|
||||
for (const [id, ele] of idToElementMap.entries()) {
|
||||
if (ele === lastElement) {
|
||||
lockId = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const ignoredEle = ignoredElementMap.get(lockId);
|
||||
return !!ignoredEle && (ignoredEle === element || ignoredEle.contains(element));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function hasFocus(element) {
|
||||
const {
|
||||
activeElement
|
||||
} = document;
|
||||
return element === activeElement || element.contains(activeElement);
|
||||
}
|
||||
function syncFocus() {
|
||||
const lastElement = getLastElement();
|
||||
const {
|
||||
activeElement
|
||||
} = document;
|
||||
|
||||
// If current focus is on an ignored element, don't force it back
|
||||
if (isIgnoredElement(activeElement)) {
|
||||
return;
|
||||
}
|
||||
if (lastElement && !hasFocus(lastElement)) {
|
||||
const focusableList = getFocusNodeList(lastElement);
|
||||
const matchElement = focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0];
|
||||
matchElement?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
} 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.
|
||||
* @param id - A stable ID for this lock instance
|
||||
*/
|
||||
export function lockFocus(element, id) {
|
||||
if (element) {
|
||||
// Store the mapping between ID and element
|
||||
idToElementMap.set(id, 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);
|
||||
idToElementMap.delete(id);
|
||||
ignoredElementMap.delete(id);
|
||||
if (focusElements.length === 0) {
|
||||
window.removeEventListener('focusin', syncFocus);
|
||||
window.removeEventListener('keydown', onWindowKeyDown, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry an effect until it reports ready.
|
||||
* When `ready` is `false`, it will schedule one more effect cycle and call `func` again
|
||||
* with the next `retryTimes`.
|
||||
*/
|
||||
|
||||
function useRetryEffect(func, deps) {
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
const retryTimesRef = useRef(0);
|
||||
const [retryMark, setRetryMark] = useState(0);
|
||||
useEffect(() => {
|
||||
retryTimesRef.current = 0;
|
||||
}, deps);
|
||||
useEffect(() => {
|
||||
const [clearFn, ready] = func(retryTimesRef.current);
|
||||
if (!ready) {
|
||||
retryTimesRef.current += 1;
|
||||
setRetryMark(count => count + 1);
|
||||
}
|
||||
return clearFn;
|
||||
}, [...deps, retryMark]);
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
||||
*/
|
||||
export function useLockFocus(lock, getElement) {
|
||||
const id = useId();
|
||||
const getElementRef = useRef(getElement);
|
||||
getElementRef.current = getElement;
|
||||
const lockEffect = retryTimes => {
|
||||
if (!lock) {
|
||||
return [undefined, true];
|
||||
}
|
||||
const element = getElementRef.current();
|
||||
if (element) {
|
||||
return [lockFocus(element, id), true];
|
||||
}
|
||||
return [undefined, retryTimes >= 1];
|
||||
};
|
||||
useRetryEffect(lockEffect, [id, lock]);
|
||||
const ignoreElement = ele => {
|
||||
if (ele) {
|
||||
// Set the ignored element using stable ID
|
||||
ignoredElementMap.set(id, ele);
|
||||
}
|
||||
};
|
||||
return [ignoreElement];
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
declare const _default: (element: Element) => boolean;
|
||||
export default _default;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
export default (element => {
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
if (element instanceof Element) {
|
||||
if (element.offsetParent) {
|
||||
return true;
|
||||
}
|
||||
if (element.getBBox) {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = element.getBBox();
|
||||
if (width || height) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (element.getBoundingClientRect) {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = element.getBoundingClientRect();
|
||||
if (width || height) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
export interface scrollLockOptions {
|
||||
container: HTMLElement;
|
||||
}
|
||||
export default class ScrollLocker {
|
||||
private lockTarget;
|
||||
private options;
|
||||
constructor(options?: scrollLockOptions);
|
||||
getContainer: () => HTMLElement | undefined;
|
||||
reLock: (options?: scrollLockOptions) => void;
|
||||
lock: () => void;
|
||||
unLock: () => void;
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
import getScrollBarSize from "../getScrollBarSize";
|
||||
import setStyle from "../setStyle";
|
||||
let uuid = 0;
|
||||
let locks = [];
|
||||
const scrollingEffectClassName = 'ant-scrolling-effect';
|
||||
const scrollingEffectClassNameReg = new RegExp(`${scrollingEffectClassName}`, 'g');
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19340
|
||||
// https://github.com/ant-design/ant-design/issues/19332
|
||||
const cacheStyle = new Map();
|
||||
export default class ScrollLocker {
|
||||
lockTarget;
|
||||
options;
|
||||
constructor(options) {
|
||||
// eslint-disable-next-line no-plusplus
|
||||
this.lockTarget = uuid++;
|
||||
this.options = options;
|
||||
}
|
||||
getContainer = () => {
|
||||
return this.options?.container;
|
||||
};
|
||||
|
||||
// if options change...
|
||||
reLock = options => {
|
||||
const findLock = locks.find(({
|
||||
target
|
||||
}) => target === this.lockTarget);
|
||||
if (findLock) {
|
||||
this.unLock();
|
||||
}
|
||||
this.options = options;
|
||||
if (findLock) {
|
||||
findLock.options = options;
|
||||
this.lock();
|
||||
}
|
||||
};
|
||||
lock = () => {
|
||||
// If lockTarget exist return
|
||||
if (locks.some(({
|
||||
target
|
||||
}) => target === this.lockTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If same container effect, return
|
||||
if (locks.some(({
|
||||
options
|
||||
}) => options?.container === this.options?.container)) {
|
||||
locks = [...locks, {
|
||||
target: this.lockTarget,
|
||||
options: this.options
|
||||
}];
|
||||
return;
|
||||
}
|
||||
let scrollBarSize = 0;
|
||||
const container = this.options?.container || document.body;
|
||||
if (container === document.body && window.innerWidth - document.documentElement.clientWidth > 0 || container.scrollHeight > container.clientHeight) {
|
||||
if (getComputedStyle(container).overflow !== 'hidden') {
|
||||
scrollBarSize = getScrollBarSize();
|
||||
}
|
||||
}
|
||||
const containerClassName = container.className;
|
||||
if (locks.filter(({
|
||||
options
|
||||
}) => options?.container === this.options?.container).length === 0) {
|
||||
cacheStyle.set(container, setStyle({
|
||||
width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
|
||||
overflow: 'hidden',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'hidden'
|
||||
}, {
|
||||
element: container
|
||||
}));
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19729
|
||||
if (!scrollingEffectClassNameReg.test(containerClassName)) {
|
||||
const addClassName = `${containerClassName} ${scrollingEffectClassName}`;
|
||||
container.className = addClassName.trim();
|
||||
}
|
||||
locks = [...locks, {
|
||||
target: this.lockTarget,
|
||||
options: this.options
|
||||
}];
|
||||
};
|
||||
unLock = () => {
|
||||
const findLock = locks.find(({
|
||||
target
|
||||
}) => target === this.lockTarget);
|
||||
locks = locks.filter(({
|
||||
target
|
||||
}) => target !== this.lockTarget);
|
||||
if (!findLock || locks.some(({
|
||||
options
|
||||
}) => options?.container === findLock.options?.container)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove Effect
|
||||
const container = this.options?.container || document.body;
|
||||
const containerClassName = container.className;
|
||||
if (!scrollingEffectClassNameReg.test(containerClassName)) return;
|
||||
setStyle(cacheStyle.get(container), {
|
||||
element: container
|
||||
});
|
||||
cacheStyle.delete(container);
|
||||
container.className = container.className.replace(scrollingEffectClassNameReg, '').trim();
|
||||
};
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Check if is in shadowRoot
|
||||
*/
|
||||
export declare function inShadow(ele: Node): boolean;
|
||||
/**
|
||||
* Return shadowRoot if possible
|
||||
*/
|
||||
export declare function getShadowRoot(ele: Node): ShadowRoot;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
function getRoot(ele) {
|
||||
return ele?.getRootNode?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is in shadowRoot
|
||||
*/
|
||||
export function inShadow(ele) {
|
||||
return getRoot(ele) instanceof ShadowRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return shadowRoot if possible
|
||||
*/
|
||||
export function getShadowRoot(ele) {
|
||||
return inShadow(ele) ? getRoot(ele) : null;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export declare function isStyleSupport(styleName: string | string[]): boolean;
|
||||
export declare function isStyleSupport(styleName: string, styleValue: any): boolean;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import canUseDom from "./canUseDom";
|
||||
const isStyleNameSupport = styleName => {
|
||||
if (canUseDom() && window.document.documentElement) {
|
||||
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
|
||||
const {
|
||||
documentElement
|
||||
} = window.document;
|
||||
return styleNameList.some(name => name in documentElement.style);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const isStyleValueSupport = (styleName, value) => {
|
||||
if (!isStyleNameSupport(styleName)) {
|
||||
return false;
|
||||
}
|
||||
const ele = document.createElement('div');
|
||||
const origin = ele.style[styleName];
|
||||
ele.style[styleName] = value;
|
||||
return ele.style[styleName] !== origin;
|
||||
};
|
||||
export function isStyleSupport(styleName, styleValue) {
|
||||
if (!Array.isArray(styleName) && styleValue !== undefined) {
|
||||
return isStyleValueSupport(styleName, styleValue);
|
||||
}
|
||||
return isStyleNameSupport(styleName);
|
||||
}
|
||||
Reference in New Issue
Block a user