wave-ui 1.45.13 → 1.47.0
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/dist/wave-ui.cjs.js +1 -1
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.es.js +273 -285
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +1 -1
- package/src/wave-ui/components/w-alert.vue +0 -1
- package/src/wave-ui/components/w-confirm.vue +35 -10
- package/src/wave-ui/components/w-input.vue +3 -1
- package/src/wave-ui/components/w-menu.vue +74 -209
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-tag.vue +17 -6
- package/src/wave-ui/components/w-tooltip.vue +128 -262
- package/src/wave-ui/mixins/detachable.js +189 -0
- package/src/wave-ui/scss/_mixins.scss +18 -4
- package/src/wave-ui/scss/_variables.scss +1 -1
package/package.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
.w-confirm
|
|
3
3
|
w-menu(v-model="showPopup" v-bind="wMenuProps")
|
|
4
4
|
template(#activator="{ on }")
|
|
5
|
-
w-button.w-confirm__button(v-on="on" v-bind="buttonProps")
|
|
5
|
+
w-button.w-confirm__button(v-on="{ ...$listeners, ...on }" v-bind="buttonProps")
|
|
6
6
|
slot
|
|
7
7
|
w-flex(:column="!inline" align-center)
|
|
8
8
|
div
|
|
9
|
-
slot(name="question")
|
|
9
|
+
slot(name="question") {{ question }}
|
|
10
10
|
.w-flex.justify-end(:class="inline ? 'ml2' : 'mt2'")
|
|
11
11
|
w-button.mr2(
|
|
12
|
-
v-if="
|
|
13
|
-
v-bind="
|
|
12
|
+
v-if="cancel !== false"
|
|
13
|
+
v-bind="cancelButtonProps"
|
|
14
14
|
:bg-color="(cancelButton || {}).bgColor || 'error'"
|
|
15
15
|
@click="onCancel")
|
|
16
|
-
slot(name="cancel")
|
|
16
|
+
slot(name="cancel") {{ cancelButton.label }}
|
|
17
17
|
w-button(
|
|
18
|
-
v-bind="
|
|
18
|
+
v-bind="confirmButtonProps"
|
|
19
19
|
:bg-color="(confirmButton || {}).bgColor || 'success'"
|
|
20
20
|
@click="onConfirm")
|
|
21
|
-
slot(name="confirm")
|
|
21
|
+
slot(name="confirm") {{ confirmButton.label }}
|
|
22
22
|
</template>
|
|
23
23
|
|
|
24
24
|
<script>
|
|
@@ -30,11 +30,16 @@ export default {
|
|
|
30
30
|
color: { type: String },
|
|
31
31
|
icon: { type: String },
|
|
32
32
|
mainButton: { type: Object }, // Allow passing down an object of props to the w-button component.
|
|
33
|
+
question: { type: String, default: 'Are you sure?' },
|
|
33
34
|
|
|
34
35
|
// Cancel & confirm buttons props.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// Allow passing down an object of props to the w-button component.
|
|
37
|
+
// If a string is given, that will be the label of the button.
|
|
38
|
+
// If false, no cancel button.
|
|
39
|
+
cancel: { type: [Boolean, Object, String], default: undefined },
|
|
40
|
+
// Allow passing down an object of props to the w-button component.
|
|
41
|
+
// If a string is given, that will be the label of the button.
|
|
42
|
+
confirm: { type: [Object, String] },
|
|
38
43
|
|
|
39
44
|
// global menu props.
|
|
40
45
|
inline: { type: Boolean }, // The layout inside the menu.
|
|
@@ -63,6 +68,26 @@ export default {
|
|
|
63
68
|
}),
|
|
64
69
|
|
|
65
70
|
computed: {
|
|
71
|
+
cancelButton () {
|
|
72
|
+
let button = { label: typeof this.cancel === 'string' ? this.cancel : 'Cancel' }
|
|
73
|
+
if (typeof this.cancel === 'object') button = Object.assign({}, button, this.cancel)
|
|
74
|
+
return button
|
|
75
|
+
},
|
|
76
|
+
// Props to pass down to the w-button component.
|
|
77
|
+
cancelButtonProps () {
|
|
78
|
+
const { label, ...props } = this.cancelButton // Everything except label.
|
|
79
|
+
return props
|
|
80
|
+
},
|
|
81
|
+
confirmButton () {
|
|
82
|
+
let button = { label: typeof this.confirm === 'string' ? this.confirm : 'Confirm' }
|
|
83
|
+
if (typeof this.confirm === 'object') button = Object.assign({}, button, this.confirm)
|
|
84
|
+
return button
|
|
85
|
+
},
|
|
86
|
+
// Props to pass down to the w-button component.
|
|
87
|
+
confirmButtonProps () {
|
|
88
|
+
const { label, ...props } = this.confirmButton // Everything except label.
|
|
89
|
+
return props
|
|
90
|
+
},
|
|
66
91
|
wMenuProps () {
|
|
67
92
|
return {
|
|
68
93
|
top: this.top,
|
|
@@ -261,6 +261,8 @@ export default {
|
|
|
261
261
|
return file
|
|
262
262
|
}))
|
|
263
263
|
this.$emit('update:modelValue', this.inputFiles)
|
|
264
|
+
this.$emit('input', this.inputFiles)
|
|
265
|
+
this.$emit('change', this.inputFiles)
|
|
264
266
|
},
|
|
265
267
|
|
|
266
268
|
// For file input.
|
|
@@ -289,7 +291,7 @@ export default {
|
|
|
289
291
|
// On page load, check if the field is autofilled by the browser.
|
|
290
292
|
// 20211229. Only a problem on Chrome. Firefox ok, Safari always prompts before filling up.
|
|
291
293
|
setTimeout(() => {
|
|
292
|
-
if (this.$refs.input.matches(':-webkit-autofill')) this.isAutofilled = true
|
|
294
|
+
if (this.$refs.input && this.$refs.input.matches(':-webkit-autofill')) this.isAutofilled = true
|
|
293
295
|
}, 400) // Can't be less than 350: time for the browser to autofill.
|
|
294
296
|
},
|
|
295
297
|
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
slot(name="activator" :on="activatorEventHandlers")
|
|
4
4
|
transition(:name="transitionName" appear)
|
|
5
5
|
.w-menu(
|
|
6
|
-
v-if="custom &&
|
|
7
|
-
ref="
|
|
8
|
-
@click="hideOnMenuClick &&
|
|
6
|
+
v-if="custom && detachableVisible"
|
|
7
|
+
ref="detachable"
|
|
8
|
+
@click="hideOnMenuClick && close(true)"
|
|
9
9
|
@mouseenter="showOnHover && (hoveringMenu = true)"
|
|
10
|
-
@mouseleave="showOnHover && ((hoveringMenu = false),
|
|
10
|
+
@mouseleave="showOnHover && ((hoveringMenu = false), close())"
|
|
11
11
|
:class="classes"
|
|
12
12
|
:style="styles")
|
|
13
13
|
slot
|
|
14
14
|
w-card.w-menu(
|
|
15
|
-
v-else-if="
|
|
16
|
-
ref="
|
|
17
|
-
@click.native="hideOnMenuClick &&
|
|
15
|
+
v-else-if="detachableVisible"
|
|
16
|
+
ref="detachable"
|
|
17
|
+
@click.native="hideOnMenuClick && close(true)"
|
|
18
18
|
@mouseenter.native="showOnHover && (hoveringMenu = true)"
|
|
19
|
-
@mouseleave.native="showOnHover && ((hoveringMenu = false),
|
|
19
|
+
@mouseleave.native="showOnHover && ((hoveringMenu = false), close())"
|
|
20
20
|
:tile="tile"
|
|
21
21
|
:title-class="titleClasses"
|
|
22
22
|
:content-class="contentClasses"
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
w-overlay(
|
|
33
33
|
v-if="overlay"
|
|
34
34
|
ref="overlay"
|
|
35
|
-
:value="
|
|
35
|
+
:value="detachableVisible"
|
|
36
36
|
:persistent="persistent"
|
|
37
37
|
:class="overlayClasses"
|
|
38
38
|
v-bind="overlayProps"
|
|
39
39
|
:z-index="(zIndex || 200) - 1"
|
|
40
|
-
@input="
|
|
40
|
+
@input="detachableVisible = false")
|
|
41
41
|
</template>
|
|
42
42
|
|
|
43
43
|
<script>
|
|
@@ -51,12 +51,13 @@
|
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
53
|
import { objectifyClasses } from '../utils/index'
|
|
54
|
-
import
|
|
54
|
+
import DetachableMixin from '../mixins/detachable'
|
|
55
55
|
|
|
56
56
|
// const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
|
|
57
57
|
|
|
58
58
|
export default {
|
|
59
59
|
name: 'w-menu',
|
|
60
|
+
mixins: [DetachableMixin],
|
|
60
61
|
|
|
61
62
|
props: {
|
|
62
63
|
value: {}, // Show or hide.
|
|
@@ -75,7 +76,8 @@ export default {
|
|
|
75
76
|
contentClass: { type: [String, Object, Array] },
|
|
76
77
|
// Position.
|
|
77
78
|
arrow: { type: Boolean }, // The small triangle pointing toward the activator.
|
|
78
|
-
detachTo: { type: [String, Boolean, Object] },
|
|
79
|
+
detachTo: { type: [String, Boolean, Object], deprecated: true },
|
|
80
|
+
appendTo: { type: [String, Boolean, Object] },
|
|
79
81
|
fixed: { type: Boolean },
|
|
80
82
|
top: { type: Boolean },
|
|
81
83
|
bottom: { type: Boolean },
|
|
@@ -97,49 +99,31 @@ export default {
|
|
|
97
99
|
emits: ['input', 'update:modelValue', 'open', 'close'],
|
|
98
100
|
|
|
99
101
|
data: () => ({
|
|
100
|
-
|
|
102
|
+
detachableVisible: false,
|
|
101
103
|
hoveringActivator: false,
|
|
102
104
|
hoveringMenu: false,
|
|
103
105
|
// The menu computed top & left coordinates.
|
|
104
|
-
|
|
106
|
+
detachableCoords: {
|
|
105
107
|
top: 0,
|
|
106
108
|
left: 0
|
|
107
109
|
},
|
|
108
110
|
activatorEl: null,
|
|
109
111
|
activatorWidth: 0,
|
|
110
|
-
|
|
112
|
+
detachableEl: null,
|
|
111
113
|
timeoutId: null
|
|
112
114
|
}),
|
|
113
115
|
|
|
114
116
|
computed: {
|
|
117
|
+
/**
|
|
118
|
+
* Other computed in the detachable mixin:
|
|
119
|
+
* - `appendToTarget`
|
|
120
|
+
* - `detachableParentEl`
|
|
121
|
+
**/
|
|
122
|
+
|
|
115
123
|
transitionName () {
|
|
116
124
|
return this.transition || 'scale-fade'
|
|
117
125
|
},
|
|
118
126
|
|
|
119
|
-
// DOM element to attach menu to.
|
|
120
|
-
detachToTarget () {
|
|
121
|
-
let target = this.detachTo || '.w-app'
|
|
122
|
-
if (target === true) target = '.w-app'
|
|
123
|
-
else if (target && !['object', 'string'].includes(typeof target)) target = '.w-app'
|
|
124
|
-
else if (typeof target === 'object' && !target.nodeType) {
|
|
125
|
-
target = '.w-app'
|
|
126
|
-
consoleWarn('Invalid node provided in w-menu `detach-to`. Falling back to .w-app.', this)
|
|
127
|
-
}
|
|
128
|
-
if (typeof target === 'string') target = document.querySelector(target)
|
|
129
|
-
|
|
130
|
-
if (!target) {
|
|
131
|
-
consoleWarn(`Unable to locate ${this.detachTo ? `target ${this.detachTo}` : '.w-app'}`, this)
|
|
132
|
-
target = document.querySelector('.w-app')
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return target
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
// DOM element that will receive the menu.
|
|
139
|
-
menuParentEl () {
|
|
140
|
-
return this.detachToTarget
|
|
141
|
-
},
|
|
142
|
-
|
|
143
127
|
position () {
|
|
144
128
|
return (
|
|
145
129
|
(this.top && 'top') ||
|
|
@@ -150,21 +134,21 @@ export default {
|
|
|
150
134
|
)
|
|
151
135
|
},
|
|
152
136
|
|
|
153
|
-
menuMinWidth () {
|
|
154
|
-
if (this.minWidth === 'activator') return this.activatorWidth ? `${this.activatorWidth}px` : 0
|
|
155
|
-
else return isNaN(this.minWidth) ? this.minWidth : (this.minWidth ? `${this.minWidth}px` : 0)
|
|
156
|
-
},
|
|
157
|
-
|
|
158
137
|
alignment () {
|
|
159
138
|
return (
|
|
160
|
-
(
|
|
161
|
-
(
|
|
162
|
-
(
|
|
163
|
-
(
|
|
139
|
+
(['top', 'bottom'].includes(this.position) && this.alignLeft && 'left') ||
|
|
140
|
+
(['top', 'bottom'].includes(this.position) && this.alignRight && 'right') ||
|
|
141
|
+
(['left', 'right'].includes(this.position) && this.alignTop && 'top') ||
|
|
142
|
+
(['left', 'right'].includes(this.position) && this.alignBottom && 'bottom') ||
|
|
164
143
|
''
|
|
165
144
|
)
|
|
166
145
|
},
|
|
167
146
|
|
|
147
|
+
menuMinWidth () {
|
|
148
|
+
if (this.minWidth === 'activator') return this.activatorWidth ? `${this.activatorWidth}px` : 0
|
|
149
|
+
else return isNaN(this.minWidth) ? this.minWidth : (this.minWidth ? `${this.minWidth}px` : 0)
|
|
150
|
+
},
|
|
151
|
+
|
|
168
152
|
menuClasses () {
|
|
169
153
|
return objectifyClasses(this.menuClass)
|
|
170
154
|
},
|
|
@@ -201,8 +185,8 @@ export default {
|
|
|
201
185
|
styles () {
|
|
202
186
|
return {
|
|
203
187
|
zIndex: this.zIndex || this.zIndex === 0 || (this.overlay && !this.zIndex && 200) || null,
|
|
204
|
-
top: (this.
|
|
205
|
-
left: (this.
|
|
188
|
+
top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
|
|
189
|
+
left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
|
|
206
190
|
minWidth: (this.minWidth && this.menuMinWidth) || null,
|
|
207
191
|
'--w-menu-bg-color': this.arrow && this.$waveui.colors[this.bgColor || 'white']
|
|
208
192
|
}
|
|
@@ -217,18 +201,20 @@ export default {
|
|
|
217
201
|
blur: this.toggleMenu,
|
|
218
202
|
mouseenter: e => {
|
|
219
203
|
this.hoveringActivator = true
|
|
220
|
-
this.
|
|
204
|
+
this.open(e)
|
|
221
205
|
},
|
|
222
206
|
mouseleave: e => {
|
|
223
207
|
this.hoveringActivator = false
|
|
224
208
|
// Wait 10ms, the time to get the hoveringMenu updated on mouseenter on the menu.
|
|
225
209
|
setTimeout(() => {
|
|
226
|
-
if (!this.hoveringMenu) this.
|
|
210
|
+
if (!this.hoveringMenu) this.close()
|
|
227
211
|
}, 10)
|
|
228
212
|
}
|
|
229
213
|
}
|
|
230
|
-
|
|
231
|
-
if ('ontouchstart' in window)
|
|
214
|
+
// Check the window exists: SSR-proof.
|
|
215
|
+
if (typeof window !== 'undefined' && 'ontouchstart' in window) {
|
|
216
|
+
handlers.click = this.toggleMenu
|
|
217
|
+
}
|
|
232
218
|
}
|
|
233
219
|
else handlers = { click: this.toggleMenu }
|
|
234
220
|
return handlers
|
|
@@ -236,8 +222,19 @@ export default {
|
|
|
236
222
|
},
|
|
237
223
|
|
|
238
224
|
methods: {
|
|
225
|
+
/**
|
|
226
|
+
* Other methods in the `detachable` mixin:
|
|
227
|
+
* - `getActivatorCoordinates`
|
|
228
|
+
* - `computeDetachableCoords`
|
|
229
|
+
* - `onResize`
|
|
230
|
+
* - `onOutsideMousedown`
|
|
231
|
+
* - `insertInDOM`
|
|
232
|
+
* - `removeFromDOM`
|
|
233
|
+
**/
|
|
234
|
+
|
|
235
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
239
236
|
toggleMenu (e) {
|
|
240
|
-
let shouldShowMenu = this.
|
|
237
|
+
let shouldShowMenu = this.detachableVisible
|
|
241
238
|
if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
|
|
242
239
|
shouldShowMenu = !shouldShowMenu
|
|
243
240
|
}
|
|
@@ -254,24 +251,25 @@ export default {
|
|
|
254
251
|
this.timeoutId = clearTimeout(this.timeoutId)
|
|
255
252
|
|
|
256
253
|
if (shouldShowMenu) {
|
|
257
|
-
this.$emit('update:modelValue', (this.
|
|
254
|
+
this.$emit('update:modelValue', (this.detachableVisible = true))
|
|
258
255
|
this.$emit('input', true)
|
|
259
256
|
this.$emit('open')
|
|
260
257
|
|
|
261
|
-
this.
|
|
258
|
+
this.open(e)
|
|
262
259
|
}
|
|
263
|
-
else this.
|
|
260
|
+
else this.close()
|
|
264
261
|
},
|
|
265
262
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
264
|
+
async open (e) {
|
|
265
|
+
this.detachableVisible = true
|
|
266
|
+
await this.insertInDOM()
|
|
269
267
|
|
|
270
268
|
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
271
269
|
|
|
272
|
-
if (!this.noPosition) this.
|
|
270
|
+
if (!this.noPosition) this.computeDetachableCoords(e)
|
|
273
271
|
|
|
274
|
-
// In `
|
|
272
|
+
// In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
|
|
275
273
|
// if we don't postpone the Menu apparition it will start transition from a visible menu and
|
|
276
274
|
// thus will not transition.
|
|
277
275
|
this.timeoutId = setTimeout(() => {
|
|
@@ -289,172 +287,35 @@ export default {
|
|
|
289
287
|
* - click of activator
|
|
290
288
|
* - hover outside if showOnHover
|
|
291
289
|
* - click inside menu if hideOnMenuClick.
|
|
290
|
+
* / ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
292
291
|
*
|
|
293
292
|
* @param {Boolean} force when showOnHover is set to true, hovering menu should keep it open.
|
|
294
293
|
* But if hideOnMenuClick is also set to true, this should force close
|
|
295
294
|
* even while hovering the menu.
|
|
296
295
|
*/
|
|
297
|
-
async
|
|
296
|
+
async close (force = false) {
|
|
298
297
|
// Might be already closed.
|
|
299
298
|
// E.g. showOnHover & hideOnMenuClick: on click, force hide then mouseleave is also firing.
|
|
300
|
-
if (!this.
|
|
299
|
+
if (!this.detachableVisible) return
|
|
301
300
|
|
|
302
301
|
if (this.showOnHover && !force) {
|
|
303
302
|
await new Promise(resolve => setTimeout(resolve, 10))
|
|
304
303
|
if (this.showOnHover && (this.hoveringMenu || this.hoveringActivator)) return
|
|
305
304
|
}
|
|
306
305
|
|
|
307
|
-
this.$emit('update:modelValue', (this.
|
|
306
|
+
this.$emit('update:modelValue', (this.detachableVisible = false))
|
|
308
307
|
this.$emit('input', false)
|
|
309
308
|
this.$emit('close')
|
|
310
309
|
// Remove the mousedown listener if the menu got closed without a mousedown outside of the menu.
|
|
311
310
|
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
312
311
|
window.removeEventListener('resize', this.onResize)
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
onOutsideMousedown (e) {
|
|
316
|
-
if (!this.menuEl.contains(e.target) && !this.activatorEl.contains(e.target)) {
|
|
317
|
-
this.$emit('update:modelValue', (this.menuVisible = false))
|
|
318
|
-
this.$emit('input', false)
|
|
319
|
-
this.$emit('close')
|
|
320
|
-
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
321
|
-
window.removeEventListener('resize', this.onResize)
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
|
|
325
|
-
onResize () {
|
|
326
|
-
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
327
|
-
this.computeMenuPosition()
|
|
328
|
-
},
|
|
329
|
-
|
|
330
|
-
getCoordinates (e) {
|
|
331
|
-
// Get the activator coordinates relative to window.
|
|
332
|
-
const { top, left, width, height } = (e ? e.target : this.activatorEl).getBoundingClientRect()
|
|
333
|
-
let coords = { top, left, width, height }
|
|
334
|
-
|
|
335
|
-
// If absolute position, adjust top & left.
|
|
336
|
-
if (!this.fixed) {
|
|
337
|
-
const { top: targetTop, left: targetLeft } = this.menuParentEl.getBoundingClientRect()
|
|
338
|
-
const computedStyles = window.getComputedStyle(this.menuParentEl, null)
|
|
339
|
-
coords = {
|
|
340
|
-
...coords,
|
|
341
|
-
top: top - targetTop + this.menuParentEl.scrollTop - parseInt(computedStyles.getPropertyValue('border-top-width')),
|
|
342
|
-
left: left - targetLeft + this.menuParentEl.scrollLeft - parseInt(computedStyles.getPropertyValue('border-left-width'))
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return coords
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
computeMenuPosition (e) {
|
|
350
|
-
// Get the activator coordinates.
|
|
351
|
-
let { top, left, width, height } = this.getCoordinates(e)
|
|
352
|
-
|
|
353
|
-
// 1. First display the menu but hide it (So we can get its dimension).
|
|
354
|
-
// --------------------------------------------------
|
|
355
|
-
this.menuEl.style.visibility = 'hidden'
|
|
356
|
-
this.menuEl.style.display = 'flex'
|
|
357
|
-
const computedStyles = window.getComputedStyle(this.menuEl, null)
|
|
358
|
-
|
|
359
|
-
// 2. Position the menu top, left, right, bottom and apply chosen alignment.
|
|
360
|
-
// --------------------------------------------------
|
|
361
|
-
// Subtract half or full activator width or height and menu width or height according to the
|
|
362
|
-
// menu alignment.
|
|
363
|
-
// Note: the menu position relies on transform translate, the custom animation may override the
|
|
364
|
-
// css transform property so do without it i.e. no translateX(-50%), and recalculate top & left
|
|
365
|
-
// manually.
|
|
366
|
-
switch (this.position) {
|
|
367
|
-
case 'top': {
|
|
368
|
-
top -= this.menuEl.offsetHeight
|
|
369
|
-
if (this.alignRight) {
|
|
370
|
-
// left: 100% of activator.
|
|
371
|
-
left += width - this.menuEl.offsetWidth +
|
|
372
|
-
parseInt(computedStyles.getPropertyValue('border-right-width'))
|
|
373
|
-
}
|
|
374
|
-
else if (!this.alignLeft) left += (width - this.menuEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
|
|
375
|
-
break
|
|
376
|
-
}
|
|
377
|
-
case 'bottom': {
|
|
378
|
-
top += height
|
|
379
|
-
if (this.alignRight) {
|
|
380
|
-
// left: 100% of activator.
|
|
381
|
-
left += width - this.menuEl.offsetWidth +
|
|
382
|
-
parseInt(computedStyles.getPropertyValue('border-right-width'))
|
|
383
|
-
}
|
|
384
|
-
else if (!this.alignLeft) left += (width - this.menuEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
|
|
385
|
-
break
|
|
386
|
-
}
|
|
387
|
-
case 'left': {
|
|
388
|
-
left -= this.menuEl.offsetWidth
|
|
389
|
-
if (this.alignBottom) top += height - this.menuEl.offsetHeight
|
|
390
|
-
else if (!this.alignTop) top += (height - this.menuEl.offsetHeight) / 2 // top: 50% of activator - half menu height.
|
|
391
|
-
break
|
|
392
|
-
}
|
|
393
|
-
case 'right': {
|
|
394
|
-
left += width
|
|
395
|
-
if (this.alignBottom) {
|
|
396
|
-
top += height - this.menuEl.offsetHeight +
|
|
397
|
-
parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
398
|
-
}
|
|
399
|
-
else if (!this.alignTop) {
|
|
400
|
-
top += (height - this.menuEl.offsetHeight) / 2 + // top: 50% of activator - half menu height.
|
|
401
|
-
parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
402
|
-
}
|
|
403
|
-
break
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// 3. Keep fully in viewport.
|
|
408
|
-
// @todo: do this.
|
|
409
|
-
// --------------------------------------------------
|
|
410
|
-
// if (this.position === 'top' && ((top - this.menuEl.offsetHeight) < 0)) {
|
|
411
|
-
// const margin = - parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
412
|
-
// top -= top - this.menuEl.offsetHeight - margin - marginFromWindowSide
|
|
413
|
-
// }
|
|
414
|
-
// else if (this.position === 'left' && left - this.menuEl.offsetWidth < 0) {
|
|
415
|
-
// const margin = - parseInt(computedStyles.getPropertyValue('margin-left'))
|
|
416
|
-
// left -= left - this.menuEl.offsetWidth - margin - marginFromWindowSide
|
|
417
|
-
// }
|
|
418
|
-
// else if (this.position === 'right' && left + width + this.menuEl.offsetWidth > window.innerWidth) {
|
|
419
|
-
// const margin = parseInt(computedStyles.getPropertyValue('margin-left'))
|
|
420
|
-
// left -= left + width + this.menuEl.offsetWidth - window.innerWidth + margin + marginFromWindowSide
|
|
421
|
-
// }
|
|
422
|
-
// else if (this.position === 'bottom' && top + height + this.menuEl.offsetHeight > window.innerHeight) {
|
|
423
|
-
// const margin = parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
424
|
-
// top -= top + height + this.menuEl.offsetHeight - window.innerHeight + margin + marginFromWindowSide
|
|
425
|
-
// }
|
|
426
|
-
|
|
427
|
-
// 4. Hide the menu again so the transition happens correctly.
|
|
428
|
-
// --------------------------------------------------
|
|
429
|
-
this.menuEl.style.visibility = null
|
|
430
|
-
|
|
431
|
-
// The menu coordinates are also recalculated while resizing window with open menu: keep the menu visible.
|
|
432
|
-
if (!this.menuVisible) this.menuEl.style.display = 'none'
|
|
433
|
-
|
|
434
|
-
this.menuCoordinates = { top, left }
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
insertMenu () {
|
|
438
|
-
return new Promise(resolve => {
|
|
439
|
-
this.$nextTick(() => {
|
|
440
|
-
this.menuEl = this.$refs.menu?.$el || this.$refs.menu
|
|
441
|
-
|
|
442
|
-
// Move the menu elsewhere in the DOM.
|
|
443
|
-
// wrapper.parentNode.insertBefore(this.menuEl, wrapper)
|
|
444
|
-
this.detachToTarget.appendChild(this.menuEl)
|
|
445
|
-
resolve()
|
|
446
|
-
})
|
|
447
|
-
})
|
|
448
|
-
},
|
|
449
|
-
|
|
450
|
-
removeMenu () {
|
|
451
|
-
if (this.menuEl && this.menuEl.parentNode) this.menuEl.remove()
|
|
452
312
|
}
|
|
453
313
|
},
|
|
454
314
|
|
|
455
315
|
mounted () {
|
|
456
316
|
const wrapper = this.$el
|
|
457
317
|
this.activatorEl = wrapper.firstElementChild
|
|
318
|
+
|
|
458
319
|
// Unwrap the activator element.
|
|
459
320
|
wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
|
|
460
321
|
|
|
@@ -468,18 +329,22 @@ export default {
|
|
|
468
329
|
},
|
|
469
330
|
|
|
470
331
|
beforeDestroy () {
|
|
471
|
-
this.
|
|
332
|
+
this.removeFromDOM()
|
|
472
333
|
if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
|
|
473
334
|
if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
|
|
474
335
|
},
|
|
475
336
|
|
|
476
337
|
watch: {
|
|
477
338
|
value (bool) {
|
|
478
|
-
if (!!bool !== this.
|
|
339
|
+
if (!!bool !== this.detachableVisible) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
479
340
|
},
|
|
480
341
|
detachTo () {
|
|
481
|
-
this.
|
|
482
|
-
this.
|
|
342
|
+
this.removeFromDOM()
|
|
343
|
+
this.insertInDOM()
|
|
344
|
+
},
|
|
345
|
+
appendTo () {
|
|
346
|
+
this.removeFromDOM()
|
|
347
|
+
this.insertInDOM()
|
|
483
348
|
}
|
|
484
349
|
}
|
|
485
350
|
}
|
|
@@ -21,7 +21,7 @@ component(
|
|
|
21
21
|
v-model="showMenu"
|
|
22
22
|
:menu-class="`w-select__menu ${menuClass || ''}`"
|
|
23
23
|
transition="slide-fade-down"
|
|
24
|
-
:
|
|
24
|
+
:append-to="(menuProps || {}).appendTo !== undefined ? (menuProps || {}).appendTo : '.w-app'"
|
|
25
25
|
align-left
|
|
26
26
|
custom
|
|
27
27
|
min-width="activator"
|
|
@@ -89,7 +89,6 @@ export default {
|
|
|
89
89
|
background-color: rgba(255, 255, 255, 0.85);
|
|
90
90
|
padding-left: 2 * $base-increment;
|
|
91
91
|
padding-right: 2 * $base-increment;
|
|
92
|
-
font-size: round(0.85 * $base-font-size);
|
|
93
92
|
cursor: default;
|
|
94
93
|
user-select: none;
|
|
95
94
|
|
|
@@ -100,23 +99,35 @@ export default {
|
|
|
100
99
|
&--tile {border-radius: initial;}
|
|
101
100
|
&--shadow {box-shadow: $box-shadow;}
|
|
102
101
|
|
|
103
|
-
&.size--xs {
|
|
102
|
+
&.size--xs {
|
|
103
|
+
$font-size: round(0.7 * $base-font-size);
|
|
104
|
+
font-size: $font-size;
|
|
105
|
+
line-height: $font-size + 2px;
|
|
106
|
+
}
|
|
104
107
|
&.size--sm {
|
|
105
|
-
font-size: round(0.82 * $base-font-size);
|
|
108
|
+
$font-size: round(0.82 * $base-font-size);
|
|
109
|
+
font-size: $font-size;
|
|
110
|
+
line-height: $font-size + 2px;
|
|
106
111
|
padding: round(0.25 * $base-increment) $base-increment;
|
|
107
112
|
}
|
|
108
113
|
&.size--md {
|
|
109
|
-
font-size: round(0.
|
|
114
|
+
$font-size: round(0.85 * $base-font-size);
|
|
115
|
+
font-size: $font-size;
|
|
116
|
+
line-height: $font-size + 4px;
|
|
110
117
|
padding-top: round(0.25 * $base-increment);
|
|
111
118
|
padding-bottom: round(0.25 * $base-increment);
|
|
112
119
|
}
|
|
113
120
|
&.size--lg {
|
|
114
|
-
font-size: round(1.1 * $base-font-size);
|
|
121
|
+
$font-size: round(1.1 * $base-font-size);
|
|
122
|
+
font-size: $font-size;
|
|
123
|
+
line-height: $font-size + 4px;
|
|
115
124
|
padding-top: round(0.5 * $base-increment);
|
|
116
125
|
padding-bottom: round(0.5 * $base-increment);
|
|
117
126
|
}
|
|
118
127
|
&.size--xl {
|
|
119
|
-
font-size: round(1.3 * $base-font-size);
|
|
128
|
+
$font-size: round(1.3 * $base-font-size);
|
|
129
|
+
font-size: $font-size;
|
|
130
|
+
line-height: $font-size + 4px;
|
|
120
131
|
padding-top: round(1 * $base-increment);
|
|
121
132
|
padding-bottom: round(1 * $base-increment);
|
|
122
133
|
}
|