@canvas-components/pivot-table 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -330,8 +330,6 @@ function indexPivotNodes(root) {
330
330
  visit(root);
331
331
  return nodeMap;
332
332
  }
333
-
334
- // src/domain/aggregation/built-in-aggregators.ts
335
333
  function createBuiltInPivotAggregators() {
336
334
  return {
337
335
  sum: createNumericAggregator("sum"),
@@ -362,14 +360,14 @@ function createNumericAggregator(type) {
362
360
  return;
363
361
  }
364
362
  state.numericCount += 1;
365
- state.sum += numericValue;
363
+ state.sum = utils.addPreciseNumbers(state.sum, numericValue) ?? state.sum;
366
364
  state.min = state.min == null ? numericValue : Math.min(state.min, numericValue);
367
365
  state.max = state.max == null ? numericValue : Math.max(state.max, numericValue);
368
366
  },
369
367
  merge(state, source) {
370
368
  state.count += source.count;
371
369
  state.numericCount += source.numericCount;
372
- state.sum += source.sum;
370
+ state.sum = utils.addPreciseNumbers(state.sum, source.sum) ?? state.sum;
373
371
  state.min = mergeMinimum(state.min, source.min);
374
372
  state.max = mergeMaximum(state.max, source.max);
375
373
  source.distinctValues.forEach((value) => state.distinctValues.add(value));
@@ -377,9 +375,11 @@ function createNumericAggregator(type) {
377
375
  finalize(state) {
378
376
  if (type === "count") return state.count;
379
377
  if (type === "distinctCount") return state.distinctValues.size;
380
- if (type === "sum") return state.sum;
378
+ if (type === "sum") return Number(state.sum);
381
379
  if (type === "avg") {
382
- return state.numericCount === 0 ? null : state.sum / state.numericCount;
380
+ if (state.numericCount === 0) return null;
381
+ const average = utils.dividePreciseNumber(state.sum, state.numericCount);
382
+ return average == null ? null : Number(average);
383
383
  }
384
384
  if (type === "min") return state.min;
385
385
  return state.max;
@@ -390,7 +390,7 @@ function createAccumulator() {
390
390
  return {
391
391
  count: 0,
392
392
  numericCount: 0,
393
- sum: 0,
393
+ sum: "0",
394
394
  min: null,
395
395
  max: null,
396
396
  distinctValues: /* @__PURE__ */ new Set()
@@ -2769,18 +2769,50 @@ function resolvePivotSelectionKind(selection) {
2769
2769
  }
2770
2770
  return selection?.activeNode?.axis ?? "cell";
2771
2771
  }
2772
- function drawPivotSelectionBorder(context, rect, color) {
2772
+ function drawPivotSelectionBorder(context, rect, color, options) {
2773
2773
  context.save();
2774
2774
  context.strokeStyle = color;
2775
2775
  context.lineWidth = 1;
2776
- context.strokeRect(
2777
- rect.x + 0.5,
2778
- rect.y + 0.5,
2779
- Math.max(rect.width - 1, 0),
2780
- Math.max(rect.height - 1, 0)
2781
- );
2776
+ const left = rect.x + 0.5;
2777
+ const top = rect.y + 0.5;
2778
+ const width = Math.max(rect.width - 1, 0);
2779
+ const height = Math.max(rect.height - 1, 0);
2780
+ const right = left + width;
2781
+ const bottom = top + height;
2782
+ const viewport = options?.viewportRect;
2783
+ const radius = Math.min(options?.radius ?? 8, width / 2, height / 2);
2784
+ const topLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
2785
+ const topRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y, viewport.y) ? radius : 0;
2786
+ const bottomRight = viewport && isSameCoordinate(rect.x + rect.width, viewport.x + viewport.width) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
2787
+ const bottomLeft = viewport && isSameCoordinate(rect.x, viewport.x) && isSameCoordinate(rect.y + rect.height, viewport.y + viewport.height) ? radius : 0;
2788
+ if (!topLeft && !topRight && !bottomRight && !bottomLeft) {
2789
+ context.strokeRect(left, top, width, height);
2790
+ context.restore();
2791
+ return;
2792
+ }
2793
+ context.beginPath();
2794
+ context.moveTo(left + topLeft, top);
2795
+ context.lineTo(right - topRight, top);
2796
+ if (topRight) context.arcTo(right, top, right, top + topRight, topRight);
2797
+ else context.lineTo(right, top);
2798
+ context.lineTo(right, bottom - bottomRight);
2799
+ if (bottomRight) {
2800
+ context.arcTo(right, bottom, right - bottomRight, bottom, bottomRight);
2801
+ } else context.lineTo(right, bottom);
2802
+ context.lineTo(left + bottomLeft, bottom);
2803
+ if (bottomLeft) {
2804
+ context.arcTo(left, bottom, left, bottom - bottomLeft, bottomLeft);
2805
+ } else context.lineTo(left, bottom);
2806
+ context.lineTo(left, top + topLeft);
2807
+ if (topLeft) context.arcTo(left, top, left + topLeft, top, topLeft);
2808
+ else context.lineTo(left, top);
2809
+ context.closePath();
2810
+ context.stroke();
2782
2811
  context.restore();
2783
2812
  }
2813
+ function isSameCoordinate(first, second) {
2814
+ return Math.abs(first - second) < 0.01;
2815
+ }
2784
2816
  function resolveSelectionIndexes(layout, selection) {
2785
2817
  const range = selection?.ranges[0];
2786
2818
  if (range) {
@@ -3077,6 +3109,17 @@ function drawPivotGridRect(context, rect, color, options) {
3077
3109
  context.stroke();
3078
3110
  context.restore();
3079
3111
  }
3112
+ function drawPivotViewportRightBorder(context, rect, color) {
3113
+ const x = rect.x + rect.width - 0.5;
3114
+ context.save();
3115
+ context.strokeStyle = color;
3116
+ context.lineWidth = 1;
3117
+ context.beginPath();
3118
+ context.moveTo(x, rect.y);
3119
+ context.lineTo(x, rect.y + rect.height);
3120
+ context.stroke();
3121
+ context.restore();
3122
+ }
3080
3123
  function clipPivotViewport(context, rect, radius = 8) {
3081
3124
  appendRoundedRectPath(context, rect, radius);
3082
3125
  context.clip();
@@ -3162,6 +3205,12 @@ function renderPivotTable(params) {
3162
3205
  );
3163
3206
  const areaSelection = isPivotAreaSelection(layout, params.selection);
3164
3207
  const viewportWidth = resolvePivotViewportWidth(layout);
3208
+ const tableViewportRect = {
3209
+ x: 0,
3210
+ y: 0,
3211
+ width: layout.tableWidth,
3212
+ height: layout.height
3213
+ };
3165
3214
  context.clearRect(0, 0, layout.width, layout.height);
3166
3215
  context.save();
3167
3216
  clipPivotViewport(context, {
@@ -3202,7 +3251,7 @@ function renderPivotTable(params) {
3202
3251
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3203
3252
  skipTop: true,
3204
3253
  skipLeft: cell.rect.x > 0,
3205
- skipRight: isSameCoordinate(
3254
+ skipRight: isSameCoordinate2(
3206
3255
  cell.rect.x + cell.rect.width,
3207
3256
  layout.rowHeaderRect.x + layout.rowHeaderRect.width
3208
3257
  )
@@ -3225,11 +3274,11 @@ function renderPivotTable(params) {
3225
3274
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3226
3275
  skipTop: cell.rect.y > 0,
3227
3276
  skipLeft: true,
3228
- skipRight: isSameCoordinate(
3277
+ skipRight: isSameCoordinate2(
3229
3278
  cell.rect.x + cell.rect.width,
3230
3279
  layout.tableWidth
3231
3280
  ),
3232
- skipBottom: isSameCoordinate(
3281
+ skipBottom: isSameCoordinate2(
3233
3282
  cell.rect.y + cell.rect.height,
3234
3283
  layout.columnHeaderRect.y + layout.columnHeaderRect.height
3235
3284
  )
@@ -3261,7 +3310,7 @@ function renderPivotTable(params) {
3261
3310
  (cell) => drawPivotGridRect(context, cell.rect, theme.borderColor, {
3262
3311
  skipTop: true,
3263
3312
  skipLeft: true,
3264
- skipRight: isSameCoordinate(
3313
+ skipRight: isSameCoordinate2(
3265
3314
  cell.rect.x + cell.rect.width,
3266
3315
  layout.tableWidth
3267
3316
  )
@@ -3290,29 +3339,37 @@ function renderPivotTable(params) {
3290
3339
  headerBackgroundColor: theme.columnHeaderBackgroundColor,
3291
3340
  bodyBackgroundColor: theme.backgroundColor
3292
3341
  });
3342
+ if (params.resizeGuideX != null) {
3343
+ drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
3344
+ }
3345
+ context.restore();
3346
+ context.save();
3347
+ clipPivotViewport(context, tableViewportRect);
3348
+ drawPivotViewportRightBorder(context, tableViewportRect, theme.borderColor);
3349
+ context.restore();
3350
+ drawPivotViewportBorder(
3351
+ context,
3352
+ { x: 0, y: 0, width: viewportWidth, height: contentHeight },
3353
+ theme.borderColor
3354
+ );
3293
3355
  const selectionRect = resolvePivotSelectionRect(layout, params.selection);
3294
3356
  if (selectionRect) {
3295
3357
  const selectionClipRect = resolvePivotSelectionClipRect(
3296
3358
  layout,
3297
3359
  params.selection
3298
3360
  );
3361
+ context.save();
3362
+ clipPivotViewport(context, tableViewportRect);
3299
3363
  withClip(context, selectionClipRect, () => {
3300
3364
  drawPivotSelectionBorder(
3301
3365
  context,
3302
3366
  selectionRect,
3303
- theme.selectionBorderColor
3367
+ theme.selectionBorderColor,
3368
+ { viewportRect: tableViewportRect }
3304
3369
  );
3305
3370
  });
3371
+ context.restore();
3306
3372
  }
3307
- if (params.resizeGuideX != null) {
3308
- drawPivotResizeGuide(context, params.resizeGuideX, contentHeight);
3309
- }
3310
- context.restore();
3311
- drawPivotViewportBorder(
3312
- context,
3313
- { x: 0, y: 0, width: viewportWidth, height: contentHeight },
3314
- theme.borderColor
3315
- );
3316
3373
  return {
3317
3374
  headerCellCount: layout.reportFilterCells.length + layout.rowFieldHeaderCells.length + layout.rowHeaderCells.length + layout.columnHeaderCells.length,
3318
3375
  bodyCellCount: layout.bodyCells.length
@@ -3357,7 +3414,7 @@ function drawDebugOverlay(context, layout, theme) {
3357
3414
  layout.bodyRect
3358
3415
  ].forEach((rect) => strokeRect(context, rect, "#f04438"));
3359
3416
  context.setLineDash([]);
3360
- context.font = `12px ${theme.fontFamily}`;
3417
+ context.font = `${theme.fontSize}px ${theme.fontFamily}`;
3361
3418
  context.textAlign = "left";
3362
3419
  context.textBaseline = "top";
3363
3420
  context.fillStyle = "#f04438";
@@ -3616,7 +3673,7 @@ function resolveSlotPathKey(slots, nodeId, indicatorKey) {
3616
3673
  (slot) => (slot.nodeId ?? "") === nodeId && (!slot.indicatorKey || slot.indicatorKey === indicatorKey)
3617
3674
  )?.pathKey ?? null;
3618
3675
  }
3619
- function isSameCoordinate(first, second) {
3676
+ function isSameCoordinate2(first, second) {
3620
3677
  return Math.abs(first - second) < 0.01;
3621
3678
  }
3622
3679
  function resolveBodyDisplayText(cell, indicator) {
@@ -5223,7 +5280,7 @@ var PivotTable = class {
5223
5280
  measurePivotText(text) {
5224
5281
  const context = this.host?.canvas.getContext("2d");
5225
5282
  if (!context) return text.length * 8;
5226
- context.font = `${this.options.theme?.fontSize ?? 13}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
5283
+ context.font = `${this.options.theme?.fontSize ?? 14}px ${this.options.theme?.fontFamily ?? "sans-serif"}`;
5227
5284
  return context.measureText(text).width;
5228
5285
  }
5229
5286
  updateRowHeaderSelectionDrag(event) {
@@ -5609,6 +5666,90 @@ function toArrayBuffer(value) {
5609
5666
  return buffer;
5610
5667
  }
5611
5668
 
5669
+ // src/adapters/storage/indexeddb-field-layout-storage.ts
5670
+ var DATABASE_NAME = "canvas_components_pivot_table_config";
5671
+ var STORE_NAME = "field_layout";
5672
+ var DB_VERSION = 1;
5673
+ var dbPromise = null;
5674
+ async function savePivotFieldLayout(key, layout) {
5675
+ try {
5676
+ const db = await openDatabase();
5677
+ await requestToPromise(
5678
+ db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneLayout(layout), key)
5679
+ );
5680
+ } catch (error) {
5681
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u4FDD\u5B58\u5931\u8D25\uFF1A", error);
5682
+ }
5683
+ }
5684
+ async function loadPivotFieldLayout(key) {
5685
+ try {
5686
+ const db = await openDatabase();
5687
+ const value = await requestToPromise(
5688
+ db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key)
5689
+ );
5690
+ return isPivotFieldLayout(value) ? cloneLayout(value) : void 0;
5691
+ } catch (error) {
5692
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u8BFB\u53D6\u5931\u8D25\uFF1A", error);
5693
+ return void 0;
5694
+ }
5695
+ }
5696
+ async function clearPivotFieldLayout(key) {
5697
+ try {
5698
+ const db = await openDatabase();
5699
+ await requestToPromise(
5700
+ db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key)
5701
+ );
5702
+ } catch (error) {
5703
+ console.error("[PivotTable] \u5B57\u6BB5\u5E03\u5C40\u6E05\u9664\u5931\u8D25\uFF1A", error);
5704
+ }
5705
+ }
5706
+ function openDatabase() {
5707
+ if (dbPromise) return dbPromise;
5708
+ dbPromise = new Promise((resolve, reject) => {
5709
+ if (typeof indexedDB === "undefined") {
5710
+ reject(new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB"));
5711
+ return;
5712
+ }
5713
+ const request = indexedDB.open(DATABASE_NAME, DB_VERSION);
5714
+ request.onupgradeneeded = () => {
5715
+ const db = request.result;
5716
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
5717
+ db.createObjectStore(STORE_NAME);
5718
+ }
5719
+ };
5720
+ request.onsuccess = () => resolve(request.result);
5721
+ request.onerror = () => reject(request.error);
5722
+ });
5723
+ dbPromise.catch(() => {
5724
+ dbPromise = null;
5725
+ });
5726
+ return dbPromise;
5727
+ }
5728
+ function requestToPromise(request) {
5729
+ return new Promise((resolve, reject) => {
5730
+ request.onsuccess = () => resolve(request.result);
5731
+ request.onerror = () => reject(request.error);
5732
+ });
5733
+ }
5734
+ function isPivotFieldLayout(value) {
5735
+ if (!value || typeof value !== "object") return false;
5736
+ const layout = value;
5737
+ return ["filters", "columns", "rows", "values"].every(
5738
+ (key) => isStringArray(layout[key])
5739
+ );
5740
+ }
5741
+ function isStringArray(value) {
5742
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
5743
+ }
5744
+ function cloneLayout(layout) {
5745
+ return {
5746
+ filters: [...layout.filters],
5747
+ columns: [...layout.columns],
5748
+ rows: [...layout.rows],
5749
+ values: [...layout.values]
5750
+ };
5751
+ }
5752
+
5612
5753
  // src/domain/field/pivot-field-layout.ts
5613
5754
  function createEmptyPivotFieldLayout() {
5614
5755
  return { filters: [], columns: [], rows: [], values: [] };
@@ -5772,6 +5913,7 @@ exports.PivotTable = PivotTable;
5772
5913
  exports.aggregatePivotRecords = aggregatePivotRecords;
5773
5914
  exports.buildPivotDimensionTree = buildPivotDimensionTree;
5774
5915
  exports.buildPivotLayoutSnapshot = buildPivotLayoutSnapshot;
5916
+ exports.clearPivotFieldLayout = clearPivotFieldLayout;
5775
5917
  exports.computePivotScrollbarLayout = computePivotScrollbarLayout;
5776
5918
  exports.createBuiltInPivotAggregators = createBuiltInPivotAggregators;
5777
5919
  exports.createEmptyPivotFieldLayout = createEmptyPivotFieldLayout;
@@ -5795,6 +5937,7 @@ exports.getPivotDimensionFilterOptions = getPivotDimensionFilterOptions;
5795
5937
  exports.hitTestPivotLayout = hitTestPivotLayout;
5796
5938
  exports.hitTestPivotResize = hitTestPivotResize;
5797
5939
  exports.hitTestPivotScrollbar = hitTestPivotScrollbar;
5940
+ exports.loadPivotFieldLayout = loadPivotFieldLayout;
5798
5941
  exports.movePivotField = movePivotField;
5799
5942
  exports.removePivotField = removePivotField;
5800
5943
  exports.renderPivotTable = renderPivotTable;
@@ -5806,5 +5949,6 @@ exports.resolvePivotDimensionWidth = resolvePivotDimensionWidth;
5806
5949
  exports.resolvePivotFieldLayoutOptions = resolvePivotFieldLayoutOptions;
5807
5950
  exports.resolvePivotIndicatorAggregator = resolvePivotIndicatorAggregator;
5808
5951
  exports.resolvePivotValueFieldTitle = resolvePivotValueFieldTitle;
5952
+ exports.savePivotFieldLayout = savePivotFieldLayout;
5809
5953
  //# sourceMappingURL=index.cjs.map
5810
5954
  //# sourceMappingURL=index.cjs.map