@canvas-components/list-table 0.3.22 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1112,16 +1112,149 @@ function applyInitialColumnVisibility(columns) {
1112
1112
  function replaceColumns(target, source) {
1113
1113
  target.splice(0, target.length, ...cloneColumns(source));
1114
1114
  }
1115
+ function preserveUserAddedColumns(params) {
1116
+ const keys = /* @__PURE__ */ new Set();
1117
+ const collect = (columns) => {
1118
+ columns.forEach((column) => {
1119
+ keys.add(resolveColumnKey(column));
1120
+ if (column.children?.length) collect(column.children);
1121
+ });
1122
+ };
1123
+ collect(params.nextColumns);
1124
+ const preserve = (source, target) => {
1125
+ source.forEach((column) => {
1126
+ const key = resolveColumnKey(column);
1127
+ if (column.userDefined && !keys.has(key)) {
1128
+ target.push({ ...column });
1129
+ keys.add(key);
1130
+ } else if (column.children?.length) {
1131
+ const matching = findColumnNode(params.nextColumns, key);
1132
+ if (matching?.children) preserve(column.children, matching.children);
1133
+ }
1134
+ });
1135
+ };
1136
+ preserve(params.runtimeColumns, params.nextColumns);
1137
+ }
1115
1138
  function buildHeaderSettingsItems(columns) {
1116
1139
  ensureColumnKeysInPlace(columns);
1117
1140
  const totalVisibleLeafCount = countVisibleLeafColumns(columns);
1118
- return columns.flatMap((column) => {
1141
+ return columns.flatMap((column, index) => {
1119
1142
  if (column.hideInSetting) {
1120
1143
  return [];
1121
1144
  }
1122
- return [buildHeaderSettingsItem(column, totalVisibleLeafCount, false)];
1145
+ return [
1146
+ buildHeaderSettingsItem(
1147
+ column,
1148
+ totalVisibleLeafCount,
1149
+ false,
1150
+ index + 1,
1151
+ columns.length
1152
+ )
1153
+ ];
1123
1154
  });
1124
1155
  }
1156
+ function saveHeaderSettingsColumn(columns, draft) {
1157
+ const title = draft.title.trim();
1158
+ if (!title) return "\u8BF7\u8F93\u5165\u5217\u540D\u79F0";
1159
+ const existing = draft.key ? findColumnNode(columns, draft.key) : null;
1160
+ if (draft.key && !existing) return "\u5217\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u5217\u663E\u793A\u83DC\u5355";
1161
+ const parent = !existing && draft.parentKey ? findColumnNode(columns, draft.parentKey) : null;
1162
+ if (draft.parentKey && !existing && !parent?.children?.length) {
1163
+ return "\u6240\u5C5E\u591A\u7EA7\u8868\u5934\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u5217\u663E\u793A\u83DC\u5355";
1164
+ }
1165
+ const siblings = existing ? findSiblingColumnsByKey(columns, draft.key) : parent?.children ?? columns;
1166
+ if (!siblings) return "\u5217\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u5217\u663E\u793A\u83DC\u5355";
1167
+ const maxOrder = siblings.length + (existing ? 0 : 1);
1168
+ if (draft.order != null && (!Number.isInteger(draft.order) || draft.order < 1 || draft.order > maxOrder)) {
1169
+ return `\u5217\u987A\u5E8F\u987B\u4ECB\u4E8E 1 \u548C ${maxOrder} \u4E4B\u95F4`;
1170
+ }
1171
+ const minWidth = draft.minWidth ?? 40;
1172
+ const maxWidth = draft.maxWidth ?? 2e3;
1173
+ const width = draft.width ?? 160;
1174
+ if (![minWidth, maxWidth, width].every(
1175
+ (value) => Number.isFinite(value) && value > 0
1176
+ ) || minWidth > maxWidth || width < minWidth || width > maxWidth) {
1177
+ return "\u5BBD\u5EA6\u987B\u4ECB\u4E8E\u6700\u5C0F\u5BBD\u5EA6\u548C\u6700\u5927\u5BBD\u5EA6\u4E4B\u95F4";
1178
+ }
1179
+ if (existing && !existing.userDefined) {
1180
+ existing.title = title;
1181
+ existing.settingTitle = title;
1182
+ existing.userTitle = title;
1183
+ existing.width = width;
1184
+ existing.minWidth = minWidth;
1185
+ existing.maxWidth = maxWidth;
1186
+ if (draft.order != null) moveColumnToOrder(siblings, existing, draft.order);
1187
+ return null;
1188
+ }
1189
+ const dataIndex = draft.dataIndex?.trim();
1190
+ if (!dataIndex || dataIndex.split(".").some((part) => !part.trim())) {
1191
+ return "dataIndex \u4E3A\u5FC5\u586B\u9879";
1192
+ }
1193
+ if (isHeaderDataIndexUsed(columns, dataIndex, draft.key)) {
1194
+ return "\u8BE5 dataIndex \u5DF2\u88AB\u5176\u4ED6\u5217\u4F7F\u7528";
1195
+ }
1196
+ const column = existing ?? { key: createUserColumnKey(columns) };
1197
+ column.title = title;
1198
+ column.settingTitle = title;
1199
+ column.dataIndex = dataIndex.split(".");
1200
+ column.width = width;
1201
+ column.minWidth = minWidth;
1202
+ column.maxWidth = maxWidth;
1203
+ column.fixed = draft.fixed;
1204
+ column.userDefined = true;
1205
+ column.allowSort = true;
1206
+ column.sorter = true;
1207
+ if (existing && draft.order != null) {
1208
+ moveColumnToOrder(siblings, existing, draft.order);
1209
+ } else if (!existing) {
1210
+ siblings.splice((draft.order ?? maxOrder) - 1, 0, column);
1211
+ }
1212
+ return null;
1213
+ }
1214
+ function findSiblingColumnsByKey(columns, key) {
1215
+ if (columns.some((column) => resolveColumnKey(column) === key))
1216
+ return columns;
1217
+ for (const column of columns) {
1218
+ if (!column.children?.length) continue;
1219
+ const siblings = findSiblingColumnsByKey(column.children, key);
1220
+ if (siblings) return siblings;
1221
+ }
1222
+ return null;
1223
+ }
1224
+ function moveColumnToOrder(siblings, column, order) {
1225
+ const currentIndex = siblings.indexOf(column);
1226
+ if (currentIndex < 0 || currentIndex === order - 1) return;
1227
+ siblings.splice(currentIndex, 1);
1228
+ siblings.splice(order - 1, 0, column);
1229
+ }
1230
+ function deleteHeaderSettingsColumn(columns, columnKey) {
1231
+ const target = findColumnNode(columns, columnKey);
1232
+ if (!target?.userDefined || countVisibleLeafColumns(columns) <= countVisibleLeafColumns([target]))
1233
+ return false;
1234
+ const remove = (items) => {
1235
+ const index = items.findIndex(
1236
+ (column) => resolveColumnKey(column) === columnKey
1237
+ );
1238
+ if (index >= 0) {
1239
+ items.splice(index, 1);
1240
+ return true;
1241
+ }
1242
+ return items.some(
1243
+ (column) => column.children?.length && remove(column.children)
1244
+ );
1245
+ };
1246
+ return remove(columns);
1247
+ }
1248
+ function createUserColumnKey(columns) {
1249
+ let index = 1;
1250
+ while (findColumnNode(columns, `user-column-${index}`)) index += 1;
1251
+ return `user-column-${index}`;
1252
+ }
1253
+ function isHeaderDataIndexUsed(columns, dataIndex, excludeKey) {
1254
+ return columns.some(
1255
+ (column) => resolveColumnKey(column) !== excludeKey && column.dataIndex != null && utils.stringifyDataIndex(column.dataIndex) === dataIndex || column.children?.length && isHeaderDataIndexUsed(column.children, dataIndex, excludeKey)
1256
+ );
1257
+ }
1125
1258
  function setHeaderColumnVisible(columns, columnKey, visible) {
1126
1259
  if (!visible) {
1127
1260
  const targetLeafCount = getColumnLeafCount(columns, columnKey);
@@ -1320,10 +1453,18 @@ function findColumnNode(columns, columnKey) {
1320
1453
  }
1321
1454
  return null;
1322
1455
  }
1323
- function buildHeaderSettingsItem(column, totalVisibleLeafCount, parentHidden) {
1456
+ function buildHeaderSettingsItem(column, totalVisibleLeafCount, parentHidden, order, siblingCount) {
1324
1457
  const hidden = parentHidden || !!column.hidden;
1325
- const children = column.children?.filter((child) => !child.hideInSetting).map(
1326
- (child) => buildHeaderSettingsItem(child, totalVisibleLeafCount, hidden)
1458
+ const children = column.children?.flatMap(
1459
+ (child, index) => child.hideInSetting ? [] : [
1460
+ buildHeaderSettingsItem(
1461
+ child,
1462
+ totalVisibleLeafCount,
1463
+ hidden,
1464
+ index + 1,
1465
+ column.children.length
1466
+ )
1467
+ ]
1327
1468
  );
1328
1469
  const leafCount = countLeafColumns([column]);
1329
1470
  const visibleLeafCount = hidden ? 0 : countVisibleLeafColumns([column]);
@@ -1335,6 +1476,14 @@ function buildHeaderSettingsItem(column, totalVisibleLeafCount, parentHidden) {
1335
1476
  title: String(
1336
1477
  column.settingTitle ?? column.title ?? resolveColumnKey(column)
1337
1478
  ),
1479
+ dataIndex: column.dataIndex == null ? void 0 : utils.stringifyDataIndex(column.dataIndex),
1480
+ userDefined: column.userDefined === true,
1481
+ width: column.width,
1482
+ minWidth: column.minWidth,
1483
+ maxWidth: column.maxWidth,
1484
+ fixed: column.fixed,
1485
+ order,
1486
+ siblingCount,
1338
1487
  checked,
1339
1488
  indeterminate,
1340
1489
  disabled,
@@ -6511,12 +6660,12 @@ function queryFilterWorkerEntrypoint() {
6511
6660
  let filters = [];
6512
6661
  const matchedIndices = [];
6513
6662
  const now2 = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
6514
- const escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6663
+ const escapeRegExp3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6515
6664
  const wildcardToRegExp2 = (value) => {
6516
6665
  const pattern = Array.from(value).map((char) => {
6517
6666
  if (char === "%") return ".*";
6518
6667
  if (char === "_") return ".";
6519
- return escapeRegExp2(char);
6668
+ return escapeRegExp3(char);
6520
6669
  }).join("");
6521
6670
  return new RegExp(`^${pattern}$`, "i");
6522
6671
  };
@@ -11233,7 +11382,6 @@ function roundRectPath3(ctx, x, y, width, height, radius) {
11233
11382
  var TAG_HORIZONTAL_PADDING = 8;
11234
11383
  var TAG_HEIGHT = 22;
11235
11384
  var TAG_RADIUS = 4;
11236
- var TAG_TEXT_OPTICAL_OFFSET_Y = 1;
11237
11385
  var ANT_TAG_PRESET_MAP = {
11238
11386
  default: {
11239
11387
  backgroundColor: "#fafafa",
@@ -11379,7 +11527,7 @@ function drawBodyTagContent(params) {
11379
11527
  textIndent,
11380
11528
  textAlign: resolveContentTextAlign(content, "center")
11381
11529
  });
11382
- const centerY = top + height / 2 + TAG_TEXT_OPTICAL_OFFSET_Y;
11530
+ const centerY = rect.y + rect.height / 2;
11383
11531
  const textY = resolveCenteredTextBaselineY({
11384
11532
  centerY,
11385
11533
  ascent: bounds.ascent,
@@ -11588,7 +11736,7 @@ function drawBodyTextContent(params) {
11588
11736
  textHeight: bounds.height,
11589
11737
  color: theme.textSelectionBackgroundColor ?? "#99c2ff"
11590
11738
  });
11591
- if (searchMatch && searchMatch.rowIndex === rowIndex && searchMatch.columnKey === column.key && searchMatch.keyword) {
11739
+ if (searchMatch && searchMatch.rowIndex === rowIndex && searchMatch.columnKey === column.key && (searchMatch.contentIndex == null || searchMatch.contentIndex === contentIndex) && searchMatch.keyword) {
11592
11740
  const start = Math.max(0, Math.min(searchMatch.start, content.text.length));
11593
11741
  const end = Math.max(start, Math.min(searchMatch.end, content.text.length));
11594
11742
  const prefixWidth = ctx.measureText(content.text.slice(0, start)).width;
@@ -12344,6 +12492,12 @@ function resolveRawBodyCellContents(params) {
12344
12492
  }
12345
12493
  ];
12346
12494
  }
12495
+ if (column.cellType === "emoji") {
12496
+ return normalizeCellContents({
12497
+ type: "emoji",
12498
+ emoji: formattedText ?? String(value ?? "")
12499
+ });
12500
+ }
12347
12501
  if (column.cellType === "money") {
12348
12502
  return [
12349
12503
  {
@@ -12401,6 +12555,21 @@ function normalizeDrawableCellContents(contents) {
12401
12555
  ).map(normalizeCellContentItem);
12402
12556
  }
12403
12557
  function normalizeCellContentItem(content) {
12558
+ if (content.type === "emoji") {
12559
+ const { emoji, size, ...rest } = content;
12560
+ return {
12561
+ ...rest,
12562
+ type: "text",
12563
+ text: emoji,
12564
+ fontSize: size ?? 20,
12565
+ style: {
12566
+ ...content.style,
12567
+ fontSize: content.style?.fontSize ?? size ?? 20,
12568
+ fontWeight: content.style?.fontWeight ?? "normal",
12569
+ fontFamily: content.style?.fontFamily ?? '"Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Symbol", sans-serif'
12570
+ }
12571
+ };
12572
+ }
12404
12573
  if (content.type === "timeline") {
12405
12574
  return { ...content, style: { ...content.style, display: "block" } };
12406
12575
  }
@@ -13862,6 +14031,9 @@ function buildBodyContentsPlainText(contents) {
13862
14031
  if (content.type === "icon") {
13863
14032
  return content.icon;
13864
14033
  }
14034
+ if (content.type === "emoji") {
14035
+ return content.emoji;
14036
+ }
13865
14037
  if (content.type === "text" || content.type === "link" || content.type === "tag") {
13866
14038
  return content.text;
13867
14039
  }
@@ -14199,6 +14371,9 @@ function isCellContentDisabled(content) {
14199
14371
  if (content.type === "tag") {
14200
14372
  return false;
14201
14373
  }
14374
+ if (content.type === "emoji") {
14375
+ return false;
14376
+ }
14202
14377
  if (content.type === "money") {
14203
14378
  return false;
14204
14379
  }
@@ -18799,7 +18974,7 @@ function openCellEditingSession(params) {
18799
18974
  }
18800
18975
  const editorConfig = resolveEditorConfig(editConfig);
18801
18976
  const editingValue = initialValue === void 0 ? currentValue : initialValue;
18802
- const selectOnFocus = !(initialValue !== void 0 && (currentValue == null || currentValue === ""));
18977
+ const selectOnFocus = initialValue === void 0;
18803
18978
  const hostRect = toHostRect ? toHostRect(descriptor.rect) : descriptor.rect;
18804
18979
  const zoom = typeof theme.contentZoom === "number" && Number.isFinite(theme.contentZoom) && theme.contentZoom > 0 ? theme.contentZoom : 1;
18805
18980
  const paddingInline = (column.cellStyle?.paddingInline ?? DEFAULT_EDITOR_PADDING_INLINE) * zoom;
@@ -26225,6 +26400,134 @@ function measureHeaderWidth(ctx, column, theme) {
26225
26400
  return Math.ceil(contentWidth + HEADER_PADDING_INLINE * 2 + actionWidth);
26226
26401
  }
26227
26402
 
26403
+ // src/features/search/search-matcher.ts
26404
+ function createListTableSearchMatcher(keyword, options) {
26405
+ const pattern = options.mode === "regex" ? keyword : keyword.trim();
26406
+ if (!pattern) {
26407
+ return { findMatches: () => [], error: null, active: false };
26408
+ }
26409
+ if (options.mode === "fuzzy") {
26410
+ const needle = options.caseSensitive ? pattern : pattern.toLocaleLowerCase();
26411
+ return {
26412
+ error: null,
26413
+ active: true,
26414
+ findMatches: (text) => {
26415
+ const source = options.caseSensitive ? text : text.toLocaleLowerCase();
26416
+ const matches = [];
26417
+ let start = source.indexOf(needle);
26418
+ while (start >= 0) {
26419
+ matches.push({ start, end: start + pattern.length });
26420
+ start = source.indexOf(needle, start + needle.length);
26421
+ }
26422
+ return matches;
26423
+ }
26424
+ };
26425
+ }
26426
+ if (options.mode === "exact") {
26427
+ const needle = options.caseSensitive ? pattern : pattern.toLocaleLowerCase();
26428
+ return {
26429
+ error: null,
26430
+ active: true,
26431
+ findMatches: (text) => {
26432
+ const source = options.caseSensitive ? text : text.toLocaleLowerCase();
26433
+ return source === needle ? [{ start: 0, end: text.length }] : [];
26434
+ }
26435
+ };
26436
+ }
26437
+ try {
26438
+ const source = options.mode === "wildcard" ? wildcardToRegExpSource(pattern) : pattern;
26439
+ const expression = new RegExp(source, options.caseSensitive ? "g" : "gi");
26440
+ return {
26441
+ error: null,
26442
+ active: true,
26443
+ findMatches: (text) => {
26444
+ const matches = [];
26445
+ expression.lastIndex = 0;
26446
+ let result;
26447
+ while ((result = expression.exec(text)) !== null) {
26448
+ if (result[0].length > 0) {
26449
+ matches.push({ start: result.index, end: expression.lastIndex });
26450
+ } else {
26451
+ expression.lastIndex += 1;
26452
+ }
26453
+ }
26454
+ return matches;
26455
+ }
26456
+ };
26457
+ } catch {
26458
+ return { findMatches: () => [], error: "\u6B63\u5219\u8868\u8FBE\u5F0F\u65E0\u6548", active: false };
26459
+ }
26460
+ }
26461
+ function wildcardToRegExpSource(pattern) {
26462
+ let source = "";
26463
+ for (let index = 0; index < pattern.length; index += 1) {
26464
+ const char = pattern[index];
26465
+ if (char === "\\" && index + 1 < pattern.length) {
26466
+ source += escapeRegExp2(pattern[++index]);
26467
+ } else if (char === "*") {
26468
+ source += ".*";
26469
+ } else if (char === "?") {
26470
+ source += ".";
26471
+ } else {
26472
+ source += escapeRegExp2(char);
26473
+ }
26474
+ }
26475
+ return source;
26476
+ }
26477
+ function escapeRegExp2(value) {
26478
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26479
+ }
26480
+ function resolveListTableSearchCellTexts(params) {
26481
+ const { record, rowIndex, column } = params;
26482
+ if (!column.render && !column.dataFormat && !column.cellType) {
26483
+ return [
26484
+ {
26485
+ contentIndex: 0,
26486
+ text: String(utils.getValueByDataIndex(record, column.dataIndex) ?? "")
26487
+ }
26488
+ ];
26489
+ }
26490
+ return resolveBodyCellContents({ record, rowIndex, column }).flatMap(
26491
+ (content, contentIndex) => content.type === "text" ? [{ contentIndex, text: content.text }] : []
26492
+ );
26493
+ }
26494
+ function buildHeaderDataIndexOptions(rows, columns) {
26495
+ const paths = /* @__PURE__ */ new Set();
26496
+ const used = /* @__PURE__ */ new Map();
26497
+ const visitColumns = (items) => {
26498
+ items.forEach((column) => {
26499
+ if (column.dataIndex != null) {
26500
+ const path = utils.stringifyDataIndex(column.dataIndex);
26501
+ if (!used.has(path)) {
26502
+ used.set(
26503
+ path,
26504
+ String(column.settingTitle ?? column.title ?? column.key ?? path)
26505
+ );
26506
+ }
26507
+ }
26508
+ if (column.children?.length) visitColumns(column.children);
26509
+ });
26510
+ };
26511
+ visitColumns(columns);
26512
+ const visitValue = (value, prefix, depth) => {
26513
+ if (!value || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
26514
+ return;
26515
+ Object.entries(value).forEach(([key, child]) => {
26516
+ if (paths.size >= 200) return;
26517
+ const path = prefix ? `${prefix}.${key}` : key;
26518
+ paths.add(path);
26519
+ if (depth < 3) visitValue(child, path, depth + 1);
26520
+ });
26521
+ };
26522
+ rows.slice(0, 50).forEach((row) => visitValue(row, "", 0));
26523
+ used.forEach((_label, path) => paths.add(path));
26524
+ return [...paths].map((value) => ({
26525
+ value,
26526
+ disabled: used.has(value),
26527
+ ...used.has(value) ? { label: used.get(value) } : null
26528
+ }));
26529
+ }
26530
+
26228
26531
  // src/features/overlay/context-menu-builder.ts
26229
26532
  function createListTableContextMenuActions(params) {
26230
26533
  return {
@@ -26387,6 +26690,8 @@ function createListTableContextMenuActions(params) {
26387
26690
  onChangeZoom: params.onChangeZoom,
26388
26691
  onToggleCurrentColumn: params.onToggleCurrentColumn ?? params.onToggleColumn,
26389
26692
  onToggleColumn: params.onToggleColumn,
26693
+ onSaveColumn: params.onSaveColumn,
26694
+ onDeleteColumn: params.onDeleteColumn,
26390
26695
  onChangeCachePreference: params.onChangeCachePreference ? (category, enabled) => {
26391
26696
  params.hideMenu();
26392
26697
  params.onChangeCachePreference?.(category, enabled);
@@ -26455,6 +26760,7 @@ function buildListTableContextMenuConfig(params) {
26455
26760
  x,
26456
26761
  y,
26457
26762
  columns,
26763
+ dataSource,
26458
26764
  columnKey,
26459
26765
  isHeaderArea,
26460
26766
  isSummaryArea = false,
@@ -26479,6 +26785,7 @@ function buildListTableContextMenuConfig(params) {
26479
26785
  const isBodyArea = bodyRowIndex != null;
26480
26786
  const isSelectionColumn = columnKey === ROW_SELECTION_COLUMN_KEY || columnKey === ROW_SELECTOR_COLUMN_KEY;
26481
26787
  const enableCopy = enabledFeatures.has("copy");
26788
+ const enableChart = enabledFeatures.has("chart");
26482
26789
  const enableSearch = enabledFeatures.has("search");
26483
26790
  const enableExport = enabledFeatures.has("export");
26484
26791
  const enableDownloadContent = enabledFeatures.has("downloadContent");
@@ -26526,6 +26833,7 @@ function buildListTableContextMenuConfig(params) {
26526
26833
  return {
26527
26834
  x,
26528
26835
  y,
26836
+ onCreateChart: enableChart && (isBodyArea || isExpandedArea || isHeaderArea && columnKey !== ROW_SELECTION_COLUMN_KEY) ? params.onCreateChart : void 0,
26529
26837
  columns: enableColumnVisibility ? headerItems : void 0,
26530
26838
  density: enableDensity ? density : void 0,
26531
26839
  zoom: enableZoom ? normalizeContextMenuZoom(zoom) : void 0,
@@ -26614,6 +26922,9 @@ function buildListTableContextMenuConfig(params) {
26614
26922
  onChangeDensity: enableDensity ? actions.onChangeDensity : void 0,
26615
26923
  onChangeZoom: enableZoom ? actions.onChangeZoom : void 0,
26616
26924
  onToggleColumn: enableColumnVisibility && isTableSettingsArea ? actions.onToggleColumn : void 0,
26925
+ dataIndexOptions: enableColumnVisibility && isTableSettingsArea ? buildHeaderDataIndexOptions(dataSource ?? [], columns) : void 0,
26926
+ onSaveColumn: enableColumnVisibility && isTableSettingsArea ? actions.onSaveColumn : void 0,
26927
+ onDeleteColumn: enableColumnVisibility && isTableSettingsArea ? actions.onDeleteColumn : void 0,
26617
26928
  columnSettingsCacheAvailable: Boolean(params.storageKey),
26618
26929
  cachePreferences: params.cachePreferences,
26619
26930
  onChangeCachePreference: enabledFeatures.has("cache") ? actions.onChangeCachePreference : void 0,
@@ -26669,6 +26980,7 @@ function findSummaryColumn(columns, columnKey) {
26669
26980
  }
26670
26981
  var DEFAULT_CONTEXT_MENU_FEATURES = [
26671
26982
  "copy",
26983
+ "chart",
26672
26984
  "search",
26673
26985
  "export",
26674
26986
  "downloadContent",
@@ -26825,6 +27137,8 @@ function showHeaderContextMenu(params) {
26825
27137
  onChangeZoom,
26826
27138
  onToggleCurrentColumn,
26827
27139
  onToggleColumn,
27140
+ onSaveColumn,
27141
+ onDeleteColumn,
26828
27142
  onExpandAllCollapsedColumns,
26829
27143
  onClose,
26830
27144
  onToggleSummary,
@@ -26852,7 +27166,8 @@ function showHeaderContextMenu(params) {
26852
27166
  const isSummaryArea = result.area === "summary";
26853
27167
  const isExpandedArea = result.area === "expanded-row" || result.columnKey === "__expanded_row__";
26854
27168
  const isSelectionColumn = result.columnKey === ROW_SELECTION_COLUMN_KEY || result.columnKey === ROW_SELECTOR_COLUMN_KEY;
26855
- if (!isHeaderArea && !isBodyArea && !groupingBarArea && !isExpandedArea || !result.columnKey && !groupingBarArea && !isExpandedArea || isHeaderArea && isSelectionColumn) {
27169
+ const isChartCorner = isHeaderArea && result.columnKey === ROW_SELECTOR_COLUMN_KEY && resolveEnabledContextMenuFeatures(options.contextMenu).has("chart");
27170
+ if (!isHeaderArea && !isBodyArea && !groupingBarArea && !isExpandedArea || !result.columnKey && !groupingBarArea && !isExpandedArea || isHeaderArea && isSelectionColumn && !isChartCorner) {
26856
27171
  headerContextMenu.hide();
26857
27172
  return false;
26858
27173
  }
@@ -26865,7 +27180,7 @@ function showHeaderContextMenu(params) {
26865
27180
  fixedPopup.hide();
26866
27181
  hideBodyTooltip();
26867
27182
  const contextColumnKey = result.columnKey ?? "";
26868
- if (isHeaderArea) {
27183
+ if (isHeaderArea && !isChartCorner) {
26869
27184
  onSelectHeader(contextColumnKey, result.rect);
26870
27185
  }
26871
27186
  const bodyRowIndex = isBodyArea && !isSummaryArea && !isExpandedArea && typeof result.rowIndex === "number" ? result.rowIndex : null;
@@ -26881,11 +27196,13 @@ function showHeaderContextMenu(params) {
26881
27196
  x: event.clientX,
26882
27197
  y: event.clientY,
26883
27198
  columns: options.columns,
27199
+ dataSource: options.dataSource,
26884
27200
  columnKey: result.columnKey ?? "",
26885
27201
  isHeaderArea,
26886
27202
  isSummaryArea,
26887
27203
  isExpandedArea,
26888
27204
  onCopySummaryRowText: params.onCopySummaryRowText,
27205
+ onCreateChart: params.onCreateChart,
26889
27206
  onCopyImage: params.onCopyImage,
26890
27207
  onExportImage: params.onExportImage,
26891
27208
  canCopySelectionImage: params.canCopySelectionImage,
@@ -26937,6 +27254,8 @@ function showHeaderContextMenu(params) {
26937
27254
  onChangeZoom,
26938
27255
  onToggleCurrentColumn,
26939
27256
  onToggleColumn,
27257
+ onSaveColumn,
27258
+ onDeleteColumn,
26940
27259
  onExpandAllCollapsedColumns,
26941
27260
  onClose,
26942
27261
  onToggleSummary,
@@ -26976,6 +27295,77 @@ function resolveContextMenuDensity(rowHeight) {
26976
27295
  return rowHeight === 48 ? "middle" : "compact";
26977
27296
  }
26978
27297
 
27298
+ // src/features/overlay/chart-data.ts
27299
+ function createListTableChartData(params) {
27300
+ const {
27301
+ rows,
27302
+ columns,
27303
+ columnKey,
27304
+ rowIndex,
27305
+ selectionRange,
27306
+ selectedRowRange,
27307
+ selectedColumnRange
27308
+ } = params;
27309
+ const startRow = Math.max(
27310
+ selectionRange?.startRowIndex ?? selectedRowRange?.start ?? Math.max((rowIndex ?? 0) - 10, 0),
27311
+ 0
27312
+ );
27313
+ const endRow = Math.min(
27314
+ selectionRange?.endRowIndex ?? selectedRowRange?.end ?? startRow + 199,
27315
+ rows.length - 1
27316
+ );
27317
+ const selectedColumns = selectionRange ? columns.slice(
27318
+ Math.max(selectionRange.startColumnIndex, 0),
27319
+ selectionRange.endColumnIndex + 1
27320
+ ) : selectedRowRange ? columns : selectedColumnRange ? columns.slice(
27321
+ Math.max(selectedColumnRange.start, 0),
27322
+ selectedColumnRange.end + 1
27323
+ ) : columns.filter((column) => column.key === columnKey);
27324
+ const chartColumns = selectedColumns.filter(
27325
+ (column) => column.key !== ROW_SELECTION_COLUMN_KEY && column.key !== ROW_SELECTOR_COLUMN_KEY && column.key !== EXPAND_COLUMN_KEY
27326
+ ).slice(0, 12);
27327
+ if (startRow > endRow || chartColumns.length === 0) return null;
27328
+ const sampledRows = rows.slice(
27329
+ startRow,
27330
+ Math.min(endRow + 1, startRow + 200)
27331
+ );
27332
+ const values = chartColumns.map(
27333
+ (column) => sampledRows.map(
27334
+ (record, offset) => getCellClipboardText(record, startRow + offset, column)
27335
+ )
27336
+ );
27337
+ const numericColumns = chartColumns.flatMap((column, index) => {
27338
+ const numbers = values[index]?.map(parseChartNumber) ?? [];
27339
+ return numbers.some((value) => value !== null) ? [{ name: column.title, data: numbers.map((value) => value ?? 0) }] : [];
27340
+ });
27341
+ if (numericColumns.length === 0) return null;
27342
+ const labelIndex = chartColumns.findIndex(
27343
+ (_, index) => values[index]?.every((value) => parseChartNumber(value) === null)
27344
+ );
27345
+ if (sampledRows.length === 1 && chartColumns.length > 1) {
27346
+ return {
27347
+ labels: numericColumns.map((series) => series.name),
27348
+ series: [
27349
+ {
27350
+ name: values[labelIndex]?.[0] || String(startRow + 1),
27351
+ data: numericColumns.map((series) => series.data[0] ?? 0)
27352
+ }
27353
+ ]
27354
+ };
27355
+ }
27356
+ const labels = sampledRows.map(
27357
+ (_, index) => labelIndex >= 0 ? values[labelIndex]?.[index] || String(startRow + index + 1) : String(startRow + index + 1)
27358
+ );
27359
+ return { labels, series: numericColumns };
27360
+ }
27361
+ function parseChartNumber(value) {
27362
+ const normalized = value.trim().replace(/[,,\s¥¥$]/g, "");
27363
+ if (!normalized) return null;
27364
+ const percentage = normalized.endsWith("%");
27365
+ const parsed = Number(percentage ? normalized.slice(0, -1) : normalized);
27366
+ return Number.isFinite(parsed) ? parsed : null;
27367
+ }
27368
+
26979
27369
  // src/features/tooltip/tooltip-feature.ts
26980
27370
  function getHeaderActionTooltipText(result, sortState) {
26981
27371
  const area = result.area;
@@ -27997,6 +28387,11 @@ var EDITABLE_CELL_ROW_SELECTION_DELAY = 300;
27997
28387
  var ListTableEventController = class {
27998
28388
  constructor(host) {
27999
28389
  __publicField(this, "searchKeyword", "");
28390
+ __publicField(this, "searchOptions", {
28391
+ mode: "fuzzy",
28392
+ caseSensitive: false
28393
+ });
28394
+ __publicField(this, "searchError", null);
28000
28395
  __publicField(this, "searchMatches", []);
28001
28396
  __publicField(this, "searchCurrentIndex", -1);
28002
28397
  __publicField(this, "textSelectionDrag", null);
@@ -28559,6 +28954,15 @@ var ListTableEventController = class {
28559
28954
  getAllRows: () => this.host.options.dataSource,
28560
28955
  getExportSummaryEnabled: () => Boolean(this.host.currentRenderSnapshot?.hasSummary),
28561
28956
  onCopyImage: (scope) => this.copyImage(scope, event),
28957
+ onCreateChart: () => createListTableChartData({
28958
+ rows: this.host.getDisplayData(),
28959
+ columns: this.host.leafColumns,
28960
+ columnKey: contextResult.columnKey ?? "",
28961
+ rowIndex: typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null,
28962
+ selectionRange: this.host.rangeHighlight.selectedCellRange,
28963
+ selectedRowRange: this.host.rangeHighlight.selectedRowRange,
28964
+ selectedColumnRange: this.host.rangeHighlight.selectedColumnRange
28965
+ }),
28562
28966
  onExportImage: (scope) => this.copyImage(scope, event, true),
28563
28967
  canCopySelectionImage: resolveImageSelection(this.host) != null,
28564
28968
  onCopySummaryRowText: () => {
@@ -28591,15 +28995,21 @@ var ListTableEventController = class {
28591
28995
  this.host.selectHeader(columnKey, rect);
28592
28996
  },
28593
28997
  onSelectBodyRow: (rowIndex, columnKey) => {
28998
+ const selectedCellRange = this.host.rangeHighlight.selectedCellRange;
28999
+ const columnIndex = this.host.leafColumns.findIndex(
29000
+ (column) => column.key === columnKey
29001
+ );
28594
29002
  this.host.selection = {
28595
29003
  rowIndex,
28596
29004
  columnKey
28597
29005
  };
28598
29006
  this.host.clearSelectedHeader();
28599
- this.host.rangeHighlight = resolveNextRowHighlightForContextMenu({
28600
- current: this.host.rangeHighlight,
28601
- rowIndex
28602
- });
29007
+ if (!selectedCellRange || rowIndex < selectedCellRange.startRowIndex || rowIndex > selectedCellRange.endRowIndex || columnIndex < selectedCellRange.startColumnIndex || columnIndex > selectedCellRange.endColumnIndex) {
29008
+ this.host.rangeHighlight = resolveNextRowHighlightForContextMenu({
29009
+ current: this.host.rangeHighlight,
29010
+ rowIndex
29011
+ });
29012
+ }
28603
29013
  this.host.summarySelected = false;
28604
29014
  },
28605
29015
  getSelectionRange: () => this.host.rangeHighlight.selectedCellRange ?? null,
@@ -28730,6 +29140,8 @@ var ListTableEventController = class {
28730
29140
  onToggleColumn: (columnKey, visible) => {
28731
29141
  this.host.toggleHeaderColumnVisible(columnKey, visible);
28732
29142
  },
29143
+ onSaveColumn: (draft) => this.host.saveHeaderColumn(draft),
29144
+ onDeleteColumn: (columnKey) => this.host.deleteHeaderColumn(columnKey),
28733
29145
  onExpandAllCollapsedColumns: () => {
28734
29146
  this.host.expandAllCollapsedColumns();
28735
29147
  },
@@ -29530,11 +29942,17 @@ var ListTableEventController = class {
29530
29942
  publishSearchRequest() {
29531
29943
  const request = {
29532
29944
  keyword: this.searchKeyword,
29945
+ mode: this.searchOptions.mode,
29946
+ caseSensitive: this.searchOptions.caseSensitive,
29947
+ error: this.searchError,
29533
29948
  matchCount: this.searchMatches.length,
29534
29949
  currentIndex: this.searchCurrentIndex,
29535
- onSubmit: (keyword) => {
29950
+ onSubmit: (keyword, options) => {
29536
29951
  this.searchKeyword = keyword;
29537
- this.searchMatches = this.resolveSearchMatches(keyword);
29952
+ this.searchOptions = options;
29953
+ const matcher = createListTableSearchMatcher(keyword, options);
29954
+ this.searchError = matcher.error;
29955
+ this.searchMatches = matcher.active ? this.resolveSearchMatches(matcher.findMatches) : [];
29538
29956
  this.searchCurrentIndex = this.searchMatches.length > 0 ? 0 : -1;
29539
29957
  this.applyCurrentSearchMatch();
29540
29958
  this.publishSearchRequest();
@@ -29548,6 +29966,7 @@ var ListTableEventController = class {
29548
29966
  },
29549
29967
  onClose: () => {
29550
29968
  this.searchKeyword = "";
29969
+ this.searchError = null;
29551
29970
  this.searchMatches = [];
29552
29971
  this.searchCurrentIndex = -1;
29553
29972
  this.host.rangeHighlight = {
@@ -29560,30 +29979,24 @@ var ListTableEventController = class {
29560
29979
  };
29561
29980
  this.host.options.ui?.onSearchRequest?.(request);
29562
29981
  }
29563
- resolveSearchMatches(keyword) {
29564
- const normalizedKeyword = keyword.trim().toLocaleLowerCase();
29565
- if (!normalizedKeyword) return [];
29982
+ resolveSearchMatches(findMatches) {
29566
29983
  const matches = [];
29567
29984
  this.host.getDisplayData().forEach((record, rowIndex) => {
29568
29985
  this.host.leafColumns.forEach((column) => {
29569
29986
  if (isSystemColumnKey(column.key)) return;
29570
- const text = String(
29571
- utils.getValueByDataIndex(record, column.dataIndex) ?? ""
29987
+ resolveListTableSearchCellTexts({ record, rowIndex, column }).forEach(
29988
+ ({ text, contentIndex }) => {
29989
+ findMatches(text).forEach(({ start, end }) => {
29990
+ matches.push({
29991
+ rowIndex,
29992
+ columnKey: column.key,
29993
+ contentIndex,
29994
+ start,
29995
+ end
29996
+ });
29997
+ });
29998
+ }
29572
29999
  );
29573
- const source = text.toLocaleLowerCase();
29574
- let start = source.indexOf(normalizedKeyword);
29575
- while (start >= 0) {
29576
- matches.push({
29577
- rowIndex,
29578
- columnKey: column.key,
29579
- start,
29580
- end: start + normalizedKeyword.length
29581
- });
29582
- start = source.indexOf(
29583
- normalizedKeyword,
29584
- start + normalizedKeyword.length
29585
- );
29586
- }
29587
30000
  });
29588
30001
  });
29589
30002
  return matches;
@@ -31122,6 +31535,8 @@ function createListTableEventHost(core) {
31122
31535
  changeTableDensity: (density) => core.headerActionController.changeTableDensity(density),
31123
31536
  changeTableZoom: (zoom) => core.headerActionController.changeTableZoom(zoom),
31124
31537
  toggleHeaderColumnVisible: (columnKey, visible) => core.headerActionController.toggleHeaderColumnVisible(columnKey, visible),
31538
+ saveHeaderColumn: (draft) => core.headerActionController.saveHeaderColumn(draft),
31539
+ deleteHeaderColumn: (columnKey) => core.headerActionController.deleteHeaderColumn(columnKey),
31125
31540
  toggleHighlightedHeaderColumns: (columnKey, visible) => core.headerActionController.toggleHighlightedHeaderColumns(
31126
31541
  columnKey,
31127
31542
  visible
@@ -31473,6 +31888,34 @@ var ListTableHeaderActionController = class {
31473
31888
  });
31474
31889
  this.options.headerContextMenu.updateColumns(resolved.menuItems);
31475
31890
  }
31891
+ saveHeaderColumn(draft) {
31892
+ const columns = this.options.getOptions().columns;
31893
+ const error = saveHeaderSettingsColumn(columns, draft);
31894
+ if (error) return error;
31895
+ this.options.refreshRuntimeColumns();
31896
+ this.options.resetSummaryValues();
31897
+ this.options.persistColumnConfig();
31898
+ this.options.headerContextMenu.updateColumns(
31899
+ buildHeaderSettingsItems(columns)
31900
+ );
31901
+ this.options.requestRender();
31902
+ return null;
31903
+ }
31904
+ deleteHeaderColumn(columnKey) {
31905
+ const columns = this.options.getOptions().columns;
31906
+ if (!deleteHeaderSettingsColumn(columns, columnKey)) return false;
31907
+ this.options.setSortState(
31908
+ this.options.getSortState().filter((entry) => entry.key !== columnKey)
31909
+ );
31910
+ this.options.refreshRuntimeColumns();
31911
+ this.options.resetSummaryValues();
31912
+ this.options.persistColumnConfig();
31913
+ this.options.headerContextMenu.updateColumns(
31914
+ buildHeaderSettingsItems(columns)
31915
+ );
31916
+ this.options.requestRender();
31917
+ return true;
31918
+ }
31476
31919
  toggleHighlightedHeaderColumns(columnKey, visible) {
31477
31920
  if (visible) {
31478
31921
  this.toggleHeaderColumnVisible(columnKey, true);
@@ -31797,20 +32240,74 @@ function applyStoredColumnConfig(columns, storedColumns, cacheColumnSettings = t
31797
32240
  return false;
31798
32241
  }
31799
32242
  let columnWidthsChanged = false;
32243
+ if (cacheColumnSettings) {
32244
+ const existingKeys = /* @__PURE__ */ new Set();
32245
+ const collectKeys = (items) => {
32246
+ items.forEach((column) => {
32247
+ if (column.key) existingKeys.add(column.key);
32248
+ if (column.children?.length) collectKeys(column.children);
32249
+ });
32250
+ };
32251
+ collectKeys(columns);
32252
+ const findChildren = (items, parentKey) => {
32253
+ for (const column of items) {
32254
+ if (column.key === parentKey) return column.children ?? null;
32255
+ if (column.children?.length) {
32256
+ const children = findChildren(column.children, parentKey);
32257
+ if (children) return children;
32258
+ }
32259
+ }
32260
+ return null;
32261
+ };
32262
+ Object.entries(storedColumns).forEach(([key, stored]) => {
32263
+ if (!stored.userDefined || existingKeys.has(key) || !stored.dataIndex)
32264
+ return;
32265
+ const target = stored.parentKey ? findChildren(columns, stored.parentKey) : columns;
32266
+ (target ?? columns).push({
32267
+ key,
32268
+ title: stored.title ?? key,
32269
+ settingTitle: stored.title ?? key,
32270
+ dataIndex: stored.dataIndex,
32271
+ width: stored.width,
32272
+ minWidth: stored.minWidth,
32273
+ maxWidth: stored.maxWidth,
32274
+ fixed: stored.fixed,
32275
+ hidden: stored.hidden,
32276
+ userDefined: true,
32277
+ allowSort: true,
32278
+ sorter: true
32279
+ });
32280
+ existingKeys.add(key);
32281
+ });
32282
+ }
31800
32283
  const applyToColumns = (cols) => {
31801
32284
  if (cacheColumnSettings && columnOrder?.length) {
31802
- const rank = new Map(columnOrder.map((key, index) => [key, index]));
31803
- cols.sort(
31804
- (a, b) => (rank.get(a.key ?? "") ?? Infinity) - (rank.get(b.key ?? "") ?? Infinity)
31805
- );
32285
+ const rank = new Map(columnOrder.map((key, index2) => [key, index2]));
32286
+ const ranked = cols.filter((column) => rank.has(column.key ?? "")).sort((a, b) => rank.get(a.key ?? "") - rank.get(b.key ?? ""));
32287
+ let index = 0;
32288
+ cols.forEach((column, position) => {
32289
+ if (rank.has(column.key ?? "")) cols[position] = ranked[index++];
32290
+ });
31806
32291
  }
31807
32292
  for (const col of cols) {
31808
32293
  if (!col.key) {
31809
32294
  continue;
31810
32295
  }
31811
32296
  const stored = storedColumns[col.key];
31812
- if (stored) {
32297
+ if (stored && (!stored.userDefined || col.userDefined)) {
32298
+ if (cacheColumnSettings && stored.userTitle != null) {
32299
+ col.title = stored.userTitle;
32300
+ col.settingTitle = stored.userTitle;
32301
+ col.userTitle = stored.userTitle;
32302
+ }
31813
32303
  if (cacheColumnSettings) {
32304
+ if (col.userDefined && stored.userDefined) {
32305
+ col.title = stored.title ?? col.title;
32306
+ col.settingTitle = stored.title ?? col.settingTitle;
32307
+ col.dataIndex = stored.dataIndex ?? col.dataIndex;
32308
+ }
32309
+ if ("minWidth" in stored) col.minWidth = stored.minWidth;
32310
+ if ("maxWidth" in stored) col.maxWidth = stored.maxWidth;
31814
32311
  if (stored.width != null) {
31815
32312
  if ((col.width ?? 160) !== stored.width) columnWidthsChanged = true;
31816
32313
  col.width = stored.width;
@@ -31873,7 +32370,7 @@ function applyStoredColumnConfig(columns, storedColumns, cacheColumnSettings = t
31873
32370
  function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences) {
31874
32371
  const result = {};
31875
32372
  const columnOrder = [];
31876
- const collectColumns = (cols) => {
32373
+ const collectColumns = (cols, parentKey) => {
31877
32374
  for (const col of cols) {
31878
32375
  if (!col.key) {
31879
32376
  continue;
@@ -31882,8 +32379,19 @@ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryR
31882
32379
  result[col.key] = {
31883
32380
  ...cacheColumnSettings ? {
31884
32381
  width: col.width ?? 160,
32382
+ minWidth: col.minWidth,
32383
+ maxWidth: col.maxWidth,
31885
32384
  fixed: col.fixed,
31886
- hidden: !!col.hidden
32385
+ hidden: !!col.hidden,
32386
+ ...col.userTitle ? { userTitle: col.userTitle } : null,
32387
+ ...col.userDefined && col.dataIndex != null ? {
32388
+ userDefined: true,
32389
+ ...parentKey ? { parentKey } : null,
32390
+ title: String(col.settingTitle ?? col.title ?? col.key),
32391
+ dataIndex: col.dataIndex,
32392
+ minWidth: col.minWidth,
32393
+ maxWidth: col.maxWidth
32394
+ } : null
31887
32395
  } : null,
31888
32396
  // IndexedDB 保留 undefined 属性;字段存在即表示显式恢复/清除,避免默认配置回弹。
31889
32397
  align: col.align,
@@ -31907,7 +32415,7 @@ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryR
31907
32415
  } : false
31908
32416
  };
31909
32417
  if (col.children?.length) {
31910
- collectColumns(col.children);
32418
+ collectColumns(col.children, col.key);
31911
32419
  }
31912
32420
  }
31913
32421
  };
@@ -35295,6 +35803,12 @@ var ListTableCore = class {
35295
35803
  const incomingColumnsAreRuntimeColumns = options.columns === this.options.columns;
35296
35804
  const nextInitialColumns = incomingColumnsAreRuntimeColumns ? cloneColumns(host.initialColumns) : cloneColumns(options.columns);
35297
35805
  const nextInitialStretchColumns = incomingColumnsAreRuntimeColumns ? host.initialStretchColumns : options.stretchColumns;
35806
+ if (!shouldResetColumnConfig && !incomingColumnsAreRuntimeColumns && this.options.storageKey === options.storageKey) {
35807
+ preserveUserAddedColumns({
35808
+ nextColumns: options.columns,
35809
+ runtimeColumns: this.options.columns
35810
+ });
35811
+ }
35298
35812
  const shouldPreserveRuntimeWidths = !shouldResetColumnConfig && this.options.stretchColumns === false && options.stretchColumns === host.initialStretchColumns;
35299
35813
  if (shouldPreserveRuntimeWidths) {
35300
35814
  preserveRuntimeColumnWidths({