Skip to content

Commit

Permalink
feat: supporting virtualisation in dataTable
Browse files Browse the repository at this point in the history
  • Loading branch information
ApoorvaCG committed Oct 1, 2024
1 parent c911b81 commit 298aec5
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 72 deletions.
9 changes: 9 additions & 0 deletions src/components/DataTable/index.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,12 @@ export const ServerSide = ServerSideTemplate.bind({});
ServerSide.args = {
data,
};

export const Virtualised = DefaultTemplate.bind({});
Virtualised.args = {
data,
options: {
...options,
isVirtualised: true,
},
};
166 changes: 94 additions & 72 deletions src/components/DataTable/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -212,89 +212,95 @@
"
>
<!-- Rows -->
<div
v-for="(entry, idx) in sortedData"
:key="idx"
class="flex flex-col justify-between hover:bg-gray-100 hover:dark:bg-gray-700"
@click="onClickRow(entry, idx)"
@mouseover="emitMouseOverRow($event, entry)"
@mouseleave="emitMouseLeaveRow($event, entry)"
@mousemove="emitMouseMoveRow($event, entry)"
@mouseenter="emitMouseEnterRow($event, entry)"
>
<div v-if="entry.isHeader">
<slot
name="header-row"
:data="entry"
:style="{
gridTemplateColumns: sizes,
}"
/>
</div>
<div v-else>
<div v-bind="options.isVirtualised ? containerProps : ''" :class="options.isVirtualised ? 'h-screen' : ''">
<div v-bind="options.isVirtualised ? wrapperProps : ''" :class="options.isVirtualised ? 'h-full' : ''">
<div
class="datatable-grid-columns flex flex-col gap-x-2 gap-y-2 sm:grid sm:items-center"
:style="{
gridTemplateColumns: sizes,
}"
:class="entry.rowClass ?? ''"
v-for="(entry, idx) in sortedData"
:key="idx"
class="flex flex-col justify-between hover:bg-gray-100 hover:dark:bg-gray-700"
@click="onClickRow(entry, idx)"
@mouseover="emitMouseOverRow($event, entry)"
@mouseleave="emitMouseLeaveRow($event, entry)"
@mousemove="emitMouseMoveRow($event, entry)"
@mouseenter="emitMouseEnterRow($event, entry)"
>
<div>
<Checkbox
v-if="options.selection !== false"
v-model="checkboxSelected"
:value="entry[options.id]"
/>
</div>
<!-- Columns -->
<div
v-for="column in tableColumns"
:key="column.key"
class="grid min-h-[48px] grid-cols-2 items-center sm:flex"
:class="column.class ?? ''"
>
<div class="block sm:hidden" :class="column.class ?? ''">
{{ column.name }}
</div>
<div v-if="entry.isHeader">
<slot
v-if="$slots[column.key] && !loading"
:name="`${column.key}`"
name="header-row"
:data="entry"
:idx="idx"
:on-click="() => onClickCell(entry)"
:style="{
gridTemplateColumns: sizes,
}"
/>
<!-- Column content -->
</div>
<div v-else>
<div
v-else-if="!$slots[column.key] && !loading"
class="w-full overflow-hidden break-words"
@click="() => onClickCell(entry)"
class="datatable-grid-columns flex flex-col gap-x-2 gap-y-2 sm:grid sm:items-center"
:style="{
gridTemplateColumns: sizes,
}"
:class="entry.rowClass ?? ''"
>
{{
entry[column.key] === undefined
? 'No data'
: entry[column.key]
}}
<div>
<Checkbox
v-if="options.selection !== false"
v-model="checkboxSelected"
:value="entry[options.id]"
/>
</div>
<!-- Columns -->
<div
v-for="column in tableColumns"
:key="column.key"
class="grid min-h-[48px] grid-cols-2 items-center sm:flex"
:class="column.class ?? ''"
>
<div class="block sm:hidden" :class="column.class ?? ''">
{{ column.name }}
</div>
<slot
v-if="$slots[column.key] && !loading"
:name="`${column.key}`"
:data="entry"
:idx="idx"
:on-click="() => onClickCell(entry)"
/>
<!-- Column content -->
<div
v-else-if="!$slots[column.key] && !loading"
class="w-full overflow-hidden break-words"
@click="() => onClickCell(entry)"
>
{{
entry[column.key] === undefined
? 'No data'
: entry[column.key]
}}
</div>
<div
v-else
class="loading dark:loading-dark h-6 w-full"
></div>
</div>
</div>
<div
v-else
class="loading dark:loading-dark h-6 w-full"
></div>
<slot
name="collapsed"
:data="entry"
:style="{
display: 'grid',
gridTemplateColumns: sizes,
}"
/>
</div>
<Separator v-if="!options.removeSeparators" />
</div>
<slot
name="collapsed"
:data="entry"
:style="{
display: 'grid',
gridTemplateColumns: sizes,
}"
/>
</div>
<Separator v-if="!options.removeSeparators" />
</div>
</div>
</div>
</div>
</div>

<slot
v-if="$slots.footer"
name="footer"
Expand Down Expand Up @@ -326,7 +332,6 @@
/>
</div>
</div>
</div>
</template>

<script setup lang="ts">
Expand All @@ -348,6 +353,7 @@ import Input from '../Input/index.vue';
import Fuse from 'fuse.js';
import { debouncedWatch } from '@vueuse/shared';
import { RobustNotice } from '..';
import { useVirtualList } from '@vueuse/core';
export type Direction = 0 | -1 | 1;
Expand Down Expand Up @@ -389,6 +395,7 @@ type DataTableOptions = {
stickyHeaderClass?: string;
firstColumnSticky?: boolean;
removeSeparators?: boolean;
isVirtualised?: boolean;
};
const defaultOptions: Partial<DataTableOptions> = {
Expand All @@ -404,6 +411,7 @@ const defaultOptions: Partial<DataTableOptions> = {
selection: true,
search: true,
searchModel: '',
isVirtualised: false,
};
const props = defineProps({
Expand Down Expand Up @@ -556,13 +564,27 @@ const ghostColumns = computed(() => {
return sortedData.value.length;
});
const sortedData = computed(() => {
const { list, containerProps, wrapperProps } = useVirtualList(
props.data,
{
itemHeight: 40,
overscan: 2,
},
);
const sortedData = computed(() => {
if (loading.value) {
return Array(rowsLimit.value).fill({});
}
if (options.value.serverSide) {
return data.value;
}
if(options.value.isVirtualised) {
const data = list.value.map((d) => {
return d.data;
})
return data;
}
const sorted = sortData();
gotoPage(page.value);
const pageOffset = page.value - 1;
Expand All @@ -571,7 +593,7 @@ const sortedData = computed(() => {
});
const initMaxPage = () => {
if (options.value.serverSide) {
if (options.value.serverSide || options.value.isVirtualised) {
return options.value.maxPage ?? defaultOptions.maxPage;
}
return Math.ceil(data.value.length / rowsLimit.value);
Expand All @@ -580,7 +602,7 @@ const maxPageController = ref(initMaxPage());
const maxPage = computed({
get() {
if (options.value.serverSide) {
if (options.value.serverSide || options.value.isVirtualised) {
return initMaxPage();
}
return maxPageController.value;
Expand Down

0 comments on commit 298aec5

Please sign in to comment.