vue2-excel-grid 1.3.0 → 1.4.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/package.json
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -917,6 +917,7 @@ import EgSelect from './EgSelect.vue'
|
|
|
917
917
|
import EgTreeSelect from './EgTreeSelect.vue'
|
|
918
918
|
import EgDatePicker from './EgDatePicker.vue'
|
|
919
919
|
import { parseDateValue, formatDate } from '../utils/dateUtils.js'
|
|
920
|
+
import { formatNumber as _formatNumber } from '../utils/numberFormat.js'
|
|
920
921
|
import { Mask, MaskInput } from '../utils/mask.js'
|
|
921
922
|
|
|
922
923
|
const SIZE_CONFIG = {
|
|
@@ -6116,6 +6117,15 @@ export default {
|
|
|
6116
6117
|
this.$refs.wrapper && this.$refs.wrapper.focus()
|
|
6117
6118
|
},
|
|
6118
6119
|
|
|
6120
|
+
// Public, ref-callable: formats a number with a custom thousands
|
|
6121
|
+
// separator (decimal point stays '.', no forced/padded decimals). Any
|
|
6122
|
+
// value that isn't a finite number — already-formatted text like
|
|
6123
|
+
// "6 000 000,00", arbitrary strings, etc. — is returned unchanged, so
|
|
6124
|
+
// hosts can call this on raw cell values without checking the type first.
|
|
6125
|
+
formatNumber(value, separator) {
|
|
6126
|
+
return _formatNumber(value, separator)
|
|
6127
|
+
},
|
|
6128
|
+
|
|
6119
6129
|
toggleLock() {
|
|
6120
6130
|
this.internalLocked = !this.internalLocked
|
|
6121
6131
|
this.$emit('update:locked', this.internalLocked)
|
|
@@ -6866,6 +6876,13 @@ export default {
|
|
|
6866
6876
|
}
|
|
6867
6877
|
.eg-cell-align-right { justify-content: flex-end; }
|
|
6868
6878
|
.eg-cell-align-center { justify-content: center; }
|
|
6879
|
+
/* .eg-cell-value is flex:1 (v1.1.126, to shrink/ellipsize correctly), so it
|
|
6880
|
+
always fills the cell's full width — .eg-cell-align-*'s justify-content
|
|
6881
|
+
above has no leftover space left to act on and never visibly moves it.
|
|
6882
|
+
Same root cause .eg-cell-action-wrap already works around below; apply the
|
|
6883
|
+
same fix here via text-align on the value span itself. */
|
|
6884
|
+
.eg-cell-align-right .eg-cell-value { text-align: right; }
|
|
6885
|
+
.eg-cell-align-center .eg-cell-value { text-align: center; }
|
|
6869
6886
|
.eg-cell--has-suffix.eg-cell-select::after { display: none; }
|
|
6870
6887
|
.eg-cell-select-trigger-wrap {
|
|
6871
6888
|
flex: 1;
|
|
@@ -7661,6 +7678,21 @@ export default {
|
|
|
7661
7678
|
color: var(--eg-color-text-muted, var(--eg-color-text-muted-default));
|
|
7662
7679
|
user-select: none;
|
|
7663
7680
|
}
|
|
7681
|
+
.eg-option-tooltip {
|
|
7682
|
+
position: fixed;
|
|
7683
|
+
max-width: 280px;
|
|
7684
|
+
background: var(--eg-color-cell-bg, var(--eg-color-cell-bg-default));
|
|
7685
|
+
border: 1px solid var(--eg-color-border, var(--eg-color-border-default));
|
|
7686
|
+
border-radius: var(--eg-radius, var(--eg-radius-default));
|
|
7687
|
+
box-shadow: var(--eg-shadow-tooltip, var(--eg-shadow-tooltip-default));
|
|
7688
|
+
color: var(--eg-color-text, var(--eg-color-text-default));
|
|
7689
|
+
font-size: var(--eg-font-size, var(--eg-font-size-default));
|
|
7690
|
+
padding: 6px 10px;
|
|
7691
|
+
pointer-events: none;
|
|
7692
|
+
white-space: normal;
|
|
7693
|
+
word-break: break-word;
|
|
7694
|
+
z-index: 10000;
|
|
7695
|
+
}
|
|
7664
7696
|
/* Grid `size` overrides — must live in this *unscoped* style block, not the
|
|
7665
7697
|
scoped one above: .eg-select-option is rendered by the separate EgSelect /
|
|
7666
7698
|
EgTreeSelect components, so Vue's scoped-CSS attribute (added only to this
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Formats a JS number with a custom thousands separator, keeping the
|
|
2
|
+
// decimal point as '.' and showing only as many decimal digits as the
|
|
3
|
+
// number itself already has (no padding, no rounding). Any input that
|
|
4
|
+
// isn't a finite number — an already-formatted string like "6 000 000,00",
|
|
5
|
+
// arbitrary text, null, NaN, etc. — is returned unchanged, so callers can
|
|
6
|
+
// pass through mixed number/string cell values safely without pre-checking
|
|
7
|
+
// the type themselves.
|
|
8
|
+
export function formatNumber(value, separator) {
|
|
9
|
+
if (typeof value !== 'number' || !isFinite(value)) return value
|
|
10
|
+
const sep = separator == null ? '' : String(separator)
|
|
11
|
+
const sign = value < 0 ? '-' : ''
|
|
12
|
+
const [intPart, fracPart] = Math.abs(value).toString().split('.')
|
|
13
|
+
const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, sep)
|
|
14
|
+
return sign + grouped + (fracPart ? '.' + fracPart : '')
|
|
15
|
+
}
|