undefined
Svelte Headless Table is designed with extensibility in mind. Its complex features are powered by an extensive suite of plugins.
-
addSortBy
-
addColumnFilters
-
addTableFilter
-
addColumnOrder
-
addHiddenColumns
-
addPagination
-
addSubRows
-
addGroupBy
-
addExpandedRows
-
addSelectedRows
-
addResizedColumns
-
addGridLayout
Defining plugins
Svelte Headless Table treats each plugin as an extension to its core. Every plugin optionally defines transformations on the rows and columns of the table, and extends the functionality of column definitions, rows, and cells.
For this example, we extend a basic table with addSortBy
and addColumnOrder
.
const table = createTable(data, {
sort: addSortBy({ disableMultiSort: true }),
colOrder: addColumnOrder(),
});
Plugins are configurable via function arguments.
sort
and colOrder
are just names to identify the plugins – they can be any name you prefer as long as they remain consistent. This lets you add multiple plugins to a table without any naming conflicts.
The order in which you define plugins matters – the order of object keys is predictable and plugins are evaluated from first to last.
Configuring columns
Plugins optionally define additional column options to configure column behavior. Column options are specified under the optional plugins
property in the column definition.
const columns = table.createColumns([
table.column({
header: 'Name',
accessor: 'name',
plugins: {
sort: { invert: true },
},
}),
table.column({
header: 'Age',
accessor: 'age',
}),
]);
The column options of a plugin are specified under its given name. Because we named our sorting plugin sort
, its column options are defined under plugins.sort
.
Connecting plugins to markup
Plugins extend the view model with prop sets that provide additional props to table components via .props()
. Props can include state and event handlers.
.props()
returns a Readable
store, so pass it into Subscribe
to access its value.
<table {...$tableAttrs}>
<thead>
{#each $headerRows as headerRow (headerRow.id)}
<Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
<tr {...rowAttrs}>
{#each headerRow.cells as cell (cell.id)}
<Subscribe
attrs={cell.attrs()} let:attrs
props={cell.props()} let:props
>
<th {...attrs} on:click={props.sort.toggle}>
<Render of={cell.render()} />
{#if props.sort.order === 'asc'}
⬇️
{:else if props.sort.order === 'desc'}
⬆️
{/if}
</th>
</Subscribe>
{/each}
</tr>
</Subscribe>
{/each}
</thead>
<tbody {...$tableBodyAttrs}>
{#each $rows as row (row.id)}
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
<tr {...rowAttrs}>
{#each row.cells as cell (cell.id)}
<Subscribe attrs={cell.attrs()} let:attrs>
<td {...attrs}>
<Render of={cell.render()} />
</td>
</Subscribe>
{/each}
</tr>
</Subscribe>
{/each}
</tbody>
</table>
The view model extensions of a plugin are specified under its given name.
Each plugin may define different extensions for different table components, including
HeaderRow
s,
HeaderCell
s,
BodyRow
s,
BodyCell
s, and more.
Controlling plugin state
All plugins are driven by Svelte stores. Therefore, Svelte Headless Table plugins are programmatically controllable by default.
Plugins expose state via the pluginStates
property on the view model. Thanks to Svelte stores, state can be easily subscribed to and modified!
const {
headerRows,
rows,
tableAttrs,
tableBodyAttrs,
pluginStates,
} = table.createViewModel(columns);
const { columnIdOrder } = pluginStates.colOrder;
$columnIdOrder = ['age', 'name'];
The state of a plugin is specified under its given name.
Final result
Putting it all together, we have a simple table with extended functionality!
Explore this example in the REPL.
Age | Name |
---|---|
21 | Ada Lovelace |
52 | Barbara Liskov |
38 | Richard Hamming |
<script>
const data = readable([
{ name: 'Ada Lovelace', age: 21 },
{ name: 'Barbara Liskov', age: 52 },
{ name: 'Richard Hamming', age: 38 },
]);
const table = createTable(data, {
sort: addSortBy({ disableMultiSort: true }),
colOrder: addColumnOrder(),
});
const columns = table.createColumns([
table.column({
header: 'Name',
accessor: 'name',
plugins: {
sort: { invert: true },
},
}),
table.column({
header: 'Age',
accessor: 'age',
}),
]);
const {
headerRows,
rows,
tableAttrs,
tableBodyAttrs,
pluginStates,
} = table.createViewModel(columns);
const { columnIdOrder } = pluginStates.colOrder;
$columnIdOrder = ['age', 'name'];
</script>
<table {...$tableAttrs}>
<thead>
{#each $headerRows as headerRow (headerRow.id)}
<Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
<tr {...rowAttrs}>
{#each headerRow.cells as cell (cell.id)}
<Subscribe
attrs={cell.attrs()} let:attrs
props={cell.props()} let:props
>
<th {...attrs} on:click={props.sort.toggle}>
<Render of={cell.render()} />
{#if props.sort.order === 'asc'}
⬇️
{:else if props.sort.order === 'desc'}
⬆️
{/if}
</th>
</Subscribe>
{/each}
</tr>
</Subscribe>
{/each}
</thead>
<tbody {...$tableBodyAttrs}>
{#each $rows as row (row.id)}
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
<tr>
{#each row.cells as cell (cell.id)}
<Subscribe attrs={cell.attrs()} let:attrs>
<td {...attrs}>
<Render of={cell.render()} />
</td>
</Subscribe>
{/each}
</tr>
</Subscribe>
{/each}
</tbody>
</table>
<style>
table {
font-family: sans-serif;
border-spacing: 0;
border-top: 1px solid black;
border-left: 1px solid black;
}
th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
}
</style>