@canvas-components/list-table 0.3.19 → 0.3.21

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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { stringifyDataIndex, getValueByDataIndex, clamp, resolveCanvasScrollbarAxis, copyTextSync, copyText, exportTableToXlsx, formatPreciseNumber, addPreciseNumbers, comparePreciseNumbers, isSameDataIndex, dividePreciseNumber, exportTableToTxt, exportTableToCsv, downloadJsonFile, mapRowsToExportRecords, copyJson, copyRows, parseNumberValue, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, normalizeDecimalPlaces, roundPreciseNumber, isDateTimeFormatPattern, formatDateTimeValue, formatCustomNumberPattern, isPointInRect as isPointInRect$1 } from '@canvas-components/utils';
2
2
  import { drawBarcodeToCanvas } from '@canvas-components/barcode';
3
3
  import { drawCanvasChartToCanvas, resolveCanvasChartOption, getCanvasChartRenderer, resolveCanvasChartTooltipText } from '@canvas-components/chart';
4
+ import * as antDesignIcons from '@ant-design/icons-svg';
4
5
  import { drawQrCodeToCanvas } from '@canvas-components/qrcode';
5
6
  import { dispatchCanvasDebugNode } from '@canvas-components/stats';
6
7
 
@@ -4532,7 +4533,7 @@ function drawImagePlaceholder(ctx, x, y, width, height, backgroundColor = PLACEH
4532
4533
  ctx.fill();
4533
4534
  }
4534
4535
 
4535
- // src/rendering/cell-content/draw-symbol.ts
4536
+ // src/rendering/icons/draw-symbol.ts
4536
4537
  var SYMBOL_COLOR_MAP = {
4537
4538
  star: "#fadb14",
4538
4539
  sun: "#faad14",
@@ -4556,6 +4557,11 @@ function drawCellSymbol(ctx, params) {
4556
4557
  return;
4557
4558
  }
4558
4559
  appendCellSymbolPath(ctx, type, x, y, size);
4560
+ if (fillRatio >= 1) {
4561
+ ctx.fillStyle = activeColor;
4562
+ ctx.fill("evenodd");
4563
+ return;
4564
+ }
4559
4565
  ctx.fillStyle = inactiveColor;
4560
4566
  ctx.fill("evenodd");
4561
4567
  if (fillRatio <= 0) {
@@ -4603,6 +4609,12 @@ function drawStatusSymbol(ctx, params) {
4603
4609
  ctx.lineJoin = "round";
4604
4610
  ctx.lineWidth = Math.max(size * 0.12, 1.5);
4605
4611
  appendStatusSymbolPath(ctx, type, x, y, size);
4612
+ if (fillRatio >= 1) {
4613
+ ctx.strokeStyle = activeColor;
4614
+ ctx.stroke();
4615
+ ctx.restore();
4616
+ return;
4617
+ }
4606
4618
  ctx.strokeStyle = inactiveColor;
4607
4619
  ctx.stroke();
4608
4620
  if (fillRatio > 0) {
@@ -4666,6 +4678,838 @@ function appendMoonPath(ctx, x, y, size) {
4666
4678
  ctx.closePath();
4667
4679
  }
4668
4680
 
4681
+ // src/rendering/icons/icon-stroke.ts
4682
+ function resolveIconStroke(ctx) {
4683
+ const identity = (value) => value;
4684
+ const fallback = { width: 2, snapX: identity, snapY: identity };
4685
+ const transform = ctx.getTransform?.();
4686
+ if (!transform || ![
4687
+ transform.a,
4688
+ transform.b,
4689
+ transform.c,
4690
+ transform.d,
4691
+ transform.e,
4692
+ transform.f
4693
+ ].every(Number.isFinite))
4694
+ return fallback;
4695
+ if (Math.abs(transform.b) > 1e-8 || Math.abs(transform.c) > 1e-8 || transform.a <= 0 || Math.abs(transform.a - transform.d) > 1e-8)
4696
+ return fallback;
4697
+ const scale = transform.a;
4698
+ const pixels = Math.max(1, Math.round(2 * scale));
4699
+ const phase = pixels % 2 / 2;
4700
+ const snap = (value, translation) => (Math.round(value * scale + translation - phase) + phase - translation) / scale;
4701
+ return {
4702
+ width: pixels / scale,
4703
+ snapX: (value) => snap(value, transform.e),
4704
+ snapY: (value) => snap(value, transform.f)
4705
+ };
4706
+ }
4707
+ var listTableBuiltinIconNames = [
4708
+ "star",
4709
+ "sun",
4710
+ "moon",
4711
+ "check",
4712
+ "close",
4713
+ "circle",
4714
+ "check-circle",
4715
+ "close-circle",
4716
+ "clock-circle",
4717
+ "info-circle",
4718
+ "question-circle",
4719
+ "warning",
4720
+ "loading",
4721
+ "plus",
4722
+ "minus",
4723
+ "plus-circle",
4724
+ "minus-circle",
4725
+ "up",
4726
+ "down",
4727
+ "left",
4728
+ "right",
4729
+ "arrow-up",
4730
+ "arrow-down",
4731
+ "arrow-left",
4732
+ "arrow-right",
4733
+ "search",
4734
+ "edit",
4735
+ "delete",
4736
+ "copy",
4737
+ "save",
4738
+ "upload",
4739
+ "download",
4740
+ "reload",
4741
+ "ellipsis",
4742
+ "file",
4743
+ "file-text",
4744
+ "file-pdf",
4745
+ "folder",
4746
+ "folder-open",
4747
+ "picture",
4748
+ "user",
4749
+ "team",
4750
+ "calendar",
4751
+ "filter",
4752
+ "sort-ascending",
4753
+ "sort-descending",
4754
+ "link",
4755
+ "eye",
4756
+ "lock",
4757
+ "unlock",
4758
+ "home",
4759
+ "mail",
4760
+ "setting",
4761
+ "header-sort",
4762
+ "header-sort-ascending",
4763
+ "header-sort-descending",
4764
+ "header-filter",
4765
+ "header-fixed"
4766
+ ];
4767
+ var listTableAntdIconNames = Object.keys(
4768
+ antDesignIcons
4769
+ );
4770
+ var listTableIconNames = [
4771
+ ...listTableBuiltinIconNames,
4772
+ ...listTableAntdIconNames
4773
+ ];
4774
+ function getAntdIconDefinition(name) {
4775
+ return Object.prototype.hasOwnProperty.call(antDesignIcons, name) ? antDesignIcons[name] : void 0;
4776
+ }
4777
+
4778
+ // src/rendering/icons/svg-path.ts
4779
+ function parseSvgPath(source) {
4780
+ const output = [];
4781
+ let offset = 0, command = "", previous = "", x = 0, y = 0, startX = 0, startY = 0, controlX = 0, controlY = 0;
4782
+ const skip = () => {
4783
+ while (/[\s,]/.test(source[offset] ?? "!") && offset < source.length)
4784
+ offset++;
4785
+ };
4786
+ const number = (flag = false) => {
4787
+ skip();
4788
+ const match = source.slice(offset).match(flag ? /^[01]/ : /^[-+]?(?:\d*\.\d+|\d+\.?\d*)(?:[eE][-+]?\d+)?/);
4789
+ if (!match) throw new Error(`\u65E0\u6548 SVG \u8DEF\u5F84\u53C2\u6570\uFF0C\u4F4D\u7F6E ${offset}`);
4790
+ offset += match[0].length;
4791
+ const value = Number(match[0]);
4792
+ if (!Number.isFinite(value)) throw new Error("SVG \u8DEF\u5F84\u5305\u542B\u975E\u6709\u9650\u5750\u6807");
4793
+ return value;
4794
+ };
4795
+ while (offset < source.length) {
4796
+ skip();
4797
+ if (offset >= source.length) break;
4798
+ if (/[a-z]/i.test(source[offset])) command = source[offset++];
4799
+ else if (!command) throw new Error(`\u7F3A\u5C11 SVG \u8DEF\u5F84\u547D\u4EE4\uFF0C\u4F4D\u7F6E ${offset}`);
4800
+ const upper = command.toUpperCase();
4801
+ const relative = upper !== command;
4802
+ const point = () => [
4803
+ number() + (relative ? x : 0),
4804
+ number() + (relative ? y : 0)
4805
+ ];
4806
+ if (upper === "Z") {
4807
+ output.push({ op: "Z", args: [] });
4808
+ x = startX;
4809
+ y = startY;
4810
+ command = "";
4811
+ } else if (upper === "M" || upper === "L") {
4812
+ const target = point();
4813
+ output.push({ op: upper, args: target });
4814
+ [x, y] = target;
4815
+ if (upper === "M") {
4816
+ startX = x;
4817
+ startY = y;
4818
+ command = relative ? "l" : "L";
4819
+ }
4820
+ } else if (upper === "H" || upper === "V") {
4821
+ const value = number();
4822
+ if (upper === "H") x = value + (relative ? x : 0);
4823
+ else y = value + (relative ? y : 0);
4824
+ output.push({ op: "L", args: [x, y] });
4825
+ } else if (upper === "C" || upper === "S") {
4826
+ const first = upper === "C" ? point() : [
4827
+ /[CS]/.test(previous) ? 2 * x - controlX : x,
4828
+ /[CS]/.test(previous) ? 2 * y - controlY : y
4829
+ ];
4830
+ const second = point();
4831
+ const target = point();
4832
+ output.push({ op: "C", args: [...first, ...second, ...target] });
4833
+ [controlX, controlY] = second;
4834
+ [x, y] = target;
4835
+ } else if (upper === "Q" || upper === "T") {
4836
+ const control = upper === "Q" ? point() : [
4837
+ /[QT]/.test(previous) ? 2 * x - controlX : x,
4838
+ /[QT]/.test(previous) ? 2 * y - controlY : y
4839
+ ];
4840
+ const target = point();
4841
+ output.push({ op: "Q", args: [...control, ...target] });
4842
+ [controlX, controlY] = control;
4843
+ [x, y] = target;
4844
+ } else if (upper === "A") {
4845
+ const rx = number(), ry = number(), rotation = number(), large = number(true), sweep = number(true);
4846
+ const target = point();
4847
+ output.push(
4848
+ ...arcCommands(
4849
+ x,
4850
+ y,
4851
+ rx,
4852
+ ry,
4853
+ rotation,
4854
+ large,
4855
+ sweep,
4856
+ target[0],
4857
+ target[1]
4858
+ )
4859
+ );
4860
+ [x, y] = target;
4861
+ } else throw new Error(`\u4E0D\u652F\u6301\u7684 SVG \u8DEF\u5F84\u547D\u4EE4 ${command}`);
4862
+ previous = upper;
4863
+ }
4864
+ return output;
4865
+ }
4866
+ function arcCommands(x1, y1, rx, ry, rotation, large, sweep, x2, y2) {
4867
+ if (x1 === x2 && y1 === y2) return [];
4868
+ rx = Math.abs(rx);
4869
+ ry = Math.abs(ry);
4870
+ if (!rx || !ry) return [{ op: "L", args: [x2, y2] }];
4871
+ const phi = rotation * Math.PI / 180, cos = Math.cos(phi), sin = Math.sin(phi);
4872
+ const px = cos * (x1 - x2) / 2 + sin * (y1 - y2) / 2;
4873
+ const py = -sin * (x1 - x2) / 2 + cos * (y1 - y2) / 2;
4874
+ const scale = Math.max(
4875
+ 1,
4876
+ Math.sqrt(px * px / (rx * rx) + py * py / (ry * ry))
4877
+ );
4878
+ rx *= scale;
4879
+ ry *= scale;
4880
+ const denominator = rx * rx * py * py + ry * ry * px * px;
4881
+ const factor = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, (rx * rx * ry * ry - denominator) / denominator));
4882
+ const cxp = factor * rx * py / ry, cyp = -factor * ry * px / rx;
4883
+ const cx = cos * cxp - sin * cyp + (x1 + x2) / 2, cy = sin * cxp + cos * cyp + (y1 + y2) / 2;
4884
+ const start = Math.atan2((py - cyp) / ry, (px - cxp) / rx);
4885
+ const finish = Math.atan2((-py - cyp) / ry, (-px - cxp) / rx);
4886
+ let delta = finish - start;
4887
+ if (sweep && delta < 0) delta += Math.PI * 2;
4888
+ if (!sweep && delta > 0) delta -= Math.PI * 2;
4889
+ const count = Math.max(1, Math.ceil(Math.abs(delta) / (Math.PI / 2)));
4890
+ const step = delta / count;
4891
+ const point = (angle) => [
4892
+ cx + rx * cos * Math.cos(angle) - ry * sin * Math.sin(angle),
4893
+ cy + rx * sin * Math.cos(angle) + ry * cos * Math.sin(angle)
4894
+ ];
4895
+ const derivative = (angle) => [
4896
+ -rx * cos * Math.sin(angle) - ry * sin * Math.cos(angle),
4897
+ -rx * sin * Math.sin(angle) + ry * cos * Math.cos(angle)
4898
+ ];
4899
+ return Array.from({ length: count }, (_, index) => {
4900
+ const a = start + step * index, b = a + step, k = 4 / 3 * Math.tan(step / 4);
4901
+ const p = point(a), q = point(b), dp = derivative(a), dq = derivative(b);
4902
+ return {
4903
+ op: "C",
4904
+ args: [
4905
+ p[0] + k * dp[0],
4906
+ p[1] + k * dp[1],
4907
+ q[0] - k * dq[0],
4908
+ q[1] - k * dq[1],
4909
+ ...index === count - 1 ? [x2, y2] : q
4910
+ ]
4911
+ };
4912
+ });
4913
+ }
4914
+ function traceSvgPath(ctx, commands) {
4915
+ ctx.beginPath();
4916
+ for (const { op, args: a } of commands) {
4917
+ if (op === "M") ctx.moveTo(a[0], a[1]);
4918
+ else if (op === "L") ctx.lineTo(a[0], a[1]);
4919
+ else if (op === "C") ctx.bezierCurveTo(a[0], a[1], a[2], a[3], a[4], a[5]);
4920
+ else if (op === "Q") ctx.quadraticCurveTo(a[0], a[1], a[2], a[3]);
4921
+ else ctx.closePath();
4922
+ }
4923
+ }
4924
+
4925
+ // src/rendering/icons/draw-antd-icon.ts
4926
+ var pathCache = /* @__PURE__ */ new Map();
4927
+ var CACHE_LIMIT = 256;
4928
+ function secondaryColor(primary) {
4929
+ let hex = primary.replace(/^#/, "");
4930
+ if (/^[\da-f]{3}$/i.test(hex))
4931
+ hex = [...hex].map((char) => char + char).join("");
4932
+ if (!/^[\da-f]{6}$/i.test(hex)) return "#e6f4ff";
4933
+ const channels = [0, 2, 4].map(
4934
+ (start) => Math.round(255 * 0.85 + parseInt(hex.slice(start, start + 2), 16) * 0.15)
4935
+ );
4936
+ return `rgb(${channels.join(",")})`;
4937
+ }
4938
+ function drawAntdIcon(ctx, params) {
4939
+ const definition = getAntdIconDefinition(params.type);
4940
+ if (!definition) return false;
4941
+ const primary = Array.isArray(params.twoToneColor) ? params.twoToneColor[0] : params.twoToneColor ?? params.color;
4942
+ const secondary = Array.isArray(params.twoToneColor) ? params.twoToneColor[1] : secondaryColor(primary);
4943
+ const root = typeof definition.icon === "function" ? definition.icon(primary, secondary) : definition.icon;
4944
+ const [vx, vy, vw, vh] = (root.attrs.viewBox ?? "0 0 1024 1024").split(/[\s,]+/).map(Number);
4945
+ ctx.save();
4946
+ try {
4947
+ const scale = params.size / Math.max(vw, vh);
4948
+ ctx.translate(
4949
+ params.x + (params.size - vw * scale) / 2,
4950
+ params.y + (params.size - vh * scale) / 2
4951
+ );
4952
+ ctx.scale(scale, scale);
4953
+ ctx.translate(-vx, -vy);
4954
+ drawNode(ctx, root, primary);
4955
+ } finally {
4956
+ ctx.restore();
4957
+ }
4958
+ return true;
4959
+ }
4960
+ function drawNode(ctx, node, inheritedColor) {
4961
+ if (node.tag === "defs" || node.tag === "style") return;
4962
+ const color = node.attrs.fill ?? inheritedColor;
4963
+ ctx.save();
4964
+ try {
4965
+ const opacity = Number(node.attrs["fill-opacity"] ?? 1);
4966
+ ctx.globalAlpha *= Number.isFinite(opacity) ? Math.max(0, Math.min(opacity, 1)) : 1;
4967
+ if (node.tag === "path" && node.attrs.d && color !== "none") {
4968
+ ctx.fillStyle = color === "currentColor" ? inheritedColor : color;
4969
+ const data = node.attrs.d;
4970
+ let entry = pathCache.get(data);
4971
+ if (entry) pathCache.delete(data);
4972
+ else if (typeof Path2D === "function") {
4973
+ try {
4974
+ entry = { path: new Path2D(data) };
4975
+ } catch {
4976
+ entry = { commands: parseSvgPath(data) };
4977
+ }
4978
+ } else entry = { commands: parseSvgPath(data) };
4979
+ pathCache.set(data, entry);
4980
+ if (pathCache.size > CACHE_LIMIT)
4981
+ pathCache.delete(pathCache.keys().next().value);
4982
+ const rule = node.attrs["fill-rule"] === "evenodd" ? "evenodd" : "nonzero";
4983
+ if (entry.path) ctx.fill(entry.path, rule);
4984
+ else {
4985
+ traceSvgPath(ctx, entry.commands);
4986
+ ctx.fill(rule);
4987
+ }
4988
+ }
4989
+ for (const child of node.children ?? []) drawNode(ctx, child, color);
4990
+ } finally {
4991
+ ctx.restore();
4992
+ }
4993
+ }
4994
+
4995
+ // src/rendering/icons/draw-header-action-icon.ts
4996
+ var FILTER_ICON_VIEWBOX_SIZE = 1024;
4997
+ var FILTER_ICON_PATH_DATA = "M922.366667 114.813333A53.04 53.04 0 0 0 874.666667 85.333333H149.333333a53.333333 53.333333 0 0 0-42.666666 85.333334l277.333333 369.78V917.333333a21.333333 21.333333 0 0 0 29.253333 19.806667l213.333334-85.333333A21.333333 21.333333 0 0 0 640 832V540.446667L917.333333 170.666667a53.04 53.04 0 0 0 5.033334-55.853334z";
4998
+ var FIXED_PIN_VIEWBOX_SIZE = 1024;
4999
+ var FIXED_PIN_PATH_DATA = "M963.925333 326.997333L697.002667 60.074667a25.6 25.6 0 0 0-43.52 21.845333l14.506666 99.498667-273.066666 151.381333c-91.477333-45.738667-170.666667-36.693333-234.496 27.306667a25.941333 25.941333 0 0 0 0 36.352L327.68 563.2 57.685333 930.645333a25.6 25.6 0 0 0 35.84 35.669334l366.250667-270.677334 167.765333 167.936a25.941333 25.941333 0 0 0 36.352 0c79.530667-79.701333 58.538667-165.546667 26.965334-233.813333l152.064-273.066667 99.157333 14.165334a25.6 25.6 0 0 0 26.624-13.824 25.941333 25.941333 0 0 0-4.778667-30.037334z";
5000
+ var filterIconPath = null;
5001
+ var fixedPinPath = null;
5002
+ function drawHeaderActionGlyph(ctx, rect, actionType, color = "#8c8c8c", sortOrder = null, activeColor = "#1677ff") {
5003
+ ctx.save();
5004
+ ctx.strokeStyle = color;
5005
+ ctx.fillStyle = color;
5006
+ if (actionType === "sort") {
5007
+ ctx.beginPath();
5008
+ ctx.fillStyle = sortOrder === "ascend" ? activeColor : color;
5009
+ ctx.moveTo(rect.x + rect.width / 2, rect.y + 1);
5010
+ ctx.lineTo(rect.x + rect.width - 2, rect.y + 6);
5011
+ ctx.lineTo(rect.x + 2, rect.y + 6);
5012
+ ctx.closePath();
5013
+ ctx.fill();
5014
+ ctx.beginPath();
5015
+ ctx.fillStyle = sortOrder === "descend" ? activeColor : color;
5016
+ ctx.moveTo(rect.x + 2, rect.y + rect.height - 6);
5017
+ ctx.lineTo(rect.x + rect.width - 2, rect.y + rect.height - 6);
5018
+ ctx.lineTo(rect.x + rect.width / 2, rect.y + rect.height - 1);
5019
+ ctx.closePath();
5020
+ ctx.fill();
5021
+ } else if (actionType === "filter") {
5022
+ drawFilterIcon(ctx, rect);
5023
+ } else {
5024
+ drawFixedPinIcon(ctx, rect);
5025
+ }
5026
+ ctx.restore();
5027
+ }
5028
+ function drawFilterIcon(ctx, rect) {
5029
+ const iconWidth = rect.width * 0.92;
5030
+ const iconHeight = rect.height * 0.7;
5031
+ const offsetX = rect.x + (rect.width - iconWidth) / 2;
5032
+ const offsetY = rect.y + (rect.height - iconHeight) / 2;
5033
+ const path = resolveIconPath("filter");
5034
+ if (path) {
5035
+ ctx.save();
5036
+ ctx.translate(offsetX, offsetY);
5037
+ ctx.scale(
5038
+ iconWidth / FILTER_ICON_VIEWBOX_SIZE,
5039
+ iconHeight / FILTER_ICON_VIEWBOX_SIZE
5040
+ );
5041
+ ctx.fill(path);
5042
+ ctx.restore();
5043
+ return;
5044
+ }
5045
+ ctx.beginPath();
5046
+ ctx.moveTo(offsetX, offsetY);
5047
+ ctx.lineTo(offsetX + iconWidth, offsetY);
5048
+ ctx.lineTo(offsetX + iconWidth * 0.62, offsetY + iconHeight * 0.52);
5049
+ ctx.lineTo(offsetX + iconWidth * 0.62, offsetY + iconHeight);
5050
+ ctx.lineTo(offsetX + iconWidth * 0.38, offsetY + iconHeight * 0.88);
5051
+ ctx.lineTo(offsetX + iconWidth * 0.38, offsetY + iconHeight * 0.52);
5052
+ ctx.closePath();
5053
+ ctx.fill();
5054
+ }
5055
+ function drawFixedPinIcon(ctx, rect) {
5056
+ const iconSize = Math.min(rect.width, rect.height) * 0.92;
5057
+ const offsetX = rect.x + (rect.width - iconSize) / 2;
5058
+ const offsetY = rect.y + (rect.height - iconSize) / 2;
5059
+ const scale = iconSize / FIXED_PIN_VIEWBOX_SIZE;
5060
+ const path = resolveIconPath("fixed");
5061
+ if (path) {
5062
+ ctx.save();
5063
+ ctx.translate(offsetX, offsetY);
5064
+ ctx.scale(scale, scale);
5065
+ ctx.fill(path);
5066
+ ctx.restore();
5067
+ return;
5068
+ }
5069
+ ctx.beginPath();
5070
+ ctx.moveTo(offsetX + iconSize * 0.68, offsetY);
5071
+ ctx.lineTo(offsetX + iconSize, offsetY + iconSize * 0.32);
5072
+ ctx.lineTo(offsetX + iconSize * 0.78, offsetY + iconSize * 0.34);
5073
+ ctx.lineTo(offsetX + iconSize * 0.62, offsetY + iconSize * 0.64);
5074
+ ctx.lineTo(offsetX + iconSize * 0.74, offsetY + iconSize * 0.76);
5075
+ ctx.lineTo(offsetX + iconSize * 0.52, offsetY + iconSize * 0.82);
5076
+ ctx.lineTo(offsetX + iconSize * 0.16, offsetY + iconSize);
5077
+ ctx.lineTo(offsetX + iconSize * 0.34, offsetY + iconSize * 0.64);
5078
+ ctx.lineTo(offsetX + iconSize * 0.2, offsetY + iconSize * 0.5);
5079
+ ctx.lineTo(offsetX + iconSize * 0.5, offsetY + iconSize * 0.34);
5080
+ ctx.closePath();
5081
+ ctx.fill();
5082
+ }
5083
+ function resolveIconPath(type) {
5084
+ if (typeof Path2D === "undefined") return null;
5085
+ if (type === "filter") {
5086
+ filterIconPath ?? (filterIconPath = new Path2D(FILTER_ICON_PATH_DATA));
5087
+ return filterIconPath;
5088
+ }
5089
+ fixedPinPath ?? (fixedPinPath = new Path2D(FIXED_PIN_PATH_DATA));
5090
+ return fixedPinPath;
5091
+ }
5092
+
5093
+ // src/rendering/icons/draw-icon.ts
5094
+ function resolveIconColor(type, fallback = "#1f2329") {
5095
+ if (isLegacy(type)) return resolveCellSymbolColor(type);
5096
+ if (type === "check-circle") return "#52c41a";
5097
+ if (type === "close-circle") return "#ff4d4f";
5098
+ if (type === "warning") return "#faad14";
5099
+ if (type === "info-circle" || type === "question-circle") return "#1677ff";
5100
+ if (type.endsWith("TwoTone")) return "#1677ff";
5101
+ if (type.startsWith("header-")) return "#8c8c8c";
5102
+ return fallback;
5103
+ }
5104
+ function isLegacy(type) {
5105
+ return type === "star" || type === "sun" || type === "moon" || type === "check" || type === "close";
5106
+ }
5107
+ function drawIcon(ctx, params) {
5108
+ const { type, x, y, size } = params;
5109
+ if (![x, y, size].every(Number.isFinite) || size <= 0) return;
5110
+ const color = params.color ?? resolveIconColor(type);
5111
+ if (drawAntdIcon(ctx, { ...params, color })) return;
5112
+ ctx.save();
5113
+ try {
5114
+ if (type.startsWith("header-")) {
5115
+ ctx.translate(x, y);
5116
+ ctx.scale(size / 14, size / 14);
5117
+ drawHeaderActionGlyph(
5118
+ ctx,
5119
+ { x: 0, y: 0, width: 14, height: 14 },
5120
+ type === "header-filter" ? "filter" : type === "header-fixed" ? "fixed" : "sort",
5121
+ color,
5122
+ type === "header-sort-ascending" ? "ascend" : type === "header-sort-descending" ? "descend" : null,
5123
+ params.color ?? "#1677ff"
5124
+ );
5125
+ return;
5126
+ }
5127
+ if (isLegacy(type)) {
5128
+ drawCellSymbol(ctx, {
5129
+ type,
5130
+ x,
5131
+ y,
5132
+ size,
5133
+ activeColor: color,
5134
+ inactiveColor: color,
5135
+ fillRatio: 1
5136
+ });
5137
+ return;
5138
+ }
5139
+ ctx.translate(x, y);
5140
+ ctx.scale(size / 24, size / 24);
5141
+ ctx.strokeStyle = color;
5142
+ ctx.fillStyle = color;
5143
+ const stroke = resolveIconStroke(ctx);
5144
+ ctx.lineWidth = stroke.width;
5145
+ ctx.lineCap = "round";
5146
+ ctx.lineJoin = "round";
5147
+ ctx.setLineDash([]);
5148
+ appendIconPath(ctx, type, stroke);
5149
+ ctx.stroke();
5150
+ } finally {
5151
+ ctx.restore();
5152
+ }
5153
+ }
5154
+ function appendIconPath(ctx, type, stroke) {
5155
+ const line = (...points) => {
5156
+ const aligned = [...points];
5157
+ for (let i = 2; i < points.length; i += 2) {
5158
+ if (points[i - 2] === points[i])
5159
+ aligned[i - 2] = aligned[i] = stroke.snapX(points[i]);
5160
+ if (points[i - 1] === points[i + 1])
5161
+ aligned[i - 1] = aligned[i + 1] = stroke.snapY(points[i + 1]);
5162
+ }
5163
+ ctx.moveTo(aligned[0], aligned[1]);
5164
+ for (let i = 2; i < aligned.length; i += 2)
5165
+ ctx.lineTo(aligned[i], aligned[i + 1]);
5166
+ };
5167
+ const rect = (x, y, width, height) => {
5168
+ const left = stroke.snapX(x);
5169
+ const top = stroke.snapY(y);
5170
+ ctx.rect(
5171
+ left,
5172
+ top,
5173
+ stroke.snapX(x + width) - left,
5174
+ stroke.snapY(y + height) - top
5175
+ );
5176
+ };
5177
+ const circle = (x = 12, y = 12, r = 9) => {
5178
+ ctx.moveTo(x + r, y);
5179
+ ctx.arc(x, y, r, 0, Math.PI * 2);
5180
+ };
5181
+ const plus = () => {
5182
+ line(7, 12, 17, 12);
5183
+ line(12, 7, 12, 17);
5184
+ };
5185
+ const check = () => line(7, 12, 10.5, 15.5, 17, 8.5);
5186
+ const cross = () => {
5187
+ line(8, 8, 16, 16);
5188
+ line(16, 8, 8, 16);
5189
+ };
5190
+ const file = () => {
5191
+ line(5, 3, 14, 3, 19, 8, 19, 21, 5, 21, 5, 3);
5192
+ line(14, 3, 14, 8, 19, 8);
5193
+ };
5194
+ ctx.beginPath();
5195
+ switch (type) {
5196
+ case "circle":
5197
+ circle();
5198
+ break;
5199
+ case "check-circle":
5200
+ circle();
5201
+ check();
5202
+ break;
5203
+ case "close-circle":
5204
+ circle();
5205
+ cross();
5206
+ break;
5207
+ case "clock-circle":
5208
+ circle();
5209
+ line(12, 6, 12, 12, 16, 14);
5210
+ break;
5211
+ case "info-circle":
5212
+ circle();
5213
+ line(12, 11, 12, 17);
5214
+ circle(12, 7, 0.4);
5215
+ break;
5216
+ case "question-circle":
5217
+ circle();
5218
+ ctx.moveTo(9, 8);
5219
+ ctx.bezierCurveTo(9, 4, 18, 6, 14, 11);
5220
+ line(14, 11, 12, 13, 12, 14);
5221
+ circle(12, 17, 0.4);
5222
+ break;
5223
+ case "warning":
5224
+ line(12, 3, 22, 20, 2, 20, 12, 3);
5225
+ line(12, 9, 12, 13);
5226
+ circle(12, 17, 0.4);
5227
+ break;
5228
+ case "loading":
5229
+ ctx.arc(12, 12, 9, -Math.PI / 2, Math.PI);
5230
+ break;
5231
+ case "plus":
5232
+ plus();
5233
+ break;
5234
+ case "minus":
5235
+ line(5, 12, 19, 12);
5236
+ break;
5237
+ case "plus-circle":
5238
+ circle();
5239
+ plus();
5240
+ break;
5241
+ case "minus-circle":
5242
+ circle();
5243
+ line(7, 12, 17, 12);
5244
+ break;
5245
+ case "up":
5246
+ line(5, 15, 12, 8, 19, 15);
5247
+ break;
5248
+ case "down":
5249
+ line(5, 9, 12, 16, 19, 9);
5250
+ break;
5251
+ case "left":
5252
+ line(15, 5, 8, 12, 15, 19);
5253
+ break;
5254
+ case "right":
5255
+ line(9, 5, 16, 12, 9, 19);
5256
+ break;
5257
+ case "arrow-up":
5258
+ line(12, 21, 12, 3);
5259
+ line(5, 10, 12, 3, 19, 10);
5260
+ break;
5261
+ case "arrow-down":
5262
+ line(12, 3, 12, 21);
5263
+ line(5, 14, 12, 21, 19, 14);
5264
+ break;
5265
+ case "arrow-left":
5266
+ line(21, 12, 3, 12);
5267
+ line(10, 5, 3, 12, 10, 19);
5268
+ break;
5269
+ case "arrow-right":
5270
+ line(3, 12, 21, 12);
5271
+ line(14, 5, 21, 12, 14, 19);
5272
+ break;
5273
+ case "search":
5274
+ circle(10, 10, 6.5);
5275
+ line(15, 15, 21, 21);
5276
+ break;
5277
+ case "edit":
5278
+ line(4, 15, 16, 3, 21, 8, 9, 20, 3, 21, 4, 15);
5279
+ line(13, 6, 18, 11);
5280
+ break;
5281
+ case "delete":
5282
+ line(3, 6, 21, 6);
5283
+ line(8, 6, 8, 3, 16, 3, 16, 6);
5284
+ line(5, 6, 6, 21, 18, 21, 19, 6);
5285
+ line(10, 10, 10, 17);
5286
+ line(14, 10, 14, 17);
5287
+ break;
5288
+ case "copy":
5289
+ rect(8, 8, 13, 13);
5290
+ line(16, 5, 16, 3, 3, 3, 3, 16, 5, 16);
5291
+ break;
5292
+ case "save":
5293
+ line(3, 3, 17, 3, 21, 7, 21, 21, 3, 21, 3, 3);
5294
+ rect(7, 3, 9, 6);
5295
+ rect(7, 14, 10, 7);
5296
+ break;
5297
+ case "upload":
5298
+ line(12, 17, 12, 3);
5299
+ line(7, 8, 12, 3, 17, 8);
5300
+ line(3, 16, 3, 21, 21, 21, 21, 16);
5301
+ break;
5302
+ case "download":
5303
+ line(12, 3, 12, 17);
5304
+ line(7, 12, 12, 17, 17, 12);
5305
+ line(3, 16, 3, 21, 21, 21, 21, 16);
5306
+ break;
5307
+ case "reload": {
5308
+ const end = Math.PI * 11 / 6;
5309
+ ctx.arc(12, 12, 8, Math.PI / 6, end);
5310
+ const tipX = 12 + 8 * Math.cos(end);
5311
+ const tipY = 12 + 8 * Math.sin(end);
5312
+ const arrowAngle = end + Math.PI / 2 - Math.PI / 9;
5313
+ const tangentX = Math.cos(arrowAngle);
5314
+ const tangentY = Math.sin(arrowAngle);
5315
+ const baseX = tipX - tangentX * 4;
5316
+ const baseY = tipY - tangentY * 4;
5317
+ line(
5318
+ baseX - tangentY * 3,
5319
+ baseY + tangentX * 3,
5320
+ tipX,
5321
+ tipY,
5322
+ baseX + tangentY * 3,
5323
+ baseY - tangentX * 3
5324
+ );
5325
+ break;
5326
+ }
5327
+ case "ellipsis":
5328
+ circle(5, 12, 1);
5329
+ circle(12, 12, 1);
5330
+ circle(19, 12, 1);
5331
+ break;
5332
+ case "file":
5333
+ file();
5334
+ break;
5335
+ case "file-text":
5336
+ file();
5337
+ line(8, 12, 16, 12);
5338
+ line(8, 16, 16, 16);
5339
+ break;
5340
+ case "file-pdf":
5341
+ file();
5342
+ line(8, 17, 8, 11, 11, 11, 12, 12, 12, 14, 8, 14);
5343
+ line(14, 17, 14, 12, 17, 12);
5344
+ line(14, 14.5, 16, 14.5);
5345
+ break;
5346
+ case "folder":
5347
+ line(2, 5, 9, 5, 11, 8, 22, 8, 22, 20, 2, 20, 2, 5);
5348
+ break;
5349
+ case "folder-open":
5350
+ line(2, 18, 2, 5, 9, 5, 11, 8, 20, 8, 20, 10);
5351
+ line(2, 20, 6, 11, 22, 11, 18, 20, 2, 20);
5352
+ break;
5353
+ case "picture":
5354
+ rect(3, 3, 18, 18);
5355
+ circle(8, 8, 2);
5356
+ line(3, 19, 9, 13, 13, 16, 17, 11, 21, 15);
5357
+ break;
5358
+ case "user":
5359
+ circle(12, 7, 4);
5360
+ ctx.moveTo(4, 21);
5361
+ ctx.bezierCurveTo(4, 10, 20, 10, 20, 21);
5362
+ break;
5363
+ case "team":
5364
+ circle(9, 7, 3.5);
5365
+ ctx.moveTo(2, 21);
5366
+ ctx.bezierCurveTo(2, 11, 16, 11, 16, 21);
5367
+ ctx.moveTo(17, 4);
5368
+ ctx.arc(17, 7, 3, -Math.PI / 2, Math.PI / 2);
5369
+ ctx.moveTo(18, 14);
5370
+ ctx.bezierCurveTo(22, 14, 22, 18, 22, 21);
5371
+ break;
5372
+ case "calendar":
5373
+ rect(3, 5, 18, 16);
5374
+ line(3, 10, 21, 10);
5375
+ line(8, 2, 8, 7);
5376
+ line(16, 2, 16, 7);
5377
+ line(7, 14, 10, 14);
5378
+ line(14, 14, 17, 14);
5379
+ line(7, 17, 10, 17);
5380
+ break;
5381
+ case "filter":
5382
+ line(3, 3, 21, 3, 14, 12, 14, 20, 10, 22, 10, 12, 3, 3);
5383
+ break;
5384
+ case "sort-ascending":
5385
+ line(4, 6, 18, 6);
5386
+ line(4, 12, 13, 12);
5387
+ line(4, 18, 8, 18);
5388
+ line(19, 10, 19, 21);
5389
+ line(16, 18, 19, 21, 22, 18);
5390
+ break;
5391
+ case "sort-descending":
5392
+ line(4, 6, 8, 6);
5393
+ line(4, 12, 13, 12);
5394
+ line(4, 18, 15, 18);
5395
+ line(19, 3, 19, 14);
5396
+ line(16, 6, 19, 3, 22, 6);
5397
+ break;
5398
+ case "link":
5399
+ line(8, 16, 16, 8);
5400
+ ctx.moveTo(10, 6);
5401
+ ctx.bezierCurveTo(16, -2, 26, 8, 18, 14);
5402
+ ctx.moveTo(14, 18);
5403
+ ctx.bezierCurveTo(8, 26, -2, 16, 6, 10);
5404
+ break;
5405
+ case "eye":
5406
+ ctx.moveTo(2, 12);
5407
+ ctx.bezierCurveTo(8, 2, 16, 2, 22, 12);
5408
+ ctx.bezierCurveTo(16, 22, 8, 22, 2, 12);
5409
+ circle(12, 12, 3);
5410
+ break;
5411
+ case "lock":
5412
+ rect(5, 10, 14, 11);
5413
+ ctx.moveTo(8, 10);
5414
+ ctx.lineTo(8, 7);
5415
+ ctx.arc(12, 7, 4, Math.PI, Math.PI * 2);
5416
+ ctx.lineTo(16, 10);
5417
+ line(12, 14, 12, 17);
5418
+ break;
5419
+ case "unlock":
5420
+ rect(5, 10, 14, 11);
5421
+ ctx.moveTo(8, 10);
5422
+ ctx.lineTo(8, 6);
5423
+ ctx.arc(12, 6, 4, Math.PI, Math.PI * 2);
5424
+ line(12, 14, 12, 17);
5425
+ break;
5426
+ case "home":
5427
+ line(2, 11, 12, 2, 22, 11);
5428
+ line(5, 9, 5, 21, 19, 21, 19, 9);
5429
+ line(9, 21, 9, 14, 15, 14, 15, 21);
5430
+ break;
5431
+ case "mail":
5432
+ rect(3, 5, 18, 14);
5433
+ line(3, 5, 12, 13, 21, 5);
5434
+ break;
5435
+ case "setting": {
5436
+ for (let i = 0; i <= 32; i++) {
5437
+ const angle = i / 32 * Math.PI * 2;
5438
+ const radius = i % 4 < 2 ? 10 : 8;
5439
+ const x = 12 + radius * Math.cos(angle);
5440
+ const y = 12 + radius * Math.sin(angle);
5441
+ if (!i) ctx.moveTo(x, y);
5442
+ else ctx.lineTo(x, y);
5443
+ }
5444
+ ctx.closePath();
5445
+ circle(12, 12, 3.5);
5446
+ break;
5447
+ }
5448
+ }
5449
+ }
5450
+
5451
+ // src/rendering/icons/draw-timeline-marker.ts
5452
+ var COLORS = {
5453
+ blue: "#1677ff",
5454
+ green: "#52c41a",
5455
+ red: "#ff4d4f",
5456
+ gray: "#8c8c8c"
5457
+ };
5458
+ function drawTimelineMarker(ctx, item, x, y, size, filled) {
5459
+ const color = COLORS[item.color ?? "blue"] ?? item.color ?? COLORS.blue;
5460
+ const radius = size / 2;
5461
+ ctx.save();
5462
+ try {
5463
+ if (item.loading) {
5464
+ drawIcon(ctx, {
5465
+ type: "loading",
5466
+ x: x - radius,
5467
+ y: y - radius,
5468
+ size,
5469
+ color
5470
+ });
5471
+ return;
5472
+ }
5473
+ const icon = item.icon ?? "circle";
5474
+ const type = icon === "cross" ? "close" : icon === "clock" ? "clock-circle" : icon;
5475
+ if (!filled && type.endsWith("-circle")) {
5476
+ const iconSize = size * 4 / 3;
5477
+ drawIcon(ctx, {
5478
+ type,
5479
+ x: x - iconSize / 2,
5480
+ y: y - iconSize / 2,
5481
+ size: iconSize,
5482
+ color
5483
+ });
5484
+ return;
5485
+ }
5486
+ ctx.strokeStyle = color;
5487
+ ctx.fillStyle = color;
5488
+ ctx.lineWidth = 2;
5489
+ ctx.beginPath();
5490
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
5491
+ if (filled) ctx.fill();
5492
+ else ctx.stroke();
5493
+ if (icon === "circle") return;
5494
+ drawIcon(ctx, {
5495
+ type,
5496
+ x: x - size * 0.36,
5497
+ y: y - size * 0.36,
5498
+ size: size * 0.72,
5499
+ color: filled ? "#fff" : color
5500
+ });
5501
+ } finally {
5502
+ ctx.restore();
5503
+ }
5504
+ }
5505
+
5506
+ // src/rendering/icons/file-type-icon.ts
5507
+ function createFileTypeIconSource(filename) {
5508
+ const extension = (filename.split(".").length > 1 ? filename.split(".").pop() : "FILE").toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 4) || "FILE";
5509
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28"><path d="M5 1h12l6 6v20H5z" fill="#eef4ff" stroke="#8caadb"/><path d="M17 1v7h6" fill="none" stroke="#8caadb"/><text x="14" y="20" text-anchor="middle" font-family="sans-serif" font-size="7" fill="#315b91">${extension}</text></svg>`;
5510
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
5511
+ }
5512
+
4669
5513
  // src/rendering/cell-content/draw-icon-content.ts
4670
5514
  var DEFAULT_ICON_SIZE = 20;
4671
5515
  function drawBodyIconContent(params) {
@@ -4685,17 +5529,16 @@ function drawBodyIconContent(params) {
4685
5529
  const size = Math.min(width, height);
4686
5530
  const left = cursorX + (width - size) / 2;
4687
5531
  const top = rect.y + (rect.height - size) / 2;
4688
- const color = content.style?.color ?? content.color ?? resolveCellSymbolColor(content.icon);
5532
+ const color = content.style?.color ?? content.color ?? (content.icon.startsWith("header-") ? void 0 : resolveIconColor(content.icon, params.theme?.textColor));
4689
5533
  ctx.save();
4690
5534
  ctx.globalAlpha *= resolveContentOpacity(content);
4691
- drawCellSymbol(ctx, {
5535
+ drawIcon(ctx, {
4692
5536
  type: content.icon,
4693
5537
  x: left,
4694
5538
  y: top,
4695
5539
  size,
4696
- activeColor: color,
4697
- inactiveColor: color,
4698
- fillRatio: 1
5540
+ color,
5541
+ twoToneColor: content.twoToneColor
4699
5542
  });
4700
5543
  ctx.restore();
4701
5544
  if (content.onClick || content.cursor) {
@@ -7494,7 +8337,7 @@ function resolveHeaderTextLayout(params) {
7494
8337
  Math.min(baseX - textLeft, textRight - baseX) * 2,
7495
8338
  0
7496
8339
  );
7497
- if (centeredWidth >= 12) {
8340
+ if (centeredWidth >= Math.max(params.contentWidth ?? 0, 12)) {
7498
8341
  return {
7499
8342
  align,
7500
8343
  x: baseX,
@@ -7553,12 +8396,6 @@ var ACTION_GAP2 = 4;
7553
8396
  var ACTION_SIZE2 = 14;
7554
8397
  var ACTION_PADDING_INLINE2 = 10;
7555
8398
  var GRID_LINE_OFFSET = 0.5;
7556
- var FILTER_ICON_VIEWBOX_SIZE = 1024;
7557
- var FILTER_ICON_PATH_DATA = "M922.366667 114.813333A53.04 53.04 0 0 0 874.666667 85.333333H149.333333a53.333333 53.333333 0 0 0-42.666666 85.333334l277.333333 369.78V917.333333a21.333333 21.333333 0 0 0 29.253333 19.806667l213.333334-85.333333A21.333333 21.333333 0 0 0 640 832V540.446667L917.333333 170.666667a53.04 53.04 0 0 0 5.033334-55.853334z";
7558
- var FIXED_PIN_VIEWBOX_SIZE = 1024;
7559
- var FIXED_PIN_PATH_DATA = "M963.925333 326.997333L697.002667 60.074667a25.6 25.6 0 0 0-43.52 21.845333l14.506666 99.498667-273.066666 151.381333c-91.477333-45.738667-170.666667-36.693333-234.496 27.306667a25.941333 25.941333 0 0 0 0 36.352L327.68 563.2 57.685333 930.645333a25.6 25.6 0 0 0 35.84 35.669334l366.250667-270.677334 167.765333 167.936a25.941333 25.941333 0 0 0 36.352 0c79.530667-79.701333 58.538667-165.546667 26.965334-233.813333l152.064-273.066667 99.157333 14.165334a25.6 25.6 0 0 0 26.624-13.824 25.941333 25.941333 0 0 0-4.778667-30.037334z";
7560
- var filterIconPath = null;
7561
- var fixedPinPath = null;
7562
8399
  var EMPTY_RANGE_HIGHLIGHT = {
7563
8400
  selectedColumnRange: null
7564
8401
  };
@@ -7954,9 +8791,14 @@ function isSameRect(left, right) {
7954
8791
  }
7955
8792
  function drawResizeHandles(params, rect, columnKey, fixed) {
7956
8793
  if (params.isResizable(columnKey)) {
8794
+ const rightHandleRect = getResizeHandleRect(params, rect, "right");
8795
+ const keys = params.leafColumnKeys;
8796
+ if (keys?.[keys.length - 1] === columnKey) {
8797
+ rightHandleRect.width += 5;
8798
+ }
7957
8799
  params.descriptors.push({
7958
8800
  kind: "resize-handle",
7959
- rect: getResizeHandleRect(params, rect, "right"),
8801
+ rect: rightHandleRect,
7960
8802
  columnKey,
7961
8803
  resizeEdge: "right",
7962
8804
  fixed
@@ -8040,95 +8882,13 @@ function drawHeaderActionIcon(ctx, rect, actionType, active, hovered = false, se
8040
8882
  ctx.fill();
8041
8883
  ctx.restore();
8042
8884
  }
8043
- const color = active ? "#1677ff" : "#8c8c8c";
8044
- ctx.save();
8045
- ctx.strokeStyle = color;
8046
- ctx.fillStyle = color;
8047
- if (actionType === "sort") {
8048
- ctx.beginPath();
8049
- ctx.fillStyle = sortOrder === "ascend" ? "#1677ff" : "#8c8c8c";
8050
- ctx.moveTo(rect.x + rect.width / 2, rect.y + 1);
8051
- ctx.lineTo(rect.x + rect.width - 2, rect.y + 6);
8052
- ctx.lineTo(rect.x + 2, rect.y + 6);
8053
- ctx.closePath();
8054
- ctx.fill();
8055
- ctx.beginPath();
8056
- ctx.fillStyle = sortOrder === "descend" ? "#1677ff" : "#8c8c8c";
8057
- ctx.moveTo(rect.x + 2, rect.y + rect.height - 6);
8058
- ctx.lineTo(rect.x + rect.width - 2, rect.y + rect.height - 6);
8059
- ctx.lineTo(rect.x + rect.width / 2, rect.y + rect.height - 1);
8060
- ctx.closePath();
8061
- ctx.fill();
8062
- } else if (actionType === "filter") {
8063
- drawFilterIcon(ctx, rect);
8064
- } else {
8065
- drawFixedPinIcon(ctx, rect);
8066
- }
8067
- ctx.restore();
8068
- }
8069
- function drawFilterIcon(ctx, rect) {
8070
- const iconWidth = rect.width * 0.92;
8071
- const iconHeight = rect.height * 0.7;
8072
- const offsetX = rect.x + (rect.width - iconWidth) / 2;
8073
- const offsetY = rect.y + (rect.height - iconHeight) / 2;
8074
- const path = resolveIconPath("filter");
8075
- if (path) {
8076
- ctx.save();
8077
- ctx.translate(offsetX, offsetY);
8078
- ctx.scale(
8079
- iconWidth / FILTER_ICON_VIEWBOX_SIZE,
8080
- iconHeight / FILTER_ICON_VIEWBOX_SIZE
8081
- );
8082
- ctx.fill(path);
8083
- ctx.restore();
8084
- return;
8085
- }
8086
- ctx.beginPath();
8087
- ctx.moveTo(offsetX, offsetY);
8088
- ctx.lineTo(offsetX + iconWidth, offsetY);
8089
- ctx.lineTo(offsetX + iconWidth * 0.62, offsetY + iconHeight * 0.52);
8090
- ctx.lineTo(offsetX + iconWidth * 0.62, offsetY + iconHeight);
8091
- ctx.lineTo(offsetX + iconWidth * 0.38, offsetY + iconHeight * 0.88);
8092
- ctx.lineTo(offsetX + iconWidth * 0.38, offsetY + iconHeight * 0.52);
8093
- ctx.closePath();
8094
- ctx.fill();
8095
- }
8096
- function drawFixedPinIcon(ctx, rect) {
8097
- const iconSize = Math.min(rect.width, rect.height) * 0.92;
8098
- const offsetX = rect.x + (rect.width - iconSize) / 2;
8099
- const offsetY = rect.y + (rect.height - iconSize) / 2;
8100
- const scale = iconSize / FIXED_PIN_VIEWBOX_SIZE;
8101
- const path = resolveIconPath("fixed");
8102
- if (path) {
8103
- ctx.save();
8104
- ctx.translate(offsetX, offsetY);
8105
- ctx.scale(scale, scale);
8106
- ctx.fill(path);
8107
- ctx.restore();
8108
- return;
8109
- }
8110
- ctx.beginPath();
8111
- ctx.moveTo(offsetX + iconSize * 0.68, offsetY);
8112
- ctx.lineTo(offsetX + iconSize, offsetY + iconSize * 0.32);
8113
- ctx.lineTo(offsetX + iconSize * 0.78, offsetY + iconSize * 0.34);
8114
- ctx.lineTo(offsetX + iconSize * 0.62, offsetY + iconSize * 0.64);
8115
- ctx.lineTo(offsetX + iconSize * 0.74, offsetY + iconSize * 0.76);
8116
- ctx.lineTo(offsetX + iconSize * 0.52, offsetY + iconSize * 0.82);
8117
- ctx.lineTo(offsetX + iconSize * 0.16, offsetY + iconSize);
8118
- ctx.lineTo(offsetX + iconSize * 0.34, offsetY + iconSize * 0.64);
8119
- ctx.lineTo(offsetX + iconSize * 0.2, offsetY + iconSize * 0.5);
8120
- ctx.lineTo(offsetX + iconSize * 0.5, offsetY + iconSize * 0.34);
8121
- ctx.closePath();
8122
- ctx.fill();
8123
- }
8124
- function resolveIconPath(type) {
8125
- if (typeof Path2D === "undefined") return null;
8126
- if (type === "filter") {
8127
- filterIconPath ?? (filterIconPath = new Path2D(FILTER_ICON_PATH_DATA));
8128
- return filterIconPath;
8129
- }
8130
- fixedPinPath ?? (fixedPinPath = new Path2D(FIXED_PIN_PATH_DATA));
8131
- return fixedPinPath;
8885
+ drawHeaderActionGlyph(
8886
+ ctx,
8887
+ rect,
8888
+ actionType,
8889
+ actionType === "sort" ? "#8c8c8c" : active ? "#1677ff" : "#8c8c8c",
8890
+ sortOrder
8891
+ );
8132
8892
  }
8133
8893
  function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender) {
8134
8894
  const gap = 4;
@@ -10882,6 +11642,131 @@ function applyTextContentStyle(ctx, content, fallbackColor, theme) {
10882
11642
  ctx.textBaseline = "alphabetic";
10883
11643
  ctx.textAlign = "left";
10884
11644
  }
11645
+
11646
+ // src/features/editing/cascader-display.ts
11647
+ function normalizeCascaderDisplayPaths(options, values, strategy) {
11648
+ const index = /* @__PURE__ */ new Map();
11649
+ const visit = (items, parent, disabled = false) => items.map((item) => {
11650
+ const path = [...parent, item.value];
11651
+ const blocked = disabled || !!item.disabled;
11652
+ const children = visit(item.children ?? [], path, blocked);
11653
+ const node = {
11654
+ path,
11655
+ children,
11656
+ leaves: blocked ? [] : children.length ? children.flatMap((child) => child.leaves) : [JSON.stringify(path)]
11657
+ };
11658
+ index.set(JSON.stringify(path), node);
11659
+ return node;
11660
+ });
11661
+ const roots = visit(options, []);
11662
+ const selected = /* @__PURE__ */ new Set();
11663
+ const preserved = /* @__PURE__ */ new Map();
11664
+ for (const path of values) {
11665
+ const key = JSON.stringify(path);
11666
+ const leaves = index.get(key)?.leaves;
11667
+ if (!leaves?.length) preserved.set(key, path);
11668
+ else leaves.forEach((leaf) => selected.add(leaf));
11669
+ }
11670
+ const result = [];
11671
+ const collect = (node) => {
11672
+ if (node.leaves.length && node.leaves.every((leaf) => selected.has(leaf)) && (strategy === "SHOW_PARENT" || !node.children.length))
11673
+ result.push(node.path);
11674
+ else node.children.forEach(collect);
11675
+ };
11676
+ roots.forEach(collect);
11677
+ return [...result, ...preserved.values()];
11678
+ }
11679
+
11680
+ // src/features/upload/upload-preview.ts
11681
+ var localUrls = /* @__PURE__ */ new WeakMap();
11682
+ var ownedUrls = /* @__PURE__ */ new Set();
11683
+ function canPreviewUploadPdf(file) {
11684
+ return (file.type?.toLowerCase() === "application/pdf" || /\.pdf$/i.test(file.name)) && Boolean(resolveRemoteUrl(file.url) || file.originFileObj);
11685
+ }
11686
+ function previewUploadPdf(file) {
11687
+ if (typeof window === "undefined" || !canPreviewUploadPdf(file)) return;
11688
+ let url = resolveRemoteUrl(file.url);
11689
+ if (!url && file.originFileObj) {
11690
+ const raw = file.originFileObj;
11691
+ url = localUrls.get(raw);
11692
+ if (!url) {
11693
+ url = URL.createObjectURL(
11694
+ raw.type === "application/pdf" ? raw : raw.slice(0, raw.size, "application/pdf")
11695
+ );
11696
+ localUrls.set(raw, url);
11697
+ if (!ownedUrls.size) {
11698
+ window.addEventListener(
11699
+ "pagehide",
11700
+ () => {
11701
+ ownedUrls.forEach((source) => URL.revokeObjectURL(source));
11702
+ ownedUrls.clear();
11703
+ localUrls = /* @__PURE__ */ new WeakMap();
11704
+ },
11705
+ { once: true }
11706
+ );
11707
+ }
11708
+ ownedUrls.add(url);
11709
+ }
11710
+ }
11711
+ if (url) window.open(url, "_blank", "noopener,noreferrer");
11712
+ }
11713
+ function resolveRemoteUrl(source) {
11714
+ if (!source || typeof window === "undefined") return void 0;
11715
+ try {
11716
+ const url = new URL(source, window.location.href);
11717
+ return ["http:", "https:", "blob:"].includes(url.protocol) ? url.href : void 0;
11718
+ } catch {
11719
+ return void 0;
11720
+ }
11721
+ }
11722
+
11723
+ // src/features/upload/upload-display.ts
11724
+ function normalizeUploadFiles(value) {
11725
+ return Array.isArray(value) ? value.filter((file) => file && typeof file.name === "string").map((file, index) => ({ ...file, uid: String(file.uid ?? index) })) : [];
11726
+ }
11727
+ function isUploadImage(file) {
11728
+ return file.type?.startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|avif|svg)$/i.test(file.name);
11729
+ }
11730
+ function uploadImageSource(file) {
11731
+ const source = file.thumbUrl || file.url || "";
11732
+ return /^(https?:|blob:|data:image\/)/i.test(source) || /^(\/|\.\/|\.\.\/)/.test(source) ? source : "";
11733
+ }
11734
+ function resolveUploadContents(value, editable = false) {
11735
+ return normalizeUploadFiles(value).flatMap((file) => {
11736
+ const src = isUploadImage(file) ? uploadImageSource(file) : "";
11737
+ if (src)
11738
+ return [
11739
+ {
11740
+ type: "image",
11741
+ src,
11742
+ alt: file.name,
11743
+ style: { width: 24, height: 24 }
11744
+ }
11745
+ ];
11746
+ const preview = canPreviewUploadPdf(file) ? { cursor: "pointer", onClick: () => previewUploadPdf(file) } : {};
11747
+ const directPreview = editable ? {} : preview;
11748
+ return [
11749
+ {
11750
+ type: "image",
11751
+ src: createFileTypeIconSource(file.name),
11752
+ preview: false,
11753
+ ...directPreview,
11754
+ style: { width: 24, height: 24 }
11755
+ },
11756
+ { type: "text", text: file.name, ...directPreview },
11757
+ ...editable && preview.onClick ? [
11758
+ {
11759
+ type: "button",
11760
+ text: "\u9884\u89C8",
11761
+ buttonType: "link",
11762
+ ...preview
11763
+ }
11764
+ ] : []
11765
+ ];
11766
+ });
11767
+ }
11768
+
11769
+ // src/features/editing/editing-utils.ts
10885
11770
  function canEditCell(column, context) {
10886
11771
  if (!column || column.key === ROW_SELECTION_COLUMN_KEY || column.key === ROW_SELECTOR_COLUMN_KEY || column.key === EXPAND_COLUMN_KEY) {
10887
11772
  return false;
@@ -10915,7 +11800,13 @@ function getColumnEditConfig(column, context) {
10915
11800
  max: resolvedFormItemProps.max,
10916
11801
  step: resolvedFormItemProps.step,
10917
11802
  options: resolvedFormItemProps.options,
11803
+ treeData: resolvedFormItemProps.treeData,
11804
+ upload: resolvedFormItemProps.upload,
11805
+ treeDefaultExpandAll: resolvedFormItemProps.treeDefaultExpandAll,
11806
+ treeDefaultExpandedKeys: resolvedFormItemProps.treeDefaultExpandedKeys,
10918
11807
  changeOnSelect: resolvedFormItemProps.changeOnSelect,
11808
+ multiple: resolvedFormItemProps.multiple,
11809
+ showCheckedStrategy: resolvedFormItemProps.showCheckedStrategy,
10919
11810
  expandTrigger: resolvedFormItemProps.expandTrigger,
10920
11811
  allowClear: resolvedFormItemProps.allowClear,
10921
11812
  displayRender: resolvedFormItemProps.displayRender,
@@ -10952,16 +11843,43 @@ function resolveFormattedCellText(params) {
10952
11843
  if (formatter === "") {
10953
11844
  return "";
10954
11845
  }
11846
+ if (resolvedFormItemProps?.valueType === "upload") {
11847
+ return normalizeUploadFiles(value).map((file) => file.name).join("\u3001");
11848
+ }
11849
+ if (resolvedFormItemProps?.valueType === "tree-select") {
11850
+ const titles = /* @__PURE__ */ new Map();
11851
+ const pending = [...resolvedFormItemProps.treeData ?? []];
11852
+ while (pending.length) {
11853
+ const node = pending.pop();
11854
+ titles.set(node.value, node.title);
11855
+ if (node.children) pending.push(...node.children);
11856
+ }
11857
+ const values = resolvedFormItemProps.multiple ? Array.isArray(value) ? value : [] : [value];
11858
+ return [
11859
+ ...new Set(
11860
+ values.filter(
11861
+ (part) => typeof part === "string" || typeof part === "number"
11862
+ )
11863
+ )
11864
+ ].map((part) => titles.get(part) ?? String(part)).join("\uFF1B");
11865
+ }
10955
11866
  if (resolvedFormItemProps?.valueType === "cascader" && Array.isArray(value)) {
10956
- let options = resolvedFormItemProps.options ?? [];
10957
- const selectedOptions = [];
10958
- const labels = value.map((part) => {
10959
- const option = options.find((item) => item.value === part);
10960
- options = option?.children ?? [];
10961
- if (option) selectedOptions.push(option);
10962
- return option?.label ?? String(part ?? "");
10963
- });
10964
- return resolvedFormItemProps.displayRender?.(labels, selectedOptions) ?? labels.join(" / ");
11867
+ const formatPath = (path) => {
11868
+ let options = resolvedFormItemProps.options ?? [];
11869
+ const selectedOptions = [];
11870
+ const labels = path.map((part) => {
11871
+ const option = options.find((item) => item.value === part);
11872
+ options = option?.children ?? [];
11873
+ if (option) selectedOptions.push(option);
11874
+ return option?.label ?? String(part ?? "");
11875
+ });
11876
+ return resolvedFormItemProps.displayRender?.(labels, selectedOptions) ?? labels.join(" / ");
11877
+ };
11878
+ return resolvedFormItemProps.multiple ? normalizeCascaderDisplayPaths(
11879
+ resolvedFormItemProps.options ?? [],
11880
+ value.filter(Array.isArray),
11881
+ resolvedFormItemProps.showCheckedStrategy ?? "SHOW_PARENT"
11882
+ ).map(formatPath).join("\uFF1B") : formatPath(value);
10965
11883
  }
10966
11884
  if (Array.isArray(value) && (resolvedFormItemProps?.valueType === "date-range" || resolvedFormItemProps?.valueType === "datetime-range" || resolvedFormItemProps?.valueType === "timerange")) {
10967
11885
  const [start = "", end = ""] = value;
@@ -11054,6 +11972,196 @@ function resolveColumnFormItemProps(column, context) {
11054
11972
  return column.formItemProps;
11055
11973
  }
11056
11974
 
11975
+ // src/rendering/cell-content/draw-timeline-content.ts
11976
+ function positive(value, fallback) {
11977
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
11978
+ }
11979
+ function timelineItems(content) {
11980
+ const items = content.items ?? [];
11981
+ return content.reverse ? [...items].reverse() : items;
11982
+ }
11983
+ function timelinePlainText(content) {
11984
+ return timelineItems(content).map((item) => [item.title, item.content].filter(Boolean).join(" ")).join("\n");
11985
+ }
11986
+ function measureTimelineWidth(content) {
11987
+ return positive(content.style?.width ?? content.width, 220);
11988
+ }
11989
+ function wrap(ctx, text, width) {
11990
+ const lines = [];
11991
+ for (const paragraph of String(text ?? "").split(/\r?\n/)) {
11992
+ let line = "";
11993
+ for (const character of paragraph) {
11994
+ if (line && ctx.measureText(line + character).width > width) {
11995
+ lines.push(line);
11996
+ line = "";
11997
+ }
11998
+ line += character;
11999
+ }
12000
+ lines.push(line);
12001
+ }
12002
+ return lines;
12003
+ }
12004
+ function layoutTimeline(ctx, content, theme, availableWidth = measureTimelineWidth(content)) {
12005
+ const width = Math.max(
12006
+ 1,
12007
+ Math.min(measureTimelineWidth(content), availableWidth)
12008
+ );
12009
+ const fontSize = positive(
12010
+ resolveContentFontSize(content, theme.fontSize),
12011
+ theme.fontSize
12012
+ );
12013
+ const font = buildCanvasFont({
12014
+ fontSize,
12015
+ fontFamily: resolveContentFontFamily(content, theme),
12016
+ fontWeight: resolveContentFontWeight(content)
12017
+ });
12018
+ const lineHeight = Math.ceil(fontSize * 1.5);
12019
+ const dot = Math.min(positive(content.dotSize, 16), lineHeight);
12020
+ const gap = typeof content.itemGap === "number" && Number.isFinite(content.itemGap) ? Math.max(0, content.itemGap) : 12;
12021
+ const items = timelineItems(content);
12022
+ const mode = content.mode ?? "start";
12023
+ const paired = mode === "alternate" || items.some(
12024
+ (item) => item.title || item.position && item.position !== mode
12025
+ );
12026
+ const axis = paired ? width / 2 : mode === "end" ? width - dot / 2 - 1 : dot / 2 + 1;
12027
+ const gutter = dot / 2 + 8;
12028
+ const leftWidth = Math.max(1, axis - gutter);
12029
+ const rightWidth = Math.max(1, width - axis - gutter);
12030
+ let height = 0;
12031
+ ctx.save();
12032
+ ctx.font = font;
12033
+ const nodes = items.map((item, index) => {
12034
+ const position = item.position ?? (mode === "alternate" ? index % 2 ? "end" : "start" : mode);
12035
+ const onRight = position === "start";
12036
+ const textWidth = onRight ? rightWidth : leftWidth;
12037
+ const titleWidth = onRight ? leftWidth : rightWidth;
12038
+ const lines = wrap(ctx, item.content, textWidth);
12039
+ const titles = item.title ? wrap(ctx, item.title, titleWidth) : [];
12040
+ const nodeHeight = Math.max(lines.length, titles.length, 1) * lineHeight;
12041
+ const node = {
12042
+ item,
12043
+ onRight,
12044
+ lines,
12045
+ titles,
12046
+ y: height,
12047
+ height: nodeHeight,
12048
+ textWidth,
12049
+ titleWidth
12050
+ };
12051
+ height += nodeHeight + (index < items.length - 1 ? gap : 0);
12052
+ return node;
12053
+ });
12054
+ ctx.restore();
12055
+ return {
12056
+ width,
12057
+ height,
12058
+ font,
12059
+ fontSize,
12060
+ lineHeight,
12061
+ dot,
12062
+ axis,
12063
+ gutter,
12064
+ nodes
12065
+ };
12066
+ }
12067
+ function drawBodyTimelineContent(params) {
12068
+ const {
12069
+ ctx,
12070
+ content,
12071
+ theme,
12072
+ rect,
12073
+ cursorX,
12074
+ descriptors,
12075
+ column,
12076
+ rowIndex,
12077
+ contentIndex,
12078
+ frozen
12079
+ } = params;
12080
+ const layout = layoutTimeline(ctx, content, theme, rect.width);
12081
+ if (!layout.nodes.length) return;
12082
+ const top = rect.y + Math.max(0, (rect.height - layout.height) / 2);
12083
+ const axis = cursorX + layout.axis;
12084
+ ctx.save();
12085
+ ctx.beginPath();
12086
+ ctx.rect(cursorX, top, layout.width, Math.min(layout.height, rect.height));
12087
+ ctx.clip();
12088
+ ctx.globalAlpha *= resolveContentOpacity(content);
12089
+ ctx.font = layout.font;
12090
+ ctx.textBaseline = "middle";
12091
+ layout.nodes.forEach((node, index) => {
12092
+ const centerY = top + node.y + layout.lineHeight / 2;
12093
+ const next = layout.nodes[index + 1];
12094
+ if (next) {
12095
+ ctx.strokeStyle = content.lineColor ?? theme.borderColor;
12096
+ ctx.lineWidth = 1;
12097
+ ctx.setLineDash(node.item.loading || next.item.loading ? [3, 3] : []);
12098
+ ctx.beginPath();
12099
+ ctx.moveTo(axis, centerY + layout.dot / 2 + 2);
12100
+ ctx.lineTo(
12101
+ axis,
12102
+ top + next.y + layout.lineHeight / 2 - layout.dot / 2 - 2
12103
+ );
12104
+ ctx.stroke();
12105
+ ctx.setLineDash([]);
12106
+ }
12107
+ drawTimelineMarker(
12108
+ ctx,
12109
+ node.item,
12110
+ axis,
12111
+ centerY,
12112
+ layout.dot,
12113
+ content.variant === "filled"
12114
+ );
12115
+ const drawLines = (lines, right, width, color) => {
12116
+ const x = right ? axis + layout.gutter : cursorX;
12117
+ ctx.save();
12118
+ ctx.beginPath();
12119
+ ctx.rect(x, top + node.y, width, node.height);
12120
+ ctx.clip();
12121
+ ctx.fillStyle = color;
12122
+ ctx.textAlign = right ? "left" : "right";
12123
+ lines.forEach(
12124
+ (line, lineIndex) => ctx.fillText(
12125
+ line,
12126
+ right ? x : axis - layout.gutter,
12127
+ centerY + lineIndex * layout.lineHeight
12128
+ )
12129
+ );
12130
+ ctx.restore();
12131
+ };
12132
+ drawLines(
12133
+ node.lines,
12134
+ node.onRight,
12135
+ node.textWidth,
12136
+ content.style?.color ?? theme.textColor
12137
+ );
12138
+ drawLines(
12139
+ node.titles,
12140
+ !node.onRight,
12141
+ node.titleWidth,
12142
+ content.titleColor ?? theme.mutedTextColor ?? theme.textColor
12143
+ );
12144
+ });
12145
+ ctx.restore();
12146
+ descriptors.push({
12147
+ kind: "body-content",
12148
+ rect: {
12149
+ x: cursorX,
12150
+ y: top,
12151
+ width: layout.width,
12152
+ height: Math.min(layout.height, rect.height)
12153
+ },
12154
+ rowIndex,
12155
+ columnKey: column.key,
12156
+ fixed: column.fixed,
12157
+ frozen,
12158
+ contentIndex,
12159
+ contentKey: content.key,
12160
+ cursor: content.cursor ?? (content.onClick ? "pointer" : void 0),
12161
+ tooltipText: timelinePlainText(content)
12162
+ });
12163
+ }
12164
+
11057
12165
  // src/rendering/cell-content/cell-content-renderer.ts
11058
12166
  var CONTENT_HORIZONTAL_PADDING = 12;
11059
12167
  var CONTENT_GAP = 8;
@@ -11071,6 +12179,12 @@ function resolveRawBodyCellContents(params) {
11071
12179
  if (rendered != null) {
11072
12180
  return normalizeCellContents(rendered);
11073
12181
  }
12182
+ if (resolveColumnFormItemProps(column, { value, record, rowIndex })?.valueType === "upload") {
12183
+ return resolveUploadContents(
12184
+ value,
12185
+ canEditCell(column, { value, record, rowIndex })
12186
+ );
12187
+ }
11074
12188
  const formattedText = resolveFormattedCellText({
11075
12189
  column,
11076
12190
  value,
@@ -11146,6 +12260,11 @@ function resolveRawBodyCellContents(params) {
11146
12260
  }
11147
12261
  ] : [];
11148
12262
  }
12263
+ if (column.cellType === "timeline") {
12264
+ return [
12265
+ { type: "timeline", items: Array.isArray(value) ? value : [] }
12266
+ ];
12267
+ }
11149
12268
  if (column.cellType === "progress") {
11150
12269
  const numericValue = typeof value === "number" ? value : typeof value === "string" ? Number(value) : 0;
11151
12270
  return [
@@ -11233,6 +12352,9 @@ function normalizeDrawableCellContents(contents) {
11233
12352
  ).map(normalizeCellContentItem);
11234
12353
  }
11235
12354
  function normalizeCellContentItem(content) {
12355
+ if (content.type === "timeline") {
12356
+ return { ...content, style: { ...content.style, display: "block" } };
12357
+ }
11236
12358
  if (content.type !== "money") {
11237
12359
  return content;
11238
12360
  }
@@ -11478,7 +12600,12 @@ function drawBodyCellContents(params) {
11478
12600
  );
11479
12601
  return void 0;
11480
12602
  }
11481
- const lineHeight = getBodyCellContentsLineHeight(contents, theme);
12603
+ const lineHeight = getBodyCellContentsLineHeight(
12604
+ contents,
12605
+ theme,
12606
+ ctx,
12607
+ availableWidth
12608
+ );
11482
12609
  const layout = layoutBodyCellContents({
11483
12610
  ctx,
11484
12611
  contents,
@@ -11651,7 +12778,12 @@ function measureBodyCellContentsHeight(params) {
11651
12778
  if (availableWidth <= 0) {
11652
12779
  return paddingBlock * 2;
11653
12780
  }
11654
- const lineHeight = getBodyCellContentsLineHeight(contents, theme);
12781
+ const lineHeight = getBodyCellContentsLineHeight(
12782
+ contents,
12783
+ theme,
12784
+ ctx,
12785
+ availableWidth
12786
+ );
11655
12787
  const layout = layoutBodyCellContents({
11656
12788
  ctx,
11657
12789
  contents,
@@ -12130,6 +13262,7 @@ function drawExpandedBodyContent(params) {
12130
13262
  if (content.type === "icon") {
12131
13263
  drawBodyIconContent({
12132
13264
  ctx,
13265
+ theme,
12133
13266
  rect,
12134
13267
  content,
12135
13268
  contentIndex,
@@ -12202,6 +13335,21 @@ function drawExpandedBodyContent(params) {
12202
13335
  });
12203
13336
  return;
12204
13337
  }
13338
+ if (content.type === "timeline") {
13339
+ drawBodyTimelineContent({
13340
+ ctx,
13341
+ rect,
13342
+ content,
13343
+ contentIndex,
13344
+ cursorX,
13345
+ rowIndex,
13346
+ column,
13347
+ theme,
13348
+ frozen,
13349
+ descriptors
13350
+ });
13351
+ return;
13352
+ }
12205
13353
  if (content.type === "progress") {
12206
13354
  drawBodyProgressContent({
12207
13355
  ctx,
@@ -12377,6 +13525,7 @@ function measureExpandedContentWidth(ctx, content, theme) {
12377
13525
  if (content.type === "button") {
12378
13526
  return measureButtonContentWidth(ctx, content, theme);
12379
13527
  }
13528
+ if (content.type === "timeline") return measureTimelineWidth(content);
12380
13529
  if (content.type === "progress") {
12381
13530
  return measureProgressContentWidth(ctx, content, theme);
12382
13531
  }
@@ -12431,9 +13580,16 @@ function getBodyCellContentsLineGap(contents, lineHeight) {
12431
13580
  }
12432
13581
  return tagHeight > 0 ? Math.max(CONTENT_GAP - (lineHeight - tagHeight), 0) : 0;
12433
13582
  }
12434
- function getBodyCellContentsLineHeight(contents, theme) {
13583
+ function getBodyCellContentsLineHeight(contents, theme, ctx, availableWidth) {
12435
13584
  let lineHeight = theme.fontSize + 8;
12436
13585
  contents.forEach((item) => {
13586
+ if (item.type === "timeline") {
13587
+ lineHeight = Math.max(
13588
+ lineHeight,
13589
+ layoutTimeline(ctx, item, theme, availableWidth).height
13590
+ );
13591
+ return;
13592
+ }
12437
13593
  if (item.type === "arrow") {
12438
13594
  lineHeight = Math.max(lineHeight, measureArrowContentHeight(item));
12439
13595
  return;
@@ -12508,6 +13664,15 @@ function getBodyCellContentsLineHeight(contents, theme) {
12508
13664
  return lineHeight;
12509
13665
  }
12510
13666
  function truncateExpandedContent(ctx, content, theme, maxWidth, withEllipsis) {
13667
+ if (content.type === "timeline") {
13668
+ return maxWidth > 0 ? {
13669
+ ...content,
13670
+ style: {
13671
+ ...content.style,
13672
+ width: Math.min(measureTimelineWidth(content), maxWidth)
13673
+ }
13674
+ } : null;
13675
+ }
12511
13676
  if (content.type === "text" || content.type === "link") {
12512
13677
  const nextText = fitTextToWidth(
12513
13678
  getBreakableContentText(content),
@@ -12657,6 +13822,7 @@ function buildBodyContentsPlainText(contents) {
12657
13822
  if (content.type === "progress") {
12658
13823
  return content.text ?? `${Math.round(resolveProgressPercent(content))}%`;
12659
13824
  }
13825
+ if (content.type === "timeline") return timelinePlainText(content);
12660
13826
  if (content.type === "chart") {
12661
13827
  return resolveChartContentPlainText(content);
12662
13828
  }
@@ -12971,6 +14137,7 @@ function isCellContentDisabled(content) {
12971
14137
  if (content.type === "button") {
12972
14138
  return content.disabled === true;
12973
14139
  }
14140
+ if (content.type === "timeline") return false;
12974
14141
  if (content.type === "progress") {
12975
14142
  return false;
12976
14143
  }
@@ -16422,6 +17589,11 @@ function getCellClipboardText(record, rowIndex, column) {
16422
17589
  if (column.cellType === "checkbox" || column.cellType === "radio") {
16423
17590
  return String(Boolean(value));
16424
17591
  }
17592
+ if (column.cellType === "timeline") {
17593
+ return timelinePlainText({
17594
+ items: Array.isArray(value) ? value : []
17595
+ });
17596
+ }
16425
17597
  return resolveFormattedCellText({
16426
17598
  column,
16427
17599
  value,
@@ -16572,7 +17744,7 @@ async function copyRowJsonContent(params) {
16572
17744
  }
16573
17745
  async function exportAllRowsJsonContent(params) {
16574
17746
  await downloadJsonFile({
16575
- fileName: buildTableExportFileName("json"),
17747
+ fileName: params.fileName?.trim() || buildTableExportFileName("json"),
16576
17748
  data: mapRowsToExportRecords({
16577
17749
  rows: params.rows,
16578
17750
  columns: buildExportColumns(params.columns)
@@ -16581,14 +17753,14 @@ async function exportAllRowsJsonContent(params) {
16581
17753
  }
16582
17754
  async function exportAllRowsCsvContent(params) {
16583
17755
  await exportTableToCsv({
16584
- fileName: buildTableExportFileName("csv"),
17756
+ fileName: params.fileName?.trim() || buildTableExportFileName("csv"),
16585
17757
  rows: params.rows,
16586
17758
  columns: buildExportColumns(params.columns)
16587
17759
  });
16588
17760
  }
16589
17761
  async function exportAllRowsTxtContent(params) {
16590
17762
  await exportTableToTxt({
16591
- fileName: buildTableExportFileName("txt"),
17763
+ fileName: params.fileName?.trim() || buildTableExportFileName("txt"),
16592
17764
  rows: params.rows,
16593
17765
  columns: buildExportColumns(params.columns)
16594
17766
  });
@@ -16658,6 +17830,7 @@ function cellContentToClipboardText(content) {
16658
17830
  if (content.type === "progress") {
16659
17831
  return content.text ?? `${Math.round(resolveProgressClipboardPercent(content))}%`;
16660
17832
  }
17833
+ if (content.type === "timeline") return timelinePlainText(content);
16661
17834
  if (content.type === "chart") {
16662
17835
  return resolveChartContentPlainText(content);
16663
17836
  }
@@ -16870,17 +18043,21 @@ var ListTableContentActionController = class {
16870
18043
  constructor(options) {
16871
18044
  __publicField(this, "options");
16872
18045
  __publicField(this, "exportTaskRunning", false);
18046
+ __publicField(this, "exportNamePending", false);
16873
18047
  this.options = options;
16874
18048
  }
16875
18049
  /**
16876
18050
  * 异步导出全部原始数据为 XLSX。
16877
18051
  */
16878
18052
  exportXlsx(options) {
18053
+ options = { ...this.options.getOptions().exportOptions, ...options };
16879
18054
  this.startExportTask(
16880
- () => exportAllRowsXlsxContent({
18055
+ (fileName) => exportAllRowsXlsxContent({
16881
18056
  rows: this.options.getOptions().dataSource,
16882
18057
  columns: this.options.getLeafColumns(),
16883
18058
  ...options,
18059
+ includeSummary: options?.includeSummary ?? this.options.getSummaryVisible?.() ?? false,
18060
+ fileName: fileName ?? options?.fileName,
16884
18061
  onProgress: (progress) => {
16885
18062
  const fileLabel = progress.fileCount && progress.fileCount > 1 ? `\uFF08\u7B2C ${progress.fileIndex}/${progress.fileCount} \u4EFD\uFF09` : "";
16886
18063
  this.options.overlayController.showLoading(
@@ -16890,16 +18067,55 @@ var ListTableContentActionController = class {
16890
18067
  }
16891
18068
  }),
16892
18069
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
16893
- options?.onError
18070
+ options?.onError,
18071
+ options?.fileName?.trim() ? `${options.fileName.trim().replace(/\.xlsx$/i, "")}.xlsx` : buildTableExportFileName("xlsx"),
18072
+ options?.promptFileName
16894
18073
  );
16895
18074
  }
16896
- startExportTask(task, text, onError) {
16897
- if (this.exportTaskRunning) {
18075
+ startExportTask(task, text, onError, defaultFileName, promptFileName = this.options.getOptions().exportOptions?.promptFileName ?? true) {
18076
+ if (this.exportTaskRunning || this.exportNamePending) {
18077
+ return;
18078
+ }
18079
+ if (defaultFileName !== void 0 && promptFileName) {
18080
+ this.exportNamePending = true;
18081
+ const notify = this.options.getOptions().ui?.onExportNameRequest;
18082
+ let settled = false;
18083
+ const close = () => {
18084
+ settled = true;
18085
+ this.exportNamePending = false;
18086
+ notify?.(null);
18087
+ };
18088
+ const request = {
18089
+ fileName: defaultFileName,
18090
+ onConfirm: (value) => {
18091
+ if (settled || !value.trim()) return;
18092
+ close();
18093
+ const extension = defaultFileName.match(
18094
+ /\.(xlsx|csv|txt|json)$/i
18095
+ )?.[0];
18096
+ const name = extension && !value.trim().toLowerCase().endsWith(extension.toLowerCase()) ? `${value.trim()}${extension}` : value.trim();
18097
+ this.startExportTask(() => task(name), text, onError);
18098
+ },
18099
+ onCancel: () => {
18100
+ if (!settled) close();
18101
+ }
18102
+ };
18103
+ if (notify) notify(request);
18104
+ else if (typeof window !== "undefined" && typeof window.prompt === "function") {
18105
+ const extension = defaultFileName.match(/\.(xlsx|csv|txt|json)$/i)?.[0] ?? "";
18106
+ const baseName = extension ? defaultFileName.slice(0, -extension.length) : defaultFileName;
18107
+ const value = window.prompt(
18108
+ `\u8BF7\u8F93\u5165\u5BFC\u51FA\u6587\u4EF6\u540D\u79F0\uFF08\u56FA\u5B9A\u540E\u7F00\uFF1A${extension}\uFF09`,
18109
+ baseName
18110
+ );
18111
+ if (value == null || !value.trim()) request.onCancel();
18112
+ else request.onConfirm(`${value.trim()}${extension}`);
18113
+ } else request.onConfirm(defaultFileName);
16898
18114
  return;
16899
18115
  }
16900
18116
  this.exportTaskRunning = true;
16901
18117
  this.options.overlayController.showLoading(text);
16902
- void task().catch((error) => {
18118
+ void Promise.resolve().then(() => task(defaultFileName)).catch((error) => {
16903
18119
  if (error?.name === "AbortError") return;
16904
18120
  console.error("[ListTable] Failed to export table data:", error);
16905
18121
  onError?.(error);
@@ -17177,6 +18393,312 @@ function setValueByDataIndex(record, dataIndex, value) {
17177
18393
  return true;
17178
18394
  }
17179
18395
 
18396
+ // src/features/upload/upload-editor.ts
18397
+ function createUploadEditor(params) {
18398
+ const config = params.config ?? {};
18399
+ let files = normalizeUploadFiles(params.value);
18400
+ let alive = true;
18401
+ let pending = 0;
18402
+ let sequence = 0;
18403
+ const requests = /* @__PURE__ */ new Map();
18404
+ const readers = /* @__PURE__ */ new Set();
18405
+ const root = document.createElement("div");
18406
+ root.setAttribute("role", "dialog");
18407
+ root.setAttribute("aria-label", "\u4E0A\u4F20\u9644\u4EF6");
18408
+ root.tabIndex = -1;
18409
+ const width = Math.min(360, Math.max(200, window.innerWidth - 16));
18410
+ Object.assign(root.style, {
18411
+ position: "fixed",
18412
+ zIndex: "10000",
18413
+ boxSizing: "border-box",
18414
+ width: `${width}px`,
18415
+ left: `${Math.max(8, Math.min(params.rect.x, window.innerWidth - width - 8))}px`,
18416
+ top: `${Math.max(8, Math.min(params.rect.y, window.innerHeight - 280))}px`,
18417
+ maxHeight: "calc(100vh - 16px)",
18418
+ overflow: "auto",
18419
+ padding: "12px",
18420
+ background: "#fff",
18421
+ color: "#1f2937",
18422
+ border: "1px solid #d9e1ec",
18423
+ borderRadius: "6px",
18424
+ boxShadow: "0 6px 24px #0002",
18425
+ font: "13px sans-serif"
18426
+ });
18427
+ for (const event of [
18428
+ "pointerdown",
18429
+ "mousedown",
18430
+ "click",
18431
+ "dblclick",
18432
+ "contextmenu"
18433
+ ])
18434
+ root.addEventListener(event, (e) => e.stopPropagation());
18435
+ const title = document.createElement("div");
18436
+ title.textContent = "\u4E0A\u4F20\u9644\u4EF6";
18437
+ title.style.fontWeight = "600";
18438
+ const list = document.createElement("div");
18439
+ Object.assign(list.style, {
18440
+ maxHeight: "180px",
18441
+ overflow: "auto",
18442
+ margin: "10px 0"
18443
+ });
18444
+ const message = document.createElement("div");
18445
+ message.setAttribute("role", "status");
18446
+ message.style.color = "#c53030";
18447
+ const input = document.createElement("input");
18448
+ input.type = "file";
18449
+ input.accept = config.accept ?? "";
18450
+ input.multiple = config.multiple ?? false;
18451
+ input.hidden = true;
18452
+ const actions = document.createElement("div");
18453
+ Object.assign(actions.style, {
18454
+ display: "flex",
18455
+ gap: "8px",
18456
+ marginTop: "10px",
18457
+ flexWrap: "wrap"
18458
+ });
18459
+ const button = (label, callback) => {
18460
+ const element = document.createElement("button");
18461
+ element.type = "button";
18462
+ element.textContent = label;
18463
+ Object.assign(element.style, {
18464
+ padding: "4px 10px",
18465
+ border: "1px solid #cbd5e1",
18466
+ background: "#fff",
18467
+ borderRadius: "4px",
18468
+ cursor: "pointer",
18469
+ color: "inherit"
18470
+ });
18471
+ element.onclick = callback;
18472
+ return element;
18473
+ };
18474
+ const choose = button("\u9009\u62E9\u6587\u4EF6", () => input.click());
18475
+ const done = button("\u5B8C\u6210", () => {
18476
+ if (!pending) params.onCommit();
18477
+ });
18478
+ const cancel = button("\u53D6\u6D88", params.onCancel);
18479
+ actions.append(choose, done, cancel);
18480
+ root.append(title, list, message, input, actions);
18481
+ function render() {
18482
+ if (!alive) return;
18483
+ list.replaceChildren();
18484
+ done.disabled = pending > 0;
18485
+ if (!files.length) list.textContent = "\u6682\u65E0\u9644\u4EF6";
18486
+ for (const file of files) {
18487
+ const row = document.createElement("div");
18488
+ Object.assign(row.style, {
18489
+ display: "flex",
18490
+ alignItems: "center",
18491
+ gap: "8px",
18492
+ padding: "6px 0"
18493
+ });
18494
+ const source = isUploadImage(file) ? uploadImageSource(file) : "";
18495
+ if (source) {
18496
+ const image = document.createElement("img");
18497
+ image.src = source;
18498
+ image.alt = file.name;
18499
+ Object.assign(image.style, {
18500
+ width: "32px",
18501
+ height: "32px",
18502
+ objectFit: "contain"
18503
+ });
18504
+ row.append(image);
18505
+ }
18506
+ const label = document.createElement("span");
18507
+ label.textContent = `${file.name} \xB7 ${file.status === "uploading" ? `${Math.round(file.percent ?? 0)}%` : file.status === "error" ? file.error || "\u4E0A\u4F20\u5931\u8D25" : file.status === "selected" ? "\u672A\u4E0A\u4F20" : "\u5DF2\u4E0A\u4F20"}`;
18508
+ Object.assign(label.style, {
18509
+ flex: "1",
18510
+ minWidth: "0",
18511
+ overflowWrap: "anywhere"
18512
+ });
18513
+ row.append(label);
18514
+ if (canPreviewUploadPdf(file)) {
18515
+ row.append(button("\u9884\u89C8", () => previewUploadPdf(file)));
18516
+ }
18517
+ if (file.status === "error" && file.originFileObj)
18518
+ row.append(button("\u91CD\u8BD5", () => upload(file)));
18519
+ row.append(
18520
+ button("\u79FB\u9664", () => {
18521
+ requests.get(file.uid)?.();
18522
+ files = files.filter((item) => item !== file);
18523
+ render();
18524
+ })
18525
+ );
18526
+ list.append(row);
18527
+ }
18528
+ }
18529
+ function upload(file) {
18530
+ if (!alive || !file.originFileObj || !config.action && !config.customRequest)
18531
+ return;
18532
+ requests.get(file.uid)?.();
18533
+ const raw = file.originFileObj;
18534
+ let active = true;
18535
+ let abort;
18536
+ pending++;
18537
+ file.status = "uploading";
18538
+ file.percent = 0;
18539
+ const finish = () => {
18540
+ active = false;
18541
+ pending--;
18542
+ requests.delete(file.uid);
18543
+ };
18544
+ requests.set(file.uid, () => {
18545
+ if (!active) return;
18546
+ finish();
18547
+ abort?.();
18548
+ });
18549
+ const onError = (error) => {
18550
+ if (!active || !alive) return;
18551
+ finish();
18552
+ file.status = "error";
18553
+ file.error = error.message || "\u4E0A\u4F20\u5931\u8D25";
18554
+ render();
18555
+ };
18556
+ const onSuccess = (response) => {
18557
+ if (!active || !alive) return;
18558
+ try {
18559
+ const url = config.getResponseUrl ? config.getResponseUrl(response, raw) : response && typeof response === "object" && "url" in response && typeof response.url === "string" ? response.url : void 0;
18560
+ file.url = url ?? file.url;
18561
+ file.response = response;
18562
+ file.status = "done";
18563
+ file.percent = 100;
18564
+ finish();
18565
+ render();
18566
+ } catch (error) {
18567
+ onError(error instanceof Error ? error : new Error(String(error)));
18568
+ }
18569
+ };
18570
+ const onProgress = (percent) => {
18571
+ if (alive && active) {
18572
+ file.percent = Math.max(0, Math.min(100, percent));
18573
+ render();
18574
+ }
18575
+ };
18576
+ try {
18577
+ if (config.customRequest) {
18578
+ const handle = config.customRequest({
18579
+ file: raw,
18580
+ onProgress,
18581
+ onSuccess,
18582
+ onError
18583
+ });
18584
+ abort = handle ? () => handle.abort() : void 0;
18585
+ } else {
18586
+ const xhr = new XMLHttpRequest();
18587
+ abort = () => xhr.abort();
18588
+ xhr.open("POST", config.action);
18589
+ xhr.withCredentials = config.withCredentials ?? false;
18590
+ for (const [key, value] of Object.entries(config.headers ?? {}))
18591
+ xhr.setRequestHeader(key, value);
18592
+ xhr.upload.onprogress = (event) => {
18593
+ if (event.lengthComputable)
18594
+ onProgress(event.loaded / event.total * 100);
18595
+ };
18596
+ xhr.onerror = () => onError(new Error("\u7F51\u7EDC\u9519\u8BEF\uFF0C\u4E0A\u4F20\u5931\u8D25"));
18597
+ xhr.onabort = () => onError(new Error("\u4E0A\u4F20\u5DF2\u53D6\u6D88"));
18598
+ xhr.onload = () => {
18599
+ if (xhr.status < 200 || xhr.status >= 300)
18600
+ return onError(new Error(`\u4E0A\u4F20\u5931\u8D25 (${xhr.status})`));
18601
+ let response = xhr.responseText;
18602
+ try {
18603
+ response = JSON.parse(xhr.responseText);
18604
+ } catch {
18605
+ }
18606
+ onSuccess(response);
18607
+ };
18608
+ const body = new FormData();
18609
+ for (const [key, value] of Object.entries(config.data ?? {}))
18610
+ body.append(key, value);
18611
+ body.append(config.name ?? "file", raw);
18612
+ xhr.send(body);
18613
+ }
18614
+ } catch (error) {
18615
+ onError(error instanceof Error ? error : new Error(String(error)));
18616
+ }
18617
+ render();
18618
+ }
18619
+ input.onchange = async () => {
18620
+ const selected = Array.from(input.files ?? []);
18621
+ input.value = "";
18622
+ message.textContent = "";
18623
+ pending++;
18624
+ render();
18625
+ for (const raw of selected) {
18626
+ try {
18627
+ if (config.maxSize && raw.size > config.maxSize)
18628
+ throw new Error(`${raw.name} \u8D85\u8FC7\u6587\u4EF6\u5927\u5C0F\u9650\u5236`);
18629
+ const allowed = await config.beforeUpload?.(raw);
18630
+ if (!alive) break;
18631
+ const max = Math.max(1, config.maxCount ?? Infinity);
18632
+ if (max === 1) {
18633
+ for (const file2 of files) requests.get(file2.uid)?.();
18634
+ files = [];
18635
+ }
18636
+ if (files.length >= max) throw new Error(`\u6700\u591A\u9009\u62E9 ${max} \u4E2A\u6587\u4EF6`);
18637
+ const file = {
18638
+ uid: `upload-${Date.now()}-${++sequence}`,
18639
+ name: raw.name,
18640
+ type: raw.type,
18641
+ size: raw.size,
18642
+ originFileObj: raw,
18643
+ status: "selected"
18644
+ };
18645
+ files.push(file);
18646
+ if (isUploadImage(file)) {
18647
+ const reader = new FileReader();
18648
+ readers.add(reader);
18649
+ pending++;
18650
+ reader.onload = () => {
18651
+ if (alive && files.includes(file)) {
18652
+ file.thumbUrl = String(reader.result);
18653
+ render();
18654
+ }
18655
+ };
18656
+ reader.onloadend = () => {
18657
+ readers.delete(reader);
18658
+ pending--;
18659
+ render();
18660
+ };
18661
+ reader.readAsDataURL(raw);
18662
+ }
18663
+ if (allowed !== false) upload(file);
18664
+ } catch (error) {
18665
+ if (alive)
18666
+ message.textContent = error instanceof Error ? error.message : String(error);
18667
+ }
18668
+ }
18669
+ pending--;
18670
+ render();
18671
+ };
18672
+ root.onkeydown = (event) => {
18673
+ event.stopPropagation();
18674
+ if (event.key === "Escape") {
18675
+ event.preventDefault();
18676
+ params.onCancel();
18677
+ }
18678
+ if (event.key === "Tab") {
18679
+ const items = Array.from(
18680
+ root.querySelectorAll("button:not(:disabled)")
18681
+ );
18682
+ const next = items[(items.indexOf(document.activeElement) + (event.shiftKey ? -1 : 1) + items.length) % items.length];
18683
+ event.preventDefault();
18684
+ next?.focus();
18685
+ }
18686
+ };
18687
+ document.body.append(root);
18688
+ render();
18689
+ return {
18690
+ element: root,
18691
+ focus: () => choose.focus(),
18692
+ getValue: () => files,
18693
+ destroy: () => {
18694
+ alive = false;
18695
+ for (const abort of [...requests.values()]) abort();
18696
+ for (const reader of readers) reader.abort();
18697
+ root.remove();
18698
+ }
18699
+ };
18700
+ }
18701
+
17180
18702
  // src/features/editing/editing-feature.ts
17181
18703
  var DEFAULT_EDITOR_PADDING_INLINE = 12;
17182
18704
  function openCellEditingSession(params) {
@@ -17228,6 +18750,7 @@ function openCellEditingSession(params) {
17228
18750
  }
17229
18751
  const editorConfig = resolveEditorConfig(editConfig);
17230
18752
  const editingValue = initialValue === void 0 ? currentValue : initialValue;
18753
+ const selectOnFocus = !(initialValue !== void 0 && (currentValue == null || currentValue === ""));
17231
18754
  const hostRect = toHostRect ? toHostRect(descriptor.rect) : descriptor.rect;
17232
18755
  const zoom = typeof theme.contentZoom === "number" && Number.isFinite(theme.contentZoom) && theme.contentZoom > 0 ? theme.contentZoom : 1;
17233
18756
  const paddingInline = (column.cellStyle?.paddingInline ?? DEFAULT_EDITOR_PADDING_INLINE) * zoom;
@@ -17241,10 +18764,26 @@ function openCellEditingSession(params) {
17241
18764
  paddingBlock,
17242
18765
  contentZoom: zoom
17243
18766
  };
18767
+ if (editorConfig.valueType === "upload" && typeof document !== "undefined") {
18768
+ const session2 = createUploadEditor({
18769
+ value: editingValue,
18770
+ config: editorConfig.upload,
18771
+ rect: toViewportRect(descriptor.rect),
18772
+ onCommit: () => onCommit(),
18773
+ onCancel
18774
+ });
18775
+ session2.focus();
18776
+ return {
18777
+ ...session2,
18778
+ rowIndex,
18779
+ columnKey
18780
+ };
18781
+ }
17244
18782
  if (onEditorRequest) {
17245
18783
  let nextValue = editingValue;
17246
18784
  onEditorRequest({
17247
18785
  editorKey: JSON.stringify([rowIndex, columnKey]),
18786
+ selectOnFocus,
17248
18787
  rect: toViewportRect(descriptor.rect),
17249
18788
  clipRect: getViewportClipRect?.() ?? void 0,
17250
18789
  hostRect,
@@ -17274,6 +18813,7 @@ function openCellEditingSession(params) {
17274
18813
  return null;
17275
18814
  }
17276
18815
  const session = editorFactory.createCellEditorSession({
18816
+ selectOnFocus,
17277
18817
  rect: toViewportRect(descriptor.rect),
17278
18818
  clipRect: getViewportClipRect?.() ?? void 0,
17279
18819
  hostRect,
@@ -17310,7 +18850,13 @@ function resolveEditorConfig(config) {
17310
18850
  max: config.max,
17311
18851
  min: config.min,
17312
18852
  options: config.options,
18853
+ treeData: config.treeData,
18854
+ treeDefaultExpandAll: config.treeDefaultExpandAll,
18855
+ treeDefaultExpandedKeys: config.treeDefaultExpandedKeys,
17313
18856
  changeOnSelect: config.changeOnSelect,
18857
+ multiple: config.multiple,
18858
+ showCheckedStrategy: config.showCheckedStrategy,
18859
+ upload: config.upload,
17314
18860
  expandTrigger: config.expandTrigger,
17315
18861
  allowClear: config.allowClear,
17316
18862
  displayRender: config.displayRender,
@@ -17503,6 +19049,7 @@ var ListTableEditingController = class {
17503
19049
  __publicField(this, "nestedEditingSource", null);
17504
19050
  __publicField(this, "validationMessages", /* @__PURE__ */ new Map());
17505
19051
  __publicField(this, "validationTaskId", 0);
19052
+ __publicField(this, "editingRequestId", 0);
17506
19053
  this.options = options;
17507
19054
  }
17508
19055
  hasSession() {
@@ -17514,6 +19061,18 @@ var ListTableEditingController = class {
17514
19061
  open(rowIndex, columnKey, initialValue) {
17515
19062
  this.close(false);
17516
19063
  this.options.clearBodyTooltip();
19064
+ const requestId = this.editingRequestId;
19065
+ const openVisibleEditor = () => {
19066
+ if (requestId !== this.editingRequestId) return;
19067
+ this.openVisibleCell(rowIndex, columnKey, initialValue);
19068
+ };
19069
+ if (this.ensureCellVisibleForEditing(rowIndex, columnKey, openVisibleEditor)) {
19070
+ return;
19071
+ }
19072
+ openVisibleEditor();
19073
+ }
19074
+ /** 使用滚动完成后的描述符定位编辑器,避免浏览器移动整个画布宿主。 */
19075
+ openVisibleCell(rowIndex, columnKey, initialValue) {
17517
19076
  const zoom = this.options.getTableZoom() > 0 ? this.options.getTableZoom() : 1;
17518
19077
  const theme = this.options.getTheme();
17519
19078
  const configuredRowHeight = this.options.getOptions().rowHeight;
@@ -17669,40 +19228,14 @@ var ListTableEditingController = class {
17669
19228
  void this.validateCellTargets(session.rowIndex, session.columnKey);
17670
19229
  this.close(false);
17671
19230
  this.options.requestRender();
17672
- if (direction === "up" || direction === "down") {
17673
- if (!nextTarget) {
17674
- this.options.focusTable?.();
17675
- return;
17676
- }
17677
- this.options.setSelection?.(nextTarget);
17678
- const revealTarget = () => {
17679
- this.options.requestRender();
19231
+ if (!nextTarget) {
19232
+ if (direction === "up" || direction === "down") {
17680
19233
  this.options.focusTable?.();
17681
- };
17682
- const scrolled2 = this.ensureCellVisibleForEditing(
17683
- nextTarget.rowIndex,
17684
- nextTarget.columnKey,
17685
- revealTarget
17686
- );
17687
- if (!scrolled2) {
17688
- revealTarget();
17689
19234
  }
17690
19235
  return;
17691
19236
  }
17692
- if (!nextTarget) {
17693
- return;
17694
- }
17695
19237
  this.options.setSelection?.(nextTarget);
17696
- const scrolled = this.ensureCellVisibleForEditing(
17697
- nextTarget.rowIndex,
17698
- nextTarget.columnKey,
17699
- () => {
17700
- this.open(nextTarget.rowIndex, nextTarget.columnKey);
17701
- }
17702
- );
17703
- if (!scrolled) {
17704
- this.open(nextTarget.rowIndex, nextTarget.columnKey);
17705
- }
19238
+ this.open(nextTarget.rowIndex, nextTarget.columnKey);
17706
19239
  }
17707
19240
  /**
17708
19241
  * 确保目标可编辑单元格完整进入视口。
@@ -17741,11 +19274,15 @@ var ListTableEditingController = class {
17741
19274
  targetLeft: leftChanged ? nextLeft : scroll.left,
17742
19275
  targetTop: topChanged ? nextTop : scroll.top,
17743
19276
  duration: 240,
17744
- onComplete: onVisible
19277
+ onComplete: () => {
19278
+ this.options.requestRender();
19279
+ onVisible?.();
19280
+ }
17745
19281
  });
17746
19282
  return true;
17747
19283
  }
17748
19284
  close(_keepFocusState = false) {
19285
+ this.editingRequestId++;
17749
19286
  this.editingSession = closeCellEditingSession(this.editingSession);
17750
19287
  this.editingSource = null;
17751
19288
  this.nestedEditingSource = null;
@@ -24656,41 +26193,50 @@ function createListTableContextMenuActions(params) {
24656
26193
  onExportJson: () => {
24657
26194
  params.hideMenu();
24658
26195
  params.startExportTask(
24659
- () => exportAllRowsJsonContent({
26196
+ (fileName) => exportAllRowsJsonContent({
26197
+ fileName,
24660
26198
  rows: params.getAllRows(),
24661
26199
  columns: params.getColumns()
24662
26200
  }),
24663
- "\u6B63\u5728\u5BFC\u51FA JSON\u2026"
26201
+ "\u6B63\u5728\u5BFC\u51FA JSON\u2026",
26202
+ buildTableExportFileName("json")
24664
26203
  );
24665
26204
  },
24666
26205
  onExportCsv: () => {
24667
26206
  params.hideMenu();
24668
26207
  params.startExportTask(
24669
- () => exportAllRowsCsvContent({
26208
+ (fileName) => exportAllRowsCsvContent({
26209
+ fileName,
24670
26210
  rows: params.getAllRows(),
24671
26211
  columns: params.getColumns()
24672
26212
  }),
24673
- "\u6B63\u5728\u5BFC\u51FA CSV\u2026"
26213
+ "\u6B63\u5728\u5BFC\u51FA CSV\u2026",
26214
+ buildTableExportFileName("csv")
24674
26215
  );
24675
26216
  },
24676
26217
  onExportTxt: () => {
24677
26218
  params.hideMenu();
24678
26219
  params.startExportTask(
24679
- () => exportAllRowsTxtContent({
26220
+ (fileName) => exportAllRowsTxtContent({
26221
+ fileName,
24680
26222
  rows: params.getAllRows(),
24681
26223
  columns: params.getColumns()
24682
26224
  }),
24683
- "\u6B63\u5728\u5BFC\u51FA TXT\u2026"
26225
+ "\u6B63\u5728\u5BFC\u51FA TXT\u2026",
26226
+ buildTableExportFileName("txt")
24684
26227
  );
24685
26228
  },
24686
26229
  onExportXlsx: () => {
24687
26230
  params.hideMenu();
24688
26231
  params.startExportTask(
24689
- () => exportAllRowsXlsxContent({
26232
+ (fileName) => exportAllRowsXlsxContent({
26233
+ includeSummary: params.getExportSummaryEnabled?.() ?? false,
26234
+ fileName,
24690
26235
  rows: params.getAllRows(),
24691
26236
  columns: params.getColumns()
24692
26237
  }),
24693
- "\u6B63\u5728\u5BFC\u51FA XLSX\u2026"
26238
+ "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
26239
+ buildTableExportFileName("xlsx")
24694
26240
  );
24695
26241
  },
24696
26242
  onFreezeRow: params.onFreezeRow,
@@ -25262,6 +26808,7 @@ function showHeaderContextMenu(params) {
25262
26808
  rowCount: getRows().length,
25263
26809
  columnCount: getColumns().length,
25264
26810
  actions: createListTableContextMenuActions({
26811
+ getExportSummaryEnabled: params.getExportSummaryEnabled,
25265
26812
  getRows,
25266
26813
  getAllRows,
25267
26814
  getColumns,
@@ -25372,9 +26919,9 @@ function updateHeaderActionTooltip(params) {
25372
26919
  tooltip.show(tooltipPoint.x, tooltipPoint.y, text);
25373
26920
  }
25374
26921
  function showDragTooltip(params) {
25375
- const { tooltip, toViewportPoint, x, y, text } = params;
26922
+ const { tooltip, toViewportPoint, x, y, text, variant } = params;
25376
26923
  const point = toViewportPoint(x, y + 16);
25377
- tooltip.show(point.x, point.y, text);
26924
+ tooltip.show(point.x, point.y, text, variant);
25378
26925
  }
25379
26926
 
25380
26927
  // src/shared/utils/hit-result.ts
@@ -26827,6 +28374,11 @@ var ListTableEventController = class {
26827
28374
  event.clientY
26828
28375
  );
26829
28376
  const result = this.host.resolvePointerHit(position.x, position.y);
28377
+ if (result.area === "resize-handle" && result.columnKey) {
28378
+ event.preventDefault();
28379
+ void this.autoFitColumnWidths(result.columnKey);
28380
+ return;
28381
+ }
26830
28382
  if (this.tryOpenNestedTableEditor(result, 2)) {
26831
28383
  return;
26832
28384
  }
@@ -26909,6 +28461,7 @@ var ListTableEventController = class {
26909
28461
  },
26910
28462
  getRows: () => this.host.getDisplayData(),
26911
28463
  getAllRows: () => this.host.options.dataSource,
28464
+ getExportSummaryEnabled: () => Boolean(this.host.currentRenderSnapshot?.hasSummary),
26912
28465
  onCopyImage: (scope) => this.copyImage(scope, event),
26913
28466
  onExportImage: (scope) => this.copyImage(scope, event, true),
26914
28467
  canCopySelectionImage: resolveImageSelection(this.host) != null,
@@ -26932,8 +28485,8 @@ var ListTableEventController = class {
26932
28485
  getColumns: () => this.host.leafColumns,
26933
28486
  getHeaderTree: () => this.host.headerTree,
26934
28487
  getFrozenRowPosition: (rowIndex) => this.host.getFrozenRowPosition(rowIndex),
26935
- startExportTask: (task, text) => {
26936
- this.host.startExportTask(task, text);
28488
+ startExportTask: (task, text, defaultFileName) => {
28489
+ this.host.startExportTask(task, text, defaultFileName);
26937
28490
  },
26938
28491
  contentDownload: this.host.resolveContentDownloadAction(hitContent),
26939
28492
  onSelectHeader: (columnKey, rect) => {
@@ -27563,7 +29116,8 @@ var ListTableEventController = class {
27563
29116
  toViewportPoint: (x, y) => this.host.toViewportPoint(x, y),
27564
29117
  x: dragPos.x,
27565
29118
  y: dragPos.y,
27566
- text: `${resizeResult.column.title}: ${resizeResult.nextWidth}px`
29119
+ text: `${resizeResult.column.title}: ${resizeResult.nextWidth}px`,
29120
+ variant: "resize"
27567
29121
  });
27568
29122
  }
27569
29123
  this.host.scheduleRender();
@@ -28082,10 +29636,11 @@ var ListTableEventController = class {
28082
29636
  const actions = createListTableContextMenuActions({
28083
29637
  getRows: () => resolveNestedTableDisplayRows(content, getNestedState()),
28084
29638
  getAllRows: () => content.dataSource,
29639
+ getExportSummaryEnabled: () => getNestedState()?.summaryVisible === true,
28085
29640
  getColumns: () => columns,
28086
29641
  getHeaderTree: () => headerTree,
28087
29642
  hideMenu,
28088
- startExportTask: (task, text) => this.host.startExportTask(task, text),
29643
+ startExportTask: (task, text, defaultFileName) => this.host.startExportTask(task, text, defaultFileName),
28089
29644
  onFreezeRow: () => {
28090
29645
  },
28091
29646
  onUnfreezeRow: () => {
@@ -29477,7 +31032,17 @@ function createListTableEventHost(core) {
29477
31032
  return snapshot?.columnSegments ?? core.columnSegments;
29478
31033
  },
29479
31034
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
29480
- startExportTask: (task, text) => core.contentActionController.startExportTask(task, text),
31035
+ startExportTask: (task, text, defaultFileName) => {
31036
+ const configured = core.options.exportOptions?.fileName?.trim();
31037
+ const extension = defaultFileName?.match(/\.(xlsx|csv|txt|json)$/i)?.[0];
31038
+ const fileName = configured && extension ? configured.replace(/\.(xlsx|csv|txt|json)$/i, "") + extension : defaultFileName;
31039
+ core.contentActionController.startExportTask(
31040
+ task,
31041
+ text,
31042
+ void 0,
31043
+ fileName
31044
+ );
31045
+ },
29481
31046
  resolveContentDownloadAction: (content) => core.contentActionController.resolveContentDownloadAction(
29482
31047
  content
29483
31048
  )
@@ -33178,6 +34743,7 @@ function assembleListTableCoreControllers(core, options) {
33178
34743
  requestRender: () => render()
33179
34744
  });
33180
34745
  core.contentActionController = new ListTableContentActionController({
34746
+ getSummaryVisible: () => Boolean(getCurrentRenderSnapshot()?.hasSummary),
33181
34747
  getOptions: () => core.options,
33182
34748
  getLeafColumns: () => core.leafColumns,
33183
34749
  overlayController: core.overlayController,
@@ -33939,6 +35505,6 @@ __publicField(ListTable, "SELECTION_COLUMN", SELECTION_COLUMN);
33939
35505
  /** `SELECTION_COLUMN` 的语义化别名。 */
33940
35506
  __publicField(ListTable, "ROW_SELECTION_COLUMN", ROW_SELECTION_COLUMN);
33941
35507
 
33942
- export { EXPAND_COLUMN, ListTable, ROW_SELECTION_COLUMN, SELECTION_COLUMN, getListTableStoredPageSize, resetListTableStoredColumnConfig };
35508
+ export { EXPAND_COLUMN, ListTable, ROW_SELECTION_COLUMN, SELECTION_COLUMN, getListTableStoredPageSize, listTableAntdIconNames, listTableBuiltinIconNames, listTableIconNames, resetListTableStoredColumnConfig };
33943
35509
  //# sourceMappingURL=index.js.map
33944
35510
  //# sourceMappingURL=index.js.map