Skip to content

Table

Basic Usage

The simplest way to create a table is by defining the row-key, columns, and data props. The row-key prop represents a unique identifier for each data item. The columns prop is used to define the columns of the table, and the data prop is used to pass the data that the table should display.

If you want to customize the content of a cell, you can define the content and then pass it to the appropriate slot. The key attribute of the columns prop represents the slot name for the corresponding cell slot.

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 150,
  },
  {
    title: "Author",
    key: "author",
  },
];
</script>

<template>
  <UniTable row-key="id" :columns="columns" :data="data">
    <template #author="{ record }"> Author is {{ record.author }} </template>
  </UniTable>
</template>

Fixed Columns

The table supports fixed columns. For keeping the left columns fixed in the table, you can set the fixed attribute of the columns prop to left. Similarly, to keep the right columns fixed in the table, you can set the fixed attribute of the columns prop to right.

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    fixed: "left",
    width: 150,
  },
  {
    title: "Author",
    key: "author",
    width: 150,
  },
  {
    title: "Post",
    key: "post",
    width: 600,
  },
  {
    title: "Actions",
    key: "actions",
    fixed: "right",
    width: 100,
  },
];
</script>

<template>
  <UniTable row-key="id" class="container" :columns="columns" :data="data">
    <template #actions>Delete</template>
  </UniTable>
</template>

<style scoped>
.container {
  width: 100%;
  height: 500px;
}
</style>

Selection

The rows in the table can be selected by using the selection prop. The selection prop is an object that contains the following properties:

  • type: the type of selection, which can be either single or multiple
  • disabledCondition: a function that determine whether a row can be selected.

You can use the v-model:selectedRowKeys to get the selected row keys.

Additionally, there are two events that you can use to listen to changes in selection:

  • select: emitted when a row is selected
  • select-all: emitted when all rows are selected

Single Selection

If you want to ensure that only one row can be selected at a time, set the type attribute of the selection prop to single.

View Source
vue
<script setup lang="ts">
import { reactive } from "vue";
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const state = reactive({
  selectedRowKeys: new Set<string>(),
});

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 150,
  },
  {
    title: "Author",
    key: "author",
    width: 150,
  },
  {
    title: "Post",
    key: "post",
    width: 500,
  },
];

function disabledCondition(record: any) {
  return record.id === 1;
}
</script>

<template>
  <UniTable
    v-model:selected-row-keys="state.selectedRowKeys"
    row-key="id"
    class="container"
    :columns="columns"
    :data="data"
    :selection="{ type: 'single', disabledCondition }"
  >
  </UniTable>
</template>

<style scoped>
.container {
  width: 100%;
  height: 500px;
}
</style>

Multiple Selection

If you want to allow multiple rows to be selected in the table simultaneously, you can set the type attribute of the selection prop to multiple.

View Source
vue
<script setup lang="ts">
import { watch, reactive } from "vue";
import { UniTable, UniButton } from "unify-ui";

import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 150,
  },
  {
    title: "Author",
    key: "author",
    width: 150,
  },
  {
    title: "Post",
    key: "post",
    width: 500,
  },
];

function disabledCondition(record: any) {
  return record.id === 1;
}

const state = reactive({
  selectedRowKeys: new Set<string>(),
  selectedRows: new Map<string, any>(),
});

watch(
  () => [...state.selectedRowKeys],
  (value) => {
    console.log("🚀 ~ file: MultipleSelection.vue:47 ~ watch ~ value:", value);
  }
);

function handleChangeSelectionRows(params: { selected: boolean; records: any[] }) {
  params.records.forEach((item) => {
    if (params.selected) {
      state.selectedRows.set(item.id, item);
    } else {
      state.selectedRows.delete(item.id);
    }
  });
}

function handleSelection(params: { selected: boolean; rowKey: string | number; record: any }) {
  handleChangeSelectionRows({
    selected: params.selected,
    records: [params.record],
  });
}

function handleSelectAll(params: { selected: boolean; rowKeys: (string | number)[]; records: any[] }) {
  handleChangeSelectionRows({
    selected: params.selected,
    records: params.records,
  });
}

function handleClearSelection() {
  state.selectedRowKeys.clear();
}
</script>

<template>
  <UniButton class="button" type="soft" @click="handleClearSelection"> Clear selectedRowKeys </UniButton>

  <UniTable
    v-model:selected-row-keys="state.selectedRowKeys"
    row-key="id"
    class="container"
    :columns="columns"
    :data="data"
    :selection="{ type: 'multiple', disabledCondition }"
    @select="handleSelection"
    @select-all="handleSelectAll"
  >
  </UniTable>
</template>

<style scoped>
.button {
  margin-bottom: 20px;
}

.container {
  width: 100%;
  height: 500px;
}
</style>

Expandable Row

The rowExpand prop can be used to mark a row expandabled. The rowExpand prop is an object that contains the following properties:

  • expandCondition: a function that determine whether a row can be expanded.
  • showExpandRowDefault: whether to show expand row by default.

Additionally, you must define the content of the expanded row and pass it to the slot named rowExpand.

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 200,
  },
  {
    title: "Author",
    key: "author",
  },
];

const rowExpand: InstanceType<typeof UniTable>["$props"]["rowExpand"] = {
  expandCondition: (row) => row.description as boolean,
  showExpandRowDefault: false,
};
</script>

<template>
  <UniTable row-key="id" :columns="columns" :data="data" :row-expand="rowExpand">
    <template #rowExpand="{ record }">
      {{ `Expand row: ${record.description}` }}
    </template>
  </UniTable>
</template>

Custom Header

Sometimes, you may want to customize the content of a column header cell. In this case, when you define a column with column.key as author, you can pass the custom content to the slot named header-author.

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 200,
  },
  {
    title: "Author",
    key: "author",
  },
];
</script>

<template>
  <UniTable row-key="id" :columns="columns" :data="data">
    <template #title="{ record }"> title: {{ record.userId }} </template>
    <template #header-title>
      <span class="customer-header-text">This is custom title!</span>
    </template>
  </UniTable>
</template>

<style scoped>
.customer-header-text {
  color: red;
}
</style>

No Data

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 120,
  },
  {
    title: "Author",
    key: "author",
    width: 200,
  },
  {
    title: "Post",
    key: "post",
  },
];
</script>

<template>
  <div :style="{ marginBottom: '50px' }">
    <UniTable row-key="id" :columns="columns" :data="[]"> </UniTable>
  </div>

  <UniTable row-key="id" :columns="columns" :data="[]">
    <template #empty>
      <div class="placeholder">
        <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 512 512">
          <path
            fill="currentColor"
            d="M256 16C123.452 16 16 123.452 16 256s107.452 240 240 240s240-107.452 240-240S388.548 16 256 16Zm147.078 387.078a207.253 207.253 0 1 1 44.589-66.125a207.332 207.332 0 0 1-44.589 66.125Z"
          />
          <path
            fill="currentColor"
            d="M168 320h176v32H168zm42.63-91.958l-24.042-21.371l21.37-24.041l-23.916-21.26l-21.371 24.042l-24.041-21.37l-21.26 23.916l24.042 21.371l-21.37 24.041l23.916 21.26l21.371-24.042l24.041 21.37l21.26-23.916zm173.328-45.412l-23.916-21.26l-21.371 24.042l-24.041-21.37l-21.26 23.916l24.042 21.371l-21.37 24.041l23.916 21.26l21.371-24.042l24.041 21.37l21.26-23.916l-24.042-21.371l21.37-24.041z"
          />
        </svg>

        <span>Customize empty data placeholder</span>
      </div>
    </template>
  </UniTable>
</template>

<style scoped>
.placeholder {
  display: flex;
  flex-direction: column;
  padding: 20px;
  align-items: center;
  color: rgba(0, 0, 0, 0.3);
}
</style>

Sort

To enable the table column sort feature, set the column.sortable property. The default shot types are ascending and descending. If you only want to sort in one direction, set the column.sortType property to either ['ascending'] or ['descending'].

When the sort event is triggered, the columnKey and order (sort type) of the current sort state are provided. You can use this information to request data from the server.

View Source
vue
<script setup lang="ts">
import { ref } from "vue";

import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

type TableType = InstanceType<typeof UniTable>;

const sort = ref<TableType["sort"]>(null);

const columns: TableType["$props"]["columns"] = [
  {
    title: "id",
    key: "id",
    sortable: true,
    sortType: ["descending"],
  },
  {
    title: "Title",
    key: "title",
    sortable: true,
    sortType: ["ascending", "descending"],
    width: 200,
  },
  {
    title: "Author",
    key: "author",
    sortable: true,
    sortType: ["ascending"],
    width: 200,
  },
];

function handleSortChange(
  params: {
    columnKey: string;
    order: "ascending" | "descending";
  } | null
) {
  console.log("params:", params);
}
</script>

<template>
  <UniTable v-model:sort="sort" row-key="id" :columns="columns" :data="data" @sort-change="handleSortChange">
    <template #userId="{ record }"> 用户标识:{{ record.userId }} </template>
  </UniTable>
</template>

Text Ellipsis

View Source
vue
<script setup lang="ts">
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: InstanceType<typeof UniTable>["$props"]["columns"] = [
  {
    title: "Title",
    key: "title",
    width: 150,
  },
  {
    title: "Author",
    key: "author",
    width: 150,
    ellipsis: true,
  },
  {
    title: "Post",
    key: "post",
    ellipsis: true,
  },
];
</script>

<template>
  <UniTable row-key="id" :columns="columns" :data="data"> </UniTable>
</template>

Column Resize

To enable table column resizing, use v-model:column to bind the reactive column definition. Set the column.resizable property to indicate which columns should be resizable. When a column's width is changed, the column.width property will be updated.

View Source
vue
<script setup lang="ts">
import { ref, type Ref } from "vue";
import { UniTable } from "unify-ui";
import useFetchData from "./useFetchData";

const { data } = useFetchData();

const columns: Ref<InstanceType<typeof UniTable>["$props"]["columns"]> = ref([
  {
    title: "Title",
    key: "title",
    fixed: "left",
    width: 150,
    resizeable: true,
  },
  {
    title: "Author",
    key: "author",
    width: 150,
    resizeable: true,
  },
  {
    title: "Post",
    key: "post",
    width: 600,
    resizeable: true,
  },
  {
    title: "Actions",
    key: "actions",
    fixed: "right",
    width: 100,
  },
]);
</script>

<template>
  <UniTable row-key="id" class="container" v-model:columns="columns" :data="data">
    <template #actions>Delete</template>
  </UniTable>
</template>

<style scoped>
.container {
  width: 100%;
  height: 500px;
}
</style>

API

Table Props

ts
export type Record = any;

export type Key = string | number;

export type SortType = "ascending" | "descending";

export interface TableProps {
  columns: Column[];

  /** Table data */
  data: Record[];

  /** Enabled row can be expandable */
  rowExpand?: {
    expandCondition: (record: Record) => boolean;
    showExpandRowDefault?: boolean;
  };

  /** Row's unique key */
  rowKey: Key;

  /** The set of selected row keys */
  selectedRowKeys?: Set<Key>;

  /** Config of row selection */
  selection?: {
    type: "multiple" | "single";
    disabledCondition?: (record: Record) => boolean;
  };

  /** Whether to show foot */
  showFoot?: boolean;

  /** Config of row sort  */
  sort?: {
    columnKey: Key;
    order: SortType;
  } | null;

  /** Class name of Table body row */
  tbodyCellClass?: string;

  /** Class name of table body row cell */
  tbodyRowClass?: string | ((record: Record) => string);
}

columns

ts
interface Column {
  /**
   * If the text content in a cell exceeds the available space, it can be clipped and
   * replaced with an ellipsis to indicate truncation
   */
  ellipsis?: boolean;

  /** `left` or `right`. To set Column to fixed left or right of the table  */
  fixed?: "left" | "right";

  /** Column key */
  key: string;

  /** To set Column to be Sortable */
  sortable?: boolean;

  /** The column sort type. Array need to be one of the following:`ascending` or `descending`. */
  sortType?: SortType[];

  /** Column title */
  title: string;

  /** Column width. When a column is fixed, it is necessary to provide the 'width' property */
  width?: number;

  /**
   * Minimum width of the column
   * @default 200
   */
  minWidth?: number;

  /** Whether the column is resizable */
  resizable?: boolean;
}

Event

ts
export type SortType = "ascending" | "descending";

interface TableEmits {
  (e: "update:selectedRowKeys", selectedRowKeys: Set<Key>): void;

  (e: "update:sort", params: { columnKey: Key; order: SortType } | null): void;

  /** Emits the "select" event when a row is selected or deselected. */
  (e: "select", params: { selected: boolean; rowKey: Key; record: Record }): void;

  /** Emits the "selectAll" event when all rows are selected or deselected. */
  (e: "selectAll", params: { selected: boolean; rowKeys: Key[]; records: Record[] }): void;

  /** Emits the "sortChange" event when the column sort order changes */
  (e: "sortChange", params: { columnKey: Key; order: SortType } | null): void;
}

Released under the MIT License.