portal-target.tsx
2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import Vue from 'vue'
import { VNode, PropOptions } from 'vue'
import { combinePassengers } from '@/utils'
import { Transport, PropWithComponent } from '../types'
import { wormhole } from '@/components/wormhole'
export default Vue.extend({
name: 'portalTarget',
props: {
multiple: { type: Boolean, default: false },
name: { type: String, required: true },
slim: { type: Boolean, default: false },
slotProps: { type: Object, default: () => ({}) },
tag: { type: String, default: 'div' },
transition: { type: [String, Object, Function] } as PropOptions<
PropWithComponent
>,
},
data() {
return {
transports: wormhole.transports,
firstRender: true,
}
},
created() {
this.$nextTick(() => {
wormhole.registerTarget(this.name, this)
})
},
watch: {
ownTransports() {
this.$emit('change', this.children().length > 0)
},
name(newVal, oldVal) {
/**
* TODO
* This should warn as well ...
*/
wormhole.unregisterTarget(oldVal)
wormhole.registerTarget(newVal, this)
},
},
mounted() {
if (this.transition) {
this.$nextTick(() => {
// only when we have a transition, because it causes a re-render
this.firstRender = false
})
}
},
beforeDestroy() {
wormhole.unregisterTarget(this.name)
},
computed: {
ownTransports(): Transport[] {
const transports: Transport[] = this.transports[this.name] || []
if (this.multiple) {
return transports
}
return transports.length === 0 ? [] : [transports[transports.length - 1]]
},
passengers(): VNode[] {
return combinePassengers(this.ownTransports, this.slotProps)
},
},
methods: {
// can't be a computed prop because it has to "react" to $slot changes.
children(): VNode[] {
return this.passengers.length !== 0
? this.passengers
: this.$scopedSlots.default
? (this.$scopedSlots.default(this.slotProps) as VNode[])
: this.$slots.default || []
},
// can't be a computed prop because it has to "react" to this.children().
noWrapper() {
const noWrapper = this.slim && !this.transition
if (noWrapper && this.children().length > 1) {
console.warn(
'[portal-vue]: PortalTarget with `slim` option received more than one child element.'
)
}
return noWrapper
},
},
render(h): VNode {
const noWrapper = this.noWrapper()
const children = this.children()
const Tag = this.transition || this.tag
return noWrapper
? children[0]
: this.slim && !Tag
? h()
: h(
Tag,
{
props: {
// if we have a transition component, pass the tag if it exists
tag: this.transition && this.tag ? this.tag : undefined,
},
class: { 'vue-portal-target': true },
},
children
)
},
})