webcake-ui-kit 1.2.13 → 1.2.15
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 +2 -2
- package/src/components/date-picker/date-picker.css +3 -1
- package/src/components/dropdown/Dropdown.vue +2 -2
- package/src/components/file-type-icon/FileTypeIcon.vue +261 -0
- package/src/components/file-type-icon/file-type-icon.css +48 -0
- package/src/components/select-option/SelectOption.vue +2 -1
- package/src/components/select-option/select_option.css +0 -62
- package/src/components/select-option/select_option_sub.css +61 -0
- package/src/components/{table-v2 → table}/ColumnDragHandle.vue +2 -2
- package/src/components/table/Table.vue +250 -772
- package/src/components/{table-v2 → table}/TableColGroup.vue +2 -2
- package/src/components/{table-v2 → table}/TableEmpty.vue +2 -2
- package/src/components/{table-v2 → table}/TableLoading.vue +3 -3
- package/src/components/{table-v2 → table}/TableWrapper.vue +14 -9
- package/src/components/{table-v2 → table}/body/DragPreview.vue +4 -18
- package/src/components/{table-v2 → table}/body/TableBody.vue +6 -6
- package/src/components/{table-v2 → table}/body/TableCell.vue +8 -8
- package/src/components/{table-v2 → table}/body/TableRow.vue +4 -4
- package/src/components/{table-v2 → table}/header/TableHeader.vue +2 -2
- package/src/components/{table-v2 → table}/header/TableHeaderCell.vue +9 -9
- package/src/components/{table-v2 → table}/header/TableHeaderRow.vue +3 -3
- package/src/components/{table-v2 → table}/style/table-loading.css +3 -3
- package/src/components/{table-v2 → table}/style/table-wrapper.css +67 -51
- package/src/components/{table-v2 → table}/style/table.css +1 -1
- package/src/components/textarea/Textarea.vue +1 -1
- package/src/index.js +1 -1
- package/src/components/table/table.css +0 -396
- package/src/components/table-v2/Table.vue +0 -305
- /package/src/components/{table-v2 → table}/TableResizeObserver.vue +0 -0
- /package/src/components/{table-v2 → table}/body/MeasureCell.vue +0 -0
- /package/src/components/{table-v2 → table}/composables/useColumns.js +0 -0
- /package/src/components/{table-v2 → table}/composables/useSorter.js +0 -0
- /package/src/components/{table-v2 → table}/composables/useSticky.js +0 -0
- /package/src/components/{table-v2 → table}/constants.js +0 -0
- /package/src/components/{table-v2 → table}/context/createTableContext.js +0 -0
- /package/src/components/{table-v2 → table}/style/table-body.css +0 -0
- /package/src/components/{table-v2 → table}/style/table-header.css +0 -0
|
@@ -1,827 +1,305 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<col v-for="col in normalizedColumns" :key="col._key" :style="col._style" />
|
|
16
|
-
</colgroup>
|
|
17
|
-
<thead ref="tableHead" class="ui-table__head">
|
|
18
|
-
<tr
|
|
19
|
-
:class="['ui-table__row', 'ui-table__row--head', scrollbarWidth > 0 && 'ui-table__row--has-scrollbar']"
|
|
20
|
-
:style="[{ height: toCssSize(headerHeight) }, rowScrollStyle]"
|
|
21
|
-
>
|
|
22
|
-
<th
|
|
23
|
-
v-if="hasDrag"
|
|
24
|
-
:class="['ui-table__cell', 'ui-table__th', 'ui-table__cell--drag', 'ui-table__cell--fixed-left']"
|
|
25
|
-
:style="[headCellStyle(0), { left: '0px' }]"
|
|
26
|
-
aria-hidden="true"
|
|
27
|
-
/>
|
|
28
|
-
<th
|
|
29
|
-
v-if="hasSelection"
|
|
30
|
-
:class="[
|
|
31
|
-
'ui-table__cell',
|
|
32
|
-
'ui-table__th',
|
|
33
|
-
'ui-table__cell--selection',
|
|
34
|
-
'ui-table__cell--fixed-left',
|
|
35
|
-
!prefixStickyLeft && 'ui-table__cell--fixed-left-last'
|
|
36
|
-
]"
|
|
37
|
-
:style="[headCellStyle(hasDrag ? 1 : 0), { left: selectionStickyOffset + 'px' }]"
|
|
38
|
-
>
|
|
39
|
-
<WkCheckbox :checked="allSelected" :disabled="!rows.length" @change="onToggleAll" />
|
|
40
|
-
</th>
|
|
41
|
-
<th
|
|
42
|
-
v-for="(col, i) in normalizedColumns"
|
|
43
|
-
:key="col._key"
|
|
44
|
-
:class="[
|
|
45
|
-
'ui-table__cell',
|
|
46
|
-
'ui-table__th',
|
|
47
|
-
'ui-table__th-flex',
|
|
48
|
-
`ui-table__cell--${col._align}`,
|
|
49
|
-
col._isSortable && 'ui-table__th--sortable',
|
|
50
|
-
col._fixed && `ui-table__cell--fixed-${col._fixed}`,
|
|
51
|
-
col._isLastFixedLeft && 'ui-table__cell--fixed-left-last',
|
|
52
|
-
col._isFirstFixedRight && 'ui-table__cell--fixed-right-first'
|
|
53
|
-
]"
|
|
54
|
-
:style="[
|
|
55
|
-
headCellStyle((hasDrag ? 1 : 0) + (hasSelection ? 1 : 0) + i, col),
|
|
56
|
-
col._fixed === 'left' ? { [col._fixed]: col._stickyOffset + 'px' } : {},
|
|
57
|
-
col._fixed === 'right' ? { [col._fixed]: col._stickyOffset + 'px' } : {}
|
|
58
|
-
]"
|
|
59
|
-
:tabindex="col._isSortable ? 0 : undefined"
|
|
60
|
-
:aria-sort="col._ariaSort"
|
|
61
|
-
@click="onHeaderClick(col)"
|
|
62
|
-
@keydown.enter.prevent="onHeaderClick(col)"
|
|
63
|
-
@keydown.space.prevent="onHeaderClick(col)"
|
|
64
|
-
v-bind="col._headerAttrs"
|
|
65
|
-
v-on="col._headerListeners"
|
|
66
|
-
>
|
|
67
|
-
<div class="ui-table__th-inner-wrap">
|
|
68
|
-
<span class="ui-table__th-inner">
|
|
69
|
-
<slot name="headerCell" :column="col">{{ col.title }}</slot>
|
|
70
|
-
<span
|
|
71
|
-
v-if="col._isSortable"
|
|
72
|
-
:class="['ui-table__sort', col._sortState ? 'ui-table__sort--active' : 'ui-table__sort--inactive']"
|
|
73
|
-
aria-hidden="true"
|
|
74
|
-
>
|
|
75
|
-
<WkiArrowUp v-if="col._sortState === 'ascend'" :size="16" />
|
|
76
|
-
<WkiArrowDown v-else-if="col._sortState === 'descend'" :size="16" />
|
|
77
|
-
<WkiArrowDownUp v-else :size="16" />
|
|
78
|
-
</span>
|
|
79
|
-
</span>
|
|
80
|
-
<div
|
|
81
|
-
v-if="col._resizable"
|
|
82
|
-
:class="['ui-table__resize-handle', col._fixed === 'right' && 'ui-table__resize-handle--left']"
|
|
83
|
-
@pointerdown.stop.prevent="onResizeStart($event, col, i)"
|
|
84
|
-
/>
|
|
85
|
-
</div>
|
|
86
|
-
</th>
|
|
87
|
-
<th
|
|
88
|
-
v-if="scrollbarWidth > 0"
|
|
89
|
-
class="ui-table__th--scrollbar"
|
|
90
|
-
:style="{ width: scrollbarWidth + 'px', position: 'sticky', right: '0px' }"
|
|
91
|
-
aria-hidden="true"
|
|
92
|
-
/>
|
|
93
|
-
</tr>
|
|
94
|
-
</thead>
|
|
95
|
-
<tbody ref="tableBody" class="ui-table__body" :style="bodyStyle" @scroll="onBodyScroll" @dragleave="onDragLeave">
|
|
96
|
-
<tr v-if="!rows.length" class="ui-table__row ui-table__row--empty">
|
|
97
|
-
<td class="ui-table__cell ui-table__empty-cell" :colspan="totalColumns">
|
|
98
|
-
<slot name="emptyText">
|
|
99
|
-
<WkEmpty :title="emptyText" />
|
|
100
|
-
</slot>
|
|
101
|
-
</td>
|
|
102
|
-
</tr>
|
|
103
|
-
<template v-else>
|
|
104
|
-
<!-- Top spacer reserves the height of the rows scrolled out above the window. -->
|
|
105
|
-
<tr v-if="isVirtual" class="ui-table__spacer" :style="{ height: topSpacerHeight + 'px' }" aria-hidden="true">
|
|
106
|
-
<td class="ui-table__spacer-cell" :colspan="totalColumns" />
|
|
107
|
-
</tr>
|
|
108
|
-
<tr
|
|
109
|
-
v-for="row in mappedRenderRows"
|
|
110
|
-
:key="row._key"
|
|
111
|
-
:ref="'row-' + row._key"
|
|
112
|
-
:class="[
|
|
113
|
-
'ui-table__row',
|
|
114
|
-
'ui-table__row--body',
|
|
115
|
-
row._selected && 'ui-table__row--selected',
|
|
116
|
-
hasDrag && dragState && row._index === dragState.currentIndex && 'ui-table__row--dragging'
|
|
117
|
-
]"
|
|
118
|
-
:style="[{ height: toCssSize(rowHeight) }, rowScrollStyle]"
|
|
119
|
-
v-bind="row._attrs"
|
|
120
|
-
v-on="row._listeners"
|
|
121
|
-
@click="hasSelection ? onRowClick(row._record, row._index, $event) : undefined"
|
|
122
|
-
@dragover.prevent="onDragOver($event, row._index)"
|
|
123
|
-
@drop.prevent="onDrop($event, row._index)"
|
|
124
|
-
>
|
|
125
|
-
<td
|
|
126
|
-
v-if="hasDrag"
|
|
127
|
-
:class="['ui-table__cell', 'ui-table__td', 'ui-table__cell--drag', 'ui-table__cell--fixed-left']"
|
|
128
|
-
:style="[{ left: '0px' }]"
|
|
129
|
-
draggable="true"
|
|
130
|
-
@dragstart.stop="onDragStart($event, row)"
|
|
131
|
-
@dragend="onDragEnd"
|
|
132
|
-
@click.stop
|
|
133
|
-
>
|
|
134
|
-
<WkiGripVertical :size="16" />
|
|
135
|
-
</td>
|
|
136
|
-
<td
|
|
137
|
-
v-if="hasSelection"
|
|
138
|
-
:class="[
|
|
139
|
-
'ui-table__cell',
|
|
140
|
-
'ui-table__td',
|
|
141
|
-
'ui-table__cell--selection',
|
|
142
|
-
'ui-table__cell--fixed-left',
|
|
143
|
-
!prefixStickyLeft && 'ui-table__cell--fixed-left-last'
|
|
144
|
-
]"
|
|
145
|
-
:style="[{ left: selectionStickyOffset + 'px' }]"
|
|
146
|
-
@click.stop="onSelectionCellClick(row._record, row._index, $event)"
|
|
147
|
-
>
|
|
148
|
-
<WkCheckbox :checked="row._selected" />
|
|
149
|
-
</td>
|
|
150
|
-
<td
|
|
151
|
-
v-for="col in normalizedColumns"
|
|
152
|
-
:key="col._key"
|
|
153
|
-
:class="[
|
|
154
|
-
'ui-table__cell',
|
|
155
|
-
'ui-table__td',
|
|
156
|
-
`ui-table__cell--${col._align}`,
|
|
157
|
-
col.ellipsis && 'ui-table__cell--ellipsis',
|
|
158
|
-
col._fixed && `ui-table__cell--fixed-${col._fixed}`,
|
|
159
|
-
col._isLastFixedLeft && 'ui-table__cell--fixed-left-last',
|
|
160
|
-
col._isFirstFixedRight && 'ui-table__cell--fixed-right-first'
|
|
161
|
-
]"
|
|
162
|
-
:style="[
|
|
163
|
-
col._style,
|
|
164
|
-
col._fixed
|
|
165
|
-
? { [col._fixed]: col._stickyOffset - (col._fixed === 'right' ? scrollbarWidth : 0) + 'px' }
|
|
166
|
-
: {}
|
|
167
|
-
]"
|
|
168
|
-
>
|
|
169
|
-
<slot
|
|
170
|
-
name="bodyCell"
|
|
171
|
-
:column="col"
|
|
172
|
-
:record="row._record"
|
|
173
|
-
:text="cellText(col, row._record)"
|
|
174
|
-
:index="row._index"
|
|
175
|
-
>{{ cellText(col, row._record) }}</slot
|
|
176
|
-
>
|
|
177
|
-
</td>
|
|
178
|
-
</tr>
|
|
179
|
-
<!-- Bottom spacer reserves the height of the rows below the window. -->
|
|
180
|
-
<tr
|
|
181
|
-
v-if="isVirtual"
|
|
182
|
-
class="ui-table__spacer"
|
|
183
|
-
:style="{ height: bottomSpacerHeight + 'px' }"
|
|
184
|
-
aria-hidden="true"
|
|
185
|
-
>
|
|
186
|
-
<td class="ui-table__spacer-cell" :colspan="totalColumns" />
|
|
187
|
-
</tr>
|
|
188
|
-
</template>
|
|
189
|
-
</tbody>
|
|
190
|
-
</table>
|
|
2
|
+
<div class="ui-table">
|
|
3
|
+
<TableLoading>
|
|
4
|
+
<TableResizeObserver @resize="handleContainerResize">
|
|
5
|
+
<TableWrapper>
|
|
6
|
+
<template #bodyCell="slotData">
|
|
7
|
+
<slot name="bodyCell" v-bind="slotData"></slot>
|
|
8
|
+
</template>
|
|
9
|
+
<template #empty>
|
|
10
|
+
<slot name="empty"></slot>
|
|
11
|
+
</template>
|
|
12
|
+
</TableWrapper>
|
|
13
|
+
</TableResizeObserver>
|
|
14
|
+
</TableLoading>
|
|
191
15
|
</div>
|
|
192
16
|
</template>
|
|
193
17
|
|
|
194
18
|
<script>
|
|
195
|
-
import
|
|
196
|
-
import
|
|
197
|
-
import {
|
|
198
|
-
import {
|
|
199
|
-
import {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (a == null) return -1
|
|
204
|
-
if (b == null) return 1
|
|
205
|
-
if (typeof a === 'number' && typeof b === 'number') return a - b
|
|
206
|
-
return String(a).localeCompare(String(b))
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const EMPTY_BINDINGS = Object.freeze({ attrs: {}, on: {} })
|
|
19
|
+
import { createTableContext } from './context/createTableContext.js'
|
|
20
|
+
import { flattenColumns } from './composables/useColumns.js'
|
|
21
|
+
import { sortData } from './composables/useSorter.js'
|
|
22
|
+
import { computeOffsets } from './composables/useSticky.js'
|
|
23
|
+
import { SELECTION_COLUMN, DRAGGABLE_COLUMN } from './constants.js'
|
|
24
|
+
import TableWrapper from './TableWrapper.vue'
|
|
25
|
+
import TableLoading from './TableLoading.vue'
|
|
26
|
+
import TableResizeObserver from './TableResizeObserver.vue'
|
|
210
27
|
|
|
211
28
|
export default {
|
|
212
29
|
name: 'Table',
|
|
213
|
-
|
|
30
|
+
|
|
31
|
+
components: { TableWrapper, TableLoading, TableResizeObserver },
|
|
32
|
+
|
|
33
|
+
provide: function () {
|
|
34
|
+
return { tableContext: this.tableCtx }
|
|
35
|
+
},
|
|
36
|
+
|
|
214
37
|
props: {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
38
|
+
columns: {
|
|
39
|
+
type: Array,
|
|
40
|
+
default: function () {
|
|
41
|
+
return []
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
dataSource: {
|
|
45
|
+
type: Array,
|
|
46
|
+
default: function () {
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
},
|
|
222
50
|
bordered: { type: Boolean, default: false },
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
isDrag: { type: Boolean, default: false },
|
|
226
|
-
// Controlled selection — array of selected row keys.
|
|
227
|
-
selectedRowKeys: { type: Array, default: () => [] },
|
|
228
|
-
// Placeholder shown when dataSource is empty.
|
|
229
|
-
emptyText: { type: String, default: 'No data' },
|
|
230
|
-
// Fixed scroll viewport: { x, y } as px (number) or any CSS size (string).
|
|
231
|
-
// x sets the table min-width (horizontal scroll); y caps the body height (vertical scroll + sticky header).
|
|
51
|
+
size: { type: String, default: 'md' },
|
|
52
|
+
rowKey: { type: String, default: 'key' },
|
|
232
53
|
scroll: { type: Object, default: null },
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
rowHeight: { type: [Number, String], default: 39 },
|
|
236
|
-
headerHeight: { type: [Number, String], default: 39 },
|
|
54
|
+
loading: { type: Boolean, default: false },
|
|
55
|
+
rowSelection: { type: Object, default: null },
|
|
237
56
|
customRow: { type: Function, default: null },
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
emits: ['change', 'sort-change', 'update:selectedRowKeys', 'selection-change', 'drag-record'],
|
|
246
|
-
data() {
|
|
247
|
-
return {
|
|
248
|
-
sortField: null,
|
|
249
|
-
sortOrder: null,
|
|
250
|
-
internalSelected: this.selectedRowKeys.slice(),
|
|
251
|
-
scrollbarWidth: 0,
|
|
252
|
-
colWidths: [],
|
|
253
|
-
lastClickedIndex: null,
|
|
254
|
-
scrollTop: 0,
|
|
255
|
-
containerHeight: 0,
|
|
256
|
-
rafId: null,
|
|
257
|
-
toCssSize,
|
|
258
|
-
resizeState: null,
|
|
259
|
-
columnWidths: {},
|
|
260
|
-
dragState: null,
|
|
261
|
-
dragOverIndex: null,
|
|
262
|
-
internalData: this.dataSource.slice()
|
|
263
|
-
}
|
|
57
|
+
customHeaderRow: { type: Function, default: null },
|
|
58
|
+
rowDraggable: { type: Object, default: null },
|
|
59
|
+
enableFixedLeft: { type: Boolean, default: false },
|
|
60
|
+
rowHeight: { type: Number, default: 50 },
|
|
61
|
+
headerHeight: { type: Number, default: 36 },
|
|
62
|
+
emptyText: { type: String, default: '' },
|
|
63
|
+
height: { type: [Number, String], default: 0 }
|
|
264
64
|
},
|
|
265
|
-
computed: {
|
|
266
|
-
selectedKeysSet() {
|
|
267
|
-
return new Set(this.internalSelected)
|
|
268
|
-
},
|
|
269
|
-
normalizedColumns() {
|
|
270
|
-
const prefixDomCount = (this.hasDrag ? 1 : 0) + (this.hasSelection ? 1 : 0)
|
|
271
|
-
|
|
272
|
-
const domWidth = domIdx => {
|
|
273
|
-
const dataIdx = domIdx - prefixDomCount
|
|
274
|
-
if (dataIdx >= 0 && dataIdx < this.columns.length) {
|
|
275
|
-
const col = this.columns[dataIdx]
|
|
276
|
-
const key = col.key != null ? col.key : col.dataIndex
|
|
277
|
-
// resize override > DOM measurement > width prop
|
|
278
|
-
if (key != null && this.columnWidths[key] != null) return this.columnWidths[key]
|
|
279
|
-
if (this.colWidths[domIdx] != null) return this.colWidths[domIdx]
|
|
280
|
-
const w = col.width
|
|
281
|
-
return w != null ? (typeof w === 'number' ? w : parseFloat(w) || 0) : 0
|
|
282
|
-
}
|
|
283
|
-
// prefix columns (drag, selection) — DOM only, no prop fallback
|
|
284
|
-
return this.colWidths[domIdx] != null ? this.colWidths[domIdx] : 0
|
|
285
|
-
}
|
|
286
65
|
|
|
287
|
-
|
|
288
|
-
const _key = col.key != null ? col.key : col.dataIndex
|
|
289
|
-
const _align = ['left', 'right', 'center'].includes(col.align) ? col.align : 'left'
|
|
290
|
-
const overrideW = this.columnWidths[_key]
|
|
291
|
-
let _style = {}
|
|
292
|
-
if (overrideW != null) {
|
|
293
|
-
_style = { width: `${overrideW}px` }
|
|
294
|
-
} else if (col.width != null) {
|
|
295
|
-
_style = { width: typeof col.width === 'number' ? `${col.width}px` : col.width }
|
|
296
|
-
}
|
|
297
|
-
const _isSortable = !!col.sorter
|
|
298
|
-
const _field = col.dataIndex != null ? col.dataIndex : col.key
|
|
66
|
+
emits: ['change', 'sort-change', 'select-change'],
|
|
299
67
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
68
|
+
data: function () {
|
|
69
|
+
return { tableCtx: createTableContext() }
|
|
70
|
+
},
|
|
304
71
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// Pre-compute header bindings once per column instead of calling
|
|
310
|
-
// customHeaderAttrs / customHeaderListeners every render.
|
|
311
|
-
const _headerBindings = this.customHeader ? this.splitBindings(this.customHeader(col, i)) : EMPTY_BINDINGS
|
|
312
|
-
const _fixed = col.fixed === 'left' ? 'left' : col.fixed === 'right' ? 'right' : null
|
|
313
|
-
return {
|
|
314
|
-
...col,
|
|
315
|
-
_key,
|
|
316
|
-
_align,
|
|
317
|
-
_style,
|
|
318
|
-
_isSortable,
|
|
319
|
-
_field,
|
|
320
|
-
_sortState,
|
|
321
|
-
_ariaSort,
|
|
322
|
-
_headerAttrs: _headerBindings.attrs,
|
|
323
|
-
_headerListeners: _headerBindings.on,
|
|
324
|
-
_fixed,
|
|
325
|
-
_isLastFixedLeft: false,
|
|
326
|
-
_isLastFixedRight: false,
|
|
327
|
-
_resizable: col.resizable != null ? col.resizable : false
|
|
328
|
-
}
|
|
329
|
-
})
|
|
72
|
+
computed: {
|
|
73
|
+
isEmpty: function () {
|
|
74
|
+
return this.tableCtx.data.display.length === 0
|
|
75
|
+
},
|
|
330
76
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
77
|
+
columnsInput: function () {
|
|
78
|
+
return {
|
|
79
|
+
columns: this.columns,
|
|
80
|
+
rowKey: this.rowKey,
|
|
81
|
+
hasSelection: !!this.rowSelection,
|
|
82
|
+
hasDraggable: !!this.rowDraggable
|
|
336
83
|
}
|
|
84
|
+
},
|
|
337
85
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
cols[i]._stickyOffset = rightAccum
|
|
342
|
-
rightAccum += domWidth(prefixDomCount + i)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
86
|
+
rowsInput: function () {
|
|
87
|
+
return { customRow: this.customRow, customHeaderRow: this.customHeaderRow }
|
|
88
|
+
},
|
|
345
89
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
90
|
+
layoutInput: function () {
|
|
91
|
+
return {
|
|
92
|
+
bordered: this.bordered,
|
|
93
|
+
size: this.size,
|
|
94
|
+
scroll: this.scroll,
|
|
95
|
+
rowHeight: this.rowHeight,
|
|
96
|
+
headerHeight: this.headerHeight,
|
|
97
|
+
height: this.height
|
|
351
98
|
}
|
|
352
|
-
|
|
353
|
-
if (lastLeftIdx >= 0) cols[lastLeftIdx]._isLastFixedLeft = true
|
|
354
|
-
if (firstRightIdx >= 0) cols[firstRightIdx]._isFirstFixedRight = true
|
|
355
|
-
|
|
356
|
-
return cols
|
|
357
99
|
},
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return this.normalizedColumns.some(c => c._fixed === 'left')
|
|
367
|
-
},
|
|
368
|
-
hasStickyRight() {
|
|
369
|
-
return this.normalizedColumns.some(c => c._fixed === 'right')
|
|
370
|
-
},
|
|
371
|
-
// Pixel offset of the selection column when sticky (sits after the drag column).
|
|
372
|
-
selectionStickyOffset() {
|
|
373
|
-
return this.hasDrag ? (this.colWidths[0] != null ? this.colWidths[0] : 44) : 0
|
|
374
|
-
},
|
|
375
|
-
totalColumns() {
|
|
376
|
-
return this.columns.length + (this.hasDrag ? 1 : 0) + (this.hasSelection ? 1 : 0)
|
|
377
|
-
},
|
|
378
|
-
rows() {
|
|
379
|
-
const data = this.internalData.slice()
|
|
380
|
-
if (!this.sortField || !this.sortOrder) return data
|
|
381
|
-
const col = this.normalizedColumns.find(c => c._field === this.sortField)
|
|
382
|
-
if (!col || !col.sorter) return data
|
|
383
|
-
const cmp =
|
|
384
|
-
typeof col.sorter === 'function' ? col.sorter : (a, b) => defaultCompare(a[this.sortField], b[this.sortField])
|
|
385
|
-
data.sort(cmp)
|
|
386
|
-
if (this.sortOrder === 'descend') data.reverse()
|
|
387
|
-
return data
|
|
388
|
-
},
|
|
389
|
-
allSelected() {
|
|
390
|
-
if (!this.rows.length) return false
|
|
391
|
-
return this.rows.every((record, index) => this.selectedKeysSet.has(this.getRowKey(record, index)))
|
|
392
|
-
},
|
|
393
|
-
isFixedLayout() {
|
|
394
|
-
const scroll = this.scroll || {}
|
|
395
|
-
return this.height != null || scroll.y != null || scroll.x != null
|
|
396
|
-
},
|
|
397
|
-
containerStyle() {
|
|
398
|
-
return {}
|
|
399
|
-
},
|
|
400
|
-
tableStyle() {
|
|
401
|
-
return {}
|
|
402
|
-
},
|
|
403
|
-
rowScrollStyle() {
|
|
404
|
-
const scroll = this.scroll || {}
|
|
405
|
-
return scroll.x != null && this.isFixedLayout ? { minWidth: toCssSize(scroll.x) } : {}
|
|
406
|
-
},
|
|
407
|
-
bodyStyle() {
|
|
408
|
-
const scroll = this.scroll || {}
|
|
409
|
-
const style = {}
|
|
410
|
-
if (this.height != null) {
|
|
411
|
-
style.height = toCssSize(this.height)
|
|
412
|
-
} else if (scroll.y != null) {
|
|
413
|
-
style.maxHeight = toCssSize(scroll.y)
|
|
414
|
-
}
|
|
415
|
-
if (this.isFixedLayout) {
|
|
416
|
-
style.overflowY = scroll.y != null || this.height != null ? 'auto' : 'visible'
|
|
417
|
-
style.overflowX = scroll.x != null ? 'auto' : 'hidden'
|
|
100
|
+
|
|
101
|
+
dataInput: function () {
|
|
102
|
+
return {
|
|
103
|
+
source: this.dataSource,
|
|
104
|
+
sortKey: this.tableCtx.sort.columnKey,
|
|
105
|
+
sortOrder: this.tableCtx.sort.order,
|
|
106
|
+
loading: this.loading,
|
|
107
|
+
emptyText: this.emptyText
|
|
418
108
|
}
|
|
419
|
-
return style
|
|
420
109
|
},
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
},
|
|
428
|
-
// Virtualization is only meaningful inside a bounded, scrollable viewport.
|
|
429
|
-
isVirtual() {
|
|
430
|
-
// Virtualization is only meaningful inside a bounded vertical viewport.
|
|
431
|
-
return this.virtual && (this.height != null || (this.scroll && this.scroll.y != null)) && this.rows.length > 0
|
|
432
|
-
},
|
|
433
|
-
virtualWindow() {
|
|
434
|
-
return computeVirtualWindow({
|
|
435
|
-
scrollTop: this.scrollTop,
|
|
436
|
-
rowHeight: this.rowHeightPx,
|
|
437
|
-
containerHeight: this.containerHeight,
|
|
438
|
-
total: this.rows.length,
|
|
439
|
-
overscan: this.overscan
|
|
440
|
-
})
|
|
441
|
-
},
|
|
442
|
-
// Rows actually committed to the DOM: a slice when virtual, otherwise everything.
|
|
443
|
-
slicedRenderRows() {
|
|
444
|
-
if (!this.isVirtual) return this.rows
|
|
445
|
-
return this.rows.slice(this.virtualWindow.startIndex, this.virtualWindow.endIndex)
|
|
446
|
-
},
|
|
447
|
-
// Pre-compute key, index, selected state, and customRow bindings for each row.
|
|
448
|
-
// This eliminates repeated method calls (getRowKey, rowIndex, isSelected,
|
|
449
|
-
// customRowAttrs, customRowListeners) inside the v-for template.
|
|
450
|
-
mappedRenderRows() {
|
|
451
|
-
const startIndex = this.isVirtual ? this.virtualWindow.startIndex : 0
|
|
452
|
-
const hasCustomRow = !!this.customRow
|
|
453
|
-
return this.slicedRenderRows.map((record, i) => {
|
|
454
|
-
const index = startIndex + i
|
|
455
|
-
const key = this.getRowKey(record, index)
|
|
456
|
-
const selected = this.selectedKeysSet.has(key)
|
|
457
|
-
const bindings = hasCustomRow ? this.splitBindings(this.customRow(record, index)) : EMPTY_BINDINGS
|
|
458
|
-
return {
|
|
459
|
-
_record: record,
|
|
460
|
-
_index: index,
|
|
461
|
-
_key: key,
|
|
462
|
-
_selected: selected,
|
|
463
|
-
_attrs: bindings.attrs,
|
|
464
|
-
_listeners: bindings.on
|
|
465
|
-
}
|
|
466
|
-
})
|
|
467
|
-
},
|
|
468
|
-
// Kept as aliases so template references (renderRows) don't break if used externally.
|
|
469
|
-
renderRows() {
|
|
470
|
-
return this.mappedRenderRows
|
|
110
|
+
|
|
111
|
+
stickyInput: function () {
|
|
112
|
+
return {
|
|
113
|
+
columns: this.tableCtx.columns.flat,
|
|
114
|
+
widths: this.tableCtx.layout.columnWidths
|
|
115
|
+
}
|
|
471
116
|
},
|
|
472
|
-
|
|
473
|
-
|
|
117
|
+
|
|
118
|
+
selectionInput: function () {
|
|
119
|
+
return this.rowSelection
|
|
474
120
|
},
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
return Math.max(0, this.virtualWindow.totalHeight - this.virtualWindow.offsetY - rendered)
|
|
121
|
+
|
|
122
|
+
draggableInput: function () {
|
|
123
|
+
return this.rowDraggable
|
|
479
124
|
}
|
|
480
125
|
},
|
|
126
|
+
|
|
481
127
|
watch: {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
} else {
|
|
514
|
-
attrs[key] = raw[key]
|
|
515
|
-
}
|
|
516
|
-
})
|
|
517
|
-
}
|
|
518
|
-
return { attrs, on }
|
|
519
|
-
},
|
|
520
|
-
teardown() {
|
|
521
|
-
if (this._resizeObserver) {
|
|
522
|
-
this._resizeObserver.disconnect()
|
|
523
|
-
this._resizeObserver = null
|
|
524
|
-
}
|
|
525
|
-
if (this.rafId != null) {
|
|
526
|
-
cancelAnimationFrame(this.rafId)
|
|
527
|
-
this.rafId = null
|
|
128
|
+
columnsInput: {
|
|
129
|
+
immediate: true,
|
|
130
|
+
handler: function (v) {
|
|
131
|
+
var cols = flattenColumns(v.columns)
|
|
132
|
+
const isHasFixedLeft = cols.some(col => col.fixed === 'left')
|
|
133
|
+
if (v.hasSelection) {
|
|
134
|
+
cols.unshift({
|
|
135
|
+
key: '__selection__',
|
|
136
|
+
type: SELECTION_COLUMN,
|
|
137
|
+
width: 36,
|
|
138
|
+
dataIndex: '',
|
|
139
|
+
title: '',
|
|
140
|
+
align: null,
|
|
141
|
+
fixed: isHasFixedLeft || this.enableFixedLeft ? 'left' : null,
|
|
142
|
+
ellipsis: false
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
if (v.hasDraggable) {
|
|
146
|
+
cols.unshift({
|
|
147
|
+
key: '__draggable__',
|
|
148
|
+
type: DRAGGABLE_COLUMN,
|
|
149
|
+
width: 36,
|
|
150
|
+
dataIndex: '',
|
|
151
|
+
title: '',
|
|
152
|
+
align: null,
|
|
153
|
+
fixed: isHasFixedLeft || this.enableFixedLeft ? 'left' : null,
|
|
154
|
+
ellipsis: false
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
this.tableCtx.columns.flat = cols
|
|
158
|
+
this.tableCtx.layout.rowKey = v.rowKey
|
|
528
159
|
}
|
|
529
160
|
},
|
|
530
|
-
// Use ResizeObserver instead of updated() + window resize to measure scrollbar
|
|
531
|
-
// gutter and container height. This avoids triggering a second render cycle
|
|
532
|
-
// every time Vue updates the DOM.
|
|
533
|
-
initResizeObserver() {
|
|
534
|
-
const body = this.$refs.tableBody
|
|
535
|
-
if (!body) return
|
|
536
|
-
this._resizeObserver = new ResizeObserver(() => {
|
|
537
|
-
this.syncScrollbarGutter()
|
|
538
|
-
})
|
|
539
|
-
this._resizeObserver.observe(body)
|
|
540
|
-
// Initial measurement.
|
|
541
|
-
this.$nextTick(this.syncScrollbarGutter)
|
|
542
|
-
},
|
|
543
|
-
onBodyScroll(e) {
|
|
544
|
-
const top = e.target.scrollTop
|
|
545
|
-
const left = e.target.scrollLeft
|
|
546
|
-
// Coalesce bursts of scroll events into a single state update per frame so we
|
|
547
|
-
// re-slice at most once per repaint — keeps 10k+ rows smooth.
|
|
548
|
-
if (this.rafId != null) return
|
|
549
|
-
this.rafId = requestAnimationFrame(() => {
|
|
550
|
-
this.rafId = null
|
|
551
|
-
this.scrollTop = top
|
|
552
|
-
// Sync horizontal scroll to keep the header aligned with the body.
|
|
553
|
-
const head = this.$refs.tableHead
|
|
554
|
-
if (head) head.scrollLeft = left
|
|
555
|
-
})
|
|
556
|
-
},
|
|
557
|
-
syncScrollbarGutter() {
|
|
558
|
-
const body = this.$refs.tableBody
|
|
559
|
-
if (!body) return
|
|
560
|
-
|
|
561
|
-
const hasY = this.height != null || (this.scroll && this.scroll.y != null)
|
|
562
|
-
this.scrollbarWidth = hasY ? body.offsetWidth - body.clientWidth : 0
|
|
563
161
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const firstRow = body.querySelector('.ui-table__row--body')
|
|
570
|
-
if (firstRow) {
|
|
571
|
-
const cells = Array.from(firstRow.children)
|
|
572
|
-
const widths = cells.map(c => c.getBoundingClientRect().width)
|
|
573
|
-
const changed =
|
|
574
|
-
widths.length !== this.colWidths.length ||
|
|
575
|
-
widths.some((w, i) => Math.abs(w - (this.colWidths[i] || 0)) > 0.1)
|
|
576
|
-
if (changed) {
|
|
577
|
-
this.colWidths = widths
|
|
578
|
-
}
|
|
579
|
-
} else if (this.colWidths.length) {
|
|
580
|
-
this.colWidths = []
|
|
581
|
-
}
|
|
582
|
-
} else if (this.colWidths.length) {
|
|
583
|
-
this.colWidths = []
|
|
162
|
+
rowsInput: {
|
|
163
|
+
immediate: true,
|
|
164
|
+
handler: function (v) {
|
|
165
|
+
this.tableCtx.rows.customRow = v.customRow
|
|
166
|
+
this.tableCtx.rows.customHeaderRow = v.customHeaderRow
|
|
584
167
|
}
|
|
585
168
|
},
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
169
|
+
|
|
170
|
+
layoutInput: {
|
|
171
|
+
immediate: true,
|
|
172
|
+
deep: true,
|
|
173
|
+
handler: function (v) {
|
|
174
|
+
this.tableCtx.layout.bordered = v.bordered
|
|
175
|
+
this.tableCtx.layout.size = v.size
|
|
176
|
+
this.tableCtx.layout.scroll = v.scroll
|
|
177
|
+
this.tableCtx.layout.tableLayout = v.scroll && v.scroll.y ? 'fixed' : 'auto'
|
|
178
|
+
this.tableCtx.layout.height = v.height
|
|
179
|
+
this.tableCtx.layout.headerHeight = v.headerHeight
|
|
180
|
+
this.tableCtx.layout.rowHeight = v.rowHeight
|
|
589
181
|
}
|
|
590
|
-
return col ? col._style : {}
|
|
591
|
-
},
|
|
592
|
-
cellText(col, record) {
|
|
593
|
-
const field = col._field
|
|
594
|
-
return field != null ? record[field] : ''
|
|
595
182
|
},
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
183
|
+
|
|
184
|
+
dataInput: {
|
|
185
|
+
immediate: true,
|
|
186
|
+
handler: function (v) {
|
|
187
|
+
this.tableCtx.data.source = v.source
|
|
188
|
+
;((this.tableCtx.data.display = v.sortKey ? sortData(v.source, v.sortKey, v.sortOrder) : v.source),
|
|
189
|
+
(this.tableCtx.data.loading = v.loading))
|
|
190
|
+
this.tableCtx.data.emptyText = v.emptyText
|
|
604
191
|
}
|
|
605
|
-
this.sortField = order ? field : null
|
|
606
|
-
this.sortOrder = order
|
|
607
|
-
const payload = { field: this.sortField, order }
|
|
608
|
-
this.$emit('sort-change', payload)
|
|
609
|
-
this.$emit('change', { sorter: payload })
|
|
610
|
-
},
|
|
611
|
-
getRowKey(record, index) {
|
|
612
|
-
if (typeof this.rowKey === 'function') return this.rowKey(record)
|
|
613
|
-
const key = record[this.rowKey]
|
|
614
|
-
return key != null ? key : index
|
|
615
|
-
},
|
|
616
|
-
isSelected(record, index) {
|
|
617
|
-
return this.selectedKeysSet.has(this.getRowKey(record, index))
|
|
618
|
-
},
|
|
619
|
-
emitSelection() {
|
|
620
|
-
const keys = this.internalSelected.slice()
|
|
621
|
-
const selectedRows = this.dataSource.filter((record, index) => keys.includes(this.getRowKey(record, index)))
|
|
622
|
-
this.$emit('update:selectedRowKeys', keys)
|
|
623
|
-
this.$emit('selection-change', keys, selectedRows)
|
|
624
192
|
},
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
rangeKeys.push(this.getRowKey(this.rows[i], i))
|
|
635
|
-
}
|
|
636
|
-
// Merge with existing selection (union)
|
|
637
|
-
const set = new Set(this.internalSelected)
|
|
638
|
-
rangeKeys.forEach(k => set.add(k))
|
|
639
|
-
this.internalSelected = Array.from(set)
|
|
640
|
-
} else {
|
|
641
|
-
// Normal click / Cmd+click: toggle individual row
|
|
642
|
-
const key = this.getRowKey(record, index)
|
|
643
|
-
const set = this.internalSelected.filter(k => k !== key)
|
|
644
|
-
if (checked) set.push(key)
|
|
645
|
-
this.internalSelected = set
|
|
193
|
+
|
|
194
|
+
stickyInput: {
|
|
195
|
+
deep: true,
|
|
196
|
+
immediate: true,
|
|
197
|
+
handler: function (v) {
|
|
198
|
+
if (!v.columns.length) return
|
|
199
|
+
var offsets = computeOffsets(v.columns, v.widths)
|
|
200
|
+
this.tableCtx.sticky.leftOffsets = offsets.left
|
|
201
|
+
this.tableCtx.sticky.rightOffsets = offsets.right
|
|
646
202
|
}
|
|
647
|
-
this.lastClickedIndex = index
|
|
648
|
-
this.emitSelection()
|
|
649
|
-
},
|
|
650
|
-
onSelectionCellClick(record, index, e) {
|
|
651
|
-
// Click on the checkbox <td> — MouseEvent has modifier keys
|
|
652
|
-
const currentlySelected = this.isSelected(record, index)
|
|
653
|
-
this.handleSelection(record, index, !currentlySelected, e)
|
|
654
203
|
},
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
this.dragState = { fromIndex: row._index, currentIndex: row._index, record: row._record }
|
|
670
|
-
e.dataTransfer.effectAllowed = 'move'
|
|
671
|
-
|
|
672
|
-
const tr = e.currentTarget.closest('tr')
|
|
673
|
-
const rect = tr.getBoundingClientRect()
|
|
674
|
-
const tableEl = this.$el.querySelector('.ui-table__table')
|
|
675
|
-
|
|
676
|
-
// Shallow-clone the full ancestor chain so scoped CSS selectors
|
|
677
|
-
// (e.g. .ui-table--scroll-y .ui-table__row[data-v-xxx]) still match.
|
|
678
|
-
const wrapper = this.$el.cloneNode(false)
|
|
679
|
-
wrapper.style.position = 'fixed'
|
|
680
|
-
wrapper.style.top = '-9999px'
|
|
681
|
-
wrapper.style.left = '-9999px'
|
|
682
|
-
wrapper.style.width = rect.width + 'px'
|
|
683
|
-
wrapper.style.pointerEvents = 'none'
|
|
684
|
-
|
|
685
|
-
const table = tableEl.cloneNode(false)
|
|
686
|
-
const colgroup = tableEl.querySelector('colgroup')
|
|
687
|
-
if (colgroup) table.appendChild(colgroup.cloneNode(true))
|
|
688
|
-
|
|
689
|
-
const tbody = this.$refs.tableBody.cloneNode(false)
|
|
690
|
-
tbody.style.overflow = 'visible'
|
|
691
|
-
tbody.style.maxHeight = 'none'
|
|
692
|
-
tbody.style.height = 'auto'
|
|
693
|
-
|
|
694
|
-
const clonedRow = tr.cloneNode(true)
|
|
695
|
-
clonedRow.classList.remove('ui-table__row--dragging')
|
|
696
|
-
clonedRow.classList.add('ui-table__row--drag-preview')
|
|
697
|
-
|
|
698
|
-
tbody.appendChild(clonedRow)
|
|
699
|
-
table.appendChild(tbody)
|
|
700
|
-
wrapper.appendChild(table)
|
|
701
|
-
document.body.appendChild(wrapper)
|
|
702
|
-
|
|
703
|
-
e.dataTransfer.setDragImage(wrapper, e.clientX - rect.left, e.clientY - rect.top)
|
|
704
|
-
requestAnimationFrame(() => document.body.removeChild(wrapper))
|
|
705
|
-
},
|
|
706
|
-
onDragOver(e, index) {
|
|
707
|
-
if (!this.dragState) return
|
|
708
|
-
if (index === this.dragState.currentIndex) return
|
|
709
|
-
// Only reorder once the pointer crosses the row's midpoint to prevent oscillation.
|
|
710
|
-
const rect = e.currentTarget.getBoundingClientRect()
|
|
711
|
-
const midY = rect.top + rect.height / 1.5
|
|
712
|
-
if (index > this.dragState.currentIndex && e.clientY < midY) return
|
|
713
|
-
if (index < this.dragState.currentIndex && e.clientY > midY) return
|
|
714
|
-
// FLIP — snapshot "First" positions before the data changes.
|
|
715
|
-
const firstRects = this.snapshotRowRects()
|
|
716
|
-
const data = this.internalData.slice()
|
|
717
|
-
const [moved] = data.splice(this.dragState.currentIndex, 1)
|
|
718
|
-
data.splice(index, 0, moved)
|
|
719
|
-
this.internalData = data
|
|
720
|
-
this.dragState = { ...this.dragState, currentIndex: index }
|
|
721
|
-
this.$nextTick(() => this.flipAnimateRows(firstRects))
|
|
722
|
-
},
|
|
723
|
-
onDragLeave(e) {
|
|
724
|
-
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
725
|
-
this.dragOverIndex = null
|
|
204
|
+
|
|
205
|
+
selectionInput: {
|
|
206
|
+
immediate: true,
|
|
207
|
+
deep: true,
|
|
208
|
+
handler: function (v) {
|
|
209
|
+
if (!v) return
|
|
210
|
+
this.tableCtx.selection.selectedRowKeys = v.selectedRowKeys || []
|
|
211
|
+
this.tableCtx.selection.hideSelectAll = v.hideSelectAll
|
|
212
|
+
this.tableCtx.selection.columnWidth = v.columnWidth
|
|
213
|
+
this.tableCtx.selection.type = v.type
|
|
214
|
+
this.tableCtx.selection.onChange = v.onChange
|
|
215
|
+
this.tableCtx.selection.onSelect = v.onSelect
|
|
216
|
+
this.tableCtx.selection.onSelectAll = v.onSelectAll
|
|
217
|
+
this.tableCtx.selection.onSelectNone = v.onSelectNone
|
|
726
218
|
}
|
|
727
219
|
},
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
this.internalData = this.dataSource.slice()
|
|
220
|
+
|
|
221
|
+
draggableInput: {
|
|
222
|
+
immediate: true,
|
|
223
|
+
deep: true,
|
|
224
|
+
handler: function (v) {
|
|
225
|
+
this.tableCtx.draggable.enabled = !!v
|
|
226
|
+
if (!v) return
|
|
227
|
+
this.tableCtx.draggable.columnWidth = v.columnWidth || 36
|
|
228
|
+
this.tableCtx.draggable.handleReorder = v.handleReorder || null
|
|
229
|
+
this.tableCtx.draggable.enableAnimationFlip = v.enableAnimationFlip || false
|
|
739
230
|
}
|
|
740
|
-
|
|
741
|
-
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
created: function () {
|
|
235
|
+
this.tableCtx.actions.sort = this.handleSort
|
|
236
|
+
this.tableCtx.actions.select = this.handleSelect
|
|
237
|
+
this.tableCtx.actions.resize = this.handleResize
|
|
238
|
+
this.tableCtx.actions.reorder = this.handleReorder
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
methods: {
|
|
242
|
+
handleSort: function (columnKey, order) {
|
|
243
|
+
this.tableCtx.sort.columnKey = columnKey
|
|
244
|
+
this.tableCtx.sort.order = order
|
|
245
|
+
this.$emit('sort-change', { columnKey: columnKey, order: order })
|
|
246
|
+
this.$emit('change', { sort: { columnKey: columnKey, order: order } })
|
|
742
247
|
},
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
this.
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
rects[row._key] = dom.getBoundingClientRect()
|
|
755
|
-
}
|
|
248
|
+
|
|
249
|
+
handleSelect: function (keys, meta) {
|
|
250
|
+
var prevKeys = this.tableCtx.selection.selectedRowKeys
|
|
251
|
+
var display = this.tableCtx.data.display
|
|
252
|
+
var rowKey = this.tableCtx.layout.rowKey
|
|
253
|
+
var rs = this.rowSelection || {}
|
|
254
|
+
|
|
255
|
+
this.tableCtx.selection.checkAll = keys.length === display.length && display.length > 0
|
|
256
|
+
|
|
257
|
+
var selectedRows = display.filter(function (r) {
|
|
258
|
+
return keys.indexOf(r[rowKey]) !== -1
|
|
756
259
|
})
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
const last = dom.getBoundingClientRect()
|
|
769
|
-
const deltaY = first.top - last.top
|
|
770
|
-
if (Math.abs(deltaY) < 1) return
|
|
771
|
-
// Invert: jump the row back to its old position.
|
|
772
|
-
dom.style.transition = 'none'
|
|
773
|
-
dom.style.transform = `translateY(${deltaY}px)`
|
|
774
|
-
// Play: animate from inverted position to final position.
|
|
775
|
-
requestAnimationFrame(() => {
|
|
776
|
-
dom.style.transition = 'transform 0.25s cubic-bezier(0.22, 1, 0.36, 1)'
|
|
777
|
-
dom.style.transform = ''
|
|
778
|
-
const onEnd = () => {
|
|
779
|
-
dom.style.transition = ''
|
|
780
|
-
dom.style.transform = ''
|
|
781
|
-
dom.removeEventListener('transitionend', onEnd)
|
|
782
|
-
}
|
|
783
|
-
dom.addEventListener('transitionend', onEnd, { once: true })
|
|
260
|
+
|
|
261
|
+
if (rs.onChange) rs.onChange(keys, selectedRows)
|
|
262
|
+
|
|
263
|
+
if (!meta) return
|
|
264
|
+
|
|
265
|
+
if (meta.type === 'row') {
|
|
266
|
+
if (rs.onSelect) rs.onSelect(meta.record, meta.selected, selectedRows, meta.nativeEvent)
|
|
267
|
+
} else if (meta.type === 'all') {
|
|
268
|
+
// changeRows = rows mới được check (chưa có trong prevKeys)
|
|
269
|
+
var changeRows = display.filter(function (r) {
|
|
270
|
+
return prevKeys.indexOf(r[rowKey]) === -1
|
|
784
271
|
})
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
startWidth: th.getBoundingClientRect().width,
|
|
794
|
-
minWidth: typeof col.minWidth === 'number' ? col.minWidth : 40,
|
|
795
|
-
reverse: col._fixed === 'right'
|
|
272
|
+
if (rs.onSelectAll) rs.onSelectAll(true, selectedRows, changeRows)
|
|
273
|
+
} else if (meta.type === 'none') {
|
|
274
|
+
// changeRows = rows bị uncheck (đang có trong prevKeys)
|
|
275
|
+
changeRows = display.filter(function (r) {
|
|
276
|
+
return prevKeys.indexOf(r[rowKey]) !== -1
|
|
277
|
+
})
|
|
278
|
+
if (rs.onSelectAll) rs.onSelectAll(false, [], changeRows)
|
|
279
|
+
if (rs.onSelectNone) rs.onSelectNone()
|
|
796
280
|
}
|
|
797
|
-
e.currentTarget.setPointerCapture(e.pointerId)
|
|
798
|
-
document.addEventListener('pointermove', this.onResizeMove)
|
|
799
|
-
document.addEventListener('pointerup', this.onResizeEnd, { once: true })
|
|
800
281
|
},
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
// Re-measure so colWidths reflects the final DOM state.
|
|
816
|
-
this.$nextTick(this.syncScrollbarGutter)
|
|
282
|
+
|
|
283
|
+
handleResize: function (colKey, width) {
|
|
284
|
+
var widths = new Map(this.tableCtx.layout.columnWidths)
|
|
285
|
+
widths.set(colKey, width)
|
|
286
|
+
this.tableCtx.layout.columnWidths = widths
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
handleReorder: function (oldIndex, newIndex) {
|
|
290
|
+
const display = this.tableCtx.data.display.slice()
|
|
291
|
+
const moved = display.splice(oldIndex, 1)[0]
|
|
292
|
+
display.splice(newIndex, 0, moved)
|
|
293
|
+
|
|
294
|
+
const cb = this.tableCtx.draggable.handleReorder
|
|
295
|
+
if (typeof cb === 'function') cb(oldIndex, newIndex, display)
|
|
817
296
|
},
|
|
818
297
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
this.resizeState = null
|
|
298
|
+
handleContainerResize: function (size) {
|
|
299
|
+
this.tableCtx.layout.containerWidth = size.width
|
|
822
300
|
}
|
|
823
301
|
}
|
|
824
302
|
}
|
|
825
303
|
</script>
|
|
826
304
|
|
|
827
|
-
<style src="./table.css" scoped></style>
|
|
305
|
+
<style src="./style/table.css" scoped></style>
|