vue2-excel-grid 1.1.13 → 1.1.16
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/dist/vue2-excel-grid.esm.js +1633 -644
- package/dist/vue2-excel-grid.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/EgCheckbox.vue +96 -0
- package/src/components/EgSelect.vue +377 -0
- package/src/components/EgTreeSelect.vue +357 -0
- package/src/components/ExcelGrid.vue +88 -437
- package/src/index.js +7 -1
|
@@ -220,23 +220,19 @@
|
|
|
220
220
|
>
|
|
221
221
|
<div v-if="isMergedAway(ri, ci)" style="display:none"></div>
|
|
222
222
|
<template v-else-if="isEditing(ri, ci)">
|
|
223
|
-
<slot v-if="
|
|
223
|
+
<slot v-if="hasCellSlot(col)" :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :isEditing="true" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
224
|
+
<slot v-else-if="hasLegacyEditSlot(col)" :name="'edit-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
224
225
|
<input v-else ref="editInput" class="eg-cell-input" v-model="editValue" @blur="commitEdit" @keydown.stop="onEditKeyDown" />
|
|
225
226
|
</template>
|
|
226
227
|
<template v-else-if="col.type === 'checkbox'">
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
:disabled="!isCellEditable(ri, ci)"
|
|
233
|
-
@change="onCheckboxChange($event, ri, ci)"
|
|
234
|
-
/>
|
|
235
|
-
<span class="eg-checkbox-box"></span>
|
|
236
|
-
</label>
|
|
228
|
+
<eg-checkbox
|
|
229
|
+
:value="isCheckboxChecked(ri, ci)"
|
|
230
|
+
:disabled="!isCellEditable(ri, ci)"
|
|
231
|
+
@change="(v) => setCheckboxValue(ri, ci, v)"
|
|
232
|
+
/>
|
|
237
233
|
</template>
|
|
238
234
|
<template v-else>
|
|
239
|
-
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)">
|
|
235
|
+
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :isEditing="false" :done="null" :cancel="null">
|
|
240
236
|
{{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}
|
|
241
237
|
</slot>
|
|
242
238
|
</template>
|
|
@@ -278,7 +274,8 @@
|
|
|
278
274
|
>
|
|
279
275
|
<div v-if="isMergedAway(ri, ci)" style="display:none"></div>
|
|
280
276
|
<template v-else-if="isEditing(ri, ci)">
|
|
281
|
-
<slot v-if="
|
|
277
|
+
<slot v-if="hasCellSlot(col)" :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :isEditing="true" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
278
|
+
<slot v-else-if="hasLegacyEditSlot(col)" :name="'edit-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
282
279
|
<input
|
|
283
280
|
v-else
|
|
284
281
|
ref="editInput"
|
|
@@ -289,19 +286,14 @@
|
|
|
289
286
|
/>
|
|
290
287
|
</template>
|
|
291
288
|
<template v-else-if="col.type === 'checkbox'">
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
:disabled="!isCellEditable(ri, ci)"
|
|
298
|
-
@change="onCheckboxChange($event, ri, ci)"
|
|
299
|
-
/>
|
|
300
|
-
<span class="eg-checkbox-box"></span>
|
|
301
|
-
</label>
|
|
289
|
+
<eg-checkbox
|
|
290
|
+
:value="isCheckboxChecked(ri, ci)"
|
|
291
|
+
:disabled="!isCellEditable(ri, ci)"
|
|
292
|
+
@change="(v) => setCheckboxValue(ri, ci, v)"
|
|
293
|
+
/>
|
|
302
294
|
</template>
|
|
303
295
|
<template v-else>
|
|
304
|
-
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)">
|
|
296
|
+
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, ci)" :isEditing="false" :done="null" :cancel="null">
|
|
305
297
|
{{ isSelectLikeCol(col) ? getSelectLabel(ci, getCellValue(ri, ci)) : getCellValue(ri, ci) }}
|
|
306
298
|
</slot>
|
|
307
299
|
</template>
|
|
@@ -336,23 +328,19 @@
|
|
|
336
328
|
>
|
|
337
329
|
<div v-if="isMergedAway(ri, col._flatIndex)" style="display:none"></div>
|
|
338
330
|
<template v-else-if="isEditing(ri, col._flatIndex)">
|
|
339
|
-
<slot v-if="
|
|
331
|
+
<slot v-if="hasCellSlot(col)" :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, col._flatIndex)" :isEditing="true" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
332
|
+
<slot v-else-if="hasLegacyEditSlot(col)" :name="'edit-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, col._flatIndex)" :done="(v) => commitEditSlot(v)" :cancel="cancelEdit" />
|
|
340
333
|
<input v-else ref="editInput" class="eg-cell-input" v-model="editValue" @blur="commitEdit" @keydown.stop="onEditKeyDown" />
|
|
341
334
|
</template>
|
|
342
335
|
<template v-else-if="col.type === 'checkbox'">
|
|
343
|
-
<
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
:disabled="!isCellEditable(ri, col._flatIndex)"
|
|
349
|
-
@change="onCheckboxChange($event, ri, col._flatIndex)"
|
|
350
|
-
/>
|
|
351
|
-
<span class="eg-checkbox-box"></span>
|
|
352
|
-
</label>
|
|
336
|
+
<eg-checkbox
|
|
337
|
+
:value="isCheckboxChecked(ri, col._flatIndex)"
|
|
338
|
+
:disabled="!isCellEditable(ri, col._flatIndex)"
|
|
339
|
+
@change="(v) => setCheckboxValue(ri, col._flatIndex, v)"
|
|
340
|
+
/>
|
|
353
341
|
</template>
|
|
354
342
|
<template v-else>
|
|
355
|
-
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, col._flatIndex)">
|
|
343
|
+
<slot :name="'cell-' + col.key" :row="_dataRaw[ri]" :rowIndex="ri" :column="col" :value="getCellValue(ri, col._flatIndex)" :isEditing="false" :done="null" :cancel="null">
|
|
356
344
|
{{ isSelectLikeCol(col) ? getSelectLabel(col._flatIndex, getCellValue(ri, col._flatIndex)) : getCellValue(ri, col._flatIndex) }}
|
|
357
345
|
</slot>
|
|
358
346
|
</template>
|
|
@@ -466,49 +454,33 @@
|
|
|
466
454
|
</div>
|
|
467
455
|
</div>
|
|
468
456
|
|
|
469
|
-
<!-- Select dropdown overlay -->
|
|
470
|
-
<
|
|
471
|
-
v-if="selectDropdown"
|
|
472
|
-
|
|
473
|
-
:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
'eg-select-option--highlighted': idx === selectDropdown.highlight
|
|
497
|
-
}"
|
|
498
|
-
:style="selectDropdown.tree && !selectDropdown.searchQuery ? { paddingLeft: (12 + opt.depth * 16) + 'px' } : null"
|
|
499
|
-
@mousedown.prevent="commitSelectValue(opt.value)"
|
|
500
|
-
@mouseover="selectDropdown.highlight = idx"
|
|
501
|
-
>
|
|
502
|
-
<span
|
|
503
|
-
v-if="selectDropdown.tree && !selectDropdown.searchQuery"
|
|
504
|
-
class="eg-tree-toggle"
|
|
505
|
-
:class="{ 'eg-tree-toggle--leaf': !opt.hasChildren }"
|
|
506
|
-
@mousedown.prevent.stop="toggleTreeNode(idx)"
|
|
507
|
-
>{{ opt.hasChildren ? (opt.expanded ? '▾' : '▸') : '' }}</span>
|
|
508
|
-
<span v-if="opt.color" class="eg-select-dot" :style="{ background: opt.color }"></span>
|
|
509
|
-
{{ opt.label }}
|
|
510
|
-
</div>
|
|
511
|
-
</div>
|
|
457
|
+
<!-- Select dropdown overlay (flat select) -->
|
|
458
|
+
<eg-select
|
|
459
|
+
v-if="selectDropdown && !selectDropdown.tree"
|
|
460
|
+
ref="selectDropdownComp"
|
|
461
|
+
:value="selectDropdown.currentValue"
|
|
462
|
+
:options="selectDropdown.rawOptions"
|
|
463
|
+
:enable-search="selectDropdown.enableSearch"
|
|
464
|
+
:trigger-rect="selectDropdown.rect"
|
|
465
|
+
:dropdown-only="true"
|
|
466
|
+
@select="onComponentSelect"
|
|
467
|
+
@close="closeSelectDropdown"
|
|
468
|
+
/>
|
|
469
|
+
<!-- Tree select dropdown overlay -->
|
|
470
|
+
<eg-tree-select
|
|
471
|
+
v-if="selectDropdown && selectDropdown.tree"
|
|
472
|
+
ref="selectDropdownComp"
|
|
473
|
+
:value="selectDropdown.currentValue"
|
|
474
|
+
:options="selectDropdown.rawOptions"
|
|
475
|
+
:value-key="selectDropdown.keys.valueKey"
|
|
476
|
+
:label-key="selectDropdown.keys.labelKey"
|
|
477
|
+
:children-key="selectDropdown.keys.childrenKey"
|
|
478
|
+
:enable-search="selectDropdown.enableSearch"
|
|
479
|
+
:trigger-rect="selectDropdown.rect"
|
|
480
|
+
:dropdown-only="true"
|
|
481
|
+
@select="onComponentSelect"
|
|
482
|
+
@close="closeSelectDropdown"
|
|
483
|
+
/>
|
|
512
484
|
|
|
513
485
|
<!-- Validation error tooltip (cellValidator) -->
|
|
514
486
|
<div
|
|
@@ -542,6 +514,9 @@ import { exportToExcel as _exportToExcel } from '../utils/excelExport.js'
|
|
|
542
514
|
import { importFromFile as _importFromFile } from '../utils/excelImport.js'
|
|
543
515
|
import { createExcelImportWorker, createExcelExportWorker } from '../utils/workerFactory'
|
|
544
516
|
import { getMessages } from '../utils/i18n.js'
|
|
517
|
+
import EgCheckbox from './EgCheckbox.vue'
|
|
518
|
+
import EgSelect from './EgSelect.vue'
|
|
519
|
+
import EgTreeSelect from './EgTreeSelect.vue'
|
|
545
520
|
|
|
546
521
|
const SIZE_CONFIG = {
|
|
547
522
|
small: { row: 22, header: 24, footer: 24 },
|
|
@@ -555,6 +530,8 @@ const OUTLINE_GUTTER = 20
|
|
|
555
530
|
export default {
|
|
556
531
|
name: 'ExcelGrid',
|
|
557
532
|
|
|
533
|
+
components: { EgCheckbox, EgSelect, EgTreeSelect },
|
|
534
|
+
|
|
558
535
|
// v-model → :data + @input
|
|
559
536
|
model: { prop: 'data', event: 'input' },
|
|
560
537
|
|
|
@@ -860,32 +837,6 @@ export default {
|
|
|
860
837
|
}
|
|
861
838
|
},
|
|
862
839
|
|
|
863
|
-
filteredSelectOptions() {
|
|
864
|
-
if (!this.selectDropdown) return []
|
|
865
|
-
const { options, searchQuery, enableSearch, tree, allFlatOptions } = this.selectDropdown
|
|
866
|
-
if (!enableSearch || !searchQuery || !searchQuery.trim()) return options
|
|
867
|
-
const q = searchQuery.trim().toLowerCase()
|
|
868
|
-
const pool = tree && allFlatOptions ? allFlatOptions : options
|
|
869
|
-
return pool.filter(o => String(o.label || '').toLowerCase().includes(q))
|
|
870
|
-
},
|
|
871
|
-
|
|
872
|
-
dropdownStyle() {
|
|
873
|
-
if (!this.selectDropdown) return {}
|
|
874
|
-
const { rect } = this.selectDropdown
|
|
875
|
-
const optionH = this._rowH + 4
|
|
876
|
-
const searchH = this.selectDropdown.enableSearch ? 42 : 0
|
|
877
|
-
const approxHeight = Math.min(this.filteredSelectOptions.length * optionH + 8 + searchH, 320)
|
|
878
|
-
const spaceBelow = window.innerHeight - rect.bottom
|
|
879
|
-
const top = spaceBelow >= approxHeight ? rect.bottom : rect.top - approxHeight
|
|
880
|
-
return {
|
|
881
|
-
position: 'fixed',
|
|
882
|
-
top: top + 'px',
|
|
883
|
-
left: rect.left + 'px',
|
|
884
|
-
minWidth: Math.max(rect.width, 120) + 'px',
|
|
885
|
-
maxWidth: Math.min(360, window.innerWidth - rect.left - 16) + 'px',
|
|
886
|
-
zIndex: 9999
|
|
887
|
-
}
|
|
888
|
-
},
|
|
889
840
|
|
|
890
841
|
invalidTooltipStyle() {
|
|
891
842
|
if (!this.invalidCell || !this.invalidCell.rect) return {}
|
|
@@ -1788,7 +1739,7 @@ export default {
|
|
|
1788
1739
|
this.invalidCell = null
|
|
1789
1740
|
if (this.editingCell) {
|
|
1790
1741
|
const editCol = this.leafCols[this.editingCell.col]
|
|
1791
|
-
if (editCol && this.
|
|
1742
|
+
if (editCol && this._hasCustomEdit(editCol)) {
|
|
1792
1743
|
this.cancelEdit()
|
|
1793
1744
|
} else {
|
|
1794
1745
|
this.commitEdit()
|
|
@@ -1817,7 +1768,7 @@ export default {
|
|
|
1817
1768
|
|
|
1818
1769
|
// Open select dropdown on click if cell was already active (2nd click)
|
|
1819
1770
|
const col = this.leafCols[ci]
|
|
1820
|
-
if (wasActive && col && this.isSelectLikeCol(col) && !this.
|
|
1771
|
+
if (wasActive && col && this.isSelectLikeCol(col) && !this._hasCustomEdit(col) && this.isCellEditable(ri, ci)) {
|
|
1821
1772
|
this.openSelectDropdown(ri, ci, e)
|
|
1822
1773
|
e.preventDefault()
|
|
1823
1774
|
this.isMouseDown = true
|
|
@@ -1856,7 +1807,7 @@ export default {
|
|
|
1856
1807
|
onCellDblClick(ri, ci) {
|
|
1857
1808
|
const col = this.leafCols[ci]
|
|
1858
1809
|
if (!this.isCellEditable(ri, ci)) return
|
|
1859
|
-
if (col && this.isSelectLikeCol(col) && !this.
|
|
1810
|
+
if (col && this.isSelectLikeCol(col) && !this._hasCustomEdit(col)) {
|
|
1860
1811
|
this.openSelectDropdownForActive()
|
|
1861
1812
|
return
|
|
1862
1813
|
}
|
|
@@ -1949,44 +1900,11 @@ export default {
|
|
|
1949
1900
|
// KEYBOARD NAVIGATION (3.2)
|
|
1950
1901
|
// =========================================================
|
|
1951
1902
|
onKeyDown(e) {
|
|
1952
|
-
//
|
|
1903
|
+
// Delegate keyboard navigation to the active dropdown component
|
|
1953
1904
|
if (this.selectDropdown) {
|
|
1954
|
-
const
|
|
1955
|
-
if (
|
|
1956
|
-
|
|
1957
|
-
this.closeSelectDropdown()
|
|
1958
|
-
return
|
|
1959
|
-
}
|
|
1960
|
-
if (key === 'Enter' || key === ' ') {
|
|
1961
|
-
e.preventDefault()
|
|
1962
|
-
const h = this.selectDropdown.highlight
|
|
1963
|
-
if (h >= 0 && this.selectDropdown.options[h]) {
|
|
1964
|
-
this.commitSelectValue(this.selectDropdown.options[h].value)
|
|
1965
|
-
} else {
|
|
1966
|
-
this.closeSelectDropdown()
|
|
1967
|
-
}
|
|
1968
|
-
return
|
|
1969
|
-
}
|
|
1970
|
-
if (key === 'ArrowDown') {
|
|
1971
|
-
e.preventDefault()
|
|
1972
|
-
this.selectDropdown.highlight = Math.min(this.selectDropdown.highlight + 1, this.selectDropdown.options.length - 1)
|
|
1973
|
-
return
|
|
1974
|
-
}
|
|
1975
|
-
if (key === 'ArrowUp') {
|
|
1976
|
-
e.preventDefault()
|
|
1977
|
-
this.selectDropdown.highlight = Math.max(this.selectDropdown.highlight - 1, 0)
|
|
1978
|
-
return
|
|
1979
|
-
}
|
|
1980
|
-
if (this.selectDropdown.tree && !this.selectDropdown.searchQuery && (key === 'ArrowRight' || key === 'ArrowLeft')) {
|
|
1981
|
-
e.preventDefault()
|
|
1982
|
-
const opt = this.selectDropdown.options[this.selectDropdown.highlight]
|
|
1983
|
-
if (opt && opt.hasChildren) {
|
|
1984
|
-
if (key === 'ArrowRight' && !opt.expanded) this.toggleTreeNode(this.selectDropdown.highlight)
|
|
1985
|
-
if (key === 'ArrowLeft' && opt.expanded) this.toggleTreeNode(this.selectDropdown.highlight)
|
|
1986
|
-
}
|
|
1987
|
-
return
|
|
1988
|
-
}
|
|
1989
|
-
return // block other keys while dropdown open
|
|
1905
|
+
const comp = this.$refs.selectDropdownComp
|
|
1906
|
+
if (comp && comp.handleKey) comp.handleKey(e)
|
|
1907
|
+
return
|
|
1990
1908
|
}
|
|
1991
1909
|
|
|
1992
1910
|
if (this.editingCell) return // handled by onEditKeyDown
|
|
@@ -2034,7 +1952,7 @@ export default {
|
|
|
2034
1952
|
if (key === 'F2') {
|
|
2035
1953
|
e.preventDefault()
|
|
2036
1954
|
const activeCol = this.leafCols[this.activeCell.col]
|
|
2037
|
-
if (activeCol && this.isSelectLikeCol(activeCol) && !this.
|
|
1955
|
+
if (activeCol && this.isSelectLikeCol(activeCol) && !this._hasCustomEdit(activeCol)) {
|
|
2038
1956
|
this.openSelectDropdownForActive()
|
|
2039
1957
|
} else if (activeCol && activeCol.type === 'checkbox') {
|
|
2040
1958
|
this.toggleCheckbox(this.activeCell.row, this.activeCell.col)
|
|
@@ -2130,7 +2048,7 @@ export default {
|
|
|
2130
2048
|
if (e.ctrlKey || e.metaKey || e.altKey) return
|
|
2131
2049
|
if (e.key && e.key.length === 1) {
|
|
2132
2050
|
const activeCol = this.leafCols[this.activeCell.col]
|
|
2133
|
-
if (activeCol && this.isSelectLikeCol(activeCol) && !this.
|
|
2051
|
+
if (activeCol && this.isSelectLikeCol(activeCol) && !this._hasCustomEdit(activeCol)) {
|
|
2134
2052
|
e.preventDefault()
|
|
2135
2053
|
this.openSelectDropdownForActive()
|
|
2136
2054
|
return
|
|
@@ -2205,7 +2123,7 @@ export default {
|
|
|
2205
2123
|
const col = this.leafCols[ci]
|
|
2206
2124
|
if (!col || !this.isCellEditable(ri, ci)) return
|
|
2207
2125
|
if (ri < 0 || ri >= this.totalRows) return
|
|
2208
|
-
if (!this.
|
|
2126
|
+
if (!this._hasCustomEdit(col)) {
|
|
2209
2127
|
if (this.isSelectLikeCol(col)) { this.openSelectDropdownForActive(); return }
|
|
2210
2128
|
if (col.type === 'checkbox') return
|
|
2211
2129
|
}
|
|
@@ -2262,9 +2180,19 @@ export default {
|
|
|
2262
2180
|
this.$refs.wrapper && this.$refs.wrapper.focus()
|
|
2263
2181
|
},
|
|
2264
2182
|
|
|
2265
|
-
|
|
2183
|
+
hasCellSlot(col) {
|
|
2184
|
+
return !!(col && col.key && this.$scopedSlots && this.$scopedSlots['cell-' + col.key])
|
|
2185
|
+
},
|
|
2186
|
+
hasLegacyEditSlot(col) {
|
|
2266
2187
|
return !!(col && col.key && this.$scopedSlots && this.$scopedSlots['edit-' + col.key])
|
|
2267
2188
|
},
|
|
2189
|
+
_hasCustomEdit(col) {
|
|
2190
|
+
// For select/treeSelect: cell-{key} slot only customizes display; dropdown still opens normally
|
|
2191
|
+
if (col.type === 'select' || col.type === 'treeSelect') {
|
|
2192
|
+
return this.hasLegacyEditSlot(col)
|
|
2193
|
+
}
|
|
2194
|
+
return this.hasCellSlot(col) || this.hasLegacyEditSlot(col)
|
|
2195
|
+
},
|
|
2268
2196
|
|
|
2269
2197
|
commitEditSlot(value) {
|
|
2270
2198
|
if (!this.editingCell) return
|
|
@@ -2315,10 +2243,6 @@ export default {
|
|
|
2315
2243
|
this.setCheckboxValue(ri, ci, !this.isCheckboxChecked(ri, ci))
|
|
2316
2244
|
},
|
|
2317
2245
|
|
|
2318
|
-
onCheckboxChange(e, ri, ci) {
|
|
2319
|
-
this.setCheckboxValue(ri, ci, e.target.checked)
|
|
2320
|
-
},
|
|
2321
|
-
|
|
2322
2246
|
// =========================================================
|
|
2323
2247
|
// CELL EDITABILITY (3.17)
|
|
2324
2248
|
// =========================================================
|
|
@@ -2398,45 +2322,6 @@ export default {
|
|
|
2398
2322
|
}
|
|
2399
2323
|
},
|
|
2400
2324
|
|
|
2401
|
-
// Flattens a tree of option nodes into a visible list, respecting which
|
|
2402
|
-
// node ids are currently expanded. Each returned entry carries enough
|
|
2403
|
-
// info (depth/hasChildren/expanded/id) to render indentation + a toggle.
|
|
2404
|
-
_treeFlatten(nodes, keys, expandedMap, depth, pathArr) {
|
|
2405
|
-
let out = []
|
|
2406
|
-
;(nodes || []).forEach((node, idx) => {
|
|
2407
|
-
const path = pathArr.concat(idx)
|
|
2408
|
-
const id = path.join('-')
|
|
2409
|
-
const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []
|
|
2410
|
-
const hasChildren = children.length > 0
|
|
2411
|
-
const expanded = hasChildren && !!expandedMap[id]
|
|
2412
|
-
out.push({
|
|
2413
|
-
value: node[keys.valueKey],
|
|
2414
|
-
label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]),
|
|
2415
|
-
color: node.color,
|
|
2416
|
-
id, depth, hasChildren, expanded
|
|
2417
|
-
})
|
|
2418
|
-
if (expanded) {
|
|
2419
|
-
out = out.concat(this._treeFlatten(children, keys, expandedMap, depth + 1, path))
|
|
2420
|
-
}
|
|
2421
|
-
})
|
|
2422
|
-
return out
|
|
2423
|
-
},
|
|
2424
|
-
|
|
2425
|
-
_treeFlattenAll(nodes, keys, depth, result) {
|
|
2426
|
-
;(nodes || []).forEach(node => {
|
|
2427
|
-
const children = Array.isArray(node[keys.childrenKey]) ? node[keys.childrenKey] : []
|
|
2428
|
-
result.push({
|
|
2429
|
-
value: node[keys.valueKey],
|
|
2430
|
-
label: node[keys.labelKey] != null ? node[keys.labelKey] : String(node[keys.valueKey]),
|
|
2431
|
-
color: node.color,
|
|
2432
|
-
id: String(node[keys.valueKey]),
|
|
2433
|
-
depth: 0, hasChildren: false, expanded: false
|
|
2434
|
-
})
|
|
2435
|
-
if (children.length) this._treeFlattenAll(children, keys, depth + 1, result)
|
|
2436
|
-
})
|
|
2437
|
-
return result
|
|
2438
|
-
},
|
|
2439
|
-
|
|
2440
2325
|
_treeFindLabel(nodes, keys, value) {
|
|
2441
2326
|
for (const node of (nodes || [])) {
|
|
2442
2327
|
if (String(node[keys.valueKey]) === value) {
|
|
@@ -2448,28 +2333,9 @@ export default {
|
|
|
2448
2333
|
return null
|
|
2449
2334
|
},
|
|
2450
2335
|
|
|
2451
|
-
// Returns the chain of ancestor node ids leading to `value` (inclusive),
|
|
2452
|
-
// so callers can pre-expand exactly that branch when opening the dropdown.
|
|
2453
|
-
_treeFindPath(nodes, keys, value, pathArr) {
|
|
2454
|
-
for (let idx = 0; idx < (nodes || []).length; idx++) {
|
|
2455
|
-
const node = nodes[idx]
|
|
2456
|
-
const path = pathArr.concat(idx)
|
|
2457
|
-
if (String(node[keys.valueKey]) === value) return [path.join('-')]
|
|
2458
|
-
const children = node[keys.childrenKey]
|
|
2459
|
-
if (Array.isArray(children)) {
|
|
2460
|
-
const sub = this._treeFindPath(children, keys, value, path)
|
|
2461
|
-
if (sub) return [path.join('-')].concat(sub)
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
return null
|
|
2465
|
-
},
|
|
2466
|
-
|
|
2467
2336
|
colOptions(ci) {
|
|
2468
2337
|
const col = this.leafCols[ci]
|
|
2469
2338
|
if (!col || !Array.isArray(col.options)) return []
|
|
2470
|
-
if (col.type === 'treeSelect') {
|
|
2471
|
-
return this._treeFlatten(col.options, this.treeOptionKeys(col), {}, 0, [])
|
|
2472
|
-
}
|
|
2473
2339
|
return col.options.map(o =>
|
|
2474
2340
|
typeof o === 'string' ? { value: o, label: o } : { value: o.value, label: o.label != null ? o.label : String(o.value), color: o.color }
|
|
2475
2341
|
)
|
|
@@ -2491,55 +2357,14 @@ export default {
|
|
|
2491
2357
|
|
|
2492
2358
|
_buildSelectDropdownState(ri, ci, col, currentValue, rect) {
|
|
2493
2359
|
const rectObj = { top: rect.top, bottom: rect.bottom, left: rect.left, width: rect.width }
|
|
2494
|
-
const enableSearch = !!col.enableSearch
|
|
2495
|
-
if (col.type === 'treeSelect') {
|
|
2496
|
-
const keys = this.treeOptionKeys(col)
|
|
2497
|
-
const path = (Array.isArray(col.options) && this._treeFindPath(col.options, keys, currentValue, [])) || []
|
|
2498
|
-
const expandedMap = {}
|
|
2499
|
-
path.forEach(id => { expandedMap[id] = true })
|
|
2500
|
-
const flat = this._treeFlatten(col.options, keys, expandedMap, 0, [])
|
|
2501
|
-
return {
|
|
2502
|
-
row: ri, col: ci, tree: true, keys, expandedMap,
|
|
2503
|
-
options: flat,
|
|
2504
|
-
allFlatOptions: enableSearch ? this._treeFlattenAll(col.options || [], keys, 0, []) : null,
|
|
2505
|
-
currentValue,
|
|
2506
|
-
highlight: flat.findIndex(o => String(o.value) === currentValue),
|
|
2507
|
-
rect: rectObj,
|
|
2508
|
-
enableSearch,
|
|
2509
|
-
searchQuery: ''
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
const opts = this.colOptions(ci)
|
|
2513
2360
|
return {
|
|
2514
|
-
row: ri, col: ci,
|
|
2515
|
-
|
|
2516
|
-
|
|
2361
|
+
row: ri, col: ci,
|
|
2362
|
+
tree: col.type === 'treeSelect',
|
|
2363
|
+
keys: col.type === 'treeSelect' ? this.treeOptionKeys(col) : null,
|
|
2364
|
+
rawOptions: col.options || [],
|
|
2517
2365
|
currentValue,
|
|
2518
|
-
highlight: opts.findIndex(o => String(o.value) === currentValue),
|
|
2519
2366
|
rect: rectObj,
|
|
2520
|
-
enableSearch
|
|
2521
|
-
searchQuery: ''
|
|
2522
|
-
}
|
|
2523
|
-
},
|
|
2524
|
-
|
|
2525
|
-
toggleTreeNode(idx) {
|
|
2526
|
-
if (!this.selectDropdown || !this.selectDropdown.tree) return
|
|
2527
|
-
const opt = this.selectDropdown.options[idx]
|
|
2528
|
-
if (!opt || !opt.hasChildren) return
|
|
2529
|
-
const { row, col, expandedMap, keys } = this.selectDropdown
|
|
2530
|
-
const colDef = this.leafCols[col]
|
|
2531
|
-
if (!colDef) return
|
|
2532
|
-
const nextExpandedMap = { ...expandedMap }
|
|
2533
|
-
if (nextExpandedMap[opt.id]) delete nextExpandedMap[opt.id]
|
|
2534
|
-
else nextExpandedMap[opt.id] = true
|
|
2535
|
-
const flat = this._treeFlatten(colDef.options, keys, nextExpandedMap, 0, [])
|
|
2536
|
-
const newHighlight = flat.findIndex(o => o.id === opt.id)
|
|
2537
|
-
this.selectDropdown = {
|
|
2538
|
-
...this.selectDropdown,
|
|
2539
|
-
row, col,
|
|
2540
|
-
expandedMap: nextExpandedMap,
|
|
2541
|
-
options: flat,
|
|
2542
|
-
highlight: newHighlight >= 0 ? newHighlight : this.selectDropdown.highlight
|
|
2367
|
+
enableSearch: !!col.enableSearch
|
|
2543
2368
|
}
|
|
2544
2369
|
},
|
|
2545
2370
|
|
|
@@ -2550,9 +2375,6 @@ export default {
|
|
|
2550
2375
|
const rect = el.getBoundingClientRect()
|
|
2551
2376
|
const currentValue = String(this.getCellValue(ri, ci) != null ? this.getCellValue(ri, ci) : '')
|
|
2552
2377
|
this.selectDropdown = this._buildSelectDropdownState(ri, ci, col, currentValue, rect)
|
|
2553
|
-
if (col.enableSearch) {
|
|
2554
|
-
this.$nextTick(() => { this.$refs.dropdownSearch && this.$refs.dropdownSearch.focus() })
|
|
2555
|
-
}
|
|
2556
2378
|
},
|
|
2557
2379
|
|
|
2558
2380
|
openSelectDropdownForActive() {
|
|
@@ -2565,13 +2387,10 @@ export default {
|
|
|
2565
2387
|
const rect = el.getBoundingClientRect()
|
|
2566
2388
|
const currentValue = String(this.getCellValue(row, col) != null ? this.getCellValue(row, col) : '')
|
|
2567
2389
|
this.selectDropdown = this._buildSelectDropdownState(row, col, colDef, currentValue, rect)
|
|
2568
|
-
if (colDef.enableSearch) {
|
|
2569
|
-
this.$nextTick(() => { this.$refs.dropdownSearch && this.$refs.dropdownSearch.focus() })
|
|
2570
|
-
}
|
|
2571
2390
|
})
|
|
2572
2391
|
},
|
|
2573
2392
|
|
|
2574
|
-
|
|
2393
|
+
onComponentSelect({ value, label }) {
|
|
2575
2394
|
if (!this.selectDropdown) return
|
|
2576
2395
|
const { row, col } = this.selectDropdown
|
|
2577
2396
|
const colDef = this.leafCols[col]
|
|
@@ -2588,59 +2407,30 @@ export default {
|
|
|
2588
2407
|
newData[row] = { ...oldData[row], [colDef.key]: value }
|
|
2589
2408
|
this._emitDataChange(oldData, newData)
|
|
2590
2409
|
if (typeof colDef.onSelect === 'function') {
|
|
2591
|
-
|
|
2592
|
-
colDef.onSelect({ value, label: _opt ? _opt.label : String(value) }, newData[row], row, colDef)
|
|
2410
|
+
colDef.onSelect({ value, label }, newData[row], row, colDef)
|
|
2593
2411
|
}
|
|
2594
2412
|
}
|
|
2595
2413
|
this.closeSelectDropdown()
|
|
2596
2414
|
this.$refs.wrapper && this.$refs.wrapper.focus()
|
|
2597
2415
|
},
|
|
2598
2416
|
|
|
2599
|
-
onDropdownSearch(e) {
|
|
2600
|
-
if (!this.selectDropdown) return
|
|
2601
|
-
this.selectDropdown.searchQuery = e.target.value
|
|
2602
|
-
this.selectDropdown.highlight = 0
|
|
2603
|
-
},
|
|
2604
|
-
|
|
2605
|
-
onDropdownSearchKeyDown(e) {
|
|
2606
|
-
if (!this.selectDropdown) return
|
|
2607
|
-
const { key } = e
|
|
2608
|
-
if (key === 'Escape') {
|
|
2609
|
-
this.closeSelectDropdown()
|
|
2610
|
-
this.$refs.wrapper && this.$refs.wrapper.focus()
|
|
2611
|
-
} else if (key === 'ArrowDown') {
|
|
2612
|
-
e.preventDefault()
|
|
2613
|
-
this.selectDropdown.highlight = Math.min(
|
|
2614
|
-
(this.selectDropdown.highlight || 0) + 1,
|
|
2615
|
-
this.filteredSelectOptions.length - 1
|
|
2616
|
-
)
|
|
2617
|
-
} else if (key === 'ArrowUp') {
|
|
2618
|
-
e.preventDefault()
|
|
2619
|
-
this.selectDropdown.highlight = Math.max((this.selectDropdown.highlight || 0) - 1, 0)
|
|
2620
|
-
} else if (key === 'Enter') {
|
|
2621
|
-
e.preventDefault()
|
|
2622
|
-
const h = this.selectDropdown.highlight
|
|
2623
|
-
const opts = this.filteredSelectOptions
|
|
2624
|
-
if (h >= 0 && opts[h]) this.commitSelectValue(opts[h].value)
|
|
2625
|
-
}
|
|
2626
|
-
},
|
|
2627
|
-
|
|
2628
2417
|
closeSelectDropdown() {
|
|
2629
2418
|
this.selectDropdown = null
|
|
2630
2419
|
},
|
|
2631
2420
|
|
|
2632
2421
|
_onWindowScrollForDropdown(e) {
|
|
2633
2422
|
if (!this.selectDropdown) return
|
|
2634
|
-
const
|
|
2423
|
+
const comp = this.$refs.selectDropdownComp
|
|
2424
|
+
const dd = comp && comp.$el ? comp.$el : null
|
|
2635
2425
|
if (dd && dd.contains(e.target)) return
|
|
2636
2426
|
this.closeSelectDropdown()
|
|
2637
2427
|
},
|
|
2638
2428
|
|
|
2639
2429
|
_onDocMouseDownForDropdown(e) {
|
|
2640
2430
|
if (!this.selectDropdown) return
|
|
2641
|
-
const
|
|
2431
|
+
const comp = this.$refs.selectDropdownComp
|
|
2432
|
+
const dd = comp && comp.$el ? comp.$el : null
|
|
2642
2433
|
if (dd && dd.contains(e.target)) return
|
|
2643
|
-
// Click outside dropdown — close it
|
|
2644
2434
|
this.closeSelectDropdown()
|
|
2645
2435
|
},
|
|
2646
2436
|
|
|
@@ -3990,122 +3780,6 @@ export default {
|
|
|
3990
3780
|
justify-content: center;
|
|
3991
3781
|
padding: 0 2px;
|
|
3992
3782
|
}
|
|
3993
|
-
.eg-checkbox {
|
|
3994
|
-
position: relative;
|
|
3995
|
-
display: inline-flex;
|
|
3996
|
-
align-items: center;
|
|
3997
|
-
justify-content: center;
|
|
3998
|
-
width: 20px;
|
|
3999
|
-
height: 20px;
|
|
4000
|
-
cursor: pointer;
|
|
4001
|
-
}
|
|
4002
|
-
.eg-checkbox-input {
|
|
4003
|
-
position: absolute;
|
|
4004
|
-
inset: 0;
|
|
4005
|
-
width: 100%;
|
|
4006
|
-
height: 100%;
|
|
4007
|
-
margin: 0;
|
|
4008
|
-
opacity: 0;
|
|
4009
|
-
cursor: pointer;
|
|
4010
|
-
}
|
|
4011
|
-
.eg-checkbox-box {
|
|
4012
|
-
position: relative;
|
|
4013
|
-
display: block;
|
|
4014
|
-
width: 19px;
|
|
4015
|
-
height: 19px;
|
|
4016
|
-
box-sizing: border-box;
|
|
4017
|
-
background-color: #fff;
|
|
4018
|
-
background-repeat: no-repeat;
|
|
4019
|
-
background-position: center;
|
|
4020
|
-
background-size: 0 0;
|
|
4021
|
-
border: 1.5px solid #c2c8d1;
|
|
4022
|
-
border-radius: 4px;
|
|
4023
|
-
transition: background-color .12s ease, border-color .12s ease, background-size .12s ease;
|
|
4024
|
-
}
|
|
4025
|
-
.eg-checkbox:hover .eg-checkbox-box {
|
|
4026
|
-
border-color: #1a73e8;
|
|
4027
|
-
}
|
|
4028
|
-
.eg-checkbox-input:checked + .eg-checkbox-box {
|
|
4029
|
-
background-color: #1a73e8;
|
|
4030
|
-
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTMuNSA4LjNsMyAzIDYtNi42IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==");
|
|
4031
|
-
background-size: 70% 70%;
|
|
4032
|
-
border-color: #1a73e8;
|
|
4033
|
-
}
|
|
4034
|
-
.eg-checkbox-input:focus-visible + .eg-checkbox-box {
|
|
4035
|
-
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2);
|
|
4036
|
-
}
|
|
4037
|
-
.eg-checkbox--disabled,
|
|
4038
|
-
.eg-checkbox--disabled .eg-checkbox-input {
|
|
4039
|
-
cursor: default;
|
|
4040
|
-
}
|
|
4041
|
-
.eg-checkbox-input:disabled + .eg-checkbox-box {
|
|
4042
|
-
background-color: #f0f1f3;
|
|
4043
|
-
border-color: #dadde2;
|
|
4044
|
-
}
|
|
4045
|
-
.eg-checkbox-input:disabled:checked + .eg-checkbox-box {
|
|
4046
|
-
background-color: #b9c2ce;
|
|
4047
|
-
border-color: #b9c2ce;
|
|
4048
|
-
}
|
|
4049
|
-
|
|
4050
|
-
.eg-select-dropdown {
|
|
4051
|
-
background: white;
|
|
4052
|
-
border: 1px solid #d0d0d0;
|
|
4053
|
-
border-radius: 4px;
|
|
4054
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
4055
|
-
overflow-y: auto;
|
|
4056
|
-
max-height: 320px;
|
|
4057
|
-
padding: 4px 0;
|
|
4058
|
-
}
|
|
4059
|
-
.eg-select-search {
|
|
4060
|
-
padding: 6px 8px;
|
|
4061
|
-
border-bottom: 1px solid #ebebeb;
|
|
4062
|
-
position: sticky;
|
|
4063
|
-
top: 0;
|
|
4064
|
-
background: #fff;
|
|
4065
|
-
z-index: 1;
|
|
4066
|
-
}
|
|
4067
|
-
.eg-select-search-input {
|
|
4068
|
-
width: 100%;
|
|
4069
|
-
box-sizing: border-box;
|
|
4070
|
-
border: 1px solid #d1d5db;
|
|
4071
|
-
border-radius: 4px;
|
|
4072
|
-
padding: 4px 8px;
|
|
4073
|
-
font-size: 13px;
|
|
4074
|
-
outline: none;
|
|
4075
|
-
color: #333;
|
|
4076
|
-
background: #fff;
|
|
4077
|
-
font-family: inherit;
|
|
4078
|
-
}
|
|
4079
|
-
.eg-select-search-input:focus {
|
|
4080
|
-
border-color: #1a73e8;
|
|
4081
|
-
box-shadow: 0 0 0 2px rgba(26,115,232,0.15);
|
|
4082
|
-
}
|
|
4083
|
-
|
|
4084
|
-
.eg-select-option {
|
|
4085
|
-
display: flex;
|
|
4086
|
-
align-items: flex-start;
|
|
4087
|
-
gap: 8px;
|
|
4088
|
-
padding: 6px 12px;
|
|
4089
|
-
font-size: 13px;
|
|
4090
|
-
cursor: pointer;
|
|
4091
|
-
white-space: normal;
|
|
4092
|
-
word-break: break-word;
|
|
4093
|
-
user-select: none;
|
|
4094
|
-
color: #333;
|
|
4095
|
-
}
|
|
4096
|
-
.eg-select-option:hover,
|
|
4097
|
-
.eg-select-option--highlighted {
|
|
4098
|
-
background: #f0f4ff;
|
|
4099
|
-
}
|
|
4100
|
-
.eg-select-option--active {
|
|
4101
|
-
font-weight: 600;
|
|
4102
|
-
color: #1a73e8;
|
|
4103
|
-
}
|
|
4104
|
-
.eg-select-option--active.eg-select-option--highlighted,
|
|
4105
|
-
.eg-select-option--active:hover {
|
|
4106
|
-
background: #e8f0fe;
|
|
4107
|
-
}
|
|
4108
|
-
|
|
4109
3783
|
.eg-validation-tooltip {
|
|
4110
3784
|
background: #fff0f0;
|
|
4111
3785
|
border: 1px solid #e53935;
|
|
@@ -4118,29 +3792,6 @@ export default {
|
|
|
4118
3792
|
pointer-events: none;
|
|
4119
3793
|
}
|
|
4120
3794
|
|
|
4121
|
-
.eg-select-dot {
|
|
4122
|
-
width: 9px;
|
|
4123
|
-
height: 9px;
|
|
4124
|
-
border-radius: 50%;
|
|
4125
|
-
flex-shrink: 0;
|
|
4126
|
-
display: inline-block;
|
|
4127
|
-
}
|
|
4128
|
-
|
|
4129
|
-
.eg-tree-toggle {
|
|
4130
|
-
display: inline-flex;
|
|
4131
|
-
align-items: center;
|
|
4132
|
-
justify-content: center;
|
|
4133
|
-
width: 14px;
|
|
4134
|
-
height: 14px;
|
|
4135
|
-
flex-shrink: 0;
|
|
4136
|
-
font-size: 10px;
|
|
4137
|
-
color: #888;
|
|
4138
|
-
margin-top: 1px;
|
|
4139
|
-
}
|
|
4140
|
-
.eg-tree-toggle--leaf {
|
|
4141
|
-
visibility: hidden;
|
|
4142
|
-
}
|
|
4143
|
-
|
|
4144
3795
|
/* ===== ROW TRANSITION ===== */
|
|
4145
3796
|
.eg-row-enter-active,
|
|
4146
3797
|
.eg-row-leave-active {
|