generated from justintaddei/npm-ts-template
-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
index.ts
153 lines (131 loc) · 4.7 KB
/
index.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { IIllusoryElementOptions, illusory, IllusoryElement } from 'illusory'
import { VNode } from 'vue'
import { PluginObject } from 'vue/types/plugin'
import { sharedElementMixin } from './mixin'
import { DEFAULT_OPTIONS, ISharedElementOptions } from './options'
import { createRouteGuard } from './routeGuard'
import { ICachedSharedElement } from './types/ICachedSharedElement'
import { ISharedElementCandidate } from './types/ISharedElementCandidate'
import { hideElement } from './utils/hideElement'
import { nextFrame } from './utils/nextFrame'
/**
* Map of all elements on the current page that
* are tagged with v-shared-element
*/
const sharedElementCandidates = new Map<string, ISharedElementCandidate>()
/**
* Map of all the shared elements that were tagged
*/
const sharedElementCache = new Map<string, ICachedSharedElement>()
async function trigger(activeElement: HTMLElement, vnode: VNode, combinedOptions: ISharedElementOptions, id: string) {
activeElement.dataset.illusoryId = id
// Add this element to the candidates list
sharedElementCandidates.set(id, {
element: activeElement,
options: combinedOptions
})
// See if
const cachedElement = sharedElementCache.get(id)
// If this element had a matching shared element
// in the previous route it would've been in the `sharedElementCache`.
// If it isn't in the cache there's no more work to do.
if (!cachedElement) return
const { finished } = illusory(cachedElement.element, activeElement, {
element: {
includeChildren: combinedOptions.includeChildren,
ignoreTransparency: cachedElement.options.ignoreTransparency,
processClone(node, depth) {
// Hide any nested shared elements if they're currently animating
// because otherwise they'll show up in the clone and in their
// animation
if (
depth > 0 &&
(node instanceof HTMLElement || node instanceof SVGElement) &&
node.dataset.illusoryId &&
sharedElementCache.has(node.dataset.illusoryId)
) {
hideElement(node)
}
return node
}
},
compositeOnly: cachedElement.options.compositeOnly,
duration: cachedElement.options.duration,
zIndex: cachedElement.options.zIndex,
easing: cachedElement.options.easing,
relativeTo: [],
async beforeAnimate(from, to) {
// Wait for the next frame
await nextFrame()
// Reset the "to" element because
// some browsers give the wrong value for
// the BCR on the first frame (I'm looking at you Safari)
to.rect = to.natural.getBoundingClientRect()
to.setStyle('left', `${to.rect.left}px`)
to.setStyle('top', `${to.rect.top}px`)
to._to(from)
await nextFrame()
},
beforeDetach(from, to) {
// if there was no `endDuration`
// then we don't need to fade out
if (
combinedOptions.includeChildren ||
!combinedOptions.endDuration ||
parseFloat(combinedOptions.endDuration) <= 0
)
return
from.hide()
to.showNatural()
to.setStyle('transition', `opacity ${combinedOptions.endDuration}`)
to.hide()
return to.waitFor('opacity')
}
})
await finished
}
/**
* Vue.js plugin
* @example
* Vue.use(SharedElementDirective, options)
*/
const SharedElementDirective: PluginObject<Partial<ISharedElementOptions>> = {
install(Vue, options) {
Vue.prototype.$illusory = illusory
Vue.prototype.$createIllusoryElement = (el: HTMLElement | SVGElement, opts: IIllusoryElementOptions) =>
new IllusoryElement(el, opts)
Vue.directive('shared-element', {
async inserted(activeElement, binding, vnode) {
const combinedOptions: ISharedElementOptions = { ...DEFAULT_OPTIONS, ...options, ...binding.value }
// v-shared-element:id
const id = binding.arg
if (!id)
throw new Error(
`Missing ID on a v-shared-element. For usage see: https://github.com/justintaddei/v-shared-element#readme`
)
if (binding.value?.$keepSharedElementAlive)
binding.value.$keepSharedElementAlive(() => {
trigger(activeElement, vnode, combinedOptions, id)
})
trigger(activeElement, vnode, combinedOptions, id)
}
})
}
}
const { NuxtSharedElementRouteGuard, SharedElementRouteGuard } = createRouteGuard(
sharedElementCandidates,
sharedElementCache
)
export {
SharedElementDirective,
SharedElementRouteGuard,
NuxtSharedElementRouteGuard,
sharedElementMixin,
ISharedElementOptions
}
declare module 'vue/types/vue' {
interface Vue {
$illusory: typeof illusory
$createIllusoryElement: (el: HTMLElement | SVGElement, opts?: Partial<IIllusoryElementOptions>) => IllusoryElement
}
}