@startlibs/components 1.0.4 → 1.1.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/lib/BasicBulletList.mjs +9 -0
- package/lib/BasicBulletList.mjs.map +1 -0
- package/lib/Button.mjs +95 -0
- package/lib/Button.mjs.map +1 -0
- package/lib/ContextMenu.mjs +452 -0
- package/lib/ContextMenu.mjs.map +1 -0
- package/lib/Dialog.mjs +232 -0
- package/lib/Dialog.mjs.map +1 -0
- package/lib/Draggable.mjs +880 -0
- package/lib/Draggable.mjs.map +1 -0
- package/lib/ErrorBoundary.mjs +108 -0
- package/lib/ErrorBoundary.mjs.map +1 -0
- package/lib/FillLastLineResizer.mjs +238 -0
- package/lib/FillLastLineResizer.mjs.map +1 -0
- package/lib/Flip.mjs +604 -0
- package/lib/Flip.mjs.map +1 -0
- package/lib/FlipList.mjs +186 -0
- package/lib/FlipList.mjs.map +1 -0
- package/lib/FormatDate.mjs +25 -0
- package/lib/FormatDate.mjs.map +1 -0
- package/lib/Icon.mjs +50 -0
- package/lib/Icon.mjs.map +1 -0
- package/lib/Image.mjs +41 -0
- package/lib/Image.mjs.map +1 -0
- package/lib/Link.mjs +116 -0
- package/lib/Link.mjs.map +1 -0
- package/lib/Loading.mjs +10 -0
- package/lib/Loading.mjs.map +1 -0
- package/lib/LoadingDiv.mjs +83 -0
- package/lib/LoadingDiv.mjs.map +1 -0
- package/lib/Notifications.mjs +63 -0
- package/lib/Notifications.mjs.map +1 -0
- package/lib/PageLoader.mjs +42 -0
- package/lib/PageLoader.mjs.map +1 -0
- package/lib/PersistentNotifications.mjs +130 -0
- package/lib/PersistentNotifications.mjs.map +1 -0
- package/lib/Popup.mjs +49 -0
- package/lib/Popup.mjs.map +1 -0
- package/lib/SplitColumnsContainer.mjs +43 -0
- package/lib/SplitColumnsContainer.mjs.map +1 -0
- package/lib/SwitchDiv.mjs +61 -0
- package/lib/SwitchDiv.mjs.map +1 -0
- package/lib/TextButton.mjs +41 -0
- package/lib/TextButton.mjs.map +1 -0
- package/lib/ToastNotifications.mjs +215 -0
- package/lib/ToastNotifications.mjs.map +1 -0
- package/lib/Tooltip.mjs +160 -0
- package/lib/Tooltip.mjs.map +1 -0
- package/lib/TransitionDiv.mjs +56 -0
- package/lib/TransitionDiv.mjs.map +1 -0
- package/lib/WarningDialog.mjs +84 -0
- package/lib/WarningDialog.mjs.map +1 -0
- package/lib/alt/buttons/OnClickLoadingButton.mjs +36 -0
- package/lib/alt/buttons/OnClickLoadingButton.mjs.map +1 -0
- package/lib/alt/dialogs/DialogWithImageHeader.mjs +14 -0
- package/lib/alt/dialogs/DialogWithImageHeader.mjs.map +1 -0
- package/lib/alt/dialogs/DialogWithSidebar.mjs +106 -0
- package/lib/alt/dialogs/DialogWithSidebar.mjs.map +1 -0
- package/lib/alt/dialogs/MobileBubbleDialog.mjs +49 -0
- package/lib/alt/dialogs/MobileBubbleDialog.mjs.map +1 -0
- package/lib/containers/DialogContainer.mjs +413 -0
- package/lib/containers/DialogContainer.mjs.map +1 -0
- package/lib/containers/Reposition.mjs +35 -0
- package/lib/containers/Reposition.mjs.map +1 -0
- package/lib/containers/usePositioning.mjs +294 -0
- package/lib/containers/usePositioning.mjs.map +1 -0
- package/lib/index.cjs +5000 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.mjs +37 -0
- package/lib/index.mjs.map +1 -0
- package/lib/useDialogWithToggle.mjs +18 -0
- package/lib/useDialogWithToggle.mjs.map +1 -0
- package/lib/willUseIsSticky.mjs +123 -0
- package/lib/willUseIsSticky.mjs.map +1 -0
- package/package.json +44 -29
- package/src/BasicBulletList.jsx +21 -0
- package/src/Button.jsx +288 -0
- package/src/ContextMenu.jsx +621 -0
- package/src/Dialog.jsx +413 -0
- package/src/Draggable.jsx +801 -0
- package/src/ErrorBoundary.jsx +75 -0
- package/src/FillLastLineResizer.jsx +226 -0
- package/src/Flip.jsx +560 -0
- package/src/FlipList.jsx +136 -0
- package/src/FormatDate.jsx +9 -0
- package/src/Icon.jsx +49 -0
- package/src/Image.jsx +23 -0
- package/src/Link.jsx +68 -0
- package/src/Loading.jsx +29 -0
- package/src/LoadingDiv.jsx +75 -0
- package/src/Notifications.jsx +89 -0
- package/src/PageLoader.jsx +48 -0
- package/src/PersistentNotifications.jsx +182 -0
- package/src/Popup.jsx +49 -0
- package/src/SplitColumnsContainer.jsx +63 -0
- package/src/SwitchDiv.jsx +30 -0
- package/src/TextButton.jsx +34 -0
- package/src/ToastNotifications.jsx +298 -0
- package/src/Tooltip.jsx +158 -0
- package/src/TransitionDiv.jsx +34 -0
- package/src/WarningDialog.jsx +70 -0
- package/src/alt/buttons/OnClickLoadingButton.jsx +16 -0
- package/src/alt/dialogs/DialogWithImageHeader.jsx +42 -0
- package/src/alt/dialogs/DialogWithSidebar.jsx +135 -0
- package/src/alt/dialogs/MobileBubbleDialog.jsx +56 -0
- package/src/containers/DialogContainer.jsx +423 -0
- package/src/containers/Reposition.jsx +24 -0
- package/src/containers/usePositioning.jsx +305 -0
- package/src/index.js +35 -0
- package/src/useDialogWithToggle.jsx +12 -0
- package/src/willUseIsSticky.jsx +113 -0
- package/.babelrc +0 -27
- package/lib/index.js +0 -5429
- package/rollup.config.js +0 -52
- package/yarn-error.log +0 -3724
package/src/Flip.jsx
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import React, {useLayoutEffect} from 'react'
|
|
2
|
+
import _ from 'lodash/fp'
|
|
3
|
+
import {_s, animationTiming, callIfFunction, oneWayAnimation} from '@startlibs/utils'
|
|
4
|
+
import {useConstant, useHiddenFill, willUseSharedCallback} from '@startlibs/core'
|
|
5
|
+
import {ifUndefined} from '@startlibs/utils'
|
|
6
|
+
|
|
7
|
+
const getMargins = (domNode) => {
|
|
8
|
+
const {marginTop,marginLeft,marginBottom,marginRight,paddingTop} = window.getComputedStyle(domNode);
|
|
9
|
+
return {marginTop,marginLeft,marginBottom,marginRight,paddingTop}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const getParentProperties = (parentNode) => {
|
|
13
|
+
if (!parentNode) return {}
|
|
14
|
+
const {marginTop,marginLeft,marginBottom,marginRight,paddingBottom,paddingTop} = window.getComputedStyle(parentNode);
|
|
15
|
+
return {parentStyle:{marginTop,marginLeft,marginBottom,marginRight,paddingBottom,paddingTop},parentNode}
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const toPosition = (domNode, parentRect = (domNode.offsetParent ? domNode.offsetParent.getBoundingClientRect() : {
|
|
20
|
+
top: 0,
|
|
21
|
+
left: 0
|
|
22
|
+
}), {allowFixed, finalPosition,withMargins} = {}) => {
|
|
23
|
+
if ((!allowFixed && domNode.style.position === 'fixed') || domNode.style.display === 'none') return
|
|
24
|
+
const {left, top, height, width} = domNode.getBoundingClientRect()
|
|
25
|
+
const margins = withMargins ? getMargins(domNode) : {}
|
|
26
|
+
|
|
27
|
+
// const {offsetLeft, offsetTop} = domNode
|
|
28
|
+
return {
|
|
29
|
+
domNode,
|
|
30
|
+
nextSibling: domNode.nextSibling,
|
|
31
|
+
top: finalPosition ? domNode.offsetTop : (top - parentRect.top),
|
|
32
|
+
left: finalPosition ? domNode.offsetLeft : (left - parentRect.left),
|
|
33
|
+
height, width,
|
|
34
|
+
...getParentProperties(domNode.parentNode),
|
|
35
|
+
...margins
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sumWithPx = (...args) => args.map(parseFloat).reduce((a, b) => a + b, 0) + 'px'
|
|
40
|
+
|
|
41
|
+
const childMAp = (childs, fn) => {
|
|
42
|
+
for (var c of childs) {
|
|
43
|
+
fn(c)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const defaultAnimationMaker = animationTiming('0.25s ease-out')
|
|
48
|
+
const defaultEnterAnimation = oneWayAnimation('opacity: 0;', 'opacity: 1;')
|
|
49
|
+
const defaultSwitchAnimation = oneWayAnimation(`opacity: 1;`, `opacity: 0;`, `opacity: 0;`)
|
|
50
|
+
|
|
51
|
+
const SHORT_ANIMATIONS = {
|
|
52
|
+
fadeIn: defaultEnterAnimation
|
|
53
|
+
}
|
|
54
|
+
const applyDefault = (k) => SHORT_ANIMATIONS[k] || k
|
|
55
|
+
|
|
56
|
+
export const formatAnimation = (animationName /* array or string or fn with array or string*/, animationTimer /* string or fn */, reverse) => {
|
|
57
|
+
const animationMaker = typeof animationTimer === 'string' ? ((k) => k + ' ' + animationTimer) : animationTimer
|
|
58
|
+
const animationNameExecuted = typeof animationName === 'function' ? animationName() : animationName
|
|
59
|
+
const keyframes = [].concat(typeof animationNameExecuted === 'string' ? animationNameExecuted.split(',') : animationNameExecuted)
|
|
60
|
+
const applyReverse = (k) => reverse ? k + ' reverse' : k
|
|
61
|
+
return keyframes.map(k => applyReverse(k.trim().indexOf(' ') < 0 ? animationMaker(k) : applyDefault(k))).join(', ')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cloneWrapper = (element) => {
|
|
65
|
+
const cloned = element.cloneNode(true)
|
|
66
|
+
const tempWrapper = document.createElement('div');
|
|
67
|
+
[].slice.call(cloned.childNodes).forEach(c => tempWrapper.append(c))
|
|
68
|
+
const {display, width, height, padding, borderLeftWidth, borderRightWidth, borderBottomWidth, borderTopWidth} = window.getComputedStyle(element)
|
|
69
|
+
Object.assign(tempWrapper.style, {
|
|
70
|
+
display,
|
|
71
|
+
width: sumWithPx(width, '-' + borderLeftWidth, '-' + borderRightWidth),
|
|
72
|
+
height: sumWithPx(height, '-' + borderBottomWidth, '-' + borderTopWidth),
|
|
73
|
+
padding,
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
top: 0,
|
|
76
|
+
left: 0
|
|
77
|
+
})
|
|
78
|
+
return tempWrapper
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const morphingEndEvents = new Map()
|
|
82
|
+
const morphTime = new Map()
|
|
83
|
+
const continueSwitchCallbacks = new Map()
|
|
84
|
+
|
|
85
|
+
const continueSwitchOnClone = (oldWrapper) => (newWrapper) => {
|
|
86
|
+
const prevOldClone = oldWrapper.querySelector('.JS-old-snap')
|
|
87
|
+
const prevNewClone = oldWrapper.querySelector('.JS-new-snap')
|
|
88
|
+
const oldClone = newWrapper.querySelector('.JS-old-snap')
|
|
89
|
+
const newClone = newWrapper.querySelector('.JS-new-snap')
|
|
90
|
+
oldClone.style.animationDelay = getDelays(prevOldClone, oldClone.style.animation)
|
|
91
|
+
newClone.style.animationDelay = getDelays(prevNewClone, newClone.style.animation)
|
|
92
|
+
morphTime.set(oldClone, morphTime.get(prevOldClone))
|
|
93
|
+
morphTime.set(newClone, morphTime.get(prevNewClone))
|
|
94
|
+
oldClone.addEventListener('animationend', oldClone.remove)
|
|
95
|
+
newClone.addEventListener('animationend', finishSwitchNew(newClone, newWrapper))
|
|
96
|
+
continueSwitchCallbacks.delete(oldWrapper)
|
|
97
|
+
continueSwitchCallbacks.set(newWrapper, continueSwitchOnClone(newWrapper))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const finishSwitchNew = (clone, wrapper) => () => {
|
|
101
|
+
clone.remove()
|
|
102
|
+
const newChildren = [].slice.call(wrapper.children)
|
|
103
|
+
childMAp(newChildren, c => {
|
|
104
|
+
if (c.dataset) {
|
|
105
|
+
// Guard against stale "0" from a prior uncompleted cycle: always restore
|
|
106
|
+
// to empty when prev is "0" since "0" is never a legitimate captured value.
|
|
107
|
+
const prev = c.dataset.prevOpacity
|
|
108
|
+
c.style.opacity = (prev && prev !== '0') ? prev : ''
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
// wrapper.style.willChange = ''
|
|
112
|
+
continueSwitchCallbacks.delete(wrapper)
|
|
113
|
+
}
|
|
114
|
+
const supportsEvent = "onanimationend" in HTMLElement.prototype
|
|
115
|
+
export const morph = (node, animation, callback) => {
|
|
116
|
+
if (!animation) {return}
|
|
117
|
+
const [cachedAnimation, cachedCallback] = morphingEndEvents.get(node) || []
|
|
118
|
+
const oldAnimation = cachedAnimation || node.style.animation
|
|
119
|
+
morphTime.set(node, Date.now())
|
|
120
|
+
const didNotStartPromise = Promise
|
|
121
|
+
.race([
|
|
122
|
+
new Promise(res => node.addEventListener('animationstart', res)),
|
|
123
|
+
new Promise((res,rej) => setTimeout(rej,supportsEvent? 1000 : 200)),
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
node.style.animation = animation
|
|
127
|
+
if (!cachedCallback) {
|
|
128
|
+
const end = ({target}) => {
|
|
129
|
+
if (target !== node) return
|
|
130
|
+
morphTime.delete(node)
|
|
131
|
+
morphingEndEvents.delete(node)
|
|
132
|
+
node.removeEventListener('animationend', end)
|
|
133
|
+
callback && callback()
|
|
134
|
+
node.style.animation = oldAnimation
|
|
135
|
+
}
|
|
136
|
+
didNotStartPromise
|
|
137
|
+
.catch(() => end({target:node}))
|
|
138
|
+
node.addEventListener('animationend', end)
|
|
139
|
+
morphingEndEvents.set(node, [oldAnimation, end])
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const hasClientRectChanged = (oldSnap, newSnap) => !['top', 'left', 'height', 'width'].every(_ => oldSnap[_] === newSnap[_])
|
|
144
|
+
|
|
145
|
+
export const getClientRectAnimation = (oldSnap, newSnap, {skipTransform, smallJumpFix } = {}) => {
|
|
146
|
+
if (oldSnap && newSnap && hasClientRectChanged(oldSnap, newSnap)) {
|
|
147
|
+
const node = newSnap.domNode
|
|
148
|
+
const {offsetLeft, offsetTop, scrollHeight, clientHeight, scrollWidth, clientWidth} = node
|
|
149
|
+
const scrollTop = (node.parentNode && node.parentNode.scrollTop) || 0
|
|
150
|
+
const scrollLeft = (node.parentNode && node.parentNode.scrollLeft) || 0
|
|
151
|
+
const left = oldSnap.left - offsetLeft + scrollLeft
|
|
152
|
+
const top = oldSnap.top - offsetTop + scrollTop
|
|
153
|
+
const deltaHeight = oldSnap.height - newSnap.height
|
|
154
|
+
let heightFix = ''
|
|
155
|
+
let finalPadding = ''
|
|
156
|
+
if (deltaHeight > 0) {
|
|
157
|
+
const {paddingBottom} = window.getComputedStyle(node)
|
|
158
|
+
finalPadding = 'padding-bottom:' + paddingBottom + ';'
|
|
159
|
+
heightFix = `padding-bottom: ${sumWithPx(deltaHeight, paddingBottom) + (smallJumpFix || 0)};`
|
|
160
|
+
}
|
|
161
|
+
const overflowFix = scrollHeight > clientHeight || scrollWidth > clientWidth ? `overflow: hidden;` : ``
|
|
162
|
+
const transform = ((left !== 0 || top !== 0) && !skipTransform) ? `transform: translate(${left}px,${top}px);` : ''
|
|
163
|
+
const parentPaddingTopDelta = (parseInt(oldSnap.parentStyle?.paddingTop) || 0) - (parseInt(newSnap.parentStyle?.paddingTop) || 0)
|
|
164
|
+
const parentPaddingBottomDelta = (parseInt(oldSnap.parentStyle?.paddingBottom) || 0) - (parseInt(newSnap.parentStyle?.paddingBottom) || 0)
|
|
165
|
+
const oldMarginTop = (parseInt(oldSnap.marginTop) || 0) + parentPaddingTopDelta
|
|
166
|
+
const oldMarginBottom = (parseInt(oldSnap.marginBottom) || 0) + parentPaddingBottomDelta
|
|
167
|
+
|
|
168
|
+
const oldTransform = node.style.transform
|
|
169
|
+
return [oneWayAnimation(`
|
|
170
|
+
${transform}
|
|
171
|
+
${heightFix}
|
|
172
|
+
${overflowFix}
|
|
173
|
+
margin-top: ${oldMarginTop}px;
|
|
174
|
+
margin-bottom: ${oldMarginBottom}px;
|
|
175
|
+
width: ${oldSnap.width}px;
|
|
176
|
+
height: ${Math.max(0,oldSnap.height+(smallJumpFix || 0))}px;
|
|
177
|
+
`, `
|
|
178
|
+
${finalPadding}
|
|
179
|
+
${oldTransform ? `transform:${oldTransform};` : ''}
|
|
180
|
+
${overflowFix}
|
|
181
|
+
margin-top:${parseInt(newSnap.marginTop) || 0}px;
|
|
182
|
+
margin-bottom:${parseInt(newSnap.marginBottom) || 0}px;
|
|
183
|
+
width: ${newSnap.width}px;
|
|
184
|
+
height: ${Math.max(0,newSnap.height+(smallJumpFix || 0))}px;
|
|
185
|
+
`)]
|
|
186
|
+
} else {
|
|
187
|
+
return []
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const getClientRectAnimationForSwitch = (oldSnap, newSnap, {skipTransform } = {}) => {
|
|
192
|
+
if (hasClientRectChanged(oldSnap, newSnap)) {
|
|
193
|
+
const node = newSnap.domNode
|
|
194
|
+
const {offsetLeft, offsetTop, scrollHeight, clientHeight, scrollWidth, clientWidth} = node
|
|
195
|
+
const scrollTop = node.parentNode?.scrollTop || 0
|
|
196
|
+
const scrollLeft = node.parentNode?.scrollLeft || 0
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
// get margin-bottom
|
|
200
|
+
const prevBorder = node.style.border
|
|
201
|
+
node.style.border = '1px solid transparent'
|
|
202
|
+
const borderHeight = node.clientHeight
|
|
203
|
+
node.style.border = prevBorder
|
|
204
|
+
|
|
205
|
+
// get negative margin top
|
|
206
|
+
const prevPadding = newSnap.paddingTop
|
|
207
|
+
const nonPaddingFixTop = newSnap.clonedNode.getBoundingClientRect().top
|
|
208
|
+
let topWithPaddingFix = nonPaddingFixTop
|
|
209
|
+
if (!parseInt(prevPadding)) {
|
|
210
|
+
node.style.paddingTop = '1px'
|
|
211
|
+
topWithPaddingFix = newSnap.clonedNode.getBoundingClientRect().top
|
|
212
|
+
node.style.paddingTop = ''
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const newSnapNegativeTopMarginDelta = topWithPaddingFix - nonPaddingFixTop
|
|
216
|
+
const newSnapBottomMarginDelta = clientHeight - borderHeight
|
|
217
|
+
|
|
218
|
+
const left = oldSnap.left - offsetLeft + scrollLeft
|
|
219
|
+
const top = oldSnap.top - offsetTop + scrollTop - newSnapNegativeTopMarginDelta
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
const deltaHeight = oldSnap.height - newSnap.height
|
|
223
|
+
let heightFix = ''
|
|
224
|
+
let finalPadding = ''
|
|
225
|
+
if (deltaHeight > 0) {
|
|
226
|
+
const {paddingBottom} = window.getComputedStyle(node)
|
|
227
|
+
finalPadding = 'padding-bottom:' + paddingBottom + ';'
|
|
228
|
+
heightFix = `padding-bottom: ${sumWithPx(deltaHeight, paddingBottom)};`
|
|
229
|
+
}
|
|
230
|
+
const overflowFix = scrollHeight > clientHeight || scrollWidth > clientWidth ? `overflow: hidden;` : ``
|
|
231
|
+
const transform = ((left !== 0 || top !== 0) && !skipTransform) ? `transform: translate(${left}px,${top}px);` : ''
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
const oldTransform = node.style.transform
|
|
235
|
+
return [oneWayAnimation(`
|
|
236
|
+
${transform}
|
|
237
|
+
${heightFix}
|
|
238
|
+
${overflowFix}
|
|
239
|
+
width: ${oldSnap.width}px;
|
|
240
|
+
height: ${oldSnap.height}px;
|
|
241
|
+
`, `
|
|
242
|
+
${finalPadding}
|
|
243
|
+
${oldTransform ? `transform:${oldTransform};` : ''}
|
|
244
|
+
${overflowFix}
|
|
245
|
+
width: ${newSnap.width}px;
|
|
246
|
+
height: ${(newSnap.height - newSnapBottomMarginDelta)}px;
|
|
247
|
+
`)]
|
|
248
|
+
} else {
|
|
249
|
+
return []
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const willPlaySwitch = (oldSnap, newSnap, {switchIn, switchOut, switchAnimation, timing, transformHeight = _.identity}) => {
|
|
254
|
+
const realWrapper = newSnap.domNode
|
|
255
|
+
const newChildren = [].slice.call(realWrapper.children)
|
|
256
|
+
// realWrapper.style.willChange = 'width,height,margin-top;'
|
|
257
|
+
if (realWrapper.firstChild) {
|
|
258
|
+
realWrapper.insertBefore(oldSnap.clonedNode, realWrapper.firstChild)
|
|
259
|
+
realWrapper.insertBefore(newSnap.clonedNode, realWrapper.firstChild)
|
|
260
|
+
} else {
|
|
261
|
+
realWrapper.append(oldSnap.clonedNode)
|
|
262
|
+
realWrapper.append(newSnap.clonedNode)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
oldSnap.clonedNode.className = oldSnap.clonedNode.className + ' JS-old-snap'
|
|
266
|
+
newSnap.clonedNode.className = newSnap.clonedNode.className + ' JS-new-snap'
|
|
267
|
+
// newSnap.clonedNode.style.willChange = 'width,height,opacity'
|
|
268
|
+
// oldSnap.clonedNode.style.willChange = 'width,height,opacity'
|
|
269
|
+
const realWrapperRect = realWrapper.getBoundingClientRect()
|
|
270
|
+
const resizeToNew = oneWayAnimation(`
|
|
271
|
+
overflow: hidden;
|
|
272
|
+
width: ${oldSnap.width}px;
|
|
273
|
+
height: ${oldSnap.height}px;
|
|
274
|
+
`, `
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
width: ${newSnap.clonedNode.style.width};
|
|
277
|
+
height: ${callIfFunction(transformHeight,newSnap.clonedNode.style.height,realWrapperRect)};
|
|
278
|
+
`)
|
|
279
|
+
|
|
280
|
+
childMAp(newChildren, c => {
|
|
281
|
+
if (c.dataset && !c.dataset.noFlip) {
|
|
282
|
+
// Don't capture "0" as prevOpacity — it's always a transient value from
|
|
283
|
+
// a prior uncompleted willPlaySwitch. Otherwise finishSwitchNew restores
|
|
284
|
+
// opacity to "0" permanently, leaving content invisible.
|
|
285
|
+
if (c.style.opacity && c.style.opacity !== '0') {
|
|
286
|
+
c.dataset.prevOpacity = c.style.opacity
|
|
287
|
+
}
|
|
288
|
+
c.style.opacity = '0'
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
childMAp([].slice.call(oldSnap.clonedNode.children), c => {
|
|
292
|
+
if (c.dataset && c.dataset.noFlip) {
|
|
293
|
+
c.style.opacity = '0'
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
const resizeOld = (oldSnap.width > newSnap.width || oldSnap.height > newSnap.height) ? [resizeToNew] : [resizeToNew]
|
|
298
|
+
const resizeNew = (oldSnap.width < newSnap.width || oldSnap.height < newSnap.height) ? [resizeToNew] : [resizeToNew]
|
|
299
|
+
|
|
300
|
+
return () => {
|
|
301
|
+
morph(oldSnap.clonedNode,
|
|
302
|
+
formatAnimation([].concat(switchOut || switchAnimation, resizeOld), timing),
|
|
303
|
+
() => oldSnap.clonedNode.remove()
|
|
304
|
+
)
|
|
305
|
+
newSnap.clonedNode.style.opacity = '0'
|
|
306
|
+
continueSwitchCallbacks.set(realWrapper, continueSwitchOnClone(realWrapper))
|
|
307
|
+
|
|
308
|
+
morph(newSnap.clonedNode,
|
|
309
|
+
formatAnimation([formatAnimation(
|
|
310
|
+
switchIn || switchAnimation,
|
|
311
|
+
timing,
|
|
312
|
+
!switchIn
|
|
313
|
+
)].concat(resizeNew), timing),
|
|
314
|
+
finishSwitchNew(newSnap.clonedNode, realWrapper)
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
const getDelays = (el, animation) => {
|
|
321
|
+
const delayStr = (morphTime.get(el) - Date.now()) + 'ms'
|
|
322
|
+
return animation.split(',').map(_ => delayStr).join(',')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
class Snapshoter extends React.Component {
|
|
326
|
+
|
|
327
|
+
getSnapshotBeforeUpdate(oldProps) {
|
|
328
|
+
if (oldProps.on !== this.props.on) {
|
|
329
|
+
console.log('on', 'UHUL')
|
|
330
|
+
} else if (oldProps.moveOn !== this.props.moveOn) {
|
|
331
|
+
console.log('moveOn', 'UHUL')
|
|
332
|
+
}
|
|
333
|
+
return null
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
render() {
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export const useFlip = ({on, moveOn}) => {
|
|
342
|
+
useHiddenFill(<Snapshoter on={on} moveOn={moveOn} callback={console.log}/>)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const getAnimationNames = (animation) => animation.split(',').map(_ => _.replace(/\s*(\S+).*/, '$1'))
|
|
346
|
+
|
|
347
|
+
export const willUseFlip = () => {
|
|
348
|
+
|
|
349
|
+
let controlElement = null
|
|
350
|
+
let morphEnabled = false
|
|
351
|
+
const disableMorph = () => {
|
|
352
|
+
controlElement = null
|
|
353
|
+
morphEnabled = false
|
|
354
|
+
}
|
|
355
|
+
const [useRegisterEnter, enterChildrenCallback] = willUseSharedCallback(useLayoutEffect)
|
|
356
|
+
const [useRegisterLeave, leaveChildrenCallback] = willUseSharedCallback(useLayoutEffect)
|
|
357
|
+
const [useRegisterMorph, morphChildrenCallback, getSize] = willUseSharedCallback(useLayoutEffect)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
const useEnterAnimation = (ref, onEnter = defaultEnterAnimation, {onEnterEnd, timing = defaultAnimationMaker, enterEffect} = {}) => {
|
|
361
|
+
useLayoutEffect(() => {
|
|
362
|
+
if (!morphEnabled && onEnter && ref.current) {
|
|
363
|
+
const el = ref.current
|
|
364
|
+
const animation = formatAnimation(onEnter, timing)
|
|
365
|
+
const endEffect = callIfFunction(enterEffect,el)
|
|
366
|
+
morph(
|
|
367
|
+
el,
|
|
368
|
+
animation,
|
|
369
|
+
() => {
|
|
370
|
+
callIfFunction(onEnterEnd)
|
|
371
|
+
callIfFunction(endEffect,el)
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
enterChildrenCallback(timing)
|
|
375
|
+
}
|
|
376
|
+
}, [onEnter])
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const useLeaveAnimation = (ref, onLeave = defaultEnterAnimation, {ref: removeRef, leaveEffect, timing = defaultAnimationMaker, nestedFlip = false, events = {}, reverse = true} = {}) => {
|
|
380
|
+
useLayoutEffect(() => () => {
|
|
381
|
+
if (onLeave && ref.current) {
|
|
382
|
+
const el = ref.current
|
|
383
|
+
const animation = formatAnimation(onLeave, timing, reverse)
|
|
384
|
+
// on leave animations require a clone, as the real node will be intantaneousy removed by react
|
|
385
|
+
leaveChildrenCallback(timing)
|
|
386
|
+
const isNested = callIfFunction(nestedFlip)
|
|
387
|
+
const clone = isNested ? el : el.cloneNode(true)
|
|
388
|
+
_s.each((callback, type) => clone.addEventListener(type, callback), events)
|
|
389
|
+
|
|
390
|
+
const endEffect = callIfFunction(leaveEffect,clone,el)
|
|
391
|
+
|
|
392
|
+
if (removeRef) { removeRef.current = clone }
|
|
393
|
+
if (!isNested) {
|
|
394
|
+
el.parentNode.insertBefore(clone, el)
|
|
395
|
+
}
|
|
396
|
+
// sometimes the clone may need to trigger events (usual case: onmouseover to reopen the element)
|
|
397
|
+
|
|
398
|
+
// allow clone to be continued by another animation
|
|
399
|
+
controlElement = clone
|
|
400
|
+
// the animation
|
|
401
|
+
const animate = () => {
|
|
402
|
+
morph(
|
|
403
|
+
clone,
|
|
404
|
+
animation,
|
|
405
|
+
() => {
|
|
406
|
+
clone.remove()
|
|
407
|
+
disableMorph()
|
|
408
|
+
callIfFunction(endEffect,clone,el)
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// wait for current animation to end and then animate
|
|
414
|
+
if (clone.style.animation) {
|
|
415
|
+
const maxAnimationTime = _.max((clone.style.animationDuration.split(",") || ["0.25s"]).map(s => parseFloat(s.trim() || "0.25") * (s.indexOf("m")>0 ? 1 : 1000)))
|
|
416
|
+
const timeoutRef = {}
|
|
417
|
+
const animationEnd = (e) => {
|
|
418
|
+
clearTimeout(timeoutRef.current || -1)
|
|
419
|
+
clone.removeEventListener('animationend', animationEnd)
|
|
420
|
+
clone.style.animationDelay = ''
|
|
421
|
+
e?.stopImmediatePropagation()
|
|
422
|
+
setTimeout(animate, 1)
|
|
423
|
+
}
|
|
424
|
+
timeoutRef.current = setTimeout(animationEnd,maxAnimationTime)
|
|
425
|
+
clone.addEventListener('animationend', animationEnd)
|
|
426
|
+
|
|
427
|
+
clone.style.animationDelay = getDelays(el, clone.style.animation)
|
|
428
|
+
} else {
|
|
429
|
+
animate()
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}, [onLeave])
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const useMorphAnimation = (ref, ready = true, {timing = defaultAnimationMaker} = {}) => {
|
|
436
|
+
useLayoutEffect(() => {
|
|
437
|
+
if (ready && controlElement && ref.current) {
|
|
438
|
+
// const controlElement =getControlElement()
|
|
439
|
+
const el = ref.current
|
|
440
|
+
const animation = oneWayAnimation(`opacity:${window.getComputedStyle(controlElement).opacity};`, `opacity: 1`)
|
|
441
|
+
const oldSnap = {
|
|
442
|
+
...toPosition(controlElement, undefined, {allowFixed: true}),
|
|
443
|
+
clonedNode: cloneWrapper(controlElement)
|
|
444
|
+
}
|
|
445
|
+
const newSnap = {...toPosition(el, undefined, {allowFixed: true}), clonedNode: cloneWrapper(el)}
|
|
446
|
+
const rectAnimation = getClientRectAnimation(oldSnap, newSnap)
|
|
447
|
+
morph(newSnap.domNode, formatAnimation(rectAnimation.concat(animation), timing), disableMorph)
|
|
448
|
+
morphChildrenCallback((executeAnimation) => executeAnimation(timing))
|
|
449
|
+
if (rectAnimation.length) {
|
|
450
|
+
willPlaySwitch(oldSnap, newSnap, {switchAnimation: defaultSwitchAnimation, timing})()
|
|
451
|
+
}
|
|
452
|
+
controlElement.remove()
|
|
453
|
+
disableMorph()
|
|
454
|
+
}
|
|
455
|
+
return () => {
|
|
456
|
+
morphEnabled = true
|
|
457
|
+
// continue existing switch animation on the clone
|
|
458
|
+
if (continueSwitchCallbacks.has(ref.current)) {
|
|
459
|
+
continueSwitchCallbacks.get(ref.current)(controlElement)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}, [ready])
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const useNestedEnterAnimation = (ref, onEnter, {timing} = {}) => {
|
|
466
|
+
useRegisterEnter((parentTiming) => {
|
|
467
|
+
const el = ref.current
|
|
468
|
+
const animation = formatAnimation(onEnter, timing || parentTiming)
|
|
469
|
+
morph(el, animation)
|
|
470
|
+
return getAnimationNames(animation)
|
|
471
|
+
})
|
|
472
|
+
}
|
|
473
|
+
const useNestLeaveAnimation = (ref, onLeave, {timing, events} = {}) => {
|
|
474
|
+
useRegisterLeave((parentTiming) => {
|
|
475
|
+
const el = ref.current
|
|
476
|
+
const animation = formatAnimation(onLeave, timing || parentTiming)
|
|
477
|
+
morph(el, animation)
|
|
478
|
+
return getAnimationNames(animation)
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const willUseNestedMorphAnimation = () => {
|
|
483
|
+
const morphId = _.uniqueId('JS-')
|
|
484
|
+
return (ref) => {
|
|
485
|
+
useLayoutEffect(() => {
|
|
486
|
+
if (ref.current?.dataset) {
|
|
487
|
+
ref.current.dataset.morphId = morphId
|
|
488
|
+
}
|
|
489
|
+
})
|
|
490
|
+
const executeAnimation = useConstant((timing) => {
|
|
491
|
+
const prev = controlElement.querySelector(`[data-morph-id='${morphId}']`)
|
|
492
|
+
const next = ref.current
|
|
493
|
+
if (next.dataset) {
|
|
494
|
+
next.dataset.noFlip = true
|
|
495
|
+
}
|
|
496
|
+
next.style.opacity = '1'
|
|
497
|
+
const prevPos = prev.getBoundingClientRect()
|
|
498
|
+
const nextPos = next.getBoundingClientRect()
|
|
499
|
+
const originalTransform = next.style.transform || ''
|
|
500
|
+
const deltaX = Math.round(nextPos.left - prevPos.left)
|
|
501
|
+
const deltaY = Math.round(nextPos.top - prevPos.top)
|
|
502
|
+
const newLeft = deltaX > 0 ? -deltaX : -deltaX
|
|
503
|
+
const animation = oneWayAnimation(`
|
|
504
|
+
transform: translate(${newLeft}px,${deltaY}px) ${originalTransform};
|
|
505
|
+
`, `
|
|
506
|
+
transform: translate(0,0) ${originalTransform};
|
|
507
|
+
`)
|
|
508
|
+
morph(next, formatAnimation(animation, timing))
|
|
509
|
+
})
|
|
510
|
+
useRegisterMorph(executeAnimation)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return [[useEnterAnimation, useLeaveAnimation, useMorphAnimation], [useNestedEnterAnimation, useNestLeaveAnimation], willUseNestedMorphAnimation]
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export const willUseFlipSwitch = () => {
|
|
518
|
+
let oldSnap = null
|
|
519
|
+
const getTempWrapper = (parent,positionConfig = {}) => {
|
|
520
|
+
const tempWrapper = cloneWrapper(parent)
|
|
521
|
+
return {...toPosition(parent,undefined,positionConfig), clonedNode: tempWrapper}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return (ref, onEnter, onLeave, {timing = defaultAnimationMaker, switchAnimation = defaultSwitchAnimation, effect} = {}) => {
|
|
525
|
+
useLayoutEffect(() => {
|
|
526
|
+
if (oldSnap && ref.current) {
|
|
527
|
+
const newSnap = getTempWrapper(ref.current,{withMargins:true})
|
|
528
|
+
let switchToPlay = _.identity
|
|
529
|
+
if (oldSnap.clonedNode) {
|
|
530
|
+
switchToPlay = willPlaySwitch(oldSnap, newSnap, {
|
|
531
|
+
switchIn: callIfFunction(onEnter),
|
|
532
|
+
switchOut: callIfFunction(onLeave),
|
|
533
|
+
switchAnimation: callIfFunction(switchAnimation),
|
|
534
|
+
timing
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
const animation = getClientRectAnimationForSwitch(oldSnap, newSnap)
|
|
538
|
+
const endEffect = callIfFunction(effect,oldSnap.clonedNode,newSnap.clonedNode,ref.current)
|
|
539
|
+
switchToPlay()
|
|
540
|
+
morph(newSnap.domNode, formatAnimation(animation, timing), () => callIfFunction(endEffect,oldSnap.clonedNode,newSnap.clonedNode))
|
|
541
|
+
}
|
|
542
|
+
return () => {
|
|
543
|
+
oldSnap = getTempWrapper(ref.current,{withMargins:true})
|
|
544
|
+
}
|
|
545
|
+
}, [])
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const flipHooks = willUseFlip()
|
|
550
|
+
const useFlipEnter = flipHooks[0][0]
|
|
551
|
+
const useFlipLeave = flipHooks[0][1]
|
|
552
|
+
export {useFlipEnter, useFlipLeave}
|
|
553
|
+
|
|
554
|
+
export const useFlipAnimation = (ref, onEnterOrBoth, onLeaveOrIgnore = true, config = {}) => {
|
|
555
|
+
const onEnter = ifUndefined(onEnterOrBoth && onEnterOrBoth.onEnter,onEnterOrBoth)
|
|
556
|
+
const onLeave = ifUndefined(onEnterOrBoth && onEnterOrBoth.onLeave,onLeaveOrIgnore)
|
|
557
|
+
useFlipEnter(ref, onEnter, config)
|
|
558
|
+
useFlipLeave(ref, onLeave === true ? onEnter : onLeave, {...config, reverse: onLeave === true})
|
|
559
|
+
}
|
|
560
|
+
|
package/src/FlipList.jsx
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom'
|
|
3
|
+
import {defaultAnimationMaker, morph, formatAnimation, getClientRectAnimation, toPosition} from './Flip'
|
|
4
|
+
import {oneWayAnimation} from '@startlibs/utils'
|
|
5
|
+
import {callIfFunction} from '@startlibs/utils'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export class FlipList extends React.Component {
|
|
9
|
+
|
|
10
|
+
static defaultProps = {
|
|
11
|
+
timing: defaultAnimationMaker,
|
|
12
|
+
animation: oneWayAnimation(`transform: scale(1); transform-origin: center; opacity: 1;`, `transform: scale(0); transform-origin: center; opacity: 0;`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
componentDidUpdate(prevProps, prevState, snap) {
|
|
16
|
+
if (snap) {
|
|
17
|
+
const useLockedParentRect = (this.props.anchoredDownList) && snap.parentRect
|
|
18
|
+
const newSnap = this.calculatePositionForAnimation(useLockedParentRect ? snap.parentRect : undefined)
|
|
19
|
+
if (newSnap) { // se não tiver parentNode, verficar se há uma div sendo offsetParent, body não serve
|
|
20
|
+
this.play(snap, newSnap)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
play = (oldSnap, newSnap) => {
|
|
26
|
+
const {animation,insertAnimation,removeAnimation, onEndAnimation, onStartAnimation, effect, skipChromeFix, timing, skipParentMorph} = this.props
|
|
27
|
+
newSnap.children.forEach(newItemSnap => {
|
|
28
|
+
const oldItemSnap = oldSnap.children.get(newItemSnap.domNode)
|
|
29
|
+
if (!oldItemSnap) return
|
|
30
|
+
const rectAnimation = getClientRectAnimation(oldItemSnap, newItemSnap)
|
|
31
|
+
morph(newItemSnap.domNode,formatAnimation(rectAnimation,timing))
|
|
32
|
+
})
|
|
33
|
+
if (newSnap.children.size > oldSnap.children.size) {
|
|
34
|
+
const newItems = Array.from(newSnap.children.values()).filter(item => !oldSnap.children.has(item.domNode))
|
|
35
|
+
newItems.forEach((newItem) => {
|
|
36
|
+
morph(newItem.domNode, formatAnimation(insertAnimation || animation, timing, !insertAnimation))
|
|
37
|
+
})
|
|
38
|
+
} else if (newSnap.children.size < oldSnap.children.size) {
|
|
39
|
+
const oldItems = Array.from(oldSnap.children.values()).filter(item => !newSnap.children.has(item.domNode))
|
|
40
|
+
|
|
41
|
+
oldItems.forEach((oldItem) => {
|
|
42
|
+
const reinserted = this.reinsertSnap(oldItem,oldSnap.scrollTop - newSnap.scrollTop, oldItems)
|
|
43
|
+
if (!reinserted) { return }
|
|
44
|
+
new Promise(res => {
|
|
45
|
+
morph(oldItem.domNode, formatAnimation(removeAnimation || animation, timing), res)
|
|
46
|
+
setTimeout(res,1500)
|
|
47
|
+
}).then(() => {
|
|
48
|
+
oldItem.domNode.remove()
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
const rectAnimation = getClientRectAnimation(oldSnap.parent, newSnap.parent,{skipTransform:true,smallJumpFix: this.props.smallJumpFix})
|
|
54
|
+
if (newSnap.parent?.domNode && !skipParentMorph) {
|
|
55
|
+
const endEffect = callIfFunction(effect, newSnap.parent.domNode, {newSnap, oldSnap})
|
|
56
|
+
callIfFunction(onStartAnimation)
|
|
57
|
+
morph(newSnap.parent.domNode,formatAnimation(rectAnimation, timing),endEffect || onEndAnimation)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
reinsertSnap = (item,scrollDelta, allChildren) => {
|
|
62
|
+
if (item.nextSibling) {
|
|
63
|
+
|
|
64
|
+
if (!item.nextSibling.parentNode) {
|
|
65
|
+
const next = allChildren.find(possible => item.nextSibling === possible.domNode)
|
|
66
|
+
if (next) {
|
|
67
|
+
return this.reinsertSnap({...item,nextSibling:next.nextSibling},scrollDelta,allChildren)
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
item.nextSibling.parentNode?.insertBefore(item.domNode, item.nextSibling)
|
|
72
|
+
} else {
|
|
73
|
+
item.parentNode.appendChild(item.domNode)
|
|
74
|
+
}
|
|
75
|
+
const {width,height} = item.domNode.getBoundingClientRect()
|
|
76
|
+
item.domNode.style.left = item.domNode.offsetLeft + 'px'
|
|
77
|
+
item.domNode.style.top = item.domNode.offsetTop - scrollDelta + 'px'
|
|
78
|
+
if (!this.props.anchoredDownList) {
|
|
79
|
+
item.domNode.style.marginTop = 0
|
|
80
|
+
}
|
|
81
|
+
item.domNode.style.marginLeft = 0
|
|
82
|
+
item.domNode.style.width = width + 'px'
|
|
83
|
+
item.domNode.style.height = height + 'px'
|
|
84
|
+
item.domNode.style.position = 'absolute'
|
|
85
|
+
callIfFunction(this.props.onReinsert,item.domNode,item)
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getSnapshotBeforeUpdate(oldProps) {
|
|
90
|
+
if (this.props.play || this.props.on !== oldProps.on || (this.props.children.length !== oldProps.children.length)) {
|
|
91
|
+
return this.calculatePositionForAnimation()
|
|
92
|
+
}
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getParents = () => {
|
|
97
|
+
const node = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.findDOMNode(this)
|
|
98
|
+
const {parentNode, offsetParent: possibleOffsetParent} = node
|
|
99
|
+
const offsetParent = possibleOffsetParent || (window.getComputedStyle(parentNode).position !== 'static' ? parentNode : parentNode.offsetParent)
|
|
100
|
+
if (!offsetParent) return {};
|
|
101
|
+
const parentRect = offsetParent.getBoundingClientRect()
|
|
102
|
+
return {
|
|
103
|
+
offsetParent,
|
|
104
|
+
parentNode,
|
|
105
|
+
parentRect
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
calculatePositionForAnimation = (lockedParentRect) => {
|
|
110
|
+
const {parentRect, parentNode, offsetParent} = this.getParents()
|
|
111
|
+
if (!offsetParent) return null
|
|
112
|
+
const effectiveParentRect = lockedParentRect || parentRect
|
|
113
|
+
const children = new Map();
|
|
114
|
+
[].slice.call(parentNode.children).map((node) => {
|
|
115
|
+
const position = toPosition(node, effectiveParentRect, {withMargins:true, numberOfSiblings: this.props.numberOfElements || 1})
|
|
116
|
+
if (position) { children.set(node, position) }
|
|
117
|
+
})
|
|
118
|
+
return {
|
|
119
|
+
children,
|
|
120
|
+
parent: toPosition(offsetParent,undefined, {withMargins:true}),
|
|
121
|
+
parentRect: effectiveParentRect,
|
|
122
|
+
scrollTop:offsetParent.scrollTop
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
render() {
|
|
127
|
+
const {children, as, play, on, onStartAnimation, onEndAnimation, ...rest} = this.props
|
|
128
|
+
const result = React.Children.count(children) ? children : <div style={{display: 'none'}}/>
|
|
129
|
+
if (as) {
|
|
130
|
+
const RenderAs = as
|
|
131
|
+
return <RenderAs {...rest}>{result}</RenderAs>
|
|
132
|
+
}
|
|
133
|
+
return result
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|