Back to components
Components

Table

A flexible and responsive Table component for displaying structured data, supporting slots for custom headers, rows and more.

Demo

Column 1
Column 2
Column 3
John Doejohndoe@example.comActive
Jane Smithjanesmith@example.comInactive
Alice Johnsonalice.j@example.comPending
Bob Brownbob.brown@example.comActive
Charlie Davischarlie.davis@example.comInactive
Emma Wilsonemma.wilson@example.comPending
Liam Taylorliam.taylor@example.comActive
Olivia Mooreolivia.moore@example.comInactive
Noah Millernoah.miller@example.comActive
Ava Martinava.martin@example.comPending

Showing 1 to 10 of 80 results

Rows
 <template>
    <TableWrapper v-if="exampleTableData.length">
        <Table>
            <TableHeader>
                <TableHeaderCell>Column 1</TableHeaderCell>
                <TableHeaderCell>Column 2</TableHeaderCell>
                <TableHeaderCell>Column 3</TableHeaderCell>
                <TableHeaderCell><!-- Cell actions --></TableHeaderCell>
            </TableHeader>
            <TableBody>
                <TableRow v-for="(item, index) in paginatedData" :key="index">
                    <TableCell>{{ item.col1 }}</TableCell>
                    <TableCell>{{ item.col2 }}</TableCell>
                    <TableCell>{{ item.col3 }}</TableCell>
                    <TableCellActions>
                        <ActionIconButton 
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <ButtonPagination 
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    <EmptyState v-else />
</template>

<script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)

// Static example data
const exampleTableData = [
   ...
]

// Computed
const paginatedData = computed(() =>
    getPaginatedData(exampleTableData, currentPage.value, currentItemsPerPage.value)
)
</script>
 

Architecture

To build a flexible and reusable table layout, you can structure your component as follows. This approach separates concerns clearly, supports pagination, and includes an empty state fallback to improve UX:

 <template>
    <TableWrapper v-if="exampleTableData.length">
        <Table>
            <TableHeader>
                <TableHeaderCell>
                    <!-- Column name -->
                </TableHeaderCell>
                ....
            </TableHeader>
            <TableBody>
                <!-- Use the data array to render all the rows -->
                <TableRow v-for="(item, index) in paginatedData" :key="index">
                    <TableCell>{{ item.col1 }}</TableCell>
                    ....

                    <!-- In case you need to use actions, use this component for a proper layout -->
                    <TableCellActions>
                        <ActionIconButton 
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <!-- Optional pagination -->
        <ButtonPagination 
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    <!-- Recommended empty state -->
    <EmptyState v-else />
<template>
 

Components

This set of components are used when creating tables.

Name Description
<TableWrapper>

Provides a container for all the table components and pagination which is usefull to set the layout correctly.

<Table>

Represents the table. Under the hood, it provides an overflow scrollable container.

<TableHeader>

Represents the head of the table.

<TableHeaderCell>

Represents a cell in the table header.

<TableBody>

Represents the body of the table.

<TableRow>

Represents a table row.

<TableCell>

Represents the cell inside a table row.

<TableCellActions>

It is a predesigned and use-specific case cell, which provides an suitable layout for actions

Usage

Paginated data

In the event of using pagined data, please follow this steps:

  1. Create local current page and and current items per page states in case you do not use any composables or stores.
  2. Create a computed function which calls the `getPaginatedData()' utils function.

This function requires:

 data: T[],
currentPage: number,
itemsPerPage: number,
itemsLimit?: number
 

Consider using itemsLimit if you want to limit the amount of items you want to handle.

 <script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)

// Static example data
const exampleTableData = [
   ...
]

// Computed
const paginatedData = computed(() =>
    getPaginatedData(exampleTableData, currentPage.value, currentItemsPerPage.value)
)
</script>
 

TableCell

The TableCell components accept some props:

Props Default Type
fitToContentfalseboolean
to — string
noWrapfalseboolean

fitToContent

Forces the cell and the column to fit is width to the content of the cell. Usefull when using badges for instance.

 <template>
    ...
    <TableCell fitToContent>
        ...
    </TableCell>
    ...
</template>
 
  • Type: boolean
  • Default: false

to

The to prop is used to set the href attribute of the a tag inside the TableCell component. It is useful when you want to link to another page or resource.

UX improvement: When using the to prop, provide the <TableRow> component the isHoverable prop to ensure a better UX experience.

 <template>
    ...
    <TableCell to="/some-page">
        ...
    </TableCell>
    ...
</template>
 

noWrap

Prevents the cell content from wrapping to a new line. Useful for cells containing dates, IDs, or other short values that should stay on a single line.

 <template>
    ...
    <TableCell noWrap>
        ...
    </TableCell>
    ...
</template>
 
  • Type: boolean
  • Default: false

TableHeaderCell

The TableHeaderCell component accepts some props:

Props Default Type
scopeTableHeaderCellScope.COLTableHeaderCellScope
sorteablefalseboolean
columnKey — string
sortKey — string
sortAsc — boolean
onToggleSort — () => void
fitToContentfalseboolean
to — string
sortAriaLabel'Toggle sort'string
noWrapfalseboolean

scope

The scope prop is used to set the scope of the header cell. It uses the TableHeaderCellScope enum.

With TableHeaderCellScope.COL (default), the header cell is placed inside TableHeader, as a column heading:

 <template>
    ...
    <TableHeader>
        <TableHeaderCell :scope="TableHeaderCellScope.COL">
            Column 1
        </TableHeaderCell>
        ...
    </TableHeader>
    ...
</template>
 

With TableHeaderCellScope.ROW, the header cell is placed inside TableRow, alongside TableCell, as a row heading:

 <template>
    ...
    <TableRow>
        <TableHeaderCell :scope="TableHeaderCellScope.ROW">
            Row header
        </TableHeaderCell>
        <TableCell>...</TableCell>
        ...
    </TableRow>
    ...
</template>
 
  • Type: TableHeaderCellScope
  • Default: TableHeaderCellScope.COL
Options
Value Description
COL

Indicates that the header cell is a column header.

ROW

Indicates that the header cell is a row header.

Note about scope

sorteable

When true, it indicates that the column is sortable.

It is recommended to use this prop together with columnKey, sortKey, sortAsc and onToggleSort props.

 <template>
    ...
    <TableHeaderCell
        sorteable
        columnKey="col1"
        :sortKey
        :sortAsc
        :onToggleSort="() => toggleSort('col1')"
    >
        Column 1
    </TableHeaderCell>
    ...
</template>
 
  • Type: boolean
  • Default: false

columnKey

The columnKey prop is used to set the key of the column. It is useful when you want to sort the column.

 <template>
    ...
    <TableHeaderCell
        sorteable
        columnKey="col1"
        :sortKey
        :sortAsc
        :onToggleSort="() => toggleSort('col1')"
    >
        Column 1
    </TableHeaderCell>
    ...
</template>
 
  • Type: string
  • Default: ''

sortKey

The sortKey prop is used to set the current sort key. It is useful when you want to sort the column.

 <template>
    ...
    <TableHeaderCell
        sorteable
        columnKey="col1"
        :sortKey
        :sortAsc
        :onToggleSort="() => toggleSort('col1')"
    >
        Column 1
    </TableHeaderCell>
    ...
</template>
 
  • Type: string
  • Default: ''

sortAsc

The sortAsc prop is used to set the current sort direction. It is useful when you want to sort the column.

 <template>
    ...
    <TableHeaderCell
        sorteable
        columnKey="col1"
        :sortKey
        :sortAsc
        :onToggleSort="() => toggleSort('col1')"
    >
        Column 1
    </TableHeaderCell>
    ...
</template>
 
  • Type: boolean
  • Default: true

onToggleSort

The onToggleSort prop is used to set the function that will be called when the user clicks on the header cell to toggle the sort direction. It is useful when you want to sort the column.

 <template>
    ...
    <TableHeaderCell
        sorteable
        columnKey="col1"
        :sortKey
        :sortAsc
        :onToggleSort="() => toggleSort('col1')"
    >
        Column 1
    </TableHeaderCell>
    ...
</template>
 
  • Type: () => void
  • Default: undefined

fitToContent / to

These props have the same behavior as in the TableCell component, but in this case they will be applied to the header cell.

 <template>
    ...
    <TableHeaderCell
        :scope="TableHeaderCellScope.ROW"
        fitToContent
        to="/some-page"
    >
        Row header
    </TableHeaderCell>
    ...
</template>
 

noWrap

Prevents the header cell content from wrapping to a new line. Applies when scope is ROW; COL header cells already prevent wrapping by default.

 <template>
    ...
    <TableHeaderCell :scope="TableHeaderCellScope.ROW" noWrap>
        Row header
    </TableHeaderCell>
    ...
</template>
 
  • Type: boolean
  • Default: false

sortAriaLabel

The sortAriaLabel prop sets the accessible label for the sort toggle button. Override it for i18n.

 <template>
    <TableHeaderCell
        sorteable
        columnKey="col1"
        sortAriaLabel="Alternar orden"
    >
        Column 1
    </TableHeaderCell>
</template>
 
  • Type: string
  • Default: 'Toggle sort'

TableRow

The TableRow component is used to render a table row. It accepts the following props:

Props Default Type
isHoverablefalseboolean
isStrippedfalseboolean

isHoverable

Displays a hover effect on the row if it is true.

 <template>
    ...
    <TableRow isHoverable>
        ...
    </TableRow>
    ...
</template>
 
  • Type: boolean
  • Default: false

isStripped

When true, it applies a different background color to even rows, creating a stripped effect.

 <template>
    ...
    <TableRow isStripped>
        ...
    </TableRow>
    ...
</template>
 
  • Type: boolean
  • Default: false

Table examples

Stripped

Column 1
Column 2
Column 3
John Doejohndoe@example.comActive
Jane Smithjanesmith@example.comInactive
Alice Johnsonalice.j@example.comPending
Bob Brownbob.brown@example.comActive
Charlie Davischarlie.davis@example.comInactive
Emma Wilsonemma.wilson@example.comPending
Liam Taylorliam.taylor@example.comActive
Olivia Mooreolivia.moore@example.comInactive
Noah Millernoah.miller@example.comActive
Ava Martinava.martin@example.comPending

Showing 1 to 10 of 17 results

Rows
 <template>
    <TableWrapper v-if="exampleTableData.length">
        <Table>
            <TableHeader>
                <TableHeaderCell>Column 1</TableHeaderCell>
                <TableHeaderCell>Column 2</TableHeaderCell>
                <TableHeaderCell>Column 3</TableHeaderCell>
                <TableHeaderCell><!-- Cell actions --></TableHeaderCell>
            </TableHeader>
            <TableBody>
                <TableRow v-for="(item, index) in paginatedData" :key="index" isStripped>
                    <TableCell>{{ item.col1 }}</TableCell>
                    <TableCell>{{ item.col2 }}</TableCell>
                    <TableCell>{{ item.col3 }}</TableCell>
                    <TableCellActions>
                        <ActionIconButton 
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <ButtonPagination 
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    <EmptyState v-else />
</template>

<script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)

// Static example data
const exampleTableData = [
    { col1: 'John Doe', col2: 'johndoe@example.com', col3: 'Active' },
    ...
]

// Computed
const paginatedData = computed(() =>
    getPaginatedData(exampleTableData, currentPage.value, currentItemsPerPage.value)
)
</script>
 

Sorteable

Column 1
Column 2
Column 3
John Doejohndoe@example.comActive
Jane Smithjanesmith@example.comInactive
Alice Johnsonalice.j@example.comPending
Bob Brownbob.brown@example.comActive
Charlie Davischarlie.davis@example.comInactive
Emma Wilsonemma.wilson@example.comPending
Liam Taylorliam.taylor@example.comActive
Olivia Mooreolivia.moore@example.comInactive
Noah Millernoah.miller@example.comActive
Ava Martinava.martin@example.comPending

Showing 1 to 10 of 17 results

Rows
 <template>
    <TableWrapper v-if="exampleTableData.length">
        <Table>
            <TableHeader>
                <TableHeaderCell
                    sorteable
                    columnKey="col1"
                    :sortKey
                    :sortAsc
                    :onToggleSort="() => toggleSort('col1')"
                >
                    Column 1
                </TableHeaderCell>

                <TableHeaderCell
                    sorteable
                    columnKey="col2"
                    :sortKey
                    :sortAsc
                    :onToggleSort="() => toggleSort('col2')"
                >
                    Column 2
                </TableHeaderCell>

                <TableHeaderCell
                    sorteable
                    columnKey="col3"
                    :sortKey
                    :sortAsc
                    :onToggleSort="() => toggleSort('col3')"
                >
                    Column 3
                </TableHeaderCell>

                <TableHeaderCell><!-- Actions --></TableHeaderCell>
            </TableHeader>
            <TableBody>
                <TableRow v-for="(item, index) in paginatedData" :key="index">
                    <TableCell>{{ item.col1 }}</TableCell>
                    <TableCell>{{ item.col2 }}</TableCell>
                    <TableCell>{{ item.col3 }}</TableCell>
                    <TableCellActions>
                        <ActionIconButton 
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <ButtonPagination 
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    <EmptyState v-else />
</template>

<script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)

// Static example data
type ExampleTableDataItem = {
    col1: string
    col2: string
    col3: string
}

const exampleTableData = ref<ExampleTableDataItem[]>([
    { col1: 'John Doe', col2: 'johndoe@example.com', col3: 'Active' },
    { col1: 'Jane Smith', col2: 'janesmith@example.com', col3: 'Inactive' },
    { col1: 'Alice Johnson', col2: 'alice.j@example.com', col3: 'Pending' },
    { col1: 'Bob Brown', col2: 'bob.brown@example.com', col3: 'Active' },
    { col1: 'Charlie Davis', col2: 'charlie.davis@example.com', col3: 'Inactive' },
    { col1: 'Emma Wilson', col2: 'emma.wilson@example.com', col3: 'Pending' },
    { col1: 'Liam Taylor', col2: 'liam.taylor@example.com', col3: 'Active' },
    { col1: 'Olivia Moore', col2: 'olivia.moore@example.com', col3: 'Inactive' },
    { col1: 'Noah Miller', col2: 'noah.miller@example.com', col3: 'Active' },
    { col1: 'Ava Martin', col2: 'ava.martin@example.com', col3: 'Pending' },
    { col1: 'Sophia Clark', col2: 'sophia.clark@example.com', col3: 'Active' },
    { col1: 'Mason Lewis', col2: 'mason.lewis@example.com', col3: 'Inactive' },
    { col1: 'Isabella Young', col2: 'isabella.young@example.com', col3: 'Active' },
    { col1: 'Logan Hall', col2: 'logan.hall@example.com', col3: 'Pending' },
    { col1: 'Mia Allen', col2: 'mia.allen@example.com', col3: 'Active' },
    { col1: 'Ethan Wright', col2: 'ethan.wright@example.com', col3: 'Inactive' },
    { col1: 'Amelia Scott', col2: 'amelia.scott@example.com', col3: 'Pending' },
])

// Composables
const {
    sortKey,
    sortAsc,
    toggleSort,
    sortedData,
} = useTable(exampleTableData)

// Computed
const paginatedData = computed(() =>
    getPaginatedData(sortedData.value, currentPage.value, currentItemsPerPage.value)
)
</script>
 

Row header cell

Col1
John Doejohndoe@example.comActive
Col2
Jane Smithjanesmith@example.comInactive
Col3
Alice Johnsonalice.j@example.comPending
Col4
Bob Brownbob.brown@example.comActive
Col5
Charlie Davischarlie.davis@example.comInactive
Col6
Emma Wilsonemma.wilson@example.comPending
Col7
Liam Taylorliam.taylor@example.comActive

Showing 7 results

Rows
 <template>
    <TableWrapper v-if="exampleTableData.length">
        <Table>
            <TableBody>
                <TableRow v-for="(item, index) in paginatedData" :key="index">
                    <TableHeaderCell :scope="TableHeaderCellScope.ROW">
                        {{ item.colHeader }}
                    </TableHeaderCell>
                    <TableCell>{{ item.col1 }}</TableCell>
                    <TableCell>{{ item.col2 }}</TableCell>
                    <TableCell>{{ item.col3 }}</TableCell>
                    <TableCellActions>
                        <ActionIconButton 
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <ButtonPagination 
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    <EmptyState v-else />
</template>

<script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)

// Static example data
const exampleTableData = [
    { colHeader: 'Col1', col1: 'John Doe', col2: 'johndoe@example.com', col3: 'Active' },
    { colHeader: 'Col2', col1: 'Jane Smith', col2: 'janesmith@example.com', col3: 'Inactive' },
    { colHeader: 'Col3', col1: 'Alice Johnson', col2: 'alice.j@example.com', col3: 'Pending' },
    { colHeader: 'Col4', col1: 'Bob Brown', col2: 'bob.brown@example.com', col3: 'Active' },
    { colHeader: 'Col5', col1: 'Charlie Davis', col2: 'charlie.davis@example.com', col3: 'Inactive' },
    { colHeader: 'Col6', col1: 'Emma Wilson', col2: 'emma.wilson@example.com', col3: 'Pending' },
    { colHeader: 'Col7', col1: 'Liam Taylor', col2: 'liam.taylor@example.com', col3: 'Active' },
]

// Computed
const paginatedData = computed(() =>
    getPaginatedData(exampleTableData, currentPage.value, currentItemsPerPage.value)
)
</script>
 

Checkbox row selection

Column 1
Column 2
Column 3
John Doejohndoe@example.comActive
Jane Smithjanesmith@example.comInactive
Alice Johnsonalice.j@example.comPending
Bob Brownbob.brown@example.comActive
Charlie Davischarlie.davis@example.comInactive
Emma Wilsonemma.wilson@example.comPending
Liam Taylorliam.taylor@example.comActive
Olivia Mooreolivia.moore@example.comInactive
Noah Millernoah.miller@example.comActive
Ava Martinava.martin@example.comPending

Showing 1 to 10 of 22 results

Rows
 <template>
    <TableWrapper v-if="exampleTableData.length">
        <!-- Batch Actions Form -->
        <Form>
            <FormRow class="gap-2">
                <SelectField
                    id="batch-action"
                    v-model="selectedAction"
                    :options="batchActions"
                    placeholder="Select an action"
                    :disabled="selectedRows.size === 0"
                    selectBoxClass="!max-w-[200px]"
                />
                <ActionButton
                    v-if="selectedRows.size > 0"
                    text="Apply"
                    :styleType="ButtonStyleType.NEUTRAL_FILLED"
                    @click="handleBatchAction"
                />
            </FormRow>
        </Form>

        <!-- Table -->
        <Table>
            <TableHeader>
                <TableHeaderCell>
                    <Checkbox
                        id="header-select-all"
                        :modelValue="isAllSelected"
                        :size="ControlFieldSize.SM"
                        @update:modelValue="toggleSelectAll"
                    />
                </TableHeaderCell>
                <TableHeaderCell>Column 1</TableHeaderCell>
                <TableHeaderCell>Column 2</TableHeaderCell>
                <TableHeaderCell>Column 3</TableHeaderCell>
                <TableHeaderCell><!-- Cell actions --></TableHeaderCell>
            </TableHeader>
            <TableBody>
                <TableRow v-for="(item, index) in paginatedData" :key="index">
                    <TableCell fitToContent>
                        <Checkbox
                            :id="`row-checkbox-${index}`"
                            :modelValue="selectedRows.has(getItemKey(item))"
                            :size="ControlFieldSize.SM"
                            @update:modelValue="toggleRowSelection(getItemKey(item), $event)"
                        />
                    </TableCell>
                    <TableCell>{{ item.col1 }}</TableCell>
                    <TableCell>{{ item.col2 }}</TableCell>
                    <TableCell>{{ item.col3 }}</TableCell>
                    <TableCellActions>
                        <ActionIconButton
                            :size="ButtonSize.MD"
                            icon="mdi:eye-outline"
                        />
                    </TableCellActions>
                </TableRow>
            </TableBody>
        </Table>
        <ButtonPagination
            v-model:modelValue="currentPage"
            v-model:items-per-page="currentItemsPerPage"
            :totalItems="exampleTableData.length"
        />
    </TableWrapper>
    
    <EmptyState v-else />
</template>

<script setup lang="ts">
// States
const currentPage = ref(1)
const currentItemsPerPage = ref(10)
const selectedRows = ref<Set<string>>(new Set())
const selectedAction = ref<string | null>(null)

// Batch actions
const batchActions = [
    { label: 'Archive', value: 'archive', text: 'Archive' },
    { label: 'Delete', value: 'delete', text: 'Delete' },
    { label: 'Move to folder', value: 'move', text: 'Move to folder' },
    { label: 'Change status', value: 'status', text: 'Change status' },
]

// Static example data
const exampleTableData = [
    { col1: 'John Doe', col2: 'johndoe@example.com', col3: 'Active' },
    { col1: 'Jane Smith', col2: 'janesmith@example.com', col3: 'Inactive' },
    { col1: 'Alice Johnson', col2: 'alice.j@example.com', col3: 'Pending' },
    { col1: 'Bob Brown', col2: 'bob.brown@example.com', col3: 'Active' },
    { col1: 'Charlie Davis', col2: 'charlie.davis@example.com', col3: 'Inactive' },
    { col1: 'Emma Wilson', col2: 'emma.wilson@example.com', col3: 'Pending' },
    { col1: 'Liam Taylor', col2: 'liam.taylor@example.com', col3: 'Active' },
    { col1: 'Olivia Moore', col2: 'olivia.moore@example.com', col3: 'Inactive' },
    { col1: 'Noah Miller', col2: 'noah.miller@example.com', col3: 'Active' },
    { col1: 'Ava Martin', col2: 'ava.martin@example.com', col3: 'Pending' },
    { col1: 'Sophia Clark', col2: 'sophia.clark@example.com', col3: 'Active' },
    { col1: 'Mason Lewis', col2: 'mason.lewis@example.com', col3: 'Inactive' },
    { col1: 'Isabella Young', col2: 'isabella.young@example.com', col3: 'Active' },
    { col1: 'Logan Hall', col2: 'logan.hall@example.com', col3: 'Pending' },
    { col1: 'Mia Allen', col2: 'mia.allen@example.com', col3: 'Active' },
    { col1: 'Ethan Wright', col2: 'ethan.wright@example.com', col3: 'Inactive' },
    { col1: 'Amelia Scott', col2: 'amelia.scott@example.com', col3: 'Pending' },
    { col1: 'James Green', col2: 'james.green@example.com', col3: 'Active' },
    { col1: 'Harper Adams', col2: 'harper.adams@example.com', col3: 'Inactive' },
    { col1: 'Benjamin Baker', col2: 'benjamin.baker@example.com', col3: 'Active' },
    { col1: 'Ella Perez', col2: 'ella.perez@example.com', col3: 'Pending' },
    { col1: 'Lucas Campbell', col2: 'lucas.campbell@example.com', col3: 'Active' },
]

// Computed
const paginatedData = computed(() =>
    getPaginatedData(exampleTableData, currentPage.value, currentItemsPerPage.value)
)

const isAllSelected = computed(() => {
    if (exampleTableData.length === 0) return false
    return exampleTableData.every(item => selectedRows.value.has(getItemKey(item)))
})

// Helpers
const getItemKey = (item: any) => `${item.col1}-${item.col2}`

const toggleSelectAll = (shouldSelect: boolean) => {
    if (shouldSelect) {
        exampleTableData.forEach(item => {
            selectedRows.value.add(getItemKey(item))
        })
    } else {
        selectedRows.value.clear()
    }
}

const toggleRowSelection = (key: string, isSelected: boolean) => {
    if (isSelected) {
        selectedRows.value.add(key)
    } else {
        selectedRows.value.delete(key)
    }
}

const handleBatchAction = () => {
    if (!selectedAction.value) return

    const actionMap: Record<string, string> = {
        archive: 'archived',
        delete: 'deleted',
        move: 'moved',
        status: 'status changed',
    }

    const action = actionMap[selectedAction.value] || 'processed'
    const message = `${selectedRows.value.size} item(s) ${action}`

    console.log(message)
    console.log('Selected action:', selectedAction.value)
    console.log('Selected items:', Array.from(selectedRows.value))

    // Reset after action
    selectedRows.value.clear()
    selectedAction.value = null
}
</script>