js-kompo-utils 1.0.26 → 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 -111
- 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,41 +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._onViewportChange = () => {
|
|
218
|
-
if (this._rafId) return
|
|
219
|
-
this._rafId = requestAnimationFrame(() => {
|
|
220
|
-
this._rafId = null
|
|
221
|
-
this.refresh()
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
window.visualViewport.addEventListener('resize', this._onViewportChange)
|
|
225
|
-
window.visualViewport.addEventListener('scroll', this._onViewportChange)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const opts = this.$refs.selectOptions
|
|
229
206
|
const anchor = this.anchorEl()
|
|
230
207
|
if (typeof ResizeObserver !== 'undefined') {
|
|
231
|
-
this._panelObserver = new ResizeObserver(() => {
|
|
232
|
-
if (this.isFocused) this.trackPosition()
|
|
233
|
-
})
|
|
234
|
-
if (opts) this._panelObserver.observe(opts)
|
|
235
208
|
this._anchorObserver = new ResizeObserver((entries) => {
|
|
236
209
|
const entry = entries[0]
|
|
237
210
|
if (!entry) return
|
|
@@ -243,93 +216,142 @@ export default {
|
|
|
243
216
|
})
|
|
244
217
|
if (anchor) this._anchorObserver.observe(anchor)
|
|
245
218
|
}
|
|
246
|
-
if (typeof IntersectionObserver !== 'undefined' && anchor) {
|
|
247
|
-
this._anchorIntersection = new IntersectionObserver((entries) => {
|
|
248
|
-
const entry = entries[entries.length - 1]
|
|
249
|
-
if (entry && entry.intersectionRatio === 0) this.blur()
|
|
250
|
-
}, { threshold: 0 })
|
|
251
|
-
this._anchorIntersection.observe(anchor)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
219
|
this._listenersAttached = true
|
|
255
220
|
},
|
|
256
221
|
detachListeners() {
|
|
257
222
|
if (!this._listenersAttached) return
|
|
258
|
-
|
|
259
|
-
cancelAnimationFrame(this._rafId)
|
|
260
|
-
this._rafId = null
|
|
261
|
-
}
|
|
262
|
-
window.removeEventListener('scroll', this._onScroll, true)
|
|
263
|
-
window.removeEventListener('resize', this._onResize)
|
|
264
|
-
if (this._onViewportChange && window.visualViewport) {
|
|
265
|
-
window.visualViewport.removeEventListener('resize', this._onViewportChange)
|
|
266
|
-
window.visualViewport.removeEventListener('scroll', this._onViewportChange)
|
|
267
|
-
this._onViewportChange = null
|
|
268
|
-
}
|
|
269
|
-
if (this._panelObserver) {
|
|
270
|
-
this._panelObserver.disconnect()
|
|
271
|
-
this._panelObserver = null
|
|
272
|
-
}
|
|
223
|
+
this.stopPositioning()
|
|
273
224
|
if (this._anchorObserver) {
|
|
274
225
|
this._anchorObserver.disconnect()
|
|
275
226
|
this._anchorObserver = null
|
|
276
227
|
}
|
|
277
|
-
if (this._anchorIntersection) {
|
|
278
|
-
this._anchorIntersection.disconnect()
|
|
279
|
-
this._anchorIntersection = null
|
|
280
|
-
}
|
|
281
228
|
this._listenersAttached = false
|
|
282
229
|
},
|
|
283
|
-
|
|
230
|
+
startPositioning() {
|
|
284
231
|
const opts = this.$refs.selectOptions
|
|
285
232
|
const anchor = this.anchorEl()
|
|
286
|
-
if (!opts || !anchor) return
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
260
|
+
},
|
|
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'
|
|
294
271
|
},
|
|
295
|
-
|
|
272
|
+
resetPanelStyles() {
|
|
296
273
|
const opts = this.$refs.selectOptions
|
|
297
|
-
|
|
298
|
-
if (!opts || !anchor) return
|
|
274
|
+
if (!opts) return
|
|
299
275
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
|
313
297
|
}
|
|
314
|
-
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
|
|
315
313
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
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
|
|
332
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'
|
|
333
355
|
})
|
|
334
356
|
},
|
|
335
357
|
},
|