js-kompo-utils 1.0.18 → 1.0.20
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 +16 -3
- package/components/VlSelect.vue +111 -0
- package/package.json +1 -1
|
@@ -49,6 +49,8 @@ export default {
|
|
|
49
49
|
menuTop: -9999,
|
|
50
50
|
menuLeft: -9999,
|
|
51
51
|
menuRight: null,
|
|
52
|
+
inModal: false,
|
|
53
|
+
autoFlipUp: false,
|
|
52
54
|
}
|
|
53
55
|
},
|
|
54
56
|
watch:{
|
|
@@ -80,15 +82,18 @@ export default {
|
|
|
80
82
|
isRightAligned() {
|
|
81
83
|
return this.dropdownPosition.includes('Right')
|
|
82
84
|
},
|
|
83
|
-
|
|
85
|
+
forcedUpward() {
|
|
84
86
|
return this.dropdownPosition.includes('Up')
|
|
85
87
|
},
|
|
88
|
+
isUpward() {
|
|
89
|
+
return this.forcedUpward || this.autoFlipUp
|
|
90
|
+
},
|
|
86
91
|
menuPositionStyle() {
|
|
87
92
|
const style = {
|
|
88
93
|
position: 'fixed',
|
|
89
94
|
top: this.menuTop + 'px',
|
|
90
95
|
bottom: 'auto',
|
|
91
|
-
zIndex: 900,
|
|
96
|
+
zIndex: this.inModal ? 5001 : 900,
|
|
92
97
|
transition: 'transform 0.2s ease-in-out, opacity 0.2s ease-in-out',
|
|
93
98
|
transformOrigin: (this.isUpward ? 'bottom' : 'top') + ' ' + (this.isRightAligned ? 'right' : 'left'),
|
|
94
99
|
}
|
|
@@ -154,6 +159,8 @@ export default {
|
|
|
154
159
|
const root = this.$refs.dropdownRoot
|
|
155
160
|
if (!menu) return
|
|
156
161
|
|
|
162
|
+
this.inModal = !!(root && root.closest('.kompoModal'))
|
|
163
|
+
|
|
157
164
|
// Transfer scroll styles from root before teleporting
|
|
158
165
|
if (root && root.classList.contains('dropdown-with-scroll')) {
|
|
159
166
|
const maxHeight = getComputedStyle(root).getPropertyValue('--dropdown-max-height').trim()
|
|
@@ -203,9 +210,15 @@ export default {
|
|
|
203
210
|
if (!root) return
|
|
204
211
|
|
|
205
212
|
const rect = root.getBoundingClientRect()
|
|
213
|
+
const menuHeight = menu ? menu.offsetHeight : 0
|
|
214
|
+
|
|
215
|
+
if (!this.forcedUpward) {
|
|
216
|
+
const spaceBelow = window.innerHeight - rect.bottom
|
|
217
|
+
const spaceAbove = rect.top
|
|
218
|
+
this.autoFlipUp = spaceBelow < menuHeight && spaceAbove > spaceBelow
|
|
219
|
+
}
|
|
206
220
|
|
|
207
221
|
if (this.isUpward) {
|
|
208
|
-
const menuHeight = menu ? menu.offsetHeight : 0
|
|
209
222
|
this.menuTop = rect.top - menuHeight
|
|
210
223
|
} else {
|
|
211
224
|
this.menuTop = rect.bottom
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<vl-form-field v-bind="$_wrapperAttributes">
|
|
3
|
+
<vlTaggableInput
|
|
4
|
+
v-click-out="blur"
|
|
5
|
+
v-bind="$_taggableInputAttributes"
|
|
6
|
+
v-on="$_taggableInputEvents">
|
|
7
|
+
<template v-slot:prepend v-if="$_icon">
|
|
8
|
+
<div v-bind="prependIcon" />
|
|
9
|
+
</template>
|
|
10
|
+
<input
|
|
11
|
+
class="vlFormControl"
|
|
12
|
+
v-bind="$_attributes"
|
|
13
|
+
v-on="$_events"
|
|
14
|
+
v-model="inputValue"
|
|
15
|
+
@keydown.enter.prevent="() => {}"
|
|
16
|
+
ref="input"
|
|
17
|
+
type="search"
|
|
18
|
+
autocomplete="off"
|
|
19
|
+
/>
|
|
20
|
+
<template v-slot:append v-if="appendIcon && !$_readOnly">
|
|
21
|
+
<div v-html="appendIcon"/>
|
|
22
|
+
</template>
|
|
23
|
+
</vlTaggableInput>
|
|
24
|
+
<div class="vlOptions"
|
|
25
|
+
:key="optionsKey"
|
|
26
|
+
ref="selectOptions"
|
|
27
|
+
:style="optionsStyle"
|
|
28
|
+
:class="{ 'vlOptionsFlipUp': flipUp }">
|
|
29
|
+
<template v-if="filteredOptions.length">
|
|
30
|
+
<div v-for="(option,key) in filteredOptions" :key="option.value" :ref="key"
|
|
31
|
+
class="vlOption"
|
|
32
|
+
:class="{
|
|
33
|
+
'vlSelected' : isSelected(option),
|
|
34
|
+
'vlHoveredOption' : key == hoveredOption
|
|
35
|
+
}"
|
|
36
|
+
@click.stop="select(option)"
|
|
37
|
+
@mouseover="setHoveredOption(key)">
|
|
38
|
+
<vlCustomLabel :vkompo="option.label" :kompoid="kompoid" />
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
<template v-else>
|
|
42
|
+
<div class="vlOption" v-html="optionsMessage" />
|
|
43
|
+
</template>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<template v-slot:after>
|
|
47
|
+
<slot name="after"/>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
</vl-form-field>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script>
|
|
54
|
+
import Select from 'vue-kompo/js/form/form-fields/Select.vue'
|
|
55
|
+
|
|
56
|
+
export default {
|
|
57
|
+
extends: Select,
|
|
58
|
+
data() {
|
|
59
|
+
return {
|
|
60
|
+
flipUp: false,
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
computed: {
|
|
64
|
+
isFocused() {
|
|
65
|
+
return this.$_state('focusedField')
|
|
66
|
+
},
|
|
67
|
+
forceUp() {
|
|
68
|
+
return !!this.$_config('openUpward')
|
|
69
|
+
},
|
|
70
|
+
optionsStyle() {
|
|
71
|
+
if (!this.flipUp) return {}
|
|
72
|
+
return {
|
|
73
|
+
top: 'auto',
|
|
74
|
+
bottom: '100%',
|
|
75
|
+
borderRadius: '5px 5px 0 0',
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
watch: {
|
|
80
|
+
isFocused(focused) {
|
|
81
|
+
if (focused) {
|
|
82
|
+
this.$nextTick(() => this.computeFlip())
|
|
83
|
+
} else {
|
|
84
|
+
this.flipUp = false
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
filteredOptions() {
|
|
88
|
+
if (this.isFocused) {
|
|
89
|
+
this.$nextTick(() => this.computeFlip())
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
methods: {
|
|
94
|
+
computeFlip() {
|
|
95
|
+
if (this.forceUp) {
|
|
96
|
+
this.flipUp = true
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const opts = this.$refs.selectOptions
|
|
100
|
+
if (!opts) return
|
|
101
|
+
const anchor = opts.parentElement
|
|
102
|
+
if (!anchor) return
|
|
103
|
+
const anchorRect = anchor.getBoundingClientRect()
|
|
104
|
+
const optsHeight = opts.offsetHeight
|
|
105
|
+
const spaceBelow = window.innerHeight - anchorRect.bottom
|
|
106
|
+
const spaceAbove = anchorRect.top
|
|
107
|
+
this.flipUp = spaceBelow < optsHeight && spaceAbove > spaceBelow
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
</script>
|