Diff: STRATO-apps/wordpress_03/app/wp-content/themes/blocksy/static/js/options/helpers/usePopoverMaker.js
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
import { useMemo, useRef, useState, useEffect } from '@wordpress/element'
2
+
import classnames from 'classnames'
3
+
import { __ } from 'ct-i18n'
4
+
5
+
// TODO: Rework this to use useLayoutEffect here.
6
+
// Also, always compute layout first, animate a bit later.
7
+
//
8
+
// Need to get rid of all the <Transition> components and use useSpring directly
9
+
// instead.
10
+
//
11
+
// This way nullifyTransforms() will not be needed anymore.
12
+
13
+
export function nullifyTransforms(el) {
14
+
const parseTransform = (el) =>
15
+
window
16
+
.getComputedStyle(el)
17
+
.transform.split(/\(|,|\)/)
18
+
.slice(1, -1)
19
+
.map((v) => parseFloat(v))
20
+
21
+
// 1
22
+
let { top, left, width, height } = el.getBoundingClientRect()
23
+
let transformArr = parseTransform(el)
24
+
25
+
if (transformArr.length == 6) {
26
+
// 2D matrix
27
+
const t = transformArr
28
+
29
+
// 2
30
+
let det = t[0] * t[3] - t[1] * t[2]
31
+
32
+
// 3
33
+
return {
34
+
width: width / t[0],
35
+
height: height / t[3],
36
+
left: (left * t[3] - top * t[2] + t[2] * t[5] - t[4] * t[3]) / det,
37
+
top: (-left * t[1] + top * t[0] + t[4] * t[1] - t[0] * t[5]) / det,
38
+
}
39
+
} else {
40
+
// This case is not handled because it's very rarely needed anyway.
41
+
// We just return the tranformed metrics, as they are, for consistency.
42
+
return { top, left, width, height }
43
+
}
44
+
}
45
+
46
+
const usePopoverMaker = ({
47
+
contentRef: contentRefProp,
48
+
shouldCalculate = true,
49
+
ref,
50
+
defaultHeight = 0,
51
+
} = {}) => {
52
+
const contentRef = useRef()
53
+
const [s, setState] = useState(null)
54
+
55
+
const refresh = () => {
56
+
if (!shouldCalculate) {
57
+
return
58
+
}
59
+
60
+
setState(Math.random())
61
+
}
62
+
63
+
const refreshOnScroll = (e) => {
64
+
if (!e.target || !e.target.classList || !e.target.classList.contains) {
65
+
return
66
+
}
67
+
68
+
let modalRef = contentRefProp || contentRef
69
+
70
+
if (e.target.classList.contains('ct-modal-scroll')) {
71
+
refresh()
72
+
}
73
+
74
+
if (
75
+
modalRef &&
76
+
modalRef.current &&
77
+
!modalRef.current.contains(e.target)
78
+
) {
79
+
refresh()
80
+
}
81
+
}
82
+
83
+
useEffect(() => {
84
+
setTimeout(() => {
85
+
refresh()
86
+
}, 500)
87
+
88
+
window.addEventListener('resize', refresh)
89
+
window.addEventListener('scroll', refreshOnScroll, true)
90
+
91
+
let observer
92
+
93
+
if (ref.current) {
94
+
observer = new window.ResizeObserver(refresh)
95
+
96
+
observer.observe(ref.current, {
97
+
attributes: true,
98
+
})
99
+
100
+
if (ref.current.closest('.ct-tabs-scroll')) {
101
+
observer.observe(ref.current.closest('.ct-tabs-scroll'), {
102
+
attributes: true,
103
+
})
104
+
}
105
+
106
+
if (ref.current.closest('.ct-modal-scroll')) {
107
+
observer.observe(ref.current.closest('.ct-modal-scroll'), {
108
+
attributes: true,
109
+
})
110
+
}
111
+
112
+
if (ref.current.closest('.customize-pane-child')) {
113
+
observer.observe(ref.current.closest('.customize-pane-child'), {
114
+
attributes: true,
115
+
})
116
+
}
117
+
}
118
+
119
+
if (contentRefProp ? contentRefProp.current : contentRef.current) {
120
+
if (!observer) {
121
+
observer = new window.ResizeObserver(refresh)
122
+
}
123
+
124
+
observer.observe(
125
+
contentRefProp ? contentRefProp.current : contentRef.current,
126
+
{
127
+
attributes: true,
128
+
}
129
+
)
130
+
}
131
+
132
+
return () => {
133
+
window.removeEventListener('resize', refresh)
134
+
window.removeEventListener('scroll', refreshOnScroll, true)
135
+
136
+
if (observer) {
137
+
observer.disconnect()
138
+
}
139
+
}
140
+
}, [shouldCalculate, contentRef.current, contentRefProp, ref.current])
141
+
142
+
let { right, yOffset, position, otherStyles } = useMemo(() => {
143
+
let right = 0
144
+
let yOffset = 0
145
+
let position = 'bottom'
146
+
let otherStyles = {}
147
+
148
+
if (!shouldCalculate) {
149
+
return { yOffset, right, position }
150
+
}
151
+
152
+
if (ref.current) {
153
+
let rect = ref.current.getBoundingClientRect()
154
+
155
+
let el = ref.current.closest('.ct-select-input')
156
+
? ref.current.closest('.ct-select-input')
157
+
: ref.current
158
+
159
+
let maybeWidthFlag = getComputedStyle(el, ':before').content
160
+
161
+
yOffset = rect.top + rect.height
162
+
right = window.innerWidth - rect.right
163
+
164
+
if (document.body.classList.contains('rtl')) {
165
+
right = rect.left
166
+
}
167
+
168
+
if (maybeWidthFlag.indexOf('ref-width') > -1) {
169
+
let width = rect.width
170
+
171
+
if (
172
+
maybeWidthFlag.indexOf('left') > -1 &&
173
+
el.previousElementSibling
174
+
) {
175
+
if (document.body.classList.contains('rtl')) {
176
+
width =
177
+
el.previousElementSibling.getBoundingClientRect()
178
+
.right - rect.left
179
+
} else {
180
+
width =
181
+
rect.right -
182
+
el.previousElementSibling.getBoundingClientRect()
183
+
.left
184
+
}
185
+
}
186
+
187
+
if (maybeWidthFlag.indexOf('right') > -1) {
188
+
let nextRect = el.parentNode // el.nextElementSibling || el.parentNode
189
+
.getBoundingClientRect()
190
+
191
+
if (document.body.classList.contains('rtl')) {
192
+
right = nextRect.left
193
+
width = rect.right - nextRect.left
194
+
} else {
195
+
right = window.innerWidth - nextRect.right
196
+
width = nextRect.right - rect.left
197
+
}
198
+
}
199
+
200
+
otherStyles['--x-select-dropdown-width'] = `${width}px`
201
+
}
202
+
203
+
let popoverRect =
204
+
(contentRefProp && contentRefProp.current) || contentRef.current
205
+
? nullifyTransforms(
206
+
contentRefProp
207
+
? contentRefProp.current
208
+
: contentRef.current
209
+
)
210
+
: { height: defaultHeight }
211
+
212
+
if (
213
+
yOffset + popoverRect.height > window.innerHeight &&
214
+
rect.top - 15 > popoverRect.height
215
+
) {
216
+
position = 'top'
217
+
yOffset = window.innerHeight - rect.bottom + rect.height
218
+
}
219
+
220
+
if (
221
+
yOffset + popoverRect.height > window.innerHeight &&
222
+
position === 'bottom'
223
+
) {
224
+
position = 'top'
225
+
yOffset = 0
226
+
}
227
+
}
228
+
229
+
return { yOffset, right, position, otherStyles }
230
+
}, [
231
+
s,
232
+
shouldCalculate,
233
+
ref,
234
+
ref.current,
235
+
contentRefProp,
236
+
contentRef.current,
237
+
defaultHeight,
238
+
])
239
+
240
+
return {
241
+
refreshPopover: refresh,
242
+
styles: {
243
+
'--modal-y-offset': `${yOffset}px`,
244
+
'--modal-x-offset': `${right}px`,
245
+
...otherStyles,
246
+
// [position === 'bottom' ? 'top' : 'bottom']: `${yOffset}px`,
247
+
// right: `${right}px`,
248
+
},
249
+
250
+
position,
251
+
252
+
popoverProps: {
253
+
ref: contentRefProp || contentRef,
254
+
...(position
255
+
? {
256
+
'data-position': position,
257
+
}
258
+
: {}),
259
+
},
260
+
}
261
+
}
262
+
263
+
export default usePopoverMaker
264
+