@dataloop-ai/components 0.19.0 → 0.19.2
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 +1 -1
- package/src/components/compound/DlTable/DlTable.vue +136 -80
- package/src/components/compound/DlTable/components/DlTd.vue +6 -5
- package/src/components/compound/DlTable/components/DlTh.vue +12 -2
- package/src/components/compound/DlTable/types.ts +1 -1
- package/src/components/compound/DlTable/utils/getCellValue.ts +5 -0
- package/src/demos/DlTableDemo.vue +5 -1
package/package.json
CHANGED
|
@@ -139,11 +139,12 @@
|
|
|
139
139
|
|
|
140
140
|
<DlTh
|
|
141
141
|
v-if="visibleColumns && visibleColumns.length"
|
|
142
|
-
key="
|
|
142
|
+
key="visibleColumnsSlot"
|
|
143
|
+
:col-index="-1"
|
|
143
144
|
>
|
|
144
145
|
<slot
|
|
145
|
-
name="visible-columns-button"
|
|
146
|
-
:
|
|
146
|
+
name="header-cell-visible-columns-button"
|
|
147
|
+
:visible-columns-state="visibleColumnsState"
|
|
147
148
|
:group-options="groupOptions"
|
|
148
149
|
:handle-visible-columns-update="
|
|
149
150
|
handleVisibleColumnsUpdate
|
|
@@ -156,9 +157,9 @@
|
|
|
156
157
|
tooltip="Manage columns"
|
|
157
158
|
>
|
|
158
159
|
<slot
|
|
159
|
-
name="visible-columns-menu"
|
|
160
|
-
:
|
|
161
|
-
|
|
160
|
+
name="header-cell-visible-columns-menu"
|
|
161
|
+
:visible-columns-state="
|
|
162
|
+
visibleColumnsState
|
|
162
163
|
"
|
|
163
164
|
:group-options="groupOptions"
|
|
164
165
|
:handle-visible-columns-update="
|
|
@@ -167,9 +168,9 @@
|
|
|
167
168
|
>
|
|
168
169
|
<dl-menu>
|
|
169
170
|
<slot
|
|
170
|
-
name="visible-columns-menu-content"
|
|
171
|
-
:
|
|
172
|
-
|
|
171
|
+
name="header-cell-visible-columns-menu-content"
|
|
172
|
+
:visible-columns-state="
|
|
173
|
+
visibleColumnsState
|
|
173
174
|
"
|
|
174
175
|
:group-options="
|
|
175
176
|
groupOptions
|
|
@@ -344,8 +345,9 @@
|
|
|
344
345
|
</slot>
|
|
345
346
|
<DlTd
|
|
346
347
|
v-if="showRowActions"
|
|
347
|
-
key="
|
|
348
|
+
key="visibleColumnsSlot"
|
|
348
349
|
class="visible-columns-justify-end"
|
|
350
|
+
:col-index="-1"
|
|
349
351
|
no-tooltip
|
|
350
352
|
>
|
|
351
353
|
<slot
|
|
@@ -469,34 +471,70 @@
|
|
|
469
471
|
</slot>
|
|
470
472
|
<DlTh
|
|
471
473
|
v-if="showRowActions"
|
|
472
|
-
key="
|
|
474
|
+
key="visibleColumnsSlot"
|
|
473
475
|
>
|
|
474
|
-
<
|
|
475
|
-
|
|
476
|
-
|
|
476
|
+
<slot
|
|
477
|
+
name="header-cell-visible-columns-button"
|
|
478
|
+
:computed-visible-cols="computedVisibleCols"
|
|
479
|
+
:group-options="groupOptions"
|
|
480
|
+
:handle-visible-columns-update="
|
|
481
|
+
handleVisibleColumnsUpdate
|
|
477
482
|
"
|
|
478
|
-
text-color="dl-color-medium"
|
|
479
|
-
flat
|
|
480
|
-
icon="icon-dl-column"
|
|
481
483
|
>
|
|
482
|
-
<dl-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
484
|
+
<dl-button
|
|
485
|
+
v-if="
|
|
486
|
+
visibleColumns &&
|
|
487
|
+
visibleColumns.length
|
|
488
|
+
"
|
|
489
|
+
text-color="dl-color-medium"
|
|
490
|
+
flat
|
|
491
|
+
icon="icon-dl-column"
|
|
492
|
+
>
|
|
493
|
+
<slot
|
|
494
|
+
name="header-cell-visible-columns-menu"
|
|
495
|
+
:computed-visible-cols="
|
|
496
|
+
computedVisibleCols
|
|
497
|
+
"
|
|
498
|
+
:group-options="groupOptions"
|
|
499
|
+
:handle-visible-columns-update="
|
|
500
|
+
handleVisibleColumnsUpdate
|
|
501
|
+
"
|
|
502
|
+
>
|
|
503
|
+
<dl-menu>
|
|
504
|
+
<slot
|
|
505
|
+
name="header-cell-visible-columns-menu-content"
|
|
506
|
+
:computed-visible-cols="
|
|
507
|
+
computedVisibleCols
|
|
508
|
+
"
|
|
509
|
+
:group-options="
|
|
510
|
+
groupOptions
|
|
511
|
+
"
|
|
512
|
+
:handle-visible-columns-update="
|
|
513
|
+
handleVisibleColumnsUpdate
|
|
514
|
+
"
|
|
515
|
+
>
|
|
516
|
+
<dl-list separator>
|
|
517
|
+
<dl-option-group
|
|
518
|
+
:model-value="
|
|
519
|
+
computedVisibleCols
|
|
520
|
+
"
|
|
521
|
+
:options="
|
|
522
|
+
groupOptions
|
|
523
|
+
"
|
|
524
|
+
:left-label="true"
|
|
525
|
+
max-width="250px"
|
|
526
|
+
type="switch"
|
|
527
|
+
class="table-options"
|
|
528
|
+
@update:model-value="
|
|
529
|
+
handleVisibleColumnsUpdate
|
|
530
|
+
"
|
|
531
|
+
/>
|
|
532
|
+
</dl-list>
|
|
533
|
+
</slot>
|
|
534
|
+
</dl-menu>
|
|
535
|
+
</slot>
|
|
536
|
+
</dl-button>
|
|
537
|
+
</slot>
|
|
500
538
|
</DlTh>
|
|
501
539
|
</DlTr>
|
|
502
540
|
|
|
@@ -656,8 +694,9 @@
|
|
|
656
694
|
</DlTd>
|
|
657
695
|
<DlTd
|
|
658
696
|
v-if="showRowActions"
|
|
659
|
-
key="
|
|
697
|
+
key="visibleColumnsSlot"
|
|
660
698
|
class="visible-columns-justify-end"
|
|
699
|
+
:col-index="-1"
|
|
661
700
|
no-tooltip
|
|
662
701
|
>
|
|
663
702
|
<slot
|
|
@@ -1097,7 +1136,25 @@ export default defineComponent({
|
|
|
1097
1136
|
dense,
|
|
1098
1137
|
draggable,
|
|
1099
1138
|
virtualScroll,
|
|
1100
|
-
rows
|
|
1139
|
+
rows,
|
|
1140
|
+
visibleColumns,
|
|
1141
|
+
rowKey,
|
|
1142
|
+
titleClass,
|
|
1143
|
+
emptyStateProps,
|
|
1144
|
+
hideNoData,
|
|
1145
|
+
loading,
|
|
1146
|
+
loadingLabel,
|
|
1147
|
+
filter,
|
|
1148
|
+
noResultsLabel,
|
|
1149
|
+
noDataLabel,
|
|
1150
|
+
columns,
|
|
1151
|
+
fitAllColumns,
|
|
1152
|
+
resizable,
|
|
1153
|
+
hidePagination,
|
|
1154
|
+
hideSelectedBanner,
|
|
1155
|
+
color,
|
|
1156
|
+
virtualScrollStickySizeStart,
|
|
1157
|
+
noHover
|
|
1101
1158
|
} = toRefs(props)
|
|
1102
1159
|
|
|
1103
1160
|
const tbodyKey = ref()
|
|
@@ -1111,12 +1168,12 @@ export default defineComponent({
|
|
|
1111
1168
|
)
|
|
1112
1169
|
|
|
1113
1170
|
const hasEmptyStateProps = computed(() =>
|
|
1114
|
-
|
|
1171
|
+
emptyStateProps.value
|
|
1115
1172
|
? Object.keys(props.emptyStateProps).length > 0
|
|
1116
1173
|
: false
|
|
1117
1174
|
)
|
|
1118
1175
|
|
|
1119
|
-
const isDataEmpty = computed(() => !
|
|
1176
|
+
const isDataEmpty = computed(() => !rows.value.length)
|
|
1120
1177
|
|
|
1121
1178
|
const groupOptions = computed(() =>
|
|
1122
1179
|
(
|
|
@@ -1130,7 +1187,7 @@ export default defineComponent({
|
|
|
1130
1187
|
)
|
|
1131
1188
|
|
|
1132
1189
|
const visibleColumnsState = ref(
|
|
1133
|
-
(
|
|
1190
|
+
(visibleColumns.value as DlTableColumn[])?.map((col) => col.name)
|
|
1134
1191
|
)
|
|
1135
1192
|
|
|
1136
1193
|
const computedVisibleCols = computed(() =>
|
|
@@ -1140,9 +1197,9 @@ export default defineComponent({
|
|
|
1140
1197
|
const { hasAnyAction } = useTableActions(props) // todo: does not work
|
|
1141
1198
|
|
|
1142
1199
|
const getRowKey = computed(() =>
|
|
1143
|
-
typeof
|
|
1144
|
-
?
|
|
1145
|
-
: (row: Record<string, any>) => row[
|
|
1200
|
+
typeof rowKey.value === 'function'
|
|
1201
|
+
? rowKey.value
|
|
1202
|
+
: (row: Record<string, any>) => row[rowKey.value as string]
|
|
1146
1203
|
)
|
|
1147
1204
|
|
|
1148
1205
|
const isResizing = ref(false)
|
|
@@ -1176,7 +1233,7 @@ export default defineComponent({
|
|
|
1176
1233
|
})
|
|
1177
1234
|
|
|
1178
1235
|
const containerStyle = computed(() => {
|
|
1179
|
-
if (
|
|
1236
|
+
if (virtualScroll.value) {
|
|
1180
1237
|
return {
|
|
1181
1238
|
height: 'var(--dl-table-height, 500px)'
|
|
1182
1239
|
}
|
|
@@ -1186,13 +1243,13 @@ export default defineComponent({
|
|
|
1186
1243
|
const nothingToDisplay = computed(() => computedRows.value.length === 0)
|
|
1187
1244
|
|
|
1188
1245
|
const titleClasses = computed(
|
|
1189
|
-
() => 'dl-table__title ' + (
|
|
1246
|
+
() => 'dl-table__title ' + (titleClass.value || '')
|
|
1190
1247
|
)
|
|
1191
1248
|
|
|
1192
1249
|
const bottomClasses = computed(() => {
|
|
1193
1250
|
let classNames = 'dl-table__bottom row items-center'
|
|
1194
1251
|
|
|
1195
|
-
if (nothingToDisplay.value && !
|
|
1252
|
+
if (nothingToDisplay.value && !hideNoData.value) {
|
|
1196
1253
|
// TODO add styles for this class
|
|
1197
1254
|
classNames = classNames + ' dl-table__bottom--nodata'
|
|
1198
1255
|
}
|
|
@@ -1202,15 +1259,15 @@ export default defineComponent({
|
|
|
1202
1259
|
//
|
|
1203
1260
|
|
|
1204
1261
|
const noDataMessage = computed(() => {
|
|
1205
|
-
if (
|
|
1206
|
-
return
|
|
1262
|
+
if (loading.value) {
|
|
1263
|
+
return loadingLabel.value
|
|
1207
1264
|
}
|
|
1208
1265
|
|
|
1209
|
-
if (
|
|
1210
|
-
return
|
|
1266
|
+
if (filter.value) {
|
|
1267
|
+
return noResultsLabel.value
|
|
1211
1268
|
}
|
|
1212
1269
|
|
|
1213
|
-
return
|
|
1270
|
+
return noDataLabel.value
|
|
1214
1271
|
})
|
|
1215
1272
|
|
|
1216
1273
|
const hasDraggableRows = computed(() =>
|
|
@@ -1236,11 +1293,11 @@ export default defineComponent({
|
|
|
1236
1293
|
nextTick(() => {
|
|
1237
1294
|
setAllColumnWidths(
|
|
1238
1295
|
tableEl,
|
|
1239
|
-
|
|
1240
|
-
|
|
1296
|
+
columns.value as DlTableColumn[],
|
|
1297
|
+
fitAllColumns.value
|
|
1241
1298
|
)
|
|
1242
1299
|
})
|
|
1243
|
-
if (
|
|
1300
|
+
if (resizable.value === true) {
|
|
1244
1301
|
applyResizableColumns(tableEl, vm)
|
|
1245
1302
|
}
|
|
1246
1303
|
if (hasDraggableColumns.value === true) {
|
|
@@ -1259,7 +1316,7 @@ export default defineComponent({
|
|
|
1259
1316
|
'table.dl-table'
|
|
1260
1317
|
) as HTMLTableElement
|
|
1261
1318
|
|
|
1262
|
-
if (
|
|
1319
|
+
if (resizable.value) {
|
|
1263
1320
|
applyResizableColumns(tableEl, vm)
|
|
1264
1321
|
}
|
|
1265
1322
|
|
|
@@ -1276,15 +1333,12 @@ export default defineComponent({
|
|
|
1276
1333
|
}
|
|
1277
1334
|
)
|
|
1278
1335
|
|
|
1279
|
-
watch(
|
|
1280
|
-
()
|
|
1281
|
-
|
|
1282
|
-
applyResizableColumns(tableEl, vm)
|
|
1283
|
-
}
|
|
1284
|
-
)
|
|
1336
|
+
watch(resizable, () => {
|
|
1337
|
+
applyResizableColumns(tableEl, vm)
|
|
1338
|
+
})
|
|
1285
1339
|
|
|
1286
1340
|
watch(
|
|
1287
|
-
|
|
1341
|
+
columns,
|
|
1288
1342
|
(newColumns) => {
|
|
1289
1343
|
setAllColumnWidths(
|
|
1290
1344
|
rootRef.value,
|
|
@@ -1298,14 +1352,15 @@ export default defineComponent({
|
|
|
1298
1352
|
)
|
|
1299
1353
|
|
|
1300
1354
|
watch(
|
|
1301
|
-
|
|
1302
|
-
(value) => {
|
|
1355
|
+
visibleColumns,
|
|
1356
|
+
(value: string[]) => {
|
|
1303
1357
|
visibleColumnsState.value = value
|
|
1304
|
-
}
|
|
1358
|
+
},
|
|
1359
|
+
{ immediate: true, deep: true }
|
|
1305
1360
|
)
|
|
1306
1361
|
|
|
1307
1362
|
watch(
|
|
1308
|
-
|
|
1363
|
+
draggable,
|
|
1309
1364
|
() => {
|
|
1310
1365
|
if (tableEl) {
|
|
1311
1366
|
if (hasDraggableColumns.value === true) {
|
|
@@ -1367,10 +1422,10 @@ export default defineComponent({
|
|
|
1367
1422
|
|
|
1368
1423
|
const { sortBy, descending } = computedPagination.value
|
|
1369
1424
|
|
|
1370
|
-
if (
|
|
1425
|
+
if (filter.value) {
|
|
1371
1426
|
filtered = computedFilterMethod.value(
|
|
1372
1427
|
rows.value,
|
|
1373
|
-
|
|
1428
|
+
filter.value,
|
|
1374
1429
|
computedCols.value,
|
|
1375
1430
|
getCellValue
|
|
1376
1431
|
)
|
|
@@ -1431,7 +1486,7 @@ export default defineComponent({
|
|
|
1431
1486
|
})
|
|
1432
1487
|
|
|
1433
1488
|
const displayPagination = computed(
|
|
1434
|
-
() => !(
|
|
1489
|
+
() => !(hidePagination.value || nothingToDisplay.value)
|
|
1435
1490
|
)
|
|
1436
1491
|
|
|
1437
1492
|
const {
|
|
@@ -1499,7 +1554,7 @@ export default defineComponent({
|
|
|
1499
1554
|
|
|
1500
1555
|
const hasBotomSelectionBanner = computed(() => {
|
|
1501
1556
|
return (
|
|
1502
|
-
|
|
1557
|
+
hideSelectedBanner.value !== true &&
|
|
1503
1558
|
hasSelectionMode.value === true &&
|
|
1504
1559
|
rowsSelectedNumber.value > 0
|
|
1505
1560
|
)
|
|
@@ -1528,8 +1583,8 @@ export default defineComponent({
|
|
|
1528
1583
|
cols: computedCols.value,
|
|
1529
1584
|
sort,
|
|
1530
1585
|
colsMap: computedColsMap.value,
|
|
1531
|
-
color:
|
|
1532
|
-
dense:
|
|
1586
|
+
color: color.value,
|
|
1587
|
+
dense: dense.value
|
|
1533
1588
|
})
|
|
1534
1589
|
|
|
1535
1590
|
if (multipleSelection.value === true) {
|
|
@@ -1590,7 +1645,7 @@ export default defineComponent({
|
|
|
1590
1645
|
)
|
|
1591
1646
|
const offsetTop =
|
|
1592
1647
|
rowEl.offsetTop -
|
|
1593
|
-
(
|
|
1648
|
+
(virtualScrollStickySizeStart.value as number)
|
|
1594
1649
|
const direction =
|
|
1595
1650
|
offsetTop < scrollTarget.scrollTop ? 'decrease' : 'increase'
|
|
1596
1651
|
|
|
@@ -1642,12 +1697,13 @@ export default defineComponent({
|
|
|
1642
1697
|
function injectBodyCommonScope(data: Record<string, any>) {
|
|
1643
1698
|
Object.assign(data, {
|
|
1644
1699
|
cols: computedCols.value,
|
|
1700
|
+
visibleColumnsState: visibleColumnsState.value,
|
|
1645
1701
|
colsMap: computedColsMap.value,
|
|
1646
1702
|
sort,
|
|
1647
1703
|
rowIndex: firstRowIndex.value + data.pageIndex,
|
|
1648
|
-
color:
|
|
1649
|
-
dense:
|
|
1650
|
-
noHover:
|
|
1704
|
+
color: color.value,
|
|
1705
|
+
dense: dense.value,
|
|
1706
|
+
noHover: noHover.value
|
|
1651
1707
|
})
|
|
1652
1708
|
|
|
1653
1709
|
if (hasSelectionMode.value === true) {
|
|
@@ -1716,18 +1772,18 @@ export default defineComponent({
|
|
|
1716
1772
|
|
|
1717
1773
|
const handleSortableEvent = (event: SortableEvent) => {
|
|
1718
1774
|
const { oldIndex, newIndex } = event
|
|
1719
|
-
const newRows = insertAtIndex(
|
|
1775
|
+
const newRows = insertAtIndex(rows.value, oldIndex, newIndex)
|
|
1720
1776
|
tbodyKey.value = v4()
|
|
1721
1777
|
emit('row-reorder', newRows)
|
|
1722
1778
|
}
|
|
1723
1779
|
|
|
1724
1780
|
const reorderColumns = (sourceIndex: number, targetIndex: number) => {
|
|
1725
1781
|
const newColumns = insertAtIndex(
|
|
1726
|
-
|
|
1782
|
+
columns.value,
|
|
1727
1783
|
sourceIndex,
|
|
1728
1784
|
targetIndex
|
|
1729
1785
|
)
|
|
1730
|
-
if (isEqual(newColumns,
|
|
1786
|
+
if (isEqual(newColumns, columns.value)) return
|
|
1731
1787
|
tableKey.value = v4()
|
|
1732
1788
|
emit('col-update', newColumns)
|
|
1733
1789
|
}
|
|
@@ -1771,7 +1827,7 @@ export default defineComponent({
|
|
|
1771
1827
|
|
|
1772
1828
|
const showRowActions = computed<boolean>(
|
|
1773
1829
|
() =>
|
|
1774
|
-
!!(
|
|
1830
|
+
!!(visibleColumns.value && visibleColumns.value.length) ||
|
|
1775
1831
|
!!hasSlotByName(`body-cell-row-actions`)
|
|
1776
1832
|
)
|
|
1777
1833
|
|
|
@@ -83,7 +83,12 @@ export default defineComponent({
|
|
|
83
83
|
return colmap ?? props.props?.col
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
const colIndex = computed(() => {
|
|
87
|
+
return props.props?.colIndex ?? props.colIndex
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (!hasOptionalProps.value || !column.value || colIndex.value === -1) {
|
|
91
|
+
// return empty if !column if you dont want line.
|
|
87
92
|
return {
|
|
88
93
|
classes: tdClasses,
|
|
89
94
|
styles: tdStyles,
|
|
@@ -92,10 +97,6 @@ export default defineComponent({
|
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
if (!column.value) {
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
|
|
99
100
|
const { row } = props.props
|
|
100
101
|
|
|
101
102
|
return {
|
|
@@ -25,7 +25,13 @@
|
|
|
25
25
|
|
|
26
26
|
<script lang="ts">
|
|
27
27
|
import { isString } from 'lodash'
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
defineComponent,
|
|
30
|
+
getCurrentInstance,
|
|
31
|
+
computed,
|
|
32
|
+
ref,
|
|
33
|
+
toRefs
|
|
34
|
+
} from 'vue-demi'
|
|
29
35
|
import { useSizeObserver } from '../../../../hooks/use-size-observer'
|
|
30
36
|
import { stringStyleToRecord } from '../../../../utils'
|
|
31
37
|
import { DlIcon } from '../../../essential'
|
|
@@ -73,7 +79,11 @@ export default defineComponent({
|
|
|
73
79
|
return col
|
|
74
80
|
})
|
|
75
81
|
|
|
76
|
-
|
|
82
|
+
const colIndex = computed(() => {
|
|
83
|
+
return props.props?.colIndex ?? props.colIndex
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
if (!column.value || colIndex.value === -1) {
|
|
77
87
|
return {
|
|
78
88
|
headerStyle: '',
|
|
79
89
|
thClasses: '',
|
|
@@ -27,7 +27,7 @@ export type DlTableFilter = string | Record<string, any>
|
|
|
27
27
|
export type DlTableColumn = {
|
|
28
28
|
name: string
|
|
29
29
|
label: string
|
|
30
|
-
field
|
|
30
|
+
field?: string | ((row: DlTableRow) => string)
|
|
31
31
|
required?: boolean
|
|
32
32
|
align?: 'right' | 'left' | 'center'
|
|
33
33
|
sortable?: boolean
|
|
@@ -14,8 +14,13 @@ export function getCellValue(
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function getNestedProperty(obj: Record<string, any>, propertyPath: string) {
|
|
17
|
+
if (!propertyPath) {
|
|
18
|
+
return obj
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
const pathArray = propertyPath.split('.')
|
|
18
22
|
let value = obj
|
|
23
|
+
|
|
19
24
|
for (const prop of pathArray) {
|
|
20
25
|
if (value && value.hasOwnProperty(prop)) {
|
|
21
26
|
value = value[prop]
|
|
@@ -380,7 +380,11 @@
|
|
|
380
380
|
:columns="tableColumns"
|
|
381
381
|
title="Editable Columns"
|
|
382
382
|
:visible-columns="tableColumns.slice(0, -1)"
|
|
383
|
-
|
|
383
|
+
>
|
|
384
|
+
<template #body-cell-row-actions>
|
|
385
|
+
<dl-button label="ActionButton" />
|
|
386
|
+
</template>
|
|
387
|
+
</DlTable>
|
|
384
388
|
</div>
|
|
385
389
|
<div>
|
|
386
390
|
<p>Virtual With editable columns</p>
|