@iyulab/flex-table 0.18.1 → 0.19.1

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
@@ -105,9 +105,11 @@ interface ColumnDefinition {
105
105
  sortable?: boolean; // Enable sorting (default: true)
106
106
  editable?: boolean; // Per-column edit control (follows global editable)
107
107
  pinned?: 'left' | 'right'; // Freeze column during horizontal scroll
108
+ format?: string | ((value, row, col) => string); // Display format, see "format vs renderer" below
108
109
  renderer?: CellRenderer; // Custom cell render: (value, row, col) => TemplateResult | string
109
110
  editor?: CellEditor; // Custom cell editor: (value, row, col) => TemplateResult
110
111
  validator?: CellValidator; // Validate before commit: (value, row, col) => string | null
112
+ conditionalRules?: ConditionalRule[]; // Per-cell style rules, see below
111
113
  }
112
114
  ```
113
115
 
@@ -115,6 +117,42 @@ The `editor` callback must return a Lit `TemplateResult` containing an input ele
115
117
 
116
118
  The `validator` callback returns `null` if valid, or an error message string. On failure, the cell shows a red border for 3 seconds and a `validation-error` event is dispatched.
117
119
 
120
+ ### `format` vs `renderer`
121
+
122
+ Both control how a cell's raw value is displayed, but they differ in what they replace:
123
+
124
+ - **`format`**: a plain string pattern (Excel-style, e.g. `'#,##0.00'`, `'0.00%'`, `'$#,##0'`, `'yyyy-MM-dd'`) or a `(value) => string` function. Only the *displayed text* changes — editing, sorting, filtering, and export all keep operating on the raw underlying value. Use this for number/date/currency display formatting.
125
+ - **`renderer`**: a `(value, row, col) => TemplateResult | string` function that replaces the cell's rendered content entirely — badges, links, icons, multi-field composites. Sorting/filtering still use the raw value, but the visual output is fully custom.
126
+
127
+ ```typescript
128
+ const columns: ColumnDefinition<Order>[] = [
129
+ { key: 'total', header: 'Total', format: '#,##0.00' }, // "1,234.50"
130
+ { key: 'placedAt', header: 'Placed', format: 'yyyy-MM-dd' }, // date pattern
131
+ { key: 'status', header: 'Status', renderer: (v) => html`<span class="badge badge-${v}">${v}</span>` },
132
+ ];
133
+ ```
134
+
135
+ If both are set on the same column, `renderer` takes precedence — `format` has no effect once a custom `renderer` fully controls the cell's output.
136
+
137
+ ### Conditional Formatting
138
+
139
+ `conditionalRules` applies a style to a cell when its `when` predicate matches — a declarative alternative to writing a `renderer` just to color-code status/threshold values:
140
+
141
+ ```typescript
142
+ const columns: ColumnDefinition<Order>[] = [
143
+ {
144
+ key: 'status',
145
+ header: 'Status',
146
+ conditionalRules: [
147
+ { when: (v) => v === 'overdue', style: { color: '#dc2626', fontWeight: 'bold' } },
148
+ { when: (v) => v === 'paid', style: { color: '#16a34a' } },
149
+ ],
150
+ },
151
+ ];
152
+ ```
153
+
154
+ Rules are evaluated in order and combined; later matching rules override earlier ones for overlapping style properties.
155
+
118
156
  ## Methods
119
157
 
120
158
  ### Row Operations
@@ -144,6 +182,14 @@ The `validator` callback returns `null` if valid, or an error message string. On
144
182
  | `deselectAll()` | `void` | Deselect all rows |
145
183
  | `getSelectedRows()` | `{ selectedIndices, selectedRows }` | Get selected row data |
146
184
 
185
+ Row selection is index-based (there is no row-key concept), so replacing `data` with a same-length but different set of rows leaves the selection pointing at the new rows occupying the old indices. If selection drives a bulk action (status changes, bulk delete, etc.), set `clear-selection-on-data-change` so a `data` swap always resets selection and re-fires `selection-change` with an empty selection:
186
+
187
+ ```html
188
+ <flex-table selectable clear-selection-on-data-change></flex-table>
189
+ ```
190
+
191
+ Default is `false`, matching `clear-undo-on-data-change`.
192
+
147
193
  ### Filtering
148
194
 
149
195
  | Method | Returns | Description |
@@ -304,6 +350,50 @@ function App() {
304
350
 
305
351
  All `<flex-table>` properties are available as React props, and all custom events are mapped to `on*` callbacks (e.g., `cell-edit-commit` → `onCellEditCommit`).
306
352
 
353
+ #### Imperative API via `ref`
354
+
355
+ `FlexTableReact` forwards `ref` to the underlying `FlexTable` custom element instance, so all [Methods](#methods) (`addRow`, `deleteRows`, `selectAll`, `setFilter`, etc.) are reachable without re-rendering the whole table:
356
+
357
+ ```tsx
358
+ import { useRef } from 'react';
359
+ import { FlexTableReact, type FlexTable } from '@iyulab/flex-table/react';
360
+
361
+ function App() {
362
+ const tableRef = useRef<FlexTable>(null);
363
+
364
+ return (
365
+ <>
366
+ <button onClick={() => tableRef.current?.addRow({ name: '', age: 0 })}>Add row</button>
367
+ <button onClick={() => tableRef.current?.deleteRows()}>Delete selected</button>
368
+ <FlexTableReact ref={tableRef} columns={columns} data={data} selectable />
369
+ </>
370
+ );
371
+ }
372
+ ```
373
+
374
+ #### Typed rows (generics)
375
+
376
+ `FlexTableReact` and `ColumnDefinition` are generic over your row type — no `as unknown as` casts needed in `data`, `columns`, or callbacks:
377
+
378
+ ```tsx
379
+ import { FlexTableReact, type ColumnDefinition } from '@iyulab/flex-table/react';
380
+
381
+ interface Order {
382
+ id: string;
383
+ total: number;
384
+ currency: string;
385
+ }
386
+
387
+ const columns: ColumnDefinition<Order>[] = [
388
+ { key: 'id', header: 'ID' },
389
+ { key: 'total', header: 'Total', renderer: (_value, row) => `${row.total} ${row.currency}` },
390
+ ];
391
+
392
+ <FlexTableReact<Order> data={orders} columns={columns} />
393
+ ```
394
+
395
+ Omitting the type argument defaults to the previous `DataRow` (`Record<string, unknown>`) behavior — fully backward compatible.
396
+
307
397
  ### Custom Editor
308
398
 
309
399
  The `editor` callback lets you provide a fully custom editing UI. The component reads `.value` from the element with class `ft-editor` when committing.
@@ -409,6 +499,31 @@ table.addEventListener('sort-change', (e) => {
409
499
  });
410
500
  ```
411
501
 
502
+ ### OData Source Hook (React)
503
+
504
+ `useODataSource(url, options)` fetches paginated/sorted/filtered data from an OData v4 endpoint and returns props ready to bind to `<FlexTableReact dataMode="server" ...>`.
505
+
506
+ ```tsx
507
+ import { useODataSource } from '@iyulab/flex-table/odata';
508
+
509
+ const source = useODataSource('/api/orders', {
510
+ pageSize: 20,
511
+ fetcher: httpClient.fetch, // custom transport, e.g. an HttpClient instance
512
+ onUnauthorized: () => navigate('/login'),
513
+ });
514
+ ```
515
+
516
+ | Option | Default | Description |
517
+ |---|---|---|
518
+ | `pageSize` | `20` | Rows per page |
519
+ | `defaultOrderBy` | — | Initial `$orderby` (e.g. `'name asc'`) |
520
+ | `fixedFilter` | — | Filter always applied in addition to search |
521
+ | `baseUrl` | `window.location.origin` | Override the request origin (proxy/BFF setups) |
522
+ | `fetcher` | global `fetch` | Custom transport — pass a wrapper that injects auth headers |
523
+ | `onUnauthorized` | — | Called on `401`/`403` responses, before the generic error is set |
524
+
525
+ `fetcher`/`onUnauthorized` should be stable references (e.g. wrap in `useCallback`) — they are intentionally excluded from the hook's internal effect dependencies to avoid refetch loops on every render.
526
+
412
527
  ## Development
413
528
 
414
529
  ```bash
@@ -1480,7 +1480,7 @@ var Y = {
1480
1480
  lte: "≤"
1481
1481
  }, X = 120, Z = 40, xe = 32, Q = 5, $ = class extends e {
1482
1482
  constructor(...e) {
1483
- super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.rowHeight = xe, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new y(), this._editing = new b(), this._rowSelection = new x(), this._undo = new D(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._onCommentPopupOutsideClick = () => {
1483
+ super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this.rowHeight = xe, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new y(), this._editing = new b(), this._rowSelection = new x(), this._undo = new D(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._onCommentPopupOutsideClick = () => {
1484
1484
  this._commitCommentPopup();
1485
1485
  }, this._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map(), this._emptyFilterState = /* @__PURE__ */ new Map();
1486
1486
  }
@@ -1492,7 +1492,7 @@ var Y = {
1492
1492
  }
1493
1493
  set data(e) {
1494
1494
  let t = this._data;
1495
- this._data = e, this.clearUndoOnDataChange && !this._inUndoRedo && this._undo.clear(), this.requestUpdate("data", t);
1495
+ this._data = e, this.clearUndoOnDataChange && !this._inUndoRedo && this._undo.clear(), this.clearSelectionOnDataChange && !this._inUndoRedo && this._rowSelection.selectedCount > 0 && (this._rowSelection.deselectAll(), this._rowSelectionVersion++, this._dispatchRowSelectionEvent()), this.requestUpdate("data", t);
1496
1496
  }
1497
1497
  set selectionMode(e) {
1498
1498
  this._rowSelection.mode = e, this.requestUpdate();
@@ -3922,6 +3922,9 @@ J([a({ type: Array })], $.prototype, "columns", void 0), J([a({
3922
3922
  type: Boolean,
3923
3923
  attribute: "clear-undo-on-data-change"
3924
3924
  })], $.prototype, "clearUndoOnDataChange", void 0), J([a({
3925
+ type: Boolean,
3926
+ attribute: "clear-selection-on-data-change"
3927
+ })], $.prototype, "clearSelectionOnDataChange", void 0), J([a({
3925
3928
  type: Number,
3926
3929
  attribute: "row-height"
3927
3930
  })], $.prototype, "rowHeight", void 0), J([a({
@@ -23,6 +23,18 @@ export declare class FlexTable extends LitElement {
23
23
  * Default: false. Internal undo/redo operations are never affected.
24
24
  */
25
25
  clearUndoOnDataChange: boolean;
26
+ /**
27
+ * When true, replacing `data` externally automatically clears row selection
28
+ * (checkbox selection) and re-dispatches `selection-change` with an empty selection.
29
+ * Default: false, for consistency with `clearUndoOnDataChange`. Internal undo/redo
30
+ * operations are never affected.
31
+ *
32
+ * Row selection is index-based (no row-key concept), so a full `data` replacement can
33
+ * leave selection pointing at different underlying rows at the same indices —
34
+ * recommended for any `selectable` grid whose selection drives bulk actions
35
+ * (e.g. server-mode grids refreshed via `useODataSource`).
36
+ */
37
+ clearSelectionOnDataChange: boolean;
26
38
  rowHeight: number;
27
39
  showRowNumbers: boolean;
28
40
  theme: 'light' | 'dark' | undefined;
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, r, t as i } from "./flex-table-BS_ADOUI.js";
1
+ import { a as e, i as t, n, r, t as i } from "./flex-table-BQc2PU5u.js";
2
2
  export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
@@ -14,17 +14,17 @@ export interface SelectOption {
14
14
  * Receives the cell value, the full row data, and the column definition.
15
15
  * Returns either a Lit TemplateResult or a plain string.
16
16
  */
17
- export type CellRenderer = (value: unknown, row: DataRow, col: ColumnDefinition) => TemplateResult | string;
17
+ export type CellRenderer<T = DataRow> = (value: unknown, row: T, col: ColumnDefinition<T>) => TemplateResult | string;
18
18
  /**
19
19
  * Custom cell editor function.
20
20
  * Receives the cell value, the full row data, and the column definition.
21
21
  * Should return a Lit TemplateResult containing an input element with class "ft-editor".
22
22
  */
23
- export type CellEditor = (value: unknown, row: DataRow, col: ColumnDefinition) => TemplateResult;
23
+ export type CellEditor<T = DataRow> = (value: unknown, row: T, col: ColumnDefinition<T>) => TemplateResult;
24
24
  /**
25
25
  * Cell validator function. Returns null/undefined if valid, or an error message string.
26
26
  */
27
- export type CellValidator = (value: unknown, row: DataRow, col: ColumnDefinition) => string | null | undefined;
27
+ export type CellValidator<T = DataRow> = (value: unknown, row: T, col: ColumnDefinition<T>) => string | null | undefined;
28
28
  /**
29
29
  * Row selection mode.
30
30
  */
@@ -37,8 +37,14 @@ export type SelectionMode = 'single' | 'multi';
37
37
  export type DataMode = 'client' | 'server';
38
38
  /**
39
39
  * Definition of a single column in the table.
40
+ *
41
+ * `T` is the consumer's row type (defaults to the schema-agnostic `DataRow`).
42
+ * Internally, `FlexTable` (the registered custom element) always operates on
43
+ * `ColumnDefinition<DataRow>` — a custom element cannot itself be generic across
44
+ * instances, so `FlexTableReact<T>` performs a single internal cast at the
45
+ * React boundary instead of requiring consumers to cast at every callback.
40
46
  */
41
- export interface ColumnDefinition {
47
+ export interface ColumnDefinition<T = DataRow> {
42
48
  /** Unique key matching data property names */
43
49
  key: string;
44
50
  /** Display header text */
@@ -54,15 +60,15 @@ export interface ColumnDefinition {
54
60
  /** Whether the column is sortable (default: true) */
55
61
  sortable?: boolean;
56
62
  /** Custom cell renderer — overrides built-in type rendering */
57
- renderer?: CellRenderer;
63
+ renderer?: CellRenderer<T>;
58
64
  /** Whether the column is editable (default: true — follows global editable setting) */
59
65
  editable?: boolean;
60
66
  /** Custom cell editor — overrides built-in type editing */
61
- editor?: CellEditor;
67
+ editor?: CellEditor<T>;
62
68
  /** Pin the column to one side during horizontal scroll */
63
69
  pinned?: 'left' | 'right';
64
70
  /** Cell validator — called before committing edits */
65
- validator?: CellValidator;
71
+ validator?: CellValidator<T>;
66
72
  /** Allowed values for select columns (strings or label/value pairs) */
67
73
  options?: string[] | SelectOption[];
68
74
  /**
@@ -76,9 +82,9 @@ export interface ColumnDefinition {
76
82
  * - String: number format pattern (e.g. '#,##0.00', '0.00%', '$#,##0') or date pattern (e.g. 'yyyy-MM-dd')
77
83
  * - Function: custom formatter receiving (value, row, col)
78
84
  */
79
- format?: string | ((value: unknown, row: DataRow, col: ColumnDefinition) => string);
85
+ format?: string | ((value: unknown, row: T, col: ColumnDefinition<T>) => string);
80
86
  /** Per-column conditional formatting rules applied during cell rendering */
81
- conditionalRules?: ConditionalRule[];
87
+ conditionalRules?: ConditionalRule<T>[];
82
88
  }
83
89
  /** Style applied to a cell by a conditional formatting rule */
84
90
  export interface CellStyle {
@@ -88,9 +94,9 @@ export interface CellStyle {
88
94
  fontStyle?: 'italic' | 'normal';
89
95
  }
90
96
  /** A single conditional formatting rule */
91
- export interface ConditionalRule {
97
+ export interface ConditionalRule<T = DataRow> {
92
98
  /** Returns true when this rule's style should be applied */
93
- when: (value: unknown, row: DataRow, col: ColumnDefinition) => boolean;
99
+ when: (value: unknown, row: T, col: ColumnDefinition<T>) => boolean;
94
100
  style: CellStyle;
95
101
  }
96
102
  /**
@@ -2,27 +2,28 @@ import { useCallback as e, useEffect as t, useRef as n, useState as r } from "re
2
2
  import i from "odata-query";
3
3
  //#region src/odata/use-odata-source.ts
4
4
  function a(a, s = {}) {
5
- let { pageSize: c = 20, defaultOrderBy: l, fixedFilter: u } = s, d = u ? JSON.stringify(u) : "", [f, p] = r([]), [m, h] = r(0), [g, _] = r(!1), [v, y] = r(null), [b, x] = r(0), [S, C] = r(() => l ? o(l) : []), [w, T] = r(""), [E, D] = r(0), O = n(null), k = e(() => {
6
- D((e) => e + 1);
7
- }, []), A = e((e) => {
8
- T(e), x(0);
9
- }, []), j = e((e) => {
5
+ let { pageSize: c = 20, defaultOrderBy: l, fixedFilter: u, baseUrl: d, fetcher: f = fetch, onUnauthorized: p } = s, m = u ? JSON.stringify(u) : "", [h, g] = r([]), [_, v] = r(0), [y, b] = r(!1), [x, S] = r(null), [C, w] = r(0), [T, E] = r(() => l ? o(l) : []), [D, O] = r(""), [k, A] = r(0), j = n(null), M = e(() => {
6
+ A((e) => e + 1);
7
+ }, []), N = e((e) => {
8
+ O(e), w(0);
9
+ }, []), P = e((e) => {
10
10
  let t = e.detail?.criteria;
11
- t && (C(t), x(0));
11
+ t && (E(t), w(0));
12
12
  }, []);
13
13
  return t(() => {
14
- O.current?.abort();
14
+ j.current?.abort();
15
15
  let e = new AbortController();
16
- O.current = e, _(!0), y(null);
17
- let t = S.length > 0 ? S.map((e) => `${e.key} ${e.direction}`).join(", ") : l, n = {
16
+ j.current = e, b(!0), S(null);
17
+ let t = T.length > 0 ? T.map((e) => `${e.key} ${e.direction}`).join(", ") : l, n = {
18
18
  top: c,
19
- skip: b * c,
19
+ skip: C * c,
20
20
  count: !0
21
21
  };
22
- t && (n.orderBy = t), u && (n.filter = u), w && (n.search = w);
23
- let r = i(n), o = `${window.location.origin}${a}${r}`;
24
- return fetch(o, { signal: e.signal }).then(async (e) => {
22
+ t && (n.orderBy = t), u && (n.filter = u), D && (n.search = D);
23
+ let r = i(n);
24
+ return f(`${d ?? window.location.origin}${a}${r}`, { signal: e.signal }).then(async (e) => {
25
25
  if (!e.ok) {
26
+ (e.status === 401 || e.status === 403) && p && p(e);
26
27
  let t = await e.text().catch(() => ""), n = `요청 실패 (${e.status})`;
27
28
  try {
28
29
  let e = JSON.parse(t);
@@ -32,33 +33,34 @@ function a(a, s = {}) {
32
33
  }
33
34
  return e.json();
34
35
  }).then((t) => {
35
- e.signal.aborted || (p(t.value ?? t), h(t["@odata.count"] ?? 0), y(null));
36
+ e.signal.aborted || (g(t.value ?? t), v(t["@odata.count"] ?? 0), S(null));
36
37
  }).catch((t) => {
37
- e.signal.aborted || t.name !== "AbortError" && (y(t.message), p([]), h(0));
38
+ e.signal.aborted || t.name !== "AbortError" && (S(t.message), g([]), v(0));
38
39
  }).finally(() => {
39
- e.signal.aborted || _(!1);
40
+ e.signal.aborted || b(!1);
40
41
  }), () => e.abort();
41
42
  }, [
42
43
  a,
43
- b,
44
+ C,
44
45
  c,
45
- S,
46
- w,
47
- d,
46
+ T,
47
+ D,
48
+ m,
48
49
  l,
49
- E
50
+ k,
51
+ d
50
52
  ]), {
51
- data: f,
52
- totalCount: m,
53
- loading: g,
54
- error: v,
55
- page: b,
56
- setPage: x,
57
- sortCriteria: S,
58
- onSortChange: j,
59
- search: w,
60
- setSearch: A,
61
- refresh: k
53
+ data: h,
54
+ totalCount: _,
55
+ loading: y,
56
+ error: x,
57
+ page: C,
58
+ setPage: w,
59
+ sortCriteria: T,
60
+ onSortChange: P,
61
+ search: D,
62
+ setSearch: N,
63
+ refresh: M
62
64
  };
63
65
  }
64
66
  function o(e) {
@@ -3,6 +3,12 @@ export interface UseODataSourceOptions {
3
3
  pageSize?: number;
4
4
  defaultOrderBy?: string;
5
5
  fixedFilter?: Record<string, unknown>;
6
+ /** 기본값: `window.location.origin`. 프록시/BFF 등 다른 origin으로 요청해야 할 때 지정. */
7
+ baseUrl?: string;
8
+ /** 커스텀 fetch transport(예: 인증 헤더를 주입하는 `HttpClient` 래퍼). 기본값: 전역 `fetch`. */
9
+ fetcher?: (input: string, init: RequestInit) => Promise<Response>;
10
+ /** 응답이 401/403일 때 호출(세션 만료 리다이렉트 등). 호출 후에도 기존 에러 처리는 계속 진행된다. */
11
+ onUnauthorized?: (response: Response) => void;
6
12
  }
7
13
  export interface UseODataSourceResult<T> {
8
14
  data: T[];
package/dist/react.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import React from 'react';
1
2
  import { type EventName } from '@lit/react';
2
3
  import { FlexTable } from './flex-table.js';
3
- export declare const FlexTableReact: import("@lit/react").ReactWebComponent<FlexTable, {
4
+ import type { ColumnDefinition, DataRow } from './models/types.js';
5
+ declare const FlexTableReactBase: import("@lit/react").ReactWebComponent<FlexTable, {
4
6
  onCellSelect: EventName<CustomEvent>;
5
7
  onCellEditCommit: EventName<CustomEvent>;
6
8
  onCellEditCancel: EventName<CustomEvent>;
@@ -25,5 +27,33 @@ export declare const FlexTableReact: import("@lit/react").ReactWebComponent<Flex
25
27
  onContextMenu: EventName<CustomEvent>;
26
28
  onFilterError: EventName<CustomEvent>;
27
29
  }>;
30
+ type BaseProps = React.ComponentProps<typeof FlexTableReactBase>;
31
+ /**
32
+ * Props for {@link FlexTableReact}, parameterized on the consumer's row type `T`
33
+ * (defaults to `DataRow` — identical to the previous non-generic behavior).
34
+ */
35
+ export type FlexTableReactProps<T = DataRow> = Omit<BaseProps, 'data' | 'columns'> & {
36
+ data?: T[];
37
+ columns?: ColumnDefinition<T>[];
38
+ };
39
+ /**
40
+ * React wrapper for the `<flex-table>` custom element, generic over the row type `T`.
41
+ *
42
+ * The underlying custom element (`FlexTable`) is a single registered class and cannot
43
+ * itself be generic across instances — the DOM has no notion of `FlexTable<Order>` vs
44
+ * `FlexTable<Consumer>`. `FlexTableReact<T>` performs one internal cast at this boundary
45
+ * so consumers get end-to-end type safety (`data`, `columns`, `renderer`/`editor`/`validator`
46
+ * callbacks) without casting at every call site.
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * const columns: ColumnDefinition<Order>[] = [
51
+ * { key: 'id', header: 'ID' },
52
+ * { key: 'total', header: 'Total', renderer: (v, row) => `${row.total} ${row.currency}` },
53
+ * ];
54
+ * <FlexTableReact<Order> data={orders} columns={columns} />
55
+ * ```
56
+ */
57
+ export declare const FlexTableReact: <T = DataRow>(props: FlexTableReactProps<T> & React.RefAttributes<FlexTable>) => React.ReactElement | null;
28
58
  export type { FlexTable };
29
- export type { ColumnDefinition, DataRow, ColumnType, CellRenderer, CellEditor, CellValidator, SelectionMode, DataMode } from './models/types.js';
59
+ export type { ColumnDefinition, DataRow, ColumnType, CellRenderer, CellEditor, CellValidator, ConditionalRule, SelectionMode, DataMode } from './models/types.js';
package/dist/react.js CHANGED
@@ -1,7 +1,6 @@
1
- import { t as e } from "./flex-table-BS_ADOUI.js";
1
+ import { t as e } from "./flex-table-BQc2PU5u.js";
2
2
  import t from "react";
3
3
  import { createComponent as n } from "@lit/react";
4
- //#region src/react.ts
5
4
  var r = n({
6
5
  tagName: "flex-table",
7
6
  elementClass: e,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.18.1",
3
+ "version": "0.19.1",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",