@iyulab/flex-table 0.14.0 → 0.15.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.
@@ -174,6 +174,14 @@ var s = t`
174
174
 
175
175
  .ft-body { position: relative; }
176
176
 
177
+ .ft-frozen-rows {
178
+ display: block;
179
+ position: sticky;
180
+ z-index: 3;
181
+ border-bottom: 2px solid var(--ft-active-color);
182
+ background: var(--ft-bg);
183
+ }
184
+
177
185
  .ft-row {
178
186
  display: block;
179
187
  position: absolute;
@@ -353,7 +361,7 @@ var s = t`
353
361
  position: absolute;
354
362
  top: 100%;
355
363
  left: 0;
356
- min-width: 160px;
364
+ min-width: 200px;
357
365
  background: var(--ft-bg);
358
366
  border: 1px solid var(--ft-border-color);
359
367
  border-radius: 4px;
@@ -407,6 +415,51 @@ var s = t`
407
415
  color: var(--ft-text-color);
408
416
  }
409
417
 
418
+ .ft-filter-mode-row {
419
+ display: flex;
420
+ gap: 4px;
421
+ margin-bottom: 4px;
422
+ }
423
+
424
+ .ft-filter-mode-select {
425
+ flex: 1;
426
+ padding: 3px 6px;
427
+ font: inherit;
428
+ font-size: 12px;
429
+ border: 1px solid var(--ft-border-color);
430
+ border-radius: 3px;
431
+ color: var(--ft-text-color);
432
+ background: var(--ft-editor-bg);
433
+ cursor: pointer;
434
+ }
435
+
436
+ .ft-num-cond-row {
437
+ display: flex;
438
+ gap: 4px;
439
+ margin-bottom: 4px;
440
+ }
441
+
442
+ .ft-num-op-select {
443
+ flex: 0 0 44px;
444
+ }
445
+
446
+ .ft-num-cond-input {
447
+ flex: 1;
448
+ }
449
+
450
+ .ft-filter-empty-row {
451
+ margin-top: 6px;
452
+ padding-top: 6px;
453
+ border-top: 1px solid var(--ft-border-color);
454
+ }
455
+
456
+ .ft-filter-empty-row label {
457
+ display: block;
458
+ font-size: 11px;
459
+ color: var(--ft-sort-indicator-color);
460
+ margin-bottom: 3px;
461
+ }
462
+
410
463
  /* --- Row Selection (Checkbox) --- */
411
464
 
412
465
  .ft-checkbox-header,
@@ -923,7 +976,7 @@ function ee(e, t, n) {
923
976
  default: return String(e).localeCompare(String(t));
924
977
  }
925
978
  }
926
- function C(e, t, n) {
979
+ function te(e, t, n) {
927
980
  let r = e.find((e) => e.key === t);
928
981
  return n ? r ? r.direction === "asc" ? e.map((e) => e.key === t ? {
929
982
  ...e,
@@ -941,7 +994,7 @@ function C(e, t, n) {
941
994
  }
942
995
  //#endregion
943
996
  //#region src/core/filtering.ts
944
- function w(e, t, n) {
997
+ function ne(e, t, n) {
945
998
  if (t.length === 0) return Array.from({ length: e.length }, (e, t) => t);
946
999
  let r = [];
947
1000
  for (let i = 0; i < e.length; i++) {
@@ -960,9 +1013,9 @@ function w(e, t, n) {
960
1013
  }
961
1014
  //#endregion
962
1015
  //#region src/core/undo.ts
963
- var T = 100, E = class {
1016
+ var C = 100, w = class {
964
1017
  constructor() {
965
- this._undoStack = [], this._redoStack = [], this._maxSize = T;
1018
+ this._undoStack = [], this._redoStack = [], this._maxSize = C;
966
1019
  }
967
1020
  get maxSize() {
968
1021
  return this._maxSize;
@@ -999,7 +1052,7 @@ var T = 100, E = class {
999
1052
  };
1000
1053
  //#endregion
1001
1054
  //#region src/clipboard/clipboard.ts
1002
- function te(e, t, n) {
1055
+ function T(e, t, n) {
1003
1056
  let r = [];
1004
1057
  for (let i = n.startRow; i <= n.endRow; i++) {
1005
1058
  let a = e[i], o = [];
@@ -1011,7 +1064,7 @@ function te(e, t, n) {
1011
1064
  }
1012
1065
  return r.join("\n");
1013
1066
  }
1014
- function ne(e) {
1067
+ function E(e) {
1015
1068
  let t = e.replace(/\r\n/g, "\n").replace(/\r/g, "\n"), n = [], r = [], i = 0, a = t.length;
1016
1069
  for (; i <= a;) {
1017
1070
  if (i === a) {
@@ -1062,56 +1115,56 @@ var re = (() => {
1062
1115
  }
1063
1116
  return e;
1064
1117
  })();
1065
- function O(e) {
1118
+ function ie(e) {
1066
1119
  let t = 4294967295;
1067
1120
  for (let n = 0; n < e.length; n++) t = re[(t ^ e[n]) & 255] ^ t >>> 8;
1068
1121
  return (t ^ 4294967295) >>> 0;
1069
1122
  }
1070
- var k = new TextEncoder();
1071
- function A(e) {
1123
+ var O = new TextEncoder();
1124
+ function k(e) {
1072
1125
  let t = [], n = [], r = 0;
1073
1126
  for (let i of e) {
1074
- let e = k.encode(i.name), a = O(i.data), o = i.data.length, s = new ArrayBuffer(30 + e.length), c = new DataView(s);
1127
+ let e = O.encode(i.name), a = ie(i.data), o = i.data.length, s = new ArrayBuffer(30 + e.length), c = new DataView(s);
1075
1128
  c.setUint32(0, 67324752, !0), c.setUint16(4, 20, !0), c.setUint16(6, 0, !0), c.setUint16(8, 0, !0), c.setUint16(10, 0, !0), c.setUint16(12, 33, !0), c.setUint32(14, a, !0), c.setUint32(18, o, !0), c.setUint32(22, o, !0), c.setUint16(26, e.length, !0), c.setUint16(28, 0, !0), new Uint8Array(s, 30).set(e), t.push(new Uint8Array(s)), t.push(i.data);
1076
1129
  let l = new ArrayBuffer(46 + e.length), u = new DataView(l);
1077
1130
  u.setUint32(0, 33639248, !0), u.setUint16(4, 20, !0), u.setUint16(6, 20, !0), u.setUint16(8, 0, !0), u.setUint16(10, 0, !0), u.setUint16(12, 0, !0), u.setUint16(14, 33, !0), u.setUint32(16, a, !0), u.setUint32(20, o, !0), u.setUint32(24, o, !0), u.setUint16(28, e.length, !0), u.setUint16(30, 0, !0), u.setUint16(32, 0, !0), u.setUint16(34, 0, !0), u.setUint16(36, 0, !0), u.setUint32(38, 0, !0), u.setUint32(42, r, !0), new Uint8Array(l, 46).set(e), n.push(new Uint8Array(l)), r += 30 + e.length + o;
1078
1131
  }
1079
- let i = j(n), a = /* @__PURE__ */ new ArrayBuffer(22), o = new DataView(a);
1080
- return o.setUint32(0, 101010256, !0), o.setUint16(4, 0, !0), o.setUint16(6, 0, !0), o.setUint16(8, e.length, !0), o.setUint16(10, e.length, !0), o.setUint32(12, i.length, !0), o.setUint32(16, r, !0), o.setUint16(20, 0, !0), j([
1132
+ let i = A(n), a = /* @__PURE__ */ new ArrayBuffer(22), o = new DataView(a);
1133
+ return o.setUint32(0, 101010256, !0), o.setUint16(4, 0, !0), o.setUint16(6, 0, !0), o.setUint16(8, e.length, !0), o.setUint16(10, e.length, !0), o.setUint32(12, i.length, !0), o.setUint32(16, r, !0), o.setUint16(20, 0, !0), A([
1081
1134
  ...t,
1082
1135
  i,
1083
1136
  new Uint8Array(a)
1084
1137
  ]);
1085
1138
  }
1086
- function j(e) {
1139
+ function A(e) {
1087
1140
  let t = e.reduce((e, t) => e + t.length, 0), n = new Uint8Array(t), r = 0;
1088
1141
  for (let t of e) n.set(t, r), r += t.length;
1089
1142
  return n;
1090
1143
  }
1091
- function M(e) {
1144
+ function j(e) {
1092
1145
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1093
1146
  }
1094
- function N(e) {
1147
+ function ae(e) {
1095
1148
  let t = "";
1096
1149
  for (e++; e > 0;) t = String.fromCharCode(64 + (e % 26 || 26)) + t, e = Math.floor((e - 1) / 26);
1097
1150
  return t;
1098
1151
  }
1099
- function P(e, t) {
1100
- return N(t) + (e + 1);
1152
+ function M(e, t) {
1153
+ return ae(t) + (e + 1);
1101
1154
  }
1102
- function F(e) {
1155
+ function N(e) {
1103
1156
  let t = Date.UTC(1899, 11, 30);
1104
1157
  return (e.getTime() - t) / 864e5;
1105
1158
  }
1106
- var I = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n <Default Extension=\"xml\" ContentType=\"application/xml\"/>\n <Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>\n <Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>\n <Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/>\n</Types>", L = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>\n</Relationships>", R = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n <sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\"/></sheets>\n</workbook>", z = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>\n <Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>\n</Relationships>", B = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\n <numFmts count=\"1\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/></numFmts>\n <fonts count=\"2\">\n <font><sz val=\"11\"/><name val=\"Calibri\"/></font>\n <font><sz val=\"11\"/><b/><name val=\"Calibri\"/></font>\n </fonts>\n <fills count=\"2\">\n <fill><patternFill patternType=\"none\"/></fill>\n <fill><patternFill patternType=\"gray125\"/></fill>\n </fills>\n <borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders>\n <cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>\n <cellXfs count=\"3\">\n <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>\n <xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/>\n <xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/>\n </cellXfs>\n</styleSheet>";
1107
- function V(e, t) {
1108
- let n = [], r = t.map((e, t) => `<c r="${P(0, t)}" t="str" s="2"><v>${M(e.header)}</v></c>`);
1159
+ var P = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n <Default Extension=\"xml\" ContentType=\"application/xml\"/>\n <Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>\n <Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>\n <Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/>\n</Types>", F = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>\n</Relationships>", I = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">\n <sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\"/></sheets>\n</workbook>", L = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>\n <Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>\n</Relationships>", R = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\n <numFmts count=\"1\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/></numFmts>\n <fonts count=\"2\">\n <font><sz val=\"11\"/><name val=\"Calibri\"/></font>\n <font><sz val=\"11\"/><b/><name val=\"Calibri\"/></font>\n </fonts>\n <fills count=\"2\">\n <fill><patternFill patternType=\"none\"/></fill>\n <fill><patternFill patternType=\"gray125\"/></fill>\n </fills>\n <borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders>\n <cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>\n <cellXfs count=\"3\">\n <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>\n <xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/>\n <xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/>\n </cellXfs>\n</styleSheet>";
1160
+ function z(e, t) {
1161
+ let n = [], r = t.map((e, t) => `<c r="${M(0, t)}" t="str" s="2"><v>${j(e.header)}</v></c>`);
1109
1162
  return n.push(`<row r="1">${r.join("")}</row>`), e.forEach((e, r) => {
1110
1163
  let i = t.map((t, n) => {
1111
- let i = P(r + 1, n), a = e[t.key];
1164
+ let i = M(r + 1, n), a = e[t.key];
1112
1165
  if (a == null) return `<c r="${i}" t="str"><v></v></c>`;
1113
1166
  let o = t.type ?? "text";
1114
- return o === "number" && typeof a == "number" ? `<c r="${i}"><v>${a}</v></c>` : (o === "date" || o === "datetime") && a instanceof Date ? `<c r="${i}" s="1"><v>${F(a).toFixed(6)}</v></c>` : o === "boolean" ? `<c r="${i}" t="b"><v>${+!!a}</v></c>` : `<c r="${i}" t="str"><v>${M(String(a))}</v></c>`;
1167
+ return o === "number" && typeof a == "number" ? `<c r="${i}"><v>${a}</v></c>` : (o === "date" || o === "datetime") && a instanceof Date ? `<c r="${i}" s="1"><v>${N(a).toFixed(6)}</v></c>` : o === "boolean" ? `<c r="${i}" t="b"><v>${+!!a}</v></c>` : `<c r="${i}" t="str"><v>${j(String(a))}</v></c>`;
1115
1168
  });
1116
1169
  n.push(`<row r="${r + 2}">${i.join("")}</row>`);
1117
1170
  }), `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -1119,55 +1172,55 @@ function V(e, t) {
1119
1172
  <sheetData>${n.join("")}</sheetData>
1120
1173
  </worksheet>`;
1121
1174
  }
1122
- function H(e, t) {
1123
- let n = V(e, t);
1124
- return A([
1175
+ function B(e, t) {
1176
+ let n = z(e, t);
1177
+ return k([
1125
1178
  {
1126
1179
  name: "[Content_Types].xml",
1127
- data: k.encode(I)
1180
+ data: O.encode(P)
1128
1181
  },
1129
1182
  {
1130
1183
  name: "_rels/.rels",
1131
- data: k.encode(L)
1184
+ data: O.encode(F)
1132
1185
  },
1133
1186
  {
1134
1187
  name: "xl/workbook.xml",
1135
- data: k.encode(R)
1188
+ data: O.encode(I)
1136
1189
  },
1137
1190
  {
1138
1191
  name: "xl/_rels/workbook.xml.rels",
1139
- data: k.encode(z)
1192
+ data: O.encode(L)
1140
1193
  },
1141
1194
  {
1142
1195
  name: "xl/worksheets/sheet1.xml",
1143
- data: k.encode(n)
1196
+ data: O.encode(n)
1144
1197
  },
1145
1198
  {
1146
1199
  name: "xl/styles.xml",
1147
- data: k.encode(B)
1200
+ data: O.encode(R)
1148
1201
  }
1149
1202
  ]);
1150
1203
  }
1151
1204
  //#endregion
1152
1205
  //#region src/export/export.ts
1153
- function U(e, t, n) {
1206
+ function V(e, t, n) {
1154
1207
  switch (n) {
1155
- case "csv": return G(e, t, ",");
1156
- case "tsv": return G(e, t, " ");
1157
- case "json": return q(e, t);
1158
- case "xlsx": return H(e, t);
1208
+ case "csv": return U(e, t, ",");
1209
+ case "tsv": return U(e, t, " ");
1210
+ case "json": return G(e, t);
1211
+ case "xlsx": return B(e, t);
1159
1212
  }
1160
1213
  }
1161
- function W(e, t) {
1214
+ function H(e, t) {
1162
1215
  return e == null ? "" : (t.type === "date" || t.type === "datetime") && e instanceof Date ? e.toISOString() : String(e);
1163
1216
  }
1164
- function G(e, t, n) {
1165
- return [t.map((e) => K(e.header, n)).join(n), ...e.map((e) => t.map((t) => K(W(e[t.key], t), n)).join(n))].join("\n");
1217
+ function U(e, t, n) {
1218
+ return [t.map((e) => W(e.header, n)).join(n), ...e.map((e) => t.map((t) => W(H(e[t.key], t), n)).join(n))].join("\n");
1166
1219
  }
1167
- function K(e, t) {
1220
+ function W(e, t) {
1168
1221
  return e.includes(t) || e.includes("\"") || e.includes("\n") || e.includes("\r") ? "\"" + e.replace(/"/g, "\"\"") + "\"" : e;
1169
1222
  }
1170
- function q(e, t) {
1223
+ function G(e, t) {
1171
1224
  let n = e.map((e) => {
1172
1225
  let n = {};
1173
1226
  for (let r of t) {
@@ -1178,30 +1231,30 @@ function q(e, t) {
1178
1231
  });
1179
1232
  return JSON.stringify(n, null, 2);
1180
1233
  }
1181
- function J(e, t, n) {
1234
+ function K(e, t, n) {
1182
1235
  let r = new Blob([e], { type: n }), i = URL.createObjectURL(r), a = document.createElement("a");
1183
1236
  a.href = i, a.download = t, a.style.display = "none", document.body.appendChild(a), a.click(), document.body.removeChild(a), setTimeout(() => URL.revokeObjectURL(i), 100);
1184
1237
  }
1185
- var ie = {
1238
+ var q = {
1186
1239
  csv: "text/csv;charset=utf-8",
1187
1240
  tsv: "text/tab-separated-values;charset=utf-8",
1188
1241
  json: "application/json;charset=utf-8",
1189
1242
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1190
- }, ae = {
1243
+ }, oe = {
1191
1244
  csv: ".csv",
1192
1245
  tsv: ".tsv",
1193
1246
  json: ".json",
1194
1247
  xlsx: ".xlsx"
1195
1248
  };
1196
- function oe(e) {
1197
- return ie[e];
1198
- }
1199
1249
  function se(e) {
1200
- return ae[e];
1250
+ return q[e];
1251
+ }
1252
+ function ce(e) {
1253
+ return oe[e];
1201
1254
  }
1202
1255
  //#endregion
1203
1256
  //#region \0@oxc-project+runtime@0.130.0/helpers/decorate.js
1204
- function Y(e, t, n, r) {
1257
+ function J(e, t, n, r) {
1205
1258
  var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
1206
1259
  if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
1207
1260
  else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
@@ -1209,9 +1262,16 @@ function Y(e, t, n, r) {
1209
1262
  }
1210
1263
  //#endregion
1211
1264
  //#region src/flex-table.ts
1212
- var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1265
+ var Y = {
1266
+ eq: "=",
1267
+ neq: "≠",
1268
+ gt: ">",
1269
+ lt: "<",
1270
+ gte: "≥",
1271
+ lte: "≤"
1272
+ }, X = 120, Z = 40, le = 32, Q = 5, $ = class extends e {
1213
1273
  constructor(...e) {
1214
- super(...e), this.columns = [], this.data = [], this.rowHeight = ce, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !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 E(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = 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._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map();
1274
+ super(...e), this.columns = [], this.data = [], this.rowHeight = le, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, 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 w(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = 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._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map(), this._emptyFilterState = /* @__PURE__ */ new Map();
1215
1275
  }
1216
1276
  static {
1217
1277
  this.styles = s;
@@ -1520,12 +1580,12 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1520
1580
  if (!t) return "";
1521
1581
  let n = this.visibleColumns.slice(t.startCol, t.endCol + 1), r = [];
1522
1582
  for (let e = t.startRow; e <= t.endRow; e++) r.push(this.data[this._toDataIndex(e)]);
1523
- return U(r, n, e);
1583
+ return V(r, n, e);
1524
1584
  }
1525
- return U(this._sortedIndices.map((e) => this.data[e]), this.visibleColumns, e);
1585
+ return V(this._sortedIndices.map((e) => this.data[e]), this.visibleColumns, e);
1526
1586
  }
1527
1587
  exportToFile(e, t) {
1528
- J(this.exportToString(e), t ?? `export${se(e)}`, oe(e));
1588
+ K(this.exportToString(e), t ?? `export${ce(e)}`, se(e));
1529
1589
  }
1530
1590
  _getSelectedDataRows() {
1531
1591
  let e = this._selection.getEffectiveRange();
@@ -1547,14 +1607,20 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1547
1607
  get _visibleRowCount() {
1548
1608
  return this._sortedIndices.length;
1549
1609
  }
1610
+ get _frozenRowCount() {
1611
+ return Math.min(this.frozenRows, this._visibleRowCount);
1612
+ }
1613
+ get frozenRowsHeight() {
1614
+ return this._frozenRowCount * this.rowHeight;
1615
+ }
1550
1616
  get totalBodyHeight() {
1551
- return this._visibleRowCount * this.rowHeight;
1617
+ return Math.max(0, this._visibleRowCount - this._frozenRowCount) * this.rowHeight;
1552
1618
  }
1553
1619
  get visibleRange() {
1554
- let e = Math.max(0, this._scrollTop - this.headerHeight), t = Math.max(0, Math.floor(e / this.rowHeight) - Q), n = Math.ceil(this._viewportHeight / this.rowHeight);
1620
+ let e = this._frozenRowCount, t = Math.max(0, this._scrollTop - this.headerHeight - this.frozenRowsHeight), n = e + Math.floor(t / this.rowHeight), r = Math.max(e, n - Q), i = Math.ceil(this._viewportHeight / this.rowHeight);
1555
1621
  return {
1556
- start: t,
1557
- end: Math.min(this._visibleRowCount, Math.floor(e / this.rowHeight) + n + Q)
1622
+ start: r,
1623
+ end: Math.min(this._visibleRowCount, n + i + Q)
1558
1624
  };
1559
1625
  }
1560
1626
  connectedCallback() {
@@ -1609,7 +1675,7 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1609
1675
  this._filteredIndices = Array.from({ length: this.data.length }, (e, t) => t), this._sortedIndices = this._filteredIndices;
1610
1676
  return;
1611
1677
  }
1612
- if (this._filteredIndices = w(this.data, this._filters, (e, t, n) => {
1678
+ if (this._filteredIndices = ne(this.data, this._filters, (e, t, n) => {
1613
1679
  this.dispatchEvent(new CustomEvent("filter-error", {
1614
1680
  detail: {
1615
1681
  error: e,
@@ -1970,7 +2036,7 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1970
2036
  async _handleCopy(e) {
1971
2037
  let t = this._selection.getEffectiveRange();
1972
2038
  if (!t) return;
1973
- let n = this.visibleColumns, r = te(this._sortedIndices.map((e) => this.data[e]), n, t);
2039
+ let n = this.visibleColumns, r = T(this._sortedIndices.map((e) => this.data[e]), n, t);
1974
2040
  try {
1975
2041
  await navigator.clipboard.writeText(r);
1976
2042
  } catch (e) {
@@ -1996,7 +2062,7 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
1996
2062
  if (!this._activeCell) return;
1997
2063
  let e = await this._readClipboardText();
1998
2064
  if (e == null) return;
1999
- let t = ne(e);
2065
+ let t = E(e);
2000
2066
  if (t.length === 0) return;
2001
2067
  let n = this._expandRowsForPaste(this._activeCell.row, t.length), r = this._applyPasteData(this._activeCell, t);
2002
2068
  if (r.length > 0 || n.length > 0) {
@@ -2193,7 +2259,7 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2193
2259
  _scrollToActiveCell() {
2194
2260
  if (!this._activeCell) return;
2195
2261
  let e = this._activeCell.row * this.rowHeight + this.headerHeight, t = e + this.rowHeight;
2196
- e < this.scrollTop + this.headerHeight ? this.scrollTop = e - this.headerHeight : t > this.scrollTop + this.clientHeight && (this.scrollTop = t - this.clientHeight);
2262
+ this._activeCell.row < this._frozenRowCount || (e < this.scrollTop + this.headerHeight + this.frozenRowsHeight ? this.scrollTop = e - this.headerHeight - this.frozenRowsHeight : t > this.scrollTop + this.clientHeight && (this.scrollTop = t - this.clientHeight));
2197
2263
  let n = this.visibleColumns, r = this._activeCell.col, i = this._colLeftOffsets[r] ?? 0, a = i + this._getColWidth(n[r]);
2198
2264
  i < this.scrollLeft ? this.scrollLeft = i : a > this.scrollLeft + this.clientWidth && (this.scrollLeft = a - this.clientWidth);
2199
2265
  }
@@ -2210,7 +2276,7 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2210
2276
  this._selectColumn(n);
2211
2277
  return;
2212
2278
  }
2213
- t.sortable !== !1 && (this._sortCriteria = C(this._sortCriteria, t.key, e.shiftKey), this.dataMode !== "server" && this._recomputeView(), this.requestUpdate(), this.dispatchEvent(new CustomEvent("sort-change", {
2279
+ t.sortable !== !1 && (this._sortCriteria = te(this._sortCriteria, t.key, e.shiftKey), this.dataMode !== "server" && this._recomputeView(), this.requestUpdate(), this.dispatchEvent(new CustomEvent("sort-change", {
2214
2280
  detail: { criteria: [...this._sortCriteria] },
2215
2281
  bubbles: !0,
2216
2282
  composed: !0
@@ -2451,36 +2517,215 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2451
2517
  </div>
2452
2518
  `;
2453
2519
  }
2520
+ _buildTextPredicate(e, t) {
2521
+ let n = e.toLowerCase();
2522
+ switch (t) {
2523
+ case "starts": return (e) => String(e ?? "").toLowerCase().startsWith(n);
2524
+ case "ends": return (e) => String(e ?? "").toLowerCase().endsWith(n);
2525
+ case "wildcard": {
2526
+ let e = n.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, "."), t = RegExp("^" + e + "$", "i");
2527
+ return (e) => t.test(String(e ?? ""));
2528
+ }
2529
+ default: return (e) => String(e ?? "").toLowerCase().includes(n);
2530
+ }
2531
+ }
2532
+ _applyTextFilter(e) {
2533
+ let t = this._emptyFilterState.get(e);
2534
+ if (t) {
2535
+ this.setFilter(e, this._buildEmptyPredicate(t));
2536
+ return;
2537
+ }
2538
+ let n = this._textFilterState.get(e);
2539
+ n?.value ? this.setFilter(e, this._buildTextPredicate(n.value, n.mode)) : this.removeFilter(e);
2540
+ }
2541
+ _buildEmptyPredicate(e) {
2542
+ return e === "empty" ? (e) => e == null || e === "" : (e) => e != null && e !== "";
2543
+ }
2544
+ _renderEmptyFilterRow(e, t) {
2545
+ return n`
2546
+ <div class="ft-filter-empty-row">
2547
+ <label>Blank cells</label>
2548
+ <select class="ft-filter-mode-select"
2549
+ .value=${this._emptyFilterState.get(e) ?? ""}
2550
+ @change=${(n) => {
2551
+ let r = n.target.value;
2552
+ (r === "" || r === "empty" || r === "non-empty") && (r ? this._emptyFilterState.set(e, r) : this._emptyFilterState.delete(e), t?.(), this._applyFilterForKey(e));
2553
+ }}
2554
+ @keydown=${(e) => {
2555
+ e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2556
+ }}>
2557
+ <option value="">— All —</option>
2558
+ <option value="empty">Empty only</option>
2559
+ <option value="non-empty">Non-empty only</option>
2560
+ </select>
2561
+ </div>
2562
+ `;
2563
+ }
2564
+ _evalNumCond(e, t) {
2565
+ let n = t.value;
2566
+ switch (t.op) {
2567
+ case "eq": return e === n;
2568
+ case "neq": return e !== n;
2569
+ case "gt": return e > n;
2570
+ case "lt": return e < n;
2571
+ case "gte": return e >= n;
2572
+ case "lte": return e <= n;
2573
+ }
2574
+ }
2575
+ _buildNumberPredicate(e) {
2576
+ let t = e.cond1.value != null, n = e.cond2.value != null;
2577
+ return !t && !n ? null : (r) => {
2578
+ let i = Number(r);
2579
+ if (isNaN(i)) return !1;
2580
+ if (t && !n) return this._evalNumCond(i, e.cond1);
2581
+ if (!t && n) return this._evalNumCond(i, e.cond2);
2582
+ let a = this._evalNumCond(i, e.cond1), o = this._evalNumCond(i, e.cond2);
2583
+ return e.join === "or" ? a || o : a && o;
2584
+ };
2585
+ }
2586
+ _buildDatePredicate(e) {
2587
+ return e.from == null && e.to == null ? null : (t) => {
2588
+ if (t == null || t === "") return !1;
2589
+ let n = t instanceof Date ? t : new Date(String(t));
2590
+ if (isNaN(n.getTime()) || e.from && n < new Date(e.from)) return !1;
2591
+ if (e.to) {
2592
+ let t = new Date(e.to);
2593
+ if (e.to.length === 10 && t.setHours(23, 59, 59, 999), n > t) return !1;
2594
+ }
2595
+ return !0;
2596
+ };
2597
+ }
2598
+ _applyFilterForKey(e) {
2599
+ let t = this._emptyFilterState.get(e);
2600
+ if (t) {
2601
+ this.setFilter(e, this._buildEmptyPredicate(t));
2602
+ return;
2603
+ }
2604
+ if (this._textFilterState.has(e)) this._applyTextFilter(e);
2605
+ else if (this._numberFilterState.has(e)) {
2606
+ let t = this._buildNumberPredicate(this._numberFilterState.get(e));
2607
+ t ? this.setFilter(e, t) : this.removeFilter(e);
2608
+ } else if (this._dateFilterState.has(e)) {
2609
+ let t = this._buildDatePredicate(this._dateFilterState.get(e));
2610
+ t ? this.setFilter(e, t) : this.removeFilter(e);
2611
+ } else this.removeFilter(e);
2612
+ }
2454
2613
  _renderTextFilter(e) {
2614
+ let t = this._textFilterState.get(e.key) ?? {
2615
+ value: "",
2616
+ mode: "contains"
2617
+ };
2455
2618
  return n`
2619
+ <div class="ft-filter-mode-row">
2620
+ <select class="ft-filter-mode-select"
2621
+ .value=${t.mode}
2622
+ @change=${(t) => {
2623
+ let n = t.target.value, r = this._textFilterState.get(e.key) ?? {
2624
+ value: "",
2625
+ mode: "contains"
2626
+ };
2627
+ this._textFilterState.set(e.key, {
2628
+ ...r,
2629
+ mode: n
2630
+ }), this._applyTextFilter(e.key);
2631
+ }}
2632
+ @keydown=${(e) => {
2633
+ e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2634
+ }}>
2635
+ <option value="contains">Contains</option>
2636
+ <option value="starts">Starts with</option>
2637
+ <option value="ends">Ends with</option>
2638
+ <option value="wildcard">Wildcard</option>
2639
+ </select>
2640
+ </div>
2456
2641
  <input class="ft-filter-input" type="text" placeholder="Search..."
2457
- .value=${this._textFilterState.get(e.key) ?? ""}
2642
+ .value=${t.value}
2458
2643
  @input=${(t) => {
2459
- let n = t.target.value;
2460
- this._textFilterState.set(e.key, n), n ? this.setFilter(e.key, (e) => String(e ?? "").toLowerCase().includes(n.toLowerCase())) : this.removeFilter(e.key);
2644
+ let n = t.target.value, r = this._textFilterState.get(e.key) ?? {
2645
+ value: "",
2646
+ mode: "contains"
2647
+ };
2648
+ this._textFilterState.set(e.key, {
2649
+ ...r,
2650
+ value: n
2651
+ }), this._emptyFilterState.delete(e.key), this._applyTextFilter(e.key);
2461
2652
  }}
2462
2653
  @keydown=${(e) => {
2463
2654
  e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2464
2655
  }}>
2656
+ ${this._renderEmptyFilterRow(e.key, () => {
2657
+ this._textFilterState.delete(e.key);
2658
+ })}
2465
2659
  `;
2466
2660
  }
2467
- _renderNumberFilter(e) {
2468
- let t = this._numberFilterState.get(e.key) ?? {};
2661
+ _defaultNumState() {
2662
+ return {
2663
+ cond1: {
2664
+ op: "gte",
2665
+ value: null
2666
+ },
2667
+ join: "and",
2668
+ cond2: {
2669
+ op: "lte",
2670
+ value: null
2671
+ }
2672
+ };
2673
+ }
2674
+ _renderNumCondRow(e, t) {
2675
+ let r = (this._numberFilterState.get(e) ?? this._defaultNumState())[t];
2469
2676
  return n`
2470
- <div class="ft-filter-range">
2471
- <input class="ft-filter-input" type="number" placeholder="Min"
2472
- .value=${t.min == null ? "" : String(t.min)}
2473
- @input=${(t) => this._applyNumberFilter(e.key, t, "min")}
2677
+ <div class="ft-num-cond-row">
2678
+ <select class="ft-filter-mode-select ft-num-op-select"
2679
+ .value=${r.op}
2680
+ @change=${(n) => {
2681
+ let r = n.target.value, i = this._numberFilterState.get(e) ?? this._defaultNumState();
2682
+ i[t] = {
2683
+ ...i[t],
2684
+ op: r
2685
+ }, this._numberFilterState.set(e, i), this._emptyFilterState.delete(e), this._applyFilterForKey(e);
2686
+ }}
2474
2687
  @keydown=${(e) => {
2475
2688
  e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2476
2689
  }}>
2477
- <input class="ft-filter-input" type="number" placeholder="Max"
2478
- .value=${t.max == null ? "" : String(t.max)}
2479
- @input=${(t) => this._applyNumberFilter(e.key, t, "max")}
2690
+ ${Object.keys(Y).map((e) => n`<option value=${e}>${Y[e]}</option>`)}
2691
+ </select>
2692
+ <input class="ft-filter-input ft-num-cond-input" type="number" placeholder="Value"
2693
+ .value=${r.value == null ? "" : String(r.value)}
2694
+ @input=${(n) => {
2695
+ let r = n.target.value, i = this._numberFilterState.get(e) ?? this._defaultNumState();
2696
+ i[t] = {
2697
+ ...i[t],
2698
+ value: r === "" ? null : Number(r)
2699
+ }, this._numberFilterState.set(e, i), this._emptyFilterState.delete(e), this._applyFilterForKey(e);
2700
+ }}
2480
2701
  @keydown=${(e) => {
2481
2702
  e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2482
2703
  }}>
2483
2704
  </div>
2705
+ `;
2706
+ }
2707
+ _renderNumberFilter(e) {
2708
+ let t = this._numberFilterState.get(e.key) ?? this._defaultNumState();
2709
+ return n`
2710
+ ${this._renderNumCondRow(e.key, "cond1")}
2711
+ <div class="ft-filter-mode-row">
2712
+ <select class="ft-filter-mode-select"
2713
+ .value=${t.join}
2714
+ @change=${(t) => {
2715
+ let n = t.target.value, r = this._numberFilterState.get(e.key) ?? this._defaultNumState();
2716
+ r.join = n, this._numberFilterState.set(e.key, r), this._emptyFilterState.delete(e.key), this._applyFilterForKey(e.key);
2717
+ }}
2718
+ @keydown=${(e) => {
2719
+ e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2720
+ }}>
2721
+ <option value="and">AND</option>
2722
+ <option value="or">OR</option>
2723
+ </select>
2724
+ </div>
2725
+ ${this._renderNumCondRow(e.key, "cond2")}
2726
+ ${this._renderEmptyFilterRow(e.key, () => {
2727
+ this._numberFilterState.delete(e.key);
2728
+ })}
2484
2729
  `;
2485
2730
  }
2486
2731
  _cellKey(e, t) {
@@ -2501,13 +2746,6 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2501
2746
  let n = this._invalidCells.get(this._cellKey(e, t));
2502
2747
  return n ? n.error : null;
2503
2748
  }
2504
- _applyNumberFilter(e, t, n) {
2505
- let r = t.target.value, i = this._numberFilterState.get(e) ?? {};
2506
- r === "" ? delete i[n] : i[n] = Number(r), this._numberFilterState.set(e, i), i.min == null && i.max == null ? this.removeFilter(e) : this.setFilter(e, (e) => {
2507
- let t = Number(e);
2508
- return !(isNaN(t) || i.min != null && t < i.min || i.max != null && t > i.max);
2509
- });
2510
- }
2511
2749
  _renderDateFilter(e) {
2512
2750
  let t = e.type === "datetime" ? "datetime-local" : "date", r = this._dateFilterState.get(e.key) ?? {};
2513
2751
  return n`
@@ -2525,20 +2763,14 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2525
2763
  e.key === "Escape" && (this._openFilterKey = null), e.stopPropagation();
2526
2764
  }}>
2527
2765
  </div>
2766
+ ${this._renderEmptyFilterRow(e.key, () => {
2767
+ this._dateFilterState.delete(e.key);
2768
+ })}
2528
2769
  `;
2529
2770
  }
2530
2771
  _applyDateFilter(e, t, n) {
2531
2772
  let r = t.target.value, i = this._dateFilterState.get(e) ?? {};
2532
- r === "" ? delete i[n] : i[n] = r, this._dateFilterState.set(e, i), i.from == null && i.to == null ? this.removeFilter(e) : this.setFilter(e, (e) => {
2533
- if (e == null || e === "") return !1;
2534
- let t = e instanceof Date ? e : new Date(String(e));
2535
- if (isNaN(t.getTime()) || i.from && t < new Date(i.from)) return !1;
2536
- if (i.to) {
2537
- let e = new Date(i.to);
2538
- if (i.to.length === 10 && e.setHours(23, 59, 59, 999), t > e) return !1;
2539
- }
2540
- return !0;
2541
- });
2773
+ r === "" ? delete i[n] : i[n] = r, this._dateFilterState.set(e, i), this._emptyFilterState.delete(e), this._applyFilterForKey(e);
2542
2774
  }
2543
2775
  _renderBooleanFilter(e) {
2544
2776
  return n`
@@ -2554,10 +2786,11 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
2554
2786
  <option value="true">\u2714 True</option>
2555
2787
  <option value="false">\u2718 False</option>
2556
2788
  </select>
2789
+ ${this._renderEmptyFilterRow(e.key)}
2557
2790
  `;
2558
2791
  }
2559
2792
  _clearColumnFilter(e) {
2560
- this.removeFilter(e), this._textFilterState.delete(e), this._numberFilterState.delete(e), this._dateFilterState.delete(e), this._openFilterKey = null;
2793
+ this.removeFilter(e), this._textFilterState.delete(e), this._numberFilterState.delete(e), this._dateFilterState.delete(e), this._emptyFilterState.delete(e), this._openFilterKey = null;
2561
2794
  }
2562
2795
  _onResizeAutoFit(e, t) {
2563
2796
  e.preventDefault(), e.stopPropagation();
@@ -3026,9 +3259,9 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
3026
3259
  </div>
3027
3260
  <div class="ft-empty">${this.data.length === 0 ? "No data" : "No matching data"}</div>
3028
3261
  `;
3029
- let { start: p, end: m } = this.visibleRange, h = [];
3030
- for (let e = p; e < m; e++) h.push(this._renderRow(e, i, a, o));
3031
- let g = this._renderFillHandle(), _ = this._rowDragIndicatorY == null ? "" : n`<div class="ft-row-drop-indicator" style="top:${this._rowDragIndicatorY}px;width:${r + this._prefixWidth}px;"></div>`;
3262
+ let { start: p, end: m } = this.visibleRange, h = this._frozenRowCount, g = [];
3263
+ for (let e = p; e < m; e++) g.push(this._renderRow(e, i, a, o, (e - h) * this.rowHeight));
3264
+ let _ = this._renderFillHandle(), v = this._rowDragIndicatorY == null ? "" : n`<div class="ft-row-drop-indicator" style="top:${this._rowDragIndicatorY}px;width:${r + this._prefixWidth}px;"></div>`;
3032
3265
  return n`
3033
3266
  ${this._renderFindPanel()}
3034
3267
  <div class="ft-header" role="row" style="width: ${r}px; height: ${t}px;">
@@ -3036,12 +3269,13 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
3036
3269
  ${d}
3037
3270
  ${f}
3038
3271
  </div>
3272
+ ${this._renderFrozenRows(i, a, o)}
3039
3273
  <div class="ft-body" style="height: ${this.totalBodyHeight}px; width: ${r}px;">
3040
- ${h}
3274
+ ${g}
3041
3275
  </div>
3042
3276
  ${this.footerData ? this._renderFooter(i, a, o) : ""}
3277
+ ${v}
3043
3278
  ${_}
3044
- ${g}
3045
3279
  ${this._renderHeaderContextMenu()}
3046
3280
  ${this._renderBodyContextMenu()}
3047
3281
  `;
@@ -3052,6 +3286,17 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
3052
3286
  _onRowCheckboxChange(e, t) {
3053
3287
  e.stopPropagation(), this._rowSelection.toggle(t), this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
3054
3288
  }
3289
+ _renderFrozenRows(e, t, i) {
3290
+ let a = this._frozenRowCount;
3291
+ if (a === 0) return r;
3292
+ let o = this._totalRowWidth, s = [];
3293
+ for (let n = 0; n < a; n++) s.push(this._renderRow(n, e, t, i, n * this.rowHeight));
3294
+ return n`
3295
+ <div class="ft-frozen-rows" style="top: ${this.headerHeight}px; height: ${this.frozenRowsHeight}px; width: ${o}px;">
3296
+ ${s}
3297
+ </div>
3298
+ `;
3299
+ }
3055
3300
  _renderFooter(e, t, r) {
3056
3301
  if (!this.footerData) return "";
3057
3302
  let i = this.visibleColumns, a = this.rowHeight, o = this._totalRowWidth, s = this._scrollLeft, c = 0, l = this.selectable ? n`<div class="ft-footer-cell ft-checkbox-cell"
@@ -3080,29 +3325,29 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
3080
3325
  </div>
3081
3326
  `;
3082
3327
  }
3083
- _renderRow(e, t, r, i) {
3084
- let a = this.visibleColumns, o = this._toDataIndex(e), s = this.data[o], c = e * this.rowHeight, l = this.rowHeight, u = this._totalRowWidth, d = e % 2 == 0 ? "ft-row-even" : "ft-row-odd", f = this.selectable && this._rowSelection.isSelected(e), p = this._scrollLeft, m = 0, h = this.selectable ? n`
3328
+ _renderRow(e, t, r, i, a) {
3329
+ let o = this.visibleColumns, s = this._toDataIndex(e), c = this.data[s], l = a ?? e * this.rowHeight, u = this.rowHeight, d = this._totalRowWidth, f = e % 2 == 0 ? "ft-row-even" : "ft-row-odd", p = this.selectable && this._rowSelection.isSelected(e), m = this._scrollLeft, h = 0, g = this.selectable ? n`
3085
3330
  <div class="ft-checkbox-cell"
3086
- style="position: absolute; top: 0; left: ${p + m}px; width: 36px; height: ${l}px; z-index: 2;">
3331
+ style="position: absolute; top: 0; left: ${m + h}px; width: 36px; height: ${u}px; z-index: 2;">
3087
3332
  <input type="checkbox"
3088
- .checked=${f}
3333
+ .checked=${p}
3089
3334
  @change=${(t) => this._onRowCheckboxChange(t, e)}>
3090
3335
  </div>
3091
3336
  ` : "";
3092
- this.selectable && (m += 36);
3093
- let g = this._rowDrag?.active && this._rowDrag.rowIndex === e, _ = this.showRowNumbers ? n`
3094
- <div class="ft-row-num ${g ? "ft-col-dragging" : ""}"
3095
- style="position: absolute; top: 0; left: ${p + m}px; width: 48px; height: ${l}px; z-index: 2; cursor: grab;"
3337
+ this.selectable && (h += 36);
3338
+ let _ = this._rowDrag?.active && this._rowDrag.rowIndex === e, v = this.showRowNumbers ? n`
3339
+ <div class="ft-row-num ${_ ? "ft-col-dragging" : ""}"
3340
+ style="position: absolute; top: 0; left: ${m + h}px; width: 48px; height: ${u}px; z-index: 2; cursor: grab;"
3096
3341
  @mousedown=${(t) => this._onRowNumMouseDown(t, e)}
3097
- @click=${() => this._onRowNumberClick(e)}>${o + 1}</div>
3098
- ` : "", v = [];
3099
- for (let t of i) v.push(this._renderCell(s, a[t], e, t));
3100
- for (let n = t; n < r; n++) v.push(this._renderCell(s, a[n], e, n));
3342
+ @click=${() => this._onRowNumberClick(e)}>${s + 1}</div>
3343
+ ` : "", y = [];
3344
+ for (let t of i) y.push(this._renderCell(c, o[t], e, t));
3345
+ for (let n = t; n < r; n++) y.push(this._renderCell(c, o[n], e, n));
3101
3346
  return n`
3102
- <div class="ft-row ${d} ${f ? "ft-row-selected" : ""}" role="row"
3103
- style="top: ${c}px; height: ${l}px; width: ${u}px;">
3104
- ${h}${_}
3105
- ${v}
3347
+ <div class="ft-row ${f} ${p ? "ft-row-selected" : ""}" role="row"
3348
+ style="top: ${l}px; height: ${u}px; width: ${d}px;">
3349
+ ${g}${v}
3350
+ ${y}
3106
3351
  </div>
3107
3352
  `;
3108
3353
  }
@@ -3223,39 +3468,42 @@ var X = 120, Z = 40, ce = 32, Q = 5, $ = class extends e {
3223
3468
  return isNaN(t.getTime()) ? "" : `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, "0")}-${String(t.getDate()).padStart(2, "0")}T${String(t.getHours()).padStart(2, "0")}:${String(t.getMinutes()).padStart(2, "0")}`;
3224
3469
  }
3225
3470
  };
3226
- Y([a({ type: Array })], $.prototype, "columns", void 0), Y([a({
3471
+ J([a({ type: Array })], $.prototype, "columns", void 0), J([a({
3227
3472
  type: Array,
3228
3473
  hasChanged: () => !0
3229
- })], $.prototype, "data", void 0), Y([a({
3474
+ })], $.prototype, "data", void 0), J([a({
3230
3475
  type: Number,
3231
3476
  attribute: "row-height"
3232
- })], $.prototype, "rowHeight", void 0), Y([a({
3477
+ })], $.prototype, "rowHeight", void 0), J([a({
3233
3478
  type: Boolean,
3234
3479
  attribute: "show-row-numbers"
3235
- })], $.prototype, "showRowNumbers", void 0), Y([a({
3480
+ })], $.prototype, "showRowNumbers", void 0), J([a({
3236
3481
  type: String,
3237
3482
  reflect: !0
3238
- })], $.prototype, "theme", void 0), Y([a({
3483
+ })], $.prototype, "theme", void 0), J([a({
3239
3484
  type: Number,
3240
3485
  attribute: "max-rows"
3241
- })], $.prototype, "maxRows", void 0), Y([a({ type: Boolean })], $.prototype, "editable", void 0), Y([a({
3486
+ })], $.prototype, "maxRows", void 0), J([a({ type: Boolean })], $.prototype, "editable", void 0), J([a({
3242
3487
  type: Boolean,
3243
3488
  attribute: "show-filters"
3244
- })], $.prototype, "showFilters", void 0), Y([a({
3489
+ })], $.prototype, "showFilters", void 0), J([a({
3245
3490
  type: Boolean,
3246
3491
  attribute: "show-context-menu"
3247
- })], $.prototype, "showContextMenu", void 0), Y([a({ type: Boolean })], $.prototype, "selectable", void 0), Y([a({
3492
+ })], $.prototype, "showContextMenu", void 0), J([a({
3493
+ type: Number,
3494
+ attribute: "frozen-rows"
3495
+ })], $.prototype, "frozenRows", void 0), J([a({ type: Boolean })], $.prototype, "selectable", void 0), J([a({
3248
3496
  type: String,
3249
3497
  attribute: "selection-mode"
3250
- })], $.prototype, "selectionMode", null), Y([a({
3498
+ })], $.prototype, "selectionMode", null), J([a({
3251
3499
  type: String,
3252
3500
  attribute: "data-mode"
3253
- })], $.prototype, "dataMode", void 0), Y([a({
3501
+ })], $.prototype, "dataMode", void 0), J([a({
3254
3502
  type: Object,
3255
3503
  attribute: "footer-data"
3256
- })], $.prototype, "footerData", void 0), Y([a({
3504
+ })], $.prototype, "footerData", void 0), J([a({
3257
3505
  type: Number,
3258
3506
  attribute: "max-undo-size"
3259
- })], $.prototype, "maxUndoSize", null), Y([o()], $.prototype, "_scrollTop", void 0), Y([o()], $.prototype, "_scrollLeft", void 0), Y([o()], $.prototype, "_viewportHeight", void 0), Y([o()], $.prototype, "_viewportWidth", void 0), Y([o()], $.prototype, "_activeCell", void 0), Y([o()], $.prototype, "_editingCell", void 0), Y([o()], $.prototype, "_sortCriteria", void 0), Y([o()], $.prototype, "_openFilterKey", void 0), Y([o()], $.prototype, "_rowSelectionVersion", void 0), Y([o()], $.prototype, "_autocompleteState", void 0), Y([o()], $.prototype, "_headerMenu", void 0), Y([o()], $.prototype, "_bodyContextMenu", void 0), $ = Y([i("flex-table")], $);
3507
+ })], $.prototype, "maxUndoSize", null), J([o()], $.prototype, "_scrollTop", void 0), J([o()], $.prototype, "_scrollLeft", void 0), J([o()], $.prototype, "_viewportHeight", void 0), J([o()], $.prototype, "_viewportWidth", void 0), J([o()], $.prototype, "_activeCell", void 0), J([o()], $.prototype, "_editingCell", void 0), J([o()], $.prototype, "_sortCriteria", void 0), J([o()], $.prototype, "_openFilterKey", void 0), J([o()], $.prototype, "_rowSelectionVersion", void 0), J([o()], $.prototype, "_autocompleteState", void 0), J([o()], $.prototype, "_headerMenu", void 0), J([o()], $.prototype, "_bodyContextMenu", void 0), $ = J([i("flex-table")], $);
3260
3508
  //#endregion
3261
- export { f as a, x as i, U as n, E as r, $ as t };
3509
+ export { f as a, x as i, V as n, w as r, $ as t };
@@ -17,6 +17,8 @@ export declare class FlexTable extends LitElement {
17
17
  showFilters: boolean;
18
18
  /** Enable built-in context menu on cell right-click. */
19
19
  showContextMenu: boolean;
20
+ /** Number of rows to freeze at the top (always visible during vertical scroll). */
21
+ frozenRows: number;
20
22
  /** Enable row-level checkbox selection. */
21
23
  selectable: boolean;
22
24
  /** Row selection mode: 'single' or 'multi' (default: 'multi'). */
@@ -169,6 +171,8 @@ export declare class FlexTable extends LitElement {
169
171
  private get headerHeight();
170
172
  /** Number of rows visible after filter + sort. */
171
173
  private get _visibleRowCount();
174
+ private get _frozenRowCount();
175
+ private get frozenRowsHeight();
172
176
  private get totalBodyHeight();
173
177
  private get visibleRange();
174
178
  connectedCallback(): void;
@@ -236,7 +240,17 @@ export declare class FlexTable extends LitElement {
236
240
  private _adjustFilterDropdown;
237
241
  private _onFilterBtnClick;
238
242
  private _renderFilterDropdown;
243
+ private _buildTextPredicate;
244
+ private _applyTextFilter;
245
+ private _buildEmptyPredicate;
246
+ private _renderEmptyFilterRow;
247
+ private _evalNumCond;
248
+ private _buildNumberPredicate;
249
+ private _buildDatePredicate;
250
+ private _applyFilterForKey;
239
251
  private _renderTextFilter;
252
+ private _defaultNumState;
253
+ private _renderNumCondRow;
240
254
  private _renderNumberFilter;
241
255
  private _invalidCells;
242
256
  private _cellKey;
@@ -245,7 +259,7 @@ export declare class FlexTable extends LitElement {
245
259
  private _textFilterState;
246
260
  private _numberFilterState;
247
261
  private _dateFilterState;
248
- private _applyNumberFilter;
262
+ private _emptyFilterState;
249
263
  private _renderDateFilter;
250
264
  private _applyDateFilter;
251
265
  private _renderBooleanFilter;
@@ -279,6 +293,7 @@ export declare class FlexTable extends LitElement {
279
293
  render(): TemplateResult<1>;
280
294
  private _onSelectAllChange;
281
295
  private _onRowCheckboxChange;
296
+ private _renderFrozenRows;
282
297
  private _renderFooter;
283
298
  private _renderRow;
284
299
  private _renderCell;
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, r, t as i } from "./flex-table-fp3j2egA.js";
1
+ import { a as e, i as t, n, r, t as i } from "./flex-table-BVzjrFBP.js";
2
2
  export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./flex-table-fp3j2egA.js";
1
+ import { t as e } from "./flex-table-BVzjrFBP.js";
2
2
  import t from "react";
3
3
  import { createComponent as n } from "@lit/react";
4
4
  //#region src/react.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",