tinky-table 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,154 @@
1
+ # tinky-table
2
+
3
+ [English](./README.md) | [简体中文](./README.zh-CN.md)
4
+
5
+ [tinky](https://github.com/ByteLandTechnology/tinky) アプリケーション向けの機能豊富でカスタマイズ可能なテーブルコンポーネントです。ソート、ページネーション、カスタムセルレンダリング、柔軟なレイアウトをサポートし、ターミナルユーザーインターフェース (TUI) 向けに構築されています。
6
+
7
+ ## 特徴
8
+
9
+ - 📦 **宣言的な列定義**: タイトル、キー、幅、配置設定を含む列定義。
10
+ - 🎨 **柔軟なスタイリング**: 複数のボーダースタイル(single, double, round, bold など)やカスタム行/ヘッダースタイルをサポート。
11
+ - 🔀 **ソート**: 並べ替えインジケーター付きの列ソートを標準サポート。
12
+ - 📄 **ページネーション**: ページネーションロジックとの統合が容易。
13
+ - 🖌️ **カスタムレンダリング**: テーブルセル内に任意の Tinky コンポーネント(プログレスバー、バッジなど)をレンダリング可能。
14
+ - 📐 **レスポンシブレイアウト**: 固定幅、パーセンテージ、自動サイズ調整の列をサポート。
15
+
16
+ ## インストール
17
+
18
+ ```bash
19
+ npm install tinky-table
20
+ ```
21
+
22
+ ## 使い方
23
+
24
+ ### 基本的な例
25
+
26
+ ```tsx
27
+ import React from "react";
28
+ import { render } from "tinky";
29
+ import { Table } from "tinky-table";
30
+
31
+ const data = [
32
+ { id: 1, name: "Alice", role: "Engineer", age: 28 },
33
+ { id: 2, name: "Bob", role: "Designer", age: 32 },
34
+ { id: 3, name: "Charlie", role: "Manager", age: 35 },
35
+ ];
36
+
37
+ const columns = [
38
+ { key: "id", title: "ID", width: 5 },
39
+ { key: "name", title: "Name", width: 20 },
40
+ { key: "role", title: "Role", width: 15 },
41
+ { key: "age", title: "Age", width: 5, align: "right" },
42
+ ];
43
+
44
+ function App() {
45
+ return <Table data={data} columns={columns} borderStyle="round" />;
46
+ }
47
+
48
+ render(<App />);
49
+ ```
50
+
51
+ ### 高度な使用法 (カスタムレンダリングとソート)
52
+
53
+ ```tsx
54
+ import React, { useState } from "react";
55
+ import { render, Text } from "tinky";
56
+ import { Table, ColumnDef, SortDirection } from "tinky-table";
57
+
58
+ interface User {
59
+ id: number;
60
+ name: string;
61
+ status: "active" | "inactive";
62
+ score: number;
63
+ }
64
+
65
+ const data: User[] = [
66
+ { id: 1, name: "Alice", status: "active", score: 95 },
67
+ { id: 2, name: "Bob", status: "inactive", score: 82 },
68
+ { id: 3, name: "Charlie", status: "active", score: 88 },
69
+ ];
70
+
71
+ function StatusBadge({ status }: { status: User["status"] }) {
72
+ return (
73
+ <Text color={status === "active" ? "green" : "gray"}>
74
+ {status.toUpperCase()}
75
+ </Text>
76
+ );
77
+ }
78
+
79
+ function App() {
80
+ const [sortState, setSortState] = useState<{
81
+ key: string;
82
+ direction: SortDirection;
83
+ }>({
84
+ key: "name",
85
+ direction: "asc",
86
+ });
87
+
88
+ const columns: ColumnDef<User>[] = [
89
+ { key: "name", title: "Name", width: 20, sortable: true },
90
+ {
91
+ key: "status",
92
+ title: "Status",
93
+ width: 10,
94
+ render: (val) => <StatusBadge status={val as User["status"]} />,
95
+ },
96
+ {
97
+ key: "score",
98
+ title: "Score",
99
+ width: 10,
100
+ align: "right",
101
+ render: (val) => <Text bold>{val}%</Text>,
102
+ },
103
+ ];
104
+
105
+ const sortedData = [...data].sort((a, b) => {
106
+ // ... ソートロジック ...
107
+ return 0;
108
+ });
109
+
110
+ return (
111
+ <Table
112
+ data={sortedData}
113
+ columns={columns}
114
+ sortState={sortState}
115
+ rowStyle={(row, index) => ({
116
+ backgroundColor: index % 2 === 0 ? undefined : "#222",
117
+ })}
118
+ />
119
+ );
120
+ }
121
+
122
+ render(<App />);
123
+ ```
124
+
125
+ ## API
126
+
127
+ ### `<Table />` プロパティ
128
+
129
+ | プロパティ | 型 | 説明 | デフォルト |
130
+ | ------------- | ------------------------------------------- | ------------------------------------------------------------------------- | ----------- |
131
+ | `data` | `T[]` | 表示するデータオブジェクトの配列。 | 必須 |
132
+ | `columns` | `ColumnDef<T>[]` | 列の定義。 | 必須 |
133
+ | `borderStyle` | `TableBorderStyle` | 枠線のスタイル (`single`, `double`, `round`, `bold`, `classic`, `none`)。 | `'single'` |
134
+ | `showHeader` | `boolean` | ヘッダー行を表示するかどうか。 | `true` |
135
+ | `width` | `number \| string` | テーブルの全幅。 | `'auto'` |
136
+ | `emptyText` | `ReactNode` | データが空の場合に表示するコンテンツ。 | `'No data'` |
137
+ | `sortState` | `{ key: string, direction: SortDirection }` | 現在のソート状態。 | `undefined` |
138
+ | `rowStyle` | `(row: T, index: number) => Style` | 行を動的にスタイルするための関数。 | `undefined` |
139
+
140
+ ### `ColumnDef` プロパティ
141
+
142
+ | プロパティ | 型 | 説明 |
143
+ | ---------- | ---------------------------------- | ------------------------------------------------------------------- |
144
+ | `key` | `string` | 列の一意な識別子。 |
145
+ | `title` | `ReactNode` | 表示するヘッダーのタイトル。 |
146
+ | `dataKey` | `keyof T` | データオブジェクトから値を抽出するためのキー (デフォルトは `key`)。 |
147
+ | `width` | `number \| string` | 幅 (`10`, `'20%'`, `'auto'`)。 |
148
+ | `align` | `'left' \| 'center' \| 'right'` | テキストの配置。 |
149
+ | `render` | `(value, row, index) => ReactNode` | セルのカスタムレンダリング関数。 |
150
+ | `sortable` | `boolean` | 列にソートインジケーターを表示するかどうか。 |
151
+
152
+ ## ライセンス
153
+
154
+ MIT
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # tinky-table
2
+
3
+ [简体中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md)
4
+
5
+ A feature-rich, customizable Table component for [tinky](https://github.com/ByteLandTechnology/tinky) applications. Built for terminal user interfaces with support for sorting, pagination, custom cell rendering, and flexible layouts.
6
+
7
+ ## Features
8
+
9
+ - 📦 **Declarative Column Definitions**: Define columns with titles, keys, widths, and alignments.
10
+ - 🎨 **Flexible Styling**: Support for multiple border styles (single, double, round, bold, etc.) and custom row/header styles.
11
+ - 🔀 **Sorting**: Built-in support for column sorting with visual indicators.
12
+ - 📄 **Pagination**: Easy integration with pagination logic.
13
+ - 🖌️ **Custom Rendering**: Render any Tinky component inside table cells (progress bars, badges, etc.).
14
+ - 📐 **Responsive Layouts**: Supports fixed, percentage-based, and auto-sizing columns.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install tinky-table
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Basic Example
25
+
26
+ ```tsx
27
+ import React from "react";
28
+ import { render } from "tinky";
29
+ import { Table } from "tinky-table";
30
+
31
+ const data = [
32
+ { id: 1, name: "Alice", role: "Engineer", age: 28 },
33
+ { id: 2, name: "Bob", role: "Designer", age: 32 },
34
+ { id: 3, name: "Charlie", role: "Manager", age: 35 },
35
+ ];
36
+
37
+ const columns = [
38
+ { key: "id", title: "ID", width: 5 },
39
+ { key: "name", title: "Name", width: 20 },
40
+ { key: "role", title: "Role", width: 15 },
41
+ { key: "age", title: "Age", width: 5, align: "right" },
42
+ ];
43
+
44
+ function App() {
45
+ return <Table data={data} columns={columns} borderStyle="round" />;
46
+ }
47
+
48
+ render(<App />);
49
+ ```
50
+
51
+ ### Advanced Usage (Custom Rendering & Sorting)
52
+
53
+ ```tsx
54
+ import React, { useState } from "react";
55
+ import { render, Text } from "tinky";
56
+ import { Table, ColumnDef, SortDirection } from "tinky-table";
57
+
58
+ interface User {
59
+ id: number;
60
+ name: string;
61
+ status: "active" | "inactive";
62
+ score: number;
63
+ }
64
+
65
+ const data: User[] = [
66
+ { id: 1, name: "Alice", status: "active", score: 95 },
67
+ { id: 2, name: "Bob", status: "inactive", score: 82 },
68
+ { id: 3, name: "Charlie", status: "active", score: 88 },
69
+ ];
70
+
71
+ function StatusBadge({ status }: { status: User["status"] }) {
72
+ return (
73
+ <Text color={status === "active" ? "green" : "gray"}>
74
+ {status.toUpperCase()}
75
+ </Text>
76
+ );
77
+ }
78
+
79
+ function App() {
80
+ const [sortState, setSortState] = useState<{
81
+ key: string;
82
+ direction: SortDirection;
83
+ }>({
84
+ key: "name",
85
+ direction: "asc",
86
+ });
87
+
88
+ const columns: ColumnDef<User>[] = [
89
+ { key: "name", title: "Name", width: 20, sortable: true },
90
+ {
91
+ key: "status",
92
+ title: "Status",
93
+ width: 10,
94
+ render: (val) => <StatusBadge status={val as User["status"]} />,
95
+ },
96
+ {
97
+ key: "score",
98
+ title: "Score",
99
+ width: 10,
100
+ align: "right",
101
+ render: (val) => <Text bold>{val}%</Text>,
102
+ },
103
+ ];
104
+
105
+ const sortedData = [...data].sort((a, b) => {
106
+ // ... sorting logic ...
107
+ return 0;
108
+ });
109
+
110
+ return (
111
+ <Table
112
+ data={sortedData}
113
+ columns={columns}
114
+ sortState={sortState}
115
+ rowStyle={(row, index) => ({
116
+ backgroundColor: index % 2 === 0 ? undefined : "#222",
117
+ })}
118
+ />
119
+ );
120
+ }
121
+
122
+ render(<App />);
123
+ ```
124
+
125
+ ## API
126
+
127
+ ### `<Table />` Properties
128
+
129
+ | Prop | Type | Description | Default |
130
+ | ------------- | ------------------------------------------- | ---------------------------------------------------------------------- | ----------- |
131
+ | `data` | `T[]` | Array of data objects to display. | Required |
132
+ | `columns` | `ColumnDef<T>[]` | Column definitions. | Required |
133
+ | `borderStyle` | `TableBorderStyle` | Border style (`single`, `double`, `round`, `bold`, `classic`, `none`). | `'single'` |
134
+ | `showHeader` | `boolean` | Whether to display the header row. | `true` |
135
+ | `width` | `number \| string` | Total width of the table. | `'auto'` |
136
+ | `emptyText` | `ReactNode` | Content to display when data is empty. | `'No data'` |
137
+ | `sortState` | `{ key: string, direction: SortDirection }` | Current sort state. | `undefined` |
138
+ | `rowStyle` | `(row: T, index: number) => Style` | Function to dynamically style rows. | `undefined` |
139
+
140
+ ### `ColumnDef` Properties
141
+
142
+ | Prop | Type | Description |
143
+ | ---------- | ---------------------------------- | ---------------------------------------------------------- |
144
+ | `key` | `string` | Unique identifier for the column. |
145
+ | `title` | `ReactNode` | Header title to display. |
146
+ | `dataKey` | `keyof T` | Key to extract value from data object (defaults to `key`). |
147
+ | `width` | `number \| string` | Width (`10`, `'20%'`, `'auto'`). |
148
+ | `align` | `'left' \| 'center' \| 'right'` | Text alignment. |
149
+ | `render` | `(value, row, index) => ReactNode` | Custom render function for the cell. |
150
+ | `sortable` | `boolean` | Whether the column shows a sort indicator. |
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,154 @@
1
+ # tinky-table
2
+
3
+ [English](./README.md) | [日本語](./README.ja-JP.md)
4
+
5
+ [tinky](https://github.com/ByteLandTechnology/tinky) 应用程序的功能丰富且可定制的表格组件。专为终端用户界面(TUI)构建,支持排序、分页、自定义单元格渲染和灵活的布局。
6
+
7
+ ## 特性
8
+
9
+ - 📦 **声明式列定义**: 定义包含标题、键、宽度和对齐方式的列。
10
+ - 🎨 **灵活的样式**: 支持多种边框样式(single, double, round, bold 等)以及自定义行/表头样式。
11
+ - 🔀 **排序**: 内置支持带视觉指示器的列排序。
12
+ - 📄 **分页**: 易于集成分页逻辑。
13
+ - 🖌️ **自定义渲染**: 在表格单元格内渲染任何 Tinky 组件(进度条、徽章等)。
14
+ - 📐 **响应式布局**: 支持固定宽度、百分比宽度和自动宽度的列。
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ npm install tinky-table
20
+ ```
21
+
22
+ ## 使用方法
23
+
24
+ ### 基本示例
25
+
26
+ ```tsx
27
+ import React from "react";
28
+ import { render } from "tinky";
29
+ import { Table } from "tinky-table";
30
+
31
+ const data = [
32
+ { id: 1, name: "Alice", role: "Engineer", age: 28 },
33
+ { id: 2, name: "Bob", role: "Designer", age: 32 },
34
+ { id: 3, name: "Charlie", role: "Manager", age: 35 },
35
+ ];
36
+
37
+ const columns = [
38
+ { key: "id", title: "ID", width: 5 },
39
+ { key: "name", title: "Name", width: 20 },
40
+ { key: "role", title: "Role", width: 15 },
41
+ { key: "age", title: "Age", width: 5, align: "right" },
42
+ ];
43
+
44
+ function App() {
45
+ return <Table data={data} columns={columns} borderStyle="round" />;
46
+ }
47
+
48
+ render(<App />);
49
+ ```
50
+
51
+ ### 高级用法 (自定义渲染与排序)
52
+
53
+ ```tsx
54
+ import React, { useState } from "react";
55
+ import { render, Text } from "tinky";
56
+ import { Table, ColumnDef, SortDirection } from "tinky-table";
57
+
58
+ interface User {
59
+ id: number;
60
+ name: string;
61
+ status: "active" | "inactive";
62
+ score: number;
63
+ }
64
+
65
+ const data: User[] = [
66
+ { id: 1, name: "Alice", status: "active", score: 95 },
67
+ { id: 2, name: "Bob", status: "inactive", score: 82 },
68
+ { id: 3, name: "Charlie", status: "active", score: 88 },
69
+ ];
70
+
71
+ function StatusBadge({ status }: { status: User["status"] }) {
72
+ return (
73
+ <Text color={status === "active" ? "green" : "gray"}>
74
+ {status.toUpperCase()}
75
+ </Text>
76
+ );
77
+ }
78
+
79
+ function App() {
80
+ const [sortState, setSortState] = useState<{
81
+ key: string;
82
+ direction: SortDirection;
83
+ }>({
84
+ key: "name",
85
+ direction: "asc",
86
+ });
87
+
88
+ const columns: ColumnDef<User>[] = [
89
+ { key: "name", title: "Name", width: 20, sortable: true },
90
+ {
91
+ key: "status",
92
+ title: "Status",
93
+ width: 10,
94
+ render: (val) => <StatusBadge status={val as User["status"]} />,
95
+ },
96
+ {
97
+ key: "score",
98
+ title: "Score",
99
+ width: 10,
100
+ align: "right",
101
+ render: (val) => <Text bold>{val}%</Text>,
102
+ },
103
+ ];
104
+
105
+ const sortedData = [...data].sort((a, b) => {
106
+ // ... 排序逻辑 ...
107
+ return 0;
108
+ });
109
+
110
+ return (
111
+ <Table
112
+ data={sortedData}
113
+ columns={columns}
114
+ sortState={sortState}
115
+ rowStyle={(row, index) => ({
116
+ backgroundColor: index % 2 === 0 ? undefined : "#222",
117
+ })}
118
+ />
119
+ );
120
+ }
121
+
122
+ render(<App />);
123
+ ```
124
+
125
+ ## API
126
+
127
+ ### `<Table />` 属性
128
+
129
+ | 属性 | 类型 | 描述 | 默认值 |
130
+ | ------------- | ------------------------------------------- | ------------------------------------------------------------------- | ----------- |
131
+ | `data` | `T[]` | 要显示的数据对象数组。 | 必填 |
132
+ | `columns` | `ColumnDef<T>[]` | 列定义。 | 必填 |
133
+ | `borderStyle` | `TableBorderStyle` | 边框样式 (`single`, `double`, `round`, `bold`, `classic`, `none`)。 | `'single'` |
134
+ | `showHeader` | `boolean` | 是否显示表头行。 | `true` |
135
+ | `width` | `number \| string` | 表格的总宽度。 | `'auto'` |
136
+ | `emptyText` | `ReactNode` | 数据为空时显示的内容。 | `'No data'` |
137
+ | `sortState` | `{ key: string, direction: SortDirection }` | 当前排序状态。 | `undefined` |
138
+ | `rowStyle` | `(row: T, index: number) => Style` | 动态设置行样式的函数。 | `undefined` |
139
+
140
+ ### `ColumnDef` 属性
141
+
142
+ | 属性 | 类型 | 描述 |
143
+ | ---------- | ---------------------------------- | ---------------------------------------- |
144
+ | `key` | `string` | 列的唯一标识符。 |
145
+ | `title` | `ReactNode` | 要显示的表头标题。 |
146
+ | `dataKey` | `keyof T` | 从数据对象中提取值的键(默认为 `key`)。 |
147
+ | `width` | `number \| string` | 宽度 (`10`, `'20%'`, `'auto'`)。 |
148
+ | `align` | `'left' \| 'center' \| 'right'` | 文本对齐方式。 |
149
+ | `render` | `(value, row, index) => ReactNode` | 单元格的自定义渲染函数。 |
150
+ | `sortable` | `boolean` | 列是否显示排序指示器。 |
151
+
152
+ ## 许可证
153
+
154
+ MIT
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Main Table component for tinky-table.
3
+ * @module tinky-table/Table
4
+ */
5
+ import { type TableProps, type RowData } from "../types.js";
6
+ export declare function Table<T extends RowData>({ data, columns, borderStyle, showHeader, emptyText, sortState, pagination, headerStyle, rowStyle, }: TableProps<T>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.Table = Table;
18
+ var jsx_runtime_1 = require("react/jsx-runtime");
19
+ /**
20
+ * @fileoverview Main Table component for tinky-table.
21
+ * @module tinky-table/Table
22
+ */
23
+ var react_1 = __importDefault(require("react"));
24
+ var tinky_1 = require("tinky");
25
+ var border_js_1 = require("../utils/border.js");
26
+ var column_width_js_1 = require("../utils/column-width.js");
27
+ /**
28
+ * Get value from row data by column definition.
29
+ */
30
+ /**
31
+ * Retrieves the value of a specific cell from a data row using the column definition.
32
+ *
33
+ * @template T - The type of the row data object.
34
+ * @param row - The data object for the current row.
35
+ * @param col - The column definition containing the key or dataKey.
36
+ * @returns The value extracted from the row.
37
+ */
38
+ function getCellValue(row, col) {
39
+ var _a;
40
+ var key = (_a = col.dataKey) !== null && _a !== void 0 ? _a : col.key;
41
+ return row[key];
42
+ }
43
+ /**
44
+ * Render sort indicator for sortable columns.
45
+ */
46
+ /**
47
+ * Renders the visual indicator for the current sort direction.
48
+ *
49
+ * @param direction - The current sort direction ('asc', 'desc', or null).
50
+ * @returns A string representing the sort arrow (e.g., " ▲", " ▼") or an empty string.
51
+ */
52
+ function renderSortIndicator(direction) {
53
+ switch (direction) {
54
+ case "asc":
55
+ return " ▲";
56
+ case "desc":
57
+ return " ▼";
58
+ default:
59
+ return "";
60
+ }
61
+ }
62
+ /**
63
+ * Internal component that renders horizontal separator lines.
64
+ *
65
+ * Used for:
66
+ * - Top border of the table
67
+ * - Separator between header and body
68
+ * - Bottom border of the table
69
+ *
70
+ * Memoized to prevent unnecessary re-calculations of the separator string during re-renders.
71
+ *
72
+ * @internal
73
+ * @param props - Component properties
74
+ */
75
+ var TableSeparator = react_1.default.memo(function TableSeparator(_a) {
76
+ var columns = _a.columns, gridTemplate = _a.gridTemplate, borderChars = _a.borderChars, noBorder = _a.noBorder, position = _a.position;
77
+ var leftChar = position === "top"
78
+ ? borderChars.topLeft
79
+ : position === "bottom"
80
+ ? borderChars.bottomLeft
81
+ : borderChars.leftCross;
82
+ var rightChar = position === "top"
83
+ ? borderChars.topRight
84
+ : position === "bottom"
85
+ ? borderChars.bottomRight
86
+ : borderChars.rightCross;
87
+ var crossChar = position === "top"
88
+ ? borderChars.topCross
89
+ : position === "bottom"
90
+ ? borderChars.bottomCross
91
+ : borderChars.cross;
92
+ // Memoize the horizontal line to avoid recreating on each render
93
+ return ((0, jsx_runtime_1.jsxs)(tinky_1.Box, { display: "grid", gridTemplateColumns: gridTemplate, children: [!noBorder && (0, jsx_runtime_1.jsx)(tinky_1.Text, { children: leftChar }), columns.map(function (_, i) { return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(tinky_1.Separator, { char: borderChars.horizontal }), !noBorder && ((0, jsx_runtime_1.jsx)(tinky_1.Text, { children: i === columns.length - 1 ? rightChar : crossChar }))] }, i)); })] }));
94
+ });
95
+ /**
96
+ * Memoized row component to prevent unnecessary re-renders.
97
+ * Each row is only re-rendered when its props change.
98
+ */
99
+ /**
100
+ * Internal memoized component for rendering a single data row.
101
+ *
102
+ * Performance optimization:
103
+ * - Only re-renders if the specific row data, index, or table configuration changes.
104
+ * - Prevents rerendering all rows when unrelated state changes (e.g. only one row updates).
105
+ *
106
+ * @internal
107
+ * @template T - The row data type.
108
+ */
109
+ var MemoizedTableRow = react_1.default.memo(function TableRowInner(_a) {
110
+ var row = _a.row, rowIndex = _a.rowIndex, columns = _a.columns, gridTemplate = _a.gridTemplate, borderChars = _a.borderChars, noBorder = _a.noBorder, rowStyle = _a.rowStyle;
111
+ var customRowStyle = rowStyle === null || rowStyle === void 0 ? void 0 : rowStyle(row, rowIndex);
112
+ return ((0, jsx_runtime_1.jsxs)(tinky_1.Box, __assign({ display: "grid", gridTemplateColumns: gridTemplate }, customRowStyle, { children: [!noBorder && (0, jsx_runtime_1.jsx)(tinky_1.Text, { children: borderChars.vertical }), columns.map(function (col) {
113
+ var value = getCellValue(row, col);
114
+ var content = col.render
115
+ ? col.render(value, row, rowIndex)
116
+ : String(value !== null && value !== void 0 ? value : "");
117
+ var align = col.align === "right"
118
+ ? "flex-end"
119
+ : col.align === "center"
120
+ ? "center"
121
+ : "flex-start";
122
+ return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(tinky_1.Box, { flexGrow: 1, justifyContent: align, children: typeof content === "string" ? ((0, jsx_runtime_1.jsx)(tinky_1.Text, { wrap: "truncate", children: content })) : (content) }), !noBorder && (0, jsx_runtime_1.jsx)(tinky_1.Text, { children: borderChars.vertical })] }, col.key));
123
+ })] })));
124
+ });
125
+ function Table(_a) {
126
+ var data = _a.data, columns = _a.columns, _b = _a.borderStyle, borderStyle = _b === void 0 ? "single" : _b, _c = _a.showHeader, showHeader = _c === void 0 ? true : _c, _d = _a.emptyText, emptyText = _d === void 0 ? "No data" : _d, sortState = _a.sortState, pagination = _a.pagination, headerStyle = _a.headerStyle, rowStyle = _a.rowStyle;
127
+ var borderChars = (0, border_js_1.getBorderChars)(borderStyle);
128
+ var contentWidths = react_1.default.useMemo(function () { return (0, column_width_js_1.columnsToGridTemplate)(columns); }, [columns]);
129
+ var noBorder = borderStyle === "none";
130
+ // Build grid template: border, col, border, col, ..., border
131
+ var gridTemplate = react_1.default.useMemo(function () {
132
+ var gt = [];
133
+ if (!noBorder)
134
+ gt.push(1); // Left border
135
+ contentWidths.forEach(function (w) {
136
+ gt.push(w);
137
+ if (!noBorder)
138
+ gt.push(1); // Right/Sep border
139
+ });
140
+ return gt;
141
+ }, [contentWidths, noBorder]);
142
+ // Pagination logic
143
+ var displayData = data;
144
+ if (pagination) {
145
+ var start = (pagination.page - 1) * pagination.pageSize;
146
+ var end = start + pagination.pageSize;
147
+ displayData = data.slice(start, end);
148
+ }
149
+ return ((0, jsx_runtime_1.jsxs)(tinky_1.Box, { flexDirection: "column", children: [!noBorder && ((0, jsx_runtime_1.jsx)(TableSeparator, { position: "top", columns: columns, gridTemplate: gridTemplate, borderChars: borderChars, noBorder: noBorder })), showHeader && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(tinky_1.Box, __assign({ display: "grid", gridTemplateColumns: gridTemplate }, headerStyle, { children: [!noBorder && (0, jsx_runtime_1.jsx)(tinky_1.Text, { children: borderChars.vertical }), columns.map(function (col) {
150
+ var _a;
151
+ var sortIndicator = (sortState === null || sortState === void 0 ? void 0 : sortState.key) === col.key
152
+ ? renderSortIndicator(sortState.direction)
153
+ : "";
154
+ var align = col.align === "right"
155
+ ? "flex-end"
156
+ : col.align === "center"
157
+ ? "center"
158
+ : "flex-start";
159
+ return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsx)(tinky_1.Box, { flexGrow: 1, justifyContent: align, children: (0, jsx_runtime_1.jsxs)(tinky_1.Text, { bold: true, wrap: "truncate", children: [String((_a = col.title) !== null && _a !== void 0 ? _a : col.key), sortIndicator] }) }), !noBorder && ((0, jsx_runtime_1.jsx)(tinky_1.Text, { children: borderChars.vertical }))] }, col.key));
160
+ })] })), !noBorder && ((0, jsx_runtime_1.jsx)(TableSeparator, { position: "middle", columns: columns, gridTemplate: gridTemplate, borderChars: borderChars, noBorder: noBorder }))] })), displayData.length === 0 ? ((0, jsx_runtime_1.jsx)(tinky_1.Box, { justifyContent: "center", borderStyle: !noBorder ? borderStyle : undefined, children: (0, jsx_runtime_1.jsx)(tinky_1.Text, { children: emptyText }) })) : (displayData.map(function (row, rowIndex) { return ((0, jsx_runtime_1.jsx)(MemoizedTableRow, { row: row, rowIndex: rowIndex, columns: columns, gridTemplate: gridTemplate, borderChars: borderChars, noBorder: noBorder, rowStyle: rowStyle }, rowIndex)); })), !noBorder && displayData.length > 0 && ((0, jsx_runtime_1.jsx)(TableSeparator, { position: "bottom", columns: columns, gridTemplate: gridTemplate, borderChars: borderChars, noBorder: noBorder })), pagination && ((0, jsx_runtime_1.jsx)(tinky_1.Box, { marginTop: 1, justifyContent: "center", children: (0, jsx_runtime_1.jsxs)(tinky_1.Text, { dimColor: true, children: ["Page ", pagination.page, " of", " ", Math.ceil(pagination.total / pagination.pageSize)] }) }))] }));
161
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @fileoverview TableCell component for rendering individual table cells.
3
+ * @module tinky-table/TableCell
4
+ */
5
+ import { type TableCellProps } from "../types.js";
6
+ /**
7
+ * TableCell renders an individual cell in the table.
8
+ *
9
+ * The cell width is handled by the parent Grid layout.
10
+ * Text truncation is handled by tinky's Text component wrap property.
11
+ *
12
+ * @param props - Cell properties
13
+ * @returns The rendered cell
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <TableCell width={20} align="right" isLast={false} borderChar="│">
18
+ * 123.45
19
+ * </TableCell>
20
+ * ```
21
+ */
22
+ export declare function TableCell({ children, align, isLast, borderChar, }: TableCellProps): import("react/jsx-runtime").JSX.Element;