@v-c/util 0.0.1
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/LICENSE +21 -0
- package/dist/Children/isFragment.cjs +1 -0
- package/dist/Children/isFragment.d.ts +3 -0
- package/dist/Children/isFragment.js +7 -0
- package/dist/Children/toArray.cjs +1 -0
- package/dist/Children/toArray.d.ts +8 -0
- package/dist/Children/toArray.js +11 -0
- package/dist/Dom/addEventListener.cjs +1 -0
- package/dist/Dom/addEventListener.d.ts +8 -0
- package/dist/Dom/addEventListener.js +12 -0
- package/dist/Dom/canUseDom.cjs +1 -0
- package/dist/Dom/canUseDom.d.ts +1 -0
- package/dist/Dom/canUseDom.js +6 -0
- package/dist/Dom/class.cjs +1 -0
- package/dist/Dom/class.d.ts +3 -0
- package/dist/Dom/class.js +19 -0
- package/dist/Dom/contains.cjs +1 -0
- package/dist/Dom/contains.d.ts +1 -0
- package/dist/Dom/contains.js +16 -0
- package/dist/Dom/css.cjs +1 -0
- package/dist/Dom/css.d.ts +20 -0
- package/dist/Dom/css.js +75 -0
- package/dist/Dom/dynamicCSS.cjs +1 -0
- package/dist/Dom/dynamicCSS.d.ts +24 -0
- package/dist/Dom/dynamicCSS.js +78 -0
- package/dist/Dom/findDOMNode.cjs +1 -0
- package/dist/Dom/findDOMNode.d.ts +7 -0
- package/dist/Dom/findDOMNode.js +12 -0
- package/dist/Dom/focus.cjs +1 -0
- package/dist/Dom/focus.d.ts +8 -0
- package/dist/Dom/focus.js +40 -0
- package/dist/Dom/isVisible.cjs +1 -0
- package/dist/Dom/isVisible.d.ts +2 -0
- package/dist/Dom/isVisible.js +22 -0
- package/dist/Dom/scrollLocker.cjs +1 -0
- package/dist/Dom/scrollLocker.d.ts +12 -0
- package/dist/Dom/scrollLocker.js +81 -0
- package/dist/Dom/shadow.cjs +1 -0
- package/dist/Dom/shadow.d.ts +8 -0
- package/dist/Dom/shadow.js +14 -0
- package/dist/Dom/styleChecker.cjs +1 -0
- package/dist/Dom/styleChecker.d.ts +2 -0
- package/dist/Dom/styleChecker.js +20 -0
- package/dist/Dom/support.cjs +1 -0
- package/dist/Dom/support.d.ts +6 -0
- package/dist/Dom/support.js +24 -0
- package/dist/EventInterface.cjs +1 -0
- package/dist/EventInterface.d.ts +18 -0
- package/dist/EventInterface.js +1 -0
- package/dist/KeyCode.cjs +1 -0
- package/dist/KeyCode.d.ts +435 -0
- package/dist/KeyCode.js +516 -0
- package/dist/Portal.cjs +1 -0
- package/dist/Portal.d.ts +8 -0
- package/dist/Portal.js +38 -0
- package/dist/PortalWrapper.cjs +1 -0
- package/dist/PortalWrapper.d.ts +28 -0
- package/dist/PortalWrapper.js +114 -0
- package/dist/composeProps.cjs +1 -0
- package/dist/composeProps.d.ts +2 -0
- package/dist/composeProps.js +16 -0
- package/dist/createRef.cjs +1 -0
- package/dist/createRef.d.ts +12 -0
- package/dist/createRef.js +21 -0
- package/dist/debug/diff.cjs +1 -0
- package/dist/debug/diff.d.ts +1 -0
- package/dist/debug/diff.js +41 -0
- package/dist/deprecated.cjs +1 -0
- package/dist/deprecated.d.ts +1 -0
- package/dist/deprecated.js +8 -0
- package/dist/getScrollBarSize.cjs +1 -0
- package/dist/getScrollBarSize.d.ts +5 -0
- package/dist/getScrollBarSize.js +37 -0
- package/dist/guid.cjs +1 -0
- package/dist/guid.d.ts +1 -0
- package/dist/guid.js +7 -0
- package/dist/hooks/useId.cjs +1 -0
- package/dist/hooks/useId.d.ts +5 -0
- package/dist/hooks/useId.js +17 -0
- package/dist/hooks/useMemo.cjs +1 -0
- package/dist/hooks/useMemo.d.ts +3 -0
- package/dist/hooks/useMemo.js +10 -0
- package/dist/hooks/useMergedState.cjs +1 -0
- package/dist/hooks/useMergedState.d.ts +8 -0
- package/dist/hooks/useMergedState.js +21 -0
- package/dist/hooks/useState.cjs +1 -0
- package/dist/hooks/useState.d.ts +3 -0
- package/dist/hooks/useState.js +11 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +8 -0
- package/dist/isEqual.cjs +1 -0
- package/dist/isEqual.d.ts +8 -0
- package/dist/isEqual.js +32 -0
- package/dist/isMobile.cjs +1 -0
- package/dist/isMobile.d.ts +2 -0
- package/dist/isMobile.js +13 -0
- package/dist/omit.cjs +1 -0
- package/dist/omit.d.ts +1 -0
- package/dist/omit.js +9 -0
- package/dist/pickAttrs.cjs +18 -0
- package/dist/pickAttrs.d.ts +11 -0
- package/dist/pickAttrs.js +41 -0
- package/dist/props-util/index.cjs +1 -0
- package/dist/props-util/index.d.ts +2 -0
- package/dist/props-util/index.js +14 -0
- package/dist/raf.cjs +1 -0
- package/dist/raf.d.ts +6 -0
- package/dist/raf.js +30 -0
- package/dist/setStyle.cjs +1 -0
- package/dist/setStyle.d.ts +13 -0
- package/dist/setStyle.js +13 -0
- package/dist/switchScrollingEffect.cjs +1 -0
- package/dist/switchScrollingEffect.d.ts +2 -0
- package/dist/switchScrollingEffect.js +31 -0
- package/dist/test/domHook.cjs +1 -0
- package/dist/test/domHook.d.ts +8 -0
- package/dist/test/domHook.js +37 -0
- package/dist/type.cjs +1 -0
- package/dist/type.d.ts +63 -0
- package/dist/type.js +49 -0
- package/dist/utils/checkSlotProp.cjs +1 -0
- package/dist/utils/checkSlotProp.d.ts +1 -0
- package/dist/utils/checkSlotProp.js +7 -0
- package/dist/utils/get.cjs +1 -0
- package/dist/utils/get.d.ts +1 -0
- package/dist/utils/get.js +12 -0
- package/dist/utils/set.cjs +1 -0
- package/dist/utils/set.d.ts +6 -0
- package/dist/utils/set.js +41 -0
- package/dist/vnode.cjs +1 -0
- package/dist/vnode.d.ts +12 -0
- package/dist/vnode.js +41 -0
- package/dist/warning.cjs +1 -0
- package/dist/warning.d.ts +18 -0
- package/dist/warning.js +48 -0
- package/package.json +37 -0
- package/src/Children/isFragment.ts +6 -0
- package/src/Children/tests/isFragment.test.tsx +15 -0
- package/src/Children/tests/toArray.test.tsx +101 -0
- package/src/Children/toArray.ts +27 -0
- package/src/Dom/addEventListener.ts +20 -0
- package/src/Dom/canUseDom.ts +7 -0
- package/src/Dom/class.ts +29 -0
- package/src/Dom/contains.ts +19 -0
- package/src/Dom/css.ts +113 -0
- package/src/Dom/dynamicCSS.ts +173 -0
- package/src/Dom/findDOMNode.ts +23 -0
- package/src/Dom/focus.ts +96 -0
- package/src/Dom/isVisible.ts +23 -0
- package/src/Dom/scrollLocker.ts +143 -0
- package/src/Dom/shadow.ts +17 -0
- package/src/Dom/styleChecker.ts +31 -0
- package/src/Dom/support.ts +27 -0
- package/src/EventInterface.ts +19 -0
- package/src/KeyCode.ts +516 -0
- package/src/Portal.tsx +50 -0
- package/src/PortalWrapper.tsx +214 -0
- package/src/composeProps.ts +23 -0
- package/src/createRef.ts +33 -0
- package/src/debug/diff.ts +66 -0
- package/src/deprecated.ts +8 -0
- package/src/getScrollBarSize.tsx +57 -0
- package/src/guid.ts +4 -0
- package/src/hooks/useId.ts +31 -0
- package/src/hooks/useMemo.ts +21 -0
- package/src/hooks/useMergedState.ts +44 -0
- package/src/hooks/useState.ts +17 -0
- package/src/index.ts +3 -0
- package/src/isEqual.ts +50 -0
- package/src/isMobile.ts +15 -0
- package/src/omit.ts +14 -0
- package/src/pickAttrs.ts +78 -0
- package/src/props-util/index.ts +22 -0
- package/src/raf.ts +55 -0
- package/src/setStyle.ts +38 -0
- package/src/switchScrollingEffect.ts +48 -0
- package/src/test/domHook.ts +67 -0
- package/src/type.ts +94 -0
- package/src/utils/checkSlotProp.ts +10 -0
- package/src/utils/get.ts +15 -0
- package/src/utils/set.ts +110 -0
- package/src/vnode.ts +86 -0
- package/src/warning.ts +79 -0
- package/tests/Portal.test.tsx +199 -0
- package/tsconfig.json +7 -0
- package/vite.config.ts +18 -0
package/src/Dom/css.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const PIXEL_PATTERN = /margin|padding|width|height|max|min|offset/
|
|
2
|
+
|
|
3
|
+
const removePixel: any = {
|
|
4
|
+
left: true,
|
|
5
|
+
top: true,
|
|
6
|
+
}
|
|
7
|
+
const floatMap: any = {
|
|
8
|
+
cssFloat: 1,
|
|
9
|
+
styleFloat: 1,
|
|
10
|
+
float: 1,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getComputedStyle(node: HTMLElement) {
|
|
14
|
+
return node.nodeType === 1 ? (node as any).ownerDocument.defaultView.getComputedStyle(node, null) : {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getStyleValue(node: HTMLElement, type: string, value: string) {
|
|
18
|
+
type = type.toLowerCase()
|
|
19
|
+
if (value === 'auto') {
|
|
20
|
+
if (type === 'height')
|
|
21
|
+
return node.offsetHeight
|
|
22
|
+
|
|
23
|
+
if (type === 'width')
|
|
24
|
+
return node.offsetWidth
|
|
25
|
+
}
|
|
26
|
+
if (!(type in removePixel))
|
|
27
|
+
removePixel[type] = PIXEL_PATTERN.test(type)
|
|
28
|
+
|
|
29
|
+
return removePixel[type] ? Number.parseFloat(value) || 0 : value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function get(node: HTMLElement, name: any) {
|
|
33
|
+
const length = arguments.length
|
|
34
|
+
const style = getComputedStyle(node)
|
|
35
|
+
|
|
36
|
+
name = floatMap[name] ? ('cssFloat' in node.style ? 'cssFloat' : 'styleFloat') : name
|
|
37
|
+
|
|
38
|
+
return length === 1 ? style : getStyleValue(node, name, style[name] || node.style[name])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function set(node: HTMLElement, name: any, value: string | number) {
|
|
42
|
+
const length = arguments.length
|
|
43
|
+
name = floatMap[name] ? ('cssFloat' in node.style ? 'cssFloat' : 'styleFloat') : name
|
|
44
|
+
if (length === 3) {
|
|
45
|
+
if (typeof value === 'number' && PIXEL_PATTERN.test(name))
|
|
46
|
+
value = `${value}px`
|
|
47
|
+
|
|
48
|
+
;(node.style as any)[name] = value // Number
|
|
49
|
+
return value
|
|
50
|
+
}
|
|
51
|
+
for (const x in name) {
|
|
52
|
+
if (x in name)
|
|
53
|
+
set(node, x, name[x])
|
|
54
|
+
}
|
|
55
|
+
return getComputedStyle(node)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getOuterWidth(el: HTMLElement) {
|
|
59
|
+
if (el === document.body)
|
|
60
|
+
return document.documentElement.clientWidth
|
|
61
|
+
|
|
62
|
+
return el.offsetWidth
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getOuterHeight(el: HTMLElement) {
|
|
66
|
+
if (el === document.body)
|
|
67
|
+
return window.innerHeight || document.documentElement.clientHeight
|
|
68
|
+
|
|
69
|
+
return el.offsetHeight
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getDocSize() {
|
|
73
|
+
const width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth)
|
|
74
|
+
const height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
width,
|
|
78
|
+
height,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getClientSize() {
|
|
83
|
+
const width = document.documentElement.clientWidth
|
|
84
|
+
const height = window.innerHeight || document.documentElement.clientHeight
|
|
85
|
+
return {
|
|
86
|
+
width,
|
|
87
|
+
height,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getScroll() {
|
|
92
|
+
return {
|
|
93
|
+
scrollLeft: Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
|
|
94
|
+
scrollTop: Math.max(document.documentElement.scrollTop, document.body.scrollTop),
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getOffset(node: any) {
|
|
99
|
+
const box = node.getBoundingClientRect()
|
|
100
|
+
const docElem = document.documentElement
|
|
101
|
+
|
|
102
|
+
// < ie8 不支持 win.pageXOffset, 则使用 docElem.scrollLeft
|
|
103
|
+
return {
|
|
104
|
+
left:
|
|
105
|
+
box.left
|
|
106
|
+
+ (window.pageXOffset || docElem.scrollLeft)
|
|
107
|
+
- (docElem.clientLeft || document.body.clientLeft || 0),
|
|
108
|
+
top:
|
|
109
|
+
box.top
|
|
110
|
+
+ (window.pageYOffset || docElem.scrollTop)
|
|
111
|
+
- (docElem.clientTop || document.body.clientTop || 0),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import canUseDom from './canUseDom'
|
|
2
|
+
import contains from './contains'
|
|
3
|
+
|
|
4
|
+
const APPEND_ORDER = 'data-vc-order'
|
|
5
|
+
const APPEND_PRIORITY = 'data-vc-priority'
|
|
6
|
+
const MARK_KEY = `vc-util-key`
|
|
7
|
+
|
|
8
|
+
const containerCache = new Map<ContainerType, Node & ParentNode>()
|
|
9
|
+
|
|
10
|
+
export type ContainerType = Element | ShadowRoot
|
|
11
|
+
export type Prepend = boolean | 'queue'
|
|
12
|
+
export type AppendType = 'prependQueue' | 'append' | 'prepend'
|
|
13
|
+
|
|
14
|
+
interface Options {
|
|
15
|
+
attachTo?: ContainerType
|
|
16
|
+
csp?: { nonce?: string }
|
|
17
|
+
prepend?: Prepend
|
|
18
|
+
/**
|
|
19
|
+
* Config the `priority` of `prependQueue`. Default is `0`.
|
|
20
|
+
* It's useful if you need to insert style before other style.
|
|
21
|
+
*/
|
|
22
|
+
priority?: number
|
|
23
|
+
mark?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getMark({ mark }: Options = {}) {
|
|
27
|
+
if (mark)
|
|
28
|
+
return mark.startsWith('data-') ? mark : `data-${mark}`
|
|
29
|
+
|
|
30
|
+
return MARK_KEY
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getContainer(option: Options) {
|
|
34
|
+
if (option.attachTo)
|
|
35
|
+
return option.attachTo
|
|
36
|
+
|
|
37
|
+
const head = document.querySelector('head')
|
|
38
|
+
return head || document.body
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getOrder(prepend?: Prepend): AppendType {
|
|
42
|
+
if (prepend === 'queue')
|
|
43
|
+
return 'prependQueue'
|
|
44
|
+
|
|
45
|
+
return prepend ? 'prepend' : 'append'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find style which inject by rc-util
|
|
50
|
+
*/
|
|
51
|
+
function findStyles(container: ContainerType) {
|
|
52
|
+
return Array.from(
|
|
53
|
+
(containerCache.get(container) || container).children,
|
|
54
|
+
).filter(node => node.tagName === 'STYLE') as HTMLStyleElement[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function injectCSS(css: string, option: Options = {}) {
|
|
58
|
+
if (!canUseDom())
|
|
59
|
+
return null
|
|
60
|
+
|
|
61
|
+
const { csp, prepend, priority = 0 } = option
|
|
62
|
+
const mergedOrder = getOrder(prepend)
|
|
63
|
+
const isPrependQueue = mergedOrder === 'prependQueue'
|
|
64
|
+
|
|
65
|
+
const styleNode = document.createElement('style')
|
|
66
|
+
styleNode.setAttribute(APPEND_ORDER, mergedOrder)
|
|
67
|
+
|
|
68
|
+
if (isPrependQueue && priority)
|
|
69
|
+
styleNode.setAttribute(APPEND_PRIORITY, `${priority}`)
|
|
70
|
+
|
|
71
|
+
if (csp?.nonce)
|
|
72
|
+
styleNode.nonce = csp?.nonce
|
|
73
|
+
|
|
74
|
+
styleNode.innerHTML = css
|
|
75
|
+
|
|
76
|
+
const container = getContainer(option)
|
|
77
|
+
const { firstChild } = container
|
|
78
|
+
|
|
79
|
+
if (prepend) {
|
|
80
|
+
// If is queue `prepend`, it will prepend first style and then append rest style
|
|
81
|
+
if (isPrependQueue) {
|
|
82
|
+
const existStyle = findStyles(container).filter((node: any) => {
|
|
83
|
+
// Ignore style which not injected by rc-util with prepend
|
|
84
|
+
if (
|
|
85
|
+
!['prepend', 'prependQueue'].includes(node.getAttribute(APPEND_ORDER))
|
|
86
|
+
)
|
|
87
|
+
return false
|
|
88
|
+
|
|
89
|
+
// Ignore style which priority less then new style
|
|
90
|
+
const nodePriority = Number(node.getAttribute(APPEND_PRIORITY) || 0)
|
|
91
|
+
return priority >= nodePriority
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (existStyle.length) {
|
|
95
|
+
container.insertBefore(
|
|
96
|
+
styleNode,
|
|
97
|
+
existStyle[existStyle.length - 1].nextSibling,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return styleNode
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Use `insertBefore` as `prepend`
|
|
105
|
+
container.insertBefore(styleNode, firstChild)
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
container.appendChild(styleNode)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return styleNode
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function findExistNode(key: string, option: Options = {}) {
|
|
115
|
+
const container = getContainer(option)
|
|
116
|
+
|
|
117
|
+
return findStyles(container).find(
|
|
118
|
+
node => node.getAttribute(getMark(option)) === key,
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function removeCSS(key: string, option: Options = {}) {
|
|
123
|
+
const existNode = findExistNode(key, option)
|
|
124
|
+
if (existNode) {
|
|
125
|
+
const container = getContainer(option)
|
|
126
|
+
container.removeChild(existNode)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* qiankun will inject `appendChild` to insert into other
|
|
132
|
+
*/
|
|
133
|
+
function syncRealContainer(container: ContainerType, option: Options) {
|
|
134
|
+
const cachedRealContainer = containerCache.get(container)
|
|
135
|
+
|
|
136
|
+
// Find real container when not cached or cached container removed
|
|
137
|
+
if (!cachedRealContainer || !contains(document, cachedRealContainer)) {
|
|
138
|
+
const placeholderStyle: any = injectCSS('', option)
|
|
139
|
+
const { parentNode } = placeholderStyle
|
|
140
|
+
containerCache.set(container, parentNode)
|
|
141
|
+
container.removeChild(placeholderStyle)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* manually clear container cache to avoid global cache in unit testes
|
|
147
|
+
*/
|
|
148
|
+
export function clearContainerCache() {
|
|
149
|
+
containerCache.clear()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function updateCSS(css: string, key: string, option: Options = {}) {
|
|
153
|
+
const container = getContainer(option)
|
|
154
|
+
|
|
155
|
+
// Sync real parent
|
|
156
|
+
syncRealContainer(container, option)
|
|
157
|
+
|
|
158
|
+
const existNode = findExistNode(key, option)
|
|
159
|
+
|
|
160
|
+
if (existNode) {
|
|
161
|
+
if (option.csp?.nonce && existNode.nonce !== option.csp?.nonce)
|
|
162
|
+
existNode.nonce = option.csp?.nonce
|
|
163
|
+
|
|
164
|
+
if (existNode.innerHTML !== css)
|
|
165
|
+
existNode.innerHTML = css
|
|
166
|
+
|
|
167
|
+
return existNode
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const newNode: any = injectCSS(css, option)
|
|
171
|
+
newNode.setAttribute(getMark(option), key)
|
|
172
|
+
return newNode
|
|
173
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, MaybeRef } from 'vue'
|
|
2
|
+
import { unref } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function isDOM(node: any): node is HTMLElement | SVGElement {
|
|
5
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Element
|
|
6
|
+
// Since XULElement is also subclass of Element, we only need HTMLElement and SVGElement
|
|
7
|
+
return node instanceof HTMLElement || node instanceof SVGElement
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Return if a node is a DOM node. Else will return by `findDOMNode`
|
|
12
|
+
*/
|
|
13
|
+
export default function findDOMNode<T = Element | Text>(
|
|
14
|
+
_node: MaybeRef<ComponentPublicInstance | HTMLElement | SVGElement>,
|
|
15
|
+
): T | null {
|
|
16
|
+
const node = unref(_node)
|
|
17
|
+
if (isDOM(node))
|
|
18
|
+
return (node as unknown) as T
|
|
19
|
+
else if (node && '$el' in node)
|
|
20
|
+
return (node.$el as unknown) as T
|
|
21
|
+
|
|
22
|
+
return null
|
|
23
|
+
}
|
package/src/Dom/focus.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import isVisible from './isVisible'
|
|
2
|
+
|
|
3
|
+
type DisabledElement =
|
|
4
|
+
| HTMLLinkElement
|
|
5
|
+
| HTMLInputElement
|
|
6
|
+
| HTMLFieldSetElement
|
|
7
|
+
| HTMLButtonElement
|
|
8
|
+
| HTMLOptGroupElement
|
|
9
|
+
| HTMLOptionElement
|
|
10
|
+
| HTMLSelectElement
|
|
11
|
+
| HTMLTextAreaElement
|
|
12
|
+
|
|
13
|
+
function focusable(node: HTMLElement, includePositive = false): boolean {
|
|
14
|
+
if (isVisible(node)) {
|
|
15
|
+
const nodeName = node.nodeName.toLowerCase()
|
|
16
|
+
const isFocusableElement
|
|
17
|
+
// Focusable element
|
|
18
|
+
= ['input', 'select', 'textarea', 'button'].includes(nodeName)
|
|
19
|
+
// Editable element
|
|
20
|
+
|| node.isContentEditable
|
|
21
|
+
// Anchor with href element
|
|
22
|
+
|| (nodeName === 'a' && !!node.getAttribute('href'))
|
|
23
|
+
|
|
24
|
+
// Get tabIndex
|
|
25
|
+
const tabIndexAttr = node.getAttribute('tabindex')
|
|
26
|
+
const tabIndexNum = Number(tabIndexAttr)
|
|
27
|
+
|
|
28
|
+
// Parse as number if validate
|
|
29
|
+
let tabIndex: number | null = null
|
|
30
|
+
if (tabIndexAttr && !Number.isNaN(tabIndexNum))
|
|
31
|
+
tabIndex = tabIndexNum
|
|
32
|
+
else if (isFocusableElement && tabIndex === null)
|
|
33
|
+
tabIndex = 0
|
|
34
|
+
|
|
35
|
+
// Block focusable if disabled
|
|
36
|
+
if (isFocusableElement && (node as DisabledElement).disabled)
|
|
37
|
+
tabIndex = null
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
tabIndex !== null && (tabIndex >= 0 || (includePositive && tabIndex < 0))
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getFocusNodeList(node: HTMLElement, includePositive = false) {
|
|
48
|
+
const res = [...node.querySelectorAll<HTMLElement>('*')].filter((child) => {
|
|
49
|
+
return focusable(child, includePositive)
|
|
50
|
+
})
|
|
51
|
+
if (focusable(node, includePositive))
|
|
52
|
+
res.unshift(node)
|
|
53
|
+
|
|
54
|
+
return res
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let lastFocusElement: any = null
|
|
58
|
+
|
|
59
|
+
/** @deprecated Do not use since this may failed when used in async */
|
|
60
|
+
export function saveLastFocusNode() {
|
|
61
|
+
lastFocusElement = document.activeElement
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @deprecated Do not use since this may failed when used in async */
|
|
65
|
+
export function clearLastFocusNode() {
|
|
66
|
+
lastFocusElement = null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** @deprecated Do not use since this may failed when used in async */
|
|
70
|
+
export function backLastFocusNode() {
|
|
71
|
+
if (lastFocusElement) {
|
|
72
|
+
try {
|
|
73
|
+
// 元素可能已经被移动了
|
|
74
|
+
lastFocusElement.focus()
|
|
75
|
+
}
|
|
76
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
77
|
+
catch (_e: any) {
|
|
78
|
+
// empty
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function limitTabRange(node: HTMLElement, e: KeyboardEvent) {
|
|
84
|
+
if (e.keyCode === 9) {
|
|
85
|
+
const tabNodeList = getFocusNodeList(node)
|
|
86
|
+
const lastTabNode = tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1]
|
|
87
|
+
const leavingTab
|
|
88
|
+
= lastTabNode === document.activeElement || node === document.activeElement
|
|
89
|
+
|
|
90
|
+
if (leavingTab) {
|
|
91
|
+
const target = tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0]
|
|
92
|
+
target.focus()
|
|
93
|
+
e.preventDefault()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default (element: Element): boolean => {
|
|
2
|
+
if (!element)
|
|
3
|
+
return false
|
|
4
|
+
|
|
5
|
+
if (element instanceof Element) {
|
|
6
|
+
if ((element as HTMLElement).offsetParent)
|
|
7
|
+
return true
|
|
8
|
+
|
|
9
|
+
if ((element as SVGGraphicsElement).getBBox) {
|
|
10
|
+
const { width, height } = (element as SVGGraphicsElement).getBBox()
|
|
11
|
+
if (width || height)
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (element.getBoundingClientRect) {
|
|
16
|
+
const { width, height } = element.getBoundingClientRect()
|
|
17
|
+
if (width || height)
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { CSSProperties } from 'vue'
|
|
2
|
+
import getScrollBarSize from '../getScrollBarSize'
|
|
3
|
+
import setStyle from '../setStyle'
|
|
4
|
+
|
|
5
|
+
export interface scrollLockOptions {
|
|
6
|
+
container: HTMLElement
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let uuid = 0
|
|
10
|
+
|
|
11
|
+
interface Ilocks {
|
|
12
|
+
target: typeof uuid
|
|
13
|
+
options: scrollLockOptions
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let locks: Ilocks[] = []
|
|
17
|
+
const scrollingEffectClassName = 'ant-scrolling-effect'
|
|
18
|
+
const scrollingEffectClassNameReg = new RegExp(
|
|
19
|
+
`${scrollingEffectClassName}`,
|
|
20
|
+
'g',
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// https://github.com/ant-design/ant-design/issues/19340
|
|
24
|
+
// https://github.com/ant-design/ant-design/issues/19332
|
|
25
|
+
const cacheStyle = new Map<Element, CSSProperties>()
|
|
26
|
+
|
|
27
|
+
export default class ScrollLocker {
|
|
28
|
+
private readonly lockTarget: typeof uuid
|
|
29
|
+
|
|
30
|
+
private options: scrollLockOptions | undefined
|
|
31
|
+
|
|
32
|
+
constructor(options?: scrollLockOptions) {
|
|
33
|
+
this.lockTarget = uuid++
|
|
34
|
+
this.options = options
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getContainer = (): HTMLElement | undefined => {
|
|
38
|
+
return this.options?.container
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// if options change...
|
|
42
|
+
reLock = (options?: scrollLockOptions) => {
|
|
43
|
+
const findLock = locks.find(({ target }) => target === this.lockTarget)
|
|
44
|
+
|
|
45
|
+
if (findLock)
|
|
46
|
+
this.unLock()
|
|
47
|
+
|
|
48
|
+
this.options = options
|
|
49
|
+
|
|
50
|
+
if (findLock) {
|
|
51
|
+
findLock.options = options!
|
|
52
|
+
|
|
53
|
+
this.lock()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
lock = () => {
|
|
58
|
+
// If lockTarget exist return
|
|
59
|
+
if (locks.some(({ target }) => target === this.lockTarget))
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
// If same container effect, return
|
|
63
|
+
if (
|
|
64
|
+
locks.some(
|
|
65
|
+
({ options }) => options?.container === this.options?.container,
|
|
66
|
+
)
|
|
67
|
+
) {
|
|
68
|
+
locks = [...locks!, { target: this.lockTarget, options: this.options! }]
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let scrollBarSize = 0
|
|
73
|
+
const container = this.options?.container || document.body
|
|
74
|
+
|
|
75
|
+
if (
|
|
76
|
+
(container === document.body
|
|
77
|
+
&& window.innerWidth - document.documentElement.clientWidth > 0)
|
|
78
|
+
|| container.scrollHeight > container.clientHeight
|
|
79
|
+
) {
|
|
80
|
+
if (getComputedStyle(container).overflow !== 'hidden')
|
|
81
|
+
scrollBarSize = getScrollBarSize()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const containerClassName = container.className
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
locks.filter(
|
|
88
|
+
({ options }) => options?.container === this.options?.container,
|
|
89
|
+
).length === 0
|
|
90
|
+
) {
|
|
91
|
+
cacheStyle.set(
|
|
92
|
+
container,
|
|
93
|
+
setStyle(
|
|
94
|
+
{
|
|
95
|
+
width:
|
|
96
|
+
scrollBarSize !== 0
|
|
97
|
+
? `calc(100% - ${scrollBarSize}px)`
|
|
98
|
+
: undefined,
|
|
99
|
+
overflow: 'hidden',
|
|
100
|
+
overflowX: 'hidden',
|
|
101
|
+
overflowY: 'hidden',
|
|
102
|
+
},
|
|
103
|
+
{ element: container },
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// https://github.com/ant-design/ant-design/issues/19729
|
|
109
|
+
if (!scrollingEffectClassNameReg.test(containerClassName)) {
|
|
110
|
+
const addClassName = `${containerClassName} ${scrollingEffectClassName}`
|
|
111
|
+
container.className = addClassName.trim()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
locks = [...locks!, { target: this.lockTarget, options: this.options! }]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
unLock = () => {
|
|
118
|
+
const findLock = locks.find(({ target }) => target === this.lockTarget)
|
|
119
|
+
|
|
120
|
+
locks = locks.filter(({ target }) => target !== this.lockTarget)
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
!findLock
|
|
124
|
+
|| locks.some(
|
|
125
|
+
({ options }) => options?.container === findLock.options?.container,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
// Remove Effect
|
|
131
|
+
const container = this.options?.container || document.body
|
|
132
|
+
const containerClassName = container.className
|
|
133
|
+
|
|
134
|
+
if (!scrollingEffectClassNameReg.test(containerClassName))
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
setStyle(cacheStyle.get(container)!, { element: container })
|
|
138
|
+
cacheStyle.delete(container)
|
|
139
|
+
container.className = container.className
|
|
140
|
+
.replace(scrollingEffectClassNameReg, '')
|
|
141
|
+
.trim()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function getRoot(ele: Node) {
|
|
2
|
+
return ele?.getRootNode?.()
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if is in shadowRoot
|
|
7
|
+
*/
|
|
8
|
+
export function inShadow(ele: Node) {
|
|
9
|
+
return getRoot(ele) instanceof ShadowRoot
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Return shadowRoot if possible
|
|
14
|
+
*/
|
|
15
|
+
export function getShadowRoot(ele: Node): ShadowRoot | null {
|
|
16
|
+
return inShadow(ele) ? (getRoot(ele) as ShadowRoot) : null
|
|
17
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import canUseDom from './canUseDom'
|
|
2
|
+
|
|
3
|
+
function isStyleNameSupport(styleName: string | string[]): boolean {
|
|
4
|
+
if (canUseDom() && window.document.documentElement) {
|
|
5
|
+
const styleNameList = Array.isArray(styleName) ? styleName : [styleName]
|
|
6
|
+
const { documentElement } = window.document
|
|
7
|
+
|
|
8
|
+
return styleNameList.some(name => name in documentElement.style)
|
|
9
|
+
}
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isStyleValueSupport(styleName: string, value: any) {
|
|
14
|
+
if (!isStyleNameSupport(styleName))
|
|
15
|
+
return false
|
|
16
|
+
|
|
17
|
+
const ele: any = document.createElement('div')
|
|
18
|
+
const origin = ele.style[styleName]
|
|
19
|
+
ele.style[styleName] = value
|
|
20
|
+
return ele.style[styleName] !== origin
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isStyleSupport(styleName: string | string[]): boolean
|
|
24
|
+
export function isStyleSupport(styleName: string, styleValue: any): boolean
|
|
25
|
+
|
|
26
|
+
export function isStyleSupport(styleName: string | string[], styleValue?: any) {
|
|
27
|
+
if (!Array.isArray(styleName) && styleValue !== undefined)
|
|
28
|
+
return isStyleValueSupport(styleName, styleValue)
|
|
29
|
+
|
|
30
|
+
return isStyleNameSupport(styleName)
|
|
31
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import canUseDOM from './canUseDom'
|
|
2
|
+
|
|
3
|
+
const animationEndEventNames = {
|
|
4
|
+
WebkitAnimation: 'webkitAnimationEnd',
|
|
5
|
+
OAnimation: 'oAnimationEnd',
|
|
6
|
+
animation: 'animationend',
|
|
7
|
+
}
|
|
8
|
+
const transitionEventNames = {
|
|
9
|
+
WebkitTransition: 'webkitTransitionEnd',
|
|
10
|
+
OTransition: 'oTransitionEnd',
|
|
11
|
+
transition: 'transitionend',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function supportEnd(names: Record<string, any>) {
|
|
15
|
+
const el: any = document.createElement('div')
|
|
16
|
+
for (const name in names) {
|
|
17
|
+
if (names.hasOwnProperty(name) && el.style[name] !== undefined) {
|
|
18
|
+
return {
|
|
19
|
+
end: names[name],
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const animation = canUseDOM() && supportEnd(animationEndEventNames)
|
|
27
|
+
export const transition = canUseDOM() && supportEnd(transitionEventNames)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type FocusEventHandler = (e: FocusEvent) => void
|
|
2
|
+
export type MouseEventHandler = (e: MouseEvent) => void
|
|
3
|
+
export type KeyboardEventHandler = (e: KeyboardEvent) => void
|
|
4
|
+
export type CompositionEventHandler = (e: CompositionEvent) => void
|
|
5
|
+
export type ClipboardEventHandler = (e: ClipboardEvent) => void
|
|
6
|
+
export type ChangeEventHandler = (e: ChangeEvent) => void
|
|
7
|
+
export type WheelEventHandler = (e: WheelEvent) => void
|
|
8
|
+
export type ChangeEvent = Event & {
|
|
9
|
+
target: {
|
|
10
|
+
value?: string | undefined
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export type CheckboxChangeEvent = Event & {
|
|
14
|
+
target: {
|
|
15
|
+
checked?: boolean
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type EventHandler = (...args: any[]) => void
|