wave-ui 2.26.0 → 2.29.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 +525 -297
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +16 -16
- package/src/wave-ui/components/index.js +2 -1
- package/src/wave-ui/components/w-accordion.vue +6 -4
- package/src/wave-ui/components/w-app.vue +42 -2
- package/src/wave-ui/components/w-card.vue +19 -5
- package/src/wave-ui/components/w-confirm.vue +103 -0
- package/src/wave-ui/components/w-flex.vue +2 -0
- package/src/wave-ui/components/w-icon.vue +1 -3
- package/src/wave-ui/components/w-input.vue +19 -3
- package/src/wave-ui/components/w-menu.vue +110 -57
- package/src/wave-ui/components/w-notification-manager.vue +1 -1
- package/src/wave-ui/components/w-table.vue +58 -29
- package/src/wave-ui/components/w-tabs/index.vue +2 -1
- package/src/wave-ui/components/w-textarea.vue +1 -1
- package/src/wave-ui/components/w-tooltip.vue +42 -110
- package/src/wave-ui/scss/_base.scss +0 -21
- package/src/wave-ui/scss/_mixins.scss +100 -0
- package/src/wave-ui/scss/_variables.scss +4 -9
- package/src/wave-ui/utils/index.js +11 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-menu-wrap
|
|
2
|
+
.w-menu-wrap
|
|
3
3
|
slot(name="activator" :on="activatorEventHandlers")
|
|
4
|
-
transition(:name="transitionName")
|
|
4
|
+
transition(:name="transitionName" appear)
|
|
5
5
|
.w-menu(
|
|
6
|
-
v-if="custom"
|
|
6
|
+
v-if="custom && menuVisible"
|
|
7
7
|
ref="menu"
|
|
8
|
-
v-show="showMenu"
|
|
9
8
|
@click="hideOnMenuClick && closeMenu(true)"
|
|
10
9
|
@mouseenter="showOnHover && (hoveringMenu = true)"
|
|
11
10
|
@mouseleave="showOnHover && ((hoveringMenu = false), closeMenu())"
|
|
@@ -13,15 +12,14 @@
|
|
|
13
12
|
:style="styles")
|
|
14
13
|
slot
|
|
15
14
|
w-card.w-menu(
|
|
16
|
-
v-else
|
|
15
|
+
v-else-if="menuVisible"
|
|
17
16
|
ref="menu"
|
|
18
|
-
v-show="showMenu"
|
|
19
17
|
@click.native="hideOnMenuClick && closeMenu(true)"
|
|
20
18
|
@mouseenter.native="showOnHover && (hoveringMenu = true)"
|
|
21
19
|
@mouseleave.native="showOnHover && ((hoveringMenu = false), closeMenu())"
|
|
22
20
|
:tile="tile"
|
|
23
|
-
:title-class="
|
|
24
|
-
:content-class="
|
|
21
|
+
:title-class="titleClasses"
|
|
22
|
+
:content-class="contentClasses"
|
|
25
23
|
:shadow="shadow"
|
|
26
24
|
:no-border="noBorder"
|
|
27
25
|
:class="classes"
|
|
@@ -34,12 +32,12 @@
|
|
|
34
32
|
w-overlay(
|
|
35
33
|
v-if="overlay"
|
|
36
34
|
ref="overlay"
|
|
37
|
-
:model-value="
|
|
35
|
+
:model-value="menuVisible"
|
|
38
36
|
:persistent="persistent"
|
|
39
|
-
:class="
|
|
37
|
+
:class="overlayClasses"
|
|
40
38
|
v-bind="overlayProps"
|
|
41
39
|
:z-index="(zIndex || 200) - 1"
|
|
42
|
-
@update:model-value="
|
|
40
|
+
@update:model-value="menuVisible = false")
|
|
43
41
|
</template>
|
|
44
42
|
|
|
45
43
|
<script>
|
|
@@ -52,6 +50,7 @@
|
|
|
52
50
|
* and move the menu elsewhere in the DOM.
|
|
53
51
|
*/
|
|
54
52
|
|
|
53
|
+
import { objectifyClasses } from '../utils/index'
|
|
55
54
|
import { consoleWarn } from '../utils/console'
|
|
56
55
|
|
|
57
56
|
// const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
|
|
@@ -71,10 +70,11 @@ export default {
|
|
|
71
70
|
round: { type: Boolean },
|
|
72
71
|
noBorder: { type: Boolean },
|
|
73
72
|
transition: { type: String },
|
|
74
|
-
menuClass: { type: String },
|
|
75
|
-
titleClass: { type: String },
|
|
76
|
-
contentClass: { type: String },
|
|
73
|
+
menuClass: { type: [String, Object, Array] },
|
|
74
|
+
titleClass: { type: [String, Object, Array] },
|
|
75
|
+
contentClass: { type: [String, Object, Array] },
|
|
77
76
|
// Position.
|
|
77
|
+
arrow: { type: Boolean }, // The small triangle pointing toward the activator.
|
|
78
78
|
detachTo: { type: [String, Boolean, Object] },
|
|
79
79
|
fixed: { type: Boolean },
|
|
80
80
|
top: { type: Boolean },
|
|
@@ -88,7 +88,7 @@ export default {
|
|
|
88
88
|
zIndex: { type: [Number, String, Boolean] },
|
|
89
89
|
minWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
|
|
90
90
|
overlay: { type: Boolean },
|
|
91
|
-
overlayClass: { type: String },
|
|
91
|
+
overlayClass: { type: [String, Object, Array] },
|
|
92
92
|
overlayProps: { type: Object }, // Allow passing down an object of props to the w-overlay component.
|
|
93
93
|
persistent: { type: Boolean },
|
|
94
94
|
noPosition: { type: Boolean }
|
|
@@ -97,7 +97,7 @@ export default {
|
|
|
97
97
|
emits: ['input', 'update:modelValue', 'open', 'close'],
|
|
98
98
|
|
|
99
99
|
data: () => ({
|
|
100
|
-
|
|
100
|
+
menuVisible: false,
|
|
101
101
|
hoveringActivator: false,
|
|
102
102
|
hoveringMenu: false,
|
|
103
103
|
// The menu computed top & left coordinates.
|
|
@@ -117,25 +117,29 @@ export default {
|
|
|
117
117
|
},
|
|
118
118
|
|
|
119
119
|
// DOM element to attach menu to.
|
|
120
|
+
// ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
120
121
|
detachToTarget () {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
const defaultTarget = '.w-app'
|
|
123
|
+
|
|
124
|
+
let target = this.detachTo || defaultTarget
|
|
125
|
+
if (target === true) target = defaultTarget
|
|
126
|
+
else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
|
|
124
127
|
else if (typeof target === 'object' && !target.nodeType) {
|
|
125
|
-
target =
|
|
128
|
+
target = defaultTarget
|
|
126
129
|
consoleWarn('Invalid node provided in w-menu `detach-to`. Falling back to .w-app.', this)
|
|
127
130
|
}
|
|
128
131
|
if (typeof target === 'string') target = document.querySelector(target)
|
|
129
132
|
|
|
130
133
|
if (!target) {
|
|
131
|
-
consoleWarn(`Unable to locate ${this.detachTo ? `target ${this.detachTo}` :
|
|
132
|
-
target = document.querySelector(
|
|
134
|
+
consoleWarn(`Unable to locate ${this.detachTo ? `target ${this.detachTo}` : defaultTarget}`, this)
|
|
135
|
+
target = document.querySelector(defaultTarget)
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
return target
|
|
136
139
|
},
|
|
137
140
|
|
|
138
141
|
// DOM element that will receive the menu.
|
|
142
|
+
// ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
139
143
|
menuParentEl () {
|
|
140
144
|
return this.detachToTarget
|
|
141
145
|
},
|
|
@@ -165,27 +169,46 @@ export default {
|
|
|
165
169
|
)
|
|
166
170
|
},
|
|
167
171
|
|
|
172
|
+
menuClasses () {
|
|
173
|
+
return objectifyClasses(this.menuClass)
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
titleClasses () {
|
|
177
|
+
return objectifyClasses(this.titleClass)
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
contentClasses () {
|
|
181
|
+
return objectifyClasses(this.contentClass)
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
overlayClasses () {
|
|
185
|
+
return objectifyClasses(this.overlayClass)
|
|
186
|
+
},
|
|
187
|
+
|
|
168
188
|
classes () {
|
|
169
189
|
return {
|
|
170
190
|
[this.color]: this.color,
|
|
171
191
|
[`${this.bgColor}--bg`]: this.bgColor,
|
|
172
|
-
|
|
173
|
-
[`w-menu--${this.position}`]:
|
|
174
|
-
[`w-menu--align-${this.alignment}`]: this.alignment,
|
|
192
|
+
...this.menuClasses,
|
|
193
|
+
[`w-menu--${this.position}`]: !this.noPosition,
|
|
194
|
+
[`w-menu--align-${this.alignment}`]: !this.noPosition && this.alignment,
|
|
175
195
|
'w-menu--tile': this.tile,
|
|
176
196
|
'w-menu--card': !this.custom,
|
|
177
197
|
'w-menu--round': this.round,
|
|
198
|
+
'w-menu--arrow': this.arrow,
|
|
178
199
|
'w-menu--shadow': this.shadow,
|
|
179
200
|
'w-menu--fixed': this.fixed
|
|
180
201
|
}
|
|
181
202
|
},
|
|
182
203
|
|
|
204
|
+
// The floating menu styles.
|
|
183
205
|
styles () {
|
|
184
206
|
return {
|
|
185
207
|
zIndex: this.zIndex || this.zIndex === 0 || (this.overlay && !this.zIndex && 200) || null,
|
|
186
208
|
top: (this.menuCoordinates.top && `${~~this.menuCoordinates.top}px`) || null,
|
|
187
209
|
left: (this.menuCoordinates.left && `${~~this.menuCoordinates.left}px`) || null,
|
|
188
|
-
minWidth: (this.minWidth && this.menuMinWidth) || null
|
|
210
|
+
minWidth: (this.minWidth && this.menuMinWidth) || null,
|
|
211
|
+
'--w-menu-bg-color': this.arrow && this.$waveui.colors[this.bgColor || 'white']
|
|
189
212
|
}
|
|
190
213
|
},
|
|
191
214
|
|
|
@@ -208,8 +231,10 @@ export default {
|
|
|
208
231
|
}, 10)
|
|
209
232
|
}
|
|
210
233
|
}
|
|
211
|
-
|
|
212
|
-
if ('ontouchstart' in window)
|
|
234
|
+
// Check the window exists: SSR-proof.
|
|
235
|
+
if (typeof window !== 'undefined' && 'ontouchstart' in window) {
|
|
236
|
+
handlers.click = this.toggleMenu
|
|
237
|
+
}
|
|
213
238
|
}
|
|
214
239
|
else handlers = { click: this.toggleMenu }
|
|
215
240
|
return handlers
|
|
@@ -217,8 +242,9 @@ export default {
|
|
|
217
242
|
},
|
|
218
243
|
|
|
219
244
|
methods: {
|
|
245
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
220
246
|
toggleMenu (e) {
|
|
221
|
-
let shouldShowMenu = this.
|
|
247
|
+
let shouldShowMenu = this.menuVisible
|
|
222
248
|
if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
|
|
223
249
|
shouldShowMenu = !shouldShowMenu
|
|
224
250
|
}
|
|
@@ -234,11 +260,21 @@ export default {
|
|
|
234
260
|
|
|
235
261
|
this.timeoutId = clearTimeout(this.timeoutId)
|
|
236
262
|
|
|
237
|
-
if (shouldShowMenu)
|
|
263
|
+
if (shouldShowMenu) {
|
|
264
|
+
this.$emit('update:modelValue', (this.menuVisible = true))
|
|
265
|
+
this.$emit('input', true)
|
|
266
|
+
this.$emit('open')
|
|
267
|
+
|
|
268
|
+
this.openMenu(e)
|
|
269
|
+
}
|
|
238
270
|
else this.closeMenu()
|
|
239
271
|
},
|
|
240
272
|
|
|
241
|
-
|
|
273
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
274
|
+
async openMenu (e) {
|
|
275
|
+
this.menuVisible = true
|
|
276
|
+
await this.insertMenu()
|
|
277
|
+
|
|
242
278
|
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
243
279
|
|
|
244
280
|
if (!this.noPosition) this.computeMenuPosition(e)
|
|
@@ -247,10 +283,10 @@ export default {
|
|
|
247
283
|
// if we don't postpone the Menu apparition it will start transition from a visible menu and
|
|
248
284
|
// thus will not transition.
|
|
249
285
|
this.timeoutId = setTimeout(() => {
|
|
250
|
-
this.$emit('update:modelValue',
|
|
286
|
+
this.$emit('update:modelValue', true)
|
|
251
287
|
this.$emit('input', true)
|
|
252
288
|
this.$emit('open')
|
|
253
|
-
},
|
|
289
|
+
}, 0)
|
|
254
290
|
|
|
255
291
|
if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
|
|
256
292
|
if (!this.noPosition) window.addEventListener('resize', this.onResize)
|
|
@@ -261,6 +297,7 @@ export default {
|
|
|
261
297
|
* - click of activator
|
|
262
298
|
* - hover outside if showOnHover
|
|
263
299
|
* - click inside menu if hideOnMenuClick.
|
|
300
|
+
* / ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
264
301
|
*
|
|
265
302
|
* @param {Boolean} force when showOnHover is set to true, hovering menu should keep it open.
|
|
266
303
|
* But if hideOnMenuClick is also set to true, this should force close
|
|
@@ -269,14 +306,14 @@ export default {
|
|
|
269
306
|
async closeMenu (force = false) {
|
|
270
307
|
// Might be already closed.
|
|
271
308
|
// E.g. showOnHover & hideOnMenuClick: on click, force hide then mouseleave is also firing.
|
|
272
|
-
if (!this.
|
|
309
|
+
if (!this.menuVisible) return
|
|
273
310
|
|
|
274
311
|
if (this.showOnHover && !force) {
|
|
275
312
|
await new Promise(resolve => setTimeout(resolve, 10))
|
|
276
313
|
if (this.showOnHover && (this.hoveringMenu || this.hoveringActivator)) return
|
|
277
314
|
}
|
|
278
315
|
|
|
279
|
-
this.$emit('update:modelValue', (this.
|
|
316
|
+
this.$emit('update:modelValue', (this.menuVisible = false))
|
|
280
317
|
this.$emit('input', false)
|
|
281
318
|
this.$emit('close')
|
|
282
319
|
// Remove the mousedown listener if the menu got closed without a mousedown outside of the menu.
|
|
@@ -284,9 +321,10 @@ export default {
|
|
|
284
321
|
window.removeEventListener('resize', this.onResize)
|
|
285
322
|
},
|
|
286
323
|
|
|
324
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
287
325
|
onOutsideMousedown (e) {
|
|
288
326
|
if (!this.menuEl.contains(e.target) && !this.activatorEl.contains(e.target)) {
|
|
289
|
-
this.$emit('update:modelValue', (this.
|
|
327
|
+
this.$emit('update:modelValue', (this.menuVisible = false))
|
|
290
328
|
this.$emit('input', false)
|
|
291
329
|
this.$emit('close')
|
|
292
330
|
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
@@ -299,6 +337,7 @@ export default {
|
|
|
299
337
|
this.computeMenuPosition()
|
|
300
338
|
},
|
|
301
339
|
|
|
340
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
302
341
|
getCoordinates (e) {
|
|
303
342
|
// Get the activator coordinates relative to window.
|
|
304
343
|
const { top, left, width, height } = (e ? e.target : this.activatorEl).getBoundingClientRect()
|
|
@@ -318,6 +357,7 @@ export default {
|
|
|
318
357
|
return coords
|
|
319
358
|
},
|
|
320
359
|
|
|
360
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
321
361
|
computeMenuPosition (e) {
|
|
322
362
|
// Get the activator coordinates.
|
|
323
363
|
let { top, left, width, height } = this.getCoordinates(e)
|
|
@@ -401,49 +441,53 @@ export default {
|
|
|
401
441
|
this.menuEl.style.visibility = null
|
|
402
442
|
|
|
403
443
|
// The menu coordinates are also recalculated while resizing window with open menu: keep the menu visible.
|
|
404
|
-
if (!this.
|
|
444
|
+
if (!this.menuVisible) this.menuEl.style.display = 'none'
|
|
405
445
|
|
|
406
446
|
this.menuCoordinates = { top, left }
|
|
407
447
|
},
|
|
408
448
|
|
|
409
449
|
insertMenu () {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
this.detachToTarget.appendChild(this.menuEl)
|
|
450
|
+
return new Promise(resolve => {
|
|
451
|
+
this.$nextTick(() => {
|
|
452
|
+
this.menuEl = this.$refs.menu?.$el || this.$refs.menu
|
|
453
|
+
|
|
454
|
+
// Move the menu elsewhere in the DOM.
|
|
455
|
+
// wrapper.parentNode.insertBefore(this.menuEl, wrapper)
|
|
456
|
+
this.detachToTarget.appendChild(this.menuEl)
|
|
457
|
+
resolve()
|
|
458
|
+
})
|
|
459
|
+
})
|
|
421
460
|
},
|
|
422
461
|
|
|
423
462
|
removeMenu () {
|
|
424
|
-
|
|
425
|
-
if (this.menuEl && this.menuEl.parentNode) this.menuEl.parentNode.removeChild(this.menuEl)
|
|
463
|
+
if (this.menuEl && this.menuEl.parentNode) this.menuEl.remove()
|
|
426
464
|
}
|
|
427
465
|
},
|
|
428
466
|
|
|
429
467
|
mounted () {
|
|
430
|
-
|
|
431
|
-
this.
|
|
432
|
-
|
|
468
|
+
const wrapper = this.$el
|
|
469
|
+
this.activatorEl = wrapper.firstElementChild
|
|
470
|
+
// Unwrap the activator element.
|
|
471
|
+
wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
|
|
472
|
+
|
|
473
|
+
// Unwrap the overlay.
|
|
474
|
+
if (this.overlay) {
|
|
475
|
+
this.overlayEl = this.$refs.overlay?.$el
|
|
476
|
+
wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
|
|
477
|
+
}
|
|
433
478
|
|
|
434
479
|
if (this.modelValue) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
435
480
|
},
|
|
436
481
|
|
|
437
482
|
beforeUnmount () {
|
|
438
483
|
this.removeMenu()
|
|
439
|
-
|
|
440
|
-
if (this.
|
|
441
|
-
if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.parentNode.removeChild(this.activatorEl)
|
|
484
|
+
if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
|
|
485
|
+
if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
|
|
442
486
|
},
|
|
443
487
|
|
|
444
488
|
watch: {
|
|
445
489
|
modelValue (bool) {
|
|
446
|
-
if (!!bool !== this.
|
|
490
|
+
if (!!bool !== this.menuVisible) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
447
491
|
},
|
|
448
492
|
detachTo () {
|
|
449
493
|
this.removeMenu()
|
|
@@ -473,5 +517,14 @@ export default {
|
|
|
473
517
|
&--bottom {margin-top: 3 * $base-increment;}
|
|
474
518
|
&--left {margin-left: -3 * $base-increment;}
|
|
475
519
|
&--right {margin-left: 3 * $base-increment;}
|
|
520
|
+
|
|
521
|
+
&--arrow {
|
|
522
|
+
&.w-menu--top {margin-top: -4 * $base-increment;}
|
|
523
|
+
&.w-menu--bottom {margin-top: 4 * $base-increment;}
|
|
524
|
+
&.w-menu--left {margin-left: -4 * $base-increment;}
|
|
525
|
+
&.w-menu--right {margin-left: 4 * $base-increment;}
|
|
526
|
+
|
|
527
|
+
@include triangle(var(--w-menu-bg-color), '.w-menu', 9px);
|
|
528
|
+
}
|
|
476
529
|
}
|
|
477
530
|
</style>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@mousedown="onMouseDown"
|
|
6
6
|
@mouseover="onMouseOver"
|
|
7
7
|
@mouseout="onMouseOut")
|
|
8
|
-
colgroup
|
|
8
|
+
colgroup(ref="colgroup")
|
|
9
9
|
col.w-table__col(
|
|
10
10
|
v-for="(header, i) in headers"
|
|
11
11
|
:key="i"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
th.w-table__header(
|
|
16
16
|
v-for="(header, i) in headers"
|
|
17
17
|
:key="i"
|
|
18
|
-
@click="header.sortable !== false && sortTable(header)"
|
|
18
|
+
@click="!colResizing.dragging && header.sortable !== false && sortTable(header)"
|
|
19
19
|
:class="headerClasses(header)")
|
|
20
20
|
w-icon.w-table__header-sort(
|
|
21
21
|
v-if="header.sortable !== false && header.align === 'right'"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
.w-table__loading-text
|
|
45
45
|
slot(name="loading") Loading...
|
|
46
46
|
//- No data.
|
|
47
|
-
tr.no-data(v-if="!tableItems.length")
|
|
47
|
+
tr.no-data(v-else-if="!tableItems.length")
|
|
48
48
|
td.w-table__cell.text-center(:colspan="headers.length")
|
|
49
49
|
slot(name="no-data") No data to show.
|
|
50
50
|
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
:item="item"
|
|
59
59
|
:index="i + 1"
|
|
60
60
|
:select="() => doSelectRow(item, i)"
|
|
61
|
-
:classes="{ 'w-table__row': true, 'w-table__row--selected': selectedRowsByUid[item._uid] !== undefined, 'w-table__row--
|
|
61
|
+
:classes="{ 'w-table__row': true, 'w-table__row--selected': selectedRowsByUid[item._uid] !== undefined, 'w-table__row--expanded': expandedRowsByUid[item._uid] !== undefined }")
|
|
62
62
|
|
|
63
63
|
tr.w-table__row(
|
|
64
64
|
v-else
|
|
65
65
|
@click="doSelectRow(item, i)"
|
|
66
|
-
:class="{ 'w-table__row--selected': selectedRowsByUid[item._uid] !== undefined, 'w-table__row--
|
|
66
|
+
:class="{ 'w-table__row--selected': selectedRowsByUid[item._uid] !== undefined, 'w-table__row--expanded': expandedRowsByUid[item._uid] !== undefined }")
|
|
67
67
|
template(v-for="(header, j) in headers")
|
|
68
68
|
td.w-table__cell(
|
|
69
69
|
v-if="$slots[`item-cell.${header.key}`] || $slots[`item-cell.${j + 1}`] || $slots['item-cell']"
|
|
@@ -106,13 +106,14 @@
|
|
|
106
106
|
:class="{ 'w-table__col-resizer--hover': colResizing.hover === j, 'w-table__col-resizer--active': colResizing.columnIndex === j }")
|
|
107
107
|
|
|
108
108
|
//- Expanded row.
|
|
109
|
-
tr.w-table__row.w-table__row--
|
|
109
|
+
tr.w-table__row.w-table__row--expansion(v-if="expandedRowsByUid[item._uid]")
|
|
110
110
|
td.w-table__cell(:colspan="headers.length")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
w-transition-expand(y)
|
|
112
|
+
div(v-if="expandedRowsByUid[item._uid]")
|
|
113
|
+
slot(name="row-expansion" :item="item" :index="i + 1")
|
|
114
|
+
span.w-table__col-resizer(
|
|
115
|
+
v-if="i < headers.length - 1 && resizableColumns"
|
|
116
|
+
:class="{ 'w-table__col-resizer--hover': colResizing.hover === i, 'w-table__col-resizer--active': colResizing.columnIndex === j }")
|
|
116
117
|
</template>
|
|
117
118
|
|
|
118
119
|
<script>
|
|
@@ -122,6 +123,9 @@
|
|
|
122
123
|
|
|
123
124
|
import { consoleError } from '../utils/console'
|
|
124
125
|
|
|
126
|
+
// When column resizing is on, this is the minimum cell width that we can resize to.
|
|
127
|
+
const minColumnWidth = 15
|
|
128
|
+
|
|
125
129
|
export default {
|
|
126
130
|
name: 'w-table',
|
|
127
131
|
props: {
|
|
@@ -171,7 +175,7 @@ export default {
|
|
|
171
175
|
resizableColumns: { type: Boolean }
|
|
172
176
|
},
|
|
173
177
|
|
|
174
|
-
emits: ['row-select', 'row-expand', 'row-click', 'update:sort', 'update:selected-rows', 'update:expanded-rows'],
|
|
178
|
+
emits: ['row-select', 'row-expand', 'row-click', 'update:sort', 'update:selected-rows', 'update:expanded-rows', 'column-resize'],
|
|
175
179
|
|
|
176
180
|
data: () => ({
|
|
177
181
|
activeSorting: [],
|
|
@@ -199,8 +203,7 @@ export default {
|
|
|
199
203
|
},
|
|
200
204
|
|
|
201
205
|
filteredItems () {
|
|
202
|
-
|
|
203
|
-
return this.tableItems
|
|
206
|
+
return typeof this.filter === 'function' ? this.tableItems.filter(this.filter) : this.tableItems
|
|
204
207
|
},
|
|
205
208
|
|
|
206
209
|
sortedItems () {
|
|
@@ -404,15 +407,17 @@ export default {
|
|
|
404
407
|
columnEl.style.width = colWidth + deltaX + 'px'
|
|
405
408
|
nextColumnEl.style.width = nextColWidth - deltaX + 'px'
|
|
406
409
|
|
|
407
|
-
// 2. Check if we went too far (the width
|
|
408
|
-
const minWidthReached = deltaX < 0 && columnEl.offsetWidth > newColWidth
|
|
410
|
+
// 2. Check if we went too far (the width applied is different than the browser-computed one).
|
|
411
|
+
const minWidthReached = (deltaX < 0 && columnEl.offsetWidth > newColWidth) ||
|
|
412
|
+
columnEl.offsetWidth <= minColumnWidth
|
|
409
413
|
const maxWidthReached = deltaX > 0 && nextColumnEl.offsetWidth > newNextColWidth
|
|
410
414
|
|
|
411
415
|
// 3. If we went too far, correct the value of both cells widths.
|
|
412
416
|
// Make sure we don't shrink enough to push other left cells.
|
|
413
417
|
if (minWidthReached) {
|
|
414
|
-
|
|
415
|
-
|
|
418
|
+
const newWidth = Math.max(columnEl.offsetWidth, minColumnWidth)
|
|
419
|
+
columnEl.style.width = newWidth + 'px'
|
|
420
|
+
nextColumnEl.style.width = maxWidth - newWidth + 'px'
|
|
416
421
|
}
|
|
417
422
|
// Make sure we don't grow enough to push other right cells.
|
|
418
423
|
else if (maxWidthReached) {
|
|
@@ -421,19 +426,28 @@ export default {
|
|
|
421
426
|
}
|
|
422
427
|
},
|
|
423
428
|
|
|
424
|
-
onResizerMouseUp (
|
|
429
|
+
onResizerMouseUp () {
|
|
425
430
|
// Remove listeners.
|
|
426
431
|
document.removeEventListener('mousemove', this.onResizerMouseMove)
|
|
427
432
|
document.removeEventListener('mouseup', this.onResizerMouseUp)
|
|
428
433
|
|
|
429
434
|
// Reset all the variables (better for debugging).
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
435
|
+
// setTimeout 0 to make sure the sorting is not applied when releasing the mouse on a header
|
|
436
|
+
// cell after resizing.
|
|
437
|
+
// (releasing the mouse on table header triggers a click event captured by the sorting feature)
|
|
438
|
+
setTimeout(() => {
|
|
439
|
+
// On Mouse up, emit an event containing all the new widths of the columns.
|
|
440
|
+
const widths = [...this.$refs.colgroup.childNodes].map(column => column.style?.width || column.offsetWidth)
|
|
441
|
+
this.$emit('column-resize', { index: this.colResizing.columnIndex, widths })
|
|
442
|
+
|
|
443
|
+
this.colResizing.dragging = false
|
|
444
|
+
this.colResizing.columnIndex = null
|
|
445
|
+
this.colResizing.startCursorX = null
|
|
446
|
+
this.colResizing.columnEl = null
|
|
447
|
+
this.colResizing.nextColumnEl = null
|
|
448
|
+
this.colResizing.colWidth = null
|
|
449
|
+
this.colResizing.nextColWidth = null
|
|
450
|
+
}, 0)
|
|
437
451
|
}
|
|
438
452
|
},
|
|
439
453
|
|
|
@@ -490,6 +504,10 @@ $tr-border-top: 1px;
|
|
|
490
504
|
border-collapse: collapse;
|
|
491
505
|
border: none;
|
|
492
506
|
|
|
507
|
+
&--resizable-cols {
|
|
508
|
+
table-layout: fixed; // Allow resizing beyond the cell minimum text width.
|
|
509
|
+
}
|
|
510
|
+
|
|
493
511
|
&--resizing {
|
|
494
512
|
&, * {cursor: col-resize;}
|
|
495
513
|
|
|
@@ -498,8 +516,11 @@ $tr-border-top: 1px;
|
|
|
498
516
|
|
|
499
517
|
// Table headers.
|
|
500
518
|
// ------------------------------------------------------
|
|
501
|
-
&__header {
|
|
502
|
-
|
|
519
|
+
&__header {padding: $base-increment;}
|
|
520
|
+
&__header--resizable {
|
|
521
|
+
overflow: hidden;
|
|
522
|
+
white-space: nowrap;
|
|
523
|
+
text-overflow: ellipsis;
|
|
503
524
|
}
|
|
504
525
|
|
|
505
526
|
&--fixed-header th {
|
|
@@ -596,7 +617,15 @@ $tr-border-top: 1px;
|
|
|
596
617
|
&__header:first-child, &__cell:first-child {padding-left: 2 * $base-increment;}
|
|
597
618
|
&__header:last-child, &__cell:last-child {padding-right: 2 * $base-increment;}
|
|
598
619
|
|
|
599
|
-
&--resizable-cols &__cell {
|
|
620
|
+
&--resizable-cols &__cell {
|
|
621
|
+
position: relative;
|
|
622
|
+
|
|
623
|
+
&, & * {
|
|
624
|
+
overflow: hidden;
|
|
625
|
+
// white-space: nowrap; // If you only want the content cell on a single line.
|
|
626
|
+
text-overflow: ellipsis;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
600
629
|
|
|
601
630
|
.no-data &__cell {
|
|
602
631
|
background-color: rgba(255, 255, 255, 0.2);
|
|
@@ -170,7 +170,8 @@ export default {
|
|
|
170
170
|
// Updates the slider position.
|
|
171
171
|
updateSlider (domLookup = true) {
|
|
172
172
|
if (domLookup) {
|
|
173
|
-
|
|
173
|
+
const ref = this.$refs['tabs-bar']
|
|
174
|
+
this.activeTabEl = ref && ref.querySelector('.w-tabs__bar-item--active')
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
if (!this.fillBar && this.activeTabEl) {
|