@opentiny/vue-hooks 3.21.0 → 3.22.0
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/README.md +3 -0
- package/README.zh-CN.md +3 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +24 -0
- package/{src → dist/src}/use-floating.d.ts +7 -2
- package/dist/src/use-floating.js +141 -0
- package/{src → dist/src}/use-lazy-show.d.ts +6 -3
- package/dist/src/use-lazy-show.js +10 -0
- package/dist/src/useEventListener.d.ts +15 -0
- package/dist/src/useEventListener.js +31 -0
- package/dist/src/useInstanceSlots.d.ts +6 -0
- package/dist/src/useInstanceSlots.js +14 -0
- package/dist/src/useRect.d.ts +1 -0
- package/dist/src/useRect.js +18 -0
- package/dist/src/useRelation.d.ts +31 -0
- package/dist/src/useRelation.js +54 -0
- package/dist/src/useTouch.d.ts +15 -0
- package/dist/src/useTouch.js +32 -0
- package/dist/src/useUserAgent.d.ts +3 -0
- package/dist/src/useUserAgent.js +10 -0
- package/dist/src/useWindowSize.d.ts +4 -0
- package/dist/src/useWindowSize.js +14 -0
- package/{index.d.ts → dist/src/vue-emitter.d.ts} +5 -3
- package/dist/src/vue-popper.js +85 -0
- package/dist/src/vue-popup.d.ts +18 -0
- package/dist/src/vue-popup.js +69 -0
- package/index.ts +23 -0
- package/package.json +15 -9
- package/src/use-floating.ts +409 -0
- package/src/use-lazy-show.ts +20 -0
- package/src/useEventListener.ts +65 -0
- package/src/useInstanceSlots.ts +29 -0
- package/src/useRect.ts +25 -0
- package/src/useRelation.ts +130 -0
- package/src/useTouch.ts +74 -0
- package/src/useUserAgent.ts +18 -0
- package/src/useWindowSize.ts +25 -0
- package/src/vue-emitter.ts +49 -0
- package/src/vue-popper.ts +277 -0
- package/src/vue-popup.ts +196 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +23 -0
- package/index.js +0 -2258
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { noop } from '@opentiny/utils'
|
|
2
|
+
import { onMountedOrActivated as createHook } from './useEventListener'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 处理组件嵌套的组合式 API
|
|
6
|
+
* relationKey 关系树上的父子组件使用同一个关系名称
|
|
7
|
+
* relationContainer 子组件顺序由关系容器确定,由根组件提供,可以不使用,子组件顺序就是组件创建顺序
|
|
8
|
+
* onChange 子组件顺序改变后的回调处理,由根组件提供,可以不使用
|
|
9
|
+
* childrenKey 在组件关系树上的所有实例中定义的子组件引用名称,默认是 instanceChildren
|
|
10
|
+
* delivery 根组件向下分发的内容
|
|
11
|
+
*/
|
|
12
|
+
export const useRelation =
|
|
13
|
+
({
|
|
14
|
+
computed,
|
|
15
|
+
getCurrentInstance,
|
|
16
|
+
inject,
|
|
17
|
+
markRaw,
|
|
18
|
+
nextTick,
|
|
19
|
+
onMounted,
|
|
20
|
+
onActivated,
|
|
21
|
+
onUnmounted,
|
|
22
|
+
provide,
|
|
23
|
+
reactive,
|
|
24
|
+
toRef
|
|
25
|
+
}) =>
|
|
26
|
+
({ relationKey, relationContainer, onChange, childrenKey, delivery } = {}) => {
|
|
27
|
+
if (!relationKey) {
|
|
28
|
+
throw new Error('[TINY Error]<relationKey> must exist.')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const instance = getCurrentInstance()
|
|
32
|
+
const state = reactive({ children: [], indexInParent: -1 })
|
|
33
|
+
const injectValue = inject(relationKey, null)
|
|
34
|
+
// 收集所有的子组件刷新回调
|
|
35
|
+
let callbacks = []
|
|
36
|
+
|
|
37
|
+
if (injectValue) {
|
|
38
|
+
const { link, unlink, callbacks: injectCbs, childrenKey: injectKey, delivery: injectDelivery } = injectValue
|
|
39
|
+
|
|
40
|
+
callbacks = injectCbs
|
|
41
|
+
childrenKey = childrenKey || injectKey || 'instanceChildren'
|
|
42
|
+
delivery = injectDelivery
|
|
43
|
+
|
|
44
|
+
state.indexInParent = link(instance)
|
|
45
|
+
|
|
46
|
+
onUnmounted(() => unlink(instance))
|
|
47
|
+
} else {
|
|
48
|
+
childrenKey = childrenKey || 'instanceChildren'
|
|
49
|
+
|
|
50
|
+
const onMountedOrActivated = createHook({ onMounted, onActivated, nextTick })
|
|
51
|
+
const changeHandler = onChange ? () => nextTick(onChange) : noop
|
|
52
|
+
|
|
53
|
+
let relationMO
|
|
54
|
+
|
|
55
|
+
nextTick(() => {
|
|
56
|
+
// 在 mounted 之后,如果表示子组件关系的 dom 元素存在,就创建 MutationObserver 观察它的子树改变
|
|
57
|
+
const targetNode = typeof relationContainer === 'function' ? relationContainer() : relationContainer
|
|
58
|
+
|
|
59
|
+
if (targetNode) {
|
|
60
|
+
relationMO = new MutationObserver((mutationList, observer) => {
|
|
61
|
+
const flattenNodes = []
|
|
62
|
+
// 对关系容器 dom 子树进行平铺处理
|
|
63
|
+
flattenChildNodes(targetNode.childNodes, flattenNodes)
|
|
64
|
+
// 使用平铺的 dom 子树更新子组件顺序
|
|
65
|
+
callbacks.forEach((callback) => callback(flattenNodes, mutationList, observer))
|
|
66
|
+
// 执行后续组件 change 处理
|
|
67
|
+
changeHandler()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
relationMO.observe(targetNode, { attributes: true, childList: true, subtree: true })
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
onMountedOrActivated(() => changeHandler())
|
|
75
|
+
|
|
76
|
+
onUnmounted(() => {
|
|
77
|
+
if (relationMO) {
|
|
78
|
+
relationMO.disconnect()
|
|
79
|
+
relationMO = null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
callbacks = null
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const link = (child) => {
|
|
87
|
+
const childPublic = child.proxy
|
|
88
|
+
|
|
89
|
+
state.children.push(markRaw(childPublic))
|
|
90
|
+
|
|
91
|
+
return computed(() => state.children.indexOf(childPublic))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const unlink = (child) => {
|
|
95
|
+
const index = state.children.indexOf(child.proxy)
|
|
96
|
+
|
|
97
|
+
if (index > -1) {
|
|
98
|
+
state.children.splice(index, 1)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 刷新子组件顺序
|
|
103
|
+
callbacks.push((flattenNodes) => sortPublicInstances(state.children, flattenNodes))
|
|
104
|
+
|
|
105
|
+
provide(relationKey, { link, unlink, callbacks, childrenKey, delivery })
|
|
106
|
+
|
|
107
|
+
// 在 Public Instance 上定义子组件数组,并且在组件卸载时移除
|
|
108
|
+
Object.defineProperty(instance.proxy, childrenKey, { configurable: true, get: () => state.children })
|
|
109
|
+
|
|
110
|
+
onUnmounted(() => delete instance.proxy[childrenKey])
|
|
111
|
+
|
|
112
|
+
// 返回子组件数组 ref、在父级中的位置索引 ref 和接收到的分发内容
|
|
113
|
+
return { children: toRef(state, 'children'), index: toRef(state, 'indexInParent'), delivery }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const flattenChildNodes = (childNodes, result) => {
|
|
117
|
+
if (childNodes.length) {
|
|
118
|
+
childNodes.forEach((childNode) => {
|
|
119
|
+
result.push(childNode)
|
|
120
|
+
|
|
121
|
+
if (childNode.childNodes) {
|
|
122
|
+
flattenChildNodes(childNode.childNodes, result)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const sortPublicInstances = (instances, flattenNodes) => {
|
|
129
|
+
instances.sort((a, b) => flattenNodes.indexOf(a.$el) - flattenNodes.indexOf(b.$el))
|
|
130
|
+
}
|
package/src/useTouch.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const TAP_OFFSET = 5
|
|
2
|
+
|
|
3
|
+
const getDirection = (x, y) => {
|
|
4
|
+
if (x > y) return 'horizontal'
|
|
5
|
+
if (y > x) return 'vertical'
|
|
6
|
+
return ''
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const touchEvent = (event) => event.touches[0]
|
|
10
|
+
|
|
11
|
+
export const useTouch = (ref) => () => {
|
|
12
|
+
const startX = ref(0)
|
|
13
|
+
const startY = ref(0)
|
|
14
|
+
const deltaX = ref(0)
|
|
15
|
+
const deltaY = ref(0)
|
|
16
|
+
const offsetX = ref(0)
|
|
17
|
+
const offsetY = ref(0)
|
|
18
|
+
const direction = ref('')
|
|
19
|
+
const isTap = ref(true)
|
|
20
|
+
|
|
21
|
+
const isVertical = () => direction.value === 'vertical'
|
|
22
|
+
const isHorizontal = () => direction.value === 'horizontal'
|
|
23
|
+
|
|
24
|
+
const reset = () => {
|
|
25
|
+
deltaX.value = 0
|
|
26
|
+
deltaY.value = 0
|
|
27
|
+
offsetX.value = 0
|
|
28
|
+
offsetY.value = 0
|
|
29
|
+
direction.value = ''
|
|
30
|
+
isTap.value = true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const start = (event) => {
|
|
34
|
+
reset()
|
|
35
|
+
const touch = touchEvent(event)
|
|
36
|
+
startX.value = touch.clientX
|
|
37
|
+
startY.value = touch.clientY
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const move = (event) => {
|
|
41
|
+
const touch = touchEvent(event)
|
|
42
|
+
// safari back will set clientX to negative number
|
|
43
|
+
deltaX.value = (touch.clientX < 0 ? 0 : touch.clientX) - startX.value
|
|
44
|
+
deltaY.value = touch.clientY - startY.value
|
|
45
|
+
offsetX.value = Math.abs(deltaX.value)
|
|
46
|
+
offsetY.value = Math.abs(deltaY.value)
|
|
47
|
+
|
|
48
|
+
// lock direction when distance is greater than a certain value
|
|
49
|
+
const LOCK_DIRECTION_DISTANCE = 10
|
|
50
|
+
if (!direction.value || (offsetX.value < LOCK_DIRECTION_DISTANCE && offsetY.value < LOCK_DIRECTION_DISTANCE)) {
|
|
51
|
+
direction.value = getDirection(offsetX.value, offsetY.value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (isTap.value && (offsetX.value > TAP_OFFSET || offsetY.value > TAP_OFFSET)) {
|
|
55
|
+
isTap.value = false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
move,
|
|
61
|
+
start,
|
|
62
|
+
reset,
|
|
63
|
+
isVertical,
|
|
64
|
+
isHorizontal,
|
|
65
|
+
startX,
|
|
66
|
+
startY,
|
|
67
|
+
deltaX,
|
|
68
|
+
deltaY,
|
|
69
|
+
offsetX,
|
|
70
|
+
offsetY,
|
|
71
|
+
direction,
|
|
72
|
+
isTap
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isServer } from '@opentiny/utils'
|
|
2
|
+
|
|
3
|
+
function getIsIOS() {
|
|
4
|
+
if (isServer) return false
|
|
5
|
+
return (
|
|
6
|
+
window.navigator &&
|
|
7
|
+
window.navigator.userAgent &&
|
|
8
|
+
(/iP(?:ad|hone|od)/.test(window.navigator.userAgent) ||
|
|
9
|
+
// The new iPad Pro Gen3 does not identify itself as iPad, but as Macintosh.
|
|
10
|
+
// https://github.com/vueuse/vueuse/issues/3577
|
|
11
|
+
(window.navigator.maxTouchPoints > 2 && /iPad|Macintosh/.test(window.navigator.userAgent)))
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useUserAgent = () => {
|
|
16
|
+
const isIOS = getIsIOS()
|
|
17
|
+
return { isIOS }
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { on, isServer } from '@opentiny/utils'
|
|
2
|
+
|
|
3
|
+
let width
|
|
4
|
+
let height
|
|
5
|
+
|
|
6
|
+
export const useWindowSize = (ref) => () => {
|
|
7
|
+
if (!width) {
|
|
8
|
+
width = ref(0)
|
|
9
|
+
height = ref(0)
|
|
10
|
+
|
|
11
|
+
if (!isServer) {
|
|
12
|
+
const update = () => {
|
|
13
|
+
width.value = window.innerWidth
|
|
14
|
+
height.value = window.innerHeight
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
update()
|
|
18
|
+
|
|
19
|
+
on(window, 'resize', update, { passive: true })
|
|
20
|
+
on(window, 'orientationchange', update, { passive: true })
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { width, height }
|
|
25
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* eslint-disable prefer-spread */
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2022 - present TinyVue Authors.
|
|
4
|
+
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license.
|
|
7
|
+
*
|
|
8
|
+
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
|
9
|
+
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
|
10
|
+
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// 全局未引用 ,待移除
|
|
15
|
+
export default (vm) => {
|
|
16
|
+
const broadcast = (vm, componentName, eventName, params) => {
|
|
17
|
+
vm.$children.forEach((child) => {
|
|
18
|
+
const name = child.$options.componentName
|
|
19
|
+
|
|
20
|
+
if (name === componentName) {
|
|
21
|
+
child.$emit(eventName, params)
|
|
22
|
+
} else {
|
|
23
|
+
broadcast(child, componentName, eventName, params)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
dispatch(componentName, eventName, params) {
|
|
30
|
+
let parent = vm.$parent || vm.$root
|
|
31
|
+
let name = parent.$options.componentName
|
|
32
|
+
|
|
33
|
+
while (parent && !parent.$attrs.novalid && (!name || name !== componentName)) {
|
|
34
|
+
parent = parent.$parent
|
|
35
|
+
|
|
36
|
+
if (parent) {
|
|
37
|
+
name = parent.$options.componentName
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (parent) {
|
|
42
|
+
parent.$emit.apply(parent, [eventName].concat(params))
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
broadcast(componentName, eventName, params) {
|
|
46
|
+
broadcast(vm, componentName, eventName, params)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022 - present TinyVue Authors.
|
|
3
|
+
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license.
|
|
6
|
+
*
|
|
7
|
+
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
|
8
|
+
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
|
9
|
+
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { PopupManager, PopperJS, on, off, isDisplayNone } from '@opentiny/utils'
|
|
14
|
+
|
|
15
|
+
// todo
|
|
16
|
+
import type { ISharedRenderlessFunctionParams } from 'types/shared.type'
|
|
17
|
+
|
|
18
|
+
import { isServer } from '@opentiny/utils'
|
|
19
|
+
|
|
20
|
+
export interface IPopperState {
|
|
21
|
+
popperJS: Popper
|
|
22
|
+
appended: boolean
|
|
23
|
+
popperElm: HTMLElement
|
|
24
|
+
showPopper: boolean
|
|
25
|
+
referenceElm: HTMLElement
|
|
26
|
+
currentPlacement: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type IPopperInputParams = ISharedRenderlessFunctionParams<never> & {
|
|
30
|
+
api: { open: Function; close: Function }
|
|
31
|
+
state: IPopperState
|
|
32
|
+
props: any
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 给 popper 的click添加stop, 阻止冒泡 */
|
|
36
|
+
const stop = (e: Event) => e.stopPropagation()
|
|
37
|
+
|
|
38
|
+
// 由于多个组件传入reference元素的方式不同,所以这里从多处查找。
|
|
39
|
+
const getReference = ({ state, props, vm, slots }: Pick<IPopperInputParams, 'state' | 'props' | 'vm' | 'slots'>) => {
|
|
40
|
+
let reference =
|
|
41
|
+
state.referenceElm || props.reference || (vm.$refs.reference && vm.$refs.reference.$el) || vm.$refs.reference
|
|
42
|
+
|
|
43
|
+
if (!reference && slots.reference && slots.reference()[0]) {
|
|
44
|
+
state.referenceElm = slots.reference()[0].elm || slots.reference()[0].el
|
|
45
|
+
reference = state.referenceElm
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return reference
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getReferMaxZIndex = (reference) => {
|
|
52
|
+
if (!reference || !reference.nodeType) return
|
|
53
|
+
|
|
54
|
+
let getZIndex = (dom) => parseInt(window.getComputedStyle(dom).zIndex, 10) || 0
|
|
55
|
+
let max = getZIndex(reference)
|
|
56
|
+
let z
|
|
57
|
+
|
|
58
|
+
do {
|
|
59
|
+
reference = reference.parentNode
|
|
60
|
+
|
|
61
|
+
// 处理遇到shadowRoot的情况
|
|
62
|
+
if (reference && reference instanceof ShadowRoot && reference.host) {
|
|
63
|
+
reference = reference.host
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (reference) {
|
|
67
|
+
z = getZIndex(reference)
|
|
68
|
+
} else {
|
|
69
|
+
break
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
max = z > max ? z : max
|
|
73
|
+
} while (reference !== document.body)
|
|
74
|
+
|
|
75
|
+
return max + 1 + ''
|
|
76
|
+
}
|
|
77
|
+
// 历史原因,暂时先命名为userPopper, 以后统一替换
|
|
78
|
+
export const userPopper = (options: IPopperInputParams) => {
|
|
79
|
+
const {
|
|
80
|
+
parent,
|
|
81
|
+
emit,
|
|
82
|
+
nextTick,
|
|
83
|
+
onBeforeUnmount,
|
|
84
|
+
onDeactivated,
|
|
85
|
+
props,
|
|
86
|
+
watch,
|
|
87
|
+
reactive,
|
|
88
|
+
vm,
|
|
89
|
+
slots,
|
|
90
|
+
toRefs,
|
|
91
|
+
popperVmRef
|
|
92
|
+
} = options
|
|
93
|
+
const state = reactive<IPopperState>({
|
|
94
|
+
popperJS: null as any,
|
|
95
|
+
appended: false, // arrow 是否添加
|
|
96
|
+
popperElm: null as any,
|
|
97
|
+
showPopper: props.manual ? Boolean(props.modelValue) : false,
|
|
98
|
+
referenceElm: null as any,
|
|
99
|
+
currentPlacement: ''
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
/** 创建箭头函数 */
|
|
103
|
+
const appendArrow = (el: HTMLElement) => {
|
|
104
|
+
if (state.appended) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
state.appended = true
|
|
109
|
+
const div = document.createElement('div')
|
|
110
|
+
|
|
111
|
+
div.setAttribute('x-arrow', '')
|
|
112
|
+
div.className = 'popper__arrow'
|
|
113
|
+
el.appendChild(div)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 如果触发源是隐藏的,其弹出层也设置为隐藏。组件可以通过 props.popperOptions.followReferenceHide = true/false来控制
|
|
117
|
+
const followHide = (popperInstance: PopperJS) => {
|
|
118
|
+
const { followReferenceHide = true } = props?.popperOptions || {}
|
|
119
|
+
const { _popper: popper, _reference: reference } = popperInstance
|
|
120
|
+
|
|
121
|
+
if (followReferenceHide && isDisplayNone(reference)) {
|
|
122
|
+
popper.style.display = 'none'
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const nextZIndex = (reference) => {
|
|
127
|
+
return props.zIndex === 'relative' ? getReferMaxZIndex(reference) : PopupManager.nextZIndex()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const createPopper = (dom) => {
|
|
131
|
+
if (isServer) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
state.currentPlacement = state.currentPlacement || props.placement
|
|
136
|
+
|
|
137
|
+
if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(state.currentPlacement)) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const options = props.popperOptions || { gpuAcceleration: false }
|
|
142
|
+
state.popperElm = state.popperElm || props.popper || vm.$refs.popper || popperVmRef.popper || dom
|
|
143
|
+
const popper = state.popperElm
|
|
144
|
+
let reference = getReference({ state, props, vm, slots })
|
|
145
|
+
|
|
146
|
+
if (!popper || !reference || reference.nodeType !== Node.ELEMENT_NODE) {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (props.visibleArrow) {
|
|
151
|
+
appendArrow(popper)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 使用的组件比较多,所以 appendToBody popperAppendToBody 这2个属性都要监听
|
|
155
|
+
if (props.appendToBody || props.popperAppendToBody) {
|
|
156
|
+
document.body.appendChild(state.popperElm)
|
|
157
|
+
} else {
|
|
158
|
+
// 只有tooltip 传入parent了
|
|
159
|
+
parent && parent.$el && parent.$el.appendChild(state.popperElm)
|
|
160
|
+
options.forceAbsolute = true
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
options.placement = state.currentPlacement
|
|
164
|
+
options.offset = props.offset || 0
|
|
165
|
+
options.arrowOffset = props.arrowOffset || 0
|
|
166
|
+
options.adjustArrow = props.adjustArrow || false
|
|
167
|
+
options.appendToBody = props.appendToBody || props.popperAppendToBody
|
|
168
|
+
|
|
169
|
+
// 创建一个popperJS, 内部会立即调用一次update() 并 applyStyle等操作
|
|
170
|
+
state.popperJS = new PopperJS(reference, popper, options)
|
|
171
|
+
// 1、所有使用vue-popper的都有该事件;2、有的组件会多次触发 created
|
|
172
|
+
emit('created', state)
|
|
173
|
+
|
|
174
|
+
if (typeof options.onUpdate === 'function') {
|
|
175
|
+
state.popperJS.onUpdate(options.onUpdate)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
state.popperJS._popper.style.zIndex = nextZIndex(state.popperJS._reference)
|
|
179
|
+
followHide(state.popperJS)
|
|
180
|
+
on(state.popperElm, 'click', stop)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** 第一次 updatePopper 的时候,才真正执行创建
|
|
184
|
+
* popperElmOrTrue===true的场景仅在select组件动态更新面版时,不更新zIndex
|
|
185
|
+
*/
|
|
186
|
+
const updatePopper = (popperElmOrTrue?: HTMLElement) => {
|
|
187
|
+
if (popperElmOrTrue && popperElmOrTrue !== true) {
|
|
188
|
+
state.popperElm = popperElmOrTrue
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const popperJS = state.popperJS
|
|
192
|
+
if (popperJS) {
|
|
193
|
+
// Tiny 新增,在动态切换renference时,需要实时获取最新的触发源
|
|
194
|
+
popperJS._reference = getReference({ state, props, vm, slots })
|
|
195
|
+
popperJS.update()
|
|
196
|
+
|
|
197
|
+
// 每次递增 z-index
|
|
198
|
+
if (popperJS._popper && popperElmOrTrue !== true) {
|
|
199
|
+
popperJS._popper.style.zIndex = nextZIndex(popperJS._reference)
|
|
200
|
+
followHide(state.popperJS)
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
createPopper(popperElmOrTrue && popperElmOrTrue !== true ? popperElmOrTrue : undefined)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** 调用state.popperJS.destroy()。 默认不会移除popper dom
|
|
208
|
+
* doDestroy() 默认执行的条件是: state.popperJS 有值且 state.showPopper = false.
|
|
209
|
+
* 当state.showPopper 为true时, 需要 doDestroy(true)!
|
|
210
|
+
*/
|
|
211
|
+
const doDestroy = (forceDestroy?: boolean) => {
|
|
212
|
+
if (!state.popperJS || (state.showPopper && !forceDestroy)) {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
state.popperJS.destroy() // 并未移除popper的dom
|
|
216
|
+
state.popperJS = null as any
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** remove时,执行真的移除popper dom操作。 */
|
|
220
|
+
const destroyPopper = (remove: 'remove' | boolean) => {
|
|
221
|
+
if (remove) {
|
|
222
|
+
// 当popper中嵌套popper时,内层popper被移除后不会重新创建,因此onDeactivated不将内层popper移除
|
|
223
|
+
if (state.popperElm && state.popperElm.parentNode === document.body) {
|
|
224
|
+
off(state.popperElm, 'click', stop)
|
|
225
|
+
state.popperElm.remove()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 注意: 一直以来,state.showPopper 为false时,并未调用doDestroy. 像popover只是依赖这个值来 给reference元素 v-show一下
|
|
231
|
+
watch(
|
|
232
|
+
() => state.showPopper,
|
|
233
|
+
(val) => {
|
|
234
|
+
if (props.disabled) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
if (val) {
|
|
238
|
+
nextTick(updatePopper)
|
|
239
|
+
}
|
|
240
|
+
props.trigger === 'manual' && emit('update:modelValue', val)
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
watch(
|
|
245
|
+
() => props.placement,
|
|
246
|
+
(val?: string) => {
|
|
247
|
+
state.currentPlacement = val
|
|
248
|
+
state.popperJS?.setOptions({ placement: val })
|
|
249
|
+
|
|
250
|
+
if (props.disabled) {
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
if (val) {
|
|
254
|
+
nextTick(updatePopper)
|
|
255
|
+
}
|
|
256
|
+
props.trigger === 'manual' && emit('update:modelValue', val)
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
onBeforeUnmount(() => {
|
|
261
|
+
nextTick(() => {
|
|
262
|
+
doDestroy(true)
|
|
263
|
+
if (props.appendToBody || props.popperAppendToBody) {
|
|
264
|
+
destroyPopper('remove')
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
onDeactivated(() => {
|
|
270
|
+
doDestroy(true)
|
|
271
|
+
if (props.appendToBody || props.popperAppendToBody) {
|
|
272
|
+
destroyPopper('remove')
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
return { updatePopper, destroyPopper, doDestroy, ...toRefs(state) }
|
|
277
|
+
}
|