wave-ui 2.23.0 → 2.27.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,12 +1,21 @@
1
1
  <template lang="pug">
2
2
  .w-table-wrap(:class="wrapClasses")
3
- table.w-table(:class="classes")
3
+ table.w-table(
4
+ :class="classes"
5
+ @mousedown="onMouseDown"
6
+ @mouseover="onMouseOver"
7
+ @mouseout="onMouseOut")
8
+ colgroup
9
+ col.w-table__col(
10
+ v-for="(header, i) in headers"
11
+ :key="i"
12
+ :width="header.width || null")
4
13
  thead(v-if="!noHeaders")
5
14
  tr
6
15
  th.w-table__header(
7
16
  v-for="(header, i) in headers"
8
17
  :key="i"
9
- @click="header.sortable !== false && sortTable(header)"
18
+ @click="!colResizing.dragging && header.sortable !== false && sortTable(header)"
10
19
  :class="headerClasses(header)")
11
20
  w-icon.w-table__header-sort(
12
21
  v-if="header.sortable !== false && header.align === 'right'"
@@ -22,14 +31,27 @@
22
31
  w-icon.w-table__header-sort(
23
32
  v-if="header.sortable !== false && header.align !== 'right'"
24
33
  :class="headerSortClasses(header)") wi-arrow-down
34
+ //- Notes: prevent click on header (`.stop`), which triggers sorting & DOM refresh.
35
+ span.w-table__col-resizer(
36
+ v-if="i < headers.length - 1 && resizableColumns"
37
+ :class="{ 'w-table__col-resizer--hover': colResizing.hover === i, 'w-table__col-resizer--active': colResizing.columnIndex === i }"
38
+ @click.stop)
25
39
  tbody
40
+ //- Progress bar.
26
41
  tr.w-table__progress-bar(v-if="loading")
27
42
  td(:colspan="headers.length")
28
43
  w-progress(tile)
29
44
  .w-table__loading-text
30
45
  slot(name="loading") Loading...
31
- template(v-else-if="tableItems.length")
46
+ //- No data.
47
+ tr.no-data(v-if="!tableItems.length")
48
+ td.w-table__cell.text-center(:colspan="headers.length")
49
+ slot(name="no-data") No data to show.
50
+
51
+ //- Normal rows.
52
+ template(v-else)
32
53
  template(v-for="(item, i) in sortedItems" :key="i")
54
+ //- Fully custom tr (`item` slot).
33
55
  slot(
34
56
  v-if="$slots.item"
35
57
  name="item"
@@ -69,25 +91,40 @@
69
91
  :item="item"
70
92
  :label="item[header.key] || ''"
71
93
  :index="i + 1")
94
+ span.w-table__col-resizer(
95
+ v-if="j < headers.length - 1 && resizableColumns"
96
+ :class="{ 'w-table__col-resizer--hover': colResizing.hover === j, 'w-table__col-resizer--active': colResizing.columnIndex === j }")
97
+
72
98
  td.w-table__cell(
73
99
  v-else
74
100
  :key="`${j}-b`"
75
101
  :data-label="header.label"
76
- :class="`text-${header.align || 'left'}`"
77
- v-html="item[header.key] || ''")
102
+ :class="`text-${header.align || 'left'}`")
103
+ div(v-html="item[header.key] || ''")
104
+ span.w-table__col-resizer(
105
+ v-if="j < headers.length - 1 && resizableColumns"
106
+ :class="{ 'w-table__col-resizer--hover': colResizing.hover === j, 'w-table__col-resizer--active': colResizing.columnIndex === j }")
107
+
108
+ //- Expanded row.
78
109
  tr.w-table__row.w-table__row--expanded(v-if="expandedRowsByUid[item._uid]")
79
110
  td.w-table__cell(:colspan="headers.length")
80
111
  div(v-if="expandedRowsByUid[item._uid]")
81
112
  slot(name="expanded-row" :item="item" :index="i + 1")
82
-
83
- tr.no-data(v-else)
84
- td.w-table__cell.text-center(:colspan="headers.length")
85
- slot(name="no-data") No data to show.
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 }")
86
116
  </template>
87
117
 
88
118
  <script>
119
+ /**
120
+ * @todo: (Column Resizing) Recalc. on browser resize.
121
+ */
122
+
89
123
  import { consoleError } from '../utils/console'
90
124
 
125
+ // When column resizing is on, this is the minimum cell width that we can resize to.
126
+ const minColumnWidth = 15
127
+
91
128
  export default {
92
129
  name: 'w-table',
93
130
  props: {
@@ -133,7 +170,8 @@ export default {
133
170
  uidKey: { type: String, default: 'id' },
134
171
 
135
172
  filter: { type: Function },
136
- mobileBreakpoint: { type: Number, default: 0 }
173
+ mobileBreakpoint: { type: Number, default: 0 },
174
+ resizableColumns: { type: Boolean }
137
175
  },
138
176
 
139
177
  emits: ['row-select', 'row-expand', 'row-click', 'update:sort', 'update:selected-rows', 'update:expanded-rows'],
@@ -141,7 +179,18 @@ export default {
141
179
  data: () => ({
142
180
  activeSorting: [],
143
181
  selectedRowsInternal: [], // Array of uids.
144
- expandedRowsInternal: [] // Array of uids.
182
+ expandedRowsInternal: [], // Array of uids.
183
+ // Column resizing feature.
184
+ colResizing: {
185
+ dragging: false,
186
+ hover: false, // False or a column number starting from 0.
187
+ columnIndex: null, // Column number starting from 0.
188
+ startCursorX: null,
189
+ colWidth: null,
190
+ nextColWidth: null,
191
+ columnEl: null,
192
+ nextColumnEl: null
193
+ }
145
194
  }),
146
195
 
147
196
  computed: {
@@ -192,6 +241,8 @@ export default {
192
241
  classes () {
193
242
  return {
194
243
  'w-table--mobile': this.isMobile || null,
244
+ 'w-table--resizable-cols': this.resizableColumns || null,
245
+ 'w-table--resizing': this.colResizing.dragging,
195
246
  'w-table--fixed-header': this.fixedHeaders
196
247
  }
197
248
  },
@@ -214,7 +265,8 @@ export default {
214
265
  methods: {
215
266
  headerClasses (header) {
216
267
  return {
217
- 'w-table__header--sortable': header.sortable !== false,
268
+ 'w-table__header--sortable': header.sortable !== false, // Can also be falsy with `0`.
269
+ 'w-table__header--resizable': !!this.resizableColumns,
218
270
  [`text-${header.align || 'left'}`]: true
219
271
  }
220
272
  },
@@ -296,6 +348,102 @@ export default {
296
348
  }
297
349
 
298
350
  this.$emit('row-click', { item, index })
351
+ },
352
+
353
+ // Attach 1 single event listener on the table rather than 1 on each resizer.
354
+ onMouseDown (e) {
355
+ if (e.target.classList.contains('w-table__col-resizer')) {
356
+ this.colResizing.columnIndex = +e.target.parentNode.cellIndex
357
+ this.colResizing.startCursorX = e.pageX // x-axis coordinate at drag start.
358
+
359
+ // Applying width on colgroup > col works with & without `no-headers`.
360
+ // So it's better than setting a condition to apply on first row tds in case of no-headers.
361
+ this.colResizing.columnEl = this.$el.querySelector(`col:nth-child(${this.colResizing.columnIndex + 1})`)
362
+ this.colResizing.nextColumnEl = this.colResizing.columnEl.nextSibling
363
+ this.colResizing.colWidth = this.colResizing.columnEl.offsetWidth
364
+ this.colResizing.nextColWidth = this.colResizing.nextColumnEl.offsetWidth
365
+
366
+ // Now that we've grabbed the resizer, bind the mousemove & mouseup events to the whole document.
367
+ document.addEventListener('mousemove', this.onResizerMouseMove)
368
+ document.addEventListener('mouseup', this.onResizerMouseUp)
369
+ }
370
+ },
371
+
372
+ // Attach 1 single event listener on the table rather than 1 on each resizer.
373
+ onMouseOver ({ target }) {
374
+ // On col resizer mouseover.
375
+ if (target.classList.contains('w-table__col-resizer')) {
376
+ this.colResizing.hover = +target.parentNode.cellIndex
377
+ }
378
+ },
379
+
380
+ // Attach 1 single event listener on the table rather than 1 on each resizer.
381
+ onMouseOut ({ target }) {
382
+ // On col resizer mouseout.
383
+ if (target.classList.contains('w-table__col-resizer')) this.colResizing.hover = false
384
+ },
385
+
386
+ /**
387
+ * Notes:
388
+ * Make sure there is no change of variable that would cause a DOM refresh,
389
+ * and glitch while dragging.
390
+ * this.$set(this.headers[columnIndex], 'width', colWidth + deltaX)
391
+
392
+ * If using the width attribute with variable (so data-driven) and not `style.width`,
393
+ * any later change of variable would cause a DOM refresh, and lose the current DOM state
394
+ * (losing the 2 columns width). So do a direct DOM manipulation using `.style.width`.
395
+ */
396
+ onResizerMouseMove (e) {
397
+ const { startCursorX, columnEl, nextColumnEl, colWidth, nextColWidth } = this.colResizing
398
+
399
+ this.colResizing.dragging = true
400
+ const deltaX = e.pageX - startCursorX
401
+
402
+ const maxWidth = colWidth + nextColWidth
403
+ const newColWidth = colWidth + deltaX
404
+ const newNextColWidth = nextColWidth - deltaX
405
+
406
+ // 1. Apply the change of width.
407
+ columnEl.style.width = colWidth + deltaX + 'px'
408
+ nextColumnEl.style.width = nextColWidth - deltaX + 'px'
409
+
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
413
+ const maxWidthReached = deltaX > 0 && nextColumnEl.offsetWidth > newNextColWidth
414
+
415
+ // 3. If we went too far, correct the value of both cells widths.
416
+ // Make sure we don't shrink enough to push other left cells.
417
+ if (minWidthReached) {
418
+ const newWidth = Math.max(columnEl.offsetWidth, minColumnWidth)
419
+ columnEl.style.width = newWidth + 'px'
420
+ nextColumnEl.style.width = maxWidth - newWidth + 'px'
421
+ }
422
+ // Make sure we don't grow enough to push other right cells.
423
+ else if (maxWidthReached) {
424
+ columnEl.style.width = maxWidth - nextColumnEl.offsetWidth + 'px'
425
+ nextColumnEl.style.width = nextColumnEl.offsetWidth + 'px'
426
+ }
427
+ },
428
+
429
+ onResizerMouseUp () {
430
+ // Remove listeners.
431
+ document.removeEventListener('mousemove', this.onResizerMouseMove)
432
+ document.removeEventListener('mouseup', this.onResizerMouseUp)
433
+
434
+ // Reset all the variables (better for debugging).
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
+ this.colResizing.dragging = false
440
+ this.colResizing.columnIndex = null
441
+ this.colResizing.startCursorX = null
442
+ this.colResizing.columnEl = null
443
+ this.colResizing.nextColumnEl = null
444
+ this.colResizing.colWidth = null
445
+ this.colResizing.nextColWidth = null
446
+ }, 0)
299
447
  }
300
448
  },
301
449
 
@@ -335,6 +483,8 @@ export default {
335
483
  </script>
336
484
 
337
485
  <style lang="scss">
486
+ $tr-border-top: 1px;
487
+
338
488
  .w-table-wrap {
339
489
  position: relative;
340
490
  border-radius: $border-radius;
@@ -350,10 +500,23 @@ export default {
350
500
  border-collapse: collapse;
351
501
  border: none;
352
502
 
503
+ &--resizable-cols {
504
+ table-layout: fixed; // Allow resizing beyond the cell minimum text width.
505
+ }
506
+
507
+ &--resizing {
508
+ &, * {cursor: col-resize;}
509
+
510
+ user-select: none;
511
+ }
512
+
353
513
  // Table headers.
354
514
  // ------------------------------------------------------
355
- &__header {
356
- padding: $base-increment;
515
+ &__header {padding: $base-increment;}
516
+ &__header--resizable {
517
+ overflow: hidden;
518
+ white-space: nowrap;
519
+ text-overflow: ellipsis;
357
520
  }
358
521
 
359
522
  &--fixed-header th {
@@ -386,6 +549,29 @@ export default {
386
549
  &--active {opacity: 0.7;}
387
550
  }
388
551
 
552
+ // Resizable columns.
553
+ &__header--resizable {position: relative;}
554
+ &__col-resizer {
555
+ position: absolute;
556
+ right: -5px;
557
+ top: -$tr-border-top;
558
+ bottom: 0;
559
+ width: 10px;
560
+ cursor: col-resize;
561
+ z-index: 1;
562
+
563
+ &:before {
564
+ content: '';
565
+ border-right: $border;
566
+ position: absolute;
567
+ left: 50%;
568
+ top: 0;
569
+ bottom: 0;
570
+ transform: translateX(-50%);
571
+ }
572
+ &--hover:before, &--active:before {border-right-width: 2px;}
573
+ }
574
+
389
575
  // Progress bar when loading.
390
576
  &__progress-bar:nth-child(odd) {background: none;}
391
577
  &__progress-bar td {padding: 0;height: 1px;}
@@ -405,7 +591,7 @@ export default {
405
591
 
406
592
  // Table body.
407
593
  // ------------------------------------------------------
408
- tbody tr {border-top: 1px solid rgba(0, 0, 0, 0.06);}
594
+ tbody tr {border-top: $tr-border-top solid rgba(0, 0, 0, 0.06);}
409
595
  // Don't apply built-in bg color if a bg color is already found on a tr.
410
596
  tbody tr:nth-child(odd):not(.no-data):not([class*="--bg"]) {background-color: $table-tr-odd-color;}
411
597
  tbody .w-table__row:hover:not(.no-data):not([class*="--bg"]) {background-color: $table-tr-hover-color;}
@@ -427,12 +613,23 @@ export default {
427
613
  &__header:first-child, &__cell:first-child {padding-left: 2 * $base-increment;}
428
614
  &__header:last-child, &__cell:last-child {padding-right: 2 * $base-increment;}
429
615
 
616
+ &--resizable-cols &__cell {
617
+ position: relative;
618
+
619
+ &, & * {
620
+ overflow: hidden;
621
+ // white-space: nowrap; // If you only want the content cell on a single line.
622
+ text-overflow: ellipsis;
623
+ }
624
+ }
625
+
430
626
  .no-data &__cell {
431
627
  background-color: rgba(255, 255, 255, 0.2);
432
628
  padding: (2 * $base-increment) $base-increment;
433
629
  }
434
630
  }
435
631
 
632
+ // Mobile layout.
436
633
  .w-table--mobile {
437
634
  thead {display: none;}
438
635
  td {display: block;}
@@ -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: {
@@ -1,6 +1,7 @@
1
1
  @font-face {
2
2
  font-family: 'wave-ui';
3
- src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
3
+ // Base64 encoded woff font (https://hellogreg.github.io/woff2base).
4
+ src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff');
4
5
  font-weight: normal;
5
6
  font-style: normal;
6
7
  }
@@ -41,3 +42,5 @@
41
42
  .wi-star:before {content: "\e013";}
42
43
  .wi-search:before {content: "\e014";}
43
44
  .wi-wave:before {content: "\e015";}
45
+ .wi-file:before {content: "\e016";}
46
+ .wi-spinner:before {content: "\e017";}