Documentation
Programmatic Control
Read and change table state from your code — sort, filter, paginate, export, and more.
Access the API
Keep a ref (or instance) to the table, then call methods on the TableAPI.
React TSX
Copy
const tableRef = useRef(null);<SimpleTable ref={tableRef} columns={columns} rows={rows} />tableRef.current?.getVisibleRows();
Angular
Copy
@ViewChild("simpleTable") tableRef!: SimpleTableComponent;<simple-table#simpleTable[columns]="columns"[rows]="rows"></simple-table>this.tableRef.getAPI()?.getVisibleRows();
Vue SFC
Copy
const tableRef = ref(null);<SimpleTable ref="tableRef" :columns="columns" :rows="rows" />tableRef.value?.getAPI()?.getVisibleRows();
Svelte
Copy
let tableRef;<SimpleTable bind:this={tableRef} {columns} {rows} />tableRef.getAPI()?.getVisibleRows();
Solid TSX
Copy
let tableRef;<SimpleTableref={(api) => (tableRef = api)}columns={columns}rows={rows()}/>tableRef?.getVisibleRows();
TypeScript
Copy
const table = new SimpleTableVanilla(container, {columns,rows,});table.getAPI().getVisibleRows();
Read data
getVisibleRows respects filters, sort, and the current page. getAllRows returns the full processed set. getHeaders returns the current column defs.
React TSX
Copy
const visible = tableRef.current?.getVisibleRows();const all = tableRef.current?.getAllRows();const headers = tableRef.current?.getHeaders();
Angular
Copy
const visible = this.tableRef.getAPI()?.getVisibleRows();const all = this.tableRef.getAPI()?.getAllRows();const headers = this.tableRef.getAPI()?.getHeaders();
Vue SFC
Copy
const visible = tableRef.value?.getAPI()?.getVisibleRows();const all = tableRef.value?.getAPI()?.getAllRows();const headers = tableRef.value?.getAPI()?.getHeaders();
Svelte
Copy
const visible = tableRef.getAPI()?.getVisibleRows();const all = tableRef.getAPI()?.getAllRows();const headers = tableRef.getAPI()?.getHeaders();
Solid TSX
Copy
const visible = tableRef.getVisibleRows();const all = tableRef.getAllRows();const headers = tableRef.getHeaders();
TypeScript
Copy
const visible = table.getAPI().getVisibleRows();const all = table.getAPI().getAllRows();const headers = table.getAPI().getHeaders();
Sort, filter, and paginate
Drive UI state from your own controls with applySortState, applyFilter, setQuickFilter, and setPage.
React TSX
Copy
await tableRef.current?.applySortState({accessor: "price",direction: "desc",});await tableRef.current?.applyFilter({accessor: "status",operator: "equals",value: "Available",});await tableRef.current?.clearAllFilters();tableRef.current?.setQuickFilter("keyboard");await tableRef.current?.setPage(2);
Angular
Copy
await this.tableRef.getAPI()?.applySortState({accessor: "price",direction: "desc",});await this.tableRef.getAPI()?.applyFilter({accessor: "status",operator: "equals",value: "Available",});await this.tableRef.getAPI()?.clearAllFilters();this.tableRef.getAPI()?.setQuickFilter("keyboard");await this.tableRef.getAPI()?.setPage(2);
Vue SFC
Copy
await tableRef.value?.getAPI()?.applySortState({accessor: "price",direction: "desc",});await tableRef.value?.getAPI()?.applyFilter({accessor: "status",operator: "equals",value: "Available",});await tableRef.value?.getAPI()?.clearAllFilters();tableRef.value?.getAPI()?.setQuickFilter("keyboard");await tableRef.value?.getAPI()?.setPage(2);
Svelte
Copy
await tableRef.getAPI()?.applySortState({accessor: "price",direction: "desc",});await tableRef.getAPI()?.applyFilter({accessor: "status",operator: "equals",value: "Available",});await tableRef.getAPI()?.clearAllFilters();tableRef.getAPI()?.setQuickFilter("keyboard");await tableRef.getAPI()?.setPage(2);
Solid TSX
Copy
await tableRef.applySortState({accessor: "price",direction: "desc",});await tableRef.applyFilter({accessor: "status",operator: "equals",value: "Available",});await tableRef.clearAllFilters();tableRef.setQuickFilter("keyboard");await tableRef.setPage(2);
TypeScript
Copy
await table.getAPI().applySortState({accessor: "price",direction: "desc",});await table.getAPI().applyFilter({accessor: "status",operator: "equals",value: "Available",});await table.getAPI().clearAllFilters();table.getAPI().setQuickFilter("keyboard");await table.getAPI().setPage(2);
Related APIs
Feature-specific methods are covered on their docs pages: updateData, exportToCSV, row selection, row grouping, pivot, column visibility, and pinning.
Example
Use the buttons to sort, filter, and read table state. Code or StackBlitz has the full example.
No status message
React TSX
Copy
1import { useRef, useState, useMemo } from "react";2import { SimpleTable } from "@simple-table/react";3import type { Theme, TableAPI, ReactColumnDef } from "@simple-table/react";4import {5 programmaticControlConfig,6 PROGRAMMATIC_CONTROL_STATUS_COLORS,7 type ProgrammaticControlProduct8} from "./programmatic-control.demo-data";9import "@simple-table/react/styles.css";1011const ProgrammaticControlDemo = ({12 height = "400px",13 theme14}: {15 height?: string | number;16 theme?: Theme;17}) => {18 const tableRef = useRef<TableAPI<ProgrammaticControlProduct>>(null);19 const [statusMessage, setStatusMessage] = useState("No status message");2021 const headers: ReactColumnDef<ProgrammaticControlProduct>[] = useMemo(22 () =>23 programmaticControlConfig.headers.map((h) => {24 if (h.accessor === "status") {25 return {26 ...h,27 cellRenderer: ({ row }) => {28 const s = String(row.status);29 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[s] ?? {30 bg: "#f3f4f6",31 color: "#374151"32 };33 return (34 <span35 style={{36 backgroundColor: colors.bg,37 color: colors.color,38 padding: "4px 8px",39 borderRadius: 4,40 fontSize: 12,41 fontWeight: "bold"42 }}43 >44 {s}45 </span>46 );47 }48 };49 }50 return h;51 }),52 [],53 );5455 const handleSortByName = () => {56 tableRef.current?.applySortState({ accessor: "name", direction: "asc" });57 setStatusMessage("Sorted by Name (A-Z)");58 };5960 const handleSortByPrice = () => {61 tableRef.current?.applySortState({ accessor: "price", direction: "desc" });62 setStatusMessage("Sorted by Price (High to Low)");63 };6465 const handleFilterAvailable = () => {66 tableRef.current?.applyFilter({ accessor: "status", operator: "equals", value: "Available" });67 setStatusMessage("Filtered to show only Available products");68 };6970 const handleClearFilters = () => {71 tableRef.current?.clearAllFilters();72 setStatusMessage("All filters cleared");73 };7475 const handleGetInfo = () => {76 const api = tableRef.current;77 if (!api) return;78 const allRows = api.getAllRows();79 const hdrs = api.getHeaders();80 const sortState = api.getSortState();81 const filterState = api.getFilterState();82 const totalValue = allRows.reduce((sum, r) => sum + r.row.price * r.row.stock, 0);83 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";84 alert(85 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,86 );87 setStatusMessage("Table info displayed");88 };8990 return (91 <div>92 <div93 style={{94 marginBottom: 12,95 padding: "8px 12px",96 backgroundColor: "#eff6ff",97 border: "1px solid #bfdbfe",98 borderRadius: 6,99 color: "#1e40af",100 fontSize: 14101 }}102 >103 {statusMessage}104 </div>105 <div style={{ marginBottom: 12, display: "flex", gap: 8, flexWrap: "wrap" }}>106 <button onClick={handleSortByName} style={{ padding: "6px 16px" }}>107 Sort by Name (A-Z)108 </button>109 <button onClick={handleSortByPrice} style={{ padding: "6px 16px" }}>110 Sort by Price (High to Low)111 </button>112 <button onClick={handleFilterAvailable} style={{ padding: "6px 16px" }}>113 Filter: Available114 </button>115 <button onClick={handleClearFilters} style={{ padding: "6px 16px" }}>116 Clear Filters117 </button>118 <button onClick={handleGetInfo} style={{ padding: "6px 16px" }}>119 Get Table Info120 </button>121 </div>122 <SimpleTable123 ref={tableRef}124 columns={headers}125 getRowId={({ row }) => row.id}126 rows={programmaticControlConfig.rows}127 height={height}128 theme={theme}129 />130 </div>131 );132};133134export default ProgrammaticControlDemo;
Angularprogrammatic-control-demo.component.ts
Copy
1import { Component, Input, ViewChild } from "@angular/core";2import {SimpleTableComponent} from "@simple-table/angular";import type { AngularCellRenderer, AngularColumnDef, CellRendererProps, GetRowIdParams, Theme } from "@simple-table/angular";3import { programmaticControlConfig, PROGRAMMATIC_CONTROL_STATUS_COLORS } from "./programmatic-control.demo-data";4import "@simple-table/angular/styles.css";5import type { ProgrammaticControlProduct } from "./programmatic-control.demo-data";67@Component({8 selector: "programmatic-control-demo",9 standalone: true,10 imports: [SimpleTableComponent],11 template: `12 <div>13 <div style="margin-bottom: 12px; padding: 8px 12px; background-color: #eff6ff; border: 1px solid #bfdbfe; border-radius: 6px; color: #1e40af; font-size: 14px">14 {{ statusMessage }}15 </div>16 <div style="margin-bottom: 12px; display: flex; gap: 8px; flex-wrap: wrap">17 <button (click)="sortByName()">Sort by Name (A-Z)</button>18 <button (click)="sortByPrice()">Sort by Price (High to Low)</button>19 <button (click)="filterAvailable()">Filter: Available</button>20 <button (click)="clearFilters()">Clear Filters</button>21 <button (click)="getInfo()">Get Table Info</button>22 </div>23 <simple-table24 [getRowId]="getRowId"25 #simpleTable26 [rows]="rows"27 [columns]="headers"28 [height]="height"29 [theme]="theme"30 ></simple-table>31 </div>32 `,33})34export class ProgrammaticControlDemoComponent {35 @ViewChild("simpleTable") tableRef!: SimpleTableComponent;36 @Input() height: string | number = "400px";37 @Input() theme?: Theme;3839 statusMessage = "No status message";40 readonly rows: ProgrammaticControlProduct[] = programmaticControlConfig.rows;41 readonly headers: AngularColumnDef<ProgrammaticControlProduct>[] = programmaticControlConfig.headers.map((h) => {42 if (h.accessor === "status") {43 return {44 ...h,45 cellRenderer: ({ row }: CellRendererProps<ProgrammaticControlProduct>) => {46 const s = row.status;47 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[s] ?? { bg: "#f3f4f6", color: "#374151" };48 return `<span style="background:${colors.bg};color:${colors.color};padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold">${s}</span>`;49 },50 };51 }52 return { ...h };53 });5455 sortByName(): void {56 this.tableRef.getAPI()?.applySortState({ accessor: "name", direction: "asc" });57 this.statusMessage = "Sorted by Name (A-Z)";58 }5960 sortByPrice(): void {61 this.tableRef.getAPI()?.applySortState({ accessor: "price", direction: "desc" });62 this.statusMessage = "Sorted by Price (High to Low)";63 }6465 filterAvailable(): void {66 this.tableRef.getAPI()?.applyFilter({ accessor: "status", operator: "equals", value: "Available" });67 this.statusMessage = "Filtered to show only Available products";68 }6970 clearFilters(): void {71 this.tableRef.getAPI()?.clearAllFilters();72 this.statusMessage = "All filters cleared";73 }7475 getInfo(): void {76 const api = this.tableRef.getAPI();77 if (!api) return;78 const allRows = api.getAllRows();79 const hdrs = api.getHeaders();80 const sortState = api.getSortState();81 const filterState = api.getFilterState();82 const totalValue = allRows.reduce((sum, r) => {83 const price = r.row.price;84 const stock = r.row.stock;85 return sum + (typeof price === "number" ? price : 0) * (typeof stock === "number" ? stock : 0);86 }, 0);87 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";88 alert(89 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,90 );91 this.statusMessage = "Table info displayed";92 }9394 getRowId = ({ row }: GetRowIdParams<ProgrammaticControlProduct>) => row.id;95}969798// programmatic-control.demo-data.ts99// Self-contained demo table setup for this example.100import type { AngularColumnDef, ValueFormatterProps } from "@simple-table/angular";101102export interface ProgrammaticControlProduct {103 id: number;104 name: string;105 category: string;106 price: number;107 stock: number;108 status: string;109}110111export const STATUS_COLORS: Record<string, { bg: string; color: string }> = {112 Available: { bg: "#dcfce7", color: "#166534" },113 "Low Stock": { bg: "#fef3c7", color: "#92400e" },114 "Out of Stock": { bg: "#fee2e2", color: "#991b1b" },115};116117export const programmaticControlData: ProgrammaticControlProduct[] = [118 { id: 1, name: "Wireless Keyboard", category: "Electronics", price: 49.99, stock: 145, status: "Available" },119 { id: 2, name: "Ergonomic Mouse", category: "Electronics", price: 29.99, stock: 12, status: "Low Stock" },120 { id: 3, name: "USB-C Hub", category: "Electronics", price: 39.99, stock: 234, status: "Available" },121 { id: 4, name: "Standing Desk", category: "Furniture", price: 399.99, stock: 0, status: "Out of Stock" },122 { id: 5, name: "Office Chair", category: "Furniture", price: 249.99, stock: 56, status: "Available" },123 { id: 6, name: "Monitor Stand", category: "Furniture", price: 79.99, stock: 8, status: "Low Stock" },124 { id: 7, name: "Notebook Set", category: "Stationery", price: 12.99, stock: 445, status: "Available" },125 { id: 8, name: "Pen Collection", category: "Stationery", price: 19.99, stock: 312, status: "Available" },126 { id: 9, name: "Desk Organizer", category: "Stationery", price: 24.99, stock: 5, status: "Low Stock" },127 { id: 10, name: "Coffee Maker", category: "Appliances", price: 89.99, stock: 78, status: "Available" },128 { id: 11, name: "Electric Kettle", category: "Appliances", price: 34.99, stock: 134, status: "Available" },129 { id: 12, name: "Desk Lamp LED", category: "Appliances", price: 44.99, stock: 0, status: "Out of Stock" },130];131132export const programmaticControlHeaders: AngularColumnDef<ProgrammaticControlProduct, any>[] = [133 { accessor: "id", label: "ID", width: 70, type: "number", sortable: true, filterable: true },134 { accessor: "name", label: "Product Name", width: "1fr", minWidth: 150, type: "string", sortable: true, filterable: true },135 {136 accessor: "category",137 label: "Category",138 width: 140,139 type: "enum",140 sortable: true,141 filterable: true,142 enumOptions: ["Electronics", "Furniture", "Stationery", "Appliances"].map((v) => ({ label: v, value: v })),143 },144 { accessor: "price", label: "Price", width: 110, align: "right", type: "number", sortable: true, filterable: true, valueFormatter: ({ value }: ValueFormatterProps<ProgrammaticControlProduct, number>) => `$${value.toFixed(2)}` },145 { accessor: "stock", label: "Stock", width: 100, align: "right", type: "number", sortable: true, filterable: true },146 {147 accessor: "status",148 label: "Status",149 width: 110,150 type: "enum",151 sortable: true,152 filterable: true,153 enumOptions: ["Available", "Low Stock", "Out of Stock"].map((v) => ({ label: v, value: v })),154 },155];156157export const programmaticControlConfig = {158 headers: programmaticControlHeaders,159 rows: programmaticControlData,160};161162export { STATUS_COLORS as PROGRAMMATIC_CONTROL_STATUS_COLORS };163
Vue SFC
Copy
1<template>2 <div>3 <div4 style="5 margin-bottom: 12px;6 padding: 8px 12px;7 background-color: #eff6ff;8 border: 1px solid #bfdbfe;9 border-radius: 6px;10 color: #1e40af;11 font-size: 14px;12 "13 >14 {{ statusMessage }}15 </div>16 <div style="display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap">17 <button @click="handleSortByName">Sort by Name (A-Z)</button>18 <button @click="handleSortByPrice">Sort by Price (High to Low)</button>19 <button @click="handleFilterAvailable">Filter: Available</button>20 <button @click="handleClearFilters">Clear Filters</button>21 <button @click="handleGetInfo">Get Table Info</button>22 </div>23 <SimpleTable24 ref="tableRef"25 :columns="headers"26 :rows="programmaticControlConfig.rows"27 :get-row-id="getRowId"28 :height="height"29 :theme="theme"30 />31 </div>32</template>3334<script setup lang="ts">35import { ref } from "vue";36import { SimpleTable } from "@simple-table/vue";37import type {38 Theme,39 VueColumnDef,40 CellRendererProps,41 GetRowIdParams,42 SimpleTableExposed,43} from "@simple-table/vue";44import { programmaticControlConfig, PROGRAMMATIC_CONTROL_STATUS_COLORS } from "./programmatic-control.demo-data";45import type { ProgrammaticControlProduct } from "./programmatic-control.demo-data";46import "@simple-table/vue/styles.css";4748withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {49 height: "400px",50});5152const tableRef = ref<SimpleTableExposed<ProgrammaticControlProduct> | null>(null);53const statusMessage = ref("No status message");54const getRowId = ({ row }: GetRowIdParams<ProgrammaticControlProduct>) => row.id;5556const headers: VueColumnDef<ProgrammaticControlProduct>[] = programmaticControlConfig.headers.map((col) => {57 if (col.accessor === "status") {58 return {59 ...col,60 cellRenderer: ({ row }: CellRendererProps<ProgrammaticControlProduct>) => {61 const s = row.status;62 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[s] ?? { bg: "#f3f4f6", color: "#374151" };63 return `<span style="background:${colors.bg};color:${colors.color};padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold">${s}</span>`;64 },65 };66 }67 return { ...col };68});6970function handleSortByName() {71 tableRef.value?.getAPI()?.applySortState({ accessor: "name", direction: "asc" });72 statusMessage.value = "Sorted by Name (A-Z)";73}7475function handleSortByPrice() {76 tableRef.value?.getAPI()?.applySortState({ accessor: "price", direction: "desc" });77 statusMessage.value = "Sorted by Price (High to Low)";78}7980function handleFilterAvailable() {81 tableRef.value?.getAPI()?.applyFilter({ accessor: "status", operator: "equals", value: "Available" });82 statusMessage.value = "Filtered to show only Available products";83}8485function handleClearFilters() {86 tableRef.value?.getAPI()?.clearAllFilters();87 statusMessage.value = "All filters cleared";88}8990function handleGetInfo() {91 const api = tableRef.value?.getAPI();92 if (!api) return;93 const allRows = api.getAllRows();94 const hdrs = api.getHeaders();95 const sortState = api.getSortState();96 const filterState = api.getFilterState();97 const totalValue = allRows.reduce(98 (sum, r) => sum + (Number(r.row.price) || 0) * (Number(r.row.stock) || 0),99 0,100 );101 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";102 alert(103 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,104 );105 statusMessage.value = "Table info displayed";106}107</script>
Svelte
Copy
1<script lang="ts">2 import { SimpleTable } from "@simple-table/svelte";3 import type {4 Theme,5 SvelteColumnDef,6 CellRendererProps,7 TableAPI,8 GetRowIdParams,9 } from "@simple-table/svelte";10 import { programmaticControlConfig, PROGRAMMATIC_CONTROL_STATUS_COLORS } from "./programmatic-control.demo-data";11 import type { ProgrammaticControlProduct } from "./programmatic-control.demo-data";12 import "@simple-table/svelte/styles.css";1314 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();1516 let tableRef = $state<{ getAPI: () => TableAPI<ProgrammaticControlProduct> | null } | null>(null);17 let statusMessage = $state("No status message");1819 const getRowId = ({ row }: GetRowIdParams<ProgrammaticControlProduct>) => row.id;2021 const headers: SvelteColumnDef<ProgrammaticControlProduct>[] = programmaticControlConfig.headers.map((h) => {22 if (h.accessor === "status") {23 return {24 ...h,25 cellRenderer: ({ row }: CellRendererProps<ProgrammaticControlProduct>) => {26 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[row.status] ?? { bg: "#f3f4f6", color: "#374151" };27 return `<span style="background:${colors.bg};color:${colors.color};padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold">${row.status}</span>`;28 },29 };30 }31 return { ...h };32 });3334 function handleSortByName() {35 tableRef?.getAPI()?.applySortState({ accessor: "name", direction: "asc" });36 statusMessage = "Sorted by Name (A-Z)";37 }3839 function handleSortByPrice() {40 tableRef?.getAPI()?.applySortState({ accessor: "price", direction: "desc" });41 statusMessage = "Sorted by Price (High to Low)";42 }4344 function handleFilterAvailable() {45 tableRef?.getAPI()?.applyFilter({ accessor: "status", operator: "equals", value: "Available" });46 statusMessage = "Filtered to show only Available products";47 }4849 function handleClearFilters() {50 tableRef?.getAPI()?.clearAllFilters();51 statusMessage = "All filters cleared";52 }5354 function handleGetInfo() {55 const api = tableRef?.getAPI();56 if (!api) return;57 const allRows = api.getAllRows();58 const hdrs = api.getHeaders();59 const sortState = api.getSortState();60 const filterState = api.getFilterState();61 const totalValue = allRows.reduce(62 (sum, r) => sum + r.row.price * r.row.stock,63 0,64 );65 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";66 alert(67 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,68 );69 statusMessage = "Table info displayed";70 }71</script>7273<div>74 <div style="margin-bottom: 12px; padding: 8px 12px; background-color: #eff6ff; border: 1px solid #bfdbfe; border-radius: 6px; color: #1e40af; font-size: 14px;">75 {statusMessage}76 </div>77 <div style="display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap;">78 <button onclick={handleSortByName}>Sort by Name (A-Z)</button>79 <button onclick={handleSortByPrice}>Sort by Price (High to Low)</button>80 <button onclick={handleFilterAvailable}>Filter: Available</button>81 <button onclick={handleClearFilters}>Clear Filters</button>82 <button onclick={handleGetInfo}>Get Table Info</button>83 </div>84 <SimpleTable85 bind:this={tableRef}86 columns={headers}87 {getRowId}88 rows={programmaticControlConfig.rows}89 {height}90 {theme}91 />92</div>
Solid TSX
Copy
1import { createSignal } from "solid-js";2import { SimpleTable } from "@simple-table/solid";3import type { Theme, TableAPI, SolidColumnDef, CellRendererProps } from "@simple-table/solid";4import {5 programmaticControlConfig,6 PROGRAMMATIC_CONTROL_STATUS_COLORS,7 type ProgrammaticControlProduct,8} from "./programmatic-control.demo-data";9import "@simple-table/solid/styles.css";1011export default function ProgrammaticControlDemo(props: {12 height?: string | number;13 theme?: Theme;14}) {15 let tableRef: TableAPI<ProgrammaticControlProduct> | undefined;16 const [statusMessage, setStatusMessage] = createSignal("No status message");1718 const headers: SolidColumnDef<ProgrammaticControlProduct>[] = programmaticControlConfig.headers.map((h) => {19 if (h.accessor === "status") {20 return {21 ...h,22 cellRenderer: (cr: CellRendererProps<ProgrammaticControlProduct>) => {23 const s = String(cr.row.status);24 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[s] ?? {25 bg: "#f3f4f6",26 color: "#374151",27 };28 return (29 <span30 style={{31 background: colors.bg,32 color: colors.color,33 padding: "4px 8px",34 "border-radius": "4px",35 "font-size": "12px",36 "font-weight": "bold",37 }}38 >39 {s}40 </span>41 );42 },43 };44 }45 return h;46 });4748 const handleSortByName = () => {49 tableRef?.applySortState({ accessor: "name", direction: "asc" });50 setStatusMessage("Sorted by Name (A-Z)");51 };5253 const handleSortByPrice = () => {54 tableRef?.applySortState({ accessor: "price", direction: "desc" });55 setStatusMessage("Sorted by Price (High to Low)");56 };5758 const handleFilterAvailable = () => {59 tableRef?.applyFilter({ accessor: "status", operator: "equals", value: "Available" });60 setStatusMessage("Filtered to show only Available products");61 };6263 const handleClearFilters = () => {64 tableRef?.clearAllFilters();65 setStatusMessage("All filters cleared");66 };6768 const handleGetInfo = () => {69 if (!tableRef) return;70 const allRows = tableRef.getAllRows();71 const hdrs = tableRef.getHeaders();72 const sortState = tableRef.getSortState();73 const filterState = tableRef.getFilterState();74 const totalValue = allRows.reduce((sum, r) => sum + r.row.price * r.row.stock, 0);75 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";76 alert(77 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,78 );79 setStatusMessage("Table info displayed");80 };8182 return (83 <div>84 <div85 style={{86 "margin-bottom": "12px",87 padding: "8px 12px",88 "background-color": "#eff6ff",89 border: "1px solid #bfdbfe",90 "border-radius": "6px",91 color: "#1e40af",92 "font-size": "14px",93 }}94 >95 {statusMessage()}96 </div>97 <div style={{ "margin-bottom": "12px", display: "flex", gap: "8px", "flex-wrap": "wrap" }}>98 <button onClick={handleSortByName}>Sort by Name (A-Z)</button>99 <button onClick={handleSortByPrice}>Sort by Price (High to Low)</button>100 <button onClick={handleFilterAvailable}>Filter: Available</button>101 <button onClick={handleClearFilters}>Clear Filters</button>102 <button onClick={handleGetInfo}>Get Table Info</button>103 </div>104 <SimpleTable105 ref={(api) => (tableRef = api)}106 columns={headers}107 getRowId={({ row }) => row.id}108 rows={programmaticControlConfig.rows}109 height={props.height ?? "400px"}110 theme={props.theme}111 />112 </div>113 );114}
TypeScriptProgrammaticControlDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { ProgrammaticControlProduct } from "./programmatic-control.demo-data";3import type { Theme, ColumnDef, CellRendererProps, GetRowIdParams } from "simple-table-core";4import {5 programmaticControlConfig,6 PROGRAMMATIC_CONTROL_STATUS_COLORS,7} from "./programmatic-control.demo-data";8import "simple-table-core/styles.css";91011const getRowId = ({ row }: GetRowIdParams<ProgrammaticControlProduct>) => row.id;12export function renderProgrammaticControlDemo(13 container: HTMLElement,14 options?: { height?: string | number; theme?: Theme },15): SimpleTableVanilla<ProgrammaticControlProduct> {16 const wrapper = document.createElement("div");1718 const banner = document.createElement("div");19 banner.style.cssText =20 "margin-bottom:12px;padding:8px 12px;background-color:#eff6ff;border:1px solid #bfdbfe;border-radius:6px;color:#1e40af;font-size:14px";21 banner.textContent = "No status message";22 wrapper.appendChild(banner);2324 const controls = document.createElement("div");25 controls.style.cssText = "margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap";2627 const headers: ColumnDef<ProgrammaticControlProduct>[] = programmaticControlConfig.headers.map((h) => {28 if (h.accessor === "status") {29 return {30 ...h,31 cellRenderer: ({ row }: CellRendererProps<ProgrammaticControlProduct>) => {32 const s = String(row.status);33 const colors = PROGRAMMATIC_CONTROL_STATUS_COLORS[s] ?? {34 bg: "#f3f4f6",35 color: "#374151",36 };37 return `<span style="background:${colors.bg};color:${colors.color};padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold">${s}</span>`;38 },39 };40 }41 return { ...h };42 });4344 let table: SimpleTableVanilla<ProgrammaticControlProduct>;4546 function setStatus(msg: string) {47 banner.textContent = msg;48 }4950 const buttons: Array<{ label: string; action: () => void }> = [51 {52 label: "Sort by Name (A-Z)",53 action: () => {54 table.getAPI().applySortState({ accessor: "name", direction: "asc" });55 setStatus("Sorted by Name (A-Z)");56 },57 },58 {59 label: "Sort by Price (High to Low)",60 action: () => {61 table.getAPI().applySortState({ accessor: "price", direction: "desc" });62 setStatus("Sorted by Price (High to Low)");63 },64 },65 {66 label: "Filter: Available",67 action: () => {68 table.getAPI().applyFilter({ accessor: "status", operator: "equals", value: "Available" });69 setStatus("Filtered to show only Available products");70 },71 },72 {73 label: "Clear Filters",74 action: () => {75 table.getAPI().clearAllFilters();76 setStatus("All filters cleared");77 },78 },79 {80 label: "Get Table Info",81 action: () => {82 const api = table.getAPI();83 const allRows = api.getAllRows();84 const hdrs = api.getHeaders();85 const sortState = api.getSortState();86 const filterState = api.getFilterState();87 const totalValue = allRows.reduce(88 (sum, r) => sum + Number(r.row.price) * Number(r.row.stock),89 0,90 );91 const sortInfo = sortState ? `${sortState.key.label} (${sortState.direction})` : "None";92 alert(93 `Table Info:\n• Rows: ${allRows.length}\n• Columns: ${hdrs.length}\n• Active filters: ${Object.keys(filterState).length}\n• Sort: ${sortInfo}\n• Total inventory value: $${totalValue.toFixed(2)}`,94 );95 setStatus("Table info displayed");96 },97 },98 ];99100 for (const { label, action } of buttons) {101 const btn = document.createElement("button");102 btn.textContent = label;103 btn.addEventListener("click", action);104 controls.appendChild(btn);105 }106107 wrapper.appendChild(controls);108109 const tableContainer = document.createElement("div");110 wrapper.appendChild(tableContainer);111 container.appendChild(wrapper);112113 table = new SimpleTableVanilla(tableContainer, {114 getRowId,115 columns: headers,116 rows: programmaticControlConfig.rows,117 height: options?.height ?? "400px",118 theme: options?.theme,119 });120121 return table;122}123124125// programmatic-control.demo-data.ts126// Self-contained demo table setup for this example.127import type { ColumnDef } from "simple-table-core";128129export interface ProgrammaticControlProduct {130 id: number;131 name: string;132 category: string;133 price: number;134 stock: number;135 status: string;136}137138export const STATUS_COLORS: Record<string, { bg: string; color: string }> = {139 Available: { bg: "#dcfce7", color: "#166534" },140 "Low Stock": { bg: "#fef3c7", color: "#92400e" },141 "Out of Stock": { bg: "#fee2e2", color: "#991b1b" },142};143144export const programmaticControlData: ProgrammaticControlProduct[] = [145 { id: 1, name: "Wireless Keyboard", category: "Electronics", price: 49.99, stock: 145, status: "Available" },146 { id: 2, name: "Ergonomic Mouse", category: "Electronics", price: 29.99, stock: 12, status: "Low Stock" },147 { id: 3, name: "USB-C Hub", category: "Electronics", price: 39.99, stock: 234, status: "Available" },148 { id: 4, name: "Standing Desk", category: "Furniture", price: 399.99, stock: 0, status: "Out of Stock" },149 { id: 5, name: "Office Chair", category: "Furniture", price: 249.99, stock: 56, status: "Available" },150 { id: 6, name: "Monitor Stand", category: "Furniture", price: 79.99, stock: 8, status: "Low Stock" },151 { id: 7, name: "Notebook Set", category: "Stationery", price: 12.99, stock: 445, status: "Available" },152 { id: 8, name: "Pen Collection", category: "Stationery", price: 19.99, stock: 312, status: "Available" },153 { id: 9, name: "Desk Organizer", category: "Stationery", price: 24.99, stock: 5, status: "Low Stock" },154 { id: 10, name: "Coffee Maker", category: "Appliances", price: 89.99, stock: 78, status: "Available" },155 { id: 11, name: "Electric Kettle", category: "Appliances", price: 34.99, stock: 134, status: "Available" },156 { id: 12, name: "Desk Lamp LED", category: "Appliances", price: 44.99, stock: 0, status: "Out of Stock" },157];158159export const programmaticControlHeaders: ColumnDef<ProgrammaticControlProduct>[] = [160 { accessor: "id", label: "ID", width: 70, type: "number", sortable: true, filterable: true },161 { accessor: "name", label: "Product Name", width: "1fr", minWidth: 150, type: "string", sortable: true, filterable: true },162 {163 accessor: "category",164 label: "Category",165 width: 140,166 type: "enum",167 sortable: true,168 filterable: true,169 enumOptions: ["Electronics", "Furniture", "Stationery", "Appliances"].map((v) => ({ label: v, value: v })),170 },171 {172 accessor: "price",173 label: "Price",174 width: 110,175 align: "right",176 type: "number",177 sortable: true,178 filterable: true,179 valueFormatter: ({ value }) => `$${Number(value).toFixed(2)}`,180 },181 { accessor: "stock", label: "Stock", width: 100, align: "right", type: "number", sortable: true, filterable: true },182 {183 accessor: "status",184 label: "Status",185 width: 110,186 type: "enum",187 sortable: true,188 filterable: true,189 enumOptions: ["Available", "Low Stock", "Out of Stock"].map((v) => ({ label: v, value: v })),190 },191];192193export const programmaticControlConfig = {194 headers: programmaticControlHeaders,195 rows: programmaticControlData,196};197198export { STATUS_COLORS as PROGRAMMATIC_CONTROL_STATUS_COLORS };199
API methods
Data manipulation
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
updateData(params: { accessor: Accessor; newValue: CellValue; rowId?: RowId; rowIndex?: number }) => void | Optional | Programmatically update a cell. Prefer rowId (requires getRowId); rowIndex targets the source rows array. Triggers cellUpdateFlash when enabled. | |
setHeaderRename(params: { accessor: Accessor }) => void | Optional | Programmatically triggers the header rename mode for a specific column. Sets the header cell to editing mode, allowing the user to rename it. The header must have enableHeaderRename enabled. |
Data access
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
getVisibleRows() => TableRow[] | Optional | Returns the currently visible rows in the table. When pagination is enabled, this returns only the rows on the current page. When filters are applied, returns only filtered rows. This is useful for getting a snapshot of what the user is currently viewing. | |
getAllRows() => TableRow[] | Optional | Returns all rows in the table as TableRow objects, flattened and including nested/grouped rows. Each TableRow contains the raw row data plus metadata like depth, position, and rowPath. Unlike getVisibleRows, this returns the complete dataset regardless of pagination, filters, or grouping state. Perfect for exporting complete data, analytics, or batch operations. | |
getHeaders() => ColumnDef[] | Optional | Returns the table's current header/column definitions. Includes all column configuration such as accessors, labels, types, and formatting options. Useful for dynamic table manipulation, export configurations, or building custom UI controls. |
Export
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
exportToCSV | Optional | Exports the current table data to a CSV file. Respects active filters and sorting. Optionally accepts a props object to customize the filename. |
Sort, filter, and pagination
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
getSortState | Optional | Returns the current sort state of the table. Returns null if no sorting is applied, or a SortColumn object containing the sorted column and direction. Useful for persisting table state, synchronizing with external state management, or implementing custom sort UI. | |
| Optional | Programmatically applies a sort state to the table. Pass a column accessor and optional direction to sort, or undefined to clear sorting. If direction is omitted, the sort cycles through: asc → desc → removed. This method is async and returns a Promise. Perfect for implementing custom sort controls or coordinating sorting with external data sources. | ||
getFilterState | Optional | Returns the current filter state of the table as a TableFilterState object. The object contains all active filters keyed by unique filter IDs. Each filter includes the column accessor, operator, and values. Useful for debugging, persisting filter state, or building custom filter UI. | |
applyFilter | Optional | Programmatically applies a filter to a specific column. Accepts a FilterCondition object specifying the column accessor, filter operator, and value(s). This method is async and returns a Promise. Supports all filter operators including equals, contains, greaterThan, between, and more. Perfect for implementing custom filter UI, applying saved filters, or creating filter presets. | |
clearFilter | Optional | Clears the filter for a specific column identified by its accessor. This method is async and returns a Promise. Only removes filters applied to the specified column, leaving other column filters intact. Useful for implementing 'clear filter' buttons on individual columns or resetting specific filters programmatically. | |
clearAllFilters() => Promise<void> | Optional | Clears all active filters from the table at once. This method is async and returns a Promise. Resets the table to show all data without any filtering applied. Perfect for 'reset all filters' buttons or starting fresh with filter state. | |
setQuickFilter(text: string) => void | Optional | Programmatically sets the quick filter text. This allows you to control the global search/quick filter from your code. Pass a string to set the filter text, or an empty string to clear it. The filter will use the mode and other settings from the quickFilter prop configuration. | |
getCurrentPage() => number | Optional | Returns the current page number when pagination is enabled. Page numbers are 1-indexed (first page is 1, not 0). Returns the current page regardless of whether pagination is client-side or server-side. Useful for tracking user navigation, syncing with URL parameters, or building custom pagination UI. | |
setPage(page: number) => Promise<void> | Optional | Programmatically navigates to a specific page when pagination is enabled. Accepts a 1-indexed page number (first page is 1). This method is async and returns a Promise. Works with both client-side and server-side pagination. If the page number is out of range, it will be clamped to valid bounds. Triggers the onPageChange callback when the page changes. Perfect for implementing custom pagination controls, deep linking, or restoring saved pagination state. |
Column editor and pinning
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
toggleColumnEditor(open?: boolean) => void | Optional | Opens, closes, or toggles the column editor menu programmatically. When called without arguments, it toggles the current state (open if closed, close if open). Pass true to explicitly open the menu, or false to explicitly close it. Requires enableColumnEditor. Pair with columnEditorConfig.showToggle: false to hide the built-in Columns strip and drive the editor from your own toolbar button. | |
applyColumnVisibility | Optional | Programmatically controls which columns are visible in the table. Accepts a partial or complete visibility state object where keys are column accessors and values are booleans (true = visible, false = hidden). This method is async and returns a Promise. You can pass just the columns you want to change, and other columns will maintain their current visibility state. Perfect for implementing custom column visibility presets, views, or user preferences. | |
| Optional | Returns { left, main, right }: root accessors in each pin band. Use with applyPinnedState to save and restore layout. | ||
| Optional | Set column order and pin sides in one call. Each root accessor must appear exactly once across left, main, and right. Columns with essential keep required order within each section. |
Row grouping
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
expandAll() => void | Optional | Expands all rows at all depths in the table. When working with hierarchical/grouped data, this will expand every level of the hierarchy, revealing all nested rows. Useful for 'expand all' buttons or when you want to show the complete data structure to users. | |
collapseAll() => void | Optional | Collapses all rows at all depths in the table. When working with hierarchical/grouped data, this will collapse every level of the hierarchy, hiding all nested rows. Perfect for 'collapse all' buttons or resetting the table to a compact view. | |
expandDepth(depth: number) => void | Optional | Expands all rows at a specific depth level (0-indexed). Depth 0 represents the top-level rows, depth 1 is the first nested level, depth 2 is the second nested level, and so on. This allows granular control over which hierarchy levels are visible. Useful for showing specific levels of detail without expanding everything. | |
collapseDepth(depth: number) => void | Optional | Collapses all rows at a specific depth level (0-indexed). Depth 0 represents the top-level rows, depth 1 is the first nested level, and so on. This allows you to selectively hide specific hierarchy levels while keeping others visible. Useful for managing complex hierarchies and controlling information density. | |
toggleDepth(depth: number) => void | Optional | Toggles the expansion state for all rows at a specific depth level (0-indexed). If the depth is currently expanded, it will be collapsed, and vice versa. This provides a convenient way to toggle visibility of an entire hierarchy level without tracking state manually. | |
setExpandedDepths(depths: Set<number>) => void | Optional | Sets which depth levels should be expanded, replacing the current expansion state entirely. Accepts a Set of depth numbers (0-indexed). This is useful for restoring saved expansion state, implementing presets, or coordinating expansion across multiple tables. Any depth not in the Set will be collapsed. | |
getExpandedDepths() => Set<number> | Optional | Returns a Set containing all currently expanded depth levels (0-indexed). This allows you to inspect which hierarchy levels are currently visible. Useful for saving expansion state, building custom UI controls, or coordinating with other components. | |
getGroupingProperty(depth: number) => Accessor | undefined | Optional | Returns the grouping property name (accessor) for a specific depth index (0-indexed). This maps depth levels to their corresponding property names in your rowGrouping configuration. Returns undefined if the depth doesn't exist. Useful for understanding the hierarchy structure or building dynamic UI that adapts to the grouping configuration. | |
getGroupingDepth(property: Accessor) => number | Optional | Returns the depth index (0-indexed) for a specific grouping property name (accessor). This is the inverse of getGroupingProperty - it maps property names to their depth levels in the hierarchy. Returns -1 if the property is not part of the grouping configuration. Useful for programmatically determining which level a property belongs to. |