@iyulab/flex-table 0.56.0 → 0.56.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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.56.1] - 2026-10-07
4
+
5
+ ### Fixed
6
+
7
+ - **An edit is written to the row it was started on.** The open editor remembered a screen position and the commit
8
+ wrote whatever row stood there, so a refresh that put a row above the one being edited (or a sort while editing)
9
+ sent the typed value into another row. The editor now follows its row — drawn again in the row's new place with
10
+ what was typed and the focus — and an edit whose row left `data` (or a filter hid) is cancelled.
11
+ - **Undo and redo write the rows they changed** (cell edits, `updateRows`, paste, clear, fill, replace). They wrote
12
+ `data` positions, so after `data` changed in between they changed other rows.
13
+ - **Comments stay on their rows.** They were kept by `data` index, so inserting or deleting a row moved every comment
14
+ below it onto the neighbouring row. A deleted row's comments leave with it (and come back on undo).
15
+ - **Validation marks stay on their cells** when the view reorders while one is shown.
16
+ - `cell-edit-cancel` reports `row` as the data index, like the other edit events (it was the screen position) —
17
+ `-1` when the row is no longer in `data`.
18
+
19
+ ### Added
20
+
21
+ - `comment-change` and `getAllComments()` carry the row's `id`.
22
+
3
23
  ## [0.56.0] - 2026-10-07
4
24
 
5
25
  ### Fixed
package/README.md CHANGED
@@ -306,7 +306,7 @@ type-checks without a cast. The React wrapper's `on*` props carry the same types
306
306
  | `cell-select` | `{ row, col }` or `null` | Cell focus changed (`null` when no cell is active) |
307
307
  | `cell-edit-start` | `{ row, col, key, value }` | Cell editing started |
308
308
  | `cell-edit-commit` | `{ row, col, key, oldValue, newValue }` | Cell value committed |
309
- | `cell-edit-cancel` | `{ row, col }` | Cell edit cancelled (Escape) |
309
+ | `cell-edit-cancel` | `{ row, col }` | Cell edit cancelled (Escape, or its row left `data`). `row` is the data index (`-1` once the row is gone) |
310
310
  | `validation-error` | `{ row, col, key, value, error }` | Cell validator rejected value, or a `number` cell got text that is not a number |
311
311
 
312
312
  ### Data Events
@@ -321,7 +321,7 @@ type-checks without a cast. The React wrapper's `on*` props carry the same types
321
321
  | `data-import` | `{ count }` | Rows imported from a file |
322
322
  | `fill-handle-apply` | `{ sourceRange, targetRange, cells }` | Fill handle wrote `cells` (`{ dataRow, key, oldValue, newValue }`) |
323
323
  | `find-replace` | `{ type, cells }` | Replace (`type: 'replace'`) or replace-all from the find panel; `cells` are `{ row, col, oldValue, newValue }` with `col` the column key |
324
- | `comment-change` | `{ dataIndex, colKey, text }` | Cell comment set, changed or removed (`text: null`) |
324
+ | `comment-change` | `{ dataIndex, id, colKey, text }` | Cell comment set, changed or removed (`text: null`). Comments stay on their rows (`id`) when rows move |
325
325
 
326
326
  ### Column Events
327
327
 
@@ -1,14 +1,20 @@
1
1
  import type { CellPosition } from './selection.js';
2
+ import type { DataRow } from '../models/types.js';
2
3
  export interface EditState {
4
+ /** Where the editor is drawn — the visual cell. The grid moves it when the view moves the row. */
3
5
  position: CellPosition;
4
6
  originalValue: unknown;
7
+ /** The row being edited — the commit writes it, wherever `data` has moved it. */
8
+ row: DataRow;
9
+ /** What the editor held when the row moved and the editor was drawn again — handed to the new one. */
10
+ draft?: unknown;
5
11
  }
6
12
  /**
7
13
  * Manages cell editing state.
8
14
  */
9
15
  export declare class EditingState {
10
16
  current: EditState | null;
11
- start(position: CellPosition, originalValue: unknown): void;
17
+ start(position: CellPosition, originalValue: unknown, row: DataRow): void;
12
18
  isEditing(row: number, col: number): boolean;
13
19
  cancel(): EditState | null;
14
20
  commit(): EditState | null;
package/dist/events.d.ts CHANGED
@@ -126,8 +126,10 @@ export interface FlexTableEventMap {
126
126
  value: unknown;
127
127
  error: string;
128
128
  }>;
129
+ /** `id` — the row's id (comments stay on their rows); `dataIndex` — its position in `data` now (-1 when not loaded). */
129
130
  'comment-change': CustomEvent<{
130
131
  dataIndex: number;
132
+ id: string;
131
133
  colKey: string;
132
134
  text: string | null;
133
135
  }>;
@@ -1059,25 +1059,25 @@ function E(e, t) {
1059
1059
  //#endregion
1060
1060
  //#region src/renderers/cell-renderer.ts
1061
1061
  function D(e, t, n) {
1062
- return n.render ? n.render(e, t, n) : n.format ? typeof n.format == "function" ? n.format(e, t, n) : C(e, n.format) : O(e, n);
1062
+ return n.render ? n.render(e, t, n) : n.format ? typeof n.format == "function" ? n.format(e, t, n) : C(e, n.format) : ee(e, n);
1063
1063
  }
1064
- function O(e, t) {
1064
+ function ee(e, t) {
1065
1065
  if (e == null) return "";
1066
1066
  switch (t.type) {
1067
- case "number": return ne(e);
1067
+ case "number": return re(e);
1068
1068
  case "boolean": return a`<span class="ft-bool">${e ? "✔" : ""}</span>`;
1069
- case "date": return re(e);
1070
- case "datetime": return ie(e);
1071
- case "select": return ee(e, t);
1069
+ case "date": return ie(e);
1070
+ case "datetime": return ae(e);
1071
+ case "select": return te(e, t);
1072
1072
  default: return String(e);
1073
1073
  }
1074
1074
  }
1075
- function ee(e, t) {
1075
+ function te(e, t) {
1076
1076
  if (!t.options || t.options.length === 0 || typeof t.options[0] == "string") return e == null ? "" : String(e);
1077
1077
  let n = t.options.find((t) => t.value === e);
1078
1078
  return n ? n.label : e == null ? "" : String(e);
1079
1079
  }
1080
- var te = {
1080
+ var ne = {
1081
1081
  year: "numeric",
1082
1082
  month: "numeric",
1083
1083
  day: "numeric",
@@ -1085,18 +1085,18 @@ var te = {
1085
1085
  minute: "numeric",
1086
1086
  second: "numeric"
1087
1087
  };
1088
- function ne(e) {
1088
+ function re(e) {
1089
1089
  return typeof e == "number" ? Number.isInteger(e) ? f(e) : f(e, { minimumFractionDigits: 1 }) : String(e);
1090
1090
  }
1091
- function re(e) {
1091
+ function ie(e) {
1092
1092
  return e instanceof Date || typeof e == "string" ? d(e) : String(e);
1093
1093
  }
1094
- function ie(e) {
1095
- return e instanceof Date || typeof e == "string" ? d(e, te) : String(e);
1094
+ function ae(e) {
1095
+ return e instanceof Date || typeof e == "string" ? d(e, ne) : String(e);
1096
1096
  }
1097
1097
  //#endregion
1098
1098
  //#region src/core/selection.ts
1099
- function ae(e) {
1099
+ function oe(e) {
1100
1100
  return {
1101
1101
  startRow: Math.min(e.startRow, e.endRow),
1102
1102
  startCol: Math.min(e.startCol, e.endCol),
@@ -1104,7 +1104,7 @@ function ae(e) {
1104
1104
  endCol: Math.max(e.startCol, e.endCol)
1105
1105
  };
1106
1106
  }
1107
- var oe = class {
1107
+ var se = class {
1108
1108
  constructor() {
1109
1109
  this.activeCell = null, this.rangeAnchor = null, this.range = null, this.extraRanges = [], this._rowCount = 0, this._colCount = 0;
1110
1110
  }
@@ -1131,7 +1131,7 @@ var oe = class {
1131
1131
  return e < 0 || e >= this._rowCount || t < 0 || t >= this._colCount ? this.activeCell : (!this.rangeAnchor && this.activeCell && (this.rangeAnchor = { ...this.activeCell }), this.activeCell = {
1132
1132
  row: e,
1133
1133
  col: t
1134
- }, this.rangeAnchor && (this.range = ae({
1134
+ }, this.rangeAnchor && (this.range = oe({
1135
1135
  startRow: this.rangeAnchor.row,
1136
1136
  startCol: this.rangeAnchor.col,
1137
1137
  endRow: e,
@@ -1202,14 +1202,15 @@ var oe = class {
1202
1202
  shiftMoveRight() {
1203
1203
  return this.activeCell ? this.setActiveWithRange(this.activeCell.row, this.activeCell.col + 1) : null;
1204
1204
  }
1205
- }, se = class {
1205
+ }, ce = class {
1206
1206
  constructor() {
1207
1207
  this.current = null;
1208
1208
  }
1209
- start(e, t) {
1209
+ start(e, t, n) {
1210
1210
  this.current = {
1211
1211
  position: e,
1212
- originalValue: t
1212
+ originalValue: t,
1213
+ row: n
1213
1214
  };
1214
1215
  }
1215
1216
  isEditing(e, t) {
@@ -1223,7 +1224,7 @@ var oe = class {
1223
1224
  let e = this.current;
1224
1225
  return this.current = null, e;
1225
1226
  }
1226
- }, k = class {
1227
+ }, O = class {
1227
1228
  constructor() {
1228
1229
  this._selected = /* @__PURE__ */ new Set(), this._mode = "multi";
1229
1230
  }
@@ -1290,7 +1291,7 @@ var oe = class {
1290
1291
  };
1291
1292
  //#endregion
1292
1293
  //#region src/core/filtering.ts
1293
- function ce(e, t, n) {
1294
+ function k(e, t, n) {
1294
1295
  if (t.length === 0) return Array.from({ length: e.length }, (e, t) => t);
1295
1296
  let r = [];
1296
1297
  for (let i = 0; i < e.length; i++) {
@@ -1762,7 +1763,9 @@ var K = {
1762
1763
  lte: "≤"
1763
1764
  }, q = 120, J = 40, Y = 20, X = 32, Z = 5, Xe = 200, Q = class extends r {
1764
1765
  constructor(...e) {
1765
- super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this.rowKey = "_id", this._rowHeightFromCss = X, this._targetFloor = 0, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.emptyMessage = "", this.noMatchingMessage = "", this.error = null, this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._headerCol = null, this._focusedCellEl = null, this._editingCell = null, this._sortCriteria = [], this._selection = new oe(), this._editing = new se(), this._rowSelection = new k(), this._undo = new A(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowId = null, this._unkeyedIds = /* @__PURE__ */ new WeakMap(), this._unkeyedSeq = 0, this._visibleIdsCache = null, this._checkboxShiftPending = !1, 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._wasHeaderDrag = !1, this._wasRowDrag = !1, 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._warnedUnconstrained = !1, this._onHostFocus = (e) => {
1766
+ super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this.rowKey = "_id", this._rowHeightFromCss = X, this._targetFloor = 0, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.emptyMessage = "", this.noMatchingMessage = "", this.error = null, this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._headerCol = null, this._focusedCellEl = null, this._editingCell = null, this._sortCriteria = [], this._selection = new se(), this._editing = new ce(), this._rowSelection = new O(), this._undo = new A(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowId = null, this._unkeyedIds = /* @__PURE__ */ new WeakMap(), this._unkeyedSeq = 0, this._visibleIdsCache = null, this._checkboxShiftPending = !1, 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._wasHeaderDrag = !1, this._wasRowDrag = !1, 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._editorMoving = !1, this._onEditorBlur = (e) => {
1767
+ !this._editorMoving && e.currentTarget?.isConnected && this._commitEdit();
1768
+ }, this._warnedUnconstrained = !1, this._onHostFocus = (e) => {
1766
1769
  e.composedPath()[0] !== this || this._activeCell || this._headerCol !== null || this.visibleColumns.length !== 0 && this.matches(":focus-visible") && (this._visibleRowCount > 0 ? (this._selection.setActive(0, 0), this._syncActiveCell()) : this._headerCol = 0);
1767
1770
  }, this._onFocusIn = (e) => {
1768
1771
  let t = e.composedPath()[0];
@@ -1790,7 +1793,7 @@ var K = {
1790
1793
  }
1791
1794
  set data(e) {
1792
1795
  let t = this._data, n = e !== t;
1793
- this._data = e, n && !this._inUndoRedo && (this.clearUndoOnDataChange && this._undo.clear(), this._rowSelection.selectedCount > 0 && (this.clearSelectionOnDataChange ? (this._rowSelection.deselectAll(), 1) : this._pruneGoneUnkeyedRows()) && (this._rowSelectionVersion++, this._dispatchRowSelectionEvent())), this.requestUpdate("data", t);
1796
+ this._data = e, n && !this._inUndoRedo && (this.clearUndoOnDataChange && this._undo.clear(), this._comments.size > 0 && this._pruneGoneUnkeyedComments(), this._rowSelection.selectedCount > 0 && (this.clearSelectionOnDataChange ? (this._rowSelection.deselectAll(), 1) : this._pruneGoneUnkeyedRows()) && (this._rowSelectionVersion++, this._dispatchRowSelectionEvent())), this.requestUpdate("data", t);
1794
1797
  }
1795
1798
  get rowHeight() {
1796
1799
  let e = this._rowHeightExplicit ?? this._rowHeightFromCss ?? X;
@@ -1894,6 +1897,11 @@ var K = {
1894
1897
  ids: t
1895
1898
  }, t;
1896
1899
  }
1900
+ _pruneGoneUnkeyedComments() {
1901
+ if (![...this._comments.keys()].some((e) => e.startsWith("#"))) return;
1902
+ let e = new Set(this.data.map((e) => this.getRowId(e)));
1903
+ for (let t of [...this._comments.keys()]) t.startsWith("#") && !e.has(t) && this._comments.delete(t);
1904
+ }
1897
1905
  _pruneGoneUnkeyedRows() {
1898
1906
  if (!this._rowSelection.selectedIds.some((e) => e.startsWith("#"))) return !1;
1899
1907
  let e = new Set(this.data.map((e) => this.getRowId(e)));
@@ -2081,20 +2089,28 @@ var K = {
2081
2089
  row: this.data[e]
2082
2090
  }), this.data.splice(e, 1));
2083
2091
  if (r.length === 0) return;
2084
- r.reverse(), this._undo.push({
2092
+ r.reverse();
2093
+ let i = /* @__PURE__ */ new Map();
2094
+ for (let { row: e } of r) {
2095
+ let t = this.getRowId(e), n = this._comments.get(t);
2096
+ n && (i.set(t, n), this._comments.delete(t));
2097
+ }
2098
+ this._undo.push({
2085
2099
  label: "row-delete",
2086
2100
  undo: () => {
2087
2101
  for (let { index: e, row: t } of r) this.data.splice(e, 0, t);
2102
+ for (let [e, t] of i) this._comments.set(e, t);
2088
2103
  this.requestUpdate();
2089
2104
  },
2090
2105
  redo: () => {
2091
2106
  let e = [...r].reverse();
2092
2107
  for (let { index: t } of e) this.data.splice(t, 1);
2108
+ for (let e of i.keys()) this._comments.delete(e);
2093
2109
  this.requestUpdate();
2094
2110
  }
2095
2111
  });
2096
- let i = r.map((e) => this.getRowId(e.row)).filter((e) => this._rowSelection.isSelected(e));
2097
- if (i.length > 0 && (this._rowSelection.deselectMany(i), this._rowSelectionVersion++, this._dispatchRowSelectionEvent()), this._activeCell) {
2112
+ let a = r.map((e) => this.getRowId(e.row)).filter((e) => this._rowSelection.isSelected(e));
2113
+ if (a.length > 0 && (this._rowSelection.deselectMany(a), this._rowSelectionVersion++, this._dispatchRowSelectionEvent()), this._activeCell) {
2098
2114
  let e = this._toDataIndex(this._activeCell.row);
2099
2115
  t.includes(e) && (this._selection.clear(), this._activeCell = null);
2100
2116
  }
@@ -2105,26 +2121,28 @@ var K = {
2105
2121
  }
2106
2122
  updateRows(e) {
2107
2123
  if (e.length === 0) return;
2108
- let t = [];
2109
- for (let n of e) {
2110
- if (n.row < 0 || n.row >= this.data.length) continue;
2111
- let e = this.data[n.row][n.key];
2112
- this.data[n.row][n.key] = n.value, t.push({
2113
- row: n.row,
2114
- key: n.key,
2124
+ let t = [], n = [];
2125
+ for (let r of e) {
2126
+ if (r.row < 0 || r.row >= this.data.length) continue;
2127
+ let e = this.data[r.row][r.key];
2128
+ this.data[r.row][r.key] = r.value, t.push({
2129
+ row: r.row,
2130
+ key: r.key,
2115
2131
  oldValue: e,
2116
- newValue: n.value
2117
- });
2132
+ newValue: r.value
2133
+ }), n.push(this.data[r.row]);
2118
2134
  }
2119
2135
  t.length !== 0 && (this._undo.push({
2120
2136
  label: "batch-update",
2121
2137
  undo: () => {
2122
- for (let e of t) this.data[e.row][e.key] = e.oldValue;
2123
- this.requestUpdate();
2138
+ t.forEach((e, t) => {
2139
+ n[t][e.key] = e.oldValue;
2140
+ }), this.requestUpdate();
2124
2141
  },
2125
2142
  redo: () => {
2126
- for (let e of t) this.data[e.row][e.key] = e.newValue;
2127
- this.requestUpdate();
2143
+ t.forEach((e, t) => {
2144
+ n[t][e.key] = e.newValue;
2145
+ }), this.requestUpdate();
2128
2146
  }
2129
2147
  }), this.requestUpdate(), this._emit("batch-update", { changes: t }), this._dispatchUndoStateEvent());
2130
2148
  }
@@ -2172,14 +2190,16 @@ var K = {
2172
2190
  };
2173
2191
  }
2174
2192
  setComment(e, t, n) {
2175
- let r = this.getComment(e, t);
2176
- this._applyComment(e, t, n), this._undo.push({
2193
+ let r = this.data[e];
2194
+ if (!r) return;
2195
+ let i = this.getRowId(r), a = this._comments.get(i)?.get(t) ?? null;
2196
+ this._applyComment(i, t, n), this._undo.push({
2177
2197
  label: "comment",
2178
2198
  undo: () => {
2179
- this._applyComment(e, t, r);
2199
+ this._applyComment(i, t, a);
2180
2200
  },
2181
2201
  redo: () => {
2182
- this._applyComment(e, t, n);
2202
+ this._applyComment(i, t, n);
2183
2203
  }
2184
2204
  });
2185
2205
  }
@@ -2189,23 +2209,29 @@ var K = {
2189
2209
  let n = this._comments.get(e);
2190
2210
  n && (n.delete(t), n.size === 0 && this._comments.delete(e));
2191
2211
  }
2212
+ let r = this.data.findIndex((t) => this.getRowId(t) === e);
2192
2213
  this._emit("comment-change", {
2193
- dataIndex: e,
2214
+ dataIndex: r,
2215
+ id: e,
2194
2216
  colKey: t,
2195
2217
  text: n ?? null
2196
2218
  }), this.requestUpdate();
2197
2219
  }
2198
2220
  getComment(e, t) {
2199
- return this._comments.get(e)?.get(t) ?? null;
2221
+ let n = this.data[e];
2222
+ return n ? this._comments.get(this.getRowId(n))?.get(t) ?? null : null;
2200
2223
  }
2201
2224
  getAllComments() {
2202
2225
  let e = [];
2203
- for (let [t, n] of this._comments) for (let [r, i] of n) e.push({
2204
- dataIndex: t,
2205
- colKey: r,
2206
- text: i
2207
- });
2208
- return e;
2226
+ return this._comments.size === 0 || this.data.forEach((t, n) => {
2227
+ let r = this.getRowId(t);
2228
+ for (let [t, i] of this._comments.get(r) ?? []) e.push({
2229
+ dataIndex: n,
2230
+ id: r,
2231
+ colKey: t,
2232
+ text: i
2233
+ });
2234
+ }), e;
2209
2235
  }
2210
2236
  clearComments() {
2211
2237
  let e = new Map([...this._comments].map(([e, t]) => [e, new Map(t)]));
@@ -2391,10 +2417,10 @@ var K = {
2391
2417
  };
2392
2418
  }
2393
2419
  willUpdate(e) {
2394
- (!(e.size <= 2 && !e.has("data") && !e.has("columns") && !e.has("_openFilterKey") && !e.has("_editingCell") && !e.has("_activeCell") && (e.has("_scrollTop") || e.has("_scrollLeft"))) || this._viewDirty) && (this._recomputeView(), this._viewDirty = !1), this._updateColOffsets(), this._selection.setDimensions(this._visibleRowCount, this.visibleColumns.length);
2420
+ (!(e.size <= 2 && !e.has("data") && !e.has("columns") && !e.has("_openFilterKey") && !e.has("_editingCell") && !e.has("_activeCell") && (e.has("_scrollTop") || e.has("_scrollLeft"))) || this._viewDirty) && (this._recomputeView(), this._viewDirty = !1, this._followEditingRow()), this._updateColOffsets(), this._selection.setDimensions(this._visibleRowCount, this.visibleColumns.length);
2395
2421
  }
2396
2422
  updated(e) {
2397
- this._focusEditor(), this._syncGridFocus(), this._adjustFilterDropdown(), e.has("_headerMenu") && this._keepMenuInView(".ft-header-menu"), e.has("_bodyContextMenu") && this._bodyContextMenu && (this._keepMenuInView(".ft-body-context-menu"), this.shadowRoot?.querySelector(".ft-body-context-menu [role=\"menuitem\"]")?.focus()), e.has("stylesheets") && this._syncStylesheets();
2423
+ this._focusEditor(), this._editorMoving = !1, this._syncGridFocus(), this._adjustFilterDropdown(), e.has("_headerMenu") && this._keepMenuInView(".ft-header-menu"), e.has("_bodyContextMenu") && this._bodyContextMenu && (this._keepMenuInView(".ft-body-context-menu"), this.shadowRoot?.querySelector(".ft-body-context-menu [role=\"menuitem\"]")?.focus()), e.has("stylesheets") && this._syncStylesheets();
2398
2424
  let t = String(1 + this._visibleRowCount + +!!this.footerData), n = String(this.visibleColumns.length);
2399
2425
  if (this.getAttribute("aria-rowcount") !== t && this.setAttribute("aria-rowcount", t), this.getAttribute("aria-colcount") !== n && this.setAttribute("aria-colcount", n), this.loading ? this.getAttribute("aria-busy") !== "true" && this.setAttribute("aria-busy", "true") : this.hasAttribute("aria-busy") && this.removeAttribute("aria-busy"), e.has("_commentPopup") && this._commentPopup) {
2400
2426
  let e = this.shadowRoot?.querySelector(".ft-comment-popup textarea");
@@ -2406,7 +2432,7 @@ var K = {
2406
2432
  this._filteredIndices = Array.from({ length: this.data.length }, (e, t) => t), this._sortedIndices = this._filteredIndices;
2407
2433
  return;
2408
2434
  }
2409
- if (this._filteredIndices = ce(this.data, this._filters, (e, t, n) => {
2435
+ if (this._filteredIndices = k(this.data, this._filters, (e, t, n) => {
2410
2436
  this._emit("filter-error", {
2411
2437
  error: e,
2412
2438
  row: t,
@@ -2419,12 +2445,32 @@ var K = {
2419
2445
  this._sortedIndices = t.map((e) => this._filteredIndices[e]);
2420
2446
  }
2421
2447
  }
2448
+ _followEditingRow() {
2449
+ let e = this._editing.current;
2450
+ if (!e) return;
2451
+ let t = this.data.indexOf(e.row), n = t < 0 ? -1 : this._sortedIndices.indexOf(t);
2452
+ if (n < 0) {
2453
+ this._cancelEdit();
2454
+ return;
2455
+ }
2456
+ if (n === e.position.row) return;
2457
+ let r = this.shadowRoot?.querySelector(".ft-editor");
2458
+ r && "value" in r && (e.draft = r.value), this._editorMoving = !0, e.position = {
2459
+ row: n,
2460
+ col: e.position.col
2461
+ }, this._editingCell = { ...e.position }, this._activeCell = this._selection.setActive(n, e.position.col);
2462
+ }
2422
2463
  _toDataIndex(e) {
2423
2464
  return this._sortedIndices[e] ?? e;
2424
2465
  }
2425
2466
  _focusEditor() {
2426
2467
  if (!this._editingCell) return;
2427
- let e = this.shadowRoot?.querySelector(".ft-editor");
2468
+ let e = this.shadowRoot?.querySelector(".ft-editor"), t = this._editing.current;
2469
+ if (e && t?.draft !== void 0) {
2470
+ let n = t.draft;
2471
+ t.draft = void 0, e.value = n, e.focus(), e.type === "text" && e.setSelectionRange(e.value.length, e.value.length);
2472
+ return;
2473
+ }
2428
2474
  if (e && this.shadowRoot.activeElement !== e) {
2429
2475
  if (e.localName === "u-date-picker") {
2430
2476
  let t = e;
@@ -2545,7 +2591,7 @@ var K = {
2545
2591
  return;
2546
2592
  }
2547
2593
  let t = this._toDataIndex(this._activeCell.row), n = this.data[t];
2548
- this._editing.start(this._activeCell, n[e.key]), this._editingCell = { ...this._activeCell }, this._emit("cell-edit-start", {
2594
+ this._editing.start(this._activeCell, n[e.key], n), this._editingCell = { ...this._activeCell }, this._emit("cell-edit-start", {
2549
2595
  row: t,
2550
2596
  col: this._activeCell.col,
2551
2597
  key: e.key,
@@ -2588,12 +2634,20 @@ var K = {
2588
2634
  this._autocompleteState = null;
2589
2635
  let n = this._editing.commit();
2590
2636
  if (this._editingCell = null, !n) return;
2591
- let { row: r, col: i } = n.position, a = this._toDataIndex(r), o = this.visibleColumns[i], s = n.originalValue;
2637
+ let { col: r } = n.position, i = n.row, a = this.data.indexOf(i);
2638
+ if (a < 0) {
2639
+ this._emit("cell-edit-cancel", {
2640
+ row: -1,
2641
+ col: r
2642
+ });
2643
+ return;
2644
+ }
2645
+ let o = this.visibleColumns[r], s = n.originalValue;
2592
2646
  if (o.autocomplete === "strict" && t != null && t !== "" && !this._getAutocompleteCandidates(o, "").includes(String(t))) {
2593
2647
  let n = e("notInList");
2594
- this._markCellInvalid(r, i, () => e("notInList")), this._emit("validation-error", {
2648
+ this._markCellInvalid(i, o, () => e("notInList")), this._emit("validation-error", {
2595
2649
  row: a,
2596
- col: i,
2650
+ col: r,
2597
2651
  key: o.key,
2598
2652
  value: t,
2599
2653
  error: n
@@ -2602,9 +2656,9 @@ var K = {
2602
2656
  }
2603
2657
  if (o.type === "number" && typeof t == "string" || o.type === "date" && typeof t == "string" && p(t) === null || o.type === "datetime" && typeof t == "string" && m(t) === null) {
2604
2658
  let n = o.type === "number" ? "notANumber" : o.type === "date" ? "notADate" : "notADateTime", s = e(n);
2605
- this._markCellInvalid(r, i, () => e(n)), this._emit("validation-error", {
2659
+ this._markCellInvalid(i, o, () => e(n)), this._emit("validation-error", {
2606
2660
  row: a,
2607
- col: i,
2661
+ col: r,
2608
2662
  key: o.key,
2609
2663
  value: t,
2610
2664
  error: s
@@ -2612,11 +2666,11 @@ var K = {
2612
2666
  return;
2613
2667
  }
2614
2668
  if (o.validator) {
2615
- let e = o.validator(t, this.data[a], o);
2669
+ let e = o.validator(t, i, o);
2616
2670
  if (e) {
2617
- this._markCellInvalid(r, i, e), this._emit("validation-error", {
2671
+ this._markCellInvalid(i, o, e), this._emit("validation-error", {
2618
2672
  row: a,
2619
- col: i,
2673
+ col: r,
2620
2674
  key: o.key,
2621
2675
  value: t,
2622
2676
  error: e
@@ -2624,17 +2678,17 @@ var K = {
2624
2678
  return;
2625
2679
  }
2626
2680
  }
2627
- this.data[a][o.key] = t, this._undo.push({
2681
+ i[o.key] = t, this._undo.push({
2628
2682
  label: "cell-edit",
2629
2683
  undo: () => {
2630
- this.data[a][o.key] = s, this.requestUpdate();
2684
+ i[o.key] = s, this.requestUpdate();
2631
2685
  },
2632
2686
  redo: () => {
2633
- this.data[a][o.key] = t, this.requestUpdate();
2687
+ i[o.key] = t, this.requestUpdate();
2634
2688
  }
2635
2689
  }), this.requestUpdate(), this._emit("cell-edit-commit", {
2636
2690
  row: a,
2637
- col: i,
2691
+ col: r,
2638
2692
  key: o.key,
2639
2693
  oldValue: s,
2640
2694
  newValue: t
@@ -2644,7 +2698,7 @@ var K = {
2644
2698
  this._autocompleteState = null;
2645
2699
  let e = this._editing.cancel();
2646
2700
  this._editingCell = null, e && this._emit("cell-edit-cancel", {
2647
- row: e.position.row,
2701
+ row: this.data.indexOf(e.row),
2648
2702
  col: e.position.col
2649
2703
  });
2650
2704
  }
@@ -2929,17 +2983,24 @@ var K = {
2929
2983
  if (t.length === 0) return;
2930
2984
  let n = this._expandRowsForPaste(this._activeCell.row, t.length), r = this._applyPasteData(this._activeCell, t);
2931
2985
  if (this._selectPastedRange(this._activeCell, t), r.length > 0 || n.length > 0) {
2932
- let e = n.length;
2986
+ let e = r.map((e) => this.data[e.row]), t = n.map((e) => this.data[e]);
2933
2987
  this._undo.push({
2934
2988
  label: "paste",
2935
2989
  undo: () => {
2936
- for (let e of r) this.data[e.row][e.key] = e.oldValue;
2937
- e > 0 && this.data.splice(this.data.length - e, e), this.requestUpdate();
2990
+ r.forEach((t, n) => {
2991
+ e[n][t.key] = t.oldValue;
2992
+ });
2993
+ for (let e of t) {
2994
+ let t = this.data.indexOf(e);
2995
+ t >= 0 && this.data.splice(t, 1);
2996
+ }
2997
+ this.requestUpdate();
2938
2998
  },
2939
2999
  redo: () => {
2940
- if (e > 0) for (let t = 0; t < e; t++) this.data.push(this._createEmptyRow());
2941
- for (let e of r) this.data[e.row][e.key] = e.newValue;
2942
- this.requestUpdate();
3000
+ for (let e of t) this.data.push(e);
3001
+ r.forEach((t, n) => {
3002
+ e[n][t.key] = t.newValue;
3003
+ }), this.requestUpdate();
2943
3004
  }
2944
3005
  }), this._dispatchUndoStateEvent();
2945
3006
  }
@@ -3063,7 +3124,7 @@ var K = {
3063
3124
  r.add(i);
3064
3125
  let o = t[e], s = this.data[a][o.key], c = o.type === "boolean" ? !1 : o.type === "number" ? 0 : "";
3065
3126
  this.data[a][o.key] = c, n.push({
3066
- dataRow: a,
3127
+ target: this.data[a],
3067
3128
  key: o.key,
3068
3129
  oldValue: s,
3069
3130
  clearValue: c
@@ -3073,11 +3134,11 @@ var K = {
3073
3134
  n.length > 0 && (this._undo.push({
3074
3135
  label: "clear",
3075
3136
  undo: () => {
3076
- for (let e of n) this.data[e.dataRow][e.key] = e.oldValue;
3137
+ for (let e of n) e.target[e.key] = e.oldValue;
3077
3138
  this.requestUpdate();
3078
3139
  },
3079
3140
  redo: () => {
3080
- for (let e of n) this.data[e.dataRow][e.key] = e.clearValue;
3141
+ for (let e of n) e.target[e.key] = e.clearValue;
3081
3142
  this.requestUpdate();
3082
3143
  }
3083
3144
  }), this._dispatchUndoStateEvent()), this.requestUpdate();
@@ -3089,7 +3150,7 @@ var K = {
3089
3150
  for (let r = e.startCol; r <= e.endCol; r++) {
3090
3151
  let e = t[r], a = this.data[i][e.key], o = e.type === "boolean" ? !1 : e.type === "number" ? 0 : "";
3091
3152
  this.data[i][e.key] = o, n.push({
3092
- dataRow: i,
3153
+ target: this.data[i],
3093
3154
  key: e.key,
3094
3155
  oldValue: a,
3095
3156
  clearValue: o
@@ -3099,11 +3160,11 @@ var K = {
3099
3160
  n.length > 0 && (this._undo.push({
3100
3161
  label: "clear",
3101
3162
  undo: () => {
3102
- for (let e of n) this.data[e.dataRow][e.key] = e.oldValue;
3163
+ for (let e of n) e.target[e.key] = e.oldValue;
3103
3164
  this.requestUpdate();
3104
3165
  },
3105
3166
  redo: () => {
3106
- for (let e of n) this.data[e.dataRow][e.key] = e.clearValue;
3167
+ for (let e of n) e.target[e.key] = e.clearValue;
3107
3168
  this.requestUpdate();
3108
3169
  }
3109
3170
  }), this._dispatchUndoStateEvent()), this.requestUpdate();
@@ -3696,7 +3757,7 @@ var K = {
3696
3757
  `;
3697
3758
  }
3698
3759
  _cellKey(e, t) {
3699
- return `${e}:${t}`;
3760
+ return `${this.getRowId(e)}\u0000${t.key}`;
3700
3761
  }
3701
3762
  _markCellInvalid(e, t, n) {
3702
3763
  let r = this._cellKey(e, t), i = this._invalidCells.get(r);
@@ -3888,38 +3949,40 @@ var K = {
3888
3949
  }, this._fillDrag.targetRange = u, this.requestUpdate();
3889
3950
  }
3890
3951
  _applyFillHandle(e, t) {
3891
- let n = this.visibleColumns, r = [];
3892
- for (let i = t.startCol; i <= t.endCol; i++) {
3893
- let a = n[i], o = [];
3894
- for (let t = e.startRow; t <= e.endRow; t++) o.push(this.data[this._toDataIndex(t)][a.key]);
3895
- let s = this._detectNumericSeries(o);
3952
+ let n = this.visibleColumns, r = [], i = [];
3953
+ for (let a = t.startCol; a <= t.endCol; a++) {
3954
+ let o = n[a], s = [];
3955
+ for (let t = e.startRow; t <= e.endRow; t++) s.push(this.data[this._toDataIndex(t)][o.key]);
3956
+ let c = this._detectNumericSeries(s);
3896
3957
  for (let n = t.startRow; n <= t.endRow; n++) {
3897
3958
  if (n >= e.startRow && n <= e.endRow) continue;
3898
- let t = this._toDataIndex(n), i = this.data[t][a.key], c;
3899
- if (s && n > e.endRow) {
3900
- let t = n - e.endRow, r = s.last + s.diff * t;
3901
- c = a.type === "number" ? r : String(r);
3959
+ let t = this._toDataIndex(n), a = this.data[t][o.key], l;
3960
+ if (c && n > e.endRow) {
3961
+ let t = n - e.endRow, r = c.last + c.diff * t;
3962
+ l = o.type === "number" ? r : String(r);
3902
3963
  } else {
3903
- let t = (n - e.startRow) % o.length;
3904
- c = o[t < 0 ? t + o.length : t];
3964
+ let t = (n - e.startRow) % s.length;
3965
+ l = s[t < 0 ? t + s.length : t];
3905
3966
  }
3906
- this.data[t][a.key] = c, r.push({
3967
+ this.data[t][o.key] = l, r.push({
3907
3968
  dataRow: t,
3908
- key: a.key,
3909
- oldValue: i,
3910
- newValue: c
3911
- });
3969
+ key: o.key,
3970
+ oldValue: a,
3971
+ newValue: l
3972
+ }), i.push(this.data[t]);
3912
3973
  }
3913
3974
  }
3914
3975
  r.length > 0 && (this._undo.push({
3915
3976
  label: "fill-handle",
3916
3977
  undo: () => {
3917
- for (let e of r) this.data[e.dataRow][e.key] = e.oldValue;
3918
- this.requestUpdate();
3978
+ r.forEach((e, t) => {
3979
+ i[t][e.key] = e.oldValue;
3980
+ }), this.requestUpdate();
3919
3981
  },
3920
3982
  redo: () => {
3921
- for (let e of r) this.data[e.dataRow][e.key] = e.newValue;
3922
- this.requestUpdate();
3983
+ r.forEach((e, t) => {
3984
+ i[t][e.key] = e.newValue;
3985
+ }), this.requestUpdate();
3923
3986
  }
3924
3987
  }), this._dispatchUndoStateEvent(), this._emit("fill-handle-apply", {
3925
3988
  sourceRange: e,
@@ -4083,25 +4146,27 @@ var K = {
4083
4146
  }
4084
4147
  _replaceAll() {
4085
4148
  if (!this._findState || this._findState.results.length === 0) return;
4086
- let { results: e, replaceWith: t } = this._findState, n = this.visibleColumns, r = [];
4087
- for (let { row: i, col: a } of e) {
4088
- let e = this._toDataIndex(i), o = n[a].key, s = this.data[e][o];
4089
- this.data[e][o] = t, r.push({
4149
+ let { results: e, replaceWith: t } = this._findState, n = this.visibleColumns, r = [], i = [];
4150
+ for (let { row: a, col: o } of e) {
4151
+ let e = this._toDataIndex(a), s = n[o].key, c = this.data[e][s];
4152
+ this.data[e][s] = t, r.push({
4090
4153
  dataRow: e,
4091
- key: o,
4092
- oldValue: s,
4154
+ key: s,
4155
+ oldValue: c,
4093
4156
  newValue: t
4094
- });
4157
+ }), i.push(this.data[e]);
4095
4158
  }
4096
4159
  r.length > 0 && (this._undo.push({
4097
4160
  label: "replace-all",
4098
4161
  undo: () => {
4099
- for (let e of r) this.data[e.dataRow][e.key] = e.oldValue;
4100
- this.requestUpdate();
4162
+ r.forEach((e, t) => {
4163
+ i[t][e.key] = e.oldValue;
4164
+ }), this.requestUpdate();
4101
4165
  },
4102
4166
  redo: () => {
4103
- for (let e of r) this.data[e.dataRow][e.key] = e.newValue;
4104
- this.requestUpdate();
4167
+ r.forEach((e, t) => {
4168
+ i[t][e.key] = e.newValue;
4169
+ }), this.requestUpdate();
4105
4170
  }
4106
4171
  }), this._dispatchUndoStateEvent(), this._emit("find-replace", {
4107
4172
  type: "replace-all",
@@ -4115,21 +4180,21 @@ var K = {
4115
4180
  }
4116
4181
  _replaceOne() {
4117
4182
  if (!this._findState || this._findState.results.length === 0) return;
4118
- let { currentIndex: e, results: t, replaceWith: n } = this._findState, { row: r, col: i } = t[e], a = this._toDataIndex(r), o = this.visibleColumns[i].key, s = this.data[a][o];
4119
- this.data[a][o] = n, this._undo.push({
4183
+ let { currentIndex: e, results: t, replaceWith: n } = this._findState, { row: r, col: i } = t[e], a = this._toDataIndex(r), o = this.visibleColumns[i].key, s = this.data[a], c = s[o];
4184
+ s[o] = n, this._undo.push({
4120
4185
  label: "replace",
4121
4186
  undo: () => {
4122
- this.data[a][o] = s, this.requestUpdate();
4187
+ s[o] = c, this.requestUpdate();
4123
4188
  },
4124
4189
  redo: () => {
4125
- this.data[a][o] = n, this.requestUpdate();
4190
+ s[o] = n, this.requestUpdate();
4126
4191
  }
4127
4192
  }), this._dispatchUndoStateEvent(), this._emit("find-replace", {
4128
4193
  type: "replace",
4129
4194
  cells: [{
4130
4195
  row: a,
4131
4196
  col: o,
4132
- oldValue: s,
4197
+ oldValue: c,
4133
4198
  newValue: n
4134
4199
  }]
4135
4200
  }), this._findSearch(), this._findGoTo(Math.min(e, this._findState.results.length - 1));
@@ -4363,7 +4428,7 @@ var K = {
4363
4428
  ${this._renderEditor(e, t)}
4364
4429
  </div>
4365
4430
  `;
4366
- let v = s || l, y = this._isCellInvalid(n, r), b = (this._findState?.results ?? []).findIndex((e) => e.row === n && e.col === r), x = b >= 0, S = x && b === this._findState.currentIndex, C = [
4431
+ let v = s || l, y = this._isCellInvalid(e, t), b = (this._findState?.results ?? []).findIndex((e) => e.row === n && e.col === r), x = b >= 0, S = x && b === this._findState.currentIndex, C = [
4367
4432
  "ft-cell",
4368
4433
  `ft-type-${t.type ?? "text"}`,
4369
4434
  `ft-align-${W(t)}`,
@@ -4376,9 +4441,9 @@ var K = {
4376
4441
  i?.continues ? "ft-merge-continued" : "",
4377
4442
  i?.opensDown ? "ft-merge-open" : "",
4378
4443
  t.reveal === "hover" ? "ft-reveal-hover" : ""
4379
- ].filter(Boolean).join(" "), w = this._toDataIndex(n), T = this._comments.get(w)?.get(t.key) ?? null, E = T !== null, O = E ? a`<div class="ft-comment-indicator" title=${T}></div>` : o;
4444
+ ].filter(Boolean).join(" "), w = this._comments.size > 0 ? this._comments.get(this.getRowId(e))?.get(t.key) ?? null : null, T = w !== null, E = T ? a`<div class="ft-comment-indicator" title=${w}></div>` : o;
4380
4445
  return a`
4381
- <div class=${E ? C + " ft-has-comment" : C}
4446
+ <div class=${T ? C + " ft-has-comment" : C}
4382
4447
  role="gridcell"
4383
4448
  tabindex="-1"
4384
4449
  aria-colindex=${r + 1}
@@ -4392,7 +4457,7 @@ var K = {
4392
4457
  @mouseenter=${() => this._onCellMouseEnter(n, r)}
4393
4458
  @click=${(e) => this._onCellClickEvent(e, n, r)}
4394
4459
  @dblclick=${() => this._onCellDblClick(n, r)}>
4395
- ${O}
4460
+ ${E}
4396
4461
  ${D(e[t.key], e, t)}
4397
4462
  </div>
4398
4463
  `;
@@ -4404,7 +4469,7 @@ var K = {
4404
4469
  <input class="ft-editor ft-editor-number" type="text" inputmode="decimal" aria-label=${t.label}
4405
4470
  .value=${typeof n == "number" ? M(n) : r}
4406
4471
  @keydown=${this._onEditorKeyDown}
4407
- @blur=${() => this._commitEdit()}>
4472
+ @blur=${this._onEditorBlur}>
4408
4473
  `;
4409
4474
  if (t.type === "date" || t.type === "datetime") {
4410
4475
  let e = t.type === "datetime";
@@ -4414,7 +4479,7 @@ var K = {
4414
4479
  .value=${Ze(n, e)}
4415
4480
  @keydown=${this._onEditorKeyDown}
4416
4481
  @change=${() => this._commitEdit()}
4417
- @blur=${() => this._commitEdit()}></u-date-picker>
4482
+ @blur=${this._onEditorBlur}></u-date-picker>
4418
4483
  `;
4419
4484
  }
4420
4485
  if (t.type === "select" && t.options && t.options.length > 0) {
@@ -4422,7 +4487,7 @@ var K = {
4422
4487
  return a`
4423
4488
  <select class="ft-editor" aria-label=${t.label}
4424
4489
  @keydown=${this._onEditorKeyDown}
4425
- @blur=${() => this._commitEdit()}
4490
+ @blur=${this._onEditorBlur}
4426
4491
  @change=${() => this._commitEdit()}>
4427
4492
  ${r ? e.map((e) => a`<option value=${e} ?selected=${e === n}>${e}</option>`) : e.map((e) => a`<option value=${String(e.value)} ?selected=${e.value === n}>${e.label}</option>`)}
4428
4493
  </select>
@@ -4439,7 +4504,7 @@ var K = {
4439
4504
  .value=${r}
4440
4505
  @input=${(e) => this._onAutocompleteInput(e, t)}
4441
4506
  @keydown=${this._onEditorKeyDown}
4442
- @blur=${() => this._commitEdit()}>
4507
+ @blur=${this._onEditorBlur}>
4443
4508
  ${e.length > 0 ? a`
4444
4509
  <div class="ft-autocomplete-dropdown" id="ft-autocomplete-list" role="listbox" aria-label=${t.label}>
4445
4510
  ${e.map((e, t) => a`
@@ -4459,7 +4524,7 @@ var K = {
4459
4524
  <input class="ft-editor" type="text" aria-label=${t.label}
4460
4525
  .value=${r}
4461
4526
  @keydown=${this._onEditorKeyDown}
4462
- @blur=${() => this._commitEdit()}>
4527
+ @blur=${this._onEditorBlur}>
4463
4528
  `;
4464
4529
  }
4465
4530
  };
@@ -4534,4 +4599,4 @@ function $(e) {
4534
4599
  }
4535
4600
  }
4536
4601
  //#endregion
4537
- export { k as a, A as i, W as n, D as o, z as r, Q as t };
4602
+ export { O as a, A as i, W as n, D as o, z as r, Q as t };
@@ -175,6 +175,7 @@ export declare class FlexTable extends LitElement {
175
175
  private _columnWidths;
176
176
  private _hostResizeObserver;
177
177
  /** Keyed by dataIndex → colKey → comment text */
178
+ /** Cell comments by row id (`getRowId`) and column key — they stay on their rows when rows move. */
178
179
  private _comments;
179
180
  private _isDragOver;
180
181
  get visibleColumns(): ColumnDefinition[];
@@ -228,6 +229,8 @@ export declare class FlexTable extends LitElement {
228
229
  private _dispatchRowSelectionEvent;
229
230
  /** Ids of the rows in view, top to bottom (after filter and sort). Cached per view and `rowKey`. */
230
231
  private _visibleRowIds;
232
+ /** Drops comments on `#n` rows `data` no longer holds — such a row cannot come back. */
233
+ private _pruneGoneUnkeyedComments;
231
234
  /** Drops selected `#n` ids whose rows `data` no longer holds. Returns whether anything was dropped. */
232
235
  private _pruneGoneUnkeyedRows;
233
236
  /**
@@ -341,10 +344,11 @@ export declare class FlexTable extends LitElement {
341
344
  */
342
345
  getComment(dataIndex: number, colKey: string): string | null;
343
346
  /**
344
- * Get all comments as a flat array.
347
+ * Every comment on a row `data` holds, in `data` order — with the row's current index and its id.
345
348
  */
346
349
  getAllComments(): Array<{
347
350
  dataIndex: number;
351
+ id: string;
348
352
  colKey: string;
349
353
  text: string;
350
354
  }>;
@@ -406,8 +410,20 @@ export declare class FlexTable extends LitElement {
406
410
  protected updated(changedProps: PropertyValues): void;
407
411
  /** Recompute filter → sort pipeline. */
408
412
  private _recomputeView;
413
+ /**
414
+ * Keeps an open editor on the row it was started on after the view changed under it (a refresh put
415
+ * rows above it, a sort moved it). A row that left `data`, or that a filter hid, ends the edit.
416
+ */
417
+ private _followEditingRow;
409
418
  /** Map visual row index to data row index */
410
419
  private _toDataIndex;
420
+ /** Set while the edited row's editor is drawn again in the row's new place (cleared after that update). */
421
+ private _editorMoving;
422
+ /**
423
+ * Blur commits the edit — except the blur of the editor the update is replacing because its row
424
+ * moved (Chromium fires it while removing the element) or of one already gone.
425
+ */
426
+ private _onEditorBlur;
411
427
  private _focusEditor;
412
428
  private _measureViewport;
413
429
  private _warnedUnconstrained;
@@ -601,6 +617,7 @@ export declare class FlexTable extends LitElement {
601
617
  private _renderNumberFilter;
602
618
  /** 셀 오류 표시 — 우리 문장은 «그릴 때 찾는» 함수로(로캘 전환에 따라오게), 소비자 `validator` 문장은 그대로. */
603
619
  private _invalidCells;
620
+ /** A cell named by its row's id and its column's key — so the mark stays on the cell when the view reorders. */
604
621
  private _cellKey;
605
622
  private _markCellInvalid;
606
623
  private _isCellInvalid;
@@ -1,3 +1,3 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./flex-table-M_oC8OXD.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./flex-table-DKBcfNA4.js";
2
2
  import { t as o } from "./locale-Cfm-vJji.js";
3
3
  export { a as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, o as flexTableLocale, r as renderCell };
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./flex-table-M_oC8OXD.js";
1
+ import { t as e } from "./flex-table-DKBcfNA4.js";
2
2
  import { i as t } from "./query-Dw4iO0ir.js";
3
3
  import { t as n } from "./source--q5s2Oti.js";
4
4
  import { t as r } from "./view-DiLr-TdY.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.56.0",
3
+ "version": "0.56.1",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",
@@ -146,7 +146,7 @@ cast, and the React `on*` props carry the same types.
146
146
  | `cell-select` | `{ row, col }` (or `null`) |
147
147
  | `cell-edit-start` | `{ row, col, key, value }` |
148
148
  | `cell-edit-commit` | `{ row, col, key, oldValue, newValue }` |
149
- | `cell-edit-cancel` | `{ row, col }` |
149
+ | `cell-edit-cancel` | `{ row, col }` — `row`: data index (`-1` once the row left `data`) |
150
150
  | `validation-error` | `{ row, col, key, value, error }` |
151
151
  | `row-add` | `{ row, index }` |
152
152
  | `row-delete` | `{ indices, rows }` |
@@ -168,7 +168,7 @@ cast, and the React `on*` props carry the same types.
168
168
  | `clipboard-error` | `{ action: 'copy' \| 'paste', error }` — copy could not put the text on the clipboard (no `clipboard-copy`/`clipboard-cut` follows; a cut clears nothing), or paste could not read it |
169
169
  | `fill-handle-apply` | `{ sourceRange, targetRange, cells }` |
170
170
  | `find-replace` | `{ type: 'replace' \| 'replace-all', cells }` |
171
- | `comment-change` | `{ dataIndex, colKey, text }` |
171
+ | `comment-change` | `{ dataIndex, id, colKey, text }` |
172
172
  | `data-import` | `{ count }` |
173
173
  | `undo-state-change` | `{ canUndo, canRedo }` |
174
174
  | `context-menu` | `{ x, y, row, col, key, value, rowData }` — cancelable; `preventDefault()` suppresses the built-in menu |