js-kompo-utils 1.0.27 → 1.0.28
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/components/VlSelect.vue +133 -124
- package/package.json +1 -1
package/components/VlSelect.vue
CHANGED
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
</template>
|
|
65
65
|
|
|
66
66
|
<script>
|
|
67
|
+
import { autoUpdate, computePosition, flip, hide, offset, shift, size } from '@floating-ui/dom'
|
|
67
68
|
import Select from 'vue-kompo/js/form/form-fields/Select.vue'
|
|
68
69
|
|
|
69
70
|
export default {
|
|
@@ -71,7 +72,6 @@ export default {
|
|
|
71
72
|
data() {
|
|
72
73
|
return {
|
|
73
74
|
flipUp: false,
|
|
74
|
-
panelStyle: null,
|
|
75
75
|
isTouchDevice: false,
|
|
76
76
|
}
|
|
77
77
|
},
|
|
@@ -82,9 +82,11 @@ export default {
|
|
|
82
82
|
forceUp() {
|
|
83
83
|
return !!this.$_config('openUpward')
|
|
84
84
|
},
|
|
85
|
+
forceDown() {
|
|
86
|
+
return !!this.$_config('openDownward') || this.$_config('autoOptionsPlacement') === false
|
|
87
|
+
},
|
|
85
88
|
optionsStyle() {
|
|
86
|
-
|
|
87
|
-
return this.panelStyle
|
|
89
|
+
return this.isFocused ? null : { display: 'none' }
|
|
88
90
|
},
|
|
89
91
|
useNativeMobile() {
|
|
90
92
|
if (!this.$_config('native_on_mobile')) return false
|
|
@@ -115,25 +117,24 @@ export default {
|
|
|
115
117
|
watch: {
|
|
116
118
|
isFocused(focused) {
|
|
117
119
|
if (focused) {
|
|
118
|
-
this.$nextTick(() => this.refresh())
|
|
119
120
|
this.attachListeners()
|
|
121
|
+
this.$nextTick(() => {
|
|
122
|
+
this.teleportOptions()
|
|
123
|
+
this.startPositioning()
|
|
124
|
+
})
|
|
120
125
|
} else {
|
|
121
126
|
this.flipUp = false
|
|
122
|
-
this.panelStyle = null
|
|
123
127
|
this.detachListeners()
|
|
128
|
+
this.resetPanelStyles()
|
|
124
129
|
}
|
|
125
130
|
},
|
|
126
131
|
filteredOptions() {
|
|
127
|
-
if (this.isFocused) this.$nextTick(() => this.
|
|
132
|
+
if (this.isFocused) this.$nextTick(() => this.queuePositionUpdate())
|
|
128
133
|
},
|
|
129
134
|
optionsKey() {
|
|
130
135
|
this.$nextTick(() => {
|
|
131
136
|
this.teleportOptions()
|
|
132
|
-
if (this.
|
|
133
|
-
this._panelObserver.disconnect()
|
|
134
|
-
this._panelObserver.observe(this.$refs.selectOptions)
|
|
135
|
-
}
|
|
136
|
-
if (this.isFocused) this.refresh()
|
|
137
|
+
if (this.isFocused) this.startPositioning()
|
|
137
138
|
})
|
|
138
139
|
},
|
|
139
140
|
},
|
|
@@ -197,42 +198,13 @@ export default {
|
|
|
197
198
|
anchorEl() {
|
|
198
199
|
return this.$refs.taggableInput?.$el || this.$refs.input?.parentElement
|
|
199
200
|
},
|
|
201
|
+
overlayAncestor(anchor = this.anchorEl()) {
|
|
202
|
+
return anchor?.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l') || null
|
|
203
|
+
},
|
|
200
204
|
attachListeners() {
|
|
201
205
|
if (this._listenersAttached) return
|
|
202
|
-
this._rafId = null
|
|
203
|
-
this._onScroll = (e) => {
|
|
204
|
-
const opts = this.$refs.selectOptions
|
|
205
|
-
if (opts && e.target instanceof Node && opts.contains(e.target)) return
|
|
206
|
-
if (this._rafId) return
|
|
207
|
-
this._rafId = requestAnimationFrame(() => {
|
|
208
|
-
this._rafId = null
|
|
209
|
-
this.trackPosition()
|
|
210
|
-
})
|
|
211
|
-
}
|
|
212
|
-
this._onResize = () => this.refresh()
|
|
213
|
-
window.addEventListener('scroll', this._onScroll, true)
|
|
214
|
-
window.addEventListener('resize', this._onResize, { passive: true })
|
|
215
|
-
|
|
216
|
-
if (window.visualViewport) {
|
|
217
|
-
this._onViewportScroll = () => {
|
|
218
|
-
if (this._rafId) return
|
|
219
|
-
this._rafId = requestAnimationFrame(() => {
|
|
220
|
-
this._rafId = null
|
|
221
|
-
this.trackPosition()
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
this._onViewportResize = () => this.refresh()
|
|
225
|
-
window.visualViewport.addEventListener('scroll', this._onViewportScroll)
|
|
226
|
-
window.visualViewport.addEventListener('resize', this._onViewportResize)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const opts = this.$refs.selectOptions
|
|
230
206
|
const anchor = this.anchorEl()
|
|
231
207
|
if (typeof ResizeObserver !== 'undefined') {
|
|
232
|
-
this._panelObserver = new ResizeObserver(() => {
|
|
233
|
-
if (this.isFocused) this.trackPosition()
|
|
234
|
-
})
|
|
235
|
-
if (opts) this._panelObserver.observe(opts)
|
|
236
208
|
this._anchorObserver = new ResizeObserver((entries) => {
|
|
237
209
|
const entry = entries[0]
|
|
238
210
|
if (!entry) return
|
|
@@ -244,105 +216,142 @@ export default {
|
|
|
244
216
|
})
|
|
245
217
|
if (anchor) this._anchorObserver.observe(anchor)
|
|
246
218
|
}
|
|
247
|
-
const inOverlay = !!(anchor && anchor.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l'))
|
|
248
|
-
if (inOverlay && typeof IntersectionObserver !== 'undefined' && anchor) {
|
|
249
|
-
let intersectionInitialised = false
|
|
250
|
-
this._anchorIntersection = new IntersectionObserver((entries) => {
|
|
251
|
-
const entry = entries[entries.length - 1]
|
|
252
|
-
if (!entry) return
|
|
253
|
-
if (!intersectionInitialised) {
|
|
254
|
-
intersectionInitialised = true
|
|
255
|
-
return
|
|
256
|
-
}
|
|
257
|
-
if (entry.intersectionRatio === 0) this.blur()
|
|
258
|
-
}, { threshold: 0 })
|
|
259
|
-
this._anchorIntersection.observe(anchor)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
219
|
this._listenersAttached = true
|
|
263
220
|
},
|
|
264
221
|
detachListeners() {
|
|
265
222
|
if (!this._listenersAttached) return
|
|
266
|
-
|
|
267
|
-
cancelAnimationFrame(this._rafId)
|
|
268
|
-
this._rafId = null
|
|
269
|
-
}
|
|
270
|
-
window.removeEventListener('scroll', this._onScroll, true)
|
|
271
|
-
window.removeEventListener('resize', this._onResize)
|
|
272
|
-
if (window.visualViewport) {
|
|
273
|
-
if (this._onViewportScroll) {
|
|
274
|
-
window.visualViewport.removeEventListener('scroll', this._onViewportScroll)
|
|
275
|
-
this._onViewportScroll = null
|
|
276
|
-
}
|
|
277
|
-
if (this._onViewportResize) {
|
|
278
|
-
window.visualViewport.removeEventListener('resize', this._onViewportResize)
|
|
279
|
-
this._onViewportResize = null
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
if (this._panelObserver) {
|
|
283
|
-
this._panelObserver.disconnect()
|
|
284
|
-
this._panelObserver = null
|
|
285
|
-
}
|
|
223
|
+
this.stopPositioning()
|
|
286
224
|
if (this._anchorObserver) {
|
|
287
225
|
this._anchorObserver.disconnect()
|
|
288
226
|
this._anchorObserver = null
|
|
289
227
|
}
|
|
290
|
-
if (this._anchorIntersection) {
|
|
291
|
-
this._anchorIntersection.disconnect()
|
|
292
|
-
this._anchorIntersection = null
|
|
293
|
-
}
|
|
294
228
|
this._listenersAttached = false
|
|
295
229
|
},
|
|
296
|
-
|
|
230
|
+
startPositioning() {
|
|
297
231
|
const opts = this.$refs.selectOptions
|
|
298
232
|
const anchor = this.anchorEl()
|
|
299
|
-
if (!opts || !anchor) return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
233
|
+
if (!opts || !anchor || !this.isFocused) return
|
|
234
|
+
|
|
235
|
+
this.stopPositioning()
|
|
236
|
+
this.applyBasePanelStyles(opts, anchor)
|
|
237
|
+
opts.style.visibility = 'hidden'
|
|
238
|
+
opts.style.transform = 'translate3d(0px, 0px, 0)'
|
|
239
|
+
|
|
240
|
+
const updatePosition = () => this.queuePositionUpdate()
|
|
241
|
+
this._autoUpdateCleanup = autoUpdate(anchor, opts, updatePosition, {
|
|
242
|
+
ancestorScroll: true,
|
|
243
|
+
ancestorResize: true,
|
|
244
|
+
elementResize: true,
|
|
245
|
+
layoutShift: true,
|
|
246
|
+
animationFrame: true,
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
this.queuePositionUpdate(true)
|
|
250
|
+
},
|
|
251
|
+
stopPositioning() {
|
|
252
|
+
if (this._positionRafId) {
|
|
253
|
+
cancelAnimationFrame(this._positionRafId)
|
|
254
|
+
this._positionRafId = null
|
|
255
|
+
}
|
|
256
|
+
this._positionVersion = (this._positionVersion || 0) + 1
|
|
257
|
+
if (!this._autoUpdateCleanup) return
|
|
258
|
+
this._autoUpdateCleanup()
|
|
259
|
+
this._autoUpdateCleanup = null
|
|
307
260
|
},
|
|
308
|
-
|
|
261
|
+
applyBasePanelStyles(opts, anchor) {
|
|
262
|
+
const inOverlay = !!this.overlayAncestor(anchor)
|
|
263
|
+
opts.style.position = 'fixed'
|
|
264
|
+
opts.style.top = '0px'
|
|
265
|
+
opts.style.left = '0px'
|
|
266
|
+
opts.style.bottom = 'auto'
|
|
267
|
+
opts.style.width = Math.round(anchor.getBoundingClientRect().width) + 'px'
|
|
268
|
+
opts.style.maxHeight = '240px'
|
|
269
|
+
opts.style.zIndex = inOverlay ? '5001' : '1000'
|
|
270
|
+
opts.style.willChange = 'transform'
|
|
271
|
+
},
|
|
272
|
+
resetPanelStyles() {
|
|
309
273
|
const opts = this.$refs.selectOptions
|
|
310
|
-
|
|
311
|
-
if (!opts || !anchor) return
|
|
274
|
+
if (!opts) return
|
|
312
275
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
276
|
+
opts.style.position = ''
|
|
277
|
+
opts.style.top = ''
|
|
278
|
+
opts.style.left = ''
|
|
279
|
+
opts.style.bottom = ''
|
|
280
|
+
opts.style.transform = ''
|
|
281
|
+
opts.style.width = ''
|
|
282
|
+
opts.style.maxHeight = ''
|
|
283
|
+
opts.style.zIndex = ''
|
|
284
|
+
opts.style.willChange = ''
|
|
285
|
+
opts.style.visibility = ''
|
|
286
|
+
opts.style.borderRadius = ''
|
|
287
|
+
},
|
|
288
|
+
queuePositionUpdate(immediate = false) {
|
|
289
|
+
if (!this.isFocused) return
|
|
290
|
+
if (immediate) {
|
|
291
|
+
if (this._positionRafId) {
|
|
292
|
+
cancelAnimationFrame(this._positionRafId)
|
|
293
|
+
this._positionRafId = null
|
|
294
|
+
}
|
|
295
|
+
this.updatePosition()
|
|
296
|
+
return
|
|
326
297
|
}
|
|
327
|
-
this.
|
|
298
|
+
if (this._positionRafId) return
|
|
299
|
+
this._positionRafId = requestAnimationFrame(() => {
|
|
300
|
+
this._positionRafId = null
|
|
301
|
+
this.updatePosition()
|
|
302
|
+
})
|
|
303
|
+
},
|
|
304
|
+
placementConfig() {
|
|
305
|
+
if (this.forceUp) return { placement: 'top-start', auto: false }
|
|
306
|
+
if (this.forceDown) return { placement: 'bottom-start', auto: false }
|
|
307
|
+
return { placement: 'bottom-start', auto: true }
|
|
308
|
+
},
|
|
309
|
+
updatePosition() {
|
|
310
|
+
const opts = this.$refs.selectOptions
|
|
311
|
+
const anchor = this.anchorEl()
|
|
312
|
+
if (!opts || !anchor || !this.isFocused) return
|
|
328
313
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
314
|
+
const floatingEl = opts
|
|
315
|
+
const referenceEl = anchor
|
|
316
|
+
const positionVersion = this._positionVersion || 0
|
|
317
|
+
const { placement, auto } = this.placementConfig()
|
|
318
|
+
|
|
319
|
+
const middleware = [
|
|
320
|
+
offset(-1),
|
|
321
|
+
...(auto ? [flip({ fallbackPlacements: ['top-start'], padding: 8 })] : []),
|
|
322
|
+
shift({ padding: 8, crossAxis: true }),
|
|
323
|
+
size({
|
|
324
|
+
padding: 8,
|
|
325
|
+
apply: ({ availableHeight, rects, elements }) => {
|
|
326
|
+
const maxHeight = Math.max(0, Math.min(240, Math.floor(availableHeight)))
|
|
327
|
+
elements.floating.style.width = Math.round(rects.reference.width) + 'px'
|
|
328
|
+
elements.floating.style.maxHeight = maxHeight + 'px'
|
|
329
|
+
},
|
|
330
|
+
}),
|
|
331
|
+
hide(),
|
|
332
|
+
]
|
|
333
|
+
|
|
334
|
+
computePosition(referenceEl, floatingEl, {
|
|
335
|
+
strategy: 'fixed',
|
|
336
|
+
placement,
|
|
337
|
+
middleware,
|
|
338
|
+
}).then(({ x, y, placement, middlewareData }) => {
|
|
339
|
+
if (positionVersion !== (this._positionVersion || 0)) return
|
|
340
|
+
if (!this.isFocused) return
|
|
341
|
+
if (floatingEl !== this.$refs.selectOptions || referenceEl !== this.anchorEl()) {
|
|
342
|
+
this.$nextTick(() => {
|
|
343
|
+
if (this.isFocused) this.startPositioning()
|
|
344
|
+
})
|
|
345
|
+
return
|
|
345
346
|
}
|
|
347
|
+
|
|
348
|
+
const hidden = middlewareData.hide?.referenceHidden || middlewareData.hide?.escaped
|
|
349
|
+
const flipsUp = placement.startsWith('top')
|
|
350
|
+
|
|
351
|
+
this.flipUp = flipsUp
|
|
352
|
+
floatingEl.style.transform = `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`
|
|
353
|
+
floatingEl.style.borderRadius = flipsUp ? '5px 5px 0 0' : '0 0 5px 5px'
|
|
354
|
+
floatingEl.style.visibility = hidden ? 'hidden' : 'visible'
|
|
346
355
|
})
|
|
347
356
|
},
|
|
348
357
|
},
|