js-kompo-utils 1.0.21 → 1.0.23
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/VlDropdown.vue +14 -20
- package/components/VlSelect.vue +142 -46
- package/package.json +1 -1
|
@@ -63,6 +63,9 @@ export default {
|
|
|
63
63
|
shouldShowMenu(visible) {
|
|
64
64
|
if (visible) {
|
|
65
65
|
this.updatePosition()
|
|
66
|
+
this.attachListeners()
|
|
67
|
+
} else {
|
|
68
|
+
this.detachListeners()
|
|
66
69
|
}
|
|
67
70
|
if (!visible && this.openOnClick) {
|
|
68
71
|
this.open = false
|
|
@@ -147,11 +150,10 @@ export default {
|
|
|
147
150
|
},
|
|
148
151
|
mounted() {
|
|
149
152
|
this.teleportMenu()
|
|
150
|
-
this.setupListeners()
|
|
151
153
|
},
|
|
152
154
|
beforeDestroy() {
|
|
153
155
|
this.cleanupMenu()
|
|
154
|
-
this.
|
|
156
|
+
this.detachListeners()
|
|
155
157
|
},
|
|
156
158
|
methods:{
|
|
157
159
|
teleportMenu() {
|
|
@@ -182,27 +184,19 @@ export default {
|
|
|
182
184
|
clearTimeout(this._closeTimeout)
|
|
183
185
|
}
|
|
184
186
|
},
|
|
185
|
-
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
this._resizeHandler = () => {
|
|
192
|
-
if (this.shouldShowMenu) {
|
|
193
|
-
this.updatePosition()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
187
|
+
attachListeners() {
|
|
188
|
+
if (this._listenersAttached) return
|
|
189
|
+
this._scrollHandler = () => requestAnimationFrame(() => this.updatePosition())
|
|
190
|
+
this._resizeHandler = () => this.updatePosition()
|
|
196
191
|
window.addEventListener('scroll', this._scrollHandler, true)
|
|
197
192
|
window.addEventListener('resize', this._resizeHandler, { passive: true })
|
|
193
|
+
this._listenersAttached = true
|
|
198
194
|
},
|
|
199
|
-
|
|
200
|
-
if (this.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
window.removeEventListener('resize', this._resizeHandler)
|
|
205
|
-
}
|
|
195
|
+
detachListeners() {
|
|
196
|
+
if (!this._listenersAttached) return
|
|
197
|
+
window.removeEventListener('scroll', this._scrollHandler, true)
|
|
198
|
+
window.removeEventListener('resize', this._resizeHandler)
|
|
199
|
+
this._listenersAttached = false
|
|
206
200
|
},
|
|
207
201
|
updatePosition() {
|
|
208
202
|
const root = this.$refs.dropdownRoot
|
package/components/VlSelect.vue
CHANGED
|
@@ -1,31 +1,42 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<vl-form-field v-bind="$_wrapperAttributes">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
3
|
+
<div :style="useNativeMobile ? { position: 'relative' } : null">
|
|
4
|
+
<vlTaggableInput
|
|
5
|
+
v-click-out="blur"
|
|
6
|
+
v-bind="$_taggableInputAttributes"
|
|
7
|
+
v-on="$_taggableInputEvents"
|
|
8
|
+
ref="taggableInput">
|
|
9
|
+
<template v-slot:prepend v-if="$_icon">
|
|
10
|
+
<div v-bind="prependIcon" />
|
|
11
|
+
</template>
|
|
12
|
+
<input
|
|
13
|
+
class="vlFormControl"
|
|
14
|
+
v-bind="$_attributes"
|
|
15
|
+
v-on="$_events"
|
|
16
|
+
v-model="inputValue"
|
|
17
|
+
@keydown.enter.prevent="() => {}"
|
|
18
|
+
ref="input"
|
|
19
|
+
type="search"
|
|
20
|
+
autocomplete="off"
|
|
21
|
+
/>
|
|
22
|
+
<template v-slot:append v-if="appendIcon && !$_readOnly">
|
|
23
|
+
<div v-html="appendIcon"/>
|
|
24
|
+
</template>
|
|
25
|
+
</vlTaggableInput>
|
|
26
|
+
<select v-if="useNativeMobile"
|
|
27
|
+
class="vlNativeSelect"
|
|
28
|
+
:value="nativeValue"
|
|
29
|
+
@change="onNativeChange"
|
|
30
|
+
:style="nativeSelectStyle">
|
|
31
|
+
<option value="">{{ nativePlaceholder }}</option>
|
|
32
|
+
<option v-for="opt in options" :key="opt.value" :value="opt.value">{{ nativeLabel(opt) }}</option>
|
|
33
|
+
</select>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="vlOptions vlOptionsTeleported"
|
|
25
36
|
:key="optionsKey"
|
|
26
37
|
ref="selectOptions"
|
|
27
38
|
:style="optionsStyle"
|
|
28
|
-
:class="{ 'vlOptionsFlipUp': flipUp }">
|
|
39
|
+
:class="{ 'vlOptionsFlipUp': flipUp, 'vlOptionsVisible': isFocused }">
|
|
29
40
|
<template v-if="filteredOptions.length">
|
|
30
41
|
<div v-for="(option,key) in filteredOptions" :key="option.value" :ref="key"
|
|
31
42
|
class="vlOption"
|
|
@@ -58,7 +69,8 @@ export default {
|
|
|
58
69
|
data() {
|
|
59
70
|
return {
|
|
60
71
|
flipUp: false,
|
|
61
|
-
|
|
72
|
+
panelStyle: null,
|
|
73
|
+
isTouchDevice: false,
|
|
62
74
|
}
|
|
63
75
|
},
|
|
64
76
|
computed: {
|
|
@@ -69,51 +81,135 @@ export default {
|
|
|
69
81
|
return !!this.$_config('openUpward')
|
|
70
82
|
},
|
|
71
83
|
optionsStyle() {
|
|
72
|
-
if (this.
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
if (!this.isFocused || !this.panelStyle) return { display: 'none' }
|
|
85
|
+
return this.panelStyle
|
|
86
|
+
},
|
|
87
|
+
useNativeMobile() {
|
|
88
|
+
if (this.$_config('disableNativeMobile')) return false
|
|
89
|
+
return this.isTouchDevice && !this.$_multiple && !this.ajaxOptions
|
|
90
|
+
},
|
|
91
|
+
nativeValue() {
|
|
92
|
+
return this.$_value && this.$_value.length ? this.$_value[0].value : ''
|
|
93
|
+
},
|
|
94
|
+
nativePlaceholder() {
|
|
95
|
+
return this.$_config('placeholder') || ''
|
|
96
|
+
},
|
|
97
|
+
nativeSelectStyle() {
|
|
98
|
+
return {
|
|
99
|
+
position: 'absolute',
|
|
100
|
+
top: 0, left: 0, right: 0, bottom: 0,
|
|
101
|
+
width: '100%', height: '100%',
|
|
102
|
+
opacity: 0,
|
|
103
|
+
appearance: 'none',
|
|
104
|
+
border: 0,
|
|
105
|
+
background: 'transparent',
|
|
106
|
+
zIndex: 2,
|
|
75
107
|
}
|
|
76
|
-
return {}
|
|
77
108
|
},
|
|
78
109
|
},
|
|
79
110
|
watch: {
|
|
80
111
|
isFocused(focused) {
|
|
81
112
|
if (focused) {
|
|
82
113
|
this.$nextTick(() => this.refresh())
|
|
114
|
+
this.attachListeners()
|
|
83
115
|
} else {
|
|
84
116
|
this.flipUp = false
|
|
85
|
-
this.
|
|
117
|
+
this.panelStyle = null
|
|
118
|
+
this.detachListeners()
|
|
86
119
|
}
|
|
87
120
|
},
|
|
88
121
|
filteredOptions() {
|
|
89
122
|
if (this.isFocused) this.$nextTick(() => this.refresh())
|
|
90
123
|
},
|
|
91
124
|
},
|
|
125
|
+
mounted() {
|
|
126
|
+
this.teleportOptions()
|
|
127
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
128
|
+
this.isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
beforeDestroy() {
|
|
132
|
+
this.cleanupOptions()
|
|
133
|
+
this.detachListeners()
|
|
134
|
+
},
|
|
92
135
|
methods: {
|
|
136
|
+
onNativeChange(e) {
|
|
137
|
+
const v = e.target.value
|
|
138
|
+
if (v === '') return
|
|
139
|
+
const opt = this.options.find(o => String(o.value) === String(v))
|
|
140
|
+
if (opt) this.select(opt)
|
|
141
|
+
},
|
|
142
|
+
nativeLabel(opt) {
|
|
143
|
+
const l = opt.label
|
|
144
|
+
if (typeof l === 'string') return l
|
|
145
|
+
if (l && typeof l === 'object') return l.label || l.text || l.search || ''
|
|
146
|
+
return String(opt.value ?? '')
|
|
147
|
+
},
|
|
148
|
+
teleportOptions() {
|
|
149
|
+
const opts = this.$refs.selectOptions
|
|
150
|
+
if (opts && opts.parentNode !== document.body) {
|
|
151
|
+
document.body.appendChild(opts)
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
cleanupOptions() {
|
|
155
|
+
const opts = this.$refs.selectOptions
|
|
156
|
+
if (opts && opts.parentNode === document.body) {
|
|
157
|
+
document.body.removeChild(opts)
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
anchorEl() {
|
|
161
|
+
return this.$refs.taggableInput?.$el || this.$refs.input?.parentElement
|
|
162
|
+
},
|
|
163
|
+
attachListeners() {
|
|
164
|
+
if (this._listenersAttached) return
|
|
165
|
+
this._onScroll = (e) => {
|
|
166
|
+
const opts = this.$refs.selectOptions
|
|
167
|
+
if (opts && e.target instanceof Node && opts.contains(e.target)) return
|
|
168
|
+
this.blur()
|
|
169
|
+
}
|
|
170
|
+
this._onResize = () => this.refresh()
|
|
171
|
+
window.addEventListener('scroll', this._onScroll, true)
|
|
172
|
+
window.addEventListener('resize', this._onResize, { passive: true })
|
|
173
|
+
this._listenersAttached = true
|
|
174
|
+
},
|
|
175
|
+
detachListeners() {
|
|
176
|
+
if (!this._listenersAttached) return
|
|
177
|
+
window.removeEventListener('scroll', this._onScroll, true)
|
|
178
|
+
window.removeEventListener('resize', this._onResize)
|
|
179
|
+
this._listenersAttached = false
|
|
180
|
+
},
|
|
93
181
|
refresh() {
|
|
94
182
|
const opts = this.$refs.selectOptions
|
|
95
|
-
const anchor =
|
|
183
|
+
const anchor = this.anchorEl()
|
|
96
184
|
if (!opts || !anchor) return
|
|
97
185
|
|
|
98
186
|
const rect = anchor.getBoundingClientRect()
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
187
|
+
const inOverlay = !!anchor.closest('.kompoModal, .kompoDrawer, .vl-sidebar-l')
|
|
188
|
+
const baseStyle = {
|
|
189
|
+
position: 'fixed',
|
|
190
|
+
left: rect.left + 'px',
|
|
191
|
+
width: rect.width + 'px',
|
|
192
|
+
zIndex: inOverlay ? 5001 : 1000,
|
|
193
|
+
top: rect.bottom + 'px',
|
|
194
|
+
bottom: 'auto',
|
|
195
|
+
}
|
|
196
|
+
this.panelStyle = baseStyle
|
|
103
197
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
198
|
+
this.$nextTick(() => {
|
|
199
|
+
const optsHeight = opts.offsetHeight
|
|
200
|
+
const spaceBelow = window.innerHeight - rect.bottom
|
|
201
|
+
const spaceAbove = rect.top
|
|
202
|
+
const shouldFlip = this.forceUp || (spaceBelow < optsHeight && spaceAbove > spaceBelow)
|
|
203
|
+
this.flipUp = shouldFlip
|
|
204
|
+
if (shouldFlip) {
|
|
205
|
+
this.panelStyle = {
|
|
206
|
+
...baseStyle,
|
|
207
|
+
top: 'auto',
|
|
208
|
+
bottom: (window.innerHeight - rect.top) + 'px',
|
|
209
|
+
borderRadius: '5px 5px 0 0',
|
|
210
|
+
}
|
|
113
211
|
}
|
|
114
|
-
}
|
|
115
|
-
this.overlayStyle = null
|
|
116
|
-
}
|
|
212
|
+
})
|
|
117
213
|
},
|
|
118
214
|
},
|
|
119
215
|
}
|