wave-ui 2.28.1 → 2.31.1

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.
@@ -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 && menuVisible"
7
- ref="menu"
8
- @click="hideOnMenuClick && closeMenu(true)"
6
+ v-if="custom && detachableVisible"
7
+ ref="detachable"
8
+ @click="hideOnMenuClick && close(true)"
9
9
  @mouseenter="showOnHover && (hoveringMenu = true)"
10
- @mouseleave="showOnHover && ((hoveringMenu = false), closeMenu())"
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="menuVisible"
16
- ref="menu"
17
- @click.native="hideOnMenuClick && closeMenu(true)"
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), closeMenu())"
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
- :model-value="menuVisible"
35
+ :model-value="detachableVisible"
36
36
  :persistent="persistent"
37
37
  :class="overlayClasses"
38
38
  v-bind="overlayProps"
39
39
  :z-index="(zIndex || 200) - 1"
40
- @update:model-value="menuVisible = false")
40
+ @update:model-value="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 { consoleWarn } from '../utils/console'
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
  modelValue: {}, // Show or hide.
@@ -73,98 +74,53 @@ export default {
73
74
  menuClass: { type: [String, Object, Array] },
74
75
  titleClass: { type: [String, Object, Array] },
75
76
  contentClass: { type: [String, Object, Array] },
76
- // Position.
77
77
  arrow: { type: Boolean }, // The small triangle pointing toward the activator.
78
- detachTo: { type: [String, Boolean, Object] },
79
- fixed: { type: Boolean },
80
- top: { type: Boolean },
81
- bottom: { type: Boolean },
82
- left: { type: Boolean },
83
- right: { type: Boolean },
84
- alignTop: { type: Boolean },
85
- alignBottom: { type: Boolean },
86
- alignLeft: { type: Boolean },
87
- alignRight: { type: Boolean },
88
- zIndex: { type: [Number, String, Boolean] },
89
78
  minWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
90
79
  overlay: { type: Boolean },
91
80
  overlayClass: { type: [String, Object, Array] },
92
81
  overlayProps: { type: Object }, // Allow passing down an object of props to the w-overlay component.
93
82
  persistent: { type: Boolean },
94
- noPosition: { type: Boolean }
83
+ delay: { type: Number }
84
+ // Other props in the detachable mixin:
85
+ // detachTo, appendTo, fixed, top, bottom, left, right, alignTop, alignBottom, alignLeft,
86
+ // alignRight, noPosition, zIndex, activator.
95
87
  },
96
88
 
97
89
  emits: ['input', 'update:modelValue', 'open', 'close'],
98
90
 
99
91
  data: () => ({
100
- menuVisible: false,
92
+ detachableVisible: false,
101
93
  hoveringActivator: false,
102
94
  hoveringMenu: false,
103
95
  // The menu computed top & left coordinates.
104
- menuCoordinates: {
96
+ detachableCoords: {
105
97
  top: 0,
106
98
  left: 0
107
99
  },
108
- activatorEl: null,
109
100
  activatorWidth: 0,
110
- menuEl: null,
101
+ detachableEl: null,
111
102
  timeoutId: null
112
103
  }),
113
104
 
114
105
  computed: {
106
+ /**
107
+ * Other computed in the detachable mixin:
108
+ * - `appendToTarget`
109
+ * - `detachableParentEl`
110
+ * - `activatorEl`
111
+ * - `position`
112
+ * - `alignment`
113
+ **/
114
+
115
115
  transitionName () {
116
116
  return this.transition || 'scale-fade'
117
117
  },
118
118
 
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
- position () {
144
- return (
145
- (this.top && 'top') ||
146
- (this.bottom && 'bottom') ||
147
- (this.left && 'left') ||
148
- (this.right && 'right') ||
149
- 'bottom'
150
- )
151
- },
152
-
153
119
  menuMinWidth () {
154
120
  if (this.minWidth === 'activator') return this.activatorWidth ? `${this.activatorWidth}px` : 0
155
121
  else return isNaN(this.minWidth) ? this.minWidth : (this.minWidth ? `${this.minWidth}px` : 0)
156
122
  },
157
123
 
158
- alignment () {
159
- return (
160
- ((this.top || this.bottom) && this.alignLeft && 'left') ||
161
- ((this.top || this.bottom) && this.alignRight && 'right') ||
162
- ((this.left || this.right) && this.alignTop && 'top') ||
163
- ((this.left || this.right) && this.alignBottom && 'bottom') ||
164
- ''
165
- )
166
- },
167
-
168
124
  menuClasses () {
169
125
  return objectifyClasses(this.menuClass)
170
126
  },
@@ -197,12 +153,12 @@ export default {
197
153
  }
198
154
  },
199
155
 
200
- // The floatting menu styles.
156
+ // The floating menu styles.
201
157
  styles () {
202
158
  return {
203
159
  zIndex: this.zIndex || this.zIndex === 0 || (this.overlay && !this.zIndex && 200) || null,
204
- top: (this.menuCoordinates.top && `${~~this.menuCoordinates.top}px`) || null,
205
- left: (this.menuCoordinates.left && `${~~this.menuCoordinates.left}px`) || null,
160
+ top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
161
+ left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
206
162
  minWidth: (this.minWidth && this.menuMinWidth) || null,
207
163
  '--w-menu-bg-color': this.arrow && this.$waveui.colors[this.bgColor || 'white']
208
164
  }
@@ -213,31 +169,44 @@ export default {
213
169
 
214
170
  if (this.showOnHover) {
215
171
  handlers = {
216
- focus: this.toggleMenu,
217
- blur: this.toggleMenu,
172
+ focus: this.toggle,
173
+ blur: this.toggle,
218
174
  mouseenter: e => {
219
175
  this.hoveringActivator = true
220
- this.openMenu(e)
176
+ this.open(e)
221
177
  },
222
178
  mouseleave: e => {
223
179
  this.hoveringActivator = false
224
180
  // Wait 10ms, the time to get the hoveringMenu updated on mouseenter on the menu.
225
181
  setTimeout(() => {
226
- if (!this.hoveringMenu) this.closeMenu()
182
+ if (!this.hoveringMenu) this.close()
227
183
  }, 10)
228
184
  }
229
185
  }
230
-
231
- if ('ontouchstart' in window) handlers.click = this.toggleMenu
186
+ // Check the window exists: SSR-proof.
187
+ if (typeof window !== 'undefined' && 'ontouchstart' in window) {
188
+ handlers.click = this.toggle
189
+ }
232
190
  }
233
- else handlers = { click: this.toggleMenu }
191
+ else handlers = { click: this.toggle }
234
192
  return handlers
235
193
  }
236
194
  },
237
195
 
238
196
  methods: {
239
- toggleMenu (e) {
240
- let shouldShowMenu = this.menuVisible
197
+ /**
198
+ * Other methods in the `detachable` mixin:
199
+ * - `getActivatorCoordinates`
200
+ * - `computeDetachableCoords`
201
+ * - `onResize`
202
+ * - `onOutsideMousedown`
203
+ * - `insertInDOM`
204
+ * - `removeFromDOM`
205
+ **/
206
+
207
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
208
+ toggle (e) {
209
+ let shouldShowMenu = this.detachableVisible
241
210
  if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
242
211
  shouldShowMenu = !shouldShowMenu
243
212
  }
@@ -253,25 +222,29 @@ export default {
253
222
 
254
223
  this.timeoutId = clearTimeout(this.timeoutId)
255
224
 
256
- if (shouldShowMenu) {
257
- this.$emit('update:modelValue', (this.menuVisible = true))
258
- this.$emit('input', true)
259
- this.$emit('open')
260
-
261
- this.openMenu(e)
262
- }
263
- else this.closeMenu()
225
+ if (shouldShowMenu) this.open(e)
226
+ else this.close()
264
227
  },
265
228
 
266
- async openMenu (e) {
267
- this.menuVisible = true
268
- await this.insertMenu()
229
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
230
+ async open (e) {
231
+ // A tiny delay may help positioning the detachable correctly in case of multiple activators
232
+ // with different menu contents.
233
+ if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
234
+
235
+ this.detachableVisible = true
236
+
237
+ // If the activator is external, there might be multiple,
238
+ // so on open, the activator will be set to the event target.
239
+ if (this.activator) this.activatorEl = e.target
240
+
241
+ await this.insertInDOM()
269
242
 
270
243
  if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
271
244
 
272
- if (!this.noPosition) this.computeMenuPosition(e)
245
+ if (!this.noPosition) this.computeDetachableCoords(e)
273
246
 
274
- // In `getCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
247
+ // In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
275
248
  // if we don't postpone the Menu apparition it will start transition from a visible menu and
276
249
  // thus will not transition.
277
250
  this.timeoutId = setTimeout(() => {
@@ -289,199 +262,32 @@ export default {
289
262
  * - click of activator
290
263
  * - hover outside if showOnHover
291
264
  * - click inside menu if hideOnMenuClick.
265
+ * / ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
292
266
  *
293
267
  * @param {Boolean} force when showOnHover is set to true, hovering menu should keep it open.
294
268
  * But if hideOnMenuClick is also set to true, this should force close
295
269
  * even while hovering the menu.
296
270
  */
297
- async closeMenu (force = false) {
271
+ async close (force = false) {
298
272
  // Might be already closed.
299
273
  // E.g. showOnHover & hideOnMenuClick: on click, force hide then mouseleave is also firing.
300
- if (!this.menuVisible) return
274
+ if (!this.detachableVisible) return
301
275
 
302
276
  if (this.showOnHover && !force) {
303
277
  await new Promise(resolve => setTimeout(resolve, 10))
304
278
  if (this.showOnHover && (this.hoveringMenu || this.hoveringActivator)) return
305
279
  }
306
280
 
307
- this.$emit('update:modelValue', (this.menuVisible = false))
281
+ this.$emit('update:modelValue', (this.detachableVisible = false))
308
282
  this.$emit('input', false)
309
283
  this.$emit('close')
310
284
  // Remove the mousedown listener if the menu got closed without a mousedown outside of the menu.
311
285
  document.removeEventListener('mousedown', this.onOutsideMousedown)
312
286
  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
- }
453
- },
454
-
455
- mounted () {
456
- const wrapper = this.$el
457
- this.activatorEl = wrapper.firstElementChild
458
- // Unwrap the activator element.
459
- wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
460
-
461
- // Unwrap the overlay.
462
- if (this.overlay) {
463
- this.overlayEl = this.$refs.overlay?.$el
464
- wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
465
- }
466
-
467
- if (this.modelValue) this.toggleMenu({ type: 'click', target: this.activatorEl })
468
- },
469
-
470
- beforeUnmount () {
471
- this.removeMenu()
472
- if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
473
- if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
474
- },
475
-
476
- watch: {
477
- modelValue (bool) {
478
- if (!!bool !== this.menuVisible) this.toggleMenu({ type: 'click', target: this.activatorEl })
479
- },
480
- detachTo () {
481
- this.removeMenu()
482
- this.insertMenu()
483
287
  }
484
288
  }
289
+
290
+ // watch, mounted & beforeDestroy hooks are set in the detachable.js mixin.
485
291
  }
486
292
  </script>
487
293
 
@@ -11,8 +11,8 @@ transition-group(
11
11
  :key="notif._uid"
12
12
  v-model="notif._value"
13
13
  @close="notifManager.dismiss(notif._uid)"
14
- v-bind="notif")
15
- | {{ notif.message }}
14
+ v-bind="notifProps(notif)")
15
+ div(v-html="notif.message")
16
16
  </template>
17
17
 
18
18
  <script>
@@ -42,6 +42,13 @@ export default {
42
42
  }
43
43
  },
44
44
 
45
+ methods: {
46
+ notifProps (notif) {
47
+ const { _value, _uid, message, timeout, ...props } = notif
48
+ return props
49
+ }
50
+ },
51
+
45
52
  created () {
46
53
  this.notifManager = new NotificationManager()
47
54
  },
@@ -1,7 +1,10 @@
1
1
  <template lang="pug">
2
2
  .w-progress(:class="classes" :style="styles")
3
3
  //- Linear progress.
4
- .w-progress__progress(v-if="!circle" :class="{ full: progressValue === 100 }" :style="`width: ${progressValue}%`")
4
+ .w-progress__progress(
5
+ v-if="!circle"
6
+ :class="{ full: progressValue === 100 }"
7
+ :style="`width: ${progressValue}%`")
5
8
 
6
9
  //- Circular progress.
7
10
  template(v-else)
@@ -3,7 +3,7 @@ component(
3
3
  ref="formEl"
4
4
  :is="formRegister ? 'w-form-element' : 'div'"
5
5
  v-bind="formRegister && { validators, inputValue: rating, disabled: isDisabled, readonly: isReadonly }"
6
- :valid.sync="valid"
6
+ v-model:valid="valid"
7
7
  @reset="$emit('update:modelValue', rating = null);$emit('input', null)"
8
8
  :class="classes")
9
9
  input(:id="inputName" :name="inputName" type="hidden" :value="rating")
@@ -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
- :detach-to="(menuProps || {}).detachTo !== undefined ? (menuProps || {}).detachTo : '.w-app'"
24
+ :append-to="(menuProps || {}).appendTo !== undefined ? (menuProps || {}).appendTo : '.w-app'"
25
25
  align-left
26
26
  custom
27
27
  min-width="activator"
@@ -329,7 +329,7 @@ export default {
329
329
  setTimeout(() => {
330
330
  const itemIndex = this.inputValue.length ? this.inputValue[0].index : 0 // Real index starts at 0.
331
331
  // User visible index starts at 1.
332
- this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._.uid}_item-${itemIndex + 1}`).focus()
332
+ this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._.uid}_item-${itemIndex + 1}`)?.focus()
333
333
  }, 100)
334
334
  },
335
335
 
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- .w-spinner(v-if="value || value === undefined" :class="classes" :style="styles")
2
+ .w-spinner(v-if="modelValue || modelValue === undefined" :class="classes" :style="styles")
3
3
  span(v-if="isThreeDots")
4
4
  </template>
5
5
 
@@ -7,7 +7,7 @@
7
7
  export default {
8
8
  name: 'w-spinner',
9
9
  props: {
10
- value: {},
10
+ modelValue: {},
11
11
  color: { type: String, default: 'primary' },
12
12
  xs: { type: Boolean },
13
13
  sm: { type: Boolean },
@@ -313,7 +313,7 @@ export default {
313
313
  }
314
314
  )
315
315
 
316
- // Keep external `expanded-rows.sync` updated.
316
+ // Keep external `expanded-rows.sync` (Vue 2) or v-model:expanded-rows (Vue 3) updated.
317
317
  this.$emit('update:expanded-rows', this.expandedRowsInternal)
318
318
  }
319
319
 
@@ -342,7 +342,7 @@ export default {
342
342
  }
343
343
  )
344
344
 
345
- // Keep external `selected-rows.sync` updated.
345
+ // Keep external `selected-rows.sync` (Vue 2) or v-model:selected-rows (Vue 3) updated.
346
346
  this.$emit('update:selected-rows', this.selectedRowsInternal)
347
347
  }
348
348
  }
@@ -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 {font-size: round(0.7 * $base-font-size);}
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.95 * $base-font-size);
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
  }
@@ -116,7 +116,7 @@ export default {
116
116
  return listeners
117
117
  },
118
118
  hasValue () {
119
- return this.inputValue
119
+ return this.inputValue || this.inputValue === 0
120
120
  },
121
121
  hasLabel () {
122
122
  return this.label || this.$slots.default