Skip to content

WtTable ​

Specs ​

Example Table ​

heading 1
heading2
heading 3
value 2
value 3
value 4
value 3
value 2
value 3
value 2
value 3
Code
vue
<script setup>
import { ref } from 'vue';

const headers = [
  {
    text: 'heading 1',
    value: 'name',
    sort: null,
  },
  {
    text: 'heading2',
    value: 'h2',
    sort: null,
  },
  {
    text: 'heading 3',
    value: 'h3',
  },
];

const data = ref([
  {
    id: 'id1',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: true,
  },
  {
    id: 'id2',
    h1: 'value 1',
    h2: 'value 4',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id3',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id4',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
]);

const sort = (col, sortValue) => {
  headers.forEach((header) => {
    header.sort = null;
  })
  const column = headers.find((header) => header.value === col.value);
  column.sort = sortValue;
};

const getRowClass = ({id}) => {
  switch (id) {
    case 'id1':
      return 'row-success';
    case 'id2':
      return 'row-warning';
    case 'id3':
      return 'row-error';
    default:
      return '';
  }
}
</script>

<template>
  <wt-table
    :headers="headers"
    :data="data"
    sortable
    grid-actions
    fixed-actions
    resizable-columns
    reorderable-columns
    :row-class="getRowClass"
    @sort="sort"
  >
  </wt-table>
</template>

<style scoped lang="scss"></style>

Table with Vertical Scroll ​

This example demonstrates how to use wt-table with virtual scrolling for large datasets. The table automatically loads more data as you scroll down.

heading 1
heading2
heading 3
Code
vue
<script setup>
import { ref } from 'vue';

let index = ref(4);
const lazy = ref(true)
const loading = ref(false)
const headers = [
  {
    text: 'heading 1',
    value: 'name',
    sort: null,
  },
  {
    text: 'heading2',
    value: 'h2',
    sort: null,
  },
  {
    text: 'heading 3',
    value: 'h3',
  },
];

const data = ref([
  {
    id: 'id1',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: true,
  },
  {
    id: 'id2',
    h1: 'value 1',
    h2: 'value 4',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id3',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id4',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
]);

const sort = (col, sortValue) => {
  headers.forEach((header) => {
    header.sort = null;
  })
  const column = headers.find((header) => header.value === col.value);
  column.sort = sortValue;
};

const getRowClass = ({id}) => {
  switch (id) {
    case 'id1':
      return 'row-success';
    case 'id2':
      return 'row-warning';
    case 'id3':
      return 'row-error';
    default:
      return '';
  }
}

const onLoading = () => {
  if (index.value > 50) {
    return;
  }

  setTimeout(() => {
  for (let i = 0; i < 20; i++) {
    data.value.push({
      id: `id${index.value}`,
      h1: 'value 1',
      h2: 'value 2',
      h3: 'value 3',
      _isSelected: true,
    })
    index.value++

  }


  }, 500)
}
</script>

<template>
  <wt-table
    class="table-scroll"
    :headers="headers"
    :data="data"
    :lazy="lazy"
    :on-loading="onLoading"
    :loading="loading"
    sortable
    grid-actions
    fixed-actions
    resizable-columns
    :row-class="getRowClass"
    @sort="sort"
  >
  </wt-table>
</template>

<style scoped lang="scss">
.table-scroll {
  height: 150px;
}
</style>

Table with Vertical Scroll ​

This example demonstrates how to use wt-table with custom headers.

custom header name: heading 1
custom header h2: heading2
heading 3
item id1
value 2
value 3
item id2
value 4
value 3
item id3
value 2
value 3
item id4
value 2
value 3
Code
vue
<script setup>
import { ref } from 'vue';

const headers = [
  {
    text: 'heading 1',
    value: 'name',
    sort: null,
  },
  {
    text: 'heading2',
    value: 'h2',
    sort: null,
  },
  {
    text: 'heading 3',
    value: 'h3',
  },
];

const data = ref([
  {
    id: 'id1',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: true,
  },
  {
    id: 'id2',
    h1: 'value 1',
    h2: 'value 4',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id3',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
  {
    id: 'id4',
    h1: 'value 1',
    h2: 'value 2',
    h3: 'value 3',
    _isSelected: false,
  },
]);

const sort = (col, sortValue) => {
  headers.forEach((header) => {
    header.sort = null;
  });
  const column = headers.find((header) => header.value === col.value);
  column.sort = sortValue;
};

const getRowClass = ({ id }) => {
  switch (id) {
    case 'id1':
      return 'row-success';
    case 'id2':
      return 'row-warning';
    case 'id3':
      return 'row-error';
    default:
      return '';
  }
};
</script>

<template>
  <wt-table
    :headers="headers"
    :data="data"
    sortable
    grid-actions
    fixed-actions
    resizable-columns
    reorderable-columns
    :row-class="getRowClass"
    @sort="sort"
  >
    <template #header-name="{ header }">
      custom header name: {{ header.text }}
    </template>
    <template #header-h2="{ header }">
      custom header h2: {{ header.text }}
    </template>
    <template #name="{ item }">
      item {{ item.id }}
    </template>
  </wt-table>
</template>

Key Features of this Example: ​

  • Lazy Loading: Data is loaded in chunks of 20 items as you scroll
  • Virtual Scrolling: Only visible rows are rendered for better performance
  • Dynamic Row Classes: Different rows have different styling based on their data
  • Sorting: Click column headers to sort data
  • Actions: Each row has action buttons (edit/delete)
  • Fixed Height: Table has a fixed height with internal scrolling
  • Loading States: Shows loading indicator during data fetch

How it Works: ​

  1. Initial Load: Starts with 4 items
  2. Scroll Detection: When you scroll near the bottom, onLoading is triggered
  3. Data Fetch: Simulates API call with 500ms delay
  4. Append Data: Adds 20 new items to the existing data
  5. Stop Condition: Stops loading after 50 items total

Virtual Scrolling Usage: ​

vue
<template>
  <wt-table
    :headers="headers"
    :data="data"
    :lazy="true"
    :loading="loading"
    :on-loading="handleLazyLoad"
    scrollable
    scroll-height="150px"
    sortable
    grid-actions
    fixed-actions
    resizable-columns
    :row-class="getRowClass"
    @sort="handleSort"
  />
</template>

<script setup>
import { ref } from 'vue';

const data = ref([]);
const loading = ref(false);
const lazy = ref(true);

const handleLazyLoad = () => {
  if (loading.value) return;

  loading.value = true;

  // Simulate API call
  setTimeout(() => {
    // Add new data
    for (let i = 0; i < 20; i++) {
      data.value.push({
        id: `id${index.value}`,
        name: 'New Item',
        // ... other properties
      });
    }

    loading.value = false;
  }, 500);
};
</script>

Filter column ​

Subject
Priority
Author
Payment is not going through
high
Anna Shevchenko
Cannot log in to the portal
high
Ihor Melnyk
Change the billing address
normal
Anna Shevchenko
Request a new SIP account
normal
Olena Kravets
Typo on the invoice template
low
Ihor Melnyk
Код
vue
<script setup>
import { computed, reactive } from 'vue';

import ExampleColumnFilter from './example-column-filter.vue';

const priorityOptions = [
	{
		id: 'high',
		label: 'High',
	},
	{
		id: 'normal',
		label: 'Normal',
	},
	{
		id: 'low',
		label: 'Low',
	},
];

const filters = reactive({});

const headers = computed(() => [
	{
		text: 'Subject',
		value: 'subject',
		width: '280px',
	},
	{
		text: 'Priority',
		value: 'priority',
		filter: 'priority',
		filtered: !!filters.priority,
		options: priorityOptions,
	},
	{
		text: 'Author',
		value: 'author',
		filter: 'author',
		filtered: !!filters.author,
	},
]);

const data = [
	{
		id: 1,
		subject: 'Payment is not going through',
		priority: 'high',
		author: 'Anna Shevchenko',
	},
	{
		id: 2,
		subject: 'Cannot log in to the portal',
		priority: 'high',
		author: 'Ihor Melnyk',
	},
	{
		id: 3,
		subject: 'Change the billing address',
		priority: 'normal',
		author: 'Anna Shevchenko',
	},
	{
		id: 4,
		subject: 'Request a new SIP account',
		priority: 'normal',
		author: 'Olena Kravets',
	},
	{
		id: 5,
		subject: 'Typo on the invoice template',
		priority: 'low',
		author: 'Ihor Melnyk',
	},
];

const shownData = computed(() =>
	data.filter((row) =>
		Object.entries(filters).every(([name, value]) =>
			String(row[name]).toLowerCase().includes(String(value).toLowerCase()),
		),
	),
);

const addFilter = ({ name, value }) => {
	filters[name] = value;
};

const deleteFilter = ({ name }) => {
	delete filters[name];
};
</script>

<template>
  <wt-table
    :headers="headers"
    :data="shownData"
  >
    <template #column-filter="{ header, formView, hide }">
      <example-column-filter
        :header="header"
        :form-view="formView"
        :hide="hide"
        :value="filters[header.value] || ''"
        @apply="addFilter"
        @clear="deleteFilter"
      />
    </template>
  </wt-table>
</template>

<style scoped lang="scss"></style>
vue
<script setup>
import { ref } from 'vue';

const props = defineProps({
	header: {
		type: Object,
		required: true,
	},
	formView: {
		type: Boolean,
		default: false,
	},
	hide: {
		type: Function,
		default: null,
	},
	value: {
		type: String,
		default: '',
	},
});

const emit = defineEmits([
	'apply',
	'clear',
]);
const draft = ref(props.value);

const apply = () => {
	emit('apply', {
		name: props.header.value,
		value: draft.value,
	});
	props.hide?.();
};

const clear = () => {
	draft.value = '';
	emit('clear', {
		name: props.header.value,
	});
	props.hide?.();
};
</script>

<template>
  <!-- vp-raw: попап телепортується в body, і без нього VitePress скидає стилі кнопок -->
  <div
    v-if="!formView"
    class="example-column-filter-preview vp-raw"
  >
    <p class="typo-body-2-bold">{{ header.text }}</p>
    <p class="typo-body-2">{{ value }}</p>
  </div>

  <form
    v-else
    class="example-column-filter vp-raw"
    @submit.prevent="apply"
  >
    <wt-single-select
      v-if="header.options"
      v-model="draft"
      :options="header.options"
      :label="header.text"
      option-value="id"
    />
    <wt-input-text
      v-else
      v-model="draft"
      :label="header.text"
    />

    <footer class="example-column-filter__footer">
      <wt-button
        :disabled="!draft"
        wide
        @click="apply"
      >
        Apply
      </wt-button>
      <wt-button
        color="secondary"
        wide
        @click="clear"
      >
        Clear
      </wt-button>
    </footer>
  </form>
</template>

<style scoped lang="scss">
.example-column-filter {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  box-sizing: border-box;
  width: 280px;
}

.example-column-filter__footer {
  display: flex;
  gap: var(--spacing-xs);
}

.example-column-filter-preview {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-3xs);
}
</style>

TIP

У таблиці на @webitel/ui-datalist цей компонент писати не треба — ColumnFilterComponent уже робить це поверх filtersManager вашого table store. Див. ui-datalist: Фільтри.