react-glide-table 1.0.1 → 1.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.
package/README.md CHANGED
@@ -1,535 +1,100 @@
1
1
  # react-glide-table
2
2
 
3
- A high-performance React data table built on [@tanstack/react-table](https://tanstack.com/table) and [@tanstack/react-virtual](https://tanstack.com/virtual).
3
+ Headless React table primitives built on [@tanstack/react-table](https://tanstack.com/table) and [@tanstack/react-virtual](https://tanstack.com/virtual).
4
4
 
5
- It ships a declarative compound `Table` API and a lower-level `DataTable` for column-def driven usage, with row virtualization, selection, inline editing, cell fill, row spanning, tree expand, sorting, and pagination.
6
-
7
- ## Inspired by Glide Data Grid
8
-
9
- [Glide Data Grid](https://github.com/glideapps/glide-data-grid) (`@glideapps/glide-data-grid`) is a Canvas-based spreadsheet-style grid. This library is **not** a wrapper around it — it reimplements the basic interaction patterns on top of a normal HTML `<table>` + TanStack stack, so you keep DOM accessibility, CSS styling, and a React-friendly column API.
10
-
11
- ### Glide-inspired capabilities layered in
12
-
13
- | Capability | What Glide provides | How `react-glide-table` adopts it |
14
- | --- | --- | --- |
15
- | **Cell range selection** | Drag / keyboard ranges (`gridSelection`, `rangeSelect`) | Click a cell, drag across cells to select a rectangular range |
16
- | **Fill handle** | `fillHandle` — copy values into adjacent cells | Selection corner handle fills neighboring cells; commits via `onDataChange` |
17
- | **Inline editing** | Built-in double-click edit with commit / cancel | Double-click editable cells; **Enter** commits, **Escape** cancels (`editable` / `editType`) |
18
- | **Row selection** | `rowSelect`: none / single / multi | `rowSelectionMode`: `"none"` \| `"single"` \| `"multi"` (controlled or uncontrolled) |
19
- | **Large-list scrolling** | Canvas lazy paint for millions of rows | DOM **row virtualization** via `@tanstack/react-virtual` (spacer rows inside `<tbody>`) |
20
- | **Merged cells** | Span / merge support | `enableRowSpan` + column `rowSpan` / `rowSpanKey` for consecutive vertical merges |
21
-
22
- ### Intentionally different from Glide
23
-
24
- - **Rendering**: HTML table DOM instead of HTML5 Canvas — easier theming, custom React cell renderers, and standard CSS.
25
- - **Column model**: TanStack `ColumnDef` or declarative `Table.Column` / `createTable`, not Glide’s canvas cell draw API.
26
- - **Extra app-table features** (beyond the Glide interaction set): compound components, sorting, controlled pagination, toolbar slots, and tree / expandable rows.
27
-
28
- ### Not ported (yet)
29
-
30
- These Glide features are **not** included: Canvas custom cell drawing, frozen/pinned columns, column reorder / resize as first-class APIs, multi-rect / column-range selection stacks, markdown / bubble / image / sparkline cell types, and million-row Canvas-level scale. Use this library when you want Glide-like **edit / select / fill** UX on a conventional React table.
31
-
32
- ## Features
33
-
34
- - **Declarative columns** via `Table` / `createTable` compound components
35
- - **Low-level API** via `DataTable` + TanStack `ColumnDef`
36
- - **Row virtualization** (HTML table + spacer rows; disabled automatically when row spanning is on)
37
- - **Row selection** — none / single / multi *(Glide-inspired)*
38
- - **Inline cell editing** — double-click to edit (`text` | `number`) *(Glide-inspired)*
39
- - **Cell range selection & fill handle** — drag to select, fill adjacent cells (Excel-like) *(Glide-inspired)*
40
- - **Row spanning** — merge consecutive cells by key *(Glide-inspired)*
41
- - **Tree / expandable rows** — nested or flat parent–child data
42
- - **Sorting** — clickable headers through `Table.Column sortable`
43
- - **Pagination** — controlled `Table.Pagination`
44
- - **Toolbar slots** — counts, summary, custom actions
45
-
46
- ## Requirements
47
-
48
- | Package | Version |
49
- | --- | --- |
50
- | `react` | `^18` or `^19` |
51
- | `react-dom` | `^18` or `^19` |
52
- | `@tanstack/react-table` | `^8` |
53
- | `@tanstack/react-virtual` | `^3` |
54
-
55
- These are **peer dependencies** — install them in your app.
5
+ You own the markup and styles. This package exposes state, handlers, and pure helpers for selection, editing, fill, row spanning, tree expand, and virtualization.
56
6
 
57
7
  ## Installation
58
8
 
59
9
  ```bash
60
- npm install react-glide-table @tanstack/react-table @tanstack/react-virtual
61
- # or
62
- pnpm add react-glide-table @tanstack/react-table @tanstack/react-virtual
10
+ npm install react-glide-table
63
11
  # or
64
- yarn add react-glide-table @tanstack/react-table @tanstack/react-virtual
12
+ pnpm add react-glide-table
65
13
  ```
66
14
 
67
- Import styles once at your app entry (or layout):
68
-
69
- ```ts
70
- import "react-glide-table/style.css"
71
- ```
15
+ Peer dependencies: `react` and `react-dom` (`^18` or `^19`).
72
16
 
73
17
  ## Quick start
74
18
 
75
- ### Recommended: typed compound table with `createTable`
76
-
77
- `createTable<T>()` returns a typed compound component (`Header`, `Column`, `Body`, `Pagination`) so `field` and `render` stay type-safe.
78
-
79
19
  ```tsx
80
- import { useState } from "react"
81
- import { createTable } from "react-glide-table"
82
- import "react-glide-table/style.css"
83
-
84
- type Product = {
85
- id: string
86
- name: string
87
- qty: number
88
- price: number
89
- }
20
+ import { flexRender } from "@tanstack/react-table"
21
+ import { useGlideTable } from "react-glide-table"
22
+ import type { ColumnDef } from "react-glide-table"
90
23
 
91
- const ProductTable = createTable<Product>()
24
+ type Product = { id: string; name: string; qty: number }
92
25
 
93
- const INITIAL: Product[] = [
94
- { id: "1", name: "Widget", qty: 10, price: 1200 },
95
- { id: "2", name: "Gadget", qty: 4, price: 3400 },
26
+ const columns: ColumnDef<Product, unknown>[] = [
27
+ { accessorKey: "name", header: "Name" },
28
+ { accessorKey: "qty", header: "Qty" },
96
29
  ]
97
30
 
98
- export function ProductList() {
99
- const [data, setData] = useState(INITIAL)
31
+ export function ProductTable({ data }: { data: Product[] }) {
32
+ const { table, rows, scrollRef } = useGlideTable({
33
+ data,
34
+ columns,
35
+ getRowId: (row) => row.id,
36
+ rowSelectionMode: "multi",
37
+ })
100
38
 
101
39
  return (
102
- <ProductTable
103
- data={data}
104
- getRowId={(row) => row.id}
105
- onDataChange={setData}
106
- rowSelectionMode="multi"
107
- filteredCount={data.length}
108
- totalCount={data.length}
109
- toolbar={<button type="button">Export</button>}
110
- >
111
- <ProductTable.Header>
112
- <ProductTable.Column field="name" sortable editable>
113
- Name
114
- </ProductTable.Column>
115
- <ProductTable.Column field="qty" sortable align="right" editable editType="number">
116
- Qty
117
- </ProductTable.Column>
118
- <ProductTable.Column
119
- field="price"
120
- align="right"
121
- render={(value) => `$${Number(value).toLocaleString()}`}
122
- >
123
- Price
124
- </ProductTable.Column>
125
- </ProductTable.Header>
126
- </ProductTable>
40
+ <div ref={scrollRef}>
41
+ <table>
42
+ <thead>
43
+ {table.getHeaderGroups().map((headerGroup) => (
44
+ <tr key={headerGroup.id}>
45
+ {headerGroup.headers.map((header) => (
46
+ <th key={header.id}>
47
+ {header.isPlaceholder
48
+ ? null
49
+ : flexRender(header.column.columnDef.header, header.getContext())}
50
+ </th>
51
+ ))}
52
+ </tr>
53
+ ))}
54
+ </thead>
55
+ <tbody>
56
+ {rows.map((row) => (
57
+ <tr key={row.id}>
58
+ {row.getVisibleCells().map((cell) => (
59
+ <td key={cell.id}>
60
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
61
+ </td>
62
+ ))}
63
+ </tr>
64
+ ))}
65
+ </tbody>
66
+ </table>
67
+ </div>
127
68
  )
128
69
  }
129
70
  ```
130
71
 
131
- ### Alternative: untyped `Table`
132
-
133
- ```tsx
134
- import { Table } from "react-glide-table"
135
-
136
- <Table data={rows} getRowId={(row) => String(row.id)}>
137
- <Table.Header>
138
- <Table.Column field="name">Name</Table.Column>
139
- <Table.Column field="amount" sortable>
140
- Amount
141
- </Table.Column>
142
- </Table.Header>
143
- </Table>
144
- ```
145
-
146
- ### Low-level: `DataTable` + `ColumnDef`
147
-
148
- Use this when you already build TanStack column definitions yourself.
149
-
150
- ```tsx
151
- import { DataTable, type ColumnDef } from "react-glide-table"
152
-
153
- type Row = { id: string; name: string; amount: number }
72
+ Wire `rowContextValue` into your own row/cell components for edit, selection, expand, and row-span behavior. See the `playground/` app for a full reference UI built on top of `useGlideTable`.
154
73
 
155
- const columns: ColumnDef<Row, unknown>[] = [
156
- { id: "name", accessorKey: "name", header: "Name" },
157
- {
158
- id: "amount",
159
- accessorKey: "amount",
160
- header: "Amount",
161
- meta: { align: "right", editable: true, editType: "number" },
162
- },
163
- ]
74
+ ## Public API
164
75
 
165
- <DataTable data={rows} columns={columns} getRowId={(row) => row.id} />
166
- ```
167
-
168
- ---
169
-
170
- ## APIs
171
-
172
- ### `createTable<T>()` / `Table`
173
-
174
- Compound wrapper around `DataTable`. It:
175
-
176
- 1. Reads `Table.Column` children inside `Table.Header` and builds column defs
177
- 2. Applies client-side sorting when a column is `sortable`
178
- 3. Applies client-side pagination when `Table.Pagination` is present
179
- 4. Forwards the rest of the props to `DataTable`
180
-
181
- | Subcomponent | Role |
76
+ | Export | Role |
182
77
  | --- | --- |
183
- | `Table.Header` | Container for column definitions |
184
- | `Table.Column` | Declares a column (renders nothing to the DOM) |
185
- | `Table.Body` | Reserved slot (optional; columns come from `Header`) |
186
- | `Table.Pagination` | Controlled pager under the table |
187
-
188
- > Always put at least one `Table.Column` inside `Table.Header`.
189
-
190
- ---
191
-
192
- ### `Table.Column` props
193
-
194
- | Prop | Type | Default | Description |
195
- | --- | --- | --- | --- |
196
- | `field` | `string` | — | Column id / accessor key |
197
- | `virtual` | `boolean` | `false` | If `true`, no `accessorKey` (for checkbox / index columns) |
198
- | `children` | `ReactNode` | — | Header label |
199
- | `sortable` | `boolean` | `false` | Clickable sort header (asc → desc → clear) |
200
- | `width` | `number` | `150` | Column width (px) |
201
- | `align` | `"left" \| "center" \| "right"` | — | Cell / header alignment |
202
- | `rowSpan` | `boolean` | — | Enable vertical merge for this column |
203
- | `rowSpanKey` | `string` | column `field` | Field used to decide merge groups |
204
- | `editable` | `boolean` | — | Double-click to edit |
205
- | `editType` | `"text" \| "number"` | `"text"` | Input type while editing |
206
- | `className` | `string` | — | Body cell class |
207
- | `headerClassName` | `string` | — | Header cell class |
208
- | `render` | `(value, row, index) => ReactNode` | — | Custom cell renderer |
209
-
210
- ---
211
-
212
- ### Shared table props (`Table` / `DataTable`)
213
-
214
- All of these (except `columns` / `children`) work on both APIs.
215
-
216
- #### Data & identity
217
-
218
- | Prop | Type | Default | Description |
219
- | --- | --- | --- | --- |
220
- | `data` | `T[]` | — | Row data |
221
- | `getRowId` | `(row, index) => string` | index as string | Stable row id (recommended) |
222
- | `columns` | `ColumnDef[]` | — | **`DataTable` only** |
223
- | `children` | `ReactNode` | — | **`Table` only** — Header / Pagination |
224
- | `onDataChange` | `(data: T[]) => void` | — | Called after cell edit or fill |
225
-
226
- #### Loading & empty
227
-
228
- | Prop | Type | Default | Description |
229
- | --- | --- | --- | --- |
230
- | `isPending` | `boolean` | `false` | Shows a loading placeholder instead of the table |
231
- | `emptyText` | `string` | `"데이터가 없습니다."` | Message when there are no rows |
232
- | `className` | `string` | — | Root class name |
233
-
234
- #### Toolbar
235
-
236
- | Prop | Type | Description |
237
- | --- | --- | --- |
238
- | `totalCount` | `number` | Total count before filter (shown as `/ N`) |
239
- | `filteredCount` | `number` | Current visible count (defaults to current data length) |
240
- | `summary` | `ReactNode` | Left-side summary slot |
241
- | `toolbar` | `ReactNode` | Right-side actions (export, delete, …) |
242
- | `selectionLabel` | `(count) => ReactNode` | Custom selection label (default: `"N개 선택됨"`) |
243
-
244
- #### Row selection
245
-
246
- | Prop | Type | Default | Description |
247
- | --- | --- | --- | --- |
248
- | `rowSelectionMode` | `"none" \| "single" \| "multi"` | `"none"` | Selection mode |
249
- | `rowSelection` | `RowSelectionState` | — | Controlled selection map (`{ [rowId]: true }`) |
250
- | `onRowSelectionChange` | `(updater) => void` | — | Controlled selection updater |
251
- | `getRowCanSelect` | `(row, index) => boolean` | all selectable | Return `false` to disable selection for a row |
252
- | `selectOnRowClick` | `boolean` | `true` | If `false`, only checkbox / explicit controls select |
253
- | `preserveRowSelection` | `boolean` | `false` | If `true`, clicking a selected row does not deselect it |
254
- | `onRowClick` | `(row, index) => void` | — | Extra row click handler |
255
- | `getRowClassName` | `(row, index) => string \| undefined` | — | Per-row class names |
256
-
257
- ```tsx
258
- const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
259
-
260
- <ProductTable
261
- data={data}
262
- getRowId={(row) => row.id}
263
- rowSelectionMode="multi"
264
- rowSelection={rowSelection}
265
- onRowSelectionChange={setRowSelection}
266
- getRowCanSelect={(row) => row.qty > 0}
267
- selectOnRowClick={false}
268
- />
269
- ```
270
-
271
- #### Row spanning
272
-
273
- | Prop | Type | Default | Description |
274
- | --- | --- | --- | --- |
275
- | `enableRowSpan` | `boolean` | `false` | Turn on vertical cell merge |
276
-
277
- Mark columns with `rowSpan` (and optionally `rowSpanKey`):
278
-
279
- ```tsx
280
- <ProductTable data={rows} enableRowSpan getRowId={(r) => r.id}>
281
- <ProductTable.Header>
282
- <ProductTable.Column field="group" rowSpan rowSpanKey="groupId">
283
- Group
284
- </ProductTable.Column>
285
- <ProductTable.Column field="name">Name</ProductTable.Column>
286
- </ProductTable.Header>
287
- </ProductTable>
288
- ```
289
-
290
- > When `enableRowSpan` is `true`, virtualization is **forced off** so merged cells stay correct.
78
+ | `useGlideTable` | Headless table engine (TanStack instance, virtualization, selection/edit/expand state) |
79
+ | `useCellEdit` / `useCellSelection` / `useConvertTreeData` | Feature hooks |
80
+ | `applyCellEdit`, `applyFillData`, `buildColumnRowSpanMap`, | Pure helpers |
81
+ | `DEFAULT_DATA_TABLE_LABELS` / `resolveDataTableLabels` | Optional English UI copy helpers |
82
+ | Tree field defaults | `id` / `parentId` / `children` / `qty` |
291
83
 
292
- #### Tree / expandable rows
84
+ ## Notable constraints
293
85
 
294
- Enable expand UI by setting `toggleField`. Nested children are read from `flattenField` (default `assemblyMaterials`). Flat parent links use `childField` (default `assemblyCode`).
86
+ - **Row span + virtualization**: when `enableRowSpan` is on, virtualization is forced off (HTML `<table>` + `rowspan` cannot safely share a virtual window).
87
+ - **No shipped CSS / opinionated components**: bring your own DOM. The published package does not export the reference UI components under `src/components/ui` (but some feature helpers are re-exported and shipped).
295
88
 
296
- | Prop | Type | Default | Description |
297
- | --- | --- | --- | --- |
298
- | `toggleField` | `string` | — | Key used as expand id (e.g. `materialCode`). Presence enables tree mode |
299
- | `childField` | `string` | `"assemblyCode"` | Parent reference field for flat trees |
300
- | `flattenField` | `string` | `"assemblyMaterials"` | Nested children array field |
301
- | `expandedRows` | `Set<string>` | — | Controlled expanded keys |
302
- | `onExpandedRowsChange` | `(next: Set<string>) => void` | — | Controlled expand updater |
303
- | `preventExpand` | `boolean` | `false` | Always show children; hide toggle |
304
-
305
- ```tsx
306
- type BomRow = {
307
- id: string
308
- materialCode: string
309
- materialName: string
310
- assemblyMaterials?: BomRow[]
311
- }
312
-
313
- const [expandedRows, setExpandedRows] = useState<Set<string>>(() => new Set())
314
-
315
- <Table
316
- data={bom}
317
- toggleField="materialCode"
318
- flattenField="assemblyMaterials"
319
- expandedRows={expandedRows}
320
- onExpandedRowsChange={setExpandedRows}
321
- getRowId={(row) => row.id}
322
- >
323
- <Table.Header>
324
- <Table.Column field="materialCode">Code</Table.Column>
325
- <Table.Column field="materialName">Name</Table.Column>
326
- </Table.Header>
327
- </Table>
328
- ```
329
-
330
- On first load, root rows with children are expanded by default.
331
-
332
- #### Virtualization
333
-
334
- | Prop | Type | Default | Description |
335
- | --- | --- | --- | --- |
336
- | `enableVirtualization` | `boolean` | `true` | Virtualize body rows for large lists |
337
- | `estimateRowHeight` | `number` | `44` | Estimated row height (px); measured and corrected at runtime |
338
- | `virtualOverscan` | `number` | `8` | Extra rows rendered above/below the viewport |
339
-
340
- ```tsx
341
- <ProductTable
342
- data={largeDataset}
343
- enableVirtualization
344
- estimateRowHeight={44}
345
- virtualOverscan={12}
346
- getRowId={(row) => row.id}
347
- >
348
- ...
349
- </ProductTable>
350
- ```
351
-
352
- ---
353
-
354
- ## Sorting
355
-
356
- Set `sortable` on a column. Click cycle: **ascending → descending → unsorted**.
357
-
358
- Sorting is applied client-side inside `Table` / `createTable` before pagination.
359
-
360
- ```tsx
361
- <ProductTable.Column field="name" sortable>
362
- Name
363
- </ProductTable.Column>
364
- ```
365
-
366
- ---
367
-
368
- ## Pagination
369
-
370
- `Table.Pagination` is **controlled**. Pass `page`, `onChange`, and usually `pageSize` / `totalCount`.
371
-
372
- The table slices `data` client-side to the current page. The toolbar still uses `filteredCount` / `totalCount` for the full dataset size.
373
-
374
- ```tsx
375
- const [page, setPage] = useState(1)
376
-
377
- <ProductTable
378
- data={allRows}
379
- filteredCount={allRows.length}
380
- totalCount={allRows.length}
381
- getRowId={(row) => row.id}
382
- >
383
- <ProductTable.Header>
384
- <ProductTable.Column field="name">Name</ProductTable.Column>
385
- </ProductTable.Header>
386
- <ProductTable.Pagination
387
- page={page}
388
- pageSize={10}
389
- totalCount={allRows.length}
390
- onChange={setPage}
391
- />
392
- </ProductTable>
393
- ```
394
-
395
- | Prop | Type | Default | Description |
396
- | --- | --- | --- | --- |
397
- | `page` | `number` | — | Current page (1-based) |
398
- | `pageSize` | `number` | `10` | Rows per page |
399
- | `totalCount` | `number` | `0` | Total items for page math |
400
- | `onChange` | `(page: number) => void` | — | Page change handler |
401
- | `className` | `string` | — | Wrapper class |
402
-
403
- For server-side pagination, pass only the current page’s `data` and set `totalCount` / `filteredCount` from the server; keep `page` / `onChange` in sync with your fetch.
404
-
405
- ---
406
-
407
- ## Inline editing
408
-
409
- 1. Mark a column with `editable` (and optional `editType`)
410
- 2. Provide `onDataChange` so edits update your state
411
- 3. **Double-click** a cell to edit
412
- 4. Press **Enter** to commit, **Escape** to cancel
413
-
414
- ```tsx
415
- const [data, setData] = useState(rows)
416
-
417
- <ProductTable data={data} onDataChange={setData} getRowId={(r) => r.id}>
418
- <ProductTable.Header>
419
- <ProductTable.Column field="name" editable editType="text">
420
- Name
421
- </ProductTable.Column>
422
- <ProductTable.Column field="qty" editable editType="number">
423
- Qty
424
- </ProductTable.Column>
425
- </ProductTable.Header>
426
- </ProductTable>
427
- ```
428
-
429
- With `DataTable`, set the same flags on `columnDef.meta`:
430
-
431
- ```ts
432
- meta: { editable: true, editType: "number" }
433
- ```
434
-
435
- ---
436
-
437
- ## Cell selection & fill
438
-
439
- When `onDataChange` is provided, users can:
440
-
441
- 1. Click a cell to select it
442
- 2. Drag across cells to select a range
443
- 3. Use the fill handle on the selection to copy values into adjacent cells
444
-
445
- This mirrors spreadsheet-style fill behavior. Keep `data` controlled via `onDataChange`.
446
-
447
- ---
448
-
449
- ## Column meta (`DataTable` / TanStack)
450
-
451
- When using `DataTable` directly, configure columns through TanStack `ColumnDef` and `meta`:
452
-
453
- ```ts
454
- import type { ColumnDef } from "react-glide-table"
455
-
456
- const columns: ColumnDef<Row, unknown>[] = [
457
- {
458
- id: "name",
459
- accessorKey: "name",
460
- header: "Name",
461
- size: 200,
462
- meta: {
463
- align: "left",
464
- editable: true,
465
- editType: "text",
466
- rowSpan: false,
467
- className: "my-cell",
468
- headerClassName: "my-header",
469
- },
470
- },
471
- ]
472
- ```
473
-
474
- | `meta` key | Type | Description |
475
- | --- | --- | --- |
476
- | `align` | `"left" \| "center" \| "right"` | Alignment |
477
- | `className` | `string` | Body cell class |
478
- | `headerClassName` | `string` | Header class |
479
- | `editable` | `boolean` | Inline edit |
480
- | `editType` | `"text" \| "number"` | Edit input type |
481
- | `rowSpan` | `boolean` | Vertical merge |
482
- | `rowSpanKey` | `string` | Merge key field |
483
-
484
- ---
485
-
486
- ## Exports
487
-
488
- ```ts
489
- import {
490
- createTable,
491
- Table,
492
- DataTable,
493
- type TableCompoundComponent,
494
- type TableProps,
495
- type TableColumnProps,
496
- type DataTableProps,
497
- type ColumnDef,
498
- type RowSelectionState,
499
- type RowSelectionMode,
500
- } from "react-glide-table"
501
-
502
- import "react-glide-table/style.css"
503
- ```
504
-
505
- ---
506
-
507
- ## Tips
508
-
509
- 1. **Always pass `getRowId`** for stable selection, expand, and edit behavior when rows reorder or paginate.
510
- 2. Prefer **`createTable<YourRowType>()`** for end-to-end TypeScript safety.
511
- 3. Import **`react-glide-table/style.css`** or the table will be unstyled.
512
- 4. Prefer **virtualization on** for large lists; turn **`enableRowSpan` off** if you need virtualization.
513
- 5. Keep **`data` immutable** when handling `onDataChange` (replace the array / row objects).
514
- 6. For checkbox-only selection, set **`selectOnRowClick={false}`** and render a virtual checkbox column with your own UI if needed.
515
-
516
- ---
517
-
518
- ## Development
89
+ ## Local playground
519
90
 
520
91
  ```bash
521
92
  pnpm install
522
- pnpm build
93
+ pnpm dev
523
94
  ```
524
95
 
525
- Build output:
526
-
527
- - `dist/index.js` / `dist/index.cjs` — ESM / CJS bundles
528
- - `dist/index.d.ts` — TypeScript declarations
529
- - `dist/style.css` — bundled styles (`react-glide-table/style.css`)
530
-
531
- ---
96
+ The playground imports a reference `createTable` / `DataTable` UI from the repo source to exercise the headless core.
532
97
 
533
98
  ## License
534
99
 
535
- Check the repository for license information.
100
+ MIT