@teppen/react-ui
v2.17.0
Published
React UI Component
Maintainers
Readme
React-UI
This is a React component library that provides some useful UI elements for your web applications.
Components
This library includes the following components:
- Table: A simple Table. (1.0.0 ~) (※ Not compatible with 0.*.*)
- FileUploader: A drop area and file picker with file type and size checks.
Installation
You can install this library using npm or yarn.
npm i @teppen/react-uiThis library has the following peer dependencies:
- react (18.2 or later, 19 supported)
- react-dom (18.2 or later, 19 supported)
Tailwind CSS Installation and Setup
Please follow the installation procedure described in the following tailwindcss website.
- URL: https://tailwindcss.com/docs/installation/framework-guides
This library works with both Tailwind CSS v4 and v3. Tell Tailwind to scan the library's files so that the classes it uses are generated.
Tailwind CSS v4
Add an @source for the library to the CSS file that imports Tailwind CSS. The path is relative to that CSS file.
@import "tailwindcss";
@source "../node_modules/@teppen/react-ui/dist";Tailwind CSS v3
Add "./node_modules/@teppen/react-ui/dist/**/*.js" to content in tailwind.config.js.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"<The path where you are placing the component files>",
"./node_modules/@teppen/react-ui/dist/**/*.js",
],
theme: {
extend: {},
},
plugins: [],
};Usage
To use this library, you need to import the components you want to use and render them in your React application.
For example, to use the Table component, you can do something like this:
import React from "react";
import { Table } from "@teppen/react-ui";
const App = () => {
return (
<div>
<Table
rows={[
{
id: 1,
name: "Taro",
age: 30,
role: "",
button: "Click",
},
{
id: 2,
name: "Yoshiko",
age: 60,
role: "1",
button: "Click",
},
{
id: 3,
name: "Koki",
age: 13,
role: "2",
button: "Click",
},
{
id: 4,
name: "Chisato",
age: 34,
role: "2",
button: "Click",
},
]}
cols={[
{ key: "id", label: "ID", type: "number" },
{
key: "name",
label: "名前",
type: "string",
editable: true,
constraints: {
maxLength: 10,
minLength: 1,
pattern: "^[a-zA-Z0-9]+$",
},
onCellBlur: (key, value, current, completeEditing) => {
console.log(key, value, current);
completeEditing();
},
},
{ key: "age", label: "年齢", type: "number" },
{
key: "role",
label: "役割",
type: "select",
editable: true,
options: [
{ value: "1", label: "管理者" },
{ value: "2", label: "オペレーター" },
],
allowEmpty: true,
onCellBlur: (key, value, current, completeEditing) => {
console.log(key, value, current);
completeEditing();
},
},
{
key: "button",
label: "ボタン",
disableSort: true,
disableFilter: true,
type: "string",
render: (value, row) => (
<button
className="px-2 py-1 text-white bg-blue-500 rounded-md"
onClick={(e) => {
e.preventDefault();
alert(`Clicked button: (id: ${row.id})`);
}}
>
{value}
</button>
),
},
]}
initialCondition={{
sort: {
key: "id",
asc: true,
},
pagination: {
rowCountPerPage: 50,
},
}}
onClickRow={(row) => {
alert(`Clicked row: (id: ${row.id})`);
}}
onUpdateRow={(newRow, oldRow) => {
alert({ newRow, oldRow });
}}
applyRowFormatting={(row) => {
if (row.role === "1") {
return "bg-red-600 text-white";
} else {
return "";
}
}}
checkbox={{
checked: (row) => row.id % 2 == 1,
onChecked: (row) => console.log("checked", row.id),
onUnchecked: (row) => console.log("unchecked", row.id),
}}
conditionalFormattings={[
{
condition: (row) => row.age < 31,
key: "age",
style: "text-red-500 font-bold text-lg bg-red-100",
},
{
condition: (row) => row.role === "2",
key: "role",
style:
"text-blue-500 font-bold text-lg shadow-inner shadow-blue-500",
},
{
condition: (row) => row.age < 35,
key: "age",
style: "text-green-500 font-bold text-lg bg-green-100",
},
]}
/>
</div>
);
};
export default App;Table Component
A component that displays a table based on the row and column information passed as arguments. The table component has the following features:
- Pagination function
- Sorting function (
disableSortturns it off for a column; columns withrenderneeddisableSort: false, see below). Click a sort button to sort by that column; Shift+click to sort by several columns (see "Sorting by several columns") - Filtering function (only when
labelis set in the column information;disableFilterturns it off for a column; columns withrenderneeddisableFilter: false)
Sorting compares numbers and dates by value and text in Japanese order ("2" before "10", hiragana and katakana together in kana order). Select columns are sorted by their labels. Empty values stay at the end in both orders.
Type a word in the search bar and press Enter to search every column for it: the first option is "「営業」をすべての列から探す" / "Search all columns for …" (it looks in what each column shows, e.g. option labels). To filter one column, choose it instead (typing its name puts it first; in the list the first option is chosen, so Enter alone picks it). A panel opens under the search bar to set the condition and its value at once:
| Column | Conditions | Value | |---|---|---| | Text | を含む / を含まない / と同じ / と違う / 空欄 / 空欄でない | text | | Number | ≥ / ≤ / = / ≠ / > / < | a number (full-width digits and "1,000" are fine) | | Date / date and time | 以降 / 以前 / の日 / 以外の日 / より後 / より前 / 空欄 | a date (and time) | | Select | が / 以外 / 空欄 / 空欄でない | check one or more options |
Enter (or 追加) adds it; Esc closes the panel. Conditions read as words, such as "部署 が 営業部・開発部 のどれか", "年齢 ≥ 30" or "氏名 に「佐」を含む". Click one to change it in the same panel (変更 / 外す); × removes it, and "すべて解除" removes them all. With two or more conditions, "すべて満たす ⇄" switches to "どれか満たす" (AND / OR).
While the table is being looked at, the conditions take one line, and those that do not fit are folded into "ほか N 件", so the table keeps its height. Clicking the search bar, a condition or "ほか N 件" shows them all. On narrow screens the folded line shows only conditions (the first one shortened with "…" if needed); tapping it opens the search bar.
The like / not like filters ignore differences between upper and lower case, full-width and half-width characters, and hiragana and katakana ("tokyo" matches "Tokyo", "とうきょう" matches "トウキョウ"). = and ≠ compare the text exactly.
- Editing function (only when
editableis true andonCellBluris set in the column information) - Checkbox function
- Paste function
- Conditional formatting function
Sorting by several columns
Click a sort button to sort by that column only (click again to reverse the order). Shift+click adds the column as the next sort condition, so rows are sorted by it where the earlier columns are equal:
| Action | Sort conditions | |---|---| | Click 部署 | ① 部署 ascending | | Shift+click 年齢 | ① 部署 ascending → ② 年齢 ascending | | Shift+click 部署 | ① 部署 descending → ② 年齢 ascending (stays first) | | Shift+click 部署 | ① 年齢 ascending (部署 is removed; the rest move up) | | Click 部署 | ① 部署 ascending (a click sorts by one column again) |
With two or more conditions, their priority (1, 2, …) is shown next to the sort icons. Each column is sorted as when sorting by it alone (sortValue, the labels of select options), and empty values stay last. The conditions are reported by onConditionChange (sorts) and restored with initialCondition.sorts; sort still holds the first one, so existing code keeps working.
Keyboard
Click a cell to select it (drag to select a range). Then:
| Key | While a cell is selected | While editing a cell | |---|---|---| | Arrow keys | Move (Shift: extend the range; Ctrl: to the end) | Move the cursor in the text | | Enter / Shift+Enter | Move down / up | Commit, then move down / up | | Tab / Shift+Tab | Move right / left | Commit, then move right / left | | F2, or start typing | Start editing (typing Japanese with an IME works too) | | | Delete / Backspace | One cell: start editing with an empty value. Several cells: empty them (see below) | | | Escape | Clear the selection | Cancel the edit | | Ctrl+C (Cmd+C) / Ctrl+V (Cmd+V) | Copy / paste the selected range | | | Ctrl+D (Cmd+D) | Fill down: copy the top row of the range to the rows below | |
After Enter / Tab, the selection moves only when the value was committed: if it fails the column's constraints, the editor stays open.
While a cell is edited, the cell itself becomes the input (a blue frame) and the keys to use are shown under it. A saved cell glows for a moment, so it is easy to see what changed. In a select cell, the options open under the cell, with the current one checked; typing filters them. Typing on a read-only column shows "この列は編集できません". The hint and the notice are marked data-teppen-part="edit-hint" / "read-only-note", so CSS can hide them.
Emptying several cells (Delete / Backspace) and filling down (Ctrl+D) change many cells at once, like a paste: each changed row is passed to onUpdateRow. Without onUpdateRow, Delete edits the first cell as before and Ctrl+D does nothing. Only editable columns change. Emptying leaves cells that cannot be empty as they are: number columns, select columns without allowEmpty, and text columns whose minLength or pattern does not accept empty text.
Arguments
The table component accepts the arguments id, rows, cols, initialCondition, onClickRow, onUpdateRow, onPasteError, onConditionChange, errorHandler, locale, loading, noDataMessage, rowCountPerPageOptions, className, style, applyRowFormatting, checkbox, conditionalFormattings, maxHeight and maxWidth. Details for each argument are described below.
All of these types are exported from the package, e.g. import type { TTableColumn, TPropsTable } from "@teppen/react-ui";.
id
id is used for the element ids inside the table. It is optional; without it, each table gets a unique id automatically.
rows
rows is an array of the following associative array (DataObject). Only id is required and it must be unique within the array.
export type DataObject = {
[key: string]: number | string | Date;
id: number | string;
};The row objects you pass are given back unchanged to callbacks such as onClickRow, onCellBlur and checkbox, so Date values stay Date objects.
※ onClick property has been deprecated since version 1.0.0 in favor of the new Table argument onClickRow
cols
In cols, you specify the data to be displayed in the table from left to right. The key here needs to match the key name in each element of rows.
it is not necessary to always include the key label, but it is required for some functions, so it is recommended to set it.
export type TColumnType = "string" | "number" | "select" | "date" | "datetime";
export type TTableColumn = {
key: string;
label?: string;
type: TColumnType;
disableSort?: boolean;
disableFilter?: boolean;
sortValue?: (row: DataObject) => string | number | Date | null | undefined; // value to sort by
filterValue?: (row: DataObject) => string | number | Date | null | undefined; // value to filter / search by
options: { value: string; label: string }[]; // Only When "type" is "select"
editable?: boolean;
pinned?: boolean; // keep the column at the left edge while scrolling sideways
disableHide?: boolean; // always shown; not listed in the column selector
render?: (value: string | number, row: DataObject) => React.ReactNode; // Only When "editable" is false
// When "type" is "date" or "datetime", "render" is required and "editable" must be false:
// render: (value: Date) => React.ReactNode;
allowEmpty?: boolean; // Only When "type" is "select"
constraints?: {
maxLength?: number; // Only when "type" is "string"
minLength?: number; // Only when "type" is "string"
pattern?: string; // Only when "type" is "string"
max?: number; // Only when "type" is "number"
min?: number; // Only when "type" is "number"
};
onCellBlur?: (
key: string,
value: number | string, // Value in cell after change
current: DataObject, // Row data before change
completeEditing: () => void // Function to exit cell edit mode
) => void;
};※ TColumnType no longer supports component since version 1.0.0. Instead, render
is now supported. A read-only column that uses render is not sorted or filtered by default, because what it shows may have nothing to do with its value in rows (for example, an id that a component uses to load and show a name). To sort or filter such a column by its value, set disableSort: false / disableFilter: false on it; the all-columns search then also looks at the text render returns, when it returns text.
// Sorted and filtered by the value 80, shown as "80点"
{ key: "score", type: "number", label: "評価", render: (v) => `${v}点`, disableSort: false, disableFilter: false }When a column should be sorted or filtered by something other than its value in rows, give sortValue / filterValue: functions that return the value to use for a row. Setting them on a column with render also makes it sortable / filterable. For example, a column that holds an id and shows a name the app already knows:
{
key: "ownerId",
type: "string",
label: "担当",
render: (id) => userNames[id],
sortValue: (row) => userNames[row.ownerId], // sorted by name
filterValue: (row) => userNames[row.ownerId], // filtered and searched by name
}A column whose display is loaded asynchronously inside the cell (so the app does not have the values) cannot use these; leave it out of sorting and filtering.
Set pinned: true to keep columns visible at the left edge while the table scrolls sideways. Only a leading run of columns can be pinned (the first column, the first two, ...); pinned on a column after an unpinned one is ignored with a warning. With checkbox, the checkbox column stays visible too.
const cols = [
{ key: "name", type: "string", label: "氏名", pinned: true },
{ key: "dept", type: "string", label: "部署", pinned: true },
{ key: "jan", type: "number", label: "1月", editable: true },
// ...
];initialCondition
In initialCondition, you can set initial values for each function. This is optional.
type TInitialCondition = {
sort?: { key: string; asc?: boolean };
sorts?: { key: string; asc?: boolean }[]; // several columns, in order of priority (used instead of sort)
filters?: TFilter[]; // filters applied on the first render
filterMode?: "and" | "or"; // how filters are combined (default "and")
columnWidths?: { [key: string]: number }; // column widths to start with (px)
hiddenColumns?: string[]; // keys of the columns to hide at first
pagination?: {
rowCountPerPage?: number | "all"; // a positive whole number, default 50
page?: number; // the page to show first (from 1)
};
};Under the table, the page navigation always shows the first and the last page number (for example ‹ 前へ 1 … 4 5 [6] 7 8 … 12 次へ ›), so the number of pages is visible and the last page is one click away.
page is kept within the pages there are. When the rows arrive later (for example loaded asynchronously), the table waits for them before choosing the page. Later changes, such as new filters, go back to page 1.
A filter is written as follows. key is a column key, or "*" to search every column (like / not_like only).
type TFilter = {
key: string;
operator:
| "eq" | "neq" | "like" | "not_like"
| "gt" | "gte" | "lt" | "lte"
| "is_null" | "is_not_null"
| "in" | "not_in"; // in / not_in: select columns
value?: string | number | Date | string[]; // string[] for in / not_in
};
// Example
initialCondition={{
filters: [
{ key: "role", operator: "in", value: ["1", "2"] },
{ key: "age", operator: "gte", value: 30 },
{ key: "*", operator: "like", value: "tokyo" },
],
}}Filters that do not fit the column (unknown key, operator or value) are ignored with a console warning.
※ pagenation (the earlier spelling) still works but is deprecated. If both are set, pagination is used.
rowCountPerPage defaults to 50. With pagination, the table stays fast regardless of the number of rows (all operations took under 50 ms with 10,000 rows in our measurements). "all" renders every row at once, so it gets slower as rows increase: with 1,000 rows sorting takes about 0.6 s, and with 10,000 rows the first render and sorting take several seconds. For more than about 1,000 rows, use pagination.
Updating one row redraws only that row, also when cols is written inline (a new array and new onCellBlur functions on every render). Functions that change what is shown, such as render and applyRowFormatting, redraw every row when they are new functions, so that the table shows what they return now; define them outside the component or with useCallback / useMemo when they do not change.
bench/ holds a browser benchmark that compares versions (see bench/README.md).
onConditionChange
onConditionChange is called with the current filters and sort, and the rows that pass the filters in the shown order (all pages, not only the current page). It is called when the table first renders and whenever the filters, the sort or the rows change. This is optional.
type TOnConditionChange = (
condition: {
filters: TFilter[];
filterMode: "and" | "or";
sort: { key: string; asc: boolean }; // the first sort condition
sorts: { key: string; asc: boolean }[]; // every sort condition, in order of priority
pagination: { rowCountPerPage: number | "all"; page: number };
columnWidths: { [key: string]: number }; // widths the user set by dragging
hiddenColumns: string[]; // keys of the columns the user hid
},
rows: DataObject[]
) => void;It is also called when the user moves to another page, when they finish dragging a column border (not while dragging), and when they show or hide a column.
condition has the same shape as initialCondition, so it can be saved (e.g. in localStorage or the URL) and passed back as initialCondition to restore the view. rows can be used for showing the number of matching rows or exporting them.
const saved = JSON.parse(localStorage.getItem("table") ?? "null");
<Table
rows={rows}
cols={cols}
initialCondition={saved ?? undefined}
onConditionChange={(condition, shownRows) => {
localStorage.setItem("table", JSON.stringify(condition));
setCount(shownRows.length);
}}
/>;※ Date values in saved filters become strings in JSON; they are read back as dates.
onClickRow
In onClickRow, you may define the behavior when a row is clicked. This is optional.
type TOnClickRow = (row: DataObject) => void;onUpdateRow
In onUpdateRow, You can define the behavior when you paste data from Excel or spreadsheets into a table. This is optional.
For multi-line data, this function is executed one line at a time.
Pasted data is read as tab-separated cells, as copied from Excel or Google Sheets. For number columns, values such as 1,000 are accepted.
type TOnUpdateRow = (newRow: DataObject, oldRow: DataObject) => void;onPasteError
In onPasteError, you can handle pasted data that cannot be applied, for example when it does not fit in the table or targets a column that is not editable. This is optional; if it is not set, the message is shown with window.alert.
Invalid values are reported to errorHandler instead.
type TOnPasteError = (error: {
type: "NOT_ENOUGH_ROWS" | "NOT_ENOUGH_COLUMNS" | "NOT_EDITABLE_COLUMN";
message: string;
}) => void;errorHandler
In errorHandler, you can handle values that break a column's constraints (when editing a cell or pasting). This is optional.
Without it, an invalid edit is shown in the table: the cell gets a red frame and a short reason under it ("150以下で入力してください"). The value stays so it can be fixed, and Esc puts back the previous value. Invalid pasted values are shown with window.alert. With errorHandler, the app shows the errors (also for edits); the cell still gets the red frame.
type TErrorHandler = (errors: TErrorValidation[]) => void;locale
locale sets the language of everything the table shows by itself: the search bar (placeholders, conditions such as "を含む" / "contains", the "すべて満たす / どれか満たす" switch), the row range under the table, the empty and loading messages, the default errorHandler alert, paste error messages, the "copied" notice and the empty option of select cells. "ja" (default) or "en".
The condition names in initialCondition.filters and onConditionChange ("like", "in" and so on) do not depend on the locale.
type TLocale = "ja" | "en";rowCountPerPageOptions
rowCountPerPageOptions lets the user choose how many rows a page shows. Give the choices as an array; a "表示件数" / "Rows per page" select appears under the table. "all" shows every row. Without it, the count is fixed (initialCondition.pagination.rowCountPerPage, 50 by default).
<Table
rows={rows}
cols={cols}
rowCountPerPageOptions={[10, 25, 50, "all"]}
initialCondition={{ pagination: { rowCountPerPage: 25 } }}
/>Choosing a count goes back to page 1. The chosen count is reported by onConditionChange (pagination.rowCountPerPage), so it can be saved and restored. Choices that are not positive whole numbers or "all" are left out with a console warning; the count in use is added to the choices if it is not one of them.
columnSelector
columnSelector shows a "表示列" / "Columns" button next to the search bar. It opens a list of the columns with a checkbox each, so the user can choose which columns are shown.
<Table
rows={rows}
cols={[
{ key: "name", type: "string", label: "氏名", disableHide: true }, // always shown
{ key: "dept", type: "string", label: "部署" },
{ key: "phone", type: "string", label: "電話" },
]}
columnSelector
initialCondition={{ hiddenColumns: ["phone"] }}
/>- Columns with
disableHide: trueare always shown and cannot be unchecked. The last shown column cannot be hidden either. - Hiding a column only changes what is shown: filters and the sort on it stay in place, and the all-columns search still looks in it. Copying and pasting use the shown columns.
- The hidden columns are reported by
onConditionChange(hiddenColumns) and can be restored withinitialCondition.hiddenColumns, as other conditions.
loading / noDataMessage
Set loading while rows are being fetched. With no rows yet, "読み込み中…" / "Loading…" is shown instead of the empty message; with rows (for example while reloading), the rows stay visible under a light veil.
noDataMessage replaces the message shown when there are no rows. When rows exist but none match the filters, a message about the filters is shown instead.
<Table rows={rows} cols={cols} loading={isFetching} noDataMessage="社員が登録されていません" />className / style (changing the design)
Colors, borders and corner radius come from CSS variables named --teppen-table-*. Set them in your CSS, either for every table (for example on :root) or for one table through a class name you choose. Without them, the table looks as before.
/* every table */
:root {
--teppen-table-header-bg: #123a4f;
--teppen-table-header-text: #e6f1f5;
--teppen-table-accent: #0b8a6e;
}
/* one table: <Table className="employee-list" ... /> */
.employee-list {
--teppen-table-radius: 10px;
}| Variable | Used for | Default |
|---|---|---|
| --teppen-table-bg | Background of the rows, inputs and the search bar | #ffffff |
| --teppen-table-text | Text color of the table | inherited |
| --teppen-table-border | Borders of the table and rows | #e5e7eb |
| --teppen-table-header-bg / --teppen-table-header-text | Header background / text | #e5e7eb / #4b5563 |
| --teppen-table-row-hover | Row under the mouse | rgb(107 114 128 / 0.1) |
| --teppen-table-selection-border / --teppen-table-selection-bg | Selected cells | #2459d6 / rgb(36 89 214 / 0.08) |
| --teppen-table-selection-readonly-border | A selected cell of a read-only column | #6b7280 |
| --teppen-table-editing-border | Frame of the cell being edited, and of a select cell's options | #2459d6 |
| --teppen-table-hint-bg / --teppen-table-hint-text | The key hint under the cell being edited | #111827 / #ffffff |
| --teppen-table-hint-muted-bg | The "cannot be edited" notice | #4b5563 |
| --teppen-table-error / --teppen-table-error-text / --teppen-table-error-bg | An invalid value: frame and message / message text / cell tint | #c4312b / #ffffff / rgb(196 49 43 / 0.06) |
| --teppen-table-chip-bg / --teppen-table-chip-border | Conditions in the search bar | #eef2fb / #c9d5f2 |
| --teppen-table-saved | The short glow on a cell that was just saved | rgb(36 89 214 / 0.18) |
| --teppen-table-accent / --teppen-table-accent-text | Condition chips, chosen values, current page | #0284c7 (#2563eb for the page) / #ffffff |
| --teppen-table-search-border | Border of the search bar | #334155 |
| --teppen-table-control-border | Borders of buttons and select boxes | #94a3b8 – #d1d5db |
| --teppen-table-hover / --teppen-table-active | Buttons and options under the mouse / the chosen option | light gray / rgb(36 89 214 / 0.1) |
| --teppen-table-muted | Secondary text (row range, search icon) | #4b5563 |
| --teppen-table-button-bg / --teppen-table-button-text | The "add" button of the search bar | #334155 / #ffffff |
| --teppen-table-sort-icon / --teppen-table-sort-icon-border | Sort icon (active / inactive outline) | #374151 / #6b7280 |
| --teppen-table-scrollbar / --teppen-table-scrollbar-track | Scroll bars | #64748b / #e2e8f0 |
| --teppen-table-toast-bg / --teppen-table-toast-text | "Copied" notice | #1f2937 / #ffffff |
| --teppen-table-loading-veil | Layer over the rows while loading | rgb(255 255 255 / 0.6) |
| --teppen-table-pinned-border | Line on the right edge of the pinned columns | #cbd5e1 |
| --teppen-table-radius | Corner radius of the table and the search bar | 0.375rem |
For finer control, the main parts carry a data-teppen-part attribute that CSS can target: table, search-bar, condition, header, header-cell, body, row, cell, selection, pagination and range.
.employee-list [data-teppen-part="cell"] {
font-variant-numeric: tabular-nums;
}className and style are put on the table's outer element.
maxHeight / maxWidth
CSS sizes (e.g. "600px") that limit the table area. The table scrolls inside it.
applyRowFormatting
In applyRowFormatting, depending on the status of each row, you can define the format of the row using TailwindCSS classes. In principle, TailwindCSS classes other than background and text color are not guaranteed to work, but any class can be specified.
/**
* The return value is a string of one TailwindCSS class or multiple TailwindCSS classes separated by spaces.
*/
type TApplyRowFormatting = (row: DataObject) => string;checkbox
In checkbox, you can easily place a checkbox at the left edge of the table. If checkbox is not defined, no checkbox will appear at the left edge.
When defining a checkbox, the following three properties must be set.
- checked: Function to determine if a checkbox is checked or not
- onChecked: Function to define the behavior when a checkbox is checked
- onUnchecked: Function to define the behavior when a checkbox is unchecked
type TCheckbox = {
checked: (row: DataObject) => boolean;
onChecked: (row: DataObject) => void;
onUnchecked: (row: DataObject) => void;
selectAll?: "all" | "filtered" | "page"; // rows the header checkbox checks (default "all")
};The checkbox in the header checks all of its rows, or unchecks them when some are checked. selectAll chooses which rows those are:
| selectAll | Rows |
|---|---|
| "all" (default) | Every row, including rows hidden by filters |
| "filtered" | The rows that pass the filters, on every page |
| "page" | The rows on the current page |
With "filtered" or "page", rows outside of it are left as they are, and the header checkbox shows the state of its rows only (checked when some are, a dash when only some are).
<Table
rows={rows}
cols={cols}
checkbox={{
checked: (row) => selectedIds.includes(row.id),
onChecked: (row) => setSelectedIds((ids) => [...ids, row.id]),
onUnchecked: (row) => setSelectedIds((ids) => ids.filter((id) => id !== row.id)),
selectAll: "filtered",
}}
/>conditionalFormattings
In conditionalFormattings, you can define the format of the cell based on the status of each row. The format is defined using TailwindCSS classes. In principle, TailwindCSS classes other than background and text color are not guaranteed to work, but any class can be specified.
type TConditionalFormatting = {
condition: (row: DataObject) => boolean;
key: string;
style: string;
};FileUploader Component
A drop area that also opens the file picker when clicked.
import { FileUploader } from "@teppen/react-ui";
<FileUploader
allowedExtensions={[".pdf", ".xlsx"]}
maxFileSize={10 * 1024 * 1024}
onFilesAdded={(files) => console.log(files)}
onFilesRejected={(files) => console.log("rejected", files)}
/>;Files are checked as follows:
- With only
allowedExtensions, only the extension is checked. With onlyacceptedFileTypes(MIME types such as"image/*"), only the MIME type is checked. - With neither or both, a file is accepted if it matches either list. The defaults cover common images, Office documents, PDF, text and archives.
- Files larger than
maxFileSize(bytes) are rejected.
For a custom UI, use the useFileUploader hook, which returns the props for the drop area and the hidden file input.
Breaking changes since 1.10.2
- Table callbacks (
onClickRow,applyRowFormatting,checkbox,onCellBlur,onUpdateRow) receive the row objects passed inrowsas they are. Before, some of them received copies in whichDatevalues had become ISO strings. - FileUploader: when only
allowedExtensionsor onlyacceptedFileTypesis given, files are checked against that list alone. Before, files that matched the other (default) list were also accepted. - Read-only columns without
rendercan now be sorted and filtered. UsedisableSort/disableFilterto turn this off. - Copying a
datetimecell givesYYYY-MM-DD HH:mm:ssin local time instead of an ISO string in UTC. - Pasted text is split only on tabs, so a value such as
a,bstays in one cell.
License
This library is licensed under MIT license. See LICENSE file for more information.
