From 5ce8b984221141121c6c31b6661762f587a7f866 Mon Sep 17 00:00:00 2001 From: Seyyed Morteza Moosavi Date: Fri, 26 Dec 2025 19:16:15 +0330 Subject: [PATCH] docs(table): add docs for @ctablex/table --- packages/ctablex-table/docs/COMPONENTS.md | 1370 +++++++++++++++++++++ packages/ctablex-table/docs/OVERVIEW.md | 744 +++++++++++ 2 files changed, 2114 insertions(+) create mode 100644 packages/ctablex-table/docs/COMPONENTS.md create mode 100644 packages/ctablex-table/docs/OVERVIEW.md diff --git a/packages/ctablex-table/docs/COMPONENTS.md b/packages/ctablex-table/docs/COMPONENTS.md new file mode 100644 index 0000000..b2c4331 --- /dev/null +++ b/packages/ctablex-table/docs/COMPONENTS.md @@ -0,0 +1,1370 @@ +# Components API Reference + +Complete reference for all components in **@ctablex/table**. + +## Table of Contents + +- [DataTable](#datatable) +- [Columns](#columns) +- [Column](#column) +- [Table](#table) +- [TableHeader](#tableheader) +- [HeaderRow](#headerrow) +- [HeaderCell](#headercell) +- [TableBody](#tablebody) +- [Rows](#rows) +- [Row](#row) +- [Cell](#cell) +- [TableFooter](#tablefooter) +- [Table Elements Context](#table-elements-context) +- [Re-exported Components](#re-exported-components) + +--- + +## DataTable + +The root component that extracts and provides column definitions and data to the table. +It serves two main purposes: + +- Extract column definitions from `` components and provide them via columns context +- Provide data to the table via content context + +### Behavior + +This component does not render any DOM elements itself. + +First, it extracts column definitions from its immediate children, typically `` components. Any component can be marked as a definition by setting the `__COLUMNS__` static property to `true` on its type. + +Next, it optionally accepts a `data` prop and provides it via content context. If the `data` prop is not provided but content context already contains data, it uses the existing data. If neither is available, it throws an error. + +Finally, it renders its children with definition components removed from the tree. + +### Examples + +**Extracting column definitions:** + +```tsx + + Columns definition... +
Other children...
+
+``` + +The `` component is extracted and removed from the render tree. Only `
Other children...
` is rendered. + +**Error case - no data available:** + +```tsx +{/* Children */} +``` + +Throws an error because neither `data` prop nor content context provides data. + +**Basic usage with data prop:** + +```tsx +{/* Children */} +``` + +Provides `data` to children via content context. + +**Using existing content context:** + +```tsx + + {/* Children */} + +``` + +No `data` prop needed—uses the existing data from content context. + +**Data prop overrides context:** + +```tsx + + {/* Children */} + +``` + +The `data` prop takes precedence. Children receive `inner` instead of `outer`. + +**Using with data fetching components:** + +```tsx + + {/* Children */} + +``` + +`` fetches and provides user data via content context. `` uses that data without needing a `data` prop. + +**Custom column container components:** + +```tsx +function MyColumns() { + return ( + <> + + + + ); +} +MyColumns.__COLUMNS__ = true; // Mark as column container +``` + +You can create reusable column definition components by marking them with `__COLUMNS__ = true`. + +```tsx + + + +``` + +`` is extracted and removed from the render tree because it's marked as a column container. + +**Non-immediate children are not extracted:** + +```tsx + + <> + {/* ... */} + + +``` + +The `` component is wrapped in a fragment, so it's not an immediate child. It will be rendered instead of being extracted. Read more about this behavior in the [Columns](./COMPONENTS.md#columns) documentation. + +## Columns + +Container for column definitions. It is marked as a column definition by setting the `__COLUMNS__` static property to `true` on its type and is extracted by `` when it is an immediate child. + +When it is not an immediate child of ``, it will be rendered. It does not render any DOM elements itself and does not render its own children. Instead, it renders children provided by `` via columns context. + +It also accepts a `part` prop to identify different column groups. When a `part` prop is provided, it renders only the children of the matching part from columns context. If more than one `` with the same `part` exists, all definitions are rendered. + +### Examples + +**Common usage scenario:** + +```tsx + + + + + + + +``` + +The `` component is extracted by ``, and the column definitions are provided to `
` via columns context. `
` expands to its default children: + +```tsx +
+ + + + + + + + + + + + +
+``` + +When `` is rendered in different contexts (header vs body), it outputs the same column definitions. This allows you to define columns once and use them in multiple places. + +**Basic usage:** + +```tsx + + + in Columns + +
+ outside Columns + +
+
+``` + +The `` definition is extracted by ``. When rendered inside the `
`, it outputs its defined children: + +```html +
+ outside Columns + in Columns +
+``` + +**Using the `part` prop:** + +```tsx + + + no part + + + in Detail + +
+ outside Columns + +
+ +
+
+
+``` + +The first `` renders the default definition, while `` renders the matching part: + +```html +
+ outside Columns + no part +
+ in Detail +
+
+``` + +## Column + +The Column component behaves differently depending on its rendering context. This dual behavior helps keep header and data cell definitions together in one place. + +**Inside a header context** (e.g., within ``): + +The Column renders a `` component (`` element) and passes the `header` prop as its children. It also passes the `thEl` prop to the `HeaderCell` as the `el` prop. + +```tsx +{props.header} +``` + +**Outside a header context** (e.g., within ``): + +The Column renders a `` component (`` element) and passes the `accessor` prop to `Cell` as the data accessor. It also passes the `el` prop to `Cell` as the `el` prop. It also passes its children to Cell. Default children of `Column` is ``. + +```tsx + + {children} + +``` + + + +## Table + +Renders the `` element and its structure. It has default children `` and ``, but you can customize the structure by providing your own children. + +The element that `
` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `
` element is rendered. + +### Examples + +**Basic usage:** + +```tsx +
+``` + +This is the same as: + +```tsx +
+ + +
+``` + +**Customizing table structure:** + +```tsx + + + + +
+``` + +You can override the default children to add additional sections like a footer. + +**Customizing table element:** + +```tsx +} /> +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + table:
, + // other elements... +}; +``` + +```tsx + +
+ +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> +
} /> + +``` + +The `el` prop takes precedence over context. The rendered table will have the class `my-table`. + +**Replacing table with a different element:** + +```tsx +
} /> +``` + +Renders a `
` with class `grid` instead of a `
`. + +**Warning about `el` children:** + +```tsx +
el child}>children
+``` + +Renders `
el child
` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use `
`'s children instead to maintain clarity and expected behavior. + +## TableHeader + +Renders the `` element. It has default children ``, but you can customize by providing your own children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +The component also provides header context (`IsHeaderContext` set to `true`) which is used by `` components to determine whether they should render as header cells (`, + // other elements... +}; +``` + +```tsx + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + } /> + +``` + +The `el` prop takes precedence over context. The rendered thead will have the class `my-header`. + +**Replacing thead with a different element:** + +```tsx +} /> +``` + +Renders a `
` with class `header-section` instead of a `
`. + +**Warning about `el` children:** + +```tsx +el child}>children +``` + +Renders `el child` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## HeaderRow + +Renders the `` element for the header row. It has default children ``, but you can customize by providing your own children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +### Examples + +**Basic usage:** + +```tsx + +``` + +This is the same as: + +```tsx + + + +``` + +**Customizing header row content:** + +```tsx + + + + +``` + +You can provide custom content or use specific column parts. + +**Customizing tr element:** + +```tsx +} /> +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + tr: , + // other elements... +}; +``` + +```tsx + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + } /> + +``` + +The `el` prop takes precedence over context. The rendered tr will have the class `my-header-row`. + +**Replacing tr with a different element:** + +```tsx +} /> +``` + +Renders a `
` with class `header-row` instead of a `
`. + +**Warning about `el` children:** + +```tsx +el child}>children +``` + +Renders `el child` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## HeaderCell + +Renders the `}>children +``` + +Renders `` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## TableBody + +Renders the `` element for the table body. It has default children ``, but you can customize by providing your own children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +### Examples + +**Basic usage:** + +```tsx + +``` + +This is the same as: + +```tsx + + + +``` + +**Customizing tbody element:** + +```tsx +} /> +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + tbody: , + // other elements... +}; +``` + +```tsx + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + } /> + +``` + +The `el` prop takes precedence over context. The rendered tbody will have the class `my-body`. + +**Replacing tbody with a different element:** + +```tsx +} /> +``` + +Renders a `
` with class `body-section` instead of a `
`. + +**Custom body structure:** + +```tsx + + + + + + +``` + +You can provide custom content in addition to or instead of ``. + +**Warning about `el` children:** + +```tsx +el child}>children +``` + +Renders `el child` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## Rows + +Iterates over data items and renders a row for each item. This component does not render any DOM elements itself. + +It has default children `` which is rendered for each item in the data array. You can customize by providing your own children. + +The component uses `` from `@ctablex/core` to iterate over items from content context. It also accepts a `keyAccessor` prop to extract React keys from each item. If not provided, the array index is used as the key. + +### Examples + +**Basic usage:** + +```tsx + +``` + +This is the same as: + +```tsx + + + +``` + +Renders a `` for each item in the data array from content context. + +**Using keyAccessor:** + +```tsx + +``` + +Extracts the `id` property from each data item to use as the React key. This is recommended for avoiding reconciliation issues, specially when stateful components are used inside rows. + +**Custom row components:** + +```tsx + + } /> + +``` + +You can customize the row component that gets rendered for each item. + +**Multiple row types:** + +```tsx + + + }> + + + +``` + +Renders multiple rows for each data item. This is useful for master-detail patterns. + +## Row + +Renders the `` element for a table row. It has default children ``, but you can customize by providing your own children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +By default, the component reads data from content context. This can be overridden by providing a `row` prop. If the `row` prop is provided, that data will be provided as content context for the children instead. + +### Examples + +**Basic usage:** + +```tsx + +``` + +This is the same as: + +```tsx + + + +``` + +**Customizing tr element:** + +```tsx +} /> +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + tr: , + // other elements... +}; +``` + +```tsx + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + } /> + +``` + +The `el` prop takes precedence over context. The rendered tr will have the class `my-row`. + +**Overriding row data:** + +```tsx + + + +``` + +Provides `customData` to children via content context instead of the data from parent context. This is useful for special rows like summaries. + +**Custom row content:** + +```tsx + + + + + +``` + +You can mix custom cells with column-based cells. + +**Replacing tr with a different element:** + +```tsx +} /> +``` + +Renders a `
` with class `row` instead of a `
`. + +**Warning about `el` children:** + +```tsx +el child}>children +``` + +Renders `el child` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## Cell + +Renders the `; +} +``` + +```tsx +}> + + +``` + +The custom `ColoredTd` component receives the extracted `status` value via `useContent()` and applies conditional styling. + +**el accessing full item:** + +```tsx +function StatusTd(props: Props) { + const item = useContent(); + const color = item.status === 'Active' ? 'green' : 'red'; + return ; +} +``` + +```tsx +}> + + + + +``` + +Without an `accessor` on ``, the custom element receives the full item and can access multiple properties. + +**Warning about `el` children:** + +```tsx +el child}> + children + +``` + +Renders `` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## TableFooter + +Renders the `` element for the table footer. It does not have default children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +### Examples + +**Basic usage:** + +```tsx + + + + + +``` + +**Customizing tfoot element:** + +```tsx +}> + + + + +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + tfoot: , + // other elements... +}; +``` + +```tsx + + + + + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + }> + + + + + +``` + +The `el` prop takes precedence over context. The rendered tfoot will have the class `my-footer`. + +**Replacing tfoot with a different element:** + +```tsx +}> + Footer content + +``` + +Renders a `
` with class `footer-section` instead of a `
`. + +**Warning about `el` children:** + +```tsx +el child}>children +``` + +Renders `el child` and ignores `children`. The `el` prop's children take precedence over ``'s children. + +**Note:** Avoid passing children to the `el` prop. Use ``'s children instead to maintain clarity and expected behavior. + +## Table Elements Context + +The table elements context system allows you to customize the HTML elements used throughout your tables. This is particularly useful when integrating with UI frameworks like Material-UI, Ant Design, or other component libraries where you want to replace standard HTML table elements with custom components. + +### API + +- **`TableElementsProvider`**: Context provider component that supplies custom table element components to all descendant table components. +- **`useTableElements()`**: Hook that returns the current table elements from context. Returns `defaultTableElements` if no custom elements are provided. +- **`defaultTableElements`**: The default set of HTML table elements (`
`) or data cells (``). + +### Examples + +**Basic usage:** + +```tsx + +``` + +This is the same as: + +```tsx + + + +``` + +**Customizing header structure:** + +```tsx + + + + +``` + +You can provide multiple header rows or other custom structure. + +**Customizing thead element:** + +```tsx +} /> +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + thead:
IDName
` element for table headers. This component does not have default children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +**Note:** Most of the time, this component is not used directly. Instead, it is rendered by the `` component when inside a header context (e.g., within ``). `header` and `thEl` props of `` are passed to `` as children and `el` respectively. + +### Examples + +**Basic usage:** + +```tsx +Name +``` + +Renders a simple header cell with text content. + +**Typical usage via Column:** + +```tsx + + + + + +``` + +The `` component internally renders `` when in header context. This is the recommended approach. + +**Customizing th element:** + +```tsx +}>Name +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + th: , + // other elements... +}; +``` + +```tsx + + Name + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + }>Name + +``` + +The `el` prop takes precedence over context. The rendered th will have the class `my-header-cell`. + +**Replacing th with a different element:** + +```tsx +}>Name +``` + +Renders a `
` with class `header-cell` instead of a `
`. + +**Warning about `el` children:** + +```tsx +el childel child
Custom summary row
+ +
CustomActions
` element for table data cells. This component does not have default children. + +The element that `` renders can be customized via table element context or the `el` prop: + +- If the `el` prop is provided, it is used to render the element. +- If table element context provides a value, it is used to render the element. +- Otherwise, a default `` element is rendered. + +The component accepts an `accessor` prop to read data from content context. It extracts the value using the accessor and provides it via content context to its children. If the `accessor` is undefined, the content context is passed as is without extraction. + +**Note:** Most of the time, this component is not used directly. Instead, it is rendered by the `` component when outside a header context. The `accessor`, `el`, and `children` props of `` are passed to ``. While `` itself has no default children, `` provides `` as default children. + +**Note:** The `el` prop renders inside the content extracted by `accessor`. This means custom element components can access the extracted value via `useContent()`. If you need access to the entire item instead of just the extracted value, omit the `accessor` prop and use `` with its own `accessor` in the children. + +### Examples + +**Basic usage:** + +```tsx + + + +``` + +Reads the `name` property from content context and provides it to children. + +**Typical usage via Column:** + +```tsx + + + + + + + +``` + +The `` component internally renders `` when outside header context. + +**Customizing td element:** + +```tsx +}> + + +``` + +Use the `el` prop to customize the rendered element with your own props like className. + +**Using table element context:** + +```tsx +const elements = { + td: , + // other elements... +}; +``` + +```tsx + + + + + +``` + +Provide custom elements via context to apply styling consistently across all table components. + +**Combining `el` prop and context:** + +```tsx + }}> + }> + + + +``` + +The `el` prop takes precedence over context. The rendered td will have the class `my-cell`. + +**No accessor (pass through):** + +```tsx + + + +``` + +Without an `accessor`, the entire content context is passed to children unchanged. Useful when you want full control over the cell content. + +**Custom cell content:** + +```tsx + + + +``` + +Provide custom components to render the cell content in a specific way. + +**Nested accessors:** + +```tsx + + + +``` + +Access nested properties using dot notation. + +**Replacing td with a different element:** + +```tsx +}> + + +``` + +Renders a `
` with class `cell` instead of a `
`. + +**el accessing extracted content:** + +```tsx +function ColoredTd() { + const content = useContent(); + const color = content === 'Active' ? 'green' : 'red'; + return {content}{props.children}el child
Footer content
Footer content
Footer content
Footer content
`, ``, ``, ``, ``, `
`, ``). These are used when no custom elements are provided via context. +- **`TableElements`**: TypeScript type definition for the table elements object structure. + +### How It Works + +When you provide custom elements via `TableElementsProvider`, all table components (`Table`, `TableHeader`, `TableBody`, `TableFooter`, `HeaderRow`, `Row`, `HeaderCell`, `Cell`) will use your custom elements instead of the default HTML elements. Each component checks for custom elements in this order: + +1. The `el` prop on the component (highest priority) +2. Elements from `TableElementsProvider` context +3. Default HTML elements (fallback) + +This provides flexibility: use context for global styling, and use the `el` prop for component-specific overrides. + +### Examples + +**Basic usage with custom styling:** + +```tsx +const customElements: TableElements = { + table: , + thead: , + tbody: , + tfoot: , + tr: , + th:
, + td: , +}; +``` + +```tsx + + + + + + + + + +``` + +All table elements will now use your custom classes. + +**Accessing table elements via hook:** + +```tsx +function MyCustomCell({ children }: { children: ReactNode }) { + const elements = useTableElements(); + return cloneElement(elements.td, { className: 'my-custom-td' }, children); +} +``` + +Use this pattern when you need to access the current table elements to create custom wrapper components. + +**Integration with Material-UI:** + +```tsx +import { + Table as MuiTable, + TableHead as MuiTableHead, + TableBody as MuiTableBody, + TableFooter as MuiTableFooter, + TableRow as MuiTableRow, + TableCell as MuiTableCell, +} from '@mui/material'; + +const muiElements: TableElements = { + table: , + thead: , + tbody: , + tfoot: , + tr: , + th: , // MUI TableCell renders as a
in and in by default. + td: , +}; +``` + +```tsx + + + + + + + + + +``` + +This seamlessly integrates ctablex with Material-UI components. + +**Partial customization:** + +```tsx +const partialElements: TableElements = { + ...defaultTableElements, + th:
, + td: , +}; +``` + +```tsx + + + + + + + + +``` + +Customize only specific elements while keeping the defaults for others. + +**Context with el prop override:** + +```tsx +const elements: TableElements = { + ...defaultTableElements, + td:
, +}; +``` + +```tsx + + + + {/* This cell gets 'default-cell' from context */} + + {/* This cell gets 'price-cell' from el prop - overrides context */} + } + /> + + + + +``` + +The `el` prop takes precedence over context elements. + +**Multiple tables with different styles:** + +```tsx +const blueElements: TableElements = { + ...defaultTableElements, + table:
, +}; + +const greenElements: TableElements = { + ...defaultTableElements, + table:
, +}; +``` + +```tsx +
+ + + + + +
+ + + + + + + + +
+ + + +``` + +Use separate providers to style different tables independently. + +**Creating a reusable themed table provider:** + +```tsx +function ThemedTableProvider({ + theme, + children, +}: { + theme: 'light' | 'dark'; + children: ReactNode; +}) { + const elements: TableElements = { + table:
, + thead: , + tbody: , + tfoot: , + tr: , + th:
, + td: , + }; + + return ( + {children} + ); +} +``` + +```tsx + + + + + + + + +``` + +Wrap the table elements context in a custom provider for reusable theming patterns. + +## Re-exported Components + +Some components are re-exported from `@ctablex/core` for convenience. + +- [DefaultContent](../../ctablex-core/docs/Contents.md#defaultcontent) +- [NullContent](../../ctablex-core/docs/Contents.md#nullcontent) +- [ContentValue](../../ctablex-core/docs/Contents.md#contentvalue) diff --git a/packages/ctablex-table/docs/OVERVIEW.md b/packages/ctablex-table/docs/OVERVIEW.md new file mode 100644 index 0000000..5f81a19 --- /dev/null +++ b/packages/ctablex-table/docs/OVERVIEW.md @@ -0,0 +1,744 @@ +# Overview + +This document provides an example and shows how it works, then step by step shows how it can be customized. + +## Table of Contents + +- [TL;DR](#tldr) +- [Prerequisites](#prerequisites) +- [Basic Example](#basic-example) + - [Step 1: DataTable extracts and removes Columns](#step-1-datatable-extracts-and-removes-columns) + - [Step 2: Table expands to default children](#step-2-table-expands-to-default-children) + - [Step 3: TableHeader](#step-3-tableheader) + - [Step 4: TableBody](#step-4-tablebody) +- [Data Providing Patterns](#data-providing-patterns) + - [Using the `data` prop](#using-the-data-prop) + - [Using Content Context](#using-content-context) + - [Using Data Provider Components](#using-data-provider-components) +- [Customization](#customization) + - [Custom Cell Content](#custom-cell-content) + - [Custom elements](#custom-elements) + - [Table Structure](#table-structure) +- [Type Safety](#type-safety) +- [Conclusion](#conclusion) + +## TL;DR + +- **Basic example and component expansion**: See how a simple `
` component automatically expands into ``, ``, rows, and cells through default children +- **Data providing patterns**: Learn three ways to provide data to the table—via `data` prop, content context, or data provider components +- **Customize cell content**: Learn to create custom content components like `NumberContent` that read from context and format data your way +- **Customize elements**: Discover how to swap default HTML elements using `TableElementsProvider` or the `el` prop to match your design system +- **Customize structure**: Explore techniques for changing table structure—add footers, remove headers, render multiple rows per item, or replace the table entirely with custom layouts + +## Prerequisites + +Before diving into this document, you should understand the **micro-context pattern** that powers ctablex. Read **[@ctablex/core MICRO-CONTEXT.md](../../ctablex-core/docs/MICRO-CONTEXT.md)** to learn about: + +- How context flows data through component hierarchies +- The `ContentProvider` and `useContent` pattern +- Why this approach enables composable, decoupled components + +## Basic Example + +This basic example shows how to create a simple data table with two columns: "Name" and "Price". The data is provided as an array of objects. + +```tsx +import { DataTable, Columns, Column, Table } from '@ctablex/table'; + +function BasicExample() { + const products = [ + { name: 'Apple', price: 1.2 }, + { name: 'Banana', price: 0.5 }, + { name: 'Cherry', price: 2.0 }, + ]; + + return ( + + + + + +
+ + ); +} +``` + +This code creates a simple data table displaying the names and prices of fruits. + +Final rendered table: + +```html +
+ + + + + + + + + + + + + + + + + + + + +
NamePrice
Apple1.2
Banana0.5
Cherry2.0
+``` + +Now let's go step by step to see how this transformation happens. + +### Step 1: DataTable extracts and removes Columns + +DataTable extracts column definitions (`...`) from its immediate children, provides them via `ColumnsContext`, and removes them from the render tree. After this step, here's what actually renders: + +```tsx + + {/* Columns removed by DataTable and provided via context */} + + +``` + +### Step 2: Table expands to default children + +Because the micro-context pattern passes data via context rather than props, child components don't receive changing data props. This allows us to use proper default children. + +`
` expands to its default children: `` and ``. So now we have the following code, which is exactly the same as above: + +```tsx + +
+ + +
+
+``` + +### Step 3: TableHeader + +`TableHeader` also has default children. It expands to `HeaderRow`: + +```tsx + + + + + + +
+
+``` + +`HeaderRow` also has default children. It expands to `Columns`: + +```tsx + + + + + + + + +
+
+``` + +As you remember, `Columns` was extracted by DataTable from immediate children, and provided via context. So here, +Columns reads columns from context and renders them here: + +```tsx + + + + + {/* Children of */} + + + + + +
+
+``` + +`TableHeader` provides `IsHeaderContext` (set to true). `Column` reads `IsHeaderContext` via `useIsHeader()` and detects that it is rendering in the header, so it renders `HeaderCell`: + +```tsx + + + + + {/* Name and Price are header props of Column */} + Name + Price + + + +
+
+``` + +### Step 4: TableBody + +`TableBody` also has default children. It expands to `Rows`: + +```tsx + + + {/* ... */} + + + +
+
+``` + +`Rows` also has default children. It expands to `Row`: + +```tsx + + + {/* ... */} + + + + + +
+
+``` + +Rows with the help of `ArrayContent` (a component from `@ctablex/core` that iterates over arrays) iterates over the products array, providing each item via ContentProvider. + +You can think of it like this: + +```tsx + + + {/* ... */} + + + + + + + + {/* ... for each product ... */} + +
+
+``` + +`Row` also has default children. It expands to `Columns`: + +```tsx + + + {/* ... */} + + + + + + + +
+
+``` + +As before, Columns reads columns from context and renders them here: + +```tsx + + + {/* ... */} + + + + {/* Children of */} + + + + + +
+
+``` + +`Column` also has default children, so if no children are provided, it uses ``: + +```tsx + + + {/* ... */} + + + + + + + + + + + + + + +
+
+``` + +`IsHeaderContext` is not provided here, so `Column` detects that it is not rendering in the header, so it renders `Cell`: + +```tsx + + + {/* ... */} + + + + + + + + + + + + + + +
+
+``` + +`Cell` extracts the value from content context using accessor and provides it via `ContentProvider` to its children. + +You can think of it like this: + +```tsx + + + {/* ... */} + + + + + + + + + + + + + + + + + + +
+
+``` + +`DefaultContent` reads the value from content context and renders it. + +`Table`, `TableHeader`, `TableBody`, `HeaderRow`, `Row`, `HeaderCell`, `Cell` render appropriate HTML elements like ``, ``, ``, ``, `
`, ``. So: + +```tsx + + + + + + + + + {/* does not render DOM itself, it iterates over products */} + + + + + + + + + {/* ... for each product ... */} + +
NamePrice
{products[0].name}{products[0].price}
{products[1].name}{products[1].price}
+``` + +This results in the final rendered table as shown in the basic example. + +## Data Providing Patterns + +There are three ways to provide data to the table, each suited for different scenarios. + +### Using the `data` prop + +The simplest approach, as shown in the basic example, is to provide data directly via the `data` prop of ``: + +```tsx +{/* ... */} +``` + +This works well for static data or when the data is already available in the component's scope. + +### Using Content Context + +You can also provide data via content context using `` from `@ctablex/core`: + +```tsx +import { ContentProvider } from '@ctablex/core'; + + + {/* Children */} +; +``` + +This is useful when you want to share data between multiple components or when data is provided by a parent component. + +### Using Data Provider Components + +The recommended pattern for production applications is to provide data via content context in a dedicated data provider component. This component can fetch data, manage loading states, or handle reactive updates internally, then provide the data via content context. For example: + +```tsx + + {/* Children */} + +``` + +`` fetches user data and provides it via content context. `` consumes this data without needing a `data` prop. This approach keeps the component tree constant, as no data prop is passed down, which can improve performance and simplify component composition. It also promotes separation of concerns—data fetching logic stays in the provider, while presentation logic stays in the table components. + +## Customization + +### Custom Cell Content + +You can customize the content of each cell by providing your own children to the `Column` component. For example, to format the price and add a suffix: + +```tsx + + + + + dollars + + + + +``` + +```tsx +import { useContent } from '@ctablex/core'; + +function NumberContent({ digits }: { digits: number }) { + const value = useContent(); + return <>{value.toFixed(digits)}; +} +``` + +You can define reusable content components like `NumberContent` that read the value from content context using `useContent()` and display it in a customized way. In this example, the `Column` will render "1.20 dollars" where `1.20` comes from `NumberContent` and `" dollars"` is rendered as the second child of the `Column`. + +If you need to access multiple values from the item, omit the `accessor` prop from `Column` to pass the entire item through. Then use `ContentValue` to select specific properties within the cell: + +```tsx + + + + + + + dollars + + +
+ +``` + +### Custom elements + +Two ways to customize elements used in table rendering: + +1. Using `TableElementsContext`: You can provide custom elements for all table components by using the `TableElementsProvider` component. +2. Using `el` prop: You can provide custom elements for specific components using the `el` prop. Child props will be added to the provided element. `Column` also supports `thEl` prop to provide custom header cell element. + +```tsx +const elements = { + table:
, + thead: , + tbody: , + tr: , + th: 10 ? 'expensive' : 'cheap'}>{children} + ); +} +``` + +### Table Structure + +A more advanced kind of customization is changing table structure, for example adding a footer. Instead of relying on default children, you can provide your own children to `Table`: + +```tsx + + + + + +
, + td: , +}; +``` + +```tsx + + + + + } + thEl={} + > + dollars + + + + + + + } /> + + +
+ + +``` + +`` will be rendered for each row. It can have access to `ContentContext` via `useContent` and render a customized row based on data. + +```tsx +function CustomRow({ children }: { children: ReactNode }) { + const item = useContent<{ name: string; price: number }>(); + return ( +
+ + + {/* custom footer content */} +
+
+``` + +Or you can remove the header: + +```tsx + + + + + + + +
+
+``` + +Or render a special row: + +```tsx + + + + + + + + + + + + {/* default rows */} + + +
Special Row
+
+``` + +You can render more than one row per item. The `part` prop will help you define more than one columns definition: + +```tsx + + + + + + + } /> + + + + + + {/* default row. uses as default children */} + + {/* additional row for description */} + + {/* find description columns definition by part="description" from columns context */} + + + + +
+
+``` + +You can integrate stateful components directly into the table structure to create interactive tables. A common pattern is expandable rows that reveal additional details on demand: + +```tsx + + + + + + + + + + } /> + + + + + + + + + + + + + +
+
+``` + +In this example: + +- **``** manages the open/closed state for each row and provides it via context +- **``** reads the state setter from context to toggle visibility +- **``** reads the open state from context and conditionally renders the detail row +- **`part="detail"`** defines a separate column layout for the expanded content + +This pattern leverages the micro-context architecture to share state between components without prop drilling, keeping your code clean and composable. + +You can even remove `Table` and implement your own table structure using lower-level components like `ArrayContent` and `ContentValue` from `@ctablex/core`: + +```tsx +import { ArrayContent, ContentValue } from '@ctablex/core'; + + + + + name:{' '} + + + + +
+ + price: $ + + + + +
+
+ +
+ +
+
+
+
; +``` + +You can also remove `Columns` from immediate children of DataTable and define children of Row and HeaderRow directly: + +```tsx + + + + + + + + + + + + + + + + +
NamePrice + + + +
+
+``` + +## Type Safety + +Micro-context provides weak type safety. Generic types must be manually specified and cannot be validated across context boundaries. See [MICRO-CONTEXT.md - Weak Type Safety](../../ctablex-core/docs/MICRO-CONTEXT.md#weak-type-safety) for details. + +However, when you provide a generic type to the `` element, strong type safety is provided for the accessor prop, including support for nested paths with autocomplete: + +```ts +interface Product { + name: string; + price: number; + info: { + weight: number; + }; +} +``` + +```tsx + + + accessor="name" header="Name" /> + accessor="price" header="Price" /> + {/* Nested paths are fully supported with autocomplete */} + accessor="info.weight" header="Weight" /> + {/* Error: Type '"invalidProp"' is not assignable to type 'name' | 'price' | 'info' | 'info.weight' */} + accessor="invalidProp" header="Invalid" /> + + +``` + +The accessor prop supports nested object paths (like `"info.weight"`) with full TypeScript autocomplete and type checking. + +## Conclusion + +That's the tour! You've seen how `@ctablex/table` works from the ground up: + +- **Step-by-step transformation**: How `` expands into headers, rows, and cells through default children +- **The micro-context magic**: Data and column definitions flow through context, so you're not passing props everywhere +- **Three data providing patterns**: Via `data` prop, content context, or dedicated data provider components +- **Customization levels**: From dropping in a custom content component to completely rebuilding the table structure +- **Type safety**: Generic types on `` give you autocomplete and type checking for nested paths + +The key insight? Because components use context instead of props, they can have proper default children that automatically work with your data. This means minimal code for simple cases, but you can drill down and customize at any level when you need to. + +Start simple, customize when needed. That's the ctablex way. Happy coding! 🚀