Move NiceTable away from defineModels

- Also improved display component support
This commit is contained in:
adroslice 2024-05-18 21:31:02 +02:00
parent 8508309b2c
commit f30b93d5dc

View file

@ -1,38 +1,68 @@
<script setup lang="ts">
import { useKeyModifier } from '@vueuse/core';
import type { Component } from 'vue';
import { equals } from '~/util';
const shiftState = $(useKeyModifier('Shift'));
const ctrlState = $(useKeyModifier('Control'));
const props = defineModels<{
columns?: {
heading: string,
path: string,
component?: string,
}[], // -
const props = withDefaults(defineProps<{
// Two-Way Bindings
data?: Record<string, any>[],
sort?: boolean,
sortSelf?: boolean, // -
sortBy?: string,
sortDesc?: boolean,
selection?: number[],
draggable?: boolean, // -
}>();
let { columns, data, sort, sortSelf, sortBy, sortDesc, selection, draggable } = $(props);
// One-Way Bindings
columns?: {
heading: string,
path: string,
component: Component,
}[],
sortSelf?: boolean,
draggable?: boolean,
}>(), {
columns: () => [],
data: () => [],
sort: false,
sortSelf: false,
sortBy: '',
sortDesc: false,
selection: () => [],
draggable: false,
});
const emit = defineEmits<{
(event: 'rowAction', index: number): void,
(event: 'selectionChanged'): void
(event: 'draggedRow', draggedRow: number, draggedOverRow: number): void,
(event: 'update:data', value: Record<string, any>[]): void,
(event: 'update:sort', value: boolean): void,
(event: 'update:sortBy', value: string): void,
(event: 'update:sortDesc', value: boolean): void,
(event: 'update:selection', value: number[]): void,
}>();
const componentProp: Record<string, string> = {
"EnumDisplay": "data",
};
// Hook up two-way bindings
let data = $ref(props.data);
watch(() => props.data, () => { if (!equals(props.data, data)) data = props.data; }, { deep: true });
watch($$(data), () => { if (!equals(data, props.data)) emit('update:data', data); }, { deep: true });
let sort = $ref(props.sort);
watch(() => props.sort, () => { if (!equals(props.sort, sort)) sort = props.sort; });
watch($$(sort), () => { if (!equals(sort, props.sort)) emit('update:sort', sort); });
let sortBy = $ref(props.sortBy);
watch(() => props.sortBy, () => { if (!equals(props.sortBy, sortBy)) sortBy = props.sortBy; });
watch($$(sortBy), () => { if (!equals(sortBy, props.sortBy)) emit('update:sortBy', sortBy); });
let sortDesc = $ref(props.sortDesc);
watch(() => props.sortDesc, () => { if (!equals(props.sortDesc, sortDesc)) sortDesc = props.sortDesc; });
watch($$(sortDesc), () => { if (!equals(sortDesc, props.sortDesc)) emit('update:sortDesc', sortDesc); });
let selection = $ref(props.selection);
watch(() => props.selection, () => { if (!equals(props.selection, selection)) selection = props.selection; }, { deep: true });
watch($$(selection), () => { if (!equals(selection, props.selection)) emit('update:selection', selection); }, { deep: true });
const displayData = $computed(() => (sortSelf && sortBy !== '')
? data?.sort((a, b) => {
const displayData = $computed(() => (props.sortSelf && sortBy !== '')
? data?.sort((a, b) => { // TODO Determine whether sorting a copy is necessary
selection = [];
let result;
if (a[sortBy ?? ''] > b[sortBy ?? '']) result = 1;
@ -59,7 +89,7 @@ function toggleRowSelection(index: number) {
} else if (ctrlState) // Toggle the presence of the row in the selection
selection = selection.includes(index)
? selection.filter(i => i !== index)
: selection = [...selection, index];
: [...selection, index];
else selection = selection.includes(index) ? [] : [index]; // Toggle between selection of none and this row
emit('selectionChanged');
}
@ -92,7 +122,7 @@ function dragDropRow() {
<table>
<thead>
<tr>
<th v-for="{heading, path} of columns" :key="path" @click="() => toggleSorting(path)">
<th v-for="{heading, path} of props.columns" :key="path" @click="() => toggleSorting(path)">
<div class="flex-row">
{{ heading }}
<template v-if="sort">
@ -106,18 +136,18 @@ function dragDropRow() {
<tbody>
<tr v-for="(row, index) in displayData" :key="index"
:draggable="draggable"
@click="() => toggleRowSelection(index)"
@dblclick="() => emit('rowAction', index)"
@dragstart="() => draggedRow = index"
@dragenter="() => draggedOverRow = index"
@dragend="() => dragDropRow()"
:class="{
'selected': (selection ?? []).includes(index),
'dragged-over-before': index === draggedOverRow && draggedOverRow < draggedRow,
'dragged-over-after': index === draggedOverRow && draggedOverRow > draggedRow,
}">
}"
@click="() => toggleRowSelection(index)"
@dblclick="() => emit('rowAction', index)"
@dragstart="() => draggedRow = index"
@dragenter="() => draggedOverRow = index"
@dragend="() => dragDropRow()">
<td v-for="{path, component} of columns" :key="path">
<component v-if="component" :is="component" v-bind="{[componentProp[component]]: atPath(row, path)}"/>
<component :is="component" v-if="component" :data="atPath(row, path)"/>
<template v-else>{{ atPath(row, path) }}</template>
</td>
</tr>