tinybase
Version:
A reactive data store and sync engine.
1,566 lines (1,547 loc) • 59.5 kB
text/typescript
/**
* The ui-react-dom module of the TinyBase project provides components to make
* it easy to create web-based reactive apps with Store objects.
*
* The components in this module use the react-dom module and so are not
* appropriate for environments like React Native (although those in the
* lower-level ui-react module are).
* @see UI Components demos
* @packageDocumentation
* @module ui-react-dom
* @since v4.1.0
*/
import type {Id, Ids} from '../common/index.d.cts';
import type {
CellProps,
ComponentReturnType,
ExtraProps,
IndexesOrIndexesId,
QueriesOrQueriesId,
RelationshipsOrRelationshipsId,
ResultCellProps,
StoreOrStoreId,
ValueProps,
} from '../ui-react/index.d.cts';
import type {ComponentType} from 'react';
/**
* The CustomCell object is used to configure custom cell rendering in an HTML
* table.
* @category Configuration
* @since v4.1.0
*/
export type CustomCell = {
/**
* An optional string that will be used as the label at the top of the table
* column for this Cell.
* @category Prop
* @since v4.1.0
*/
label?: string;
/**
* An optional custom component for rendering each Cell in the Table (to
* override the default CellView component).
* @category Prop
* @since v4.1.0
*/
component?: ComponentType<CellProps>;
/**
* An optional function for generating extra props for each custom Cell
* component based on Row and Cell Id.
* @category Prop
* @since v4.1.0
*/
getComponentProps?: (rowId: Id, cellId: Id) => ExtraProps;
};
/**
* The CustomResultCell object is used to configure custom cell rendering for
* query results in an HTML table.
* @category Configuration
* @since v4.1.0
*/
export type CustomResultCell = {
/**
* An optional string that will be used as the label at the top of the table
* column for this Cell.
* @category Prop
* @since v4.1.0
*/
label?: string;
/**
* An optional custom component for rendering each Cell in the ResultTable (to
* override the default ResultCellView component).
* @category Prop
* @since v4.1.0
*/
component?: ComponentType<ResultCellProps>;
/**
* An optional function for generating extra props for each custom Cell
* component based on Row and Cell Id.
* @category Prop
* @since v4.1.0
*/
getComponentProps?: (rowId: Id, cellId: Id) => ExtraProps;
};
/**
* HtmlTableProps props are used for components that will render in an HTML
* table, such as the TableInHtmlTable component or SortedTableInHtmlTable
* component.
* @category Props
* @since v4.1.0
*/
export type HtmlTableProps = {
/**
* A string className to use on the root of the resulting element.
* @category Prop
* @since v4.1.0
*/
readonly className?: string;
/**
* Whether a header row should be rendered at the top of the table, defaulting
* to `true`.
* @category Prop
* @since v4.1.0
*/
readonly headerRow?: boolean;
/**
* Whether an Id column should be rendered on the left of the table,
* defaulting to `true`.
* @category Prop
* @since v4.1.0
*/
readonly idColumn?: boolean;
};
/**
* TableInHtmlTableProps props are used for components that will render a Table
* in an HTML table, such as the TableInHtmlTable component.
* @category Props
* @since v4.1.0
*/
export type TableInHtmlTableProps = {
/**
* The Id of the Table in the Store to be rendered.
* @category Prop
* @since v4.1.0
*/
readonly tableId: Id;
/**
* The Store to be accessed: omit for the default context Store, provide an Id
* for a named context Store, or provide an explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly store?: StoreOrStoreId;
/**
* Whether the Cells should be editable. This affects the default CellView
* component (to use the EditableCellView component instead) but of course
* will not affect custom Cell components if you have set them.
* @category Prop
* @since v4.1.0
*/
readonly editable?: boolean;
/**
* An optional list of Cell Ids to use for rendering a prescribed set of the
* Table's Cells in a given order. This can also be an object with the desired
* Cell Ids as keys, and with a value that can either be a string label to
* show in the column header, or a CustomCell object to further configure the
* column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomCell};
};
/**
* SortedTableInHtmlTableProps props are used for components that will render a
* sorted Table in an HTML table, such as the SortedTableInHtmlTable component.
* @category Props
* @since v4.1.0
*/
export type SortedTableInHtmlTableProps = {
/**
* The Id of the Table in the Store to be rendered.
* @category Prop
* @since v4.1.0
*/
readonly tableId: Id;
/**
* The Id of the Cell whose values are used for the sorting. If omitted, the
* view will sort the Row Id itself.
* @category Prop
* @since v4.1.0
*/
readonly cellId?: Id;
/**
* Whether the sorting should be in descending order.
* @category Prop
* @since v4.1.0
*/
readonly descending?: boolean;
/**
* The number of Row Ids to skip for pagination purposes.
* @category Prop
* @since v4.1.0
*/
readonly offset?: number;
/**
* The maximum number of Row Ids to return.
* @category Prop
* @since v4.1.0
*/
readonly limit?: number;
/**
* The Store to be accessed: omit for the default context Store, provide an Id
* for a named context Store, or provide an explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly store?: StoreOrStoreId;
/**
* Whether the Cells should be editable. This affects the default CellView
* component (to use the EditableCellView component instead) but of course
* will not affect custom Cell components if you have set them.
* @category Prop
* @since v4.1.0
*/
readonly editable?: boolean;
/**
* An optional list of Cell Ids to use for rendering a prescribed set of the
* Table's Cells in a given order. This can also be an object with the desired
* Cell Ids as keys, and with a value that can either be a string label to
* show in the column header, or a CustomCell object to further configure the
* column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomCell};
/**
* Whether the table should be interactive such that clicking a header changes
* the sorting and/or direction.
* @category Prop
* @since v4.1.0
*/
readonly sortOnClick?: boolean;
/**
* Either `true` to show the default SortedTablePaginator for the Table, or
* provide your own paginator component that takes SortedTablePaginatorProps.
* @category Prop
* @since v4.1.0
*/
readonly paginator?: boolean | ComponentType<SortedTablePaginatorProps>;
/**
* A function that is called whenever the sorting or pagination of the Table
* is changed by the user, invoked with the sorted Cell Id, whether descending
* or not, and the offset of the pagination.
* @category Prop
* @since v4.1.0
*/
readonly onChange?: (
sortAndOffset: [
cellId: Id | undefined,
descending: boolean,
offset: number,
],
) => void;
};
/**
* ValuesInHtmlTableProps props are used for components that will render Values
* in an HTML table, such as the ValuesInHtmlTable component.
* @category Props
* @since v4.1.0
*/
export type ValuesInHtmlTableProps = {
/**
* The Store to be accessed: omit for the default context Store, provide an Id
* for a named context Store, or provide an explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly store?: StoreOrStoreId;
/**
* Whether the Values should be editable. This affects the default ValueView
* component (to use the EditableValueView component instead) but of course
* will not affect a custom valueComponent if you have set one.
* @category Prop
* @since v4.1.0
*/
readonly editable?: boolean;
/**
* A custom component for rendering each Value in the Store (to override the
* default ValueView component).
* @category Prop
* @since v4.1.0
*/
readonly valueComponent?: ComponentType<ValueProps>;
/**
* A function for generating extra props for each custom Value component based
* on its Id.
* @category Prop
* @since v4.1.0
*/
readonly getValueComponentProps?: (valueId: Id) => ExtraProps;
};
/**
* SliceInHtmlTableProps props are used for components that will render an Index
* Slice in an HTML table, such as the SliceInHtmlTable component.
* @category Props
* @since v4.1.0
*/
export type SliceInHtmlTableProps = {
/**
* The Id of the Index in the Indexes object.
* @category Prop
* @since v4.1.0
*/
readonly indexId: Id;
/**
* The Id of the Slice in the Index to be rendered.
* @category Prop
* @since v4.1.0
*/
readonly sliceId: Id;
/**
* The Indexes object to be accessed: omit for the default context Indexes
* object, provide an Id for a named context Indexes object, or provide an
* explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly indexes?: IndexesOrIndexesId;
/**
* Whether the Cells should be editable. This affects the default CellView
* component (to use the EditableCellView component instead) but of course
* will not affect custom Cell components if you have set them.
* @category Prop
* @since v4.1.0
*/
readonly editable?: boolean;
/**
* An optional list of Cell Ids to use for rendering a prescribed set of the
* Slice's Cells in a given order. This can also be an object with the desired
* Cell Ids as keys, and with a value that can either be a string label to
* show in the column header, or a CustomCell object to further configure the
* column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomCell};
};
/**
* RelationshipInHtmlTableProps props are used for components that will render
* the contents of the two Tables linked by a Relationship as an HTML table,
* such as the RelationshipInHtmlTable component.
*
* Note the use of dotted 'tableId.cellId' string pairs when specifying custom
* rendering for the cells in this table, since Cells from both the
* relationship's 'local' and 'remote' Table objects can be rendered and need to
* be distinguished.
* @category Props
* @since v4.1.0
*/
export type RelationshipInHtmlTableProps = {
/**
* The Id of the relationship in the Relationships object for which the
* relationship Table Rows will be rendered.
* @category Prop
* @since v4.1.0
*/
readonly relationshipId: Id;
/**
* The Relationships object to be accessed: omit for the default context
* Relationships object, provide an Id for a named context Relationships
* object, or provide an explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly relationships?: RelationshipsOrRelationshipsId;
/**
* Whether the Cells should be editable. This affects the default CellView
* component (to use the EditableCellView component instead) but of course
* will not affect custom Cell components if you have set them.
* @category Prop
* @since v4.1.0
*/
readonly editable?: boolean;
/**
* An optional list of dotted 'tableId.cellId' string pairs to use for
* rendering a prescribed set of the relationship Tables' Cells in a given
* order. This can also be an object with the desired 'tableId.cellId' string
* pairs as keys, and with a value that can either be a string label to show
* in the column header, or a CustomCell object to further configure the
* column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomCell};
};
/**
* ResultTableInHtmlTableProps props are used for components that will render a
* ResultTable in an HTML table, such as the ResultTableInHtmlTable component.
* @category Props
* @since v4.1.0
*/
export type ResultTableInHtmlTableProps = {
/**
* The Id of the query in the Queries object for which the ResultTable will be
* rendered.
* @category Prop
* @since v4.1.0
*/
readonly queryId: Id;
/**
* The Queries object to be accessed: omit for the default context Queries
* object, provide an Id for a named context Queries object, or provide an
* explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly queries?: QueriesOrQueriesId;
/**
* An optional list of Cell Ids to use for rendering a prescribed set of the
* ResultTable's Cells in a given order. This can also be an object with the
* desired Cell Ids as keys, and with a value that can either be a string
* label to show in the column header, or a ResultCustomCell object to further
* configure the column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomResultCell};
};
/**
* ResultSortedTableInHtmlTableProps props are used for components that will
* render a sorted Table in an HTML table, such as the SortedTableInHtmlTable
* component.
* @category Props
* @since v4.1.0
*/
export type ResultSortedTableInHtmlTableProps = {
/**
* The Id of the query in the Queries object for which the ResultTable will be
* rendered.
* @category Prop
* @since v4.1.0
*/
readonly queryId: Id;
/**
* The Id of the Cell whose values are used for the sorting. If omitted, the
* view will sort the Row Id itself.
* @category Prop
* @since v4.1.0
*/
readonly cellId?: Id;
/**
* Whether the sorting should be in descending order.
* @category Prop
* @since v4.1.0
*/
readonly descending?: boolean;
/**
* The number of Row Ids to skip for pagination purposes.
* @category Prop
* @since v4.1.0
*/
readonly offset?: number;
/**
* The maximum number of Row Ids to return.
* @category Prop
* @since v4.1.0
*/
readonly limit?: number;
/**
* The Queries object to be accessed: omit for the default context Queries
* object, provide an Id for a named context Queries object, or provide an
* explicit reference.
* @category Prop
* @since v4.1.0
*/
readonly queries?: QueriesOrQueriesId;
/**
* An optional list of Cell Ids to use for rendering a prescribed set of the
* ResultTable's Cells in a given order. This can also be an object with the
* desired Cell Ids as keys, and with a value that can either be a string
* label to show in the column header, or a ResultCustomCell object to further
* configure the column.
* @category Prop
* @since v4.1.0
*/
readonly customCells?: Ids | {[cellId: Id]: string | CustomResultCell};
/**
* Whether the table should be interactive such that clicking a header changes
* the sorting and/or direction.
* @category Prop
* @since v4.1.0
*/
readonly sortOnClick?: boolean;
/**
* Either `true` to show the default SortedTablePaginator for the ResultTable,
* or provide your own paginator component that takes
* SortedTablePaginatorProps.
* @category Prop
* @since v4.1.0
*/
readonly paginator?: boolean | ComponentType<SortedTablePaginatorProps>;
/**
* A function that is called whenever the sorting or pagination of the
* ResultTable is changed by the user, invoked with the sorted Cell Id,
* whether descending or not, and the offset of the pagination.
* @category Prop
* @since v4.1.0
*/
readonly onChange?: (
sortAndOffset: [
cellId: Id | undefined,
descending: boolean,
offset: number,
],
) => void;
};
/**
* SortedTablePaginatorProps props are used for components that will be used as
* a table paginator, such as the SortedTablePaginator component.
* @category Props
* @since v4.1.0
*/
export type SortedTablePaginatorProps = {
/**
* An event that will fire when the offset is updated, called with the new
* offset.
* @category Prop
* @since v4.1.0
*/
readonly onChange: (offset: number) => void;
/**
* The number of Row Ids to skip for pagination.
* @category Prop
* @since v4.1.0
*/
readonly offset?: number;
/**
* The maximum number of Row Ids being returned.
* @category Prop
* @since v4.1.0
*/
readonly limit?: number;
/**
* The total number of Row Ids in the paginated table.
* @category Prop
* @since v4.1.0
*/
readonly total: number;
/**
* A noun to use in the pagination label for a single row, defaulting to
* 'row'.
* @category Prop
* @since v4.1.0
*/
readonly singular?: string;
/**
* A noun to use in the pagination label for multiple rows, defaulting to the
* value of the singular noun suffixed with the letter 's'.
* @category Prop
* @since v4.1.0
*/
readonly plural?: string;
};
/**
* The TableInHtmlTable component renders the contents of a single Table in a
* Store as an HTML <table> element, and registers a listener so that any
* changes to that result will cause a re-render.
*
* See the <TableInHtmlTable /> demo for this component in action.
*
* The component's props identify which Table to render based on Table Id, and
* Store (which is either the default context Store, a named context Store, or
* by explicit reference).
*
* This component renders a Table by iterating over its Row objects. By default
* the Cells are in turn rendered with the CellView component, but you can
* override this behavior by providing a `component` for each Cell in the
* `customCells` prop. You can pass additional props to that custom component
* with the `getComponentProps` callback. See the CustomCell type for more
* details.
*
* This component uses the useRowIds hook under the covers, which means that any
* changes to the structure of the Table will cause a re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether the Ids
* appear in a <th> element at the top of the table, and the start of each row.
* @param props The props for this component.
* @returns A rendering of the Table in a <table> element.
* @example
* This example creates a Provider context into which a default Store is
* provided. The TableInHtmlTable component within it then renders the Table in
* a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {TableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
* const Pane = () => <TableInHtmlTable tableId="pets" className="table" />;
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* });
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="table">
* <thead>
* <tr>
* <th>Id</th>
* <th>species</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>fido</th>
* <td>dog</td>
* </tr>
* <tr>
* <th>felix</th>
* <td>cat</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Store is
* provided. The TableInHtmlTable component within it then renders the Table
* with a custom component and a custom props callback for the `species` Cell.
* The header row at the top of the table and the Id column at the start of each
* row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {CellView, Provider} from 'tinybase/ui-react';
* import {TableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <TableInHtmlTable
* tableId="pets"
* customCells={customCells}
* headerRow={false}
* idColumn={false}
* />
* );
*
* const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
* <>
* {bold ? <b>{rowId}</b> : rowId}:
* <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
* </>
* );
* const customCells = {
* species: {
* component: FormattedCellView,
* getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
* },
* };
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* });
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr>
* <td><b>fido</b>:dog</td>
* </tr>
* <tr>
* <td>felix:cat</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @category Store components
* @since v4.1.0
*/
export function TableInHtmlTable(
props: TableInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The SortedTableInHtmlTable component renders the contents of a single sorted
* Table in a Store, as an HTML <table> element, and registers a listener so
* that any changes to that result will cause a re-render.
*
* See the <SortedTableInHtmlTable /> demo for this component in action.
*
* The component's props identify which Table to render based on Table Id, and
* Store (which is either the default context Store, a named context Store, or
* by explicit reference). It also takes a Cell Id to sort by and a boolean to
* indicate that the sorting should be in descending order. The `offset` and
* `limit` props are used to paginate results, but default to `0` and
* `undefined` to return all available Row Ids if not specified.
*
* This component renders a ResultTable by iterating over its Row objects, in
* the order dictated by the sort parameters. By default the Cells are in turn
* rendered with the CellView component, but you can override this behavior by
* providing a `component` for each Cell in the `customCells` prop. You can pass
* additional props to that custom component with the `getComponentProps`
* callback. See the CustomCell type for more details.
*
* This component uses the useSortedRowIds hook under the covers, which means
* that any changes to the structure or sorting of the Table will cause a
* re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether the Ids
* appear in a <th> element at the top of the table, and the start of each row.
*
* The `sortOnClick` prop makes the table's sorting interactive such that the
* user can click on a column heading to sort by that column. The style classes
* `sorted` and `ascending` (or `descending`) are added so that you can provide
* hints to the user how the sorting is being applied.
*
* Provide a paginator component for the Table with the `paginator` prop. Set to
* `true` to use the default SortedTablePaginator, or provide your own component
* that accepts SortedTablePaginatorProps.
*
* Finally, the `onChange` prop lets you listen to a user's changes to the
* Table's sorting or pagination.
* @param props The props for this component.
* @returns A rendering of the Table in a <table> element.
* @example
* This example creates a Provider context into which a default Store is
* provided. The SortedTableInHtmlTable component within it then renders the
* Table in a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {SortedTableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <SortedTableInHtmlTable
* tableId="pets"
* cellId="species"
* className="table"
* />
* );
*
* const store = createStore().setTables({
* pets: {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* },
* });
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="table">
* <thead>
* <tr>
* <th>Id</th>
* <th class="sorted ascending">↑ species</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>felix</th>
* <td>cat</td>
* </tr>
* <tr>
* <th>fido</th>
* <td>dog</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Store is
* provided. The SortedTableInHtmlTable component within it then renders the
* Table with a custom component and a custom props callback for the `species`
* Cell. The header row at the top of the table and the Id column at the start
* of each row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {CellView, Provider} from 'tinybase/ui-react';
* import {SortedTableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
*
* const Pane = () => (
* <SortedTableInHtmlTable
* tableId="pets"
* cellId="species"
* customCells={customCells}
* headerRow={false}
* idColumn={false}
* />
* );
*
* const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
* <>
* {bold ? <b>{rowId}</b> : rowId}:
* <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
* </>
* );
* const customCells = {
* species: {
* component: FormattedCellView,
* getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
* },
* };
*
* const store = createStore().setTables({
* pets: {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* },
* });
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr>
* <td>felix:cat</td>
* </tr>
* <tr>
* <td><b>fido</b>:dog</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @category Store components
* @since v4.1.0
*/
export function SortedTableInHtmlTable(
props: SortedTableInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The ValuesInHtmlTable component renders the keyed value contents of a Store
* as an HTML <table> element, and registers a listener so that any changes to
* that result will cause a re-render.
*
* See the <ValuesInHtmlTable /> demo for this component in action.
*
* The component's props identify which Row to render based on Table Id, Row Id,
* and Store (which is either the default context Store, a named context Store,
* or an explicit reference).
*
* This component renders a Store by iterating over its Value objects. By
* default the Values are in turn rendered with the ValueView component, but you
* can override this behavior by providing a `valueComponent` prop, a custom
* component of your own that will render a Value based on ValueProps. You can
* also pass additional props to your custom component with the
* `getValueComponentProps` callback prop.
*
* This component uses the useValueIds hook under the covers, which means that
* any changes to the structure of the Values in the Store will cause a
* re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether labels
* and Ids appear in a <th> element at the top of the table, and the start of
* each row.
* @param props The props for this component.
* @returns A rendering of the Values in a <table> element.
* @example
* This example creates a Provider context into which a default Store is
* provided. The ValuesInHtmlTable component within it then renders the Values
* in a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {ValuesInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
* const Pane = () => <ValuesInHtmlTable className="values" />;
*
* const store = createStore().setValues({open: true, employees: 3});
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="values">
* <thead>
* <tr>
* <th>Id</th>
* <th>Value</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>open</th>
* <td>true</td>
* </tr>
* <tr>
* <th>employees</th>
* <td>3</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Store is
* provided. The ValuesInHtmlTable component within it then renders the Row
* with a custom Cell component and a custom props callback. The header row at
* the top of the table and the Id column at the start of each row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createStore} from 'tinybase';
* import {Provider, ValueView} from 'tinybase/ui-react';
* import {ValuesInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({store}) => (
* <Provider store={store}>
* <Pane />
* </Provider>
* );
* const getBoldProp = (valueId) => ({bold: valueId == 'open'});
* const Pane = () => (
* <ValuesInHtmlTable
* valueComponent={FormattedValueView}
* getValueComponentProps={getBoldProp}
* headerRow={false}
* idColumn={false}
* />
* );
* const FormattedValueView = ({valueId, bold}) => (
* <>
* {bold ? <b>{valueId}</b> : valueId}
* {': '}
* <ValueView valueId={valueId} />
* </>
* );
*
* const store = createStore().setValues({open: true, employees: 3});
* const app = document.createElement('div');
* createRoot(app).render(<App store={store} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr><td><b>open</b>: true</td></tr>
* <tr><td>employees: 3</td></tr>
* </tbody>
* </table>
* `;
* ```
* @category Store components
* @since v4.1.0
*/
export function ValuesInHtmlTable(
props: ValuesInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The SliceInHtmlTable component renders the contents of a Slice as an HTML
* <table> element, and registers a listener so that any changes to that result
* will cause a re-render.
*
* See the <SliceInHtmlTable /> demo for this component in action.
*
* The component's props identify which Slice to render based on Index Id, Slice
* Id, and Indexes object (which is either the default context Indexes object, a
* named context Indexes object, or an explicit reference).
*
* This component renders a Slice by iterating over its Row objects. By default
* the Cells are in turn rendered with the CellView component, but you can
* override this behavior by providing a `component` for each Cell in the
* `customCells` prop. You can pass additional props to that custom component
* with the `getComponentProps` callback. See the CustomCell type for more
* details.
*
* This component uses the useSliceRowIds hook under the covers, which means
* that any changes to the structure of the Slice will cause a re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether labels
* and Ids appear in a <th> element at the top of the table, and the start of
* each row.
* @param props The props for this component.
* @returns A rendering of the Slice in a <table> element.
* @example
* This example creates a Provider context into which a default Indexes object
* is provided. The SliceInHtmlTable component within it then renders the Slice
* in a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createIndexes, createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {SliceInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({indexes}) => (
* <Provider indexes={indexes}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <SliceInHtmlTable indexId="bySpecies" sliceId="dog" className="slice" />
* );
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* const app = document.createElement('div');
* createRoot(app).render(<App indexes={indexes} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="slice">
* <thead>
* <tr>
* <th>Id</th>
* <th>species</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>fido</th>
* <td>dog</td>
* </tr>
* <tr>
* <th>cujo</th>
* <td>dog</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Indexes object
* is provided. The SliceInHtmlTable component within it then renders the Slice
* with a custom component and a custom props callback for the `species` Cell.
* The header row at the top of the table and the Id column at the start of each
* row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createIndexes, createStore} from 'tinybase';
* import {CellView, Provider} from 'tinybase/ui-react';
* import {SliceInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({indexes}) => (
* <Provider indexes={indexes}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <SliceInHtmlTable
* indexId="bySpecies"
* sliceId="dog"
* customCells={customCells}
* headerRow={false}
* idColumn={false}
* />
* );
*
* const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
* <>
* {bold ? <b>{rowId}</b> : rowId}:
* <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
* </>
* );
* const customCells = {
* species: {
* component: FormattedCellView,
* getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
* },
* };
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* const app = document.createElement('div');
* createRoot(app).render(<App indexes={indexes} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr>
* <td><b>fido</b>:</td>
* </tr>
* <tr>
* <td>cujo:</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @category Indexes components
* @since v4.1.0
*/
export function SliceInHtmlTable(
props: SliceInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The RelationshipInHtmlTable component renders the contents of the two Tables
* linked by a Relationship as an HTML <table> element, and registers a listener
* so that any changes to that result will cause a re-render.
*
* See the <RelationshipInHtmlTable /> demo for this component in action.
*
* The component's props identify which Relationship to render based on
* Relationship Id and Relationships object (which is either the default context
* Relationships object, a named context Relationships object, or an explicit
* reference).
*
* This component renders the two Table objects by iterating over their related
* Row objects. By default the Cells are in turn rendered with the CellView
* component, but you can override this behavior by providing a `component` for
* each Cell in the `customCells` prop. You can pass additional props to that
* custom component with the `getComponentProps` callback. See the CustomCell
* type for more details.
*
* Note the use of dotted 'tableId.cellId' string pairs when specifying custom
* rendering for the cells in this table, since Cells from both the
* relationship's 'local' and 'remote' Table objects can be rendered and need to
* be distinguished.
*
* This component uses the useRowIds and useRemoteRowId hooks under the covers,
* which means that any changes to the structure of either Table resulting in a
* change to the relationship will cause a re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether labels
* and Ids appear in a <th> element at the top of the table, and the start of
* each row.
* @param props The props for this component.
* @returns A rendering of the two Tables linked by a Relationship in a
* <table> element.
* @example
* This example creates a Provider context into which a default Relationships
* object is provided. The RelationshipInHtmlTable component within it then
* renders the two Tables linked by a relationship in a <table> element with a
* CSS class. Note the dotted pairs that are used as column headings.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createRelationships, createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {RelationshipInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({relationships}) => (
* <Provider relationships={relationships}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <RelationshipInHtmlTable
* relationshipId="petSpecies"
* className="relationship"
* />
* );
*
* const relationships = createRelationships(
* createStore()
* .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
* .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
* ).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');
*
* const app = document.createElement('div');
* const root = createRoot(app);
* root.render(<App relationships={relationships} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="relationship">
* <thead>
* <tr>
* <th>pets.Id</th>
* <th>species.Id</th>
* <th>pets.species</th>
* <th>species.price</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>fido</th>
* <th>dog</th>
* <td>dog</td>
* <td>5</td>
* </tr>
* <tr>
* <th>cujo</th>
* <th>dog</th>
* <td>dog</td>
* <td>5</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Relationships
* object is provided. The RelationshipInHtmlTable component within it then
* renders the two Tables linked by a relationship with a custom component and a
* custom props callback for the `species` Cell. The header row at the top of
* the table and the Id column at the start of each row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createRelationships, createStore} from 'tinybase';
* import {CellView, Provider} from 'tinybase/ui-react';
* import {RelationshipInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({relationships}) => (
* <Provider relationships={relationships}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <RelationshipInHtmlTable
* relationshipId="petSpecies"
* customCells={customCells}
* idColumn={false}
* headerRow={false}
* />
* );
*
* const FormattedCellView = ({tableId, rowId, cellId, store, bold}) => (
* <>
* {bold ? <b>{rowId}</b> : rowId}:
* <CellView
* tableId={tableId}
* rowId={rowId}
* cellId={cellId}
* store={store}
* />
* </>
* );
* const customCells = {
* 'species.price': {
* component: FormattedCellView,
* getComponentProps: (rowId) => ({bold: rowId == 'dog'}),
* },
* };
*
* const relationships = createRelationships(
* createStore()
* .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'wolf'}})
* .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
* ).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');
*
* const app = document.createElement('div');
* const root = createRoot(app);
* root.render(<App relationships={relationships} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr>
* <td><b>dog</b>:5</td>
* </tr>
* <tr>
* <td>wolf:10</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @category Relationships components
* @since v4.1.0
*/
export function RelationshipInHtmlTable(
props: RelationshipInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The ResultTableInHtmlTable component renders the contents of a single query's
* ResultTable in a Queries object as an HTML <table> element, and registers a
* listener so that any changes to that result will cause a re-render.
*
* See the <ResultTableInHtmlTable /> demo for this component in action.
*
* The component's props identify which ResultTable to render based on query Id,
* and Queries object (which is either the default context Queries object, a
* named context Queries object, or by explicit reference).
*
* This component renders a ResultTable by iterating over its Row objects. By
* default the Cells are in turn rendered with the CellView component, but you
* can override this behavior by providing a `component` for each Cell in the
* `customCells` prop. You can pass additional props to that custom component
* with the `getComponentProps` callback. See the ResultCustomCell type for more
* details.
*
* This component uses the useRowIds hook under the covers, which means that any
* changes to the structure of the Table will cause a re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether the Ids
* appear in a <th> element at the top of the table, and the start of each row.
* @param props The props for this component.
* @returns A rendering of the ResultTable in a <table> element.
* @example
* This example creates a Provider context into which a default Queries object
* is provided. The ResultTableInHtmlTable component within it then renders the
* ResultTable in a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createQueries, createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {ResultTableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({queries}) => (
* <Provider queries={queries}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <ResultTableInHtmlTable queryId="petColors" className="table" />
* );
*
* const queries = createQueries(
* createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* }),
* ).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
* const app = document.createElement('div');
* createRoot(app).render(<App queries={queries} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="table">
* <thead>
* <tr>
* <th>Id</th>
* <th>color</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>fido</th>
* <td>brown</td>
* </tr>
* <tr>
* <th>felix</th>
* <td>black</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @example
* This example creates a Provider context into which a default Queries object
* is provided. The ResultTableInHtmlTable component within it then renders the
* ResultTable with a custom component and a custom props callback for the
* `color` Cell. The header row at the top of the table and the Id column at
* the start of each row is removed.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createQueries, createStore} from 'tinybase';
* import {Provider, ResultCellView} from 'tinybase/ui-react';
* import {ResultTableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({queries}) => (
* <Provider queries={queries}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <ResultTableInHtmlTable
* queryId="petColors"
* customCells={customCells}
* headerRow={false}
* idColumn={false}
* />
* );
*
* const FormattedResultCellView = ({queryId, rowId, cellId, bold}) => (
* <>
* {bold ? <b>{rowId}</b> : rowId}:
* <ResultCellView queryId={queryId} rowId={rowId} cellId={cellId} />
* </>
* );
* const customCells = {
* color: {
* component: FormattedResultCellView,
* getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
* },
* };
*
* const queries = createQueries(
* createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* }),
* ).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
* const app = document.createElement('div');
* createRoot(app).render(<App queries={queries} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table>
* <tbody>
* <tr>
* <td><b>fido</b>:brown</td>
* </tr>
* <tr>
* <td>felix:black</td>
* </tr>
* </tbody>
* </table>
* `;
* ```
* @category Queries components
* @since v4.1.0
*/
export function ResultTableInHtmlTable(
props: ResultTableInHtmlTableProps & HtmlTableProps,
): ComponentReturnType;
/**
* The SortedTableInHtmlTable component renders the contents of a single query's
* sorted ResultTable in a Queries object as an HTML <table> element, and
* registers a listener so that any changes to that result will cause a
* re-render.
*
* See the <ResultSortedTableInHtmlTable /> demo for this component in action.
*
* The component's props identify which ResultTable to render based on query Id,
* and Queries object (which is either the default context Queries object, a
* named context Queries object, or by explicit reference). It also takes a Cell
* Id to sort by and a boolean to indicate that the sorting should be in
* descending order. The `offset` and `limit` props are used to paginate
* results, but default to `0` and `undefined` to return all available Row Ids
* if not specified.
*
* This component renders a ResultTable by iterating over its Row objects, in
* the order dictated by the sort parameters. By default the Cells are in turn
* rendered with the CellView component, but you can override this behavior by
* providing a `component` for each Cell in the `customCells` prop. You can pass
* additional props to that custom component with the `getComponentProps`
* callback. See the ResultCustomCell type for more details.
*
* This component uses the useSortedRowIds hook under the covers, which means
* that any changes to the structure or sorting of the ResultTable will cause a
* re-render.
*
* You can use the `headerRow` and `idColumn` props to control whether the Ids
* appear in a <th> element at the top of the table, and the start of each row.
*
* The `sortOnClick` prop makes the table's sorting interactive such that the
* user can click on a column heading to sort by that column. The style classes
* `sorted` and `ascending` (or `descending`) are added so that you can provide
* hints to the user how the sorting is being applied.
*
* Provide a paginator component for the ResultTable with the `paginator` prop.
* Set to `true` to use the default SortedTablePaginator, or provide your own
* component that accepts SortedTablePaginatorProps.
*
* Finally, the `onChange` prop lets you listen to a user's changes to the
* ResultTable's sorting or pagination.
* @param props The props for this component.
* @returns A rendering of the ResultTable in a <table> element.
* @example
* This example creates a Provider context into which a default Queries object
* is provided. The ResultSortedTableInHtmlTable component within it then
* renders the ResultTable in a <table> element with a CSS class.
*
* ```jsx
* import React from 'react';
* import {createRoot} from 'react-dom/client';
* import {createQueries, createStore} from 'tinybase';
* import {Provider} from 'tinybase/ui-react';
* import {ResultSortedTableInHtmlTable} from 'tinybase/ui-react-dom';
*
* const App = ({queries}) => (
* <Provider queries={queries}>
* <Pane />
* </Provider>
* );
* const Pane = () => (
* <ResultSortedTableInHtmlTable
* queryId="petColors"
* cellId="color"
* className="table"
* />
* );
*
* const queries = createQueries(
* createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* }),
* ).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
* const app = document.createElement('div');
* createRoot(app).render(<App queries={queries} />); // !act
* console.log(app.innerHTML);
* // ->
* `
* <table class="table">
* <thead>
* <tr>
* <th>Id</th>
* <th class="sorted ascending">↑ color</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th>felix</th>
* <td>black</td>
* </tr>
* <tr>
* <th>fido</th>
* <td>brown</td>
* </tr>