@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,22 @@
|
|
|
1
|
+
import { Fragment } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function isEmptyElement(c: any) {
|
|
4
|
+
return (
|
|
5
|
+
c
|
|
6
|
+
&& (c.type === Comment
|
|
7
|
+
|| (c.type === Fragment && c.children.length === 0)
|
|
8
|
+
|| (c.type === Text && c.children.trim() === ''))
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
export function filterEmpty(children: any[] = []) {
|
|
12
|
+
const res: any[] = []
|
|
13
|
+
children.forEach((child: any) => {
|
|
14
|
+
if (Array.isArray(child))
|
|
15
|
+
res.push(...child)
|
|
16
|
+
else if (child?.type === Fragment)
|
|
17
|
+
res.push(...filterEmpty(child.children))
|
|
18
|
+
else
|
|
19
|
+
res.push(child)
|
|
20
|
+
})
|
|
21
|
+
return res.filter(c => !isEmptyElement(c))
|
|
22
|
+
}
|
package/src/raf.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
let raf = (callback: FrameRequestCallback) => +setTimeout(callback, 16)
|
|
2
|
+
let caf = (num: number) => clearTimeout(num)
|
|
3
|
+
|
|
4
|
+
if (typeof window !== 'undefined' && 'requestAnimationFrame' in window) {
|
|
5
|
+
raf = (callback: FrameRequestCallback) =>
|
|
6
|
+
window.requestAnimationFrame(callback)
|
|
7
|
+
caf = (handle: number) => window.cancelAnimationFrame(handle)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let rafUUID = 0
|
|
11
|
+
|
|
12
|
+
const rafIds = new Map<number, number>()
|
|
13
|
+
|
|
14
|
+
function cleanup(id: number) {
|
|
15
|
+
rafIds.delete(id)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function wrapperRaf(callback: () => void, times = 1): number {
|
|
19
|
+
rafUUID += 1
|
|
20
|
+
const id = rafUUID
|
|
21
|
+
|
|
22
|
+
function callRef(leftTimes: number) {
|
|
23
|
+
if (leftTimes === 0) {
|
|
24
|
+
// Clean up
|
|
25
|
+
cleanup(id)
|
|
26
|
+
|
|
27
|
+
// Trigger
|
|
28
|
+
callback()
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Next raf
|
|
32
|
+
const realId = raf(() => {
|
|
33
|
+
callRef(leftTimes - 1)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Bind real raf id
|
|
37
|
+
rafIds.set(id, realId)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
callRef(times)
|
|
42
|
+
|
|
43
|
+
return id
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
wrapperRaf.cancel = (id: number) => {
|
|
47
|
+
const realId = rafIds.get(id)
|
|
48
|
+
cleanup(id)
|
|
49
|
+
return caf(realId!)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (process.env.NODE_ENV !== 'production')
|
|
53
|
+
wrapperRaf.ids = () => rafIds
|
|
54
|
+
|
|
55
|
+
export default wrapperRaf
|
package/src/setStyle.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CSSProperties } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface SetStyleOptions {
|
|
4
|
+
element?: HTMLElement
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Easy to set element style, return previous style
|
|
9
|
+
* IE browser compatible(IE browser doesn't merge overflow style, need to set it separately)
|
|
10
|
+
* https://github.com/ant-design/ant-design/issues/19393
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function setStyle(
|
|
15
|
+
style: CSSProperties,
|
|
16
|
+
options: SetStyleOptions = {},
|
|
17
|
+
): CSSProperties {
|
|
18
|
+
if (!style)
|
|
19
|
+
return {}
|
|
20
|
+
|
|
21
|
+
const { element = document.body } = options
|
|
22
|
+
const oldStyle: CSSProperties = {}
|
|
23
|
+
|
|
24
|
+
const styleKeys = Object.keys(style)
|
|
25
|
+
|
|
26
|
+
// IE browser compatible
|
|
27
|
+
styleKeys.forEach((key: any) => {
|
|
28
|
+
oldStyle[key] = element.style[key]
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
styleKeys.forEach((key: any) => {
|
|
32
|
+
(element as any).style[key] = style[key]
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return oldStyle
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default setStyle
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import getScrollBarSize from './getScrollBarSize'
|
|
2
|
+
import setStyle from './setStyle'
|
|
3
|
+
|
|
4
|
+
function isBodyOverflowing() {
|
|
5
|
+
return (
|
|
6
|
+
document.body.scrollHeight
|
|
7
|
+
> (window.innerHeight || document.documentElement.clientHeight)
|
|
8
|
+
&& window.innerWidth > document.body.offsetWidth
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let cacheStyle = {}
|
|
13
|
+
|
|
14
|
+
export default (close: any) => {
|
|
15
|
+
if (!isBodyOverflowing() && !close)
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
// https://github.com/ant-design/ant-design/issues/19729
|
|
19
|
+
const scrollingEffectClassName = 'ant-scrolling-effect'
|
|
20
|
+
const scrollingEffectClassNameReg = new RegExp(
|
|
21
|
+
`${scrollingEffectClassName}`,
|
|
22
|
+
'g',
|
|
23
|
+
)
|
|
24
|
+
const bodyClassName = document.body.className
|
|
25
|
+
|
|
26
|
+
if (close) {
|
|
27
|
+
if (!scrollingEffectClassNameReg.test(bodyClassName))
|
|
28
|
+
return
|
|
29
|
+
setStyle(cacheStyle)
|
|
30
|
+
cacheStyle = {}
|
|
31
|
+
document.body.className = bodyClassName
|
|
32
|
+
.replace(scrollingEffectClassNameReg, '')
|
|
33
|
+
.trim()
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const scrollBarSize = getScrollBarSize()
|
|
38
|
+
if (scrollBarSize) {
|
|
39
|
+
cacheStyle = setStyle({
|
|
40
|
+
position: 'relative',
|
|
41
|
+
width: `calc(100% - ${scrollBarSize}px)`,
|
|
42
|
+
})
|
|
43
|
+
if (!scrollingEffectClassNameReg.test(bodyClassName)) {
|
|
44
|
+
const addClassName = `${bodyClassName} ${scrollingEffectClassName}`
|
|
45
|
+
document.body.className = addClassName.trim()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const NO_EXIST = { __NOT_EXIST: true }
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line ts/no-unsafe-function-type
|
|
4
|
+
export type ElementClass = Function
|
|
5
|
+
// eslint-disable-next-line ts/no-unsafe-function-type
|
|
6
|
+
export type Property = PropertyDescriptor | Function
|
|
7
|
+
|
|
8
|
+
export function spyElementPrototypes<T extends ElementClass>(
|
|
9
|
+
elementClass: T,
|
|
10
|
+
properties: Record<string, Property>,
|
|
11
|
+
) {
|
|
12
|
+
const propNames = Object.keys(properties)
|
|
13
|
+
const originDescriptors: any = {}
|
|
14
|
+
|
|
15
|
+
propNames.forEach((propName) => {
|
|
16
|
+
const originDescriptor: any = Object.getOwnPropertyDescriptor(elementClass.prototype, propName)
|
|
17
|
+
originDescriptors[propName] = originDescriptor || NO_EXIST
|
|
18
|
+
|
|
19
|
+
const spyProp: any = properties[propName]
|
|
20
|
+
|
|
21
|
+
if (typeof spyProp === 'function') {
|
|
22
|
+
// If is a function
|
|
23
|
+
elementClass.prototype[propName] = function spyFunc(...args: any[]) {
|
|
24
|
+
return spyProp.call(this, originDescriptor, ...args)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Otherwise tread as a property
|
|
29
|
+
Object.defineProperty(elementClass.prototype, propName, {
|
|
30
|
+
...spyProp,
|
|
31
|
+
set(value) {
|
|
32
|
+
if (spyProp.set)
|
|
33
|
+
return (spyProp as any).set.call(this, originDescriptor, value)
|
|
34
|
+
|
|
35
|
+
return originDescriptor.set(value)
|
|
36
|
+
},
|
|
37
|
+
get() {
|
|
38
|
+
if (spyProp.get)
|
|
39
|
+
return spyProp.get.call(this, originDescriptor)
|
|
40
|
+
|
|
41
|
+
return originDescriptor.get()
|
|
42
|
+
},
|
|
43
|
+
configurable: true,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
mockRestore() {
|
|
50
|
+
propNames.forEach((propName) => {
|
|
51
|
+
const originDescriptor = originDescriptors[propName]
|
|
52
|
+
if (originDescriptor === NO_EXIST)
|
|
53
|
+
delete elementClass.prototype[propName]
|
|
54
|
+
else if (typeof originDescriptor === 'function')
|
|
55
|
+
elementClass.prototype[propName] = originDescriptor
|
|
56
|
+
else
|
|
57
|
+
Object.defineProperty(elementClass.prototype, propName, originDescriptor)
|
|
58
|
+
})
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function spyElementPrototype(Element: ElementClass, propName: string, property: Property) {
|
|
64
|
+
return spyElementPrototypes(Element, {
|
|
65
|
+
[propName]: property,
|
|
66
|
+
})
|
|
67
|
+
}
|
package/src/type.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { App, Plugin, PropType, Ref, SlotsType, VNode } from 'vue'
|
|
2
|
+
|
|
3
|
+
// https://stackoverflow.com/questions/46176165/ways-to-get-string-literal-type-of-array-values-without-enum-overhead
|
|
4
|
+
export const tuple = <T extends string[]>(...args: T) => args
|
|
5
|
+
|
|
6
|
+
export const tupleNum = <T extends number[]>(...args: T) => args
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* https://stackoverflow.com/a/59187769
|
|
10
|
+
* Extract the type of an element of an array/tuple without performing indexing
|
|
11
|
+
*/
|
|
12
|
+
export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* https://github.com/Microsoft/TypeScript/issues/29729
|
|
16
|
+
*/
|
|
17
|
+
export type LiteralUnion<T extends string> = T | (string & {})
|
|
18
|
+
|
|
19
|
+
export type Data = Record<string, unknown>
|
|
20
|
+
|
|
21
|
+
export type Key = string | number
|
|
22
|
+
|
|
23
|
+
type DefaultFactory<T> = (props: Data) => T | null | undefined
|
|
24
|
+
|
|
25
|
+
export interface PropOptions<T = any, D = T> {
|
|
26
|
+
type?: PropType<T> | true | null
|
|
27
|
+
required?: boolean
|
|
28
|
+
default?: D | DefaultFactory<D> | null | undefined | object
|
|
29
|
+
validator?: (value: unknown) => boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare type VNodeChildAtom = VNode | string | number | boolean | null | undefined | void
|
|
33
|
+
|
|
34
|
+
export type VueNode = VNodeChildAtom | VNodeChildAtom[] | VNode
|
|
35
|
+
|
|
36
|
+
export function withInstall<T>(comp: T) {
|
|
37
|
+
const c = comp as any
|
|
38
|
+
c.install = function (app: App) {
|
|
39
|
+
app.component(c.displayName || c.name, comp as any)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return comp as T & Plugin
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type MaybeRef<T> = T | Ref<T>
|
|
46
|
+
|
|
47
|
+
export function eventType<T>() {
|
|
48
|
+
return { type: [Function, Array] as PropType<T | T[]> }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function objectType<T = object>(defaultVal?: T) {
|
|
52
|
+
return { type: Object as PropType<T>, default: defaultVal as T }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function booleanType(defaultVal?: boolean) {
|
|
56
|
+
return { type: Boolean, default: defaultVal as boolean }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function functionType<T = () => object>(defaultVal?: T) {
|
|
60
|
+
return { type: Function as PropType<T>, default: defaultVal as T }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function anyType<T = any>(defaultVal?: T, required?: boolean) {
|
|
64
|
+
const type = { validator: () => true, default: defaultVal as T } as unknown
|
|
65
|
+
return required
|
|
66
|
+
? (type as {
|
|
67
|
+
type: PropType<T>
|
|
68
|
+
default: T
|
|
69
|
+
required: true
|
|
70
|
+
})
|
|
71
|
+
: (type as {
|
|
72
|
+
default: T
|
|
73
|
+
type: PropType<T>
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
export function vNodeType<T = VueNode>() {
|
|
77
|
+
return { validator: () => true } as unknown as { type: PropType<T> }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function arrayType<T extends any[]>(defaultVal?: T) {
|
|
81
|
+
return { type: Array as unknown as PropType<T>, default: defaultVal as T }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function stringType<T extends string = string>(defaultVal?: T) {
|
|
85
|
+
return { type: String as unknown as PropType<T>, default: defaultVal as T }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function someType<T>(types?: any[], defaultVal?: T) {
|
|
89
|
+
return types ? { type: types as PropType<T>, default: defaultVal as T } : anyType<T>(defaultVal)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type CustomSlotsType<T extends Record<string, any>> = SlotsType<T>
|
|
93
|
+
|
|
94
|
+
export type AnyObject = Record<PropertyKey, any>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function checkSlotProp(props: Record<string, any>, slots: Record<string, any>, name: string, ...args: any[]) {
|
|
2
|
+
if (slots[name])
|
|
3
|
+
return slots[name]?.(...args)
|
|
4
|
+
if (name in props) {
|
|
5
|
+
if (typeof props[name] === 'function')
|
|
6
|
+
return props[name]?.(...args)
|
|
7
|
+
return props[name]
|
|
8
|
+
}
|
|
9
|
+
return null
|
|
10
|
+
}
|
package/src/utils/get.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function get(
|
|
2
|
+
entity: any,
|
|
3
|
+
path: (string | number | symbol)[] | readonly (string | number | symbol)[],
|
|
4
|
+
) {
|
|
5
|
+
let current = entity
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
8
|
+
if (current === null || current === undefined)
|
|
9
|
+
return undefined
|
|
10
|
+
|
|
11
|
+
current = current[path[i]]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return current
|
|
15
|
+
}
|
package/src/utils/set.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import get from './get'
|
|
2
|
+
|
|
3
|
+
export type Path = (string | number | symbol)[]
|
|
4
|
+
|
|
5
|
+
function internalSet<Entity = any, Output = Entity, Value = any>(
|
|
6
|
+
entity: Entity,
|
|
7
|
+
paths: Path,
|
|
8
|
+
value: Value,
|
|
9
|
+
removeIfUndefined: boolean,
|
|
10
|
+
): Output {
|
|
11
|
+
if (!paths.length)
|
|
12
|
+
return (value as unknown) as Output
|
|
13
|
+
|
|
14
|
+
const [path, ...restPath] = paths
|
|
15
|
+
|
|
16
|
+
let clone: Output
|
|
17
|
+
if (!entity && typeof path === 'number')
|
|
18
|
+
clone = ([] as unknown) as Output
|
|
19
|
+
else if (Array.isArray(entity))
|
|
20
|
+
clone = ([...entity] as unknown) as Output
|
|
21
|
+
else
|
|
22
|
+
clone = ({ ...entity } as unknown) as Output
|
|
23
|
+
|
|
24
|
+
// Delete prop if `removeIfUndefined` and value is undefined
|
|
25
|
+
if (removeIfUndefined && value === undefined && restPath.length === 1)
|
|
26
|
+
delete (clone as any)[path][restPath[0]]
|
|
27
|
+
else
|
|
28
|
+
(clone as any)[path] = internalSet((clone as any)[path], restPath, value, removeIfUndefined)
|
|
29
|
+
|
|
30
|
+
return clone
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default function set<Entity = any, Output = Entity, Value = any>(
|
|
34
|
+
entity: Entity,
|
|
35
|
+
paths: Path,
|
|
36
|
+
value: Value,
|
|
37
|
+
removeIfUndefined: boolean = false,
|
|
38
|
+
): Output {
|
|
39
|
+
// Do nothing if `removeIfUndefined` and parent object not exist
|
|
40
|
+
if (
|
|
41
|
+
paths.length
|
|
42
|
+
&& removeIfUndefined
|
|
43
|
+
&& value === undefined
|
|
44
|
+
&& !get(entity, paths.slice(0, -1))
|
|
45
|
+
) {
|
|
46
|
+
return (entity as unknown) as Output
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return internalSet(entity, paths, value, removeIfUndefined)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isObject(obj: any) {
|
|
53
|
+
return (
|
|
54
|
+
typeof obj === 'object'
|
|
55
|
+
&& obj !== null
|
|
56
|
+
&& Object.getPrototypeOf(obj) === Object.prototype
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createEmpty<T>(source: T) {
|
|
61
|
+
return (Array.isArray(source) ? [] : {}) as T
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const keys = typeof Reflect === 'undefined' ? Object.keys : Reflect.ownKeys
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Merge objects which will create
|
|
68
|
+
*/
|
|
69
|
+
export function merge<T extends object>(...sources: T[]) {
|
|
70
|
+
let clone = createEmpty(sources[0])
|
|
71
|
+
|
|
72
|
+
sources.forEach((src) => {
|
|
73
|
+
function internalMerge(path: Path, parentLoopSet?: Set<object>) {
|
|
74
|
+
const loopSet = new Set(parentLoopSet)
|
|
75
|
+
|
|
76
|
+
const value = get(src, path)
|
|
77
|
+
|
|
78
|
+
const isArr = Array.isArray(value)
|
|
79
|
+
|
|
80
|
+
if (isArr || isObject(value)) {
|
|
81
|
+
// Only add not loop obj
|
|
82
|
+
if (!loopSet.has(value)) {
|
|
83
|
+
loopSet.add(value)
|
|
84
|
+
|
|
85
|
+
const originValue = get(clone, path)
|
|
86
|
+
|
|
87
|
+
if (isArr) {
|
|
88
|
+
// Array will always be overridden
|
|
89
|
+
clone = set(clone, path, [])
|
|
90
|
+
}
|
|
91
|
+
else if (!originValue || typeof originValue !== 'object') {
|
|
92
|
+
// Init container if not exist
|
|
93
|
+
clone = set(clone, path, createEmpty(value))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
keys(value).forEach((key) => {
|
|
97
|
+
internalMerge([...path, key], loopSet)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
clone = set(clone, path, value)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
internalMerge([])
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return clone
|
|
110
|
+
}
|
package/src/vnode.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue'
|
|
2
|
+
import { Comment, Fragment, render as VueRender, cloneVNode, isVNode } from 'vue'
|
|
3
|
+
import { filterEmpty } from './props-util'
|
|
4
|
+
import warning from './warning'
|
|
5
|
+
import type { RefObject } from './createRef'
|
|
6
|
+
|
|
7
|
+
type NodeProps = Record<string, any> &
|
|
8
|
+
Omit<VNodeProps, 'ref'> & { ref?: VNodeProps['ref'] | RefObject }
|
|
9
|
+
|
|
10
|
+
export function cloneElement<T, U>(
|
|
11
|
+
vnode: VNode<T, U> | VNode<T, U>[],
|
|
12
|
+
nodeProps: NodeProps = {},
|
|
13
|
+
override = true,
|
|
14
|
+
mergeRef = false,
|
|
15
|
+
): VNode<T, U> | null {
|
|
16
|
+
let ele = vnode
|
|
17
|
+
if (Array.isArray(vnode))
|
|
18
|
+
ele = filterEmpty(vnode)[0]
|
|
19
|
+
|
|
20
|
+
if (!ele)
|
|
21
|
+
return null
|
|
22
|
+
|
|
23
|
+
const node: any = cloneVNode(ele as VNode<T, U>, nodeProps as any, mergeRef)
|
|
24
|
+
|
|
25
|
+
// cloneVNode内部是合并属性,这里改成覆盖属性
|
|
26
|
+
node.props = (override ? { ...node.props, ...nodeProps } : node.props) as any
|
|
27
|
+
warning(typeof node.props.class !== 'object', 'class must be string')
|
|
28
|
+
return node
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function cloneVNodes(vnodes: any, nodeProps = {}, override = true) {
|
|
32
|
+
return vnodes.map((vnode: any) => cloneElement(vnode, nodeProps, override))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function deepCloneElement<T, U>(
|
|
36
|
+
vnode: VNode<T, U> | VNode<T, U>[],
|
|
37
|
+
nodeProps: NodeProps = {},
|
|
38
|
+
override = true,
|
|
39
|
+
mergeRef = false,
|
|
40
|
+
): any {
|
|
41
|
+
if (Array.isArray(vnode)) {
|
|
42
|
+
return vnode.map((item: any) => deepCloneElement(item, nodeProps, override, mergeRef))
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// 需要判断是否为vnode方可进行clone操作
|
|
46
|
+
if (!isVNode(vnode))
|
|
47
|
+
return vnode
|
|
48
|
+
|
|
49
|
+
const cloned: any = cloneElement(vnode, nodeProps, override, mergeRef)
|
|
50
|
+
if (Array.isArray(cloned.children))
|
|
51
|
+
cloned.children = deepCloneElement(cloned.children as VNode<T, U>[])
|
|
52
|
+
|
|
53
|
+
return cloned
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function triggerVNodeUpdate(vm: VNode, attrs: Record<string, any>, dom: any) {
|
|
58
|
+
VueRender(cloneVNode(vm, { ...attrs }), dom)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function ensureValidVNode(slot: VNodeArrayChildren | null) {
|
|
62
|
+
return (slot || []).some((child) => {
|
|
63
|
+
if (!isVNode(child))
|
|
64
|
+
return true
|
|
65
|
+
if (child.type === Comment)
|
|
66
|
+
return false
|
|
67
|
+
if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren))
|
|
68
|
+
return false
|
|
69
|
+
return true
|
|
70
|
+
})
|
|
71
|
+
? slot
|
|
72
|
+
: null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function customRenderSlot(
|
|
76
|
+
slots: Slots,
|
|
77
|
+
name: string,
|
|
78
|
+
props: Record<string, unknown>,
|
|
79
|
+
fallback?: () => VNodeArrayChildren,
|
|
80
|
+
) {
|
|
81
|
+
const slot = slots[name]?.(props)
|
|
82
|
+
if (ensureValidVNode(slot as any))
|
|
83
|
+
return slot
|
|
84
|
+
|
|
85
|
+
return fallback?.()
|
|
86
|
+
}
|
package/src/warning.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
let warned: Record<string, boolean> = {}
|
|
2
|
+
|
|
3
|
+
export type preMessageFn = (
|
|
4
|
+
message: string,
|
|
5
|
+
type: 'warning' | 'note',
|
|
6
|
+
) => string | null | undefined | number
|
|
7
|
+
|
|
8
|
+
const preWarningFns: preMessageFn[] = []
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Pre warning enable you to parse content before console.error.
|
|
12
|
+
* Modify to null will prevent warning.
|
|
13
|
+
*/
|
|
14
|
+
export function preMessage(fn: preMessageFn) {
|
|
15
|
+
preWarningFns.push(fn)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function warning(valid: boolean, message: string) {
|
|
19
|
+
// Support uglify
|
|
20
|
+
if (
|
|
21
|
+
process.env.NODE_ENV !== 'production'
|
|
22
|
+
&& !valid
|
|
23
|
+
&& console !== undefined
|
|
24
|
+
) {
|
|
25
|
+
const finalMessage = preWarningFns.reduce(
|
|
26
|
+
(msg, preMessageFn) => (preMessageFn as any)(msg ?? '', 'warning'),
|
|
27
|
+
message,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if (finalMessage)
|
|
31
|
+
console.error(`Warning: ${finalMessage}`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function note(valid: boolean, message: string) {
|
|
36
|
+
// Support uglify
|
|
37
|
+
if (
|
|
38
|
+
process.env.NODE_ENV !== 'production'
|
|
39
|
+
&& !valid
|
|
40
|
+
&& console !== undefined
|
|
41
|
+
) {
|
|
42
|
+
const finalMessage = preWarningFns.reduce(
|
|
43
|
+
(msg, preMessageFn) => (preMessageFn as any)(msg ?? '', 'note'),
|
|
44
|
+
message,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (finalMessage)
|
|
48
|
+
console.warn(`Note: ${finalMessage}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resetWarned() {
|
|
53
|
+
warned = {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function call(
|
|
57
|
+
method: (valid: boolean, message: string) => void,
|
|
58
|
+
valid: boolean,
|
|
59
|
+
message: string,
|
|
60
|
+
) {
|
|
61
|
+
if (!valid && !warned[message]) {
|
|
62
|
+
method(false, message)
|
|
63
|
+
warned[message] = true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function warningOnce(valid: boolean, message: string) {
|
|
68
|
+
call(warning, valid, message)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function noteOnce(valid: boolean, message: string) {
|
|
72
|
+
call(note, valid, message)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
warningOnce.preMessage = preMessage
|
|
76
|
+
warningOnce.resetWarned = resetWarned
|
|
77
|
+
warningOnce.noteOnce = noteOnce
|
|
78
|
+
|
|
79
|
+
export default warningOnce
|