@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
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { SlotsType } from 'vue'
|
|
2
|
+
import { defineComponent, onBeforeUnmount, onMounted, onUpdated, shallowRef } from 'vue'
|
|
3
|
+
import raf from './raf'
|
|
4
|
+
import canUseDom from './Dom/canUseDom'
|
|
5
|
+
import ScrollLocker from './Dom/scrollLocker'
|
|
6
|
+
import setStyle from './setStyle'
|
|
7
|
+
import type { PortalRef } from './Portal'
|
|
8
|
+
import Portal from './Portal'
|
|
9
|
+
|
|
10
|
+
// import raf from './raf';
|
|
11
|
+
|
|
12
|
+
let openCount = 0
|
|
13
|
+
const supportDom = canUseDom()
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
export function getOpenCount() {
|
|
19
|
+
return process.env.NODE_ENV === 'test' ? openCount : 0
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// https://github.com/ant-design/ant-design/issues/19340
|
|
23
|
+
// https://github.com/ant-design/ant-design/issues/19332
|
|
24
|
+
let cacheOverflow = {}
|
|
25
|
+
|
|
26
|
+
function getParent(getContainer: GetContainer) {
|
|
27
|
+
if (!supportDom)
|
|
28
|
+
return null
|
|
29
|
+
|
|
30
|
+
if (getContainer) {
|
|
31
|
+
if (typeof getContainer === 'string')
|
|
32
|
+
return document.querySelectorAll(getContainer)[0]
|
|
33
|
+
|
|
34
|
+
if (typeof getContainer === 'function')
|
|
35
|
+
return getContainer()
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
typeof getContainer === 'object'
|
|
39
|
+
&& getContainer instanceof window.HTMLElement
|
|
40
|
+
)
|
|
41
|
+
return getContainer
|
|
42
|
+
}
|
|
43
|
+
return document.body
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type GetContainer = string | HTMLElement | (() => HTMLElement)
|
|
47
|
+
|
|
48
|
+
export type DefaultSlotInfo = SlotsType<{
|
|
49
|
+
default: {
|
|
50
|
+
getOpenCount: () => number
|
|
51
|
+
getContainer: () => HTMLElement
|
|
52
|
+
switchScrollingEffect: () => void
|
|
53
|
+
scrollLocker: ScrollLocker
|
|
54
|
+
}
|
|
55
|
+
}>
|
|
56
|
+
|
|
57
|
+
export interface PortalWrapperProps {
|
|
58
|
+
visible?: boolean
|
|
59
|
+
getContainer?: GetContainer
|
|
60
|
+
wrapperClassName?: string
|
|
61
|
+
forceRender?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const PortalWrapper = defineComponent<PortalWrapperProps, any, string, DefaultSlotInfo>((props, ctx) => {
|
|
65
|
+
const container = shallowRef<HTMLElement>()
|
|
66
|
+
const componentRef = shallowRef<PortalRef>()
|
|
67
|
+
const rafId = shallowRef<number>()
|
|
68
|
+
const scrollLocker = shallowRef<ScrollLocker>()
|
|
69
|
+
|
|
70
|
+
const removeCurrentContainer = () => {
|
|
71
|
+
// Portal will remove from `parentNode`.
|
|
72
|
+
// Let's handle this again to avoid refactor issue.
|
|
73
|
+
container.value?.parentNode?.removeChild(container.value)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const updateOpenCount = (prevProps?: Partial<PortalWrapperProps>) => {
|
|
77
|
+
const { visible: prevVisible, getContainer: prevGetContainer }
|
|
78
|
+
= prevProps || {}
|
|
79
|
+
const { visible, getContainer } = props
|
|
80
|
+
|
|
81
|
+
// Update count
|
|
82
|
+
if (
|
|
83
|
+
visible !== prevVisible
|
|
84
|
+
&& supportDom
|
|
85
|
+
&& getParent(getContainer!) === document.body
|
|
86
|
+
) {
|
|
87
|
+
if (visible && !prevVisible)
|
|
88
|
+
openCount += 1
|
|
89
|
+
else if (prevProps)
|
|
90
|
+
openCount -= 1
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Clean up container if needed
|
|
94
|
+
const getContainerIsFunc
|
|
95
|
+
= typeof getContainer === 'function'
|
|
96
|
+
&& typeof prevGetContainer === 'function'
|
|
97
|
+
if (
|
|
98
|
+
getContainerIsFunc
|
|
99
|
+
? getContainer.toString() !== prevGetContainer.toString()
|
|
100
|
+
: getContainer !== prevGetContainer
|
|
101
|
+
)
|
|
102
|
+
removeCurrentContainer()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const attachToParent = (force = false) => {
|
|
106
|
+
if (force || (container.value && !container.value.parentNode)) {
|
|
107
|
+
const parent = getParent(props.getContainer!)
|
|
108
|
+
if (parent) {
|
|
109
|
+
parent.appendChild(container.value!)
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
const setWrapperClassName = () => {
|
|
119
|
+
const { wrapperClassName } = props
|
|
120
|
+
if (
|
|
121
|
+
container.value
|
|
122
|
+
&& wrapperClassName
|
|
123
|
+
&& wrapperClassName !== container.value.className
|
|
124
|
+
)
|
|
125
|
+
container.value.className = wrapperClassName
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const getContainer = () => {
|
|
129
|
+
if (!supportDom)
|
|
130
|
+
return null
|
|
131
|
+
|
|
132
|
+
if (!container.value) {
|
|
133
|
+
container.value = document.createElement('div')
|
|
134
|
+
attachToParent(true)
|
|
135
|
+
}
|
|
136
|
+
setWrapperClassName()
|
|
137
|
+
return container.value
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
onMounted(() => {
|
|
141
|
+
scrollLocker.value = new ScrollLocker({
|
|
142
|
+
container: getParent(props.getContainer!) as HTMLElement,
|
|
143
|
+
})
|
|
144
|
+
updateOpenCount()
|
|
145
|
+
if (!attachToParent()) {
|
|
146
|
+
rafId.value = raf(() => {
|
|
147
|
+
// 对组件执行强制更新 forceUpdate,准备采用改变key的方式来实现
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
onUpdated(() => {
|
|
153
|
+
updateOpenCount(props)
|
|
154
|
+
updateOpenCount(props)
|
|
155
|
+
setWrapperClassName()
|
|
156
|
+
attachToParent()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
onBeforeUnmount(() => {
|
|
160
|
+
const { visible, getContainer } = props
|
|
161
|
+
if (supportDom && getParent(getContainer!) === document.body) {
|
|
162
|
+
// 离开时不会 render, 导到离开时数值不变,改用 func 。。
|
|
163
|
+
openCount = visible && openCount ? openCount - 1 : openCount
|
|
164
|
+
}
|
|
165
|
+
removeCurrentContainer()
|
|
166
|
+
raf.cancel(rafId.value!)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Enhance ./switchScrollingEffect
|
|
171
|
+
* 1. Simulate document body scroll bar with
|
|
172
|
+
* 2. Record body has overflow style and recover when all of PortalWrapper invisible
|
|
173
|
+
* 3. Disable body scroll when PortalWrapper has open
|
|
174
|
+
*
|
|
175
|
+
* @memberof PortalWrapper
|
|
176
|
+
*/
|
|
177
|
+
const switchScrollingEffect = () => {
|
|
178
|
+
if (openCount === 1 && !Object.keys(cacheOverflow).length) {
|
|
179
|
+
switchScrollingEffect()
|
|
180
|
+
// Must be set after switchScrollingEffect
|
|
181
|
+
cacheOverflow = setStyle({
|
|
182
|
+
overflow: 'hidden',
|
|
183
|
+
overflowX: 'hidden',
|
|
184
|
+
overflowY: 'hidden',
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
else if (!openCount) {
|
|
188
|
+
setStyle(cacheOverflow)
|
|
189
|
+
cacheOverflow = {}
|
|
190
|
+
switchScrollingEffect()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return () => {
|
|
194
|
+
const { forceRender, visible } = props
|
|
195
|
+
let portal = null
|
|
196
|
+
const childProps = {
|
|
197
|
+
getOpenCount,
|
|
198
|
+
getContainer,
|
|
199
|
+
switchScrollingEffect,
|
|
200
|
+
scrollLocker: scrollLocker.value,
|
|
201
|
+
}
|
|
202
|
+
if (forceRender || visible || componentRef.value) {
|
|
203
|
+
portal = (
|
|
204
|
+
<Portal getContainer={getContainer as any} ref={componentRef}>
|
|
205
|
+
{ctx?.slots?.default(childProps as any)}
|
|
206
|
+
</Portal>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
return portal
|
|
210
|
+
}
|
|
211
|
+
// TODO
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
export default PortalWrapper
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function composeProps<T extends Record<string, any>>(
|
|
2
|
+
originProps: T,
|
|
3
|
+
patchProps: Partial<T>,
|
|
4
|
+
isAll?: boolean,
|
|
5
|
+
) {
|
|
6
|
+
const composedProps: Record<string, any> = {
|
|
7
|
+
...originProps,
|
|
8
|
+
...(isAll ? patchProps : {}),
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
Object.keys(patchProps).forEach((key) => {
|
|
12
|
+
const func = patchProps[key]
|
|
13
|
+
if (typeof func === 'function') {
|
|
14
|
+
composedProps[key] = (...args: any[]) => {
|
|
15
|
+
func(...args)
|
|
16
|
+
return originProps[key]?.(...args)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
return composedProps
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default composeProps
|
package/src/createRef.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line ts/no-unsafe-function-type
|
|
4
|
+
export interface RefObject extends Function {
|
|
5
|
+
current?: any
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function createRef(): any {
|
|
9
|
+
const func: RefObject = (node: any) => {
|
|
10
|
+
func.current = node
|
|
11
|
+
}
|
|
12
|
+
return func
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function fillRef<T>(ref: Ref, node: T) {
|
|
16
|
+
if (typeof ref === 'function')
|
|
17
|
+
(ref as any)(node)
|
|
18
|
+
else if (typeof ref === 'object' && ref && 'current' in ref)
|
|
19
|
+
(ref as any).current = node
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Merge refs into one ref function to support ref passing.
|
|
24
|
+
*/
|
|
25
|
+
export function composeRef<T>(...refs: any[]) {
|
|
26
|
+
return (node: T) => {
|
|
27
|
+
refs.forEach((ref) => {
|
|
28
|
+
fillRef(ref, node)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default createRef
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// eslint-disable-next-line ts/ban-ts-comment
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
function createArray() {
|
|
4
|
+
const arr: any[] = []
|
|
5
|
+
// eslint-disable-next-line no-proto,no-restricted-properties
|
|
6
|
+
arr.__proto__ = []
|
|
7
|
+
// eslint-disable-next-line no-proto,no-restricted-properties
|
|
8
|
+
arr.__proto__.format = function toString() {
|
|
9
|
+
return this.map(obj => ({
|
|
10
|
+
...obj,
|
|
11
|
+
path: obj.path.join(' > '),
|
|
12
|
+
}))
|
|
13
|
+
}
|
|
14
|
+
// eslint-disable-next-line no-proto,no-restricted-properties
|
|
15
|
+
arr.__proto__.toString = function toString() {
|
|
16
|
+
return JSON.stringify(this.format(), null, 2)
|
|
17
|
+
}
|
|
18
|
+
return arr
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function diff(obj1, obj2, depth = 10, path = [], diffList = createArray()) {
|
|
22
|
+
if (depth <= 0)
|
|
23
|
+
return diffList
|
|
24
|
+
|
|
25
|
+
const keys = new Set([...Object.keys(obj1), ...Object.keys(obj2)])
|
|
26
|
+
keys.forEach((key) => {
|
|
27
|
+
const value1 = obj1[key]
|
|
28
|
+
const value2 = obj2[key]
|
|
29
|
+
|
|
30
|
+
// Same value
|
|
31
|
+
if (value1 === value2)
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
const type1 = typeof value1
|
|
35
|
+
const type2 = typeof value2
|
|
36
|
+
|
|
37
|
+
// Diff type
|
|
38
|
+
if (type1 !== type2) {
|
|
39
|
+
diffList.push({
|
|
40
|
+
path: path.concat(key),
|
|
41
|
+
value1,
|
|
42
|
+
value2,
|
|
43
|
+
})
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// NaN
|
|
48
|
+
if (Number.isNaN(value1) && Number.isNaN(value2))
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
// Object & Array
|
|
52
|
+
if (type1 === 'object' && value1 !== null && value2 !== null) {
|
|
53
|
+
diff(value1, value2, depth - 1, path.concat(key), diffList)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Rest
|
|
58
|
+
diffList.push({
|
|
59
|
+
path: path.concat(key),
|
|
60
|
+
value1,
|
|
61
|
+
value2,
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return diffList
|
|
66
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default function deprecated(props: any, instead: any, component: any) {
|
|
2
|
+
if (typeof window !== 'undefined' && window.console && window.console.error) {
|
|
3
|
+
window.console.error(
|
|
4
|
+
`Warning: ${props} is deprecated at [ ${component} ], `
|
|
5
|
+
+ `use [ ${instead} ] instead of it.`,
|
|
6
|
+
)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
let cached: number
|
|
2
|
+
|
|
3
|
+
export default function getScrollBarSize(fresh?: boolean) {
|
|
4
|
+
if (typeof document === 'undefined')
|
|
5
|
+
return 0
|
|
6
|
+
|
|
7
|
+
if (fresh || cached === undefined) {
|
|
8
|
+
const inner = document.createElement('div')
|
|
9
|
+
inner.style.width = '100%'
|
|
10
|
+
inner.style.height = '200px'
|
|
11
|
+
|
|
12
|
+
const outer = document.createElement('div')
|
|
13
|
+
const outerStyle = outer.style
|
|
14
|
+
|
|
15
|
+
outerStyle.position = 'absolute'
|
|
16
|
+
outerStyle.top = '0'
|
|
17
|
+
outerStyle.left = '0'
|
|
18
|
+
outerStyle.pointerEvents = 'none'
|
|
19
|
+
outerStyle.visibility = 'hidden'
|
|
20
|
+
outerStyle.width = '200px'
|
|
21
|
+
outerStyle.height = '150px'
|
|
22
|
+
outerStyle.overflow = 'hidden'
|
|
23
|
+
|
|
24
|
+
outer.appendChild(inner)
|
|
25
|
+
|
|
26
|
+
document.body.appendChild(outer)
|
|
27
|
+
|
|
28
|
+
const widthContained = inner.offsetWidth
|
|
29
|
+
outer.style.overflow = 'scroll'
|
|
30
|
+
let widthScroll = inner.offsetWidth
|
|
31
|
+
|
|
32
|
+
if (widthContained === widthScroll)
|
|
33
|
+
widthScroll = outer.clientWidth
|
|
34
|
+
|
|
35
|
+
document.body.removeChild(outer)
|
|
36
|
+
|
|
37
|
+
cached = widthContained - widthScroll
|
|
38
|
+
}
|
|
39
|
+
return cached
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ensureSize(str: string) {
|
|
43
|
+
const match = str.match(/^(.*)px$/)
|
|
44
|
+
const value = Number(match?.[1])
|
|
45
|
+
return Number.isNaN(value) ? getScrollBarSize() : value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getTargetScrollBarSize(target: HTMLElement) {
|
|
49
|
+
if (typeof document === 'undefined' || !target || !(target instanceof Element))
|
|
50
|
+
return { width: 0, height: 0 }
|
|
51
|
+
|
|
52
|
+
const { width, height } = getComputedStyle(target, '::-webkit-scrollbar')
|
|
53
|
+
return {
|
|
54
|
+
width: ensureSize(width),
|
|
55
|
+
height: ensureSize(height),
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/guid.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import canUseDom from '../Dom/canUseDom'
|
|
3
|
+
|
|
4
|
+
let uuid = 0
|
|
5
|
+
|
|
6
|
+
/** Is client side and not jsdom */
|
|
7
|
+
export const isBrowserClient = process.env.NODE_ENV !== 'test' && canUseDom()
|
|
8
|
+
|
|
9
|
+
/** Get unique id for accessibility usage */
|
|
10
|
+
export function getUUID(): number | string {
|
|
11
|
+
let retId: string | number
|
|
12
|
+
|
|
13
|
+
// Test never reach
|
|
14
|
+
/* istanbul ignore if */
|
|
15
|
+
if (isBrowserClient) {
|
|
16
|
+
retId = uuid
|
|
17
|
+
uuid += 1
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
retId = 'TEST_OR_SSR'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return retId
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function useId(id = ref('')) {
|
|
27
|
+
// Inner id for accessibility usage. Only work in client side
|
|
28
|
+
const innerId = `vc_unique_${getUUID()}`
|
|
29
|
+
|
|
30
|
+
return id.value || innerId
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Ref, WatchSource } from 'vue'
|
|
2
|
+
import { ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
export default function useMemo<T>(
|
|
5
|
+
getValue: () => T,
|
|
6
|
+
condition: (WatchSource<unknown> | object)[],
|
|
7
|
+
shouldUpdate?: (prev: any[], next: any[]) => boolean,
|
|
8
|
+
) {
|
|
9
|
+
const cacheRef: Ref<T> = ref(getValue() as any)
|
|
10
|
+
watch(condition, (next, pre) => {
|
|
11
|
+
if (shouldUpdate) {
|
|
12
|
+
if (shouldUpdate(next, pre))
|
|
13
|
+
cacheRef.value = getValue()
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
cacheRef.value = getValue()
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return cacheRef
|
|
21
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, UnwrapRef } from 'vue'
|
|
2
|
+
import { ref, toRaw, unref, watch, watchEffect } from 'vue'
|
|
3
|
+
|
|
4
|
+
export default function useMergedState<T, R = Ref<T>>(
|
|
5
|
+
defaultStateValue: T | (() => T),
|
|
6
|
+
option?: {
|
|
7
|
+
defaultValue?: T | (() => T)
|
|
8
|
+
value?: Ref<T> | Ref<UnwrapRef<T>> | ComputedRef<T>
|
|
9
|
+
onChange?: (val: T, prevValue: T) => void
|
|
10
|
+
postState?: (val: T) => T
|
|
11
|
+
},
|
|
12
|
+
): [R, (val: T) => void] {
|
|
13
|
+
const { defaultValue, value = ref() } = option || {}
|
|
14
|
+
let initValue: T
|
|
15
|
+
= typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue
|
|
16
|
+
if (value.value !== undefined)
|
|
17
|
+
initValue = unref(value as any) as T
|
|
18
|
+
|
|
19
|
+
if (defaultValue !== undefined)
|
|
20
|
+
initValue = typeof defaultValue === 'function' ? (defaultValue as any)() : defaultValue
|
|
21
|
+
|
|
22
|
+
const innerValue = ref(initValue) as Ref<T>
|
|
23
|
+
const mergedValue = ref(initValue) as Ref<T>
|
|
24
|
+
watchEffect(() => {
|
|
25
|
+
let val = value.value !== undefined ? value.value : innerValue.value
|
|
26
|
+
if (option?.postState)
|
|
27
|
+
val = option.postState(val as T)
|
|
28
|
+
mergedValue.value = val as T
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
function triggerChange(newValue: T) {
|
|
32
|
+
const preVal = mergedValue.value
|
|
33
|
+
innerValue.value = newValue
|
|
34
|
+
if (toRaw(mergedValue.value) !== newValue && option?.onChange)
|
|
35
|
+
option.onChange(newValue, preVal)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Effect of reset value to `undefined`
|
|
39
|
+
watch(value, () => {
|
|
40
|
+
innerValue.value = value.value as T
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return [mergedValue as unknown as R, triggerChange]
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
export default function useState<T, R = Ref<T>>(
|
|
5
|
+
defaultStateValue?: T | (() => T),
|
|
6
|
+
): [R, (val: T) => void] {
|
|
7
|
+
const initValue: T
|
|
8
|
+
= typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue
|
|
9
|
+
|
|
10
|
+
const innerValue = ref(initValue) as Ref<T>
|
|
11
|
+
|
|
12
|
+
function triggerChange(newValue: T) {
|
|
13
|
+
innerValue.value = newValue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return [innerValue as unknown as R, triggerChange]
|
|
17
|
+
}
|
package/src/index.ts
ADDED
package/src/isEqual.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import warning from './warning'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deeply compares two object literals.
|
|
5
|
+
* @param obj1 object 1
|
|
6
|
+
* @param obj2 object 2
|
|
7
|
+
* @param shallow shallow compare
|
|
8
|
+
*/
|
|
9
|
+
function isEqual(obj1: any, obj2: any, shallow = false): boolean {
|
|
10
|
+
// https://github.com/mapbox/mapbox-gl-js/pull/5979/files#diff-fde7145050c47cc3a306856efd5f9c3016e86e859de9afbd02c879be5067e58f
|
|
11
|
+
const refSet = new Set<any>()
|
|
12
|
+
function deepEqual(a: any, b: any, level = 1): boolean {
|
|
13
|
+
const circular = refSet.has(a)
|
|
14
|
+
warning(!circular, 'Warning: There may be circular references')
|
|
15
|
+
if (circular)
|
|
16
|
+
return false
|
|
17
|
+
|
|
18
|
+
if (a === b)
|
|
19
|
+
return true
|
|
20
|
+
|
|
21
|
+
if (shallow && level > 1)
|
|
22
|
+
return false
|
|
23
|
+
|
|
24
|
+
refSet.add(a)
|
|
25
|
+
const newLevel = level + 1
|
|
26
|
+
if (Array.isArray(a)) {
|
|
27
|
+
if (!Array.isArray(b) || a.length !== b.length)
|
|
28
|
+
return false
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < a.length; i++) {
|
|
31
|
+
if (!deepEqual(a[i], b[i], newLevel))
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
37
|
+
const keys = Object.keys(a)
|
|
38
|
+
if (keys.length !== Object.keys(b).length)
|
|
39
|
+
return false
|
|
40
|
+
|
|
41
|
+
return keys.every(key => deepEqual(a[key], b[key], newLevel))
|
|
42
|
+
}
|
|
43
|
+
// other
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return deepEqual(obj1, obj2)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default isEqual
|
package/src/isMobile.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default () => {
|
|
2
|
+
if (typeof navigator === 'undefined' || typeof window === 'undefined')
|
|
3
|
+
return false
|
|
4
|
+
|
|
5
|
+
const agent
|
|
6
|
+
= navigator.userAgent || navigator.vendor || (window as any).opera
|
|
7
|
+
return (
|
|
8
|
+
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
|
|
9
|
+
agent,
|
|
10
|
+
)
|
|
11
|
+
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
|
|
12
|
+
agent?.substr(0, 4),
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
}
|
package/src/omit.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default function omit<T extends object, K extends keyof T>(
|
|
2
|
+
obj: T,
|
|
3
|
+
fields: K[] | readonly K[],
|
|
4
|
+
): Omit<T, K> {
|
|
5
|
+
const clone = { ...obj }
|
|
6
|
+
|
|
7
|
+
if (Array.isArray(fields)) {
|
|
8
|
+
fields.forEach((key) => {
|
|
9
|
+
delete clone[key]
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return clone
|
|
14
|
+
}
|
package/src/pickAttrs.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const attributes = `accept acceptCharset accessKey action allowFullScreen allowTransparency
|
|
2
|
+
alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
|
|
3
|
+
charSet checked classID className colSpan cols content contentEditable contextMenu
|
|
4
|
+
controls coords crossOrigin data dateTime default defer dir disabled download draggable
|
|
5
|
+
encType form formAction formEncType formMethod formNoValidate formTarget frameBorder
|
|
6
|
+
headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity
|
|
7
|
+
is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media
|
|
8
|
+
mediaGroup method min minLength multiple muted name noValidate nonce open
|
|
9
|
+
optimum pattern placeholder poster preload radioGroup readOnly rel required
|
|
10
|
+
reversed role rowSpan rows sandbox scope scoped scrolling seamless selected
|
|
11
|
+
shape size sizes span spellCheck src srcDoc srcLang srcSet start step style
|
|
12
|
+
summary tabIndex target title type useMap value width wmode wrap`
|
|
13
|
+
|
|
14
|
+
const eventsName = `onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown
|
|
15
|
+
onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick
|
|
16
|
+
onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown
|
|
17
|
+
onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel
|
|
18
|
+
onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough
|
|
19
|
+
onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata
|
|
20
|
+
onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError`
|
|
21
|
+
|
|
22
|
+
const propList = `${attributes} ${eventsName}`.split(/[\s\n]+/)
|
|
23
|
+
|
|
24
|
+
const ariaPrefix = 'aria-'
|
|
25
|
+
const dataPrefix = 'data-'
|
|
26
|
+
|
|
27
|
+
function match(key: string, prefix: string) {
|
|
28
|
+
return key.indexOf(prefix) === 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PickConfig {
|
|
32
|
+
aria?: boolean
|
|
33
|
+
data?: boolean
|
|
34
|
+
attr?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Picker props from exist props with filter
|
|
39
|
+
* @param props Passed props
|
|
40
|
+
* @param ariaOnly boolean | { aria?: boolean; data?: boolean; attr?: boolean; } filter config
|
|
41
|
+
*/
|
|
42
|
+
export default function pickAttrs(
|
|
43
|
+
props: object,
|
|
44
|
+
ariaOnly: boolean | PickConfig = false,
|
|
45
|
+
) {
|
|
46
|
+
let mergedConfig: PickConfig
|
|
47
|
+
if (ariaOnly === false) {
|
|
48
|
+
mergedConfig = {
|
|
49
|
+
aria: true,
|
|
50
|
+
data: true,
|
|
51
|
+
attr: true,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (ariaOnly === true) {
|
|
55
|
+
mergedConfig = {
|
|
56
|
+
aria: true,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
mergedConfig = {
|
|
61
|
+
...ariaOnly,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const attrs: Record<string, any> = {}
|
|
66
|
+
Object.keys(props).forEach((key) => {
|
|
67
|
+
if (
|
|
68
|
+
// Aria
|
|
69
|
+
(mergedConfig.aria && (key === 'role' || match(key, ariaPrefix)))
|
|
70
|
+
// Data
|
|
71
|
+
|| (mergedConfig.data && match(key, dataPrefix))
|
|
72
|
+
// Attr
|
|
73
|
+
|| (mergedConfig.attr && propList.includes(key))
|
|
74
|
+
)
|
|
75
|
+
attrs[key] = (props as any)[key]
|
|
76
|
+
})
|
|
77
|
+
return attrs
|
|
78
|
+
}
|