@canvas-components/list-table 0.3.2 → 0.3.3

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.d.cts CHANGED
@@ -299,7 +299,7 @@ interface ListTableTheme {
299
299
  * - `count`:统计当前列中有值的记录数量
300
300
  * - `unionCount`:统计当前列中非空值的去重数量
301
301
  * - `rowCount`:统计可见记录总行数
302
- * - `mergedRowCount`:统计合并单元格后的逻辑行数,`rowSpan > 0` 视为一条
302
+ * - `mergedRowCount`:统计行合并后的逻辑行数,同一连续合并分组只计一条
303
303
  * - `avg`:对当前列做平均值计算
304
304
  * - `min`:取当前列最小值
305
305
  * - `max`:取当前列最大值
package/dist/index.d.ts CHANGED
@@ -299,7 +299,7 @@ interface ListTableTheme {
299
299
  * - `count`:统计当前列中有值的记录数量
300
300
  * - `unionCount`:统计当前列中非空值的去重数量
301
301
  * - `rowCount`:统计可见记录总行数
302
- * - `mergedRowCount`:统计合并单元格后的逻辑行数,`rowSpan > 0` 视为一条
302
+ * - `mergedRowCount`:统计行合并后的逻辑行数,同一连续合并分组只计一条
303
303
  * - `avg`:对当前列做平均值计算
304
304
  * - `min`:取当前列最小值
305
305
  * - `max`:取当前列最大值
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { stringifyDataIndex, getValueByDataIndex, clamp, copyTextSync, copyText, exportTableToXlsx, formatPreciseNumber, addPreciseNumbers, comparePreciseNumbers, dividePreciseNumber, roundPreciseNumber, parseNumberValue, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, exportTableToTxt, exportTableToCsv, downloadJsonFile, mapRowsToExportRecords, copyJson, copyRows, isDateTimeFormatPattern, normalizeDecimalPlaces, formatDateTimeValue, formatCustomNumberPattern, isSameDataIndex, isPointInRect as isPointInRect$1 } from '@canvas-components/utils';
1
+ import { stringifyDataIndex, getValueByDataIndex, clamp, copyTextSync, copyText, exportTableToXlsx, formatPreciseNumber, addPreciseNumbers, comparePreciseNumbers, isSameDataIndex, dividePreciseNumber, roundPreciseNumber, parseNumberValue, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, exportTableToTxt, exportTableToCsv, downloadJsonFile, mapRowsToExportRecords, copyJson, copyRows, isDateTimeFormatPattern, normalizeDecimalPlaces, formatDateTimeValue, formatCustomNumberPattern, isPointInRect as isPointInRect$1 } from '@canvas-components/utils';
2
2
  import { drawBarcodeToCanvas } from '@canvas-components/barcode';
3
3
  import { drawCanvasChartToCanvas, getCanvasChartRenderer, resolveCanvasChartTooltipText } from '@canvas-components/chart';
4
4
  import { drawQrCodeToCanvas } from '@canvas-components/qrcode';
@@ -333,6 +333,30 @@ function stretchColumnWidths(columns, containerWidth) {
333
333
  stretchableColumns[stretchableColumns.length - 1].width += remain;
334
334
  }
335
335
  }
336
+ function shrinkColumnWidthsToFit(columns, containerWidth) {
337
+ let remaining = Math.max(
338
+ Math.ceil(computeColumnWidths(columns) - containerWidth),
339
+ 0
340
+ );
341
+ let shrinkableColumns = columns.filter(
342
+ (column) => column.width > column.minWidth
343
+ );
344
+ while (remaining > 0 && shrinkableColumns.length > 0) {
345
+ const share = Math.max(Math.floor(remaining / shrinkableColumns.length), 1);
346
+ shrinkableColumns.forEach((column) => {
347
+ if (remaining <= 0) {
348
+ return;
349
+ }
350
+ const shrink = Math.min(column.width - column.minWidth, share, remaining);
351
+ column.width -= shrink;
352
+ remaining -= shrink;
353
+ });
354
+ shrinkableColumns = shrinkableColumns.filter(
355
+ (column) => column.width > column.minWidth
356
+ );
357
+ }
358
+ return remaining === 0;
359
+ }
336
360
 
337
361
  // src/domain/columns/flatten-leaf-columns.ts
338
362
  function flattenLeafColumns(columns) {
@@ -3216,7 +3240,7 @@ function drawBodyButtonContent(params) {
3216
3240
  const top = rect.y + (rect.height - height) / 2;
3217
3241
  const palette = resolveButtonPalette(content);
3218
3242
  const textIndent = resolveContentTextIndent(content);
3219
- const paddingInline = BUTTON_PADDING_INLINE_MAP[size];
3243
+ const paddingInline = resolveButtonPaddingInline(content, size);
3220
3244
  const radius = BUTTON_RADIUS_MAP[size];
3221
3245
  ctx.save();
3222
3246
  ctx.globalAlpha *= resolveContentOpacity(content);
@@ -3324,9 +3348,12 @@ function measureButtonContentWidth(ctx, content, theme) {
3324
3348
  const intrinsicWidth = Math.max(
3325
3349
  ctx.measureText(content.text).width + resolveContentTextIndent(content),
3326
3350
  0
3327
- ) + BUTTON_PADDING_INLINE_MAP[size] * 2;
3351
+ ) + resolveButtonPaddingInline(content, size) * 2;
3328
3352
  return resolveContentLayoutWidth(content, intrinsicWidth);
3329
3353
  }
3354
+ function resolveButtonPaddingInline(content, size) {
3355
+ return content.buttonType === "link" || content.buttonType === "text" ? 4 : BUTTON_PADDING_INLINE_MAP[size];
3356
+ }
3330
3357
  function measureButtonContentHeight(content) {
3331
3358
  return BUTTON_HEIGHT_MAP[resolveButtonSize(content.size)];
3332
3359
  }
@@ -6853,7 +6880,7 @@ function expandBodyContents(contents) {
6853
6880
  expanded.push({
6854
6881
  content,
6855
6882
  contentIndex,
6856
- gapBefore: expanded.length === 0 || textOnly ? 0 : previousContent?.type === "link" && content.type === "link" ? LINK_CONTENT_GAP : CONTENT_GAP
6883
+ gapBefore: expanded.length === 0 || textOnly ? 0 : resolveInlineContentGap(previousContent, content)
6857
6884
  });
6858
6885
  if ((content.type === "checkbox" || content.type === "radio") && content.label) {
6859
6886
  const textLabelContent = {
@@ -6872,6 +6899,12 @@ function expandBodyContents(contents) {
6872
6899
  });
6873
6900
  return expanded;
6874
6901
  }
6902
+ function resolveInlineContentGap(previous, current) {
6903
+ return isInlineActionContent(previous) && isInlineActionContent(current) ? LINK_CONTENT_GAP : CONTENT_GAP;
6904
+ }
6905
+ function isInlineActionContent(content) {
6906
+ return content?.type === "link" || content?.type === "button" && (content.buttonType === "link" || content.buttonType === "text");
6907
+ }
6875
6908
  function buildSingleLineLayout(ctx, contents, availableWidth, theme) {
6876
6909
  const line = {
6877
6910
  items: [],
@@ -10461,7 +10494,7 @@ var ListTableCarouselController = class {
10461
10494
  var SUMMARY_ASYNC_BATCH_SIZE = 2e3;
10462
10495
  var SUMMARY_ASYNC_FRAME_BUDGET = 8;
10463
10496
  async function computeSummaryValuesAsync(params) {
10464
- const { rows, columns, signal } = params;
10497
+ const { rows, columns, mergeCell, signal } = params;
10465
10498
  const summaryValues = /* @__PURE__ */ new Map();
10466
10499
  const accumulators = [];
10467
10500
  columns.forEach((column) => {
@@ -10514,8 +10547,14 @@ async function computeSummaryValuesAsync(params) {
10514
10547
  }
10515
10548
  startIndex = endIndex;
10516
10549
  }
10550
+ const mergedRowCount = accumulators.some(
10551
+ (item) => item.type === "mergedRowCount"
10552
+ ) ? countMergedRows(rows, columns, mergeCell) : null;
10517
10553
  accumulators.forEach((item) => {
10518
- summaryValues.set(item.column.key, finalizeSummaryValue(item, rows));
10554
+ summaryValues.set(
10555
+ item.column.key,
10556
+ finalizeSummaryValue(item, rows, mergedRowCount)
10557
+ );
10519
10558
  });
10520
10559
  return summaryValues;
10521
10560
  }
@@ -10570,7 +10609,7 @@ function updateSummaryAccumulator(item, record) {
10570
10609
  item.max = numericValue;
10571
10610
  }
10572
10611
  }
10573
- function finalizeSummaryValue(item, rows) {
10612
+ function finalizeSummaryValue(item, rows, mergedRowCount) {
10574
10613
  if (item.column.summaryTitle != null) {
10575
10614
  return item.column.summaryTitle;
10576
10615
  }
@@ -10578,7 +10617,11 @@ function finalizeSummaryValue(item, rows) {
10578
10617
  return formatSummaryOutput(item, String(rows.length), rows);
10579
10618
  }
10580
10619
  if (item.type === "mergedRowCount") {
10581
- return formatSummaryOutput(item, String(countMergedRows(rows)), rows);
10620
+ return formatSummaryOutput(
10621
+ item,
10622
+ String(mergedRowCount ?? rows.length),
10623
+ rows
10624
+ );
10582
10625
  }
10583
10626
  if (item.type === "count") {
10584
10627
  return formatSummaryOutput(item, String(item.valueCount), rows);
@@ -10669,7 +10712,76 @@ function formatSummaryOutput(item, value, rows) {
10669
10712
  }
10670
10713
  return value;
10671
10714
  }
10672
- function countMergedRows(rows) {
10715
+ function countMergedRows(rows, columns, mergeCell) {
10716
+ const coveredRowIndexes = /* @__PURE__ */ new Set();
10717
+ const explicitRowSpans = /* @__PURE__ */ new Map();
10718
+ columns.forEach((column) => {
10719
+ if (!column.onCell) {
10720
+ return;
10721
+ }
10722
+ rows.forEach((record, rowIndex) => {
10723
+ const value = getValueByDataIndex(record, column.dataIndex);
10724
+ const span = column.onCell?.(value, record, rowIndex, column);
10725
+ if (!span || !Object.prototype.hasOwnProperty.call(span, "rowSpan")) {
10726
+ return;
10727
+ }
10728
+ const rowSpan = span.rowSpan ?? 1;
10729
+ explicitRowSpans.set(`${rowIndex}::${column.key}`, rowSpan);
10730
+ if (rowSpan === 0) {
10731
+ coveredRowIndexes.add(rowIndex);
10732
+ return;
10733
+ }
10734
+ if (!Number.isFinite(rowSpan) || rowSpan <= 1) {
10735
+ return;
10736
+ }
10737
+ const spanEnd = Math.min(
10738
+ rows.length,
10739
+ rowIndex + Math.floor(rowSpan)
10740
+ );
10741
+ for (let index = rowIndex + 1; index < spanEnd; index += 1) {
10742
+ coveredRowIndexes.add(index);
10743
+ }
10744
+ });
10745
+ });
10746
+ const rowMergeRule = mergeCell?.row;
10747
+ const targetDataIndexes = rowMergeRule?.columns?.length ? rowMergeRule.columns : rowMergeRule?.keys;
10748
+ const targetMergeColumns = columns.filter(
10749
+ (column) => targetDataIndexes?.some(
10750
+ (dataIndex) => isSameDataIndex(column.dataIndex, dataIndex)
10751
+ )
10752
+ );
10753
+ if (rowMergeRule?.keys.length && targetMergeColumns.length) {
10754
+ targetMergeColumns.forEach((column) => {
10755
+ let rowIndex = 0;
10756
+ while (rowIndex < rows.length) {
10757
+ if (explicitRowSpans.has(`${rowIndex}::${column.key}`)) {
10758
+ rowIndex += 1;
10759
+ continue;
10760
+ }
10761
+ const currentGroupKey = getMergeGroupKey(
10762
+ rows[rowIndex],
10763
+ rowMergeRule.keys
10764
+ );
10765
+ let rowSpan = 1;
10766
+ for (let index = rowIndex + 1; index < rows.length; index += 1) {
10767
+ if (explicitRowSpans.has(`${index}::${column.key}`)) {
10768
+ break;
10769
+ }
10770
+ if (getMergeGroupKey(rows[index], rowMergeRule.keys) !== currentGroupKey) {
10771
+ break;
10772
+ }
10773
+ rowSpan += 1;
10774
+ }
10775
+ for (let index = rowIndex + 1; index < rowIndex + rowSpan; index += 1) {
10776
+ coveredRowIndexes.add(index);
10777
+ }
10778
+ rowIndex += rowSpan;
10779
+ }
10780
+ });
10781
+ }
10782
+ if (coveredRowIndexes.size > 0) {
10783
+ return rows.length - coveredRowIndexes.size;
10784
+ }
10673
10785
  return rows.reduce((count, record) => {
10674
10786
  if (record == null || typeof record !== "object") {
10675
10787
  return count + 1;
@@ -10681,6 +10793,20 @@ function countMergedRows(rows) {
10681
10793
  return rowSpan > 0 ? count + 1 : count;
10682
10794
  }, 0);
10683
10795
  }
10796
+ function getMergeGroupKey(record, keys) {
10797
+ return keys.map(
10798
+ (dataIndex) => normalizeMergeValue(getValueByDataIndex(record, dataIndex))
10799
+ ).join("|");
10800
+ }
10801
+ function normalizeMergeValue(value) {
10802
+ if (value == null) {
10803
+ return "";
10804
+ }
10805
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
10806
+ return String(value);
10807
+ }
10808
+ return JSON.stringify(value) ?? "";
10809
+ }
10684
10810
  function yieldToMainThread() {
10685
10811
  return new Promise((resolve) => {
10686
10812
  if (typeof requestAnimationFrame === "function") {
@@ -20189,6 +20315,10 @@ function buildListTableRenderSnapshot(params) {
20189
20315
  groupedColumnKeys
20190
20316
  );
20191
20317
  const leafColumns = flattenLeafColumns(normalizedColumns);
20318
+ const totalColumnWidth = computeColumnWidths(leafColumns);
20319
+ if (width > 0 && totalColumnWidth > width && totalColumnWidth <= hostWidth) {
20320
+ shrinkColumnWidthsToFit(leafColumns, width);
20321
+ }
20192
20322
  if (options.stretchColumns !== false && width > 0 && !draggingResize) {
20193
20323
  stretchColumnWidths(leafColumns, width);
20194
20324
  }
@@ -20732,8 +20862,8 @@ function updateListTableRenderSnapshotScroll(params) {
20732
20862
  } : snapshot.frozenRows;
20733
20863
  return {
20734
20864
  ...snapshot,
20735
- width,
20736
- height,
20865
+ width: snapshot.width,
20866
+ height: snapshot.height,
20737
20867
  columnSegments,
20738
20868
  headerRows,
20739
20869
  scrollbarLayout,
@@ -21118,13 +21248,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
21118
21248
  verticalFrame.bodyBackgroundColor
21119
21249
  );
21120
21250
  }
21121
- if (layout.vertical && verticalFrame) {
21122
- drawVerticalScrollbarFrame(
21123
- ctx,
21124
- layout.vertical,
21125
- verticalFrame,
21126
- borderColor
21127
- );
21251
+ if (verticalFrame) {
21252
+ drawVerticalScrollbarFrame(ctx, verticalFrame, borderColor);
21128
21253
  }
21129
21254
  if (layout.horizontal) {
21130
21255
  drawAxisScrollbar(
@@ -21242,28 +21367,28 @@ function drawTrackBorder(ctx, rect, axis, borderColor) {
21242
21367
  ctx.stroke();
21243
21368
  ctx.restore();
21244
21369
  }
21245
- function drawVerticalScrollbarFrame(ctx, layout, frame, borderColor) {
21246
- const left = Math.round(layout.decreaseButton.x) + 0.5;
21247
- const right = Math.round(layout.decreaseButton.x + layout.decreaseButton.width) - 0.5;
21370
+ function drawVerticalScrollbarFrame(ctx, frame, borderColor) {
21371
+ const left = Math.round(frame.x) + 0.5;
21372
+ const right = Math.round(frame.x + frame.width) - 0.5;
21248
21373
  const top = Math.round(frame.top) + 0.5;
21249
21374
  const bottom = Math.round(frame.top + frame.height) - 0.5;
21250
21375
  const headerBottom = Math.round(frame.headerBottom) + 0.5;
21251
- if (right <= left || bottom <= top) {
21376
+ if (frame.width <= 0 || right <= left || bottom <= top) {
21252
21377
  return;
21253
21378
  }
21254
21379
  ctx.save();
21255
21380
  ctx.fillStyle = frame.headerBackgroundColor;
21256
21381
  ctx.fillRect(
21257
- layout.decreaseButton.x,
21382
+ frame.x,
21258
21383
  frame.top,
21259
- layout.decreaseButton.width,
21384
+ frame.width,
21260
21385
  Math.max(frame.headerBottom - frame.top, 0)
21261
21386
  );
21262
21387
  ctx.fillStyle = frame.bodyBackgroundColor;
21263
21388
  ctx.fillRect(
21264
- layout.decreaseButton.x,
21389
+ frame.x,
21265
21390
  frame.headerBottom,
21266
- layout.decreaseButton.width,
21391
+ frame.width,
21267
21392
  Math.max(frame.top + frame.height - frame.headerBottom, 0)
21268
21393
  );
21269
21394
  ctx.strokeStyle = borderColor;
@@ -22109,7 +22234,7 @@ function buildAutoRowSpanMap(rows, columns, rule, spanConfigMap, sourceRowIndexM
22109
22234
  rowIndex += 1;
22110
22235
  continue;
22111
22236
  }
22112
- const currentGroupKey = getMergeGroupKey(rows[rowIndex], groupKeys);
22237
+ const currentGroupKey = getMergeGroupKey2(rows[rowIndex], groupKeys);
22113
22238
  let rowSpan = 1;
22114
22239
  for (let index = rowIndex + 1; index < rows.length; index += 1) {
22115
22240
  if (sourceRowIndexMap[index] !== (sourceRowIndexMap[index - 1] ?? index - 1) + 1) {
@@ -22119,7 +22244,7 @@ function buildAutoRowSpanMap(rows, columns, rule, spanConfigMap, sourceRowIndexM
22119
22244
  if (hasExplicitSpan(spanConfigMap.get(nextCellKey), "rowSpan")) {
22120
22245
  break;
22121
22246
  }
22122
- if (getMergeGroupKey(rows[index], groupKeys) !== currentGroupKey) {
22247
+ if (getMergeGroupKey2(rows[index], groupKeys) !== currentGroupKey) {
22123
22248
  break;
22124
22249
  }
22125
22250
  rowSpan += 1;
@@ -22222,13 +22347,13 @@ function clampSpan(span, max) {
22222
22347
  }
22223
22348
  return Math.max(1, Math.min(Math.floor(span), max));
22224
22349
  }
22225
- function getMergeGroupKey(record, keys) {
22350
+ function getMergeGroupKey2(record, keys) {
22226
22351
  if (!keys?.length) {
22227
22352
  return "";
22228
22353
  }
22229
- return keys.map((key) => normalizeMergeValue(getValueByDataIndex(record, key))).join("|");
22354
+ return keys.map((key) => normalizeMergeValue2(getValueByDataIndex(record, key))).join("|");
22230
22355
  }
22231
- function normalizeMergeValue(value) {
22356
+ function normalizeMergeValue2(value) {
22232
22357
  if (value == null) {
22233
22358
  return "";
22234
22359
  }
@@ -22238,7 +22363,7 @@ function normalizeMergeValue(value) {
22238
22363
  return JSON.stringify(value) ?? "";
22239
22364
  }
22240
22365
  function isSameMergeValue(left, right) {
22241
- return normalizeMergeValue(left) === normalizeMergeValue(right);
22366
+ return normalizeMergeValue2(left) === normalizeMergeValue2(right);
22242
22367
  }
22243
22368
  function getMergedCellWidth(columns, startIndex, colSpan) {
22244
22369
  let width = 0;
@@ -24501,6 +24626,11 @@ function executeListTableRenderPipeline(params) {
24501
24626
  scrollbarOpacity,
24502
24627
  isScrollbarInteractive,
24503
24628
  {
24629
+ x: width,
24630
+ width: options.scrollbar?.visible === false ? 0 : Math.max(
24631
+ options.scrollbar?.thickness ?? DEFAULT_SCROLLBAR_THICKNESS,
24632
+ 6
24633
+ ),
24504
24634
  top: 0,
24505
24635
  height,
24506
24636
  headerBottom: headerHeight,
@@ -26002,6 +26132,7 @@ var ListTableSummaryController = class {
26002
26132
  const summaryTask = computeSummaryValuesAsync({
26003
26133
  rows: snapshot.displayRows,
26004
26134
  columns: snapshot.leafColumns,
26135
+ mergeCell: this.options.getOptions().query?.mergeCell,
26005
26136
  signal
26006
26137
  }).then((summaryValues) => {
26007
26138
  if (signal.aborted || taskId !== this.summaryTaskId) {