@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.
Files changed (187) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Children/isFragment.cjs +1 -0
  3. package/dist/Children/isFragment.d.ts +3 -0
  4. package/dist/Children/isFragment.js +7 -0
  5. package/dist/Children/toArray.cjs +1 -0
  6. package/dist/Children/toArray.d.ts +8 -0
  7. package/dist/Children/toArray.js +11 -0
  8. package/dist/Dom/addEventListener.cjs +1 -0
  9. package/dist/Dom/addEventListener.d.ts +8 -0
  10. package/dist/Dom/addEventListener.js +12 -0
  11. package/dist/Dom/canUseDom.cjs +1 -0
  12. package/dist/Dom/canUseDom.d.ts +1 -0
  13. package/dist/Dom/canUseDom.js +6 -0
  14. package/dist/Dom/class.cjs +1 -0
  15. package/dist/Dom/class.d.ts +3 -0
  16. package/dist/Dom/class.js +19 -0
  17. package/dist/Dom/contains.cjs +1 -0
  18. package/dist/Dom/contains.d.ts +1 -0
  19. package/dist/Dom/contains.js +16 -0
  20. package/dist/Dom/css.cjs +1 -0
  21. package/dist/Dom/css.d.ts +20 -0
  22. package/dist/Dom/css.js +75 -0
  23. package/dist/Dom/dynamicCSS.cjs +1 -0
  24. package/dist/Dom/dynamicCSS.d.ts +24 -0
  25. package/dist/Dom/dynamicCSS.js +78 -0
  26. package/dist/Dom/findDOMNode.cjs +1 -0
  27. package/dist/Dom/findDOMNode.d.ts +7 -0
  28. package/dist/Dom/findDOMNode.js +12 -0
  29. package/dist/Dom/focus.cjs +1 -0
  30. package/dist/Dom/focus.d.ts +8 -0
  31. package/dist/Dom/focus.js +40 -0
  32. package/dist/Dom/isVisible.cjs +1 -0
  33. package/dist/Dom/isVisible.d.ts +2 -0
  34. package/dist/Dom/isVisible.js +22 -0
  35. package/dist/Dom/scrollLocker.cjs +1 -0
  36. package/dist/Dom/scrollLocker.d.ts +12 -0
  37. package/dist/Dom/scrollLocker.js +81 -0
  38. package/dist/Dom/shadow.cjs +1 -0
  39. package/dist/Dom/shadow.d.ts +8 -0
  40. package/dist/Dom/shadow.js +14 -0
  41. package/dist/Dom/styleChecker.cjs +1 -0
  42. package/dist/Dom/styleChecker.d.ts +2 -0
  43. package/dist/Dom/styleChecker.js +20 -0
  44. package/dist/Dom/support.cjs +1 -0
  45. package/dist/Dom/support.d.ts +6 -0
  46. package/dist/Dom/support.js +24 -0
  47. package/dist/EventInterface.cjs +1 -0
  48. package/dist/EventInterface.d.ts +18 -0
  49. package/dist/EventInterface.js +1 -0
  50. package/dist/KeyCode.cjs +1 -0
  51. package/dist/KeyCode.d.ts +435 -0
  52. package/dist/KeyCode.js +516 -0
  53. package/dist/Portal.cjs +1 -0
  54. package/dist/Portal.d.ts +8 -0
  55. package/dist/Portal.js +38 -0
  56. package/dist/PortalWrapper.cjs +1 -0
  57. package/dist/PortalWrapper.d.ts +28 -0
  58. package/dist/PortalWrapper.js +114 -0
  59. package/dist/composeProps.cjs +1 -0
  60. package/dist/composeProps.d.ts +2 -0
  61. package/dist/composeProps.js +16 -0
  62. package/dist/createRef.cjs +1 -0
  63. package/dist/createRef.d.ts +12 -0
  64. package/dist/createRef.js +21 -0
  65. package/dist/debug/diff.cjs +1 -0
  66. package/dist/debug/diff.d.ts +1 -0
  67. package/dist/debug/diff.js +41 -0
  68. package/dist/deprecated.cjs +1 -0
  69. package/dist/deprecated.d.ts +1 -0
  70. package/dist/deprecated.js +8 -0
  71. package/dist/getScrollBarSize.cjs +1 -0
  72. package/dist/getScrollBarSize.d.ts +5 -0
  73. package/dist/getScrollBarSize.js +37 -0
  74. package/dist/guid.cjs +1 -0
  75. package/dist/guid.d.ts +1 -0
  76. package/dist/guid.js +7 -0
  77. package/dist/hooks/useId.cjs +1 -0
  78. package/dist/hooks/useId.d.ts +5 -0
  79. package/dist/hooks/useId.js +17 -0
  80. package/dist/hooks/useMemo.cjs +1 -0
  81. package/dist/hooks/useMemo.d.ts +3 -0
  82. package/dist/hooks/useMemo.js +10 -0
  83. package/dist/hooks/useMergedState.cjs +1 -0
  84. package/dist/hooks/useMergedState.d.ts +8 -0
  85. package/dist/hooks/useMergedState.js +21 -0
  86. package/dist/hooks/useState.cjs +1 -0
  87. package/dist/hooks/useState.d.ts +3 -0
  88. package/dist/hooks/useState.js +11 -0
  89. package/dist/index.cjs +1 -0
  90. package/dist/index.d.ts +3 -0
  91. package/dist/index.js +8 -0
  92. package/dist/isEqual.cjs +1 -0
  93. package/dist/isEqual.d.ts +8 -0
  94. package/dist/isEqual.js +32 -0
  95. package/dist/isMobile.cjs +1 -0
  96. package/dist/isMobile.d.ts +2 -0
  97. package/dist/isMobile.js +13 -0
  98. package/dist/omit.cjs +1 -0
  99. package/dist/omit.d.ts +1 -0
  100. package/dist/omit.js +9 -0
  101. package/dist/pickAttrs.cjs +18 -0
  102. package/dist/pickAttrs.d.ts +11 -0
  103. package/dist/pickAttrs.js +41 -0
  104. package/dist/props-util/index.cjs +1 -0
  105. package/dist/props-util/index.d.ts +2 -0
  106. package/dist/props-util/index.js +14 -0
  107. package/dist/raf.cjs +1 -0
  108. package/dist/raf.d.ts +6 -0
  109. package/dist/raf.js +30 -0
  110. package/dist/setStyle.cjs +1 -0
  111. package/dist/setStyle.d.ts +13 -0
  112. package/dist/setStyle.js +13 -0
  113. package/dist/switchScrollingEffect.cjs +1 -0
  114. package/dist/switchScrollingEffect.d.ts +2 -0
  115. package/dist/switchScrollingEffect.js +31 -0
  116. package/dist/test/domHook.cjs +1 -0
  117. package/dist/test/domHook.d.ts +8 -0
  118. package/dist/test/domHook.js +37 -0
  119. package/dist/type.cjs +1 -0
  120. package/dist/type.d.ts +63 -0
  121. package/dist/type.js +49 -0
  122. package/dist/utils/checkSlotProp.cjs +1 -0
  123. package/dist/utils/checkSlotProp.d.ts +1 -0
  124. package/dist/utils/checkSlotProp.js +7 -0
  125. package/dist/utils/get.cjs +1 -0
  126. package/dist/utils/get.d.ts +1 -0
  127. package/dist/utils/get.js +12 -0
  128. package/dist/utils/set.cjs +1 -0
  129. package/dist/utils/set.d.ts +6 -0
  130. package/dist/utils/set.js +41 -0
  131. package/dist/vnode.cjs +1 -0
  132. package/dist/vnode.d.ts +12 -0
  133. package/dist/vnode.js +41 -0
  134. package/dist/warning.cjs +1 -0
  135. package/dist/warning.d.ts +18 -0
  136. package/dist/warning.js +48 -0
  137. package/package.json +37 -0
  138. package/src/Children/isFragment.ts +6 -0
  139. package/src/Children/tests/isFragment.test.tsx +15 -0
  140. package/src/Children/tests/toArray.test.tsx +101 -0
  141. package/src/Children/toArray.ts +27 -0
  142. package/src/Dom/addEventListener.ts +20 -0
  143. package/src/Dom/canUseDom.ts +7 -0
  144. package/src/Dom/class.ts +29 -0
  145. package/src/Dom/contains.ts +19 -0
  146. package/src/Dom/css.ts +113 -0
  147. package/src/Dom/dynamicCSS.ts +173 -0
  148. package/src/Dom/findDOMNode.ts +23 -0
  149. package/src/Dom/focus.ts +96 -0
  150. package/src/Dom/isVisible.ts +23 -0
  151. package/src/Dom/scrollLocker.ts +143 -0
  152. package/src/Dom/shadow.ts +17 -0
  153. package/src/Dom/styleChecker.ts +31 -0
  154. package/src/Dom/support.ts +27 -0
  155. package/src/EventInterface.ts +19 -0
  156. package/src/KeyCode.ts +516 -0
  157. package/src/Portal.tsx +50 -0
  158. package/src/PortalWrapper.tsx +214 -0
  159. package/src/composeProps.ts +23 -0
  160. package/src/createRef.ts +33 -0
  161. package/src/debug/diff.ts +66 -0
  162. package/src/deprecated.ts +8 -0
  163. package/src/getScrollBarSize.tsx +57 -0
  164. package/src/guid.ts +4 -0
  165. package/src/hooks/useId.ts +31 -0
  166. package/src/hooks/useMemo.ts +21 -0
  167. package/src/hooks/useMergedState.ts +44 -0
  168. package/src/hooks/useState.ts +17 -0
  169. package/src/index.ts +3 -0
  170. package/src/isEqual.ts +50 -0
  171. package/src/isMobile.ts +15 -0
  172. package/src/omit.ts +14 -0
  173. package/src/pickAttrs.ts +78 -0
  174. package/src/props-util/index.ts +22 -0
  175. package/src/raf.ts +55 -0
  176. package/src/setStyle.ts +38 -0
  177. package/src/switchScrollingEffect.ts +48 -0
  178. package/src/test/domHook.ts +67 -0
  179. package/src/type.ts +94 -0
  180. package/src/utils/checkSlotProp.ts +10 -0
  181. package/src/utils/get.ts +15 -0
  182. package/src/utils/set.ts +110 -0
  183. package/src/vnode.ts +86 -0
  184. package/src/warning.ts +79 -0
  185. package/tests/Portal.test.tsx +199 -0
  186. package/tsconfig.json +7 -0
  187. 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
@@ -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
+ }
@@ -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
+ }
@@ -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