@sia.soul/sia-react-ui 0.1.80 → 0.1.82

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/chart.cjs CHANGED
@@ -254,10 +254,18 @@ function installTitleTooltips(doc) {
254
254
  let root;
255
255
  let observer;
256
256
  let tooltipId = "";
257
+ const canShow = (element) => {
258
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
259
+ return [element, ...element.querySelectorAll("*")].some((node) => {
260
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
261
+ const style = doc.defaultView?.getComputedStyle(node);
262
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
263
+ });
264
+ };
257
265
  const candidate = (target) => {
258
266
  if (!(target instanceof Element)) return null;
259
267
  const element = target.closest("[data-sia-title]");
260
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
268
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
261
269
  };
262
270
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
263
271
  const hide = () => {
@@ -275,7 +283,7 @@ function installTitleTooltips(doc) {
275
283
  };
276
284
  const render = () => {
277
285
  const text = active?.getAttribute("data-sia-title");
278
- if (!active?.isConnected || !text) {
286
+ if (!active?.isConnected || !text || !canShow(active)) {
279
287
  hide();
280
288
  return;
281
289
  }
package/dist/chart.js CHANGED
@@ -56,9 +56,9 @@ import {
56
56
  smoothSvgPath,
57
57
  svgArcPath,
58
58
  transferCompositeValue
59
- } from "./chunk-BDS42FR3.js";
60
- import "./chunk-JZWTTSOZ.js";
61
- import "./chunk-H7IAG72O.js";
59
+ } from "./chunk-ZHQ7PCCF.js";
60
+ import "./chunk-SJFT5OSX.js";
61
+ import "./chunk-PUZWLIIJ.js";
62
62
  export {
63
63
  BarChart,
64
64
  CHART_VIEWBOX_HEIGHT,
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  Popover,
3
3
  Tooltip
4
- } from "./chunk-WXERMWTW.js";
4
+ } from "./chunk-V4GGPMKS.js";
5
5
  import {
6
6
  Dropdown,
7
7
  Icon,
8
8
  useOverlayLifecycle
9
- } from "./chunk-XHLIIDD2.js";
9
+ } from "./chunk-CLBWJEID.js";
10
10
  import {
11
11
  Button,
12
12
  TextArea
13
- } from "./chunk-JZWTTSOZ.js";
13
+ } from "./chunk-SJFT5OSX.js";
14
14
  import {
15
15
  useControlSize,
16
16
  useControllableState,
17
17
  withTitleTooltip
18
- } from "./chunk-H7IAG72O.js";
18
+ } from "./chunk-PUZWLIIJ.js";
19
19
 
20
20
  // src/components/Toolbar.tsx
21
21
  import { useLayoutEffect, useRef, useState } from "react";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  Button,
3
3
  TextArea
4
- } from "./chunk-JZWTTSOZ.js";
4
+ } from "./chunk-SJFT5OSX.js";
5
5
  import {
6
6
  PopupPortal,
7
7
  useControlSize,
8
8
  useControllableState,
9
9
  usePlaceholderMode,
10
10
  withTitleTooltip
11
- } from "./chunk-H7IAG72O.js";
11
+ } from "./chunk-PUZWLIIJ.js";
12
12
 
13
13
  // src/components/Icon.tsx
14
14
  import { forwardRef } from "react";
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  FloatingScrollbarProvider,
3
3
  Select
4
- } from "./chunk-WXERMWTW.js";
4
+ } from "./chunk-V4GGPMKS.js";
5
5
  import {
6
6
  Icon
7
- } from "./chunk-XHLIIDD2.js";
7
+ } from "./chunk-CLBWJEID.js";
8
8
  import {
9
9
  PopupPortal,
10
10
  useControlSize,
11
11
  useControllableState,
12
12
  useFloatingPlaceholder,
13
13
  withTitleTooltip
14
- } from "./chunk-H7IAG72O.js";
14
+ } from "./chunk-PUZWLIIJ.js";
15
15
 
16
16
  // src/components/DatePicker.tsx
17
17
  import { useEffect, useId, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState } from "react";
@@ -20,10 +20,18 @@ function installTitleTooltips(doc) {
20
20
  let root;
21
21
  let observer;
22
22
  let tooltipId = "";
23
+ const canShow = (element) => {
24
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
25
+ return [element, ...element.querySelectorAll("*")].some((node) => {
26
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
27
+ const style = doc.defaultView?.getComputedStyle(node);
28
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
29
+ });
30
+ };
23
31
  const candidate = (target) => {
24
32
  if (!(target instanceof Element)) return null;
25
33
  const element = target.closest("[data-sia-title]");
26
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
34
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
27
35
  };
28
36
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
29
37
  const hide = () => {
@@ -41,7 +49,7 @@ function installTitleTooltips(doc) {
41
49
  };
42
50
  const render = () => {
43
51
  const text = active?.getAttribute("data-sia-title");
44
- if (!active?.isConnected || !text) {
52
+ if (!active?.isConnected || !text || !canShow(active)) {
45
53
  hide();
46
54
  return;
47
55
  }
@@ -2,14 +2,14 @@ import {
2
2
  Dropdown,
3
3
  Icon,
4
4
  Input
5
- } from "./chunk-XHLIIDD2.js";
5
+ } from "./chunk-CLBWJEID.js";
6
6
  import {
7
7
  Button
8
- } from "./chunk-JZWTTSOZ.js";
8
+ } from "./chunk-SJFT5OSX.js";
9
9
  import {
10
10
  useFloatingPlaceholder,
11
11
  withTitleTooltip
12
- } from "./chunk-H7IAG72O.js";
12
+ } from "./chunk-PUZWLIIJ.js";
13
13
 
14
14
  // src/components/RichTextEditor.tsx
15
15
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -2,7 +2,7 @@ import {
2
2
  useControlSize,
3
3
  useFloatingPlaceholder,
4
4
  withTitleTooltip
5
- } from "./chunk-H7IAG72O.js";
5
+ } from "./chunk-PUZWLIIJ.js";
6
6
 
7
7
  // src/components/Button.tsx
8
8
  import { forwardRef, useRef } from "react";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  useFloatingPlaceholder,
3
3
  withTitleTooltip
4
- } from "./chunk-H7IAG72O.js";
4
+ } from "./chunk-PUZWLIIJ.js";
5
5
 
6
6
  // src/components/MarkdownEditor.tsx
7
7
  import { useEffect, useRef, useState } from "react";
@@ -3,10 +3,10 @@ import {
3
3
  Icon,
4
4
  Input,
5
5
  Modal
6
- } from "./chunk-XHLIIDD2.js";
6
+ } from "./chunk-CLBWJEID.js";
7
7
  import {
8
8
  Button
9
- } from "./chunk-JZWTTSOZ.js";
9
+ } from "./chunk-SJFT5OSX.js";
10
10
  import {
11
11
  ControlSizeContext,
12
12
  PopupPortal,
@@ -14,7 +14,7 @@ import {
14
14
  useControllableState,
15
15
  useFloatingPlaceholder,
16
16
  withTitleTooltip
17
- } from "./chunk-H7IAG72O.js";
17
+ } from "./chunk-PUZWLIIJ.js";
18
18
 
19
19
  // src/components/Checkbox.tsx
20
20
  import { createContext, forwardRef, useContext, useEffect, useId, useMemo, useRef } from "react";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  Button,
3
3
  TextArea
4
- } from "./chunk-JZWTTSOZ.js";
4
+ } from "./chunk-SJFT5OSX.js";
5
5
  import {
6
6
  withTitleTooltip
7
- } from "./chunk-H7IAG72O.js";
7
+ } from "./chunk-PUZWLIIJ.js";
8
8
 
9
9
  // src/components/ChartWhirlingLoading.tsx
10
10
  import { useEffect, useState } from "react";
package/dist/core.cjs CHANGED
@@ -182,10 +182,18 @@ function installTitleTooltips(doc) {
182
182
  let root;
183
183
  let observer;
184
184
  let tooltipId = "";
185
+ const canShow = (element) => {
186
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
187
+ return [element, ...element.querySelectorAll("*")].some((node) => {
188
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
189
+ const style = doc.defaultView?.getComputedStyle(node);
190
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
191
+ });
192
+ };
185
193
  const candidate = (target) => {
186
194
  if (!(target instanceof Element)) return null;
187
195
  const element = target.closest("[data-sia-title]");
188
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
196
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
189
197
  };
190
198
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
191
199
  const hide = () => {
@@ -203,7 +211,7 @@ function installTitleTooltips(doc) {
203
211
  };
204
212
  const render = () => {
205
213
  const text = active?.getAttribute("data-sia-title");
206
- if (!active?.isConnected || !text) {
214
+ if (!active?.isConnected || !text || !canShow(active)) {
207
215
  hide();
208
216
  return;
209
217
  }
package/dist/core.css CHANGED
@@ -10376,10 +10376,14 @@ button.sia-treemap__tile:focus-visible,
10376
10376
  }
10377
10377
  .sia-treemap__value {
10378
10378
  flex: 0 0 auto;
10379
+ max-width: 100%;
10380
+ overflow: hidden;
10379
10381
  color: rgb(21 34 56 / 72%);
10380
10382
  font-size: var(--sia-treemap-value-font-size, 11px);
10381
10383
  font-variant-numeric: tabular-nums;
10382
10384
  line-height: 1.2;
10385
+ text-overflow: ellipsis;
10386
+ white-space: nowrap;
10383
10387
  }
10384
10388
  [data-sia-theme=dark] .sia-treemap__tile {
10385
10389
  --sia-treemap-tile-color: color-mix(in srgb, var(--sia-treemap-color, #7aa8e8) 34%, #243248);
package/dist/core.js CHANGED
@@ -10,20 +10,20 @@ import {
10
10
  Typography,
11
11
  Upload,
12
12
  message
13
- } from "./chunk-ZNJK3IKA.js";
13
+ } from "./chunk-BCOFQJ76.js";
14
14
  import {
15
15
  Select
16
- } from "./chunk-WXERMWTW.js";
16
+ } from "./chunk-V4GGPMKS.js";
17
17
  import {
18
18
  Icon,
19
19
  Input,
20
20
  Modal,
21
21
  Segmented
22
- } from "./chunk-XHLIIDD2.js";
22
+ } from "./chunk-CLBWJEID.js";
23
23
  import {
24
24
  Button
25
- } from "./chunk-JZWTTSOZ.js";
26
- import "./chunk-H7IAG72O.js";
25
+ } from "./chunk-SJFT5OSX.js";
26
+ import "./chunk-PUZWLIIJ.js";
27
27
  export {
28
28
  Breadcrumb,
29
29
  Button,
package/dist/index.cjs CHANGED
@@ -331,10 +331,18 @@ function installTitleTooltips(doc) {
331
331
  let root;
332
332
  let observer;
333
333
  let tooltipId = "";
334
+ const canShow = (element) => {
335
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
336
+ return [element, ...element.querySelectorAll("*")].some((node) => {
337
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
338
+ const style = doc.defaultView?.getComputedStyle(node);
339
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
340
+ });
341
+ };
334
342
  const candidate = (target) => {
335
343
  if (!(target instanceof Element)) return null;
336
344
  const element = target.closest("[data-sia-title]");
337
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
345
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
338
346
  };
339
347
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
340
348
  const hide = () => {
@@ -352,7 +360,7 @@ function installTitleTooltips(doc) {
352
360
  };
353
361
  const render = () => {
354
362
  const text = active?.getAttribute("data-sia-title");
355
- if (!active?.isConnected || !text) {
363
+ if (!active?.isConnected || !text || !canShow(active)) {
356
364
  hide();
357
365
  return;
358
366
  }
@@ -11912,12 +11920,86 @@ function collapseNarrowNodes(nodes, placements, bounds, minimumArea, minimumWidt
11912
11920
  };
11913
11921
  return squarify([...kept, overflowNode], bounds, minimumArea);
11914
11922
  }
11915
- function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWidth, minTileHeight, maxVisibleNodes, overflowLabel, activeKey, depth = 0, parentKey = "root") {
11923
+ function placeCompactGrid(nodes, bounds, minimumWidth, minimumHeight) {
11924
+ if (nodes.length === 0 || bounds.width <= 0 || bounds.height <= 0) return [];
11925
+ const preferredAspectRatio = Math.max(0.1, minimumWidth / Math.max(1, minimumHeight));
11926
+ let columnCount = 1;
11927
+ let bestScore = Number.POSITIVE_INFINITY;
11928
+ for (let columns = 1; columns <= nodes.length; columns += 1) {
11929
+ const rows = Math.ceil(nodes.length / columns);
11930
+ const cellWidth = bounds.width / columns;
11931
+ const cellHeight = bounds.height / rows;
11932
+ const widthDeficit = Math.max(0, minimumWidth - cellWidth) / Math.max(1, minimumWidth);
11933
+ const heightDeficit = Math.max(0, minimumHeight - cellHeight) / Math.max(1, minimumHeight);
11934
+ const aspectPenalty = Math.abs(Math.log(Math.max(0.01, cellWidth / Math.max(1, cellHeight)) / preferredAspectRatio));
11935
+ const emptyPenalty = (rows * columns - nodes.length) / nodes.length;
11936
+ const score = (widthDeficit + heightDeficit) * 12 + aspectPenalty + emptyPenalty * 0.25;
11937
+ if (score < bestScore) {
11938
+ bestScore = score;
11939
+ columnCount = columns;
11940
+ }
11941
+ }
11942
+ const orderedNodes = [...nodes].sort((left, right) => layoutValue(right) - layoutValue(left));
11943
+ const rowCount = Math.ceil(orderedNodes.length / columnCount);
11944
+ const rowHeight = bounds.height / rowCount;
11945
+ return orderedNodes.map((node, index) => {
11946
+ const row = Math.floor(index / columnCount);
11947
+ const firstIndex = row * columnCount;
11948
+ const itemsInRow = Math.min(columnCount, orderedNodes.length - firstIndex);
11949
+ const column = index - firstIndex;
11950
+ const cellWidth = bounds.width / itemsInRow;
11951
+ return {
11952
+ node,
11953
+ rectangle: {
11954
+ x: bounds.x + column * cellWidth,
11955
+ y: bounds.y + row * rowHeight,
11956
+ width: column === itemsInRow - 1 ? bounds.x + bounds.width - (bounds.x + column * cellWidth) : cellWidth,
11957
+ height: row === rowCount - 1 ? bounds.y + bounds.height - (bounds.y + row * rowHeight) : rowHeight
11958
+ }
11959
+ };
11960
+ });
11961
+ }
11962
+ function preserveNarrowNodes(nodes, placements, bounds, minimumArea, minimumWidth, minimumHeight, groupKey) {
11963
+ const narrowKeys = new Set(
11964
+ placements.filter(({ rectangle }) => rectangle.width < minimumWidth || rectangle.height < minimumHeight).map(({ node }) => node.key)
11965
+ );
11966
+ if (narrowKeys.size === 0) return placements;
11967
+ let kept = nodes.filter((node) => !narrowKeys.has(node.key));
11968
+ let compact = nodes.filter((node) => narrowKeys.has(node.key));
11969
+ const totalArea = Math.max(1, bounds.width * bounds.height);
11970
+ while (kept.length > 0) {
11971
+ const keptWeight = kept.reduce((sum, node) => sum + layoutValue(node), 0);
11972
+ const compactWeight = compact.reduce((sum, node) => sum + layoutValue(node), 0);
11973
+ const proportionalArea = totalArea * compactWeight / Math.max(1, keptWeight + compactWeight);
11974
+ const readableArea = Math.min(
11975
+ totalArea * 0.82,
11976
+ Math.max(proportionalArea, compact.length * minimumArea)
11977
+ );
11978
+ const compactLayoutWeight = readableArea * keptWeight / Math.max(1, totalArea - readableArea);
11979
+ const compactGroup = {
11980
+ key: `${groupKey}::sia-compact-grid`,
11981
+ label: "",
11982
+ value: compact.reduce((sum, node) => sum + Math.max(0, node.value), 0),
11983
+ layoutValue: Math.max(compactWeight, compactLayoutWeight)
11984
+ };
11985
+ const outerPlacements = squarify([...kept, compactGroup], bounds, minimumArea);
11986
+ const newlyNarrowKeys = new Set(
11987
+ outerPlacements.filter(({ node, rectangle }) => node.key !== compactGroup.key && (rectangle.width < minimumWidth || rectangle.height < minimumHeight)).map(({ node }) => node.key)
11988
+ );
11989
+ if (newlyNarrowKeys.size === 0) {
11990
+ return outerPlacements.flatMap(({ node, rectangle }) => node.key === compactGroup.key ? placeCompactGrid(compact, rectangle, minimumWidth, minimumHeight) : [{ node, rectangle }]);
11991
+ }
11992
+ compact = [...compact, ...kept.filter((node) => newlyNarrowKeys.has(node.key))];
11993
+ kept = kept.filter((node) => !newlyNarrowKeys.has(node.key));
11994
+ }
11995
+ return placeCompactGrid(compact, bounds, minimumWidth, minimumHeight);
11996
+ }
11997
+ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWidth, minTileHeight, maxVisibleNodes, aggregateOverflow, overflowLabel, activeKey, depth = 0, parentKey = "root") {
11916
11998
  const result = [];
11917
11999
  const minimumWidthWithGap = minTileWidth + gap * 2;
11918
12000
  const minimumHeightWithGap = minTileHeight + gap * 2;
11919
12001
  const minimumArea = Math.max(1, minimumWidthWithGap * minimumHeightWithGap);
11920
- const visibleNodes = limitNodes(
12002
+ const visibleNodes = aggregateOverflow ? limitNodes(
11921
12003
  nodes,
11922
12004
  bounds,
11923
12005
  minimumArea,
@@ -11925,9 +12007,9 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
11925
12007
  `${parentKey}:${depth}`,
11926
12008
  overflowLabel,
11927
12009
  activeKey
11928
- );
12010
+ ) : nodes;
11929
12011
  const initialPlacements = squarify(visibleNodes, bounds, minimumArea);
11930
- const placements = parentKey.endsWith("::sia-readable-overflow") ? initialPlacements : collapseNarrowNodes(
12012
+ const placements = aggregateOverflow ? parentKey.endsWith("::sia-readable-overflow") ? initialPlacements : collapseNarrowNodes(
11931
12013
  visibleNodes,
11932
12014
  initialPlacements,
11933
12015
  bounds,
@@ -11937,6 +12019,14 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
11937
12019
  `${parentKey}:${depth}`,
11938
12020
  overflowLabel,
11939
12021
  activeKey
12022
+ ) : preserveNarrowNodes(
12023
+ visibleNodes,
12024
+ initialPlacements,
12025
+ bounds,
12026
+ minimumArea,
12027
+ minimumWidthWithGap,
12028
+ minimumHeightWithGap,
12029
+ `${parentKey}:${depth}`
11940
12030
  );
11941
12031
  placements.forEach(({ node, rectangle }) => {
11942
12032
  const inset = Math.min(gap, rectangle.width / 4, rectangle.height / 4);
@@ -11965,6 +12055,7 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
11965
12055
  minTileWidth,
11966
12056
  minTileHeight,
11967
12057
  maxVisibleNodes,
12058
+ aggregateOverflow,
11968
12059
  overflowLabel,
11969
12060
  activeKey,
11970
12061
  depth + 1,
@@ -12009,9 +12100,11 @@ function Treemap({
12009
12100
  minTileWidth = 28,
12010
12101
  minTileHeight = 18,
12011
12102
  maxVisibleNodes = 180,
12103
+ aggregateOverflow = true,
12012
12104
  emptyContent = "\u6682\u65E0\u6570\u636E",
12013
12105
  valueFormatter = defaultValueFormatter,
12014
12106
  overflowLabel = (count) => `\u5176\u4ED6 ${count} \u9879`,
12107
+ showValueInCompactTiles = false,
12015
12108
  onExpandedChange,
12016
12109
  onFocusedChange,
12017
12110
  onNodeClick,
@@ -12041,10 +12134,11 @@ function Treemap({
12041
12134
  Math.max(1, minTileWidth),
12042
12135
  Math.max(1, minTileHeight),
12043
12136
  Math.max(1, maxVisibleNodes),
12137
+ aggregateOverflow,
12044
12138
  overflowLabel,
12045
12139
  activeKey
12046
12140
  ),
12047
- [activeKey, data, effectiveFocusedNode, expandedSet, gap, headerHeight, maxVisibleNodes, minTileHeight, minTileWidth, overflowLabel, size]
12141
+ [activeKey, aggregateOverflow, data, effectiveFocusedNode, expandedSet, gap, headerHeight, maxVisibleNodes, minTileHeight, minTileWidth, overflowLabel, size]
12048
12142
  );
12049
12143
  (0, import_react47.useEffect)(() => {
12050
12144
  const element = containerRef.current;
@@ -12102,7 +12196,7 @@ function Treemap({
12102
12196
  "--sia-treemap-value-font-size": `${Math.max(7, fontSize - 1)}px`
12103
12197
  };
12104
12198
  const showLabel = rectangle.width >= minLabelWidth && rectangle.height >= minLabelHeight;
12105
- const showValue = rectangle.width >= Math.max(52, minLabelWidth) && rectangle.height >= Math.max(34, minLabelHeight + 12);
12199
+ const showValue = showLabel && (showValueInCompactTiles || rectangle.width >= Math.max(52, minLabelWidth) && rectangle.height >= Math.max(34, minLabelHeight + 12));
12106
12200
  const hasChildren = Boolean(node.hasChildren || node.children?.length);
12107
12201
  const title = `${node.label} \xB7 ${String(valueFormatter(node.value, node))}`;
12108
12202
  if (expanded) {
@@ -12494,6 +12588,69 @@ var import_react53 = require("react");
12494
12588
 
12495
12589
  // src/components/Table/plugins.tsx
12496
12590
  var import_react52 = require("react");
12591
+
12592
+ // src/components/Table/columnSettings.ts
12593
+ function reconcileColumnSettings(columns, defaults, saved) {
12594
+ const stored = new Map(saved.map((item) => [item.key, item]));
12595
+ const base = new Map(defaults.map((item) => [item.key, item]));
12596
+ const result = [];
12597
+ const rank = (column) => {
12598
+ const own = stored.get(column.key)?.order;
12599
+ if (own !== void 0) return own;
12600
+ const children = column.children?.map(rank).filter((value) => value !== void 0);
12601
+ return children?.length ? Math.min(...children) : void 0;
12602
+ };
12603
+ const walk = (source, parent) => {
12604
+ const entries = source.map((column, index) => ({ column, index, rank: rank(column) }));
12605
+ let anchor = -1;
12606
+ for (const entry of entries) {
12607
+ if (entry.rank !== void 0) anchor = entry.rank;
12608
+ else entry.rank = anchor + (entry.index + 1) / (entries.length + 1);
12609
+ }
12610
+ entries.sort((a, b) => a.rank - b.rank || a.index - b.index);
12611
+ for (const { column } of entries) {
12612
+ const own = stored.get(column.key);
12613
+ if (column.children?.length) {
12614
+ walk(column.children, own ?? parent);
12615
+ } else {
12616
+ const fallback = base.get(column.key);
12617
+ if (!fallback) continue;
12618
+ const item = own ?? {
12619
+ ...fallback,
12620
+ fixed: parent?.fixed ?? fallback.fixed,
12621
+ visible: fallback.visible && (parent?.visible ?? true)
12622
+ };
12623
+ result.push({ ...item, order: result.length });
12624
+ }
12625
+ }
12626
+ };
12627
+ walk(columns);
12628
+ return result;
12629
+ }
12630
+ function mergeColumnSettings(columns, saved, active) {
12631
+ const merged = new Map(saved.filter((item) => !["__index__", "__selection__"].includes(item.key)).map((item) => [item.key, item]));
12632
+ active.forEach((item) => merged.set(item.key, item));
12633
+ const current = new Map(active.map((item) => [item.key, item]));
12634
+ const walk = (column) => {
12635
+ if (!column.children?.length) return current.has(column.key) ? [current.get(column.key)] : [];
12636
+ const leaves = column.children.flatMap(walk);
12637
+ if (leaves.length) {
12638
+ const old = merged.get(column.key);
12639
+ merged.set(column.key, {
12640
+ key: column.key,
12641
+ width: old?.width ?? column.width,
12642
+ fixed: old?.fixed,
12643
+ visible: leaves.some((item) => item.visible),
12644
+ order: Math.min(...leaves.map((item) => item.order))
12645
+ });
12646
+ }
12647
+ return leaves;
12648
+ };
12649
+ columns.forEach(walk);
12650
+ return [...merged.values()].sort((left, right) => left.order - right.order);
12651
+ }
12652
+
12653
+ // src/components/Table/plugins.tsx
12497
12654
  var import_jsx_runtime51 = require("react/jsx-runtime");
12498
12655
  function TableCheckbox({
12499
12656
  checked,
@@ -14584,13 +14741,14 @@ function createColumnSettingPlugin(options = {}) {
14584
14741
  const keys = new Set(baseItems(context).map((item) => item.key));
14585
14742
  return items.filter((item) => keys.has(item.key));
14586
14743
  };
14587
- const currentItems = (context) => context.state.items?.length ? editableItems(context.state.items, context) : baseItems(context);
14744
+ const currentItems = (context) => reconcileColumnSettings(context.baseColumns, baseItems(context), context.state.items ?? []);
14588
14745
  const save = (snapshot) => options.adapter && options.scope ? options.adapter.save(options.scope, snapshot).catch(() => void 0) : Promise.resolve();
14589
14746
  function apply(items, context, persist = true) {
14590
14747
  const normalized = orderColumnSettings(
14591
14748
  editableItems(items, context).sort((a, b) => a.order - b.order)
14592
14749
  );
14593
- context.setState((state) => ({ ...state, items: normalized, open: false }));
14750
+ const snapshot = mergeColumnSettings(context.baseColumns, context.state.items, normalized);
14751
+ context.setState((state) => ({ ...state, items: snapshot, open: false }));
14594
14752
  const columnMap = new Map(
14595
14753
  flattenTableColumns(context.baseColumns).map((column) => [
14596
14754
  column.key,
@@ -14605,7 +14763,7 @@ function createColumnSettingPlugin(options = {}) {
14605
14763
  );
14606
14764
  if (persist)
14607
14765
  void save({
14608
- columns: normalized,
14766
+ columns: snapshot,
14609
14767
  pageSize: context.state.pageSize,
14610
14768
  version: 1
14611
14769
  });
@@ -14670,9 +14828,10 @@ function createColumnSettingPlugin(options = {}) {
14670
14828
  const next = currentItems(context).map(
14671
14829
  (item) => item.key === columnKey ? { ...item, width } : item
14672
14830
  );
14673
- context.setState((state) => ({ ...state, items: next }));
14831
+ const snapshot = mergeColumnSettings(context.baseColumns, context.state.items, next);
14832
+ context.setState((state) => ({ ...state, items: snapshot }));
14674
14833
  void save({
14675
- columns: next,
14834
+ columns: snapshot,
14676
14835
  pageSize: context.state.pageSize,
14677
14836
  version: 1
14678
14837
  });
@@ -14680,7 +14839,7 @@ function createColumnSettingPlugin(options = {}) {
14680
14839
  if (event.type === "pageSizeChange") {
14681
14840
  const pageSize = event.payload;
14682
14841
  context.setState((state) => ({ ...state, pageSize }));
14683
- void save({ columns: currentItems(context), pageSize, version: 1 });
14842
+ void save({ columns: mergeColumnSettings(context.baseColumns, context.state.items, currentItems(context)), pageSize, version: 1 });
14684
14843
  }
14685
14844
  },
14686
14845
  renderToolbarEnd: options.showButton === false ? void 0 : (context) => {
@@ -15526,7 +15685,7 @@ function TableInner(props, ref) {
15526
15685
  function renderHeaderContent(column, content) {
15527
15686
  const title = getHeaderTitleText(column.title ?? column.name) || column.name || column.key;
15528
15687
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "sia-table__header-label", children: [
15529
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "sia-table__header-label-content", ...withTitleTooltip({ title }), children: content }),
15688
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "sia-table__header-label-content", "data-sia-title-overflow-only": true, ...withTitleTooltip({ title }), children: content }),
15530
15689
  column.description != null && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Tooltip, { title: column.description, placement: "top", trigger: ["hover", "focus"], children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", className: "sia-table__header-description", "aria-label": `${column.name ?? column.key}\u5217\u8BF4\u660E`, onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Icon, { name: "info", size: 14 }) }) })
15531
15690
  ] });
15532
15691
  }
package/dist/index.css CHANGED
@@ -10376,10 +10376,14 @@ button.sia-treemap__tile:focus-visible,
10376
10376
  }
10377
10377
  .sia-treemap__value {
10378
10378
  flex: 0 0 auto;
10379
+ max-width: 100%;
10380
+ overflow: hidden;
10379
10381
  color: rgb(21 34 56 / 72%);
10380
10382
  font-size: var(--sia-treemap-value-font-size, 11px);
10381
10383
  font-variant-numeric: tabular-nums;
10382
10384
  line-height: 1.2;
10385
+ text-overflow: ellipsis;
10386
+ white-space: nowrap;
10383
10387
  }
10384
10388
  [data-sia-theme=dark] .sia-treemap__tile {
10385
10389
  --sia-treemap-tile-color: color-mix(in srgb, var(--sia-treemap-color, #7aa8e8) 34%, #243248);
package/dist/index.d.cts CHANGED
@@ -1802,10 +1802,15 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1802
1802
  */
1803
1803
  minTileHeight?: number;
1804
1804
  /**
1805
- * 最大可见节点数,超出后合并为其他项
1805
+ * 启用节点聚合时允许直接显示的最大节点数;超出后合并为一个汇总节点。
1806
1806
  * @defaultValue 180
1807
1807
  */
1808
1808
  maxVisibleNodes?: number;
1809
+ /**
1810
+ * 是否把数量超限或无法满足最小尺寸的节点合并为汇总节点。关闭后保留每个原始节点,并将尺寸不足的节点排列为紧凑网格;当容器面积不足以容纳所有最小尺寸时,网格仍会按现有空间等分。
1811
+ * @defaultValue true
1812
+ */
1813
+ aggregateOverflow?: boolean;
1809
1814
  /**
1810
1815
  * 没有数据时的显示内容。
1811
1816
  * @defaultValue "暂无数据"
@@ -1816,8 +1821,13 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1816
1821
  * @defaultValue defaultValueFormatter
1817
1822
  */
1818
1823
  valueFormatter?: (value: number, node: TreemapNode) => ReactNode;
1819
- /** 自定义合并节点文案 */
1824
+ /** 启用节点聚合时用于生成汇总节点名称;参数为被合并的节点数量。 */
1820
1825
  overflowLabel?: (count: number) => string;
1826
+ /**
1827
+ * 是否在紧凑矩形块中也显示格式化后的数值;内容超出矩形块时按组件样式截断。
1828
+ * @defaultValue false
1829
+ */
1830
+ showValueInCompactTiles?: boolean;
1821
1831
  /** 展开状态改变时调用。 */
1822
1832
  onExpandedChange?: (keys: string[]) => void;
1823
1833
  /** 聚焦节点改变时调用。 */
@@ -1826,7 +1836,7 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1826
1836
  onNodeClick?: (info: TreemapNodeClickInfo) => void;
1827
1837
  }
1828
1838
  /** 矩形树图组件。 */
1829
- declare function Treemap({ data, activeKey, expandedKeys, defaultExpandedKeys, focusedKey, defaultFocusedKey, gap, headerHeight, minLabelWidth, minLabelHeight, minTileWidth, minTileHeight, maxVisibleNodes, emptyContent, valueFormatter, overflowLabel, onExpandedChange, onFocusedChange, onNodeClick, className, style, ...props }: TreemapProps): react.JSX.Element;
1839
+ declare function Treemap({ data, activeKey, expandedKeys, defaultExpandedKeys, focusedKey, defaultFocusedKey, gap, headerHeight, minLabelWidth, minLabelHeight, minTileWidth, minTileHeight, maxVisibleNodes, aggregateOverflow, emptyContent, valueFormatter, overflowLabel, showValueInCompactTiles, onExpandedChange, onFocusedChange, onNodeClick, className, style, ...props }: TreemapProps): react.JSX.Element;
1830
1840
 
1831
1841
  /** 表格行或列的唯一标识类型。 */
1832
1842
  type TableKey = Key;
@@ -1890,7 +1900,7 @@ interface TableColumn<T = Record<string, unknown>> {
1890
1900
  code?: string;
1891
1901
  /** 名称或字段标识。 */
1892
1902
  name?: string;
1893
- /** 列头内容,可传入文本或 React 节点;默认单行显示,超宽省略,并通过框架 title 提示展示完整文本。节点无法提取文本时使用 name 或 key。 */
1903
+ /** 列头内容,可传入文本或 React 节点;默认单行显示,仅文字实际超宽省略时通过框架 title 提示展示完整文本,完整显示时不提示。节点无法提取文本时使用 name 或 key。 */
1894
1904
  title?: ReactNode;
1895
1905
  /** 补充说明内容。 */
1896
1906
  description?: ReactNode;
@@ -1914,7 +1924,7 @@ interface TableColumn<T = Record<string, unknown>> {
1914
1924
  hidden?: boolean;
1915
1925
  /** 数据单元格默认单行显示并省略溢出内容(含 Space 标签组);设为 false 允许多行,不影响列头始终单行省略。 */
1916
1926
  ellipsis?: boolean;
1917
- /** 数据单元格是否使用 Sia Tooltip 显示完整文本;默认 false,不生成浏览器原生 title。列头始终提供完整标题提示,不受此属性影响。 */
1927
+ /** 数据单元格是否使用 Sia Tooltip 显示完整文本;默认 false,不生成浏览器原生 title。列头仅在文字实际省略时提示完整标题,不受此属性影响。 */
1918
1928
  showTitle?: boolean;
1919
1929
  /** 是否允许该列排序;配合排序插件显示按钮,依次切换升序、降序、未排序,当前排序方向以高亮单箭头显示。 */
1920
1930
  sortable?: boolean;
@@ -2744,7 +2754,7 @@ interface TableColumnSettingApi {
2744
2754
  /** 应用传入的配置;是否持久化由方法参数或插件选项控制。 */
2745
2755
  apply: (settings: readonly TableColumnSettingItem[]) => void;
2746
2756
  }
2747
- /** 创建表格列设置和持久化插件。将返回值加入 Table 的 plugins 后生效;序号和勾选附加列不显示在设置中,不参与业务列排序、显隐、固定位置与宽度配置,旧存储中的附加列配置会被忽略。 */
2757
+ /** 创建表格列设置和持久化插件。将返回值加入 Table 的 plugins 后生效;序号和勾选附加列不显示在设置中,不参与业务列排序、显隐、固定位置与宽度配置,旧存储中的附加列配置会被忽略。动态分组展开时自动补齐新叶子列,收起时保留子列设置供再次展开恢复。 */
2748
2758
  declare function createColumnSettingPlugin<T extends Record<string, unknown>>(options?: TableColumnSettingPluginOptions<T>): TablePlugin<T, ColumnSettingState, TableColumnSettingApi>;
2749
2759
  /** 创建使用 localStorage 持久化表格设置的适配器,按模块及业务标识隔离配置。 */
2750
2760
  declare function createLocalStorageTableSettingsAdapter(prefix?: string): TableSettingsAdapter;
package/dist/index.d.ts CHANGED
@@ -1802,10 +1802,15 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1802
1802
  */
1803
1803
  minTileHeight?: number;
1804
1804
  /**
1805
- * 最大可见节点数,超出后合并为其他项
1805
+ * 启用节点聚合时允许直接显示的最大节点数;超出后合并为一个汇总节点。
1806
1806
  * @defaultValue 180
1807
1807
  */
1808
1808
  maxVisibleNodes?: number;
1809
+ /**
1810
+ * 是否把数量超限或无法满足最小尺寸的节点合并为汇总节点。关闭后保留每个原始节点,并将尺寸不足的节点排列为紧凑网格;当容器面积不足以容纳所有最小尺寸时,网格仍会按现有空间等分。
1811
+ * @defaultValue true
1812
+ */
1813
+ aggregateOverflow?: boolean;
1809
1814
  /**
1810
1815
  * 没有数据时的显示内容。
1811
1816
  * @defaultValue "暂无数据"
@@ -1816,8 +1821,13 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1816
1821
  * @defaultValue defaultValueFormatter
1817
1822
  */
1818
1823
  valueFormatter?: (value: number, node: TreemapNode) => ReactNode;
1819
- /** 自定义合并节点文案 */
1824
+ /** 启用节点聚合时用于生成汇总节点名称;参数为被合并的节点数量。 */
1820
1825
  overflowLabel?: (count: number) => string;
1826
+ /**
1827
+ * 是否在紧凑矩形块中也显示格式化后的数值;内容超出矩形块时按组件样式截断。
1828
+ * @defaultValue false
1829
+ */
1830
+ showValueInCompactTiles?: boolean;
1821
1831
  /** 展开状态改变时调用。 */
1822
1832
  onExpandedChange?: (keys: string[]) => void;
1823
1833
  /** 聚焦节点改变时调用。 */
@@ -1826,7 +1836,7 @@ interface TreemapProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange">
1826
1836
  onNodeClick?: (info: TreemapNodeClickInfo) => void;
1827
1837
  }
1828
1838
  /** 矩形树图组件。 */
1829
- declare function Treemap({ data, activeKey, expandedKeys, defaultExpandedKeys, focusedKey, defaultFocusedKey, gap, headerHeight, minLabelWidth, minLabelHeight, minTileWidth, minTileHeight, maxVisibleNodes, emptyContent, valueFormatter, overflowLabel, onExpandedChange, onFocusedChange, onNodeClick, className, style, ...props }: TreemapProps): react.JSX.Element;
1839
+ declare function Treemap({ data, activeKey, expandedKeys, defaultExpandedKeys, focusedKey, defaultFocusedKey, gap, headerHeight, minLabelWidth, minLabelHeight, minTileWidth, minTileHeight, maxVisibleNodes, aggregateOverflow, emptyContent, valueFormatter, overflowLabel, showValueInCompactTiles, onExpandedChange, onFocusedChange, onNodeClick, className, style, ...props }: TreemapProps): react.JSX.Element;
1830
1840
 
1831
1841
  /** 表格行或列的唯一标识类型。 */
1832
1842
  type TableKey = Key;
@@ -1890,7 +1900,7 @@ interface TableColumn<T = Record<string, unknown>> {
1890
1900
  code?: string;
1891
1901
  /** 名称或字段标识。 */
1892
1902
  name?: string;
1893
- /** 列头内容,可传入文本或 React 节点;默认单行显示,超宽省略,并通过框架 title 提示展示完整文本。节点无法提取文本时使用 name 或 key。 */
1903
+ /** 列头内容,可传入文本或 React 节点;默认单行显示,仅文字实际超宽省略时通过框架 title 提示展示完整文本,完整显示时不提示。节点无法提取文本时使用 name 或 key。 */
1894
1904
  title?: ReactNode;
1895
1905
  /** 补充说明内容。 */
1896
1906
  description?: ReactNode;
@@ -1914,7 +1924,7 @@ interface TableColumn<T = Record<string, unknown>> {
1914
1924
  hidden?: boolean;
1915
1925
  /** 数据单元格默认单行显示并省略溢出内容(含 Space 标签组);设为 false 允许多行,不影响列头始终单行省略。 */
1916
1926
  ellipsis?: boolean;
1917
- /** 数据单元格是否使用 Sia Tooltip 显示完整文本;默认 false,不生成浏览器原生 title。列头始终提供完整标题提示,不受此属性影响。 */
1927
+ /** 数据单元格是否使用 Sia Tooltip 显示完整文本;默认 false,不生成浏览器原生 title。列头仅在文字实际省略时提示完整标题,不受此属性影响。 */
1918
1928
  showTitle?: boolean;
1919
1929
  /** 是否允许该列排序;配合排序插件显示按钮,依次切换升序、降序、未排序,当前排序方向以高亮单箭头显示。 */
1920
1930
  sortable?: boolean;
@@ -2744,7 +2754,7 @@ interface TableColumnSettingApi {
2744
2754
  /** 应用传入的配置;是否持久化由方法参数或插件选项控制。 */
2745
2755
  apply: (settings: readonly TableColumnSettingItem[]) => void;
2746
2756
  }
2747
- /** 创建表格列设置和持久化插件。将返回值加入 Table 的 plugins 后生效;序号和勾选附加列不显示在设置中,不参与业务列排序、显隐、固定位置与宽度配置,旧存储中的附加列配置会被忽略。 */
2757
+ /** 创建表格列设置和持久化插件。将返回值加入 Table 的 plugins 后生效;序号和勾选附加列不显示在设置中,不参与业务列排序、显隐、固定位置与宽度配置,旧存储中的附加列配置会被忽略。动态分组展开时自动补齐新叶子列,收起时保留子列设置供再次展开恢复。 */
2748
2758
  declare function createColumnSettingPlugin<T extends Record<string, unknown>>(options?: TableColumnSettingPluginOptions<T>): TablePlugin<T, ColumnSettingState, TableColumnSettingApi>;
2749
2759
  /** 创建使用 localStorage 持久化表格设置的适配器,按模块及业务标识隔离配置。 */
2750
2760
  declare function createLocalStorageTableSettingsAdapter(prefix?: string): TableSettingsAdapter;
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  smoothSvgPath,
57
57
  svgArcPath,
58
58
  transferCompositeValue
59
- } from "./chunk-BDS42FR3.js";
59
+ } from "./chunk-ZHQ7PCCF.js";
60
60
  import {
61
61
  Badge,
62
62
  Breadcrumb,
@@ -80,19 +80,19 @@ import {
80
80
  Watermark,
81
81
  message,
82
82
  notification
83
- } from "./chunk-ZNJK3IKA.js";
83
+ } from "./chunk-BCOFQJ76.js";
84
84
  import {
85
85
  MarkdownEditor
86
- } from "./chunk-JZPAIZYK.js";
86
+ } from "./chunk-TUXNOSCT.js";
87
87
  import {
88
88
  RichTextContent,
89
89
  RichTextEditor
90
- } from "./chunk-GODBVCIQ.js";
90
+ } from "./chunk-SBNHEETW.js";
91
91
  import {
92
92
  DatePicker,
93
93
  SelectDateRange,
94
94
  TimePanel
95
- } from "./chunk-Y4Y2NVBM.js";
95
+ } from "./chunk-N6DVNCZT.js";
96
96
  import {
97
97
  Checkbox,
98
98
  FloatingScrollbarProvider,
@@ -104,7 +104,7 @@ import {
104
104
  Tooltip,
105
105
  Tree,
106
106
  useScrollbarProximity
107
- } from "./chunk-WXERMWTW.js";
107
+ } from "./chunk-V4GGPMKS.js";
108
108
  import {
109
109
  Dropdown,
110
110
  FILLED_ICON_NAMES,
@@ -117,11 +117,11 @@ import {
117
117
  Segmented,
118
118
  Steps,
119
119
  useOverlayLifecycle
120
- } from "./chunk-XHLIIDD2.js";
120
+ } from "./chunk-CLBWJEID.js";
121
121
  import {
122
122
  Button,
123
123
  TextArea
124
- } from "./chunk-JZWTTSOZ.js";
124
+ } from "./chunk-SJFT5OSX.js";
125
125
  import {
126
126
  ControlSizeContext,
127
127
  PlaceholderModeContext,
@@ -131,7 +131,7 @@ import {
131
131
  useFloatingPlaceholder,
132
132
  usePlaceholderMode,
133
133
  withTitleTooltip
134
- } from "./chunk-H7IAG72O.js";
134
+ } from "./chunk-PUZWLIIJ.js";
135
135
 
136
136
  // src/components/Alert.tsx
137
137
  import { useState } from "react";
@@ -4434,12 +4434,86 @@ function collapseNarrowNodes(nodes, placements, bounds, minimumArea, minimumWidt
4434
4434
  };
4435
4435
  return squarify([...kept, overflowNode], bounds, minimumArea);
4436
4436
  }
4437
- function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWidth, minTileHeight, maxVisibleNodes, overflowLabel, activeKey, depth = 0, parentKey = "root") {
4437
+ function placeCompactGrid(nodes, bounds, minimumWidth, minimumHeight) {
4438
+ if (nodes.length === 0 || bounds.width <= 0 || bounds.height <= 0) return [];
4439
+ const preferredAspectRatio = Math.max(0.1, minimumWidth / Math.max(1, minimumHeight));
4440
+ let columnCount = 1;
4441
+ let bestScore = Number.POSITIVE_INFINITY;
4442
+ for (let columns = 1; columns <= nodes.length; columns += 1) {
4443
+ const rows = Math.ceil(nodes.length / columns);
4444
+ const cellWidth = bounds.width / columns;
4445
+ const cellHeight = bounds.height / rows;
4446
+ const widthDeficit = Math.max(0, minimumWidth - cellWidth) / Math.max(1, minimumWidth);
4447
+ const heightDeficit = Math.max(0, minimumHeight - cellHeight) / Math.max(1, minimumHeight);
4448
+ const aspectPenalty = Math.abs(Math.log(Math.max(0.01, cellWidth / Math.max(1, cellHeight)) / preferredAspectRatio));
4449
+ const emptyPenalty = (rows * columns - nodes.length) / nodes.length;
4450
+ const score = (widthDeficit + heightDeficit) * 12 + aspectPenalty + emptyPenalty * 0.25;
4451
+ if (score < bestScore) {
4452
+ bestScore = score;
4453
+ columnCount = columns;
4454
+ }
4455
+ }
4456
+ const orderedNodes = [...nodes].sort((left, right) => layoutValue(right) - layoutValue(left));
4457
+ const rowCount = Math.ceil(orderedNodes.length / columnCount);
4458
+ const rowHeight = bounds.height / rowCount;
4459
+ return orderedNodes.map((node, index) => {
4460
+ const row = Math.floor(index / columnCount);
4461
+ const firstIndex = row * columnCount;
4462
+ const itemsInRow = Math.min(columnCount, orderedNodes.length - firstIndex);
4463
+ const column = index - firstIndex;
4464
+ const cellWidth = bounds.width / itemsInRow;
4465
+ return {
4466
+ node,
4467
+ rectangle: {
4468
+ x: bounds.x + column * cellWidth,
4469
+ y: bounds.y + row * rowHeight,
4470
+ width: column === itemsInRow - 1 ? bounds.x + bounds.width - (bounds.x + column * cellWidth) : cellWidth,
4471
+ height: row === rowCount - 1 ? bounds.y + bounds.height - (bounds.y + row * rowHeight) : rowHeight
4472
+ }
4473
+ };
4474
+ });
4475
+ }
4476
+ function preserveNarrowNodes(nodes, placements, bounds, minimumArea, minimumWidth, minimumHeight, groupKey) {
4477
+ const narrowKeys = new Set(
4478
+ placements.filter(({ rectangle }) => rectangle.width < minimumWidth || rectangle.height < minimumHeight).map(({ node }) => node.key)
4479
+ );
4480
+ if (narrowKeys.size === 0) return placements;
4481
+ let kept = nodes.filter((node) => !narrowKeys.has(node.key));
4482
+ let compact = nodes.filter((node) => narrowKeys.has(node.key));
4483
+ const totalArea = Math.max(1, bounds.width * bounds.height);
4484
+ while (kept.length > 0) {
4485
+ const keptWeight = kept.reduce((sum, node) => sum + layoutValue(node), 0);
4486
+ const compactWeight = compact.reduce((sum, node) => sum + layoutValue(node), 0);
4487
+ const proportionalArea = totalArea * compactWeight / Math.max(1, keptWeight + compactWeight);
4488
+ const readableArea = Math.min(
4489
+ totalArea * 0.82,
4490
+ Math.max(proportionalArea, compact.length * minimumArea)
4491
+ );
4492
+ const compactLayoutWeight = readableArea * keptWeight / Math.max(1, totalArea - readableArea);
4493
+ const compactGroup = {
4494
+ key: `${groupKey}::sia-compact-grid`,
4495
+ label: "",
4496
+ value: compact.reduce((sum, node) => sum + Math.max(0, node.value), 0),
4497
+ layoutValue: Math.max(compactWeight, compactLayoutWeight)
4498
+ };
4499
+ const outerPlacements = squarify([...kept, compactGroup], bounds, minimumArea);
4500
+ const newlyNarrowKeys = new Set(
4501
+ outerPlacements.filter(({ node, rectangle }) => node.key !== compactGroup.key && (rectangle.width < minimumWidth || rectangle.height < minimumHeight)).map(({ node }) => node.key)
4502
+ );
4503
+ if (newlyNarrowKeys.size === 0) {
4504
+ return outerPlacements.flatMap(({ node, rectangle }) => node.key === compactGroup.key ? placeCompactGrid(compact, rectangle, minimumWidth, minimumHeight) : [{ node, rectangle }]);
4505
+ }
4506
+ compact = [...compact, ...kept.filter((node) => newlyNarrowKeys.has(node.key))];
4507
+ kept = kept.filter((node) => !newlyNarrowKeys.has(node.key));
4508
+ }
4509
+ return placeCompactGrid(compact, bounds, minimumWidth, minimumHeight);
4510
+ }
4511
+ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWidth, minTileHeight, maxVisibleNodes, aggregateOverflow, overflowLabel, activeKey, depth = 0, parentKey = "root") {
4438
4512
  const result = [];
4439
4513
  const minimumWidthWithGap = minTileWidth + gap * 2;
4440
4514
  const minimumHeightWithGap = minTileHeight + gap * 2;
4441
4515
  const minimumArea = Math.max(1, minimumWidthWithGap * minimumHeightWithGap);
4442
- const visibleNodes = limitNodes(
4516
+ const visibleNodes = aggregateOverflow ? limitNodes(
4443
4517
  nodes,
4444
4518
  bounds,
4445
4519
  minimumArea,
@@ -4447,9 +4521,9 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
4447
4521
  `${parentKey}:${depth}`,
4448
4522
  overflowLabel,
4449
4523
  activeKey
4450
- );
4524
+ ) : nodes;
4451
4525
  const initialPlacements = squarify(visibleNodes, bounds, minimumArea);
4452
- const placements = parentKey.endsWith("::sia-readable-overflow") ? initialPlacements : collapseNarrowNodes(
4526
+ const placements = aggregateOverflow ? parentKey.endsWith("::sia-readable-overflow") ? initialPlacements : collapseNarrowNodes(
4453
4527
  visibleNodes,
4454
4528
  initialPlacements,
4455
4529
  bounds,
@@ -4459,6 +4533,14 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
4459
4533
  `${parentKey}:${depth}`,
4460
4534
  overflowLabel,
4461
4535
  activeKey
4536
+ ) : preserveNarrowNodes(
4537
+ visibleNodes,
4538
+ initialPlacements,
4539
+ bounds,
4540
+ minimumArea,
4541
+ minimumWidthWithGap,
4542
+ minimumHeightWithGap,
4543
+ `${parentKey}:${depth}`
4462
4544
  );
4463
4545
  placements.forEach(({ node, rectangle }) => {
4464
4546
  const inset = Math.min(gap, rectangle.width / 4, rectangle.height / 4);
@@ -4487,6 +4569,7 @@ function collectPositions(nodes, bounds, expanded, gap, headerHeight, minTileWid
4487
4569
  minTileWidth,
4488
4570
  minTileHeight,
4489
4571
  maxVisibleNodes,
4572
+ aggregateOverflow,
4490
4573
  overflowLabel,
4491
4574
  activeKey,
4492
4575
  depth + 1,
@@ -4531,9 +4614,11 @@ function Treemap({
4531
4614
  minTileWidth = 28,
4532
4615
  minTileHeight = 18,
4533
4616
  maxVisibleNodes = 180,
4617
+ aggregateOverflow = true,
4534
4618
  emptyContent = "\u6682\u65E0\u6570\u636E",
4535
4619
  valueFormatter = defaultValueFormatter,
4536
4620
  overflowLabel = (count) => `\u5176\u4ED6 ${count} \u9879`,
4621
+ showValueInCompactTiles = false,
4537
4622
  onExpandedChange,
4538
4623
  onFocusedChange,
4539
4624
  onNodeClick,
@@ -4563,10 +4648,11 @@ function Treemap({
4563
4648
  Math.max(1, minTileWidth),
4564
4649
  Math.max(1, minTileHeight),
4565
4650
  Math.max(1, maxVisibleNodes),
4651
+ aggregateOverflow,
4566
4652
  overflowLabel,
4567
4653
  activeKey
4568
4654
  ),
4569
- [activeKey, data, effectiveFocusedNode, expandedSet, gap, headerHeight, maxVisibleNodes, minTileHeight, minTileWidth, overflowLabel, size]
4655
+ [activeKey, aggregateOverflow, data, effectiveFocusedNode, expandedSet, gap, headerHeight, maxVisibleNodes, minTileHeight, minTileWidth, overflowLabel, size]
4570
4656
  );
4571
4657
  useEffect10(() => {
4572
4658
  const element = containerRef.current;
@@ -4624,7 +4710,7 @@ function Treemap({
4624
4710
  "--sia-treemap-value-font-size": `${Math.max(7, fontSize - 1)}px`
4625
4711
  };
4626
4712
  const showLabel = rectangle.width >= minLabelWidth && rectangle.height >= minLabelHeight;
4627
- const showValue = rectangle.width >= Math.max(52, minLabelWidth) && rectangle.height >= Math.max(34, minLabelHeight + 12);
4713
+ const showValue = showLabel && (showValueInCompactTiles || rectangle.width >= Math.max(52, minLabelWidth) && rectangle.height >= Math.max(34, minLabelHeight + 12));
4628
4714
  const hasChildren = Boolean(node.hasChildren || node.children?.length);
4629
4715
  const title = `${node.label} \xB7 ${String(valueFormatter(node.value, node))}`;
4630
4716
  if (expanded) {
@@ -5016,6 +5102,69 @@ import { Fragment as Fragment8, forwardRef as forwardRef5, isValidElement as isV
5016
5102
 
5017
5103
  // src/components/Table/plugins.tsx
5018
5104
  import { isValidElement as isValidElement3, useEffect as useEffect11, useRef as useRef15, useState as useState18 } from "react";
5105
+
5106
+ // src/components/Table/columnSettings.ts
5107
+ function reconcileColumnSettings(columns, defaults, saved) {
5108
+ const stored = new Map(saved.map((item) => [item.key, item]));
5109
+ const base = new Map(defaults.map((item) => [item.key, item]));
5110
+ const result = [];
5111
+ const rank = (column) => {
5112
+ const own = stored.get(column.key)?.order;
5113
+ if (own !== void 0) return own;
5114
+ const children = column.children?.map(rank).filter((value) => value !== void 0);
5115
+ return children?.length ? Math.min(...children) : void 0;
5116
+ };
5117
+ const walk = (source, parent) => {
5118
+ const entries = source.map((column, index) => ({ column, index, rank: rank(column) }));
5119
+ let anchor = -1;
5120
+ for (const entry of entries) {
5121
+ if (entry.rank !== void 0) anchor = entry.rank;
5122
+ else entry.rank = anchor + (entry.index + 1) / (entries.length + 1);
5123
+ }
5124
+ entries.sort((a, b) => a.rank - b.rank || a.index - b.index);
5125
+ for (const { column } of entries) {
5126
+ const own = stored.get(column.key);
5127
+ if (column.children?.length) {
5128
+ walk(column.children, own ?? parent);
5129
+ } else {
5130
+ const fallback = base.get(column.key);
5131
+ if (!fallback) continue;
5132
+ const item = own ?? {
5133
+ ...fallback,
5134
+ fixed: parent?.fixed ?? fallback.fixed,
5135
+ visible: fallback.visible && (parent?.visible ?? true)
5136
+ };
5137
+ result.push({ ...item, order: result.length });
5138
+ }
5139
+ }
5140
+ };
5141
+ walk(columns);
5142
+ return result;
5143
+ }
5144
+ function mergeColumnSettings(columns, saved, active) {
5145
+ const merged = new Map(saved.filter((item) => !["__index__", "__selection__"].includes(item.key)).map((item) => [item.key, item]));
5146
+ active.forEach((item) => merged.set(item.key, item));
5147
+ const current = new Map(active.map((item) => [item.key, item]));
5148
+ const walk = (column) => {
5149
+ if (!column.children?.length) return current.has(column.key) ? [current.get(column.key)] : [];
5150
+ const leaves = column.children.flatMap(walk);
5151
+ if (leaves.length) {
5152
+ const old = merged.get(column.key);
5153
+ merged.set(column.key, {
5154
+ key: column.key,
5155
+ width: old?.width ?? column.width,
5156
+ fixed: old?.fixed,
5157
+ visible: leaves.some((item) => item.visible),
5158
+ order: Math.min(...leaves.map((item) => item.order))
5159
+ });
5160
+ }
5161
+ return leaves;
5162
+ };
5163
+ columns.forEach(walk);
5164
+ return [...merged.values()].sort((left, right) => left.order - right.order);
5165
+ }
5166
+
5167
+ // src/components/Table/plugins.tsx
5019
5168
  import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
5020
5169
  function TableCheckbox({
5021
5170
  checked,
@@ -7106,13 +7255,14 @@ function createColumnSettingPlugin(options = {}) {
7106
7255
  const keys = new Set(baseItems(context).map((item) => item.key));
7107
7256
  return items.filter((item) => keys.has(item.key));
7108
7257
  };
7109
- const currentItems = (context) => context.state.items?.length ? editableItems(context.state.items, context) : baseItems(context);
7258
+ const currentItems = (context) => reconcileColumnSettings(context.baseColumns, baseItems(context), context.state.items ?? []);
7110
7259
  const save = (snapshot) => options.adapter && options.scope ? options.adapter.save(options.scope, snapshot).catch(() => void 0) : Promise.resolve();
7111
7260
  function apply(items, context, persist = true) {
7112
7261
  const normalized = orderColumnSettings(
7113
7262
  editableItems(items, context).sort((a, b) => a.order - b.order)
7114
7263
  );
7115
- context.setState((state) => ({ ...state, items: normalized, open: false }));
7264
+ const snapshot = mergeColumnSettings(context.baseColumns, context.state.items, normalized);
7265
+ context.setState((state) => ({ ...state, items: snapshot, open: false }));
7116
7266
  const columnMap = new Map(
7117
7267
  flattenTableColumns(context.baseColumns).map((column) => [
7118
7268
  column.key,
@@ -7127,7 +7277,7 @@ function createColumnSettingPlugin(options = {}) {
7127
7277
  );
7128
7278
  if (persist)
7129
7279
  void save({
7130
- columns: normalized,
7280
+ columns: snapshot,
7131
7281
  pageSize: context.state.pageSize,
7132
7282
  version: 1
7133
7283
  });
@@ -7192,9 +7342,10 @@ function createColumnSettingPlugin(options = {}) {
7192
7342
  const next = currentItems(context).map(
7193
7343
  (item) => item.key === columnKey ? { ...item, width } : item
7194
7344
  );
7195
- context.setState((state) => ({ ...state, items: next }));
7345
+ const snapshot = mergeColumnSettings(context.baseColumns, context.state.items, next);
7346
+ context.setState((state) => ({ ...state, items: snapshot }));
7196
7347
  void save({
7197
- columns: next,
7348
+ columns: snapshot,
7198
7349
  pageSize: context.state.pageSize,
7199
7350
  version: 1
7200
7351
  });
@@ -7202,7 +7353,7 @@ function createColumnSettingPlugin(options = {}) {
7202
7353
  if (event.type === "pageSizeChange") {
7203
7354
  const pageSize = event.payload;
7204
7355
  context.setState((state) => ({ ...state, pageSize }));
7205
- void save({ columns: currentItems(context), pageSize, version: 1 });
7356
+ void save({ columns: mergeColumnSettings(context.baseColumns, context.state.items, currentItems(context)), pageSize, version: 1 });
7206
7357
  }
7207
7358
  },
7208
7359
  renderToolbarEnd: options.showButton === false ? void 0 : (context) => {
@@ -8048,7 +8199,7 @@ function TableInner(props, ref) {
8048
8199
  function renderHeaderContent(column, content) {
8049
8200
  const title = getHeaderTitleText(column.title ?? column.name) || column.name || column.key;
8050
8201
  return /* @__PURE__ */ jsxs21("span", { className: "sia-table__header-label", children: [
8051
- /* @__PURE__ */ jsx23("span", { className: "sia-table__header-label-content", ...withTitleTooltip({ title }), children: content }),
8202
+ /* @__PURE__ */ jsx23("span", { className: "sia-table__header-label-content", "data-sia-title-overflow-only": true, ...withTitleTooltip({ title }), children: content }),
8052
8203
  column.description != null && /* @__PURE__ */ jsx23(Tooltip, { title: column.description, placement: "top", trigger: ["hover", "focus"], children: /* @__PURE__ */ jsx23("button", { type: "button", className: "sia-table__header-description", "aria-label": `${column.name ?? column.key}\u5217\u8BF4\u660E`, onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx23(Icon, { name: "info", size: 14 }) }) })
8053
8204
  ] });
8054
8205
  }
@@ -173,10 +173,18 @@ function installTitleTooltips(doc) {
173
173
  let root;
174
174
  let observer;
175
175
  let tooltipId = "";
176
+ const canShow = (element) => {
177
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
178
+ return [element, ...element.querySelectorAll("*")].some((node) => {
179
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
180
+ const style = doc.defaultView?.getComputedStyle(node);
181
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
182
+ });
183
+ };
176
184
  const candidate = (target) => {
177
185
  if (!(target instanceof Element)) return null;
178
186
  const element = target.closest("[data-sia-title]");
179
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
187
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
180
188
  };
181
189
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
182
190
  const hide = () => {
@@ -194,7 +202,7 @@ function installTitleTooltips(doc) {
194
202
  };
195
203
  const render = () => {
196
204
  const text = active?.getAttribute("data-sia-title");
197
- if (!active?.isConnected || !text) {
205
+ if (!active?.isConnected || !text || !canShow(active)) {
198
206
  hide();
199
207
  return;
200
208
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MarkdownEditor
3
- } from "./chunk-JZPAIZYK.js";
4
- import "./chunk-H7IAG72O.js";
3
+ } from "./chunk-TUXNOSCT.js";
4
+ import "./chunk-PUZWLIIJ.js";
5
5
  export {
6
6
  MarkdownEditor
7
7
  };
@@ -174,10 +174,18 @@ function installTitleTooltips(doc) {
174
174
  let root;
175
175
  let observer;
176
176
  let tooltipId = "";
177
+ const canShow = (element) => {
178
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
179
+ return [element, ...element.querySelectorAll("*")].some((node) => {
180
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
181
+ const style = doc.defaultView?.getComputedStyle(node);
182
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
183
+ });
184
+ };
177
185
  const candidate = (target) => {
178
186
  if (!(target instanceof Element)) return null;
179
187
  const element = target.closest("[data-sia-title]");
180
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
188
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
181
189
  };
182
190
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
183
191
  const hide = () => {
@@ -195,7 +203,7 @@ function installTitleTooltips(doc) {
195
203
  };
196
204
  const render = () => {
197
205
  const text = active?.getAttribute("data-sia-title");
198
- if (!active?.isConnected || !text) {
206
+ if (!active?.isConnected || !text || !canShow(active)) {
199
207
  hide();
200
208
  return;
201
209
  }
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  RichTextContent,
3
3
  RichTextEditor
4
- } from "./chunk-GODBVCIQ.js";
5
- import "./chunk-XHLIIDD2.js";
6
- import "./chunk-JZWTTSOZ.js";
7
- import "./chunk-H7IAG72O.js";
4
+ } from "./chunk-SBNHEETW.js";
5
+ import "./chunk-CLBWJEID.js";
6
+ import "./chunk-SJFT5OSX.js";
7
+ import "./chunk-PUZWLIIJ.js";
8
8
  export {
9
9
  RichTextContent,
10
10
  RichTextEditor
@@ -163,10 +163,18 @@ function installTitleTooltips(doc) {
163
163
  let root;
164
164
  let observer;
165
165
  let tooltipId = "";
166
+ const canShow = (element) => {
167
+ if (!element.hasAttribute("data-sia-title-overflow-only")) return true;
168
+ return [element, ...element.querySelectorAll("*")].some((node) => {
169
+ if (node.clientWidth <= 0 || node.scrollWidth <= node.clientWidth) return false;
170
+ const style = doc.defaultView?.getComputedStyle(node);
171
+ return style?.textOverflow === "ellipsis" && (style.overflowX === "hidden" || style.overflowX === "clip");
172
+ });
173
+ };
166
174
  const candidate = (target) => {
167
175
  if (!(target instanceof Element)) return null;
168
176
  const element = target.closest("[data-sia-title]");
169
- return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
177
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") && canShow(element) ? element : null;
170
178
  };
171
179
  const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
172
180
  const hide = () => {
@@ -184,7 +192,7 @@ function installTitleTooltips(doc) {
184
192
  };
185
193
  const render = () => {
186
194
  const text = active?.getAttribute("data-sia-title");
187
- if (!active?.isConnected || !text) {
195
+ if (!active?.isConnected || !text || !canShow(active)) {
188
196
  hide();
189
197
  return;
190
198
  }
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  SelectDateRange
3
- } from "./chunk-Y4Y2NVBM.js";
4
- import "./chunk-WXERMWTW.js";
5
- import "./chunk-XHLIIDD2.js";
6
- import "./chunk-JZWTTSOZ.js";
7
- import "./chunk-H7IAG72O.js";
3
+ } from "./chunk-N6DVNCZT.js";
4
+ import "./chunk-V4GGPMKS.js";
5
+ import "./chunk-CLBWJEID.js";
6
+ import "./chunk-SJFT5OSX.js";
7
+ import "./chunk-PUZWLIIJ.js";
8
8
  export {
9
9
  SelectDateRange
10
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sia.soul/sia-react-ui",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "Sia Design components and tokens for React",
5
5
  "publishConfig": {
6
6
  "access": "public",