@zvndev/yable-react 0.1.1 → 0.2.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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Table as Table$1, RowData, Cell, Header, Row, TableOptions, ColumnDef, SortDirection, CellContext, ClipboardOptions, CellFlashInfo } from '@zvndev/yable-core';
2
- export { AccessorFnColumnDef, AccessorKeyColumnDef, AggregationFn, Cell, CellStatus as CellCommitStatus, CellContext, CellFlashEvent, CellPatch, ClipboardOptions, Column, ColumnDef, ColumnDefBase, ColumnFiltersState, ColumnMeta, ColumnOrderState, ColumnPinningState, ColumnSizingInfoState, ColumnSizingState, CommitError, CommitErrorCells, CommitRecord, CommitResult, CommitsSlice, DisplayColumnDef, EditingState, ExpandedState, FillHandleState, FilterFn, FormulaState, GroupColumnDef, Header, HeaderContext, HeaderGroup, KeyboardNavigationAction, KeyboardNavigationCell, KeyboardNavigationDirection, KeyboardNavigationState, OnChangeFn, OnCommitFn, PaginationState, PartialLocale, PivotConfig, PivotState, Row, RowData, RowDragEndEvent, RowDragEvent, RowDragState, RowEditCommitEvent, RowEditEvent, RowPinningState, RowReorderEvent, RowSelectionState, SortDirection, SortingFn, SortingState, Table as TableInstance, TableOptions, TableOptionsResolved, TableState, UndoAction, UndoRedoState, Updater, VisibilityState, YableLocale, aggregationFns, createColumnHelper, createLocale, en, filterFns, functionalUpdate, getDefaultLocale, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
2
+ export { AccessorFnColumnDef, AccessorKeyColumnDef, AggregationFn, Cell, CellStatus as CellCommitStatus, CellContext, CellFlashEvent, CellPatch, ClipboardOptions, Column, ColumnDef, ColumnDefBase, ColumnFiltersState, ColumnMeta, ColumnOrderState, ColumnPinningState, ColumnSizingInfoState, ColumnSizingState, CommitError, CommitErrorCells, CommitRecord, CommitResult, CommitsSlice, DisplayColumnDef, EditingState, ExpandedState, FillHandleState, FilterFn, FormulaEngine, FormulaError, FormulaFunction, FormulaState, GroupColumnDef, Header, HeaderContext, HeaderGroup, KeyboardNavigationAction, KeyboardNavigationCell, KeyboardNavigationDirection, KeyboardNavigationState, OnChangeFn, OnCommitFn, PaginationState, PartialLocale, PivotColumn, PivotConfig, PivotEngine, PivotFieldConfig, PivotRow, PivotState, PivotValueConfig, Row, RowData, RowDragEndEvent, RowDragEvent, RowDragState, RowEditCommitEvent, RowEditEvent, RowPinningState, RowReorderEvent, RowSelectionState, SortDirection, SortingFn, SortingState, Table as TableInstance, TableOptions, TableOptionsResolved, TableState, UndoAction, UndoRedoOptions, UndoRedoState, UndoStack, Updater, VisibilityState, YableLocale, aggregationFns, createColumnHelper, createLocale, createUndoRedoIntegration, en, filterFns, formulaFunctions, functionalUpdate, generatePivotColumnDefs, getDefaultLocale, getInitialPivotState, getPivotRowModel, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -75,6 +75,20 @@ interface TableHeaderCellProps<TData extends RowData, TValue = unknown> extends
75
75
  header: Header<TData, TValue>;
76
76
  }
77
77
 
78
+ /**
79
+ * React hook that wraps the framework-agnostic core table engine.
80
+ *
81
+ * Returns a stable {@link Table} instance whose `state` reflects either
82
+ * the consumer's controlled `options.state` or the hook's internal state.
83
+ *
84
+ * Re-renders are triggered by `setState` from the default `onStateChange`
85
+ * implementation. Consumers may pass their own `onStateChange` to control
86
+ * state externally.
87
+ *
88
+ * Gotcha: `options.onStateChange` is captured via a latest-ref, so a fresh
89
+ * function identity from the parent is always invoked — even if every other
90
+ * option key is shallow-equal to the previous render.
91
+ */
78
92
  declare function useTable<TData extends RowData>(options: TableOptions<TData>): Table$1<TData>;
79
93
 
80
94
  interface VirtualRow {
@@ -104,7 +118,12 @@ interface UseVirtualizationResult {
104
118
  * Computes which rows are visible in a scrollable container and returns
105
119
  * positioning data so only those rows (plus an overscan buffer) are rendered.
106
120
  *
107
- * Supports both fixed and variable row heights.
121
+ * Returns `{ virtualRows, totalHeight, startIndex, endIndex, scrollTo }`.
122
+ * Re-renders are triggered by scroll (rAF-throttled) and ResizeObserver
123
+ * container resize. Supports fixed, variable, and Pretext-pre-measured heights.
124
+ *
125
+ * Gotcha: variable-height mode caches measured heights per index — pass new
126
+ * `pretextHeights` arrays (or change `totalRows`) to invalidate the cache.
108
127
  */
109
128
  declare function useVirtualization({ containerRef, totalRows, rowHeight, overscan, estimateRowHeight: _estimateRowHeight, pretextHeights, pretextPrefixSums, }: UseVirtualizationOptions): UseVirtualizationResult;
110
129
 
@@ -154,11 +173,15 @@ interface UsePretextMeasurementResult {
154
173
  }
155
174
  /**
156
175
  * Pre-computes exact pixel heights for every row using Pretext's
157
- * DOM-free text measurement. Returns typed arrays for cache-friendly
158
- * access by the virtualizer.
176
+ * DOM-free text measurement.
177
+ *
178
+ * Returns `{ rowHeights, prefixSums, totalHeight, ready, ... }` as typed
179
+ * arrays for cache-friendly access by the virtualizer. `prepare()` re-runs
180
+ * when `data` or fonts change; `layout()` re-runs only when column widths
181
+ * change (pure math, ~0.0003ms per cell).
159
182
  *
160
- * layout() is pure arithmetic (~0.0003ms per cell) so column resizing
161
- * re-computes all heights near-instantly.
183
+ * Gotcha: pretext is loaded lazily `ready` flips to true on the first
184
+ * render after the dynamic import resolves and heights are computed.
162
185
  */
163
186
  declare function usePretextMeasurement({ data, columns, getCellText, minRowHeight, enabled, }: UsePretextMeasurementOptions): UsePretextMeasurementResult;
164
187
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Table as Table$1, RowData, Cell, Header, Row, TableOptions, ColumnDef, SortDirection, CellContext, ClipboardOptions, CellFlashInfo } from '@zvndev/yable-core';
2
- export { AccessorFnColumnDef, AccessorKeyColumnDef, AggregationFn, Cell, CellStatus as CellCommitStatus, CellContext, CellFlashEvent, CellPatch, ClipboardOptions, Column, ColumnDef, ColumnDefBase, ColumnFiltersState, ColumnMeta, ColumnOrderState, ColumnPinningState, ColumnSizingInfoState, ColumnSizingState, CommitError, CommitErrorCells, CommitRecord, CommitResult, CommitsSlice, DisplayColumnDef, EditingState, ExpandedState, FillHandleState, FilterFn, FormulaState, GroupColumnDef, Header, HeaderContext, HeaderGroup, KeyboardNavigationAction, KeyboardNavigationCell, KeyboardNavigationDirection, KeyboardNavigationState, OnChangeFn, OnCommitFn, PaginationState, PartialLocale, PivotConfig, PivotState, Row, RowData, RowDragEndEvent, RowDragEvent, RowDragState, RowEditCommitEvent, RowEditEvent, RowPinningState, RowReorderEvent, RowSelectionState, SortDirection, SortingFn, SortingState, Table as TableInstance, TableOptions, TableOptionsResolved, TableState, UndoAction, UndoRedoState, Updater, VisibilityState, YableLocale, aggregationFns, createColumnHelper, createLocale, en, filterFns, functionalUpdate, getDefaultLocale, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
2
+ export { AccessorFnColumnDef, AccessorKeyColumnDef, AggregationFn, Cell, CellStatus as CellCommitStatus, CellContext, CellFlashEvent, CellPatch, ClipboardOptions, Column, ColumnDef, ColumnDefBase, ColumnFiltersState, ColumnMeta, ColumnOrderState, ColumnPinningState, ColumnSizingInfoState, ColumnSizingState, CommitError, CommitErrorCells, CommitRecord, CommitResult, CommitsSlice, DisplayColumnDef, EditingState, ExpandedState, FillHandleState, FilterFn, FormulaEngine, FormulaError, FormulaFunction, FormulaState, GroupColumnDef, Header, HeaderContext, HeaderGroup, KeyboardNavigationAction, KeyboardNavigationCell, KeyboardNavigationDirection, KeyboardNavigationState, OnChangeFn, OnCommitFn, PaginationState, PartialLocale, PivotColumn, PivotConfig, PivotEngine, PivotFieldConfig, PivotRow, PivotState, PivotValueConfig, Row, RowData, RowDragEndEvent, RowDragEvent, RowDragState, RowEditCommitEvent, RowEditEvent, RowPinningState, RowReorderEvent, RowSelectionState, SortDirection, SortingFn, SortingState, Table as TableInstance, TableOptions, TableOptionsResolved, TableState, UndoAction, UndoRedoOptions, UndoRedoState, UndoStack, Updater, VisibilityState, YableLocale, aggregationFns, createColumnHelper, createLocale, createUndoRedoIntegration, en, filterFns, formulaFunctions, functionalUpdate, generatePivotColumnDefs, getDefaultLocale, getInitialPivotState, getPivotRowModel, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -75,6 +75,20 @@ interface TableHeaderCellProps<TData extends RowData, TValue = unknown> extends
75
75
  header: Header<TData, TValue>;
76
76
  }
77
77
 
78
+ /**
79
+ * React hook that wraps the framework-agnostic core table engine.
80
+ *
81
+ * Returns a stable {@link Table} instance whose `state` reflects either
82
+ * the consumer's controlled `options.state` or the hook's internal state.
83
+ *
84
+ * Re-renders are triggered by `setState` from the default `onStateChange`
85
+ * implementation. Consumers may pass their own `onStateChange` to control
86
+ * state externally.
87
+ *
88
+ * Gotcha: `options.onStateChange` is captured via a latest-ref, so a fresh
89
+ * function identity from the parent is always invoked — even if every other
90
+ * option key is shallow-equal to the previous render.
91
+ */
78
92
  declare function useTable<TData extends RowData>(options: TableOptions<TData>): Table$1<TData>;
79
93
 
80
94
  interface VirtualRow {
@@ -104,7 +118,12 @@ interface UseVirtualizationResult {
104
118
  * Computes which rows are visible in a scrollable container and returns
105
119
  * positioning data so only those rows (plus an overscan buffer) are rendered.
106
120
  *
107
- * Supports both fixed and variable row heights.
121
+ * Returns `{ virtualRows, totalHeight, startIndex, endIndex, scrollTo }`.
122
+ * Re-renders are triggered by scroll (rAF-throttled) and ResizeObserver
123
+ * container resize. Supports fixed, variable, and Pretext-pre-measured heights.
124
+ *
125
+ * Gotcha: variable-height mode caches measured heights per index — pass new
126
+ * `pretextHeights` arrays (or change `totalRows`) to invalidate the cache.
108
127
  */
109
128
  declare function useVirtualization({ containerRef, totalRows, rowHeight, overscan, estimateRowHeight: _estimateRowHeight, pretextHeights, pretextPrefixSums, }: UseVirtualizationOptions): UseVirtualizationResult;
110
129
 
@@ -154,11 +173,15 @@ interface UsePretextMeasurementResult {
154
173
  }
155
174
  /**
156
175
  * Pre-computes exact pixel heights for every row using Pretext's
157
- * DOM-free text measurement. Returns typed arrays for cache-friendly
158
- * access by the virtualizer.
176
+ * DOM-free text measurement.
177
+ *
178
+ * Returns `{ rowHeights, prefixSums, totalHeight, ready, ... }` as typed
179
+ * arrays for cache-friendly access by the virtualizer. `prepare()` re-runs
180
+ * when `data` or fonts change; `layout()` re-runs only when column widths
181
+ * change (pure math, ~0.0003ms per cell).
159
182
  *
160
- * layout() is pure arithmetic (~0.0003ms per cell) so column resizing
161
- * re-computes all heights near-instantly.
183
+ * Gotcha: pretext is loaded lazily `ready` flips to true on the first
184
+ * render after the dynamic import resolves and heights are computed.
162
185
  */
163
186
  declare function usePretextMeasurement({ data, columns, getCellText, minRowHeight, enabled, }: UsePretextMeasurementOptions): UsePretextMeasurementResult;
164
187
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { canCellEnterEditMode, functionalUpdate, createTable, getFirstKeyboardCell, getResolvedFocusedCell, detectCellChanges } from '@zvndev/yable-core';
2
- export { CommitError, aggregationFns, createColumnHelper, createLocale, en, filterFns, functionalUpdate, getDefaultLocale, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
1
+ import { canCellEnterEditMode, functionalUpdate, createTable, getDefaultLocale, getFirstKeyboardCell, getResolvedFocusedCell, detectCellChanges } from '@zvndev/yable-core';
2
+ export { CommitError, FormulaEngine, FormulaError, PivotEngine, UndoStack, aggregationFns, createColumnHelper, createLocale, createUndoRedoIntegration, en, filterFns, formulaFunctions, functionalUpdate, generatePivotColumnDefs, getDefaultLocale, getInitialPivotState, getPivotRowModel, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
3
3
  import React3, { createContext, useCallback, useMemo, useState, useRef, useEffect, useContext } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { getInitialRowDragState, moveRow } from '@zvndev/yable-core/features/rowDragging';
@@ -64,6 +64,8 @@ function useTable(options) {
64
64
  prevOptionsRef.current = options;
65
65
  return options;
66
66
  }, [options]);
67
+ const onStateChangeRef = useRef(options.onStateChange);
68
+ onStateChangeRef.current = options.onStateChange;
67
69
  const resolvedState = useMemo(
68
70
  () => ({
69
71
  ...state,
@@ -73,13 +75,14 @@ function useTable(options) {
73
75
  );
74
76
  const onStateChange = useCallback(
75
77
  (updater) => {
76
- if (stableOptions.onStateChange) {
77
- stableOptions.onStateChange(updater);
78
+ const latest = onStateChangeRef.current;
79
+ if (latest) {
80
+ latest(updater);
78
81
  } else {
79
82
  setState((prev) => functionalUpdate(updater, prev));
80
83
  }
81
84
  },
82
- [stableOptions.onStateChange]
85
+ []
83
86
  );
84
87
  const resolvedOptions = useMemo(
85
88
  () => ({
@@ -1715,19 +1718,20 @@ function Spinner() {
1715
1718
  function LoadingOverlay({
1716
1719
  loading,
1717
1720
  loadingComponent,
1718
- loadingText = "Loading..."
1721
+ loadingText
1719
1722
  }) {
1720
1723
  if (!loading) return null;
1724
+ const resolvedText = loadingText ?? getDefaultLocale().loadingText;
1721
1725
  return /* @__PURE__ */ jsx(
1722
1726
  "div",
1723
1727
  {
1724
1728
  className: "yable-overlay-loading",
1725
1729
  role: "alert",
1726
1730
  "aria-busy": "true",
1727
- "aria-label": loadingText,
1731
+ "aria-label": resolvedText,
1728
1732
  children: /* @__PURE__ */ jsx("div", { className: "yable-overlay-loading-content", children: loadingComponent ?? /* @__PURE__ */ jsxs(Fragment, { children: [
1729
1733
  /* @__PURE__ */ jsx(Spinner, {}),
1730
- loadingText && /* @__PURE__ */ jsx("span", { className: "yable-overlay-loading-text", children: loadingText })
1734
+ resolvedText && /* @__PURE__ */ jsx("span", { className: "yable-overlay-loading-text", children: resolvedText })
1731
1735
  ] }) })
1732
1736
  }
1733
1737
  );
@@ -1845,8 +1849,9 @@ function NoRowsOverlay({
1845
1849
  if (emptyComponent) {
1846
1850
  return /* @__PURE__ */ jsx("div", { className: "yable-overlay-empty", children: emptyComponent });
1847
1851
  }
1848
- const defaultMessage = isFiltered ? "No results found" : "No data";
1849
- const defaultDetail = isFiltered ? "Try adjusting your search or filter criteria." : "There are no rows to display.";
1852
+ const locale = getDefaultLocale();
1853
+ const defaultMessage = isFiltered ? locale.emptyNoResults : locale.emptyNoData;
1854
+ const defaultDetail = isFiltered ? locale.emptyNoResultsDetail : locale.emptyNoDataDetail;
1850
1855
  const icon = emptyIcon ?? (isFiltered ? /* @__PURE__ */ jsx(DefaultFilteredIcon, {}) : /* @__PURE__ */ jsx(DefaultEmptyIcon, {}));
1851
1856
  return /* @__PURE__ */ jsxs("div", { className: "yable-overlay-empty", role: "status", children: [
1852
1857
  /* @__PURE__ */ jsx("div", { className: "yable-overlay-empty-icon-wrapper", children: icon }),
@@ -2835,7 +2840,7 @@ function Table({
2835
2840
  loading,
2836
2841
  loadingComponent,
2837
2842
  loadingText,
2838
- emptyMessage = "No data",
2843
+ emptyMessage,
2839
2844
  emptyComponent,
2840
2845
  emptyIcon,
2841
2846
  emptyDetail,
@@ -2987,9 +2992,10 @@ function Pagination({
2987
2992
  const to = Math.min((pageIndex + 1) * pageSize, totalRows);
2988
2993
  const canPrev = table.getCanPreviousPage();
2989
2994
  const canNext = table.getCanNextPage();
2995
+ const locale = getDefaultLocale();
2990
2996
  return /* @__PURE__ */ jsxs("nav", { className: "yable-pagination", role: "navigation", "aria-label": "Table pagination", children: [
2991
2997
  showInfo && /* @__PURE__ */ jsxs("div", { className: "yable-pagination-info", children: [
2992
- /* @__PURE__ */ jsx("span", { className: "yable-pagination-info-text", children: totalRows > 0 ? `${from}\u2013${to} of ${totalRows}` : "No results" }),
2998
+ /* @__PURE__ */ jsx("span", { className: "yable-pagination-info-text", children: totalRows > 0 ? `${from}\u2013${to} ${locale.paginationOf} ${totalRows}` : locale.paginationNoResults }),
2993
2999
  showPageSize && /* @__PURE__ */ jsxs("div", { className: "yable-pagination-select-wrapper", children: [
2994
3000
  /* @__PURE__ */ jsx(
2995
3001
  "select",
@@ -3002,7 +3008,8 @@ function Pagination({
3002
3008
  "aria-label": "Rows per page",
3003
3009
  children: pageSizes.map((size) => /* @__PURE__ */ jsxs("option", { value: size, children: [
3004
3010
  size,
3005
- " rows"
3011
+ " ",
3012
+ locale.paginationRows
3006
3013
  ] }, size))
3007
3014
  }
3008
3015
  ),
@@ -3017,8 +3024,8 @@ function Pagination({
3017
3024
  className: "yable-pagination-btn yable-pagination-btn--nav",
3018
3025
  onClick: () => table.setPageIndex(0),
3019
3026
  disabled: !canPrev,
3020
- "aria-label": "First page",
3021
- title: "First page",
3027
+ "aria-label": locale.paginationFirstPage,
3028
+ title: locale.paginationFirstPage,
3022
3029
  children: /* @__PURE__ */ jsx(ChevronFirstIcon, {})
3023
3030
  }
3024
3031
  ),
@@ -3029,8 +3036,8 @@ function Pagination({
3029
3036
  className: "yable-pagination-btn yable-pagination-btn--nav",
3030
3037
  onClick: () => table.previousPage(),
3031
3038
  disabled: !canPrev,
3032
- "aria-label": "Previous page",
3033
- title: "Previous page",
3039
+ "aria-label": locale.paginationPreviousPage,
3040
+ title: locale.paginationPreviousPage,
3034
3041
  children: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
3035
3042
  }
3036
3043
  ),
@@ -3054,7 +3061,7 @@ function Pagination({
3054
3061
  className: `yable-pagination-btn yable-pagination-btn--page${page === pageIndex ? " yable-pagination-btn--active" : ""}`,
3055
3062
  "data-active": page === pageIndex ? "true" : void 0,
3056
3063
  onClick: () => table.setPageIndex(page),
3057
- "aria-label": `Page ${page + 1}`,
3064
+ "aria-label": `${locale.paginationPage} ${page + 1}`,
3058
3065
  "aria-current": page === pageIndex ? "page" : void 0,
3059
3066
  children: page + 1
3060
3067
  },
@@ -3068,8 +3075,8 @@ function Pagination({
3068
3075
  className: "yable-pagination-btn yable-pagination-btn--nav",
3069
3076
  onClick: () => table.nextPage(),
3070
3077
  disabled: !canNext,
3071
- "aria-label": "Next page",
3072
- title: "Next page",
3078
+ "aria-label": locale.paginationNextPage,
3079
+ title: locale.paginationNextPage,
3073
3080
  children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
3074
3081
  }
3075
3082
  ),
@@ -3080,8 +3087,8 @@ function Pagination({
3080
3087
  className: "yable-pagination-btn yable-pagination-btn--nav",
3081
3088
  onClick: () => table.setPageIndex(pageCount - 1),
3082
3089
  disabled: !canNext,
3083
- "aria-label": "Last page",
3084
- title: "Last page",
3090
+ "aria-label": locale.paginationLastPage,
3091
+ title: locale.paginationLastPage,
3085
3092
  children: /* @__PURE__ */ jsx(ChevronLastIcon, {})
3086
3093
  }
3087
3094
  )
@@ -3130,10 +3137,12 @@ function ClearIcon() {
3130
3137
  }
3131
3138
  function GlobalFilter({
3132
3139
  table,
3133
- placeholder = "Search...",
3140
+ placeholder,
3134
3141
  debounce = 300,
3135
3142
  className
3136
3143
  }) {
3144
+ const locale = getDefaultLocale();
3145
+ const resolvedPlaceholder = placeholder ?? locale.searchPlaceholder;
3137
3146
  const [value, setValue] = useState(
3138
3147
  table.getState().globalFilter ?? ""
3139
3148
  );
@@ -3190,8 +3199,8 @@ function GlobalFilter({
3190
3199
  value,
3191
3200
  onChange: handleChange,
3192
3201
  onKeyDown: handleKeyDown,
3193
- placeholder,
3194
- "aria-label": "Search table",
3202
+ placeholder: resolvedPlaceholder,
3203
+ "aria-label": locale.searchAriaLabel,
3195
3204
  role: "searchbox"
3196
3205
  }
3197
3206
  ),
@@ -3278,10 +3287,25 @@ function CellSelect({
3278
3287
  const isAlwaysEditable = cell.getIsAlwaysEditable();
3279
3288
  const pending = table.getPendingValue(row.id, column.id);
3280
3289
  const currentValue = pending !== void 0 ? pending : cell.getValue();
3290
+ const commitTimerRef = useRef(null);
3291
+ useEffect(() => {
3292
+ return () => {
3293
+ if (commitTimerRef.current !== null) {
3294
+ clearTimeout(commitTimerRef.current);
3295
+ commitTimerRef.current = null;
3296
+ }
3297
+ };
3298
+ }, []);
3281
3299
  const handleChange = (e) => {
3282
3300
  table.setPendingValue(row.id, column.id, e.target.value);
3283
3301
  if (isEditing && !isAlwaysEditable) {
3284
- setTimeout(() => table.commitEdit(), 0);
3302
+ if (commitTimerRef.current !== null) {
3303
+ clearTimeout(commitTimerRef.current);
3304
+ }
3305
+ commitTimerRef.current = setTimeout(() => {
3306
+ commitTimerRef.current = null;
3307
+ table.commitEdit();
3308
+ }, 0);
3285
3309
  }
3286
3310
  };
3287
3311
  return /* @__PURE__ */ jsxs(