forked from fullcalendar/fullcalendar-vue
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathFullCalendar.ts
130 lines (97 loc) · 3.51 KB
/
FullCalendar.ts
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { PropType, defineComponent, h } from 'vue'
import { NormalizedScopedSlot } from 'vue/types/vnode'
import { Calendar, CalendarOptions } from '@fullcalendar/core'
import { OPTION_IS_COMPLEX } from './options'
import { shallowCopy, mapHash } from './utils'
import { wrapVDomGenerator, createVueContentTypePlugin } from './custom-content-type'
interface FullCalendarInternal {
calendar: Calendar
scopedSlotOptions: { [name: string]: NormalizedScopedSlot }
}
const FullCalendar = {
props: {
options: Object as PropType<CalendarOptions>
},
data: initData, // separate func b/c of type inferencing
render() {
return h('div', {
// when renderId is changed, Vue will trigger a real-DOM async rerender, calling beforeUpdate/updated
attrs: { 'data-fc-render-id': this.renderId }
})
},
mounted() {
let internal = this.$options as FullCalendarInternal
internal.scopedSlotOptions = mapHash(this.$scopedSlots, wrapVDomGenerator) // needed for buildOptions
let calendar = new Calendar(this.$el as HTMLElement, this.buildOptions(this.options, this))
internal.calendar = calendar
calendar.render()
},
methods: { // separate funcs b/c of type inferencing
getApi,
buildOptions,
},
beforeUpdate() {
this.getApi().resumeRendering() // the watcher handlers paused it
},
beforeUnmount() {
this.getApi().destroy()
},
watch: buildWatchers()
}
function initData() {
return {
renderId: 0
}
}
function buildOptions(this: { $options: any }, suppliedOptions: CalendarOptions, parent: Vue): CalendarOptions {
let internal = this.$options as FullCalendarInternal
suppliedOptions = suppliedOptions || {}
return {
...internal.scopedSlotOptions,
...suppliedOptions, // spread will pull out the values from the options getter functions
plugins: (suppliedOptions.plugins || []).concat([
createVueContentTypePlugin(parent)
])
}
}
function getApi(this: { $options: any }) {
let internal = this.$options as FullCalendarInternal
return internal.calendar
}
type FullCalendarInstance = InstanceType<typeof FullCalendar>
function buildWatchers() {
let watchers: { [member: string]: any } = {
// watches changes of ALL options and their nested objects,
// but this is only a means to be notified of top-level non-complex options changes.
options: {
deep: true,
handler(this: FullCalendarInstance, options: CalendarOptions) {
let calendar = this.getApi()
calendar.pauseRendering()
calendar.resetOptions(this.buildOptions(options, this))
this.renderId++ // will queue a rerender
}
}
}
for (let complexOptionName in OPTION_IS_COMPLEX) {
// handlers called when nested objects change
watchers[`options.${complexOptionName}`] = {
deep: true,
handler(this: FullCalendarInstance, val: any) {
// unfortunately the handler is called with undefined if new props were set, but the complex one wasn't ever set
if (val !== undefined) {
let calendar = this.getApi()
calendar.pauseRendering()
calendar.resetOptions({
// the only reason we shallow-copy is to trick FC into knowing there's a nested change.
// TODO: future versions of FC will more gracefully handle event option-changes that are same-reference.
[complexOptionName]: shallowCopy(val)
}, true)
this.renderId++ // will queue a rerender
}
}
}
}
return watchers
}
export default defineComponent(FullCalendar)