vue2-excel-grid 1.3.0 → 1.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-excel-grid",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Excel-style grid component for Vue 2.6.14 with virtual scrolling",
5
5
  "license": "MIT",
6
6
  "author": "Fozilbek Raimberdiyev",
@@ -3,6 +3,7 @@
3
3
  :class="dropdownOnly ? 'eg-select-dropdown' : 'eg-select-wrapper'"
4
4
  :style="dropdownOnly ? overlayStyle : null"
5
5
  @mousedown="onRootMouseDown"
6
+ @scroll.passive="dropdownOnly && _hideTooltip()"
6
7
  >
7
8
  <!-- Standalone mode: trigger button -->
8
9
  <div
@@ -15,7 +16,8 @@
15
16
  <span
16
17
  class="eg-select-trigger-label"
17
18
  :class="{ 'eg-select-placeholder': !selectedOpt }"
18
- :title="selectedOpt ? selectedOpt.tooltip : null"
19
+ @mouseenter="_showTooltip(selectedOpt && selectedOpt.tooltip, $event.currentTarget)"
20
+ @mouseleave="_hideTooltip"
19
21
  >
20
22
  {{ selectedOpt ? selectedOpt.label : placeholder }}
21
23
  </span>
@@ -46,9 +48,10 @@
46
48
  'eg-select-option--active': String(opt.value) === String(value),
47
49
  'eg-select-option--highlighted': idx === highlight
48
50
  }"
49
- :title="opt.tooltip"
50
51
  @mousedown.prevent="pickOption(opt)"
51
52
  @mouseover="highlight = idx"
53
+ @mouseenter="_showTooltip(opt.tooltip, $event.currentTarget)"
54
+ @mouseleave="_hideTooltip"
52
55
  >
53
56
  <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>
54
57
  {{ opt.label }}
@@ -61,6 +64,7 @@
61
64
  v-else-if="open"
62
65
  class="eg-select-dropdown eg-select-dropdown--floating"
63
66
  @mousedown.stop.prevent
67
+ @scroll.passive="_hideTooltip"
64
68
  >
65
69
  <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>
66
70
  <input
@@ -84,15 +88,18 @@
84
88
  'eg-select-option--active': String(opt.value) === String(value),
85
89
  'eg-select-option--highlighted': idx === highlight
86
90
  }"
87
- :title="opt.tooltip"
88
91
  @mousedown.prevent="pickOption(opt)"
89
92
  @mouseover="highlight = idx"
93
+ @mouseenter="_showTooltip(opt.tooltip, $event.currentTarget)"
94
+ @mouseleave="_hideTooltip"
90
95
  >
91
96
  <span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>
92
97
  {{ opt.label }}
93
98
  </div>
94
99
  <div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
95
100
  </div>
101
+
102
+ <div v-if="hoverTooltip" class="eg-option-tooltip" :style="hoverTooltipStyle">{{ hoverTooltip.text }}</div>
96
103
  </div>
97
104
  </template>
98
105
 
@@ -132,7 +139,12 @@ export default {
132
139
  // True right after Arrow/Page keyboard navigation — lets Space commit the
133
140
  // highlighted option instead of typing a literal space into the search box.
134
141
  // Reset on every text edit so normal multi-word search typing is unaffected.
135
- _navigatedByKeyboard: false
142
+ _navigatedByKeyboard: false,
143
+ // { text, rect } while a themed option tooltip should be visible — see
144
+ // _showTooltip/_hideTooltip. rect is a snapshot from getBoundingClientRect()
145
+ // at the moment the hover delay elapsed.
146
+ hoverTooltip: null,
147
+ _tooltipTimer: null
136
148
  }
137
149
  },
138
150
  computed: {
@@ -171,6 +183,15 @@ export default {
171
183
  maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + 'px',
172
184
  zIndex: 9999
173
185
  }
186
+ },
187
+ hoverTooltipStyle() {
188
+ if (!this.hoverTooltip) return {}
189
+ const { rect, text } = this.hoverTooltip
190
+ const estH = 20 + Math.ceil(text.length / 40) * 16
191
+ const spaceBelow = window.innerHeight - rect.bottom
192
+ const top = spaceBelow >= estH + 8 ? rect.bottom + 6 : rect.top - estH - 6
193
+ const left = Math.min(rect.left, window.innerWidth - 280 - 8)
194
+ return { position: 'fixed', top: top + 'px', left: Math.max(8, left) + 'px', zIndex: 10000 }
174
195
  }
175
196
  },
176
197
  watch: {
@@ -194,6 +215,7 @@ export default {
194
215
  }
195
216
  },
196
217
  beforeDestroy() {
218
+ this._clearTooltipTimer()
197
219
  if (!this.dropdownOnly) {
198
220
  document.removeEventListener('click', this._onDocClick)
199
221
  }
@@ -218,6 +240,7 @@ export default {
218
240
  }
219
241
  },
220
242
  onSearch(e) {
243
+ this._hideTooltip()
221
244
  this.searchQuery = e.target.value
222
245
  this.highlight = 0
223
246
  this._navigatedByKeyboard = false
@@ -332,6 +355,27 @@ export default {
332
355
  if (!this.open) return
333
356
  if (this.$el && this.$el.contains(e.target)) return
334
357
  this.open = false
358
+ },
359
+ // Shared by option rows and the closed trigger label — shows a themed
360
+ // popover (see .eg-option-tooltip) after a short hover delay, instead of
361
+ // the browser's native title tooltip.
362
+ _showTooltip(text, el) {
363
+ this._clearTooltipTimer()
364
+ if (!text) return
365
+ const rect = el.getBoundingClientRect()
366
+ this._tooltipTimer = setTimeout(() => {
367
+ this.hoverTooltip = { text, rect }
368
+ }, 400)
369
+ },
370
+ _hideTooltip() {
371
+ this._clearTooltipTimer()
372
+ this.hoverTooltip = null
373
+ },
374
+ _clearTooltipTimer() {
375
+ if (this._tooltipTimer) {
376
+ clearTimeout(this._tooltipTimer)
377
+ this._tooltipTimer = null
378
+ }
335
379
  }
336
380
  }
337
381
  }
@@ -490,4 +534,24 @@ export default {
490
534
  display: inline-block;
491
535
  margin-top: 3px;
492
536
  }
537
+
538
+ /* Themed option/trigger tooltip — same recipe as .eg-select-dropdown
539
+ (cell-bg/border/radius) plus .eg-validation-tooltip's shadow, so it reads
540
+ as part of the grid's own chrome in every built-in theme instead of a
541
+ generic OS tooltip box. */
542
+ .eg-option-tooltip {
543
+ position: fixed;
544
+ max-width: 280px;
545
+ background: var(--eg-color-cell-bg, var(--eg-color-cell-bg-default));
546
+ border: 1px solid var(--eg-color-border, var(--eg-color-border-default));
547
+ border-radius: var(--eg-radius, var(--eg-radius-default));
548
+ box-shadow: var(--eg-shadow-tooltip, var(--eg-shadow-tooltip-default));
549
+ color: var(--eg-color-text, var(--eg-color-text-default));
550
+ font-size: var(--eg-font-size, var(--eg-font-size-default));
551
+ padding: 6px 10px;
552
+ pointer-events: none;
553
+ white-space: normal;
554
+ word-break: break-word;
555
+ z-index: 10000;
556
+ }
493
557
  </style>
@@ -3,6 +3,7 @@
3
3
  :class="dropdownOnly ? 'eg-select-dropdown' : 'eg-select-wrapper'"
4
4
  :style="dropdownOnly ? overlayStyle : null"
5
5
  @mousedown="onRootMouseDown"
6
+ @scroll.passive="dropdownOnly && _hideTooltip()"
6
7
  >
7
8
  <!-- Standalone mode: trigger button -->
8
9
  <div
@@ -14,7 +15,8 @@
14
15
  <span
15
16
  class="eg-select-trigger-label"
16
17
  :class="{ 'eg-select-placeholder': selectedLabel == null }"
17
- :title="selectedTooltip"
18
+ @mouseenter="_showTooltip(selectedTooltip, $event.currentTarget)"
19
+ @mouseleave="_hideTooltip"
18
20
  >
19
21
  {{ selectedLabel != null ? selectedLabel : placeholder }}
20
22
  </span>
@@ -46,9 +48,10 @@
46
48
  'eg-select-option--highlighted': idx === highlight
47
49
  }"
48
50
  :style="!searchQuery ? { paddingLeft: (12 + opt.depth * 16) + 'px' } : null"
49
- :title="opt.tooltip"
50
51
  @mousedown.prevent="pickOption(opt)"
51
52
  @mouseover="highlight = idx"
53
+ @mouseenter="_showTooltip(opt.tooltip, $event.currentTarget)"
54
+ @mouseleave="_hideTooltip"
52
55
  >
53
56
  <span
54
57
  v-if="!searchQuery"
@@ -67,6 +70,7 @@
67
70
  v-else-if="open"
68
71
  class="eg-select-dropdown eg-select-dropdown--floating"
69
72
  @mousedown.stop.prevent
73
+ @scroll.passive="_hideTooltip"
70
74
  >
71
75
  <div v-if="enableSearch" class="eg-select-search" @mousedown.stop>
72
76
  <input
@@ -91,9 +95,10 @@
91
95
  'eg-select-option--highlighted': idx === highlight
92
96
  }"
93
97
  :style="!searchQuery ? { paddingLeft: (12 + opt.depth * 16) + 'px' } : null"
94
- :title="opt.tooltip"
95
98
  @mousedown.prevent="pickOption(opt)"
96
99
  @mouseover="highlight = idx"
100
+ @mouseenter="_showTooltip(opt.tooltip, $event.currentTarget)"
101
+ @mouseleave="_hideTooltip"
97
102
  >
98
103
  <span
99
104
  v-if="!searchQuery"
@@ -106,6 +111,8 @@
106
111
  </div>
107
112
  <div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
108
113
  </div>
114
+
115
+ <div v-if="hoverTooltip" class="eg-option-tooltip" :style="hoverTooltipStyle">{{ hoverTooltip.text }}</div>
109
116
  </div>
110
117
  </template>
111
118
 
@@ -144,7 +151,12 @@ export default {
144
151
  // True right after Arrow/Page keyboard navigation — lets Space commit the
145
152
  // highlighted option instead of typing a literal space into the search box.
146
153
  // Reset on every text edit so normal multi-word search typing is unaffected.
147
- _navigatedByKeyboard: false
154
+ _navigatedByKeyboard: false,
155
+ // { text, rect } while a themed option tooltip should be visible — see
156
+ // _showTooltip/_hideTooltip. rect is a snapshot from getBoundingClientRect()
157
+ // at the moment the hover delay elapsed.
158
+ hoverTooltip: null,
159
+ _tooltipTimer: null
148
160
  }
149
161
  },
150
162
  computed: {
@@ -195,6 +207,15 @@ export default {
195
207
  maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + 'px',
196
208
  zIndex: 9999
197
209
  }
210
+ },
211
+ hoverTooltipStyle() {
212
+ if (!this.hoverTooltip) return {}
213
+ const { rect, text } = this.hoverTooltip
214
+ const estH = 20 + Math.ceil(text.length / 40) * 16
215
+ const spaceBelow = window.innerHeight - rect.bottom
216
+ const top = spaceBelow >= estH + 8 ? rect.bottom + 6 : rect.top - estH - 6
217
+ const left = Math.min(rect.left, window.innerWidth - 280 - 8)
218
+ return { position: 'fixed', top: top + 'px', left: Math.max(8, left) + 'px', zIndex: 10000 }
198
219
  }
199
220
  },
200
221
  watch: {
@@ -216,6 +237,7 @@ export default {
216
237
  }
217
238
  },
218
239
  beforeDestroy() {
240
+ this._clearTooltipTimer()
219
241
  if (!this.dropdownOnly) {
220
242
  document.removeEventListener('click', this._onDocClick)
221
243
  }
@@ -259,6 +281,7 @@ export default {
259
281
  this.highlight = newIdx >= 0 ? newIdx : this.highlight
260
282
  },
261
283
  onSearch(e) {
284
+ this._hideTooltip()
262
285
  this.searchQuery = e.target.value
263
286
  this.highlight = 0
264
287
  this._navigatedByKeyboard = false
@@ -438,6 +461,27 @@ export default {
438
461
  }
439
462
  }
440
463
  return null
464
+ },
465
+ // Shared by option rows and the closed trigger label — shows a themed
466
+ // popover (see .eg-option-tooltip) after a short hover delay, instead of
467
+ // the browser's native title tooltip.
468
+ _showTooltip(text, el) {
469
+ this._clearTooltipTimer()
470
+ if (!text) return
471
+ const rect = el.getBoundingClientRect()
472
+ this._tooltipTimer = setTimeout(() => {
473
+ this.hoverTooltip = { text, rect }
474
+ }, 400)
475
+ },
476
+ _hideTooltip() {
477
+ this._clearTooltipTimer()
478
+ this.hoverTooltip = null
479
+ },
480
+ _clearTooltipTimer() {
481
+ if (this._tooltipTimer) {
482
+ clearTimeout(this._tooltipTimer)
483
+ this._tooltipTimer = null
484
+ }
441
485
  }
442
486
  }
443
487
  }
@@ -7661,6 +7661,21 @@ export default {
7661
7661
  color: var(--eg-color-text-muted, var(--eg-color-text-muted-default));
7662
7662
  user-select: none;
7663
7663
  }
7664
+ .eg-option-tooltip {
7665
+ position: fixed;
7666
+ max-width: 280px;
7667
+ background: var(--eg-color-cell-bg, var(--eg-color-cell-bg-default));
7668
+ border: 1px solid var(--eg-color-border, var(--eg-color-border-default));
7669
+ border-radius: var(--eg-radius, var(--eg-radius-default));
7670
+ box-shadow: var(--eg-shadow-tooltip, var(--eg-shadow-tooltip-default));
7671
+ color: var(--eg-color-text, var(--eg-color-text-default));
7672
+ font-size: var(--eg-font-size, var(--eg-font-size-default));
7673
+ padding: 6px 10px;
7674
+ pointer-events: none;
7675
+ white-space: normal;
7676
+ word-break: break-word;
7677
+ z-index: 10000;
7678
+ }
7664
7679
  /* Grid `size` overrides — must live in this *unscoped* style block, not the
7665
7680
  scoped one above: .eg-select-option is rendered by the separate EgSelect /
7666
7681
  EgTreeSelect components, so Vue's scoped-CSS attribute (added only to this