wave-ui 2.24.0 → 2.28.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.
@@ -1,11 +1,10 @@
1
1
  <template lang="pug">
2
- .w-menu-wrap(ref="wrapper")
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="titleClass"
24
- :content-class="contentClass"
21
+ :title-class="titleClasses"
22
+ :content-class="contentClasses"
25
23
  :shadow="shadow"
26
24
  :no-border="noBorder"
27
25
  :class="classes"
@@ -34,10 +32,12 @@
34
32
  w-overlay(
35
33
  v-if="overlay"
36
34
  ref="overlay"
37
- :model-value="showMenu"
35
+ :model-value="menuVisible"
38
36
  :persistent="persistent"
37
+ :class="overlayClasses"
38
+ v-bind="overlayProps"
39
39
  :z-index="(zIndex || 200) - 1"
40
- @update:model-value="showMenu = false")
40
+ @update:model-value="menuVisible = false")
41
41
  </template>
42
42
 
43
43
  <script>
@@ -50,6 +50,7 @@
50
50
  * and move the menu elsewhere in the DOM.
51
51
  */
52
52
 
53
+ import { objectifyClasses } from '../utils/index'
53
54
  import { consoleWarn } from '../utils/console'
54
55
 
55
56
  // const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
@@ -69,10 +70,11 @@ export default {
69
70
  round: { type: Boolean },
70
71
  noBorder: { type: Boolean },
71
72
  transition: { type: String },
72
- menuClass: { type: String },
73
- titleClass: { type: String },
74
- contentClass: { type: String },
73
+ menuClass: { type: [String, Object, Array] },
74
+ titleClass: { type: [String, Object, Array] },
75
+ contentClass: { type: [String, Object, Array] },
75
76
  // Position.
77
+ arrow: { type: Boolean }, // The small triangle pointing toward the activator.
76
78
  detachTo: { type: [String, Boolean, Object] },
77
79
  fixed: { type: Boolean },
78
80
  top: { type: Boolean },
@@ -86,6 +88,8 @@ export default {
86
88
  zIndex: { type: [Number, String, Boolean] },
87
89
  minWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
88
90
  overlay: { type: Boolean },
91
+ overlayClass: { type: [String, Object, Array] },
92
+ overlayProps: { type: Object }, // Allow passing down an object of props to the w-overlay component.
89
93
  persistent: { type: Boolean },
90
94
  noPosition: { type: Boolean }
91
95
  },
@@ -93,7 +97,7 @@ export default {
93
97
  emits: ['input', 'update:modelValue', 'open', 'close'],
94
98
 
95
99
  data: () => ({
96
- showMenu: false,
100
+ menuVisible: false,
97
101
  hoveringActivator: false,
98
102
  hoveringMenu: false,
99
103
  // The menu computed top & left coordinates.
@@ -161,27 +165,46 @@ export default {
161
165
  )
162
166
  },
163
167
 
168
+ menuClasses () {
169
+ return objectifyClasses(this.menuClass)
170
+ },
171
+
172
+ titleClasses () {
173
+ return objectifyClasses(this.titleClass)
174
+ },
175
+
176
+ contentClasses () {
177
+ return objectifyClasses(this.contentClass)
178
+ },
179
+
180
+ overlayClasses () {
181
+ return objectifyClasses(this.overlayClass)
182
+ },
183
+
164
184
  classes () {
165
185
  return {
166
186
  [this.color]: this.color,
167
187
  [`${this.bgColor}--bg`]: this.bgColor,
168
- [this.menuClass]: this.menuClass,
169
- [`w-menu--${this.position}`]: true,
170
- [`w-menu--align-${this.alignment}`]: this.alignment,
188
+ ...this.menuClasses,
189
+ [`w-menu--${this.position}`]: !this.noPosition,
190
+ [`w-menu--align-${this.alignment}`]: !this.noPosition && this.alignment,
171
191
  'w-menu--tile': this.tile,
172
192
  'w-menu--card': !this.custom,
173
193
  'w-menu--round': this.round,
194
+ 'w-menu--arrow': this.arrow,
174
195
  'w-menu--shadow': this.shadow,
175
196
  'w-menu--fixed': this.fixed
176
197
  }
177
198
  },
178
199
 
200
+ // The floatting menu styles.
179
201
  styles () {
180
202
  return {
181
203
  zIndex: this.zIndex || this.zIndex === 0 || (this.overlay && !this.zIndex && 200) || null,
182
204
  top: (this.menuCoordinates.top && `${~~this.menuCoordinates.top}px`) || null,
183
205
  left: (this.menuCoordinates.left && `${~~this.menuCoordinates.left}px`) || null,
184
- minWidth: (this.minWidth && this.menuMinWidth) || null
206
+ minWidth: (this.minWidth && this.menuMinWidth) || null,
207
+ '--w-menu-bg-color': this.arrow && this.$waveui.colors[this.bgColor || 'white']
185
208
  }
186
209
  },
187
210
 
@@ -214,7 +237,7 @@ export default {
214
237
 
215
238
  methods: {
216
239
  toggleMenu (e) {
217
- let shouldShowMenu = this.showMenu
240
+ let shouldShowMenu = this.menuVisible
218
241
  if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
219
242
  shouldShowMenu = !shouldShowMenu
220
243
  }
@@ -230,11 +253,20 @@ export default {
230
253
 
231
254
  this.timeoutId = clearTimeout(this.timeoutId)
232
255
 
233
- if (shouldShowMenu) this.openMenu(e)
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
+ }
234
263
  else this.closeMenu()
235
264
  },
236
265
 
237
- openMenu (e) {
266
+ async openMenu (e) {
267
+ this.menuVisible = true
268
+ await this.insertMenu()
269
+
238
270
  if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
239
271
 
240
272
  if (!this.noPosition) this.computeMenuPosition(e)
@@ -243,10 +275,10 @@ export default {
243
275
  // if we don't postpone the Menu apparition it will start transition from a visible menu and
244
276
  // thus will not transition.
245
277
  this.timeoutId = setTimeout(() => {
246
- this.$emit('update:modelValue', (this.showMenu = true))
278
+ this.$emit('update:modelValue', true)
247
279
  this.$emit('input', true)
248
280
  this.$emit('open')
249
- }, 10)
281
+ }, 0)
250
282
 
251
283
  if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
252
284
  if (!this.noPosition) window.addEventListener('resize', this.onResize)
@@ -265,14 +297,14 @@ export default {
265
297
  async closeMenu (force = false) {
266
298
  // Might be already closed.
267
299
  // E.g. showOnHover & hideOnMenuClick: on click, force hide then mouseleave is also firing.
268
- if (!this.showMenu) return
300
+ if (!this.menuVisible) return
269
301
 
270
302
  if (this.showOnHover && !force) {
271
303
  await new Promise(resolve => setTimeout(resolve, 10))
272
304
  if (this.showOnHover && (this.hoveringMenu || this.hoveringActivator)) return
273
305
  }
274
306
 
275
- this.$emit('update:modelValue', (this.showMenu = false))
307
+ this.$emit('update:modelValue', (this.menuVisible = false))
276
308
  this.$emit('input', false)
277
309
  this.$emit('close')
278
310
  // Remove the mousedown listener if the menu got closed without a mousedown outside of the menu.
@@ -282,7 +314,7 @@ export default {
282
314
 
283
315
  onOutsideMousedown (e) {
284
316
  if (!this.menuEl.contains(e.target) && !this.activatorEl.contains(e.target)) {
285
- this.$emit('update:modelValue', (this.showMenu = false))
317
+ this.$emit('update:modelValue', (this.menuVisible = false))
286
318
  this.$emit('input', false)
287
319
  this.$emit('close')
288
320
  document.removeEventListener('mousedown', this.onOutsideMousedown)
@@ -397,49 +429,53 @@ export default {
397
429
  this.menuEl.style.visibility = null
398
430
 
399
431
  // The menu coordinates are also recalculated while resizing window with open menu: keep the menu visible.
400
- if (!this.showMenu) this.menuEl.style.display = 'none'
432
+ if (!this.menuVisible) this.menuEl.style.display = 'none'
401
433
 
402
434
  this.menuCoordinates = { top, left }
403
435
  },
404
436
 
405
437
  insertMenu () {
406
- const wrapper = this.$refs.wrapper
407
- this.menuEl = this.$refs.menu.$el || this.$refs.menu
408
- // Unwrap the activator element.
409
- wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
410
-
411
- // Unwrap the overlay.
412
- if (this.overlay) wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
413
-
414
- // Move the menu elsewhere in the DOM.
415
- // wrapper.parentNode.insertBefore(this.menuEl, wrapper)
416
- this.detachToTarget.appendChild(this.menuEl)
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
+ })
417
448
  },
418
449
 
419
450
  removeMenu () {
420
- // el.remove() doesn't work on IE11.
421
- if (this.menuEl && this.menuEl.parentNode) this.menuEl.parentNode.removeChild(this.menuEl)
451
+ if (this.menuEl && this.menuEl.parentNode) this.menuEl.remove()
422
452
  }
423
453
  },
424
454
 
425
455
  mounted () {
426
- this.activatorEl = this.$refs.wrapper.firstElementChild
427
- this.overlayEl = this.overlay ? this.$refs.overlay.$el : null
428
- this.insertMenu()
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
+ }
429
466
 
430
467
  if (this.modelValue) this.toggleMenu({ type: 'click', target: this.activatorEl })
431
468
  },
432
469
 
433
470
  beforeUnmount () {
434
471
  this.removeMenu()
435
- // el.remove() doesn't work on IE11.
436
- if (this.overlay && this.overlayEl.parentNode) this.overlayEl.parentNode.removeChild(this.overlayEl)
437
- if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.parentNode.removeChild(this.activatorEl)
472
+ if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
473
+ if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
438
474
  },
439
475
 
440
476
  watch: {
441
477
  modelValue (bool) {
442
- if (!!bool !== this.showMenu) this.toggleMenu({ type: 'click', target: this.activatorEl })
478
+ if (!!bool !== this.menuVisible) this.toggleMenu({ type: 'click', target: this.activatorEl })
443
479
  },
444
480
  detachTo () {
445
481
  this.removeMenu()
@@ -469,5 +505,14 @@ export default {
469
505
  &--bottom {margin-top: 3 * $base-increment;}
470
506
  &--left {margin-left: -3 * $base-increment;}
471
507
  &--right {margin-left: 3 * $base-increment;}
508
+
509
+ &--arrow {
510
+ &.w-menu--top {margin-top: -4 * $base-increment;}
511
+ &.w-menu--bottom {margin-top: 4 * $base-increment;}
512
+ &.w-menu--left {margin-left: -4 * $base-increment;}
513
+ &.w-menu--right {margin-left: 4 * $base-increment;}
514
+
515
+ @include triangle(var(--w-menu-bg-color), '.w-menu', 9px);
516
+ }
472
517
  }
473
518
  </style>
@@ -62,7 +62,7 @@ export default {
62
62
  z-index: 1000;
63
63
  pointer-events: none;
64
64
  width: 280px;
65
- overflow: auto;
65
+ overflow-x: hidden;
66
66
 
67
67
  &--left {right: auto;left: 0;}
68
68
 
@@ -27,6 +27,7 @@ component(
27
27
  label.w-switch__label.w-form-el-shakable(v-else-if="label" :for="`w-switch--${_.uid}`" v-html="label")
28
28
  .w-switch__input(
29
29
  @click="$refs.input.focus();$refs.input.click()"
30
+ v-on="$attrs"
30
31
  :class="inputClasses")
31
32
  template(v-if="hasLabel && !labelOnLeft")
32
33
  label.w-switch__label.w-form-el-shakable(v-if="$slots.default" :for="`w-switch--${_.uid}`")
@@ -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--has-expanded': expandedRowsByUid[item._uid] !== undefined }")
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--has-expanded': expandedRowsByUid[item._uid] !== undefined }")
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--expanded(v-if="expandedRowsByUid[item._uid]")
109
+ tr.w-table__row.w-table__row--expansion(v-if="expandedRowsByUid[item._uid]")
110
110
  td.w-table__cell(:colspan="headers.length")
111
- div(v-if="expandedRowsByUid[item._uid]")
112
- slot(name="expanded-row" :item="item" :index="i + 1")
113
- span.w-table__col-resizer(
114
- v-if="j < headers.length - 1 && resizableColumns"
115
- :class="{ 'w-table__col-resizer--hover': colResizing.hover === j, 'w-table__col-resizer--active': colResizing.columnIndex === j }")
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
- if (typeof this.filter === 'function') return this.tableItems.filter(this.filter)
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 applyed is different than the browser-computed one).
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
- columnEl.style.width = columnEl.offsetWidth + 'px'
415
- nextColumnEl.style.width = maxWidth - columnEl.offsetWidth + 'px'
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 (e) {
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
- this.colResizing.dragging = false
431
- this.colResizing.columnIndex = null
432
- this.colResizing.startCursorX = null
433
- this.colResizing.columnEl = null
434
- this.colResizing.nextColumnEl = null
435
- this.colResizing.colWidth = null
436
- this.colResizing.nextColWidth = null
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
- padding: $base-increment;
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 {position: relative;}
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);
@@ -27,26 +27,30 @@
27
27
  .w-tabs__bar-extra(v-if="$slots['tabs-bar-extra']")
28
28
  slot(name="tabs-bar-extra")
29
29
  .w-tabs__slider(v-if="!noSlider && !card" :class="sliderColor" :style="sliderStyles")
30
+
30
31
  .w-tabs__content-wrap(v-if="tabsItems.length")
31
32
  transition(:name="transitionName" :mode="transitionMode")
32
- .w-tabs__content(v-if="activeTab" :key="activeTab._index" :class="contentClass")
33
- slot(
34
- v-if="$slots[`item-content.${activeTab.id || activeTab._index + 1}`]"
35
- :name="`item-content.${activeTab.id || activeTab._index + 1}`"
36
- :item="getOriginalItem(activeTab)"
37
- :index="activeTab._index + 1"
38
- :active="activeTab._index === activeTabIndex")
39
- slot(
40
- v-else
41
- name="item-content"
42
- :item="getOriginalItem(activeTab)"
43
- :index="activeTab._index + 1"
44
- :active="activeTab._index === activeTabIndex")
45
- div(v-html="activeTab[itemContentKey]")
33
+ keep-alive
34
+ //- Keep-alive only works with components, not with DOM nodes.
35
+ tab-content(:key="activeTab._index" :class="contentClass")
36
+ slot(
37
+ v-if="$slots[`item-content.${activeTab.id || activeTab._index + 1}`]"
38
+ :name="`item-content.${activeTab.id || activeTab._index + 1}`"
39
+ :item="getOriginalItem(activeTab)"
40
+ :index="activeTab._index + 1"
41
+ :active="activeTab._index === activeTabIndex")
42
+ slot(
43
+ v-else
44
+ name="item-content"
45
+ :item="getOriginalItem(activeTab)"
46
+ :index="activeTab._index + 1"
47
+ :active="activeTab._index === activeTabIndex")
48
+ div(v-html="activeTab[itemContentKey]")
46
49
  </template>
47
50
 
48
51
  <script>
49
52
  import { reactive } from 'vue'
53
+ import TabContent from './tab-content.vue'
50
54
 
51
55
  export default {
52
56
  name: 'w-tabs',
@@ -70,6 +74,8 @@ export default {
70
74
  card: { type: Boolean }
71
75
  },
72
76
 
77
+ components: { TabContent },
78
+
73
79
  emits: ['input', 'update:modelValue', 'focus'],
74
80
 
75
81
  data: () => ({
@@ -0,0 +1,8 @@
1
+ <template lang="pug">
2
+ .w-tabs__content
3
+ slot
4
+ </template>
5
+
6
+ <script>
7
+ // Keep-alive only works with components, not with DOM nodes.
8
+ </script>
@@ -1,7 +1,7 @@
1
1
  <template lang="pug">
2
2
  .w-tooltip-wrap(ref="wrapper" :class="{ 'w-tooltip-wrap--attached': !detachTo }")
3
3
  slot(name="activator" :on="eventHandlers")
4
- transition(:name="transitionName")
4
+ transition(:name="transitionName" appear)
5
5
  //- In Vue 3, a ref in a transition doesn't stay in $refs, it must be set as a function.
6
6
  .w-tooltip(
7
7
  :ref="el => tooltipEl = el"
@@ -26,6 +26,7 @@
26
26
  * and move the tooltip elsewhere in the DOM.
27
27
  */
28
28
 
29
+ import { objectifyClasses } from '../utils/index'
29
30
  import { consoleWarn } from '../utils/console'
30
31
 
31
32
  const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
@@ -42,8 +43,8 @@ export default {
42
43
  shadow: { type: Boolean },
43
44
  tile: { type: Boolean },
44
45
  round: { type: Boolean },
45
- transition: { type: String, default: '' },
46
- tooltipClass: { type: String },
46
+ transition: { type: String },
47
+ tooltipClass: { type: [String, Object, Array] },
47
48
  // Position.
48
49
  detachTo: {},
49
50
  fixed: { type: Boolean },
@@ -71,6 +72,10 @@ export default {
71
72
  }),
72
73
 
73
74
  computed: {
75
+ tooltipClasses () {
76
+ return objectifyClasses(this.tooltipClass)
77
+ },
78
+
74
79
  transitionName () {
75
80
  const direction = this.position.replace(/top|bottom/, m => ({ top: 'up', bottom: 'down' }[m]))
76
81
  return this.transition || `w-tooltip-slide-fade-${direction}`
@@ -143,7 +148,7 @@ export default {
143
148
  return {
144
149
  [this.color]: !this.bgColor,
145
150
  [`${this.bgColor} ${this.bgColor}--bg`]: this.bgColor,
146
- [this.tooltipClass]: this.tooltipClass,
151
+ ...this.tooltipClasses,
147
152
  [`w-tooltip--${this.position}`]: true,
148
153
  'w-tooltip--tile': this.tile,
149
154
  'w-tooltip--round': this.round,
@@ -280,8 +285,7 @@ export default {
280
285
  },
281
286
 
282
287
  removeTooltip () {
283
- // el.remove() doesn't work on IE11.
284
- if (this.tooltipEl && this.tooltipEl.parentNode) this.tooltipEl.parentNode.removeChild(this.tooltipEl)
288
+ if (this.tooltipEl && this.tooltipEl.parentNode) this.tooltipEl.remove()
285
289
  }
286
290
  },
287
291
 
@@ -295,8 +299,7 @@ export default {
295
299
  beforeUnmount () {
296
300
  this.removeTooltip()
297
301
 
298
- // el.remove() doesn't work on IE11.
299
- if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.parentNode.removeChild(this.activatorEl)
302
+ if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
300
303
  },
301
304
 
302
305
  watch: {
@@ -10,24 +10,3 @@ a {text-decoration: none;}
10
10
  padding-left: 3 * $base-increment;
11
11
  padding-right: 3 * $base-increment;
12
12
  }
13
-
14
- .nav-menu ~ .main-content {padding-left: 4em;}
15
-
16
- footer {
17
- margin-top: 5em;
18
-
19
- .nav-drawer ~ & {padding-left: 12px;}
20
-
21
- .heart:hover {animation: heartbeat 1s infinite;}
22
- small {font-size: 10px;}
23
- .caption {padding-top: 1px;}
24
- }
25
-
26
- @keyframes heartbeat {
27
- 0%, 30%, 60%, 100% {transform: scale(1);}
28
- 15%, 45% {transform: scale(1.2);}
29
- }
30
-
31
- @media screen and (max-width: 560px) {
32
- button.go-top {display: none;}
33
- }