@vueye-table/headless 3.0.0-alpha.1 → 3.0.0-alpha.10
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/README.md +2 -0
- package/dist/index.d.ts +893 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1400 -118
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { injectDataGrid, injectDataTable, provideDataGrid, provideDataTable, useDataGrid } from "@vueye-table/vue";
|
|
2
|
-
import { defineComponent, h, inject, nextTick, onMounted, onScopeDispose, provide, ref, useId, watch } from "vue";
|
|
1
|
+
import { injectDataGrid, injectDataTable, provideDataGrid, provideDataTable, useDataGrid, useVirtualColumns, useVirtualRows } from "@vueye-table/vue";
|
|
2
|
+
import { Fragment, computed, defineComponent, h, inject, nextTick, onMounted, onScopeDispose, provide, ref, shallowRef, useId, watch } from "vue";
|
|
3
|
+
import { formatValue, getRowItemKey, gridCommand } from "@vueye-table/core";
|
|
3
4
|
|
|
4
5
|
//#region src/shared.ts
|
|
5
6
|
/** The element a component renders. Every headless component accepts `as`. */
|
|
@@ -76,14 +77,16 @@ const DataTableSelectRow = defineComponent({
|
|
|
76
77
|
setup(props, { slots }) {
|
|
77
78
|
const table = injectDataTable("<DataTableSelectRow>");
|
|
78
79
|
return () => {
|
|
79
|
-
const selected = table.isSelected(props.row.key);
|
|
80
|
+
const selected = table.tree ? props.row.selection === "all" : table.isSelected(props.row.key);
|
|
81
|
+
const indeterminate = table.tree && props.row.selection === "some";
|
|
80
82
|
const toggle = () => table.toggleRow(props.row.key);
|
|
81
83
|
return slots.default?.({
|
|
82
84
|
selected,
|
|
85
|
+
indeterminate,
|
|
83
86
|
toggle
|
|
84
87
|
}) ?? checkbox({
|
|
85
88
|
checked: selected,
|
|
86
|
-
indeterminate
|
|
89
|
+
indeterminate,
|
|
87
90
|
label: props.label,
|
|
88
91
|
disabled: table.selectionMode === "none",
|
|
89
92
|
onChange: toggle
|
|
@@ -244,10 +247,6 @@ const DataTableColumnVisibility = defineComponent({
|
|
|
244
247
|
};
|
|
245
248
|
}
|
|
246
249
|
});
|
|
247
|
-
/**
|
|
248
|
-
* A polite live region describing what is shown, such as "1–10 of 57 rows". Screen readers
|
|
249
|
-
* announce it after a search, filter, or page change.
|
|
250
|
-
*/
|
|
251
250
|
const DataTableStatus = defineComponent({
|
|
252
251
|
name: "DataTableStatus",
|
|
253
252
|
slots: Object,
|
|
@@ -260,16 +259,737 @@ const DataTableStatus = defineComponent({
|
|
|
260
259
|
end: table.pageEnd,
|
|
261
260
|
rowCount: table.rowCount,
|
|
262
261
|
totalRowCount: table.totalRowCount,
|
|
263
|
-
selectedCount: table.selectedCount
|
|
262
|
+
selectedCount: table.selectedCount,
|
|
263
|
+
loadState: table.loadState,
|
|
264
|
+
loadedRowCount: table.loadedRowCount,
|
|
265
|
+
loadError: table.loadError,
|
|
266
|
+
canLoadMore: table.canLoadMore,
|
|
267
|
+
retry: () => table.retry(),
|
|
268
|
+
loadNext: () => table.loadNext(),
|
|
269
|
+
loadingChildrenCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "loading").length : 0,
|
|
270
|
+
childErrorCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "error").length : 0
|
|
264
271
|
};
|
|
265
|
-
const
|
|
272
|
+
const count = `Loaded ${table.loadedRowCount.toLocaleString()} ${table.loadedRowCount === 1 ? "row" : "rows"}`;
|
|
273
|
+
const text = table.loadingMode ? table.loadState === "error" ? `${count}. Could not load rows. Retry loading.` : table.loadState === "aborted" ? `${count}. Loading stopped.` : table.loadState === "loading" || table.loadState === "streaming" ? `${count}…` : `${count}${table.loadState === "done" ? ". All rows loaded." : "."}` : table.rowCount === 0 ? "No rows" : `${table.pageStart}–${table.pageEnd} of ${table.rowCount} rows` + (table.selectedCount > 0 ? `, ${table.selectedCount} selected` : "");
|
|
266
274
|
return h(props.as, {
|
|
267
275
|
role: "status",
|
|
268
276
|
"aria-live": "polite"
|
|
269
|
-
}, slots.default?.(slotProps) ?? text);
|
|
277
|
+
}, slots.default?.(slotProps) ?? text + (slotProps.loadingChildrenCount ? " Loading children…" : "") + (slotProps.childErrorCount ? " Could not load children. Retry children in the affected row." : ""));
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/loading.ts
|
|
284
|
+
/** Keep this visual sentinel out of the live region and logical data-row count. */
|
|
285
|
+
function loadSentinel(table, colspan) {
|
|
286
|
+
return table.loadingMode === "cursor" && table.loadedRowCount > 0 && (table.loadState === "loading" || table.loadState === "streaming") ? h("tr", {
|
|
287
|
+
key: "vt-load-sentinel",
|
|
288
|
+
"data-load-sentinel": "",
|
|
289
|
+
"aria-hidden": "true"
|
|
290
|
+
}, [h("td", {
|
|
291
|
+
colspan: Math.max(1, colspan),
|
|
292
|
+
class: "vt-load-sentinel"
|
|
293
|
+
}, "Loading more rows…")]) : void 0;
|
|
294
|
+
}
|
|
295
|
+
/** A keyboard-accessible alternative to automatic virtual-end loading, with error recovery. */
|
|
296
|
+
const DataTableLoadMore = defineComponent({
|
|
297
|
+
name: "DataTableLoadMore",
|
|
298
|
+
props: {
|
|
299
|
+
label: {
|
|
300
|
+
type: String,
|
|
301
|
+
default: "Load more rows"
|
|
302
|
+
},
|
|
303
|
+
retryLabel: {
|
|
304
|
+
type: String,
|
|
305
|
+
default: "Retry loading"
|
|
306
|
+
},
|
|
307
|
+
loadingLabel: {
|
|
308
|
+
type: String,
|
|
309
|
+
default: "Loading rows…"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
setup(props) {
|
|
313
|
+
const table = injectDataTable("<DataTableLoadMore>");
|
|
314
|
+
return () => table.loadState === "error" ? h("button", {
|
|
315
|
+
type: "button",
|
|
316
|
+
onClick: () => table.retry()
|
|
317
|
+
}, props.retryLabel) : table.loadingMode === "cursor" && table.loadState !== "done" ? h("button", {
|
|
318
|
+
type: "button",
|
|
319
|
+
disabled: !table.canLoadMore,
|
|
320
|
+
onClick: () => {
|
|
321
|
+
table.loadNext();
|
|
322
|
+
}
|
|
323
|
+
}, table.loadState === "loading" || table.loadState === "streaming" ? props.loadingLabel : props.label) : null;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/editor.ts
|
|
329
|
+
function editorText(value, kind) {
|
|
330
|
+
if (value === null || value === void 0) return "";
|
|
331
|
+
if (kind === "date" && value instanceof Date) return Number.isFinite(value.getTime()) ? value.toISOString().slice(0, 10) : "";
|
|
332
|
+
return formatValue(value);
|
|
333
|
+
}
|
|
334
|
+
const cellEditorProps = {
|
|
335
|
+
editor: {
|
|
336
|
+
type: Object,
|
|
337
|
+
required: true
|
|
338
|
+
},
|
|
339
|
+
spec: {
|
|
340
|
+
type: Object,
|
|
341
|
+
default: () => ({ kind: "text" })
|
|
342
|
+
},
|
|
343
|
+
/** Keyboard movement is owned by the surrounding cell. */
|
|
344
|
+
finish: {
|
|
345
|
+
type: Function,
|
|
346
|
+
default: void 0
|
|
347
|
+
},
|
|
348
|
+
autofocus: {
|
|
349
|
+
type: Boolean,
|
|
350
|
+
default: true
|
|
351
|
+
},
|
|
352
|
+
blurCommit: {
|
|
353
|
+
type: Boolean,
|
|
354
|
+
default: true
|
|
355
|
+
},
|
|
356
|
+
tabCommit: {
|
|
357
|
+
type: Boolean,
|
|
358
|
+
default: true
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
/** Plain native fields. Select results are searched and capped at 50 mounted options. */
|
|
362
|
+
const DataCellEditor = defineComponent({
|
|
363
|
+
name: "DataCellEditor",
|
|
364
|
+
props: cellEditorProps,
|
|
365
|
+
setup(props) {
|
|
366
|
+
const field = ref();
|
|
367
|
+
const query = ref("");
|
|
368
|
+
const options = props.spec.options ?? [];
|
|
369
|
+
const matches = computed(() => {
|
|
370
|
+
const search = query.value.trim().toLocaleLowerCase();
|
|
371
|
+
return options.map((value, index) => ({
|
|
372
|
+
value,
|
|
373
|
+
index
|
|
374
|
+
})).filter(({ value }) => String(value ?? "").toLocaleLowerCase().includes(search));
|
|
375
|
+
});
|
|
376
|
+
onMounted(() => {
|
|
377
|
+
if (!props.autofocus) return;
|
|
378
|
+
field.value?.focus();
|
|
379
|
+
if (field.value && "setSelectionRange" in field.value && ["text", "number"].includes(props.spec.kind)) field.value.setSelectionRange(field.value.value.length, field.value.value.length);
|
|
380
|
+
});
|
|
381
|
+
const finish = (direction) => {
|
|
382
|
+
if (props.finish) props.finish(direction);
|
|
383
|
+
else props.editor.commit();
|
|
384
|
+
};
|
|
385
|
+
const keydown = (event) => {
|
|
386
|
+
if (event.isComposing) return;
|
|
387
|
+
if (event.key === "Escape") {
|
|
388
|
+
event.preventDefault();
|
|
389
|
+
event.stopPropagation();
|
|
390
|
+
if (!props.editor.pending) props.editor.cancel();
|
|
391
|
+
} else if (event.key === "Enter" || event.key === "Tab") {
|
|
392
|
+
if (event.key === "Tab" && !props.tabCommit) return;
|
|
393
|
+
event.preventDefault();
|
|
394
|
+
event.stopPropagation();
|
|
395
|
+
if (!props.editor.pending) finish(event.key === "Tab" ? event.shiftKey ? "left" : "right" : event.shiftKey ? "up" : "down");
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const paste = (event) => {
|
|
399
|
+
const text = event.clipboardData?.getData("text/plain");
|
|
400
|
+
if (text === void 0 || props.editor.pending) return;
|
|
401
|
+
event.preventDefault();
|
|
402
|
+
const target = event.target;
|
|
403
|
+
const start = target.selectionStart ?? 0;
|
|
404
|
+
const end = target.selectionEnd ?? target.value.length;
|
|
405
|
+
props.editor.input(target.value.slice(0, start) + text + target.value.slice(end));
|
|
406
|
+
};
|
|
407
|
+
return () => {
|
|
408
|
+
const { editor, spec } = props;
|
|
409
|
+
const attrs = {
|
|
410
|
+
...editor.attrs,
|
|
411
|
+
"aria-label": `Edit ${editor.column.header}`,
|
|
412
|
+
"data-editor": "",
|
|
413
|
+
disabled: editor.pending,
|
|
414
|
+
onKeydown: keydown
|
|
415
|
+
};
|
|
416
|
+
if (spec.kind === "select") {
|
|
417
|
+
const shown = matches.value.slice(0, 50);
|
|
418
|
+
return h("div", {
|
|
419
|
+
"data-select-editor": "",
|
|
420
|
+
onKeydown: (event) => {
|
|
421
|
+
if (event.key === "Escape") keydown(event);
|
|
422
|
+
}
|
|
423
|
+
}, [
|
|
424
|
+
h("input", {
|
|
425
|
+
ref: field,
|
|
426
|
+
type: "search",
|
|
427
|
+
value: query.value,
|
|
428
|
+
"aria-label": `Search ${editor.column.header} options`,
|
|
429
|
+
disabled: editor.pending,
|
|
430
|
+
onInput: (event) => {
|
|
431
|
+
query.value = event.target.value;
|
|
432
|
+
},
|
|
433
|
+
onKeydown: (event) => {
|
|
434
|
+
if (event.key === "Enter" && shown[0]) {
|
|
435
|
+
event.preventDefault();
|
|
436
|
+
editor.commit(shown[0].value);
|
|
437
|
+
} else keydown(event);
|
|
438
|
+
}
|
|
439
|
+
}),
|
|
440
|
+
h("select", {
|
|
441
|
+
...attrs,
|
|
442
|
+
value: options.findIndex((value) => Object.is(value, editor.value)),
|
|
443
|
+
size: Math.max(2, Math.min(5, shown.length)),
|
|
444
|
+
onChange: (event) => {
|
|
445
|
+
const index = Number(event.target.value);
|
|
446
|
+
if (index >= 0) editor.commit(options[index]);
|
|
447
|
+
}
|
|
448
|
+
}, shown.map(({ value, index }) => h("option", {
|
|
449
|
+
key: index,
|
|
450
|
+
value: index
|
|
451
|
+
}, String(value ?? "(empty)")))),
|
|
452
|
+
h("span", {
|
|
453
|
+
role: "status",
|
|
454
|
+
"data-editor-hint": ""
|
|
455
|
+
}, matches.value.length === 0 ? "No matching options. Change your search." : matches.value.length > 50 ? "Showing the first 50 matches. Refine your search." : `${matches.value.length} options`)
|
|
456
|
+
]);
|
|
457
|
+
}
|
|
458
|
+
return h("input", {
|
|
459
|
+
...attrs,
|
|
460
|
+
ref: field,
|
|
461
|
+
type: spec.kind === "date" ? "date" : spec.kind === "checkbox" ? "checkbox" : "text",
|
|
462
|
+
inputmode: spec.kind === "number" ? "decimal" : void 0,
|
|
463
|
+
value: editor.draft,
|
|
464
|
+
checked: spec.kind === "checkbox" ? editor.value === true : void 0,
|
|
465
|
+
onInput: spec.kind === "checkbox" ? void 0 : (event) => editor.input(event.target.value),
|
|
466
|
+
onChange: spec.kind === "checkbox" ? (event) => editor.commit(event.target.checked) : void 0,
|
|
467
|
+
onBlur: () => {
|
|
468
|
+
if (props.blurCommit && !editor.pending) finish();
|
|
469
|
+
},
|
|
470
|
+
onPaste: spec.kind === "checkbox" ? void 0 : paste
|
|
471
|
+
});
|
|
270
472
|
};
|
|
271
473
|
}
|
|
272
474
|
});
|
|
475
|
+
/** The cell owns the id so it remains stable while its editor mounts and unmounts. */
|
|
476
|
+
function issueAttrs(id, issues, pending) {
|
|
477
|
+
return {
|
|
478
|
+
"aria-invalid": issues.length ? "true" : void 0,
|
|
479
|
+
"aria-describedby": issues.length ? id : void 0,
|
|
480
|
+
"aria-busy": pending ? "true" : void 0
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function editorIssues(id, issues, pending = false) {
|
|
484
|
+
return issues.length || pending ? h("span", {
|
|
485
|
+
id,
|
|
486
|
+
"data-edit-issues": "",
|
|
487
|
+
role: "status"
|
|
488
|
+
}, pending ? "Checking changes…" : issues.map((problem) => problem.message).join(" ")) : null;
|
|
489
|
+
}
|
|
490
|
+
function useEditorId() {
|
|
491
|
+
return `vt-edit-${useId()}`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/virtual.ts
|
|
496
|
+
/** Shared opt-in renderer props. Options are applied when the virtual renderer is created. */
|
|
497
|
+
const virtualProps = {
|
|
498
|
+
virtual: {
|
|
499
|
+
type: [Boolean, Object],
|
|
500
|
+
default: false
|
|
501
|
+
},
|
|
502
|
+
height: {
|
|
503
|
+
type: String,
|
|
504
|
+
default: void 0
|
|
505
|
+
},
|
|
506
|
+
rowHeight: {
|
|
507
|
+
type: Number,
|
|
508
|
+
default: 40
|
|
509
|
+
},
|
|
510
|
+
overscan: {
|
|
511
|
+
type: Number,
|
|
512
|
+
default: 5
|
|
513
|
+
},
|
|
514
|
+
virtualColumns: {
|
|
515
|
+
type: Boolean,
|
|
516
|
+
default: false
|
|
517
|
+
},
|
|
518
|
+
columnWidth: {
|
|
519
|
+
type: Number,
|
|
520
|
+
default: 120
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
function virtualOptions(props) {
|
|
524
|
+
return {
|
|
525
|
+
rowHeight: props.rowHeight,
|
|
526
|
+
overscan: props.overscan,
|
|
527
|
+
columnWidth: props.columnWidth,
|
|
528
|
+
...typeof props.virtual === "object" ? props.virtual : {}
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
const viewportKey = Symbol("vt-viewport");
|
|
532
|
+
const virtualKey = Symbol("vt-virtual-renderer");
|
|
533
|
+
function injectVirtual() {
|
|
534
|
+
return inject(virtualKey, void 0);
|
|
535
|
+
}
|
|
536
|
+
/** A scroll container for a headless table/grid. It supplies its element without global reads. */
|
|
537
|
+
const DataTableViewport = defineComponent({
|
|
538
|
+
name: "DataTableViewport",
|
|
539
|
+
slots: Object,
|
|
540
|
+
props: {
|
|
541
|
+
as: asProp("div"),
|
|
542
|
+
height: {
|
|
543
|
+
type: String,
|
|
544
|
+
default: "24rem"
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
setup(props, { slots, expose }) {
|
|
548
|
+
const element = shallowRef(null);
|
|
549
|
+
provide(viewportKey, element);
|
|
550
|
+
expose({ element });
|
|
551
|
+
return () => h(props.as, {
|
|
552
|
+
ref: element,
|
|
553
|
+
"data-virtual-viewport": "",
|
|
554
|
+
style: {
|
|
555
|
+
height: props.height,
|
|
556
|
+
overflow: "auto",
|
|
557
|
+
overflowAnchor: "none"
|
|
558
|
+
}
|
|
559
|
+
}, slots.default?.());
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
function createComponentVirtual(table, options, tableElement, grid, virtualColumns = false, gutter = 0) {
|
|
563
|
+
const scrollElement = inject(viewportKey, shallowRef(null));
|
|
564
|
+
const rows = useVirtualRows(table, {
|
|
565
|
+
scrollElement,
|
|
566
|
+
estimateRowHeight: options.rowHeight,
|
|
567
|
+
overscan: options.overscan,
|
|
568
|
+
initialCount: options.initialCount,
|
|
569
|
+
grid,
|
|
570
|
+
scrollMargin: () => tableElement()?.querySelector("thead")?.getBoundingClientRect().height ?? 0
|
|
571
|
+
});
|
|
572
|
+
onMounted(() => {
|
|
573
|
+
watch(() => [
|
|
574
|
+
rows.endIndex,
|
|
575
|
+
table.renderItems.length,
|
|
576
|
+
table.canLoadMore
|
|
577
|
+
], () => {
|
|
578
|
+
if (table.loadingMode === "cursor" && table.canLoadMore && rows.endIndex >= table.renderItems.length - 1 - table.endThreshold) table.loadNext();
|
|
579
|
+
}, { immediate: true });
|
|
580
|
+
});
|
|
581
|
+
const columns = grid && virtualColumns ? useVirtualColumns(grid, {
|
|
582
|
+
scrollElement,
|
|
583
|
+
estimateColumnWidth: (index) => table.columns[index]?.width ?? options.columnWidth ?? 120,
|
|
584
|
+
overscan: options.columnOverscan ?? 1,
|
|
585
|
+
initialCount: options.initialColumnCount ?? 5,
|
|
586
|
+
scrollMargin: gutter
|
|
587
|
+
}) : void 0;
|
|
588
|
+
const pinnedRows = computed(() => {
|
|
589
|
+
const result = [...rows.items];
|
|
590
|
+
const positions = /* @__PURE__ */ new Set([grid?.selection?.focus.row, grid?.editor?.position.row]);
|
|
591
|
+
for (const position of positions) {
|
|
592
|
+
if (position === void 0 || result.some((item) => item.renderItem.kind === "row" && item.renderItem.rowIndex === position)) continue;
|
|
593
|
+
const direct = table.renderItems[position];
|
|
594
|
+
const index = direct?.kind === "row" && direct.rowIndex === position ? position : table.renderItems.findIndex((item) => item.kind === "row" && item.rowIndex === position);
|
|
595
|
+
const item = rows.getItem(index);
|
|
596
|
+
if (item) result.push(item);
|
|
597
|
+
}
|
|
598
|
+
return result.sort((a, b) => a.index - b.index);
|
|
599
|
+
});
|
|
600
|
+
const pinnedColumns = computed(() => {
|
|
601
|
+
if (!columns) return void 0;
|
|
602
|
+
const result = [...columns.items];
|
|
603
|
+
const positions = /* @__PURE__ */ new Set([grid?.selection?.focus.column, grid?.editor?.position.column]);
|
|
604
|
+
for (const position of positions) {
|
|
605
|
+
if (position === void 0 || result.some((item) => item.index === position)) continue;
|
|
606
|
+
const item = columns.getItem(position);
|
|
607
|
+
if (item) result.push(item);
|
|
608
|
+
}
|
|
609
|
+
return result.sort((a, b) => a.index - b.index);
|
|
610
|
+
});
|
|
611
|
+
const binding = {
|
|
612
|
+
rows,
|
|
613
|
+
columns,
|
|
614
|
+
gutter,
|
|
615
|
+
get rowItems() {
|
|
616
|
+
return pinnedRows.value;
|
|
617
|
+
},
|
|
618
|
+
get columnItems() {
|
|
619
|
+
return pinnedColumns.value;
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
provide(virtualKey, binding);
|
|
623
|
+
return binding;
|
|
624
|
+
}
|
|
625
|
+
/** Create composables only for an enabled renderer, preserving the ordinary component path. */
|
|
626
|
+
const VirtualContent = defineComponent({
|
|
627
|
+
name: "DataTableVirtualContent",
|
|
628
|
+
props: {
|
|
629
|
+
table: {
|
|
630
|
+
type: Object,
|
|
631
|
+
required: true
|
|
632
|
+
},
|
|
633
|
+
options: {
|
|
634
|
+
type: Object,
|
|
635
|
+
required: true
|
|
636
|
+
},
|
|
637
|
+
tableElement: {
|
|
638
|
+
type: Function,
|
|
639
|
+
required: true
|
|
640
|
+
},
|
|
641
|
+
grid: {
|
|
642
|
+
type: Object,
|
|
643
|
+
default: void 0
|
|
644
|
+
},
|
|
645
|
+
virtualColumns: {
|
|
646
|
+
type: Boolean,
|
|
647
|
+
default: false
|
|
648
|
+
},
|
|
649
|
+
gutter: {
|
|
650
|
+
type: Number,
|
|
651
|
+
default: 0
|
|
652
|
+
},
|
|
653
|
+
render: {
|
|
654
|
+
type: Function,
|
|
655
|
+
required: true
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
setup(props) {
|
|
659
|
+
const binding = createComponentVirtual(props.table, props.options, props.tableElement, props.grid, props.virtualColumns, props.gutter);
|
|
660
|
+
return () => props.render(binding);
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
function rowSpacer(size, colspan, key) {
|
|
664
|
+
return size > 0 ? h("tr", {
|
|
665
|
+
key,
|
|
666
|
+
"aria-hidden": "true",
|
|
667
|
+
role: "presentation",
|
|
668
|
+
"data-virtual-spacer": "",
|
|
669
|
+
style: { height: `${size}px` }
|
|
670
|
+
}, [h("td", {
|
|
671
|
+
colspan: Math.max(1, colspan),
|
|
672
|
+
style: {
|
|
673
|
+
height: `${size}px`,
|
|
674
|
+
padding: "0",
|
|
675
|
+
border: "0",
|
|
676
|
+
lineHeight: "0"
|
|
677
|
+
}
|
|
678
|
+
})]) : null;
|
|
679
|
+
}
|
|
680
|
+
function columnSpacer(size, tag, key) {
|
|
681
|
+
return size > 0 ? h(tag, {
|
|
682
|
+
key,
|
|
683
|
+
"aria-hidden": "true",
|
|
684
|
+
role: "presentation",
|
|
685
|
+
"data-virtual-spacer": "",
|
|
686
|
+
style: {
|
|
687
|
+
width: `${size}px`,
|
|
688
|
+
minWidth: `${size}px`,
|
|
689
|
+
padding: "0",
|
|
690
|
+
border: "0"
|
|
691
|
+
}
|
|
692
|
+
}) : null;
|
|
693
|
+
}
|
|
694
|
+
function renderColumns(tableColumns, binding, render, tag) {
|
|
695
|
+
if (!binding?.columns || !binding.columnItems) return tableColumns.map(render);
|
|
696
|
+
const result = [];
|
|
697
|
+
let end = 0;
|
|
698
|
+
for (const item of binding.columnItems) {
|
|
699
|
+
result.push(columnSpacer(item.start - end, tag, `gap-${item.index}`), render(item.column, item.index));
|
|
700
|
+
end = item.start + item.size;
|
|
701
|
+
}
|
|
702
|
+
result.push(columnSpacer(binding.columns.totalSize - end, tag, "gap-end"));
|
|
703
|
+
return result;
|
|
704
|
+
}
|
|
705
|
+
function renderRows(binding, colspan, render) {
|
|
706
|
+
const result = [];
|
|
707
|
+
let end = 0;
|
|
708
|
+
for (const item of binding.rowItems) {
|
|
709
|
+
result.push(rowSpacer(item.start - end, colspan, `gap-${item.index}`), render(item));
|
|
710
|
+
end = item.start + item.size;
|
|
711
|
+
}
|
|
712
|
+
result.push(rowSpacer(binding.rows.totalSize - end, colspan, "gap-end"));
|
|
713
|
+
return result;
|
|
714
|
+
}
|
|
715
|
+
function virtualColgroup(tableColumns, binding) {
|
|
716
|
+
if (!binding.columns || !binding.columnItems) return null;
|
|
717
|
+
const cols = binding.gutter ? [h("col", { style: { width: `${binding.gutter}px` } })] : [];
|
|
718
|
+
let end = 0;
|
|
719
|
+
for (const item of binding.columnItems) {
|
|
720
|
+
if (item.start > end) cols.push(h("col", { style: { width: `${item.start - end}px` } }));
|
|
721
|
+
cols.push(h("col", { style: { width: `${item.size}px` } }));
|
|
722
|
+
end = item.start + item.size;
|
|
723
|
+
}
|
|
724
|
+
if (binding.columns.totalSize > end) cols.push(h("col", { style: { width: `${binding.columns.totalSize - end}px` } }));
|
|
725
|
+
return h("colgroup", cols);
|
|
726
|
+
}
|
|
727
|
+
function virtualRow(binding, row) {
|
|
728
|
+
return binding.rowItems.find((item) => item.renderItem.kind === "row" && item.renderItem.row.key === row.key);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/hierarchy.ts
|
|
733
|
+
const hierarchyProps = {
|
|
734
|
+
treeColumn: {
|
|
735
|
+
type: String,
|
|
736
|
+
default: void 0
|
|
737
|
+
},
|
|
738
|
+
keepAliveDetail: {
|
|
739
|
+
type: Boolean,
|
|
740
|
+
default: false
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
const hierarchyKey = Symbol("vueye-table-hierarchy");
|
|
744
|
+
function provideHierarchy(table, props) {
|
|
745
|
+
const focus = shallowRef();
|
|
746
|
+
const focusKey = computed(() => table.rows.some((row) => row.key === focus.value) ? focus.value : table.rows[0]?.key);
|
|
747
|
+
const context = {
|
|
748
|
+
id: `vt-tree-${useId()}`,
|
|
749
|
+
table,
|
|
750
|
+
get treeColumn() {
|
|
751
|
+
return props.treeColumn;
|
|
752
|
+
},
|
|
753
|
+
get keepAliveDetail() {
|
|
754
|
+
return props.keepAliveDetail;
|
|
755
|
+
},
|
|
756
|
+
focus,
|
|
757
|
+
get focusKey() {
|
|
758
|
+
return focusKey.value;
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
provide(hierarchyKey, context);
|
|
762
|
+
return context;
|
|
763
|
+
}
|
|
764
|
+
function injectHierarchy() {
|
|
765
|
+
return inject(hierarchyKey, void 0);
|
|
766
|
+
}
|
|
767
|
+
function resolveTreeColumn(table, column) {
|
|
768
|
+
return table.columns.find((candidate) => candidate.id === column)?.id ?? table.columns[0]?.id;
|
|
769
|
+
}
|
|
770
|
+
function itemId(context, row, detail = false) {
|
|
771
|
+
return context ? `${context.id}-${encodeURIComponent(getRowItemKey(row.key, detail ? "detail" : "row"))}` : void 0;
|
|
772
|
+
}
|
|
773
|
+
const siblingCache = /* @__PURE__ */ new WeakMap();
|
|
774
|
+
function hierarchyRowAttrs(table, row, context) {
|
|
775
|
+
if (!table.tree) return {};
|
|
776
|
+
let positions = siblingCache.get(table.processedRows);
|
|
777
|
+
if (!positions) {
|
|
778
|
+
const groups = /* @__PURE__ */ new Map();
|
|
779
|
+
for (const candidate of table.processedRows) {
|
|
780
|
+
const group = groups.get(candidate.parentKey) ?? [];
|
|
781
|
+
group.push(candidate);
|
|
782
|
+
groups.set(candidate.parentKey, group);
|
|
783
|
+
}
|
|
784
|
+
const indices = new Map(table.processedRows.map((candidate, index) => [candidate.key, index]));
|
|
785
|
+
const result = /* @__PURE__ */ new Map();
|
|
786
|
+
for (const group of groups.values()) group.forEach((candidate, index) => result.set(candidate.key, {
|
|
787
|
+
position: index + 1,
|
|
788
|
+
size: group.length,
|
|
789
|
+
index: indices.get(candidate.key)
|
|
790
|
+
}));
|
|
791
|
+
positions = result;
|
|
792
|
+
siblingCache.set(table.processedRows, result);
|
|
793
|
+
}
|
|
794
|
+
const position = positions.get(row.key);
|
|
795
|
+
return {
|
|
796
|
+
id: itemId(context, row),
|
|
797
|
+
"data-tree-key": getRowItemKey(row.key),
|
|
798
|
+
"aria-level": row.depth + 1,
|
|
799
|
+
"aria-expanded": row.canExpand ? String(row.isExpanded) : void 0,
|
|
800
|
+
"aria-setsize": position?.size,
|
|
801
|
+
"aria-posinset": position?.position,
|
|
802
|
+
"aria-rowindex": position ? position.index + 2 : void 0,
|
|
803
|
+
"aria-busy": row.childStatus === "loading" ? "true" : void 0
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
/** Row-focused table navigation; cell-focused grids use the same pure core commands. */
|
|
807
|
+
function treeKeydown(event, context, virtual) {
|
|
808
|
+
const table = context.table;
|
|
809
|
+
const target = event.target;
|
|
810
|
+
if (!table.tree || !target?.closest) return;
|
|
811
|
+
if (target.closest("input, textarea, select, [contenteditable=true]") || target.closest("button") && !target.closest("[data-expand-toggle]")) return;
|
|
812
|
+
const key = target.closest("tr")?.getAttribute("data-tree-key");
|
|
813
|
+
const index = table.rows.findIndex((row) => getRowItemKey(row.key) === key);
|
|
814
|
+
const row = table.rows[index];
|
|
815
|
+
if (!row) return;
|
|
816
|
+
const command = gridCommand(event, {
|
|
817
|
+
canExpand: row.canExpand,
|
|
818
|
+
expanded: row.isExpanded
|
|
819
|
+
});
|
|
820
|
+
let next = row;
|
|
821
|
+
if (command?.type === "tree") {
|
|
822
|
+
if (command.action === "expand" || command.action === "collapse") table.toggleExpanded(row.key, command.action === "expand");
|
|
823
|
+
else if (command.action === "siblings") for (const sibling of table.processedRows.filter((candidate) => candidate.parentKey === row.parentKey && candidate.canExpand)) table.toggleExpanded(sibling.key, true);
|
|
824
|
+
else if (command.action === "child") next = table.rows.find((candidate) => candidate.parentKey === row.key) ?? row;
|
|
825
|
+
else next = table.rows.find((candidate) => candidate.key === row.parentKey) ?? row;
|
|
826
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowUp") next = table.rows[index + (event.key === "ArrowDown" ? 1 : -1)] ?? row;
|
|
827
|
+
else if (event.key === "Home" || event.key === "End") next = table.rows[event.key === "Home" ? 0 : table.rows.length - 1] ?? row;
|
|
828
|
+
else return;
|
|
829
|
+
event.preventDefault();
|
|
830
|
+
context.focus.value = next.key;
|
|
831
|
+
virtual?.rows.scrollToKey(next.key);
|
|
832
|
+
const id = itemId(context, next);
|
|
833
|
+
nextTick(() => target.closest("table")?.ownerDocument.getElementById(id ?? "")?.focus());
|
|
834
|
+
}
|
|
835
|
+
const expandToggleProps = {
|
|
836
|
+
row: rowProp,
|
|
837
|
+
controls: {
|
|
838
|
+
type: String,
|
|
839
|
+
default: void 0
|
|
840
|
+
},
|
|
841
|
+
expandLabel: {
|
|
842
|
+
type: String,
|
|
843
|
+
default: "Expand row"
|
|
844
|
+
},
|
|
845
|
+
collapseLabel: {
|
|
846
|
+
type: String,
|
|
847
|
+
default: "Collapse row"
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
const treeCellProps = {
|
|
851
|
+
row: rowProp,
|
|
852
|
+
as: asProp("div")
|
|
853
|
+
};
|
|
854
|
+
const DataTableExpandToggle = defineComponent({
|
|
855
|
+
name: "DataTableExpandToggle",
|
|
856
|
+
props: expandToggleProps,
|
|
857
|
+
slots: Object,
|
|
858
|
+
setup(props, { slots }) {
|
|
859
|
+
const table = injectDataTable("<DataTableExpandToggle>");
|
|
860
|
+
const context = injectHierarchy();
|
|
861
|
+
const grid = injectDataGrid();
|
|
862
|
+
return () => props.row.canExpand ? h("button", {
|
|
863
|
+
type: "button",
|
|
864
|
+
"data-expand-toggle": "",
|
|
865
|
+
tabindex: table.tree ? -1 : void 0,
|
|
866
|
+
"aria-expanded": String(props.row.isExpanded),
|
|
867
|
+
"aria-controls": props.controls ?? (!table.tree ? itemId(context, props.row, true) : props.row.isExpanded ? table.rows.filter((row) => row.parentKey === props.row.key).map((row) => itemId(context, row)).filter(Boolean).join(" ") || void 0 : void 0),
|
|
868
|
+
"aria-label": props.row.isExpanded ? props.collapseLabel : props.expandLabel,
|
|
869
|
+
onMousedown: (event) => event.stopPropagation(),
|
|
870
|
+
onDblclick: (event) => event.stopPropagation(),
|
|
871
|
+
onClick: (event) => {
|
|
872
|
+
event.stopPropagation();
|
|
873
|
+
if (grid?.table === table) grid.focusCell({
|
|
874
|
+
row: table.rows.indexOf(props.row),
|
|
875
|
+
column: Math.max(0, table.columns.findIndex((column) => column.id === resolveTreeColumn(table, context?.treeColumn)))
|
|
876
|
+
});
|
|
877
|
+
table.toggleExpanded(props.row.key);
|
|
878
|
+
if (grid?.table === table) nextTick(() => event.target.closest("table")?.focus());
|
|
879
|
+
}
|
|
880
|
+
}, slots.default?.({
|
|
881
|
+
row: props.row,
|
|
882
|
+
expanded: props.row.isExpanded
|
|
883
|
+
}) ?? (props.row.isExpanded ? props.collapseLabel : props.expandLabel)) : null;
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
const DataTableTreeCell = defineComponent({
|
|
887
|
+
name: "DataTableTreeCell",
|
|
888
|
+
props: treeCellProps,
|
|
889
|
+
slots: Object,
|
|
890
|
+
setup(props, { slots }) {
|
|
891
|
+
const table = injectDataTable("<DataTableTreeCell>");
|
|
892
|
+
const context = injectHierarchy();
|
|
893
|
+
const grid = injectDataGrid();
|
|
894
|
+
return () => h(props.as, {
|
|
895
|
+
class: "vt-tree-cell",
|
|
896
|
+
"data-tree-cell": "",
|
|
897
|
+
style: { paddingInlineStart: `calc(var(--vt-tree-indent, 1.25rem) * ${props.row.depth})` }
|
|
898
|
+
}, [
|
|
899
|
+
h("span", { class: "vt-tree-toggle" }, slots.toggle?.({ row: props.row }) ?? h(DataTableExpandToggle, { row: props.row })),
|
|
900
|
+
h("span", { class: "vt-tree-label" }, [slots.default?.()]),
|
|
901
|
+
props.row.childStatus === "loading" ? h("span", { class: "vt-tree-loading" }, "Loading children…") : null,
|
|
902
|
+
props.row.childStatus === "error" ? h("span", { class: "vt-tree-error" }, ["Could not load children. ", h("button", {
|
|
903
|
+
type: "button",
|
|
904
|
+
class: "vt-tree-retry",
|
|
905
|
+
onMousedown: (event) => event.stopPropagation(),
|
|
906
|
+
onClick: (event) => {
|
|
907
|
+
event.stopPropagation();
|
|
908
|
+
const element = event.currentTarget.closest("table");
|
|
909
|
+
if (grid?.table === table) grid.focusCell({
|
|
910
|
+
row: table.rows.indexOf(props.row),
|
|
911
|
+
column: Math.max(0, table.columns.findIndex((column) => column.id === resolveTreeColumn(table, context?.treeColumn)))
|
|
912
|
+
});
|
|
913
|
+
table.toggleExpanded(props.row.key, true);
|
|
914
|
+
nextTick(() => {
|
|
915
|
+
if (grid?.table === table) element?.focus();
|
|
916
|
+
else element?.ownerDocument.getElementById(itemId(context, props.row) ?? "")?.focus();
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
}, "Retry children")]) : null
|
|
920
|
+
]);
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
/** One full-width detail; retained collapsed content remains hidden and unmeasured. */
|
|
924
|
+
const DataTableDetailRow = defineComponent({
|
|
925
|
+
name: "DataTableDetailRow",
|
|
926
|
+
props: {
|
|
927
|
+
row: rowProp,
|
|
928
|
+
colspan: {
|
|
929
|
+
type: Number,
|
|
930
|
+
default: void 0
|
|
931
|
+
},
|
|
932
|
+
hidden: {
|
|
933
|
+
type: Boolean,
|
|
934
|
+
default: false
|
|
935
|
+
},
|
|
936
|
+
keepAlive: {
|
|
937
|
+
type: Boolean,
|
|
938
|
+
default: void 0
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
slots: Object,
|
|
942
|
+
setup(props, { slots }) {
|
|
943
|
+
const table = injectDataTable("<DataTableDetailRow>");
|
|
944
|
+
const context = injectHierarchy();
|
|
945
|
+
const virtual = injectVirtual();
|
|
946
|
+
let opened = false;
|
|
947
|
+
return () => {
|
|
948
|
+
if (props.row.isExpanded && !props.hidden) opened = true;
|
|
949
|
+
if (!opened || (!props.row.isExpanded || props.hidden) && !(props.keepAlive ?? context?.keepAliveDetail)) return null;
|
|
950
|
+
const hidden = props.hidden || !props.row.isExpanded;
|
|
951
|
+
return h("tr", {
|
|
952
|
+
id: itemId(context, props.row, true),
|
|
953
|
+
"data-detail": "",
|
|
954
|
+
hidden,
|
|
955
|
+
...virtual && !hidden ? { ref: (target) => virtual.rows.measureElement(target, getRowItemKey(props.row.key, "detail")) } : {}
|
|
956
|
+
}, [h("td", {
|
|
957
|
+
colspan: Math.max(1, props.colspan ?? table.columns.length),
|
|
958
|
+
class: "vt-detail-cell"
|
|
959
|
+
}, [slots.default?.({ row: props.row })])]);
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
function createDetails(table, slot, colspan) {
|
|
964
|
+
const context = injectHierarchy();
|
|
965
|
+
const visited = /* @__PURE__ */ new Set();
|
|
966
|
+
const render = (row, rowIndex, hidden = false) => {
|
|
967
|
+
if (!hidden && context?.keepAliveDetail) visited.add(row.key);
|
|
968
|
+
return h(DataTableDetailRow, {
|
|
969
|
+
key: getRowItemKey(row.key, "detail"),
|
|
970
|
+
row,
|
|
971
|
+
colspan: colspan(),
|
|
972
|
+
hidden
|
|
973
|
+
}, { default: () => slot()?.({
|
|
974
|
+
row,
|
|
975
|
+
rowIndex
|
|
976
|
+
}) });
|
|
977
|
+
};
|
|
978
|
+
return {
|
|
979
|
+
render,
|
|
980
|
+
retained(visible) {
|
|
981
|
+
if (!context?.keepAliveDetail) return [];
|
|
982
|
+
return [...visited].flatMap((key) => {
|
|
983
|
+
const index = table.rows.findIndex((row) => row.key === key);
|
|
984
|
+
if (index < 0) {
|
|
985
|
+
visited.delete(key);
|
|
986
|
+
return [];
|
|
987
|
+
}
|
|
988
|
+
return visible.has(key) ? [] : [render(table.rows[index], index, true)];
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
}
|
|
273
993
|
|
|
274
994
|
//#endregion
|
|
275
995
|
//#region src/table.ts
|
|
@@ -285,15 +1005,34 @@ const DataTableRoot = defineComponent({
|
|
|
285
1005
|
type: Object,
|
|
286
1006
|
required: true
|
|
287
1007
|
},
|
|
288
|
-
as: asProp("table")
|
|
1008
|
+
as: asProp("table"),
|
|
1009
|
+
...virtualProps,
|
|
1010
|
+
...hierarchyProps
|
|
289
1011
|
},
|
|
290
1012
|
setup(props, { slots }) {
|
|
291
1013
|
provideDataTable(props.table);
|
|
292
|
-
|
|
293
|
-
|
|
1014
|
+
const hierarchy = provideHierarchy(props.table, props);
|
|
1015
|
+
const element = shallowRef();
|
|
1016
|
+
const editingGrid = injectEditingGrid();
|
|
1017
|
+
const render = (virtual) => h(props.as, {
|
|
1018
|
+
role: props.table.tree ? "treegrid" : void 0,
|
|
1019
|
+
"aria-rowcount": (props.table.tree ? props.table.processedRows.length : props.table.rowCount) + 1,
|
|
294
1020
|
"aria-colcount": props.table.columns.length,
|
|
295
|
-
"
|
|
1021
|
+
"aria-busy": props.table.loadState === "loading" || props.table.loadState === "streaming" ? "true" : void 0,
|
|
1022
|
+
"data-empty": flag(props.table.rowCount === 0),
|
|
1023
|
+
onKeydown: (event) => treeKeydown(event, hierarchy, virtual),
|
|
1024
|
+
...virtual ? {
|
|
1025
|
+
ref: element,
|
|
1026
|
+
"data-virtual": ""
|
|
1027
|
+
} : {}
|
|
296
1028
|
}, slots.default?.({ table: props.table }) ?? [h(DataTableHeader), h(DataTableBody)]);
|
|
1029
|
+
return () => props.virtual ? h(VirtualContent, {
|
|
1030
|
+
table: props.table,
|
|
1031
|
+
...editingGrid ? { grid: editingGrid } : {},
|
|
1032
|
+
options: virtualOptions(props),
|
|
1033
|
+
tableElement: () => element.value,
|
|
1034
|
+
render
|
|
1035
|
+
}) : render();
|
|
297
1036
|
}
|
|
298
1037
|
});
|
|
299
1038
|
const DataTableCaption = defineComponent({
|
|
@@ -311,7 +1050,11 @@ const DataTableHeader = defineComponent({
|
|
|
311
1050
|
props: { as: asProp("thead") },
|
|
312
1051
|
setup(props, { slots }) {
|
|
313
1052
|
const table = injectDataTable("<DataTableHeader>");
|
|
314
|
-
|
|
1053
|
+
const virtual = injectVirtual();
|
|
1054
|
+
return () => h(props.as, slots.default?.({ columns: table.columns }) ?? h(DataTableHeaderRow, () => virtual?.columns ? h(DataTableVirtualColumns, null, { default: ({ column }) => [h(DataTableHeaderCell, {
|
|
1055
|
+
key: column.id,
|
|
1056
|
+
column
|
|
1057
|
+
})] }) : table.columns.map((column) => h(DataTableHeaderCell, {
|
|
315
1058
|
key: column.id,
|
|
316
1059
|
column
|
|
317
1060
|
}))));
|
|
@@ -325,6 +1068,23 @@ const DataTableHeaderRow = defineComponent({
|
|
|
325
1068
|
return () => h(props.as, { "aria-rowindex": 1 }, slots.default?.());
|
|
326
1069
|
}
|
|
327
1070
|
});
|
|
1071
|
+
/** Shared header/body column slice, with native spacer cells for omitted columns. */
|
|
1072
|
+
const DataTableVirtualColumns = defineComponent({
|
|
1073
|
+
name: "DataTableVirtualColumns",
|
|
1074
|
+
slots: Object,
|
|
1075
|
+
props: { as: {
|
|
1076
|
+
type: String,
|
|
1077
|
+
default: "th"
|
|
1078
|
+
} },
|
|
1079
|
+
setup(props, { slots }) {
|
|
1080
|
+
const table = injectDataTable("<DataTableVirtualColumns>");
|
|
1081
|
+
const virtual = injectVirtual();
|
|
1082
|
+
return () => h(Fragment, renderColumns(table.columns, virtual, (column, index) => slots.default?.({
|
|
1083
|
+
column,
|
|
1084
|
+
index
|
|
1085
|
+
}), props.as));
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
328
1088
|
/**
|
|
329
1089
|
* A column header with `aria-sort`. Without a slot it shows the header text inside a sort button
|
|
330
1090
|
* for sortable columns.
|
|
@@ -338,6 +1098,7 @@ const DataTableHeaderCell = defineComponent({
|
|
|
338
1098
|
},
|
|
339
1099
|
setup(props, { slots }) {
|
|
340
1100
|
const table = injectDataTable("<DataTableHeaderCell>");
|
|
1101
|
+
const virtual = injectVirtual();
|
|
341
1102
|
return () => {
|
|
342
1103
|
const { column } = props;
|
|
343
1104
|
const sort = table.getSort(column.id);
|
|
@@ -354,7 +1115,8 @@ const DataTableHeaderCell = defineComponent({
|
|
|
354
1115
|
"data-align": column.align,
|
|
355
1116
|
"data-sortable": flag(column.sortable),
|
|
356
1117
|
"data-sort": sort?.direction,
|
|
357
|
-
style: columnStyle(column)
|
|
1118
|
+
style: columnStyle(column),
|
|
1119
|
+
...virtual?.columns ? { "aria-colindex": table.columns.indexOf(column) + 1 } : {}
|
|
358
1120
|
}, content);
|
|
359
1121
|
};
|
|
360
1122
|
}
|
|
@@ -397,38 +1159,114 @@ const DataTableSortButton = defineComponent({
|
|
|
397
1159
|
const DataTableBody = defineComponent({
|
|
398
1160
|
name: "DataTableBody",
|
|
399
1161
|
slots: Object,
|
|
400
|
-
props: {
|
|
1162
|
+
props: {
|
|
1163
|
+
as: asProp("tbody"),
|
|
1164
|
+
...virtualProps,
|
|
1165
|
+
colspan: {
|
|
1166
|
+
type: Number,
|
|
1167
|
+
default: void 0
|
|
1168
|
+
}
|
|
1169
|
+
},
|
|
401
1170
|
setup(props, { slots }) {
|
|
402
1171
|
const table = injectDataTable("<DataTableBody>");
|
|
1172
|
+
const virtual = injectVirtual();
|
|
1173
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length);
|
|
403
1174
|
return () => {
|
|
1175
|
+
if (props.virtual && !virtual) return h(VirtualDataTableBody, props, slots);
|
|
1176
|
+
if (virtual) return renderTableBody(table, virtual, props.as, props.colspan, slots, details);
|
|
404
1177
|
let content = slots.default?.({ rows: table.rows });
|
|
405
|
-
if (!content) content = table.rows.length === 0 && slots.empty ? h("tr", { "data-empty": "" }, [h("td", { colspan: Math.max(1, table.columns.length) }, slots.empty())]) : table.
|
|
406
|
-
key:
|
|
407
|
-
row
|
|
408
|
-
|
|
409
|
-
|
|
1178
|
+
if (!content) content = table.rows.length === 0 && slots.empty ? h("tr", { "data-empty": "" }, [h("td", { colspan: Math.max(1, table.columns.length) }, slots.empty())]) : table.renderItems.map((item) => item.kind === "row" ? slots.row ? h(Fragment, { key: item.key }, [slots.row(item)]) : h(DataTableRow, {
|
|
1179
|
+
key: item.key,
|
|
1180
|
+
row: item.row,
|
|
1181
|
+
rowIndex: item.rowIndex
|
|
1182
|
+
}) : details.render(item.row, item.rowIndex));
|
|
1183
|
+
const visibleDetails = new Set(table.renderItems.filter((item) => item.kind === "detail").map((item) => item.row.key));
|
|
1184
|
+
const retained = slots.default ? [] : details.retained(visibleDetails);
|
|
1185
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length);
|
|
1186
|
+
return h(props.as, [
|
|
1187
|
+
...Array.isArray(content) ? content : content ? [content] : [],
|
|
1188
|
+
...retained,
|
|
1189
|
+
...sentinel ? [sentinel] : []
|
|
1190
|
+
]);
|
|
410
1191
|
};
|
|
411
1192
|
}
|
|
412
1193
|
});
|
|
1194
|
+
function renderTableBody(table, virtual, as, colspan, slots, details, element) {
|
|
1195
|
+
const width = colspan ?? table.columns.length;
|
|
1196
|
+
const attrs = element ? { ref: element } : {};
|
|
1197
|
+
const content = slots.default?.({ rows: virtual.rowItems.filter((item) => item.renderItem.kind === "row").map((item) => item.renderItem.row) });
|
|
1198
|
+
if (table.rows.length === 0 && slots.empty) return h(as, attrs, h("tr", { "data-empty": "" }, [h("td", { colspan: Math.max(1, width) }, slots.empty())]));
|
|
1199
|
+
if (content) return h(as, attrs, [
|
|
1200
|
+
rowSpacer(virtual.rows.paddingStart, width, "gap-start"),
|
|
1201
|
+
...content,
|
|
1202
|
+
rowSpacer(virtual.rows.paddingEnd, width, "gap-end"),
|
|
1203
|
+
...loadSentinel(table, width) ? [loadSentinel(table, width)] : []
|
|
1204
|
+
]);
|
|
1205
|
+
return h(as, attrs, [
|
|
1206
|
+
...renderRows(virtual, width, (item) => item.renderItem.kind === "row" ? slots.row ? h(Fragment, { key: item.key }, [slots.row(item.renderItem)]) : h(DataTableRow, {
|
|
1207
|
+
key: item.key,
|
|
1208
|
+
row: item.renderItem.row,
|
|
1209
|
+
rowIndex: item.renderItem.rowIndex
|
|
1210
|
+
}) : details.render(item.renderItem.row, item.renderItem.rowIndex)),
|
|
1211
|
+
...details.retained(new Set(virtual.rowItems.filter((item) => item.renderItem.kind === "detail").map((item) => item.renderItem.row.key))),
|
|
1212
|
+
...loadSentinel(table, width) ? [loadSentinel(table, width)] : []
|
|
1213
|
+
]);
|
|
1214
|
+
}
|
|
1215
|
+
const VirtualDataTableBody = defineComponent({
|
|
1216
|
+
name: "DataTableVirtualBody",
|
|
1217
|
+
props: {
|
|
1218
|
+
as: asProp("tbody"),
|
|
1219
|
+
...virtualProps,
|
|
1220
|
+
colspan: {
|
|
1221
|
+
type: Number,
|
|
1222
|
+
default: void 0
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
slots: Object,
|
|
1226
|
+
setup(props, { slots }) {
|
|
1227
|
+
const table = injectDataTable("<DataTableBody>");
|
|
1228
|
+
const element = shallowRef();
|
|
1229
|
+
const virtual = createComponentVirtual(table, virtualOptions(props), () => element.value?.closest("table"));
|
|
1230
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length);
|
|
1231
|
+
return () => renderTableBody(table, virtual, props.as, props.colspan, slots, details, element);
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
413
1234
|
/** A body row; `aria-selected` and `data-selected` follow the selection. */
|
|
414
1235
|
const DataTableRow = defineComponent({
|
|
415
1236
|
name: "DataTableRow",
|
|
416
1237
|
slots: Object,
|
|
417
1238
|
props: {
|
|
418
1239
|
row: rowProp,
|
|
419
|
-
as: asProp("tr")
|
|
1240
|
+
as: asProp("tr"),
|
|
1241
|
+
rowIndex: {
|
|
1242
|
+
type: Number,
|
|
1243
|
+
default: void 0
|
|
1244
|
+
}
|
|
420
1245
|
},
|
|
421
1246
|
setup(props, { slots }) {
|
|
422
1247
|
const table = injectDataTable("<DataTableRow>");
|
|
1248
|
+
const virtual = injectVirtual();
|
|
1249
|
+
const hierarchy = injectHierarchy();
|
|
423
1250
|
return () => {
|
|
424
1251
|
const { row } = props;
|
|
425
1252
|
const selected = table.isSelected(row.key);
|
|
426
|
-
const
|
|
1253
|
+
const item = virtual ? virtualRow(virtual, row) : void 0;
|
|
1254
|
+
const position = props.rowIndex ?? item?.renderItem.rowIndex ?? table.rows.indexOf(row);
|
|
427
1255
|
return h(props.as, {
|
|
428
1256
|
"aria-rowindex": position === -1 ? void 0 : table.pageStart + position + 1,
|
|
429
1257
|
"aria-selected": table.selectionMode === "none" ? void 0 : String(selected),
|
|
430
1258
|
"data-selected": flag(selected),
|
|
431
|
-
"data-key": String(row.key)
|
|
1259
|
+
"data-key": String(row.key),
|
|
1260
|
+
"data-dirty": flag(row.isDirty),
|
|
1261
|
+
...hierarchyRowAttrs(table, row, hierarchy),
|
|
1262
|
+
tabindex: table.tree ? (hierarchy?.focusKey ?? table.rows[0]?.key) === row.key ? 0 : -1 : void 0,
|
|
1263
|
+
onFocus: () => {
|
|
1264
|
+
if (hierarchy) hierarchy.focus.value = row.key;
|
|
1265
|
+
},
|
|
1266
|
+
...virtual && item ? {
|
|
1267
|
+
ref: (element) => virtual.rows.measureElement(element, item.key),
|
|
1268
|
+
"data-virtual-row": ""
|
|
1269
|
+
} : {}
|
|
432
1270
|
}, slots.default?.({
|
|
433
1271
|
row,
|
|
434
1272
|
selected,
|
|
@@ -448,22 +1286,29 @@ const DataTableCell = defineComponent({
|
|
|
448
1286
|
props: {
|
|
449
1287
|
row: rowProp,
|
|
450
1288
|
column: columnProp,
|
|
451
|
-
as: asProp("td")
|
|
1289
|
+
as: asProp("td"),
|
|
1290
|
+
tree: {
|
|
1291
|
+
type: Boolean,
|
|
1292
|
+
default: true
|
|
1293
|
+
}
|
|
452
1294
|
},
|
|
453
1295
|
setup(props, { slots }) {
|
|
1296
|
+
const table = injectDataTable("<DataTableCell>");
|
|
1297
|
+
const hierarchy = injectHierarchy();
|
|
454
1298
|
return () => {
|
|
455
1299
|
const { row, column } = props;
|
|
456
1300
|
const value = row.getValue(column.id);
|
|
457
1301
|
const display = row.getDisplay(column.id);
|
|
458
|
-
|
|
459
|
-
"data-column": column.id,
|
|
460
|
-
"data-align": column.align
|
|
461
|
-
}, slots.default?.({
|
|
1302
|
+
const content = () => slots.default?.({
|
|
462
1303
|
row,
|
|
463
1304
|
column,
|
|
464
1305
|
value,
|
|
465
1306
|
display
|
|
466
|
-
}) ?? display
|
|
1307
|
+
}) ?? display;
|
|
1308
|
+
return h(props.as, {
|
|
1309
|
+
"data-column": column.id,
|
|
1310
|
+
"data-align": column.align
|
|
1311
|
+
}, props.tree && table.tree && column.id === resolveTreeColumn(table, hierarchy?.treeColumn) ? h(DataTableTreeCell, { row }, { default: content }) : content());
|
|
467
1312
|
};
|
|
468
1313
|
}
|
|
469
1314
|
});
|
|
@@ -478,6 +1323,292 @@ const DataTableEmpty = defineComponent({
|
|
|
478
1323
|
}
|
|
479
1324
|
});
|
|
480
1325
|
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/editing.ts
|
|
1328
|
+
const editingKey = Symbol("vueye-table-editing");
|
|
1329
|
+
function injectEditingGrid() {
|
|
1330
|
+
return inject(editingKey, void 0)?.grid;
|
|
1331
|
+
}
|
|
1332
|
+
/** Renderless provider for editable data-table cells and row forms. */
|
|
1333
|
+
const DataTableEditRoot = defineComponent({
|
|
1334
|
+
name: "DataTableEditRoot",
|
|
1335
|
+
slots: Object,
|
|
1336
|
+
props: {
|
|
1337
|
+
table: {
|
|
1338
|
+
type: Object,
|
|
1339
|
+
required: true
|
|
1340
|
+
},
|
|
1341
|
+
mode: {
|
|
1342
|
+
type: String,
|
|
1343
|
+
default: "cell"
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
emits: {
|
|
1347
|
+
save: (_result) => true,
|
|
1348
|
+
cancel: (_key) => true
|
|
1349
|
+
},
|
|
1350
|
+
setup(props, { slots, emit, expose }) {
|
|
1351
|
+
const grid = useDataGrid(props.table);
|
|
1352
|
+
const draft = shallowRef();
|
|
1353
|
+
const context = {
|
|
1354
|
+
grid,
|
|
1355
|
+
get mode() {
|
|
1356
|
+
return props.mode;
|
|
1357
|
+
},
|
|
1358
|
+
get draft() {
|
|
1359
|
+
return draft.value;
|
|
1360
|
+
},
|
|
1361
|
+
start(row) {
|
|
1362
|
+
if (draft.value?.pending) return;
|
|
1363
|
+
if (draft.value) context.cancel(draft.value.rowKey);
|
|
1364
|
+
grid.cancelEdit();
|
|
1365
|
+
const column = props.table.columns.findIndex((candidate) => candidate.isEditable(row.original));
|
|
1366
|
+
grid.focusCell({
|
|
1367
|
+
row: props.table.rows.findIndex((candidate) => candidate.key === row.key),
|
|
1368
|
+
column
|
|
1369
|
+
});
|
|
1370
|
+
grid.startEdit();
|
|
1371
|
+
draft.value = props.table.editRow(row.key);
|
|
1372
|
+
},
|
|
1373
|
+
async save() {
|
|
1374
|
+
const active = draft.value;
|
|
1375
|
+
if (!active || active.pending) return void 0;
|
|
1376
|
+
const result = await active.save();
|
|
1377
|
+
if (draft.value === active && active.status !== "editing" && !active.pending) {
|
|
1378
|
+
draft.value = void 0;
|
|
1379
|
+
grid.cancelEdit();
|
|
1380
|
+
}
|
|
1381
|
+
if (result.status !== "rejected") emit("save", result);
|
|
1382
|
+
return result;
|
|
1383
|
+
},
|
|
1384
|
+
cancel(key) {
|
|
1385
|
+
if (draft.value?.rowKey === key && !draft.value.pending) {
|
|
1386
|
+
draft.value.cancel();
|
|
1387
|
+
draft.value = void 0;
|
|
1388
|
+
grid.cancelEdit();
|
|
1389
|
+
emit("cancel", key);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
provide(editingKey, context);
|
|
1394
|
+
expose({ editing: context });
|
|
1395
|
+
watch(() => props.table.rows, (rows) => {
|
|
1396
|
+
if (draft.value && !rows.some((row) => row.key === draft.value?.rowKey)) context.cancel(draft.value.rowKey);
|
|
1397
|
+
});
|
|
1398
|
+
onScopeDispose(() => draft.value?.cancel());
|
|
1399
|
+
return () => slots.default?.({ editing: context });
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
/** A cell with on-demand editing; row mode shares one draft and one save operation. */
|
|
1403
|
+
const DataTableEditCell = defineComponent({
|
|
1404
|
+
name: "DataTableEditCell",
|
|
1405
|
+
props: {
|
|
1406
|
+
row: rowProp,
|
|
1407
|
+
column: columnProp,
|
|
1408
|
+
rowIndex: {
|
|
1409
|
+
type: Number,
|
|
1410
|
+
default: void 0
|
|
1411
|
+
},
|
|
1412
|
+
tree: {
|
|
1413
|
+
type: Boolean,
|
|
1414
|
+
default: true
|
|
1415
|
+
}
|
|
1416
|
+
},
|
|
1417
|
+
slots: Object,
|
|
1418
|
+
emits: {
|
|
1419
|
+
save: (_result) => true,
|
|
1420
|
+
cancel: (_key) => true
|
|
1421
|
+
},
|
|
1422
|
+
setup(props, { slots, emit }) {
|
|
1423
|
+
const table = injectDataTable("<DataTableEditCell>");
|
|
1424
|
+
const context = inject(editingKey);
|
|
1425
|
+
if (!context) throw new Error("<DataTableEditCell> requires <DataTableEditRoot>.");
|
|
1426
|
+
const { grid } = context;
|
|
1427
|
+
const id = useEditorId();
|
|
1428
|
+
const element = shallowRef();
|
|
1429
|
+
const position = () => ({
|
|
1430
|
+
row: props.rowIndex ?? table.rows.indexOf(props.row),
|
|
1431
|
+
column: table.columns.indexOf(props.column)
|
|
1432
|
+
});
|
|
1433
|
+
const active = computed(() => context.mode === "row" ? context.draft?.rowKey === props.row.key : grid.isEditing(position()));
|
|
1434
|
+
const pending = computed(() => context.mode === "row" ? !!context.draft?.pending : active.value && grid.lastResult?.status === "pending");
|
|
1435
|
+
const localText = shallowRef("");
|
|
1436
|
+
watch(active, (value) => {
|
|
1437
|
+
if (value) localText.value = editorText(props.row.getValue(props.column.id), grid.editorFor(position())?.kind ?? "text");
|
|
1438
|
+
});
|
|
1439
|
+
const focus = (direction) => {
|
|
1440
|
+
nextTick(() => {
|
|
1441
|
+
if (!direction) {
|
|
1442
|
+
element.value?.focus();
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const all = [...element.value?.closest("table")?.querySelectorAll("[data-edit-cell][data-editable]") ?? []];
|
|
1446
|
+
all[all.indexOf(element.value) + (direction === "left" ? -1 : 1)]?.focus();
|
|
1447
|
+
});
|
|
1448
|
+
};
|
|
1449
|
+
const submit = (value, direction) => {
|
|
1450
|
+
if (context.mode === "row") {
|
|
1451
|
+
if (value !== void 0) {
|
|
1452
|
+
if (typeof value === "string") {
|
|
1453
|
+
localText.value = value;
|
|
1454
|
+
context.draft?.setValue(props.column.id, value);
|
|
1455
|
+
context.draft?.setInput(props.column.id, value);
|
|
1456
|
+
} else context.draft?.setValue(props.column.id, value);
|
|
1457
|
+
}
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
const result = grid.submitEdit(value === void 0 ? void 0 : typeof value === "string" ? { input: value } : { value });
|
|
1461
|
+
const finish = (final) => {
|
|
1462
|
+
if (final.status !== "rejected") {
|
|
1463
|
+
emit("save", final);
|
|
1464
|
+
focus(direction);
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
if (result?.completion) result.completion.then(finish);
|
|
1468
|
+
else if (result) finish(result);
|
|
1469
|
+
return result;
|
|
1470
|
+
};
|
|
1471
|
+
return () => {
|
|
1472
|
+
const { row, column } = props;
|
|
1473
|
+
const editable = column.isEditable(row.original);
|
|
1474
|
+
const editing = editable && active.value;
|
|
1475
|
+
const draft = context.draft?.rowKey === row.key ? context.draft : void 0;
|
|
1476
|
+
const issues = draft?.issues.filter((problem) => problem.column === column.id) ?? (row.cellIssues?.get(column.id) ? [row.cellIssues.get(column.id)] : active.value ? grid.lastResult?.issues.filter((problem) => problem.rowKey === row.key && (problem.column === column.id || !problem.column)) ?? [] : []);
|
|
1477
|
+
const spec = editing ? grid.editorFor(position()) ?? { kind: "text" } : void 0;
|
|
1478
|
+
const input = (text) => {
|
|
1479
|
+
localText.value = text;
|
|
1480
|
+
if (context.mode === "row") {
|
|
1481
|
+
draft?.setValue(column.id, text);
|
|
1482
|
+
draft?.setInput(column.id, text);
|
|
1483
|
+
} else grid.updateDraft(text);
|
|
1484
|
+
};
|
|
1485
|
+
const cancel = () => {
|
|
1486
|
+
if (pending.value) return;
|
|
1487
|
+
if (context.mode === "row") context.cancel(row.key);
|
|
1488
|
+
else {
|
|
1489
|
+
grid.cancelEdit();
|
|
1490
|
+
emit("cancel", row.key);
|
|
1491
|
+
}
|
|
1492
|
+
focus();
|
|
1493
|
+
};
|
|
1494
|
+
const editor = {
|
|
1495
|
+
row,
|
|
1496
|
+
column,
|
|
1497
|
+
value: context.mode === "row" ? Reflect.get(draft?.values ?? {}, column.id) : row.getValue(column.id),
|
|
1498
|
+
draft: context.mode === "row" ? localText.value : grid.editor?.draft ?? "",
|
|
1499
|
+
spec: spec ?? { kind: "text" },
|
|
1500
|
+
finish: (direction) => {
|
|
1501
|
+
if (context.mode === "row") context.save();
|
|
1502
|
+
else submit(void 0, direction === "left" || direction === "right" ? direction : void 0);
|
|
1503
|
+
},
|
|
1504
|
+
input,
|
|
1505
|
+
update: input,
|
|
1506
|
+
commit: submit,
|
|
1507
|
+
cancel,
|
|
1508
|
+
issues,
|
|
1509
|
+
pending: pending.value,
|
|
1510
|
+
attrs: issueAttrs(id, issues, pending.value)
|
|
1511
|
+
};
|
|
1512
|
+
return h(DataTableCell, {
|
|
1513
|
+
ref: (target) => {
|
|
1514
|
+
element.value = target && typeof target === "object" && "$el" in target ? target.$el : void 0;
|
|
1515
|
+
},
|
|
1516
|
+
row,
|
|
1517
|
+
column,
|
|
1518
|
+
tree: props.tree,
|
|
1519
|
+
"data-edit-cell": "",
|
|
1520
|
+
"data-editable": editable ? "" : void 0,
|
|
1521
|
+
"data-editing": editing ? "" : void 0,
|
|
1522
|
+
"data-dirty": row.isDirty ? "" : void 0,
|
|
1523
|
+
...issueAttrs(id, issues, pending.value),
|
|
1524
|
+
tabindex: editable ? 0 : void 0,
|
|
1525
|
+
onFocus: (event) => {
|
|
1526
|
+
if (event.target === element.value && !grid.editor) grid.focusCell(position());
|
|
1527
|
+
},
|
|
1528
|
+
onDblclick: () => {
|
|
1529
|
+
if (editable && context.mode === "cell") {
|
|
1530
|
+
grid.focusCell(position());
|
|
1531
|
+
grid.startEdit(editorText(row.getValue(column.id), grid.editorFor(position())?.kind ?? "text"));
|
|
1532
|
+
}
|
|
1533
|
+
},
|
|
1534
|
+
onKeydown: (event) => {
|
|
1535
|
+
if (event.target === element.value && event.key === "Enter" && editable) {
|
|
1536
|
+
event.preventDefault();
|
|
1537
|
+
event.stopPropagation();
|
|
1538
|
+
if (context.mode === "row") context.start(row);
|
|
1539
|
+
else {
|
|
1540
|
+
grid.focusCell(position());
|
|
1541
|
+
grid.startEdit(editorText(row.getValue(column.id), grid.editorFor(position())?.kind ?? "text"));
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}, { default: ({ value, display }) => [editing ? slots.editor?.(editor) ?? h(DataCellEditor, {
|
|
1546
|
+
editor,
|
|
1547
|
+
spec: spec ?? { kind: "text" },
|
|
1548
|
+
autofocus: context.mode === "cell" || table.columns.find((candidate) => candidate.isEditable(row.original))?.id === column.id,
|
|
1549
|
+
blurCommit: context.mode === "cell",
|
|
1550
|
+
tabCommit: context.mode === "cell",
|
|
1551
|
+
finish: (direction) => {
|
|
1552
|
+
if (context.mode === "row") context.save();
|
|
1553
|
+
else submit(void 0, direction === "left" || direction === "right" ? direction : void 0);
|
|
1554
|
+
}
|
|
1555
|
+
}) : slots.default?.({
|
|
1556
|
+
row,
|
|
1557
|
+
value,
|
|
1558
|
+
display
|
|
1559
|
+
}) ?? display, editorIssues(id, issues, pending.value)] });
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
/** One active row form. Controls share the provider's draft and core save/undo path. */
|
|
1564
|
+
const DataTableRowActions = defineComponent({
|
|
1565
|
+
name: "DataTableRowActions",
|
|
1566
|
+
props: {
|
|
1567
|
+
row: rowProp,
|
|
1568
|
+
editable: {
|
|
1569
|
+
type: Boolean,
|
|
1570
|
+
default: true
|
|
1571
|
+
},
|
|
1572
|
+
removable: {
|
|
1573
|
+
type: Boolean,
|
|
1574
|
+
default: false
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
setup(props) {
|
|
1578
|
+
const table = injectDataTable("<DataTableRowActions>");
|
|
1579
|
+
const context = inject(editingKey, void 0);
|
|
1580
|
+
const id = useEditorId();
|
|
1581
|
+
const element = shallowRef();
|
|
1582
|
+
watch(() => context?.draft?.rowKey === props.row.key, (active, previous) => {
|
|
1583
|
+
const target = element.value;
|
|
1584
|
+
if (previous && !active && !context?.draft && target?.closest("tr")?.contains(target.ownerDocument.activeElement)) nextTick(() => target.querySelector("button[data-edit-row]")?.focus());
|
|
1585
|
+
});
|
|
1586
|
+
return () => {
|
|
1587
|
+
const draft = context?.draft?.rowKey === props.row.key ? context.draft : void 0;
|
|
1588
|
+
const issues = [...new Map(draft?.issues.map((problem) => [problem.message, problem])).values()];
|
|
1589
|
+
const button = (label, action, disabled = false) => h("button", {
|
|
1590
|
+
type: "button",
|
|
1591
|
+
disabled,
|
|
1592
|
+
"aria-label": `${label}, row ${String(props.row.key)}`,
|
|
1593
|
+
"data-edit-row": label === "Edit row" ? "" : void 0,
|
|
1594
|
+
...label === "Save row" ? issueAttrs(id, issues, !!draft?.pending) : {},
|
|
1595
|
+
onClick: action
|
|
1596
|
+
}, label);
|
|
1597
|
+
return h("div", {
|
|
1598
|
+
ref: element,
|
|
1599
|
+
"data-row-actions": "",
|
|
1600
|
+
role: "group",
|
|
1601
|
+
"aria-label": `Row ${String(props.row.key)} actions`,
|
|
1602
|
+
"aria-busy": draft?.pending ? "true" : void 0
|
|
1603
|
+
}, [
|
|
1604
|
+
...context?.mode === "row" && props.editable ? draft ? [button("Save row", () => context.save(), draft.pending), button("Cancel", () => context.cancel(props.row.key), draft.pending)] : [button("Edit row", () => context.start(props.row))] : [],
|
|
1605
|
+
h("details", [h("summary", { "aria-label": `Actions for row ${String(props.row.key)}` }, "Actions"), h("div", { "data-row-menu": "" }, [button("Revert row", () => table.revert([props.row.key]), !props.row.isDirty || !!draft), props.removable ? button("Remove row", () => table.removeRows([props.row.key]), !!draft) : null])]),
|
|
1606
|
+
editorIssues(id, issues, !!draft?.pending)
|
|
1607
|
+
]);
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
|
|
481
1612
|
//#endregion
|
|
482
1613
|
//#region src/grid.ts
|
|
483
1614
|
const gridContextKey = Symbol("vueye-table-grid-context");
|
|
@@ -510,12 +1641,24 @@ const DataGridRoot = defineComponent({
|
|
|
510
1641
|
label: {
|
|
511
1642
|
type: String,
|
|
512
1643
|
default: void 0
|
|
1644
|
+
},
|
|
1645
|
+
...virtualProps,
|
|
1646
|
+
...hierarchyProps,
|
|
1647
|
+
/** Width reserved before data columns, for a row-number/header column. */
|
|
1648
|
+
gutter: {
|
|
1649
|
+
type: Number,
|
|
1650
|
+
default: 0
|
|
513
1651
|
}
|
|
514
1652
|
},
|
|
515
|
-
|
|
1653
|
+
emits: {
|
|
1654
|
+
save: (_result) => true,
|
|
1655
|
+
cancel: (_key) => true
|
|
1656
|
+
},
|
|
1657
|
+
setup(props, { slots, expose, emit }) {
|
|
516
1658
|
provideDataTable(props.table);
|
|
517
|
-
const grid = useDataGrid(props.table);
|
|
1659
|
+
const grid = useDataGrid(props.table, { treeColumn: () => props.treeColumn });
|
|
518
1660
|
provideDataGrid(grid);
|
|
1661
|
+
provideHierarchy(props.table, props);
|
|
519
1662
|
const element = ref();
|
|
520
1663
|
const id = `vt-grid-${useId()}`;
|
|
521
1664
|
const focus = () => {
|
|
@@ -523,14 +1666,52 @@ const DataGridRoot = defineComponent({
|
|
|
523
1666
|
};
|
|
524
1667
|
provide(gridContextKey, {
|
|
525
1668
|
id,
|
|
526
|
-
focus
|
|
1669
|
+
focus,
|
|
1670
|
+
cancel: (key) => emit("cancel", key)
|
|
1671
|
+
});
|
|
1672
|
+
watch(() => grid.lastResult, (result) => {
|
|
1673
|
+
if (result && [
|
|
1674
|
+
"applied",
|
|
1675
|
+
"partial",
|
|
1676
|
+
"unchanged"
|
|
1677
|
+
].includes(result.status)) emit("save", result);
|
|
527
1678
|
});
|
|
528
1679
|
expose({
|
|
529
1680
|
grid,
|
|
530
1681
|
focus
|
|
531
1682
|
});
|
|
1683
|
+
let virtual;
|
|
532
1684
|
const onKeydown = (event) => {
|
|
533
1685
|
if (event.target !== element.value) return;
|
|
1686
|
+
if (event.key === " " && !grid.editor && !event.ctrlKey && !event.metaKey && !event.altKey && grid.toggleCheckbox()) {
|
|
1687
|
+
event.preventDefault();
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
if (virtual && !grid.editor && (event.ctrlKey || event.metaKey) && (event.key === "Home" || event.key === "End")) {
|
|
1691
|
+
grid.focusCell(event.key === "Home" ? {
|
|
1692
|
+
row: 0,
|
|
1693
|
+
column: 0
|
|
1694
|
+
} : {
|
|
1695
|
+
row: props.table.rows.length - 1,
|
|
1696
|
+
column: props.table.columns.length - 1
|
|
1697
|
+
}, { extend: event.shiftKey });
|
|
1698
|
+
event.preventDefault();
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
if (virtual && !grid.editor && (event.key === "PageUp" || event.key === "PageDown")) {
|
|
1702
|
+
const current = grid.selection?.focus ?? {
|
|
1703
|
+
row: 0,
|
|
1704
|
+
column: 0
|
|
1705
|
+
};
|
|
1706
|
+
const item = virtual.rowItems.find((entry) => entry.renderItem.rowIndex === current.row);
|
|
1707
|
+
const step = Math.max(1, Math.floor(virtual.rows.viewportSize / (item?.size ?? props.rowHeight)));
|
|
1708
|
+
grid.focusCell({
|
|
1709
|
+
row: current.row + (event.key === "PageDown" ? step : -step),
|
|
1710
|
+
column: current.column
|
|
1711
|
+
}, { extend: event.shiftKey });
|
|
1712
|
+
event.preventDefault();
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
534
1715
|
if (grid.handleKey(event)) event.preventDefault();
|
|
535
1716
|
};
|
|
536
1717
|
const onCopy = (event, cut = false) => {
|
|
@@ -540,32 +1721,54 @@ const DataGridRoot = defineComponent({
|
|
|
540
1721
|
if (cut) grid.clear();
|
|
541
1722
|
};
|
|
542
1723
|
const onPaste = (event) => {
|
|
1724
|
+
if (grid.editor || event.defaultPrevented) return;
|
|
543
1725
|
const text = event.clipboardData?.getData("text/plain");
|
|
544
|
-
if (
|
|
1726
|
+
if (text === void 0 || text === "") return;
|
|
545
1727
|
event.preventDefault();
|
|
546
1728
|
grid.paste(text);
|
|
547
1729
|
};
|
|
548
|
-
|
|
1730
|
+
const render = (binding) => {
|
|
1731
|
+
virtual = binding;
|
|
549
1732
|
const focusPosition = grid.selection?.focus;
|
|
550
1733
|
return h(props.as, {
|
|
551
1734
|
ref: element,
|
|
552
1735
|
id,
|
|
553
|
-
role: "grid",
|
|
1736
|
+
role: props.table.tree ? "treegrid" : "grid",
|
|
554
1737
|
tabindex: 0,
|
|
555
1738
|
"aria-label": props.label,
|
|
556
1739
|
"aria-multiselectable": "true",
|
|
557
|
-
"aria-rowcount": props.table.rows.length + 1,
|
|
1740
|
+
"aria-rowcount": (props.table.tree ? props.table.processedRows.length : binding ? props.table.rowCount : props.table.rows.length) + 1,
|
|
558
1741
|
"aria-colcount": props.table.columns.length,
|
|
1742
|
+
"aria-busy": props.table.loadState === "loading" || props.table.loadState === "streaming" ? "true" : void 0,
|
|
559
1743
|
"aria-activedescendant": focusPosition && !grid.editor ? cellId(id, focusPosition.row, focusPosition.column) : void 0,
|
|
560
1744
|
onKeydown,
|
|
561
1745
|
onCopy: (event) => onCopy(event),
|
|
562
1746
|
onCut: (event) => onCopy(event, true),
|
|
563
|
-
onPaste
|
|
564
|
-
|
|
1747
|
+
onPaste,
|
|
1748
|
+
...binding ? {
|
|
1749
|
+
"data-virtual": "",
|
|
1750
|
+
style: binding.columns ? {
|
|
1751
|
+
tableLayout: "fixed",
|
|
1752
|
+
width: `${binding.columns.totalSize + binding.gutter}px`
|
|
1753
|
+
} : void 0
|
|
1754
|
+
} : {}
|
|
1755
|
+
}, binding ? [virtualColgroup(props.table.columns, binding), slots.default?.({
|
|
1756
|
+
table: props.table,
|
|
1757
|
+
grid
|
|
1758
|
+
}) ?? [h(DataTableHeader), h(DataGridBody)]] : slots.default?.({
|
|
565
1759
|
table: props.table,
|
|
566
1760
|
grid
|
|
567
1761
|
}) ?? [h(DataTableHeader), h(DataGridBody)]);
|
|
568
1762
|
};
|
|
1763
|
+
return () => props.virtual ? h(VirtualContent, {
|
|
1764
|
+
table: props.table,
|
|
1765
|
+
grid,
|
|
1766
|
+
options: virtualOptions(props),
|
|
1767
|
+
virtualColumns: props.virtualColumns,
|
|
1768
|
+
gutter: props.gutter,
|
|
1769
|
+
tableElement: () => element.value,
|
|
1770
|
+
render
|
|
1771
|
+
}) : render();
|
|
569
1772
|
}
|
|
570
1773
|
});
|
|
571
1774
|
/**
|
|
@@ -575,70 +1778,103 @@ const DataGridRoot = defineComponent({
|
|
|
575
1778
|
const DataGridBody = defineComponent({
|
|
576
1779
|
name: "DataGridBody",
|
|
577
1780
|
slots: Object,
|
|
578
|
-
props: {
|
|
1781
|
+
props: {
|
|
1782
|
+
as: asProp("tbody"),
|
|
1783
|
+
...virtualProps,
|
|
1784
|
+
colspan: {
|
|
1785
|
+
type: Number,
|
|
1786
|
+
default: void 0
|
|
1787
|
+
},
|
|
1788
|
+
treeCell: {
|
|
1789
|
+
type: Boolean,
|
|
1790
|
+
default: true
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
579
1793
|
setup(props, { slots }) {
|
|
580
1794
|
const table = injectDataTable("<DataGridBody>");
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
row,
|
|
592
|
-
column,
|
|
593
|
-
rowIndex,
|
|
594
|
-
columnIndex
|
|
595
|
-
}, slots.cell ? { default: (cellProps) => slots.cell?.(cellProps) } : void 0))])));
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
/** The text box shown in a cell while it is edited. */
|
|
599
|
-
const DataGridEditor = defineComponent({
|
|
600
|
-
name: "DataGridEditor",
|
|
601
|
-
props: { label: {
|
|
602
|
-
type: String,
|
|
603
|
-
required: true
|
|
604
|
-
} },
|
|
605
|
-
setup(props) {
|
|
606
|
-
const { grid, context } = useGrid("<DataGridEditor>");
|
|
607
|
-
const input = ref();
|
|
608
|
-
onMounted(() => {
|
|
609
|
-
const element = input.value;
|
|
610
|
-
if (element) {
|
|
611
|
-
element.focus();
|
|
612
|
-
element.setSelectionRange(element.value.length, element.value.length);
|
|
1795
|
+
const { grid } = useGrid("<DataGridBody>");
|
|
1796
|
+
const virtual = injectVirtual();
|
|
1797
|
+
const hierarchy = injectHierarchy();
|
|
1798
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
1799
|
+
const element = ref();
|
|
1800
|
+
const render = (binding) => {
|
|
1801
|
+
if (binding && slots.default) {
|
|
1802
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
1803
|
+
const content = slots.default({ rows: binding.rowItems.filter((item) => item.renderItem.kind === "row").map((item) => item.renderItem.row) });
|
|
1804
|
+
return h(props.as, { ref: element }, sentinel ? [...content, sentinel] : content);
|
|
613
1805
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1806
|
+
if (binding) return h(props.as, { ref: element }, [
|
|
1807
|
+
...renderRows(binding, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0), (item) => {
|
|
1808
|
+
if (item.renderItem.kind !== "row") return details.render(item.renderItem.row, item.renderItem.rowIndex);
|
|
1809
|
+
const { row, rowIndex } = item.renderItem;
|
|
1810
|
+
const columns = renderColumns(table.columns, binding, (column, columnIndex) => h(DataGridCell, {
|
|
1811
|
+
key: column.id,
|
|
1812
|
+
row,
|
|
1813
|
+
column,
|
|
1814
|
+
rowIndex,
|
|
1815
|
+
columnIndex,
|
|
1816
|
+
tree: props.treeCell
|
|
1817
|
+
}, {
|
|
1818
|
+
default: slots.cell,
|
|
1819
|
+
editor: slots.editor,
|
|
1820
|
+
$stable: true
|
|
1821
|
+
}), "td");
|
|
1822
|
+
return h("tr", {
|
|
1823
|
+
key: item.key,
|
|
1824
|
+
role: "row",
|
|
1825
|
+
"aria-rowindex": table.pageStart + rowIndex + 1,
|
|
1826
|
+
"data-key": String(row.key),
|
|
1827
|
+
"data-dirty": flag(row.isDirty),
|
|
1828
|
+
...hierarchyRowAttrs(table, row, hierarchy),
|
|
1829
|
+
"data-virtual-row": "",
|
|
1830
|
+
ref: (target) => binding.rows.measureElement(target, item.key)
|
|
1831
|
+
}, [slots.rowHeader?.({
|
|
1832
|
+
row,
|
|
1833
|
+
index: rowIndex
|
|
1834
|
+
}), ...columns]);
|
|
1835
|
+
}),
|
|
1836
|
+
...details.retained(new Set(binding.rowItems.filter((item) => item.renderItem.kind === "detail").map((item) => item.renderItem.row.key))),
|
|
1837
|
+
...loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0)) ? [loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0))] : []
|
|
1838
|
+
]);
|
|
1839
|
+
const content = slots.default?.({ rows: table.rows }) ?? table.renderItems.map((item) => item.kind === "detail" ? details.render(item.row, item.rowIndex) : (() => {
|
|
1840
|
+
const { row, rowIndex } = item;
|
|
1841
|
+
return h("tr", {
|
|
1842
|
+
key: item.key,
|
|
1843
|
+
role: "row",
|
|
1844
|
+
"aria-rowindex": rowIndex + 2,
|
|
1845
|
+
"data-key": String(row.key),
|
|
1846
|
+
"data-dirty": flag(row.isDirty),
|
|
1847
|
+
...hierarchyRowAttrs(table, row, hierarchy)
|
|
1848
|
+
}, [slots.rowHeader?.({
|
|
1849
|
+
row,
|
|
1850
|
+
index: rowIndex
|
|
1851
|
+
}), ...table.columns.map((column, columnIndex) => h(DataGridCell, {
|
|
1852
|
+
key: column.id,
|
|
1853
|
+
row,
|
|
1854
|
+
column,
|
|
1855
|
+
rowIndex,
|
|
1856
|
+
columnIndex,
|
|
1857
|
+
tree: props.treeCell
|
|
1858
|
+
}, {
|
|
1859
|
+
default: slots.cell,
|
|
1860
|
+
editor: slots.editor,
|
|
1861
|
+
$stable: true
|
|
1862
|
+
}))]);
|
|
1863
|
+
})());
|
|
1864
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
1865
|
+
return h(props.as, [
|
|
1866
|
+
...content,
|
|
1867
|
+
...slots.default ? [] : details.retained(new Set(table.renderItems.filter((item) => item.kind === "detail").map((item) => item.row.key))),
|
|
1868
|
+
sentinel
|
|
1869
|
+
]);
|
|
618
1870
|
};
|
|
619
|
-
return () => h(
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (grid.editor) grid.commitEdit();
|
|
627
|
-
},
|
|
628
|
-
onKeydown: (event) => {
|
|
629
|
-
if (event.key === "Enter") {
|
|
630
|
-
event.preventDefault();
|
|
631
|
-
finish(event.shiftKey ? "up" : "down");
|
|
632
|
-
} else if (event.key === "Tab") {
|
|
633
|
-
event.preventDefault();
|
|
634
|
-
finish(event.shiftKey ? "left" : "right");
|
|
635
|
-
} else if (event.key === "Escape") {
|
|
636
|
-
event.preventDefault();
|
|
637
|
-
grid.cancelEdit();
|
|
638
|
-
context.focus();
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
});
|
|
1871
|
+
return () => props.virtual && !virtual ? h(VirtualContent, {
|
|
1872
|
+
table,
|
|
1873
|
+
grid,
|
|
1874
|
+
options: virtualOptions(props),
|
|
1875
|
+
tableElement: () => element.value?.closest("table"),
|
|
1876
|
+
render
|
|
1877
|
+
}) : render(virtual);
|
|
642
1878
|
}
|
|
643
1879
|
});
|
|
644
1880
|
/**
|
|
@@ -652,6 +1888,10 @@ const DataGridCell = defineComponent({
|
|
|
652
1888
|
row: rowProp,
|
|
653
1889
|
column: columnProp,
|
|
654
1890
|
as: asProp("td"),
|
|
1891
|
+
tree: {
|
|
1892
|
+
type: Boolean,
|
|
1893
|
+
default: true
|
|
1894
|
+
},
|
|
655
1895
|
/** The row's position on the page. Found by searching the rows when left out. */
|
|
656
1896
|
rowIndex: {
|
|
657
1897
|
type: Number,
|
|
@@ -666,13 +1906,16 @@ const DataGridCell = defineComponent({
|
|
|
666
1906
|
setup(props, { slots }) {
|
|
667
1907
|
const table = injectDataTable("<DataGridCell>");
|
|
668
1908
|
const { grid, context } = useGrid("<DataGridCell>");
|
|
1909
|
+
const virtual = injectVirtual();
|
|
1910
|
+
const hierarchy = injectHierarchy();
|
|
669
1911
|
const element = ref();
|
|
1912
|
+
const issueId = useEditorId();
|
|
670
1913
|
const position = () => ({
|
|
671
1914
|
row: props.rowIndex ?? table.rows.indexOf(props.row),
|
|
672
1915
|
column: props.columnIndex ?? table.columns.indexOf(props.column)
|
|
673
1916
|
});
|
|
674
1917
|
watch(() => grid.isFocused(position()), (focused) => {
|
|
675
|
-
if (focused) element.value?.scrollIntoView?.({
|
|
1918
|
+
if (focused && !virtual) element.value?.scrollIntoView?.({
|
|
676
1919
|
block: "nearest",
|
|
677
1920
|
inline: "nearest"
|
|
678
1921
|
});
|
|
@@ -684,20 +1927,57 @@ const DataGridCell = defineComponent({
|
|
|
684
1927
|
const editable = column.isEditable(row.original);
|
|
685
1928
|
const value = row.getValue(column.id);
|
|
686
1929
|
const display = row.getDisplay(column.id);
|
|
1930
|
+
const issues = row.cellIssues?.get(column.id) ? [row.cellIssues.get(column.id)] : grid.lastResult?.issues.filter((problem) => problem.rowKey === row.key && (problem.column === column.id || !problem.column)) ?? [];
|
|
1931
|
+
const pending = table.pendingCells.some((cell) => cell.rowKey === row.key && cell.column === column.id);
|
|
1932
|
+
const finish = (edit, direction) => {
|
|
1933
|
+
const result = grid.submitEdit(edit, direction);
|
|
1934
|
+
const focus = () => {
|
|
1935
|
+
if (!grid.editor) context.focus();
|
|
1936
|
+
};
|
|
1937
|
+
if (result?.completion) result.completion.then(focus);
|
|
1938
|
+
else focus();
|
|
1939
|
+
return result;
|
|
1940
|
+
};
|
|
687
1941
|
let content;
|
|
688
|
-
if (editing)
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
1942
|
+
if (editing) {
|
|
1943
|
+
const editor = {
|
|
1944
|
+
row,
|
|
1945
|
+
column,
|
|
1946
|
+
value,
|
|
1947
|
+
draft: grid.editor?.draft ?? "",
|
|
1948
|
+
spec: grid.editorFor(at) ?? { kind: "text" },
|
|
1949
|
+
finish: (direction) => {
|
|
1950
|
+
finish(void 0, direction);
|
|
1951
|
+
},
|
|
1952
|
+
input: (text) => grid.updateDraft(text),
|
|
1953
|
+
update: (text) => grid.updateDraft(text),
|
|
1954
|
+
commit: (next) => finish(next === void 0 ? void 0 : typeof next === "string" ? { input: next } : { value: next }),
|
|
1955
|
+
cancel: () => {
|
|
1956
|
+
if (!pending) {
|
|
1957
|
+
grid.cancelEdit();
|
|
1958
|
+
context.cancel(row.key);
|
|
1959
|
+
context.focus();
|
|
1960
|
+
}
|
|
1961
|
+
},
|
|
1962
|
+
issues,
|
|
1963
|
+
pending,
|
|
1964
|
+
attrs: issueAttrs(issueId, issues, pending)
|
|
1965
|
+
};
|
|
1966
|
+
content = slots.editor?.(editor) ?? h(DataCellEditor, {
|
|
1967
|
+
editor,
|
|
1968
|
+
spec: grid.editorFor(at) ?? { kind: "text" },
|
|
1969
|
+
finish: (direction) => finish(void 0, direction)
|
|
1970
|
+
});
|
|
1971
|
+
} else {
|
|
1972
|
+
const children = () => slots.default?.({
|
|
1973
|
+
row,
|
|
1974
|
+
column,
|
|
1975
|
+
value,
|
|
1976
|
+
display,
|
|
1977
|
+
editable
|
|
1978
|
+
}) ?? display;
|
|
1979
|
+
content = props.tree && table.tree && column.id === resolveTreeColumn(table, hierarchy?.treeColumn) ? h(DataTableTreeCell, { row }, { default: children }) : children();
|
|
1980
|
+
}
|
|
701
1981
|
return h(props.as, {
|
|
702
1982
|
ref: element,
|
|
703
1983
|
id: cellId(context.id, at.row, at.column),
|
|
@@ -711,6 +1991,8 @@ const DataGridCell = defineComponent({
|
|
|
711
1991
|
"data-selected": flag(grid.isSelected(at)),
|
|
712
1992
|
"data-editing": flag(editing),
|
|
713
1993
|
"data-readonly": flag(!editable),
|
|
1994
|
+
"data-dirty": flag(row.isDirty),
|
|
1995
|
+
...issueAttrs(issueId, issues, pending),
|
|
714
1996
|
style: columnStyle(column),
|
|
715
1997
|
onMousedown: (event) => {
|
|
716
1998
|
if (editing || event.button !== 0) return;
|
|
@@ -722,13 +2004,13 @@ const DataGridCell = defineComponent({
|
|
|
722
2004
|
},
|
|
723
2005
|
onDblclick: () => {
|
|
724
2006
|
grid.focusCell(at);
|
|
725
|
-
grid.startEdit();
|
|
2007
|
+
grid.startEdit(editorText(value, grid.editorFor(at)?.kind ?? "text"));
|
|
726
2008
|
}
|
|
727
|
-
}, content);
|
|
2009
|
+
}, [content, editorIssues(issueId, issues, pending)]);
|
|
728
2010
|
};
|
|
729
2011
|
}
|
|
730
2012
|
});
|
|
731
2013
|
|
|
732
2014
|
//#endregion
|
|
733
|
-
export { DataGridBody, DataGridCell, DataGridRoot, DataTableBody, DataTableCaption, DataTableCell, DataTableColumnVisibility, DataTableEmpty, DataTableHeader, DataTableHeaderCell, DataTableHeaderRow, DataTablePageSize, DataTablePagination, DataTableRoot, DataTableRow, DataTableSearch, DataTableSelectAll, DataTableSelectRow, DataTableSortButton, DataTableStatus, cellId, columnStyle };
|
|
2015
|
+
export { DataCellEditor, DataGridBody, DataGridCell, DataGridRoot, DataTableBody, DataTableCaption, DataTableCell, DataTableColumnVisibility, DataTableDetailRow, DataTableEditCell, DataTableEditRoot, DataTableEmpty, DataTableExpandToggle, DataTableHeader, DataTableHeaderCell, DataTableHeaderRow, DataTableLoadMore, DataTablePageSize, DataTablePagination, DataTableRoot, DataTableRow, DataTableRowActions, DataTableSearch, DataTableSelectAll, DataTableSelectRow, DataTableSortButton, DataTableStatus, DataTableTreeCell, DataTableViewport, DataTableVirtualColumns, cellEditorProps, cellId, columnStyle, expandToggleProps, hierarchyProps, injectVirtual as injectVirtualRenderer, resolveTreeColumn, treeCellProps, virtualProps };
|
|
734
2016
|
//# sourceMappingURL=index.js.map
|