From 20e608f2447e9ffdb135d98e4cc7f39f1cfb308d Mon Sep 17 00:00:00 2001 From: NoobNot <56724970+NoobNotN@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:02:45 +0800 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20=E5=B0=8F=E8=AE=A1/=E6=80=BB?= =?UTF-8?q?=E8=AE=A1=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E6=8C=89?= =?UTF-8?q?=E7=BB=B4=E5=BA=A6=E5=88=86=E7=BB=84=E6=B1=87=E6=80=BB=20(#2346?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(Api): 添加 totalDimensionGroup/subTotalDimensionGroup api,以及一些临时的开发函数 * feat(Hierarchy): 总计小计结点下添加Hierarchy * feat(Render): getMultipleMap 实现,计算总计小计下的布局信息 * feat(Render): 按维度分组的小计总计下表头位置的调整和渲染 * feat(DataSet): 存在维度分组时的汇总值获取 * feat(DataSet): 存在维度分组时的汇总值获取 * feat(DataSet): 存在维度分组时的汇总值获取 * feat: 补充注释 * feat: 单测快照更新,添加isTotalRoot属性 * fix: 有多个 Value 时不允许隐藏度量列 * fix: 有多个 Value 时不允许隐藏度量列 * fix: 删除了一个莫名其妙的函数 * test: 按维度分组汇总能力单测 * docs: 按维度分组汇总能力文档 * test: 更新,多度量指标不允许隐藏指标头 * docs: 图片示例 * test: 更新 snap 数据文件 * chore: 版本号更新 * chore: 版本号更新 * chore: 版本号更新 * chore: 版本号更新 * chore: 版本号更新 * chore: 版本号更新 * chore: 版本号更新 * Merge remote-tracking branch 'origin/Juze_TotalsDimGroup' into Juze_TotalsDimGroup * test: 更新快照 * chore: 删除开发测试文件 * fix: 汇总指标节点也是汇总节点 * chore: 删除无用文件 * fix: isTotalRoot 替换 isTotals * fix: isTotalRoot 替换 isTotals * fix: isTotalRoot 替换 isTotals * test: 更新 React 包快照 * fix: 修复树状模式下总计节点的指标节点没有被格式化数据 * refactor: 修改代码风格和编码规范 * test: 修改代码风格和编码规范 * test: 修改代码风格和编码规范 * test: 修改代码风格和编码规范 * chore: 更改 interface 命名规范 --------- Co-authored-by: JuZe Co-authored-by: Wenjun Xu <906626481@qq.com> Co-authored-by: Jinke Li --- .../s2-core/__tests__/bugs/issue-1715-spec.ts | 7 +- .../__snapshots__/corner-spec.ts.snap | 24 ++ .../__tests__/spreadsheet/corner-spec.ts | 8 + .../spreadsheet/sort-by-order-spec.ts | 11 + .../__tests__/unit/cell/header-cell-spec.ts | 5 +- .../pivot-data-set-total-spec.ts.snap | 121 +++++++ .../data-set/pivot-data-set-total-spec.ts | 240 ++++++++++++++ .../unit/utils/export/export-spec.ts | 71 ++++ .../__tests__/unit/utils/facet-spec.ts | 19 -- packages/s2-core/src/cell/header-cell.ts | 7 +- .../s2-core/src/common/interface/basic.ts | 4 + .../s2-core/src/data-set/base-data-set.ts | 22 ++ packages/s2-core/src/data-set/interface.ts | 18 + .../s2-core/src/data-set/pivot-data-set.ts | 308 ++++++++++++++---- .../s2-core/src/data-set/table-data-set.ts | 4 + .../README-adjustTotalNodesCoordinate.md | 57 ++++ .../src/facet/layout/build-gird-hierarchy.ts | 163 +++++---- .../facet/layout/build-row-tree-hierarchy.ts | 8 +- .../s2-core/src/facet/layout/interface.ts | 15 +- packages/s2-core/src/facet/layout/node.ts | 5 + .../s2-core/src/facet/layout/total-class.ts | 21 +- packages/s2-core/src/facet/pivot-facet.ts | 193 +++++------ .../s2-core/src/sheet-type/spread-sheet.ts | 10 +- .../src/utils/dataset/pivot-data-set.ts | 16 +- packages/s2-core/src/utils/export/copy.ts | 40 ++- packages/s2-core/src/utils/facet.ts | 12 - .../s2-core/src/utils/layout/add-totals.ts | 14 +- .../src/utils/layout/generate-header-nodes.ts | 41 ++- .../layout/get-dims-condition-by-node.ts | 2 +- .../src/utils/layout/whether-leaf-by-level.ts | 17 + .../build-table-hierarchy-spec.tsx.snap | 21 ++ s2-site/docs/api/basic-class/node.en.md | 3 +- s2-site/docs/api/basic-class/node.zh.md | 63 ++-- s2-site/docs/common/totals.en.md | 2 + s2-site/docs/common/totals.zh.md | 2 + s2-site/docs/manual/basic/totals.en.md | 30 +- s2-site/docs/manual/basic/totals.zh.md | 42 ++- .../totals/demo/dimension-group-col.ts | 68 ++++ .../totals/demo/dimension-group-row.ts | 67 ++++ .../examples/analysis/totals/demo/meta.json | 16 + 40 files changed, 1410 insertions(+), 387 deletions(-) create mode 100644 packages/s2-core/__tests__/unit/data-set/__snapshots__/pivot-data-set-total-spec.ts.snap create mode 100644 packages/s2-core/src/facet/README-adjustTotalNodesCoordinate.md create mode 100644 packages/s2-core/src/utils/layout/whether-leaf-by-level.ts create mode 100644 s2-site/examples/analysis/totals/demo/dimension-group-col.ts create mode 100644 s2-site/examples/analysis/totals/demo/dimension-group-row.ts diff --git a/packages/s2-core/__tests__/bugs/issue-1715-spec.ts b/packages/s2-core/__tests__/bugs/issue-1715-spec.ts index 28436e1d61..7018af631d 100644 --- a/packages/s2-core/__tests__/bugs/issue-1715-spec.ts +++ b/packages/s2-core/__tests__/bugs/issue-1715-spec.ts @@ -81,9 +81,10 @@ describe('Multi Values GrandTotal Height Test', () => { const grandTotalsNode = s2 .getColumnNodes() - .find((node) => node.isGrandTotals); + .find((node) => node.isGrandTotals && node.isTotalRoot); - expect(s2.facet.layoutResult.colsHierarchy.height).toBe(60); - expect(grandTotalsNode.height).toEqual(30); + // 有多个 Value 时不允许隐藏度量列 + expect(s2.facet.layoutResult.colsHierarchy.height).toBe(90); + expect(grandTotalsNode.height).toEqual(60); }); }); diff --git a/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap b/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap index aa859d5c67..b3dc716fbd 100644 --- a/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap +++ b/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap @@ -18,6 +18,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -47,6 +48,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -81,6 +83,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -110,6 +113,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -139,6 +143,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -173,6 +178,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city/数值", @@ -207,6 +213,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -236,6 +243,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city/数值", @@ -270,6 +278,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -299,6 +308,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -333,6 +343,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -362,6 +373,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -391,6 +403,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -425,6 +438,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city", @@ -459,6 +473,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -488,6 +503,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city", @@ -522,6 +538,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -551,6 +568,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -585,6 +603,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -614,6 +633,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "province", "label": "province", @@ -643,6 +663,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "city", "label": "city", @@ -677,6 +698,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city", @@ -711,6 +733,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "series-number-node", "label": "序号", @@ -740,6 +763,7 @@ Array [ "isPivotMode": true, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "province/city", diff --git a/packages/s2-core/__tests__/spreadsheet/corner-spec.ts b/packages/s2-core/__tests__/spreadsheet/corner-spec.ts index bdc06461fd..b2d7396bb8 100644 --- a/packages/s2-core/__tests__/spreadsheet/corner-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/corner-spec.ts @@ -49,6 +49,7 @@ describe('PivotSheet Corner Tests', () => { fields: { ...simpleDataConfig.fields, columns: [], + values: ['price'], }, }); s2.setOptions({ @@ -88,6 +89,13 @@ describe('PivotSheet Corner Tests', () => { }, }, }); + s2.setDataCfg({ + ...simpleDataConfig, + fields: { + ...simpleDataConfig.fields, + values: ['price'], + }, + }); s2.render(); const cornerNodes = s2.facet.getCornerNodes(); diff --git a/packages/s2-core/__tests__/spreadsheet/sort-by-order-spec.ts b/packages/s2-core/__tests__/spreadsheet/sort-by-order-spec.ts index 468d201e4b..b45c75bc02 100644 --- a/packages/s2-core/__tests__/spreadsheet/sort-by-order-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/sort-by-order-spec.ts @@ -104,4 +104,15 @@ describe('Manual Sort Tests', () => { }), ).toEqual(['整体访问', '小程序访问', '支付宝访问']); }); + + test('getTotalDimensionValues should include correct values', () => { + const sortedType1 = s2.dataSet.getTotalDimensionValues('type1', {}); + expect(sortedType1).toEqual(['整体访问', '小程序访问', '支付宝访问']); + + expect(s2.dataSet.getTotalDimensionValues('type2', {})).toEqual([ + '整体访问', + '小程序访问', + '支付宝访问', + ]); + }); }); diff --git a/packages/s2-core/__tests__/unit/cell/header-cell-spec.ts b/packages/s2-core/__tests__/unit/cell/header-cell-spec.ts index 14b9f566ee..870c3947db 100644 --- a/packages/s2-core/__tests__/unit/cell/header-cell-spec.ts +++ b/packages/s2-core/__tests__/unit/cell/header-cell-spec.ts @@ -60,7 +60,7 @@ describe('header cell formatter test', () => { value: '总计', parent: root, label: '总计', - isTotals: true, + isTotalRoot: true, }); const rowNode = new Node({ id: `root[&]杭州[&]小计`, @@ -68,7 +68,7 @@ describe('header cell formatter test', () => { value: '小计', parent: root, label: '小计', - isTotals: true, + isTotalRoot: true, }); const formatter: Formatter = (value) => { @@ -92,6 +92,7 @@ describe('header cell formatter test', () => { label: '总计', isTotals: true, isGrandTotals: true, + isTotalRoot: true, }); const rowSubTotalNode = new Node({ diff --git a/packages/s2-core/__tests__/unit/data-set/__snapshots__/pivot-data-set-total-spec.ts.snap b/packages/s2-core/__tests__/unit/data-set/__snapshots__/pivot-data-set-total-spec.ts.snap new file mode 100644 index 0000000000..91b43b95ef --- /dev/null +++ b/packages/s2-core/__tests__/unit/data-set/__snapshots__/pivot-data-set-total-spec.ts.snap @@ -0,0 +1,121 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pivot Dataset Total Test test for total with dimension group get correct MultiData when query need to be processed 1`] = ` +Array [ + Object { + "$$extra$$": "number", + "$$value$$": 7789, + "city": "杭州市", + "number": 7789, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 2367, + "city": "绍兴市", + "number": 2367, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 3877, + "city": "宁波市", + "number": 3877, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 4342, + "city": "舟山市", + "number": 4342, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, +] +`; + +exports[`Pivot Dataset Total Test test for total with dimension group get correct MultiData when query need to be processed 2`] = `Array []`; + +exports[`Pivot Dataset Total Test test for total with dimension group get correct MultiData when query need to be processed 3`] = ` +Array [ + Object { + "$$extra$$": "number", + "$$value$$": 7789, + "city": "杭州市", + "number": 7789, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 2367, + "city": "绍兴市", + "number": 2367, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 3877, + "city": "宁波市", + "number": 3877, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 4342, + "city": "舟山市", + "number": 4342, + "province": "浙江省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 1723, + "city": "成都市", + "number": 1723, + "province": "四川省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 1822, + "city": "绵阳市", + "number": 1822, + "province": "四川省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 1943, + "city": "南充市", + "number": 1943, + "province": "四川省", + "sub_type": "桌子", + "type": "家具", + }, + Object { + "$$extra$$": "number", + "$$value$$": 2330, + "city": "乐山市", + "number": 2330, + "province": "四川省", + "sub_type": "桌子", + "type": "家具", + }, +] +`; diff --git a/packages/s2-core/__tests__/unit/data-set/pivot-data-set-total-spec.ts b/packages/s2-core/__tests__/unit/data-set/pivot-data-set-total-spec.ts index cb791efaed..2a7ac2d86b 100644 --- a/packages/s2-core/__tests__/unit/data-set/pivot-data-set-total-spec.ts +++ b/packages/s2-core/__tests__/unit/data-set/pivot-data-set-total-spec.ts @@ -443,6 +443,62 @@ describe('Pivot Dataset Total Test', () => { dataSet = new PivotDataSet(mockSheet); dataSet.setDataCfg(dataCfg); }); + + test('should get correct total cell data when totals calculated by calcFunc and Existential dimension grouping', () => { + const totalStatus = { + isRowTotal: true, + isColTotal: true, + isRowSubTotal: true, + isColSubTotal: true, + }; + + expect( + dataSet.getCellData({ + query: { + province: '浙江省', + sub_type: '桌子', + [EXTRA_FIELD]: 'number', + }, + isTotals: true, + totalStatus, + }), + ).toContainEntries([[VALUE_FIELD, 18375]]); + + expect( + dataSet.getCellData({ + query: { + province: '浙江省', + [EXTRA_FIELD]: 'number', + }, + totalStatus, + isTotals: true, + }), + ).toContainEntries([[VALUE_FIELD, 43098]]); + + expect( + dataSet.getCellData({ + query: { + sub_type: '桌子', + [EXTRA_FIELD]: 'number', + }, + totalStatus, + isTotals: true, + }), + ).toContainEntries([[VALUE_FIELD, 26193]]); + + expect( + dataSet.getCellData({ + query: { + province: '浙江省', + type: '家具', + [EXTRA_FIELD]: 'number', + }, + isTotals: true, + totalStatus, + }), + ).toContainEntries([[VALUE_FIELD, 32418]]); + }); + test('should get correct total cell data when totals calculated by calcFunc', () => { expect( dataSet.getCellData({ @@ -675,4 +731,188 @@ describe('Pivot Dataset Total Test', () => { expect(isColSubTotal4).toBeTrue(); }); }); + + describe('test for total with dimension group', () => { + beforeEach(() => { + MockPivotSheet.mockClear(); + const mockSheet = new MockPivotSheet(); + mockSheet.store = new Store(); + mockSheet.isValueInCols = () => true; + dataSet = new PivotDataSet(mockSheet); + + dataCfg = assembleDataCfg({ + meta: [], + fields: { + rows: ['province', 'city', 'type', 'sub_type'], + columns: [], + }, + }); + dataSet.setDataCfg(dataCfg); + }); + test('should get correct total dimension values', () => { + expect( + dataSet.getTotalDimensionValues('sub_type', { + province: '浙江省', + city: undefined, + }), + ).toEqual(['桌子', '沙发', '笔', '纸张']); + + expect( + dataSet.getTotalDimensionValues('sub_type', { + province: '浙江省', + city: undefined, + }), + ).toEqual(['桌子', '沙发', '笔', '纸张']); + + expect( + dataSet.getTotalDimensionValues('sub_type', { + province: undefined, + city: undefined, + type: '办公用品', + }), + ).toEqual(['笔', '纸张']); + + expect(dataSet.getTotalDimensionValues('city', {})).toEqual([ + '杭州市', + '绍兴市', + '宁波市', + '舟山市', + '成都市', + '绵阳市', + '南充市', + '乐山市', + ]); + + expect( + dataSet.getTotalDimensionValues('sub_type', { + province: undefined, + city: undefined, + type: undefined, + }), + ).toEqual(['桌子', '沙发', '笔', '纸张']); + }); + + test('should get correct boolean of grouping scenarios where query need to be processed', () => { + expect( + dataSet.checkExistDimensionGroup({ + province: 'A', + type: 'A', + sub_type: 'A', + }), + ).toEqual(true); + expect( + dataSet.checkExistDimensionGroup({ + province: 'A', + sub_type: 'A', + }), + ).toEqual(true); + expect( + dataSet.checkExistDimensionGroup({ + province: 'A', + city: 'A', + sub_type: 'A', + }), + ).toEqual(true); + expect( + dataSet.checkExistDimensionGroup({ + city: 'A', + sub_type: 'A', + }), + ).toEqual(true); + expect( + dataSet.checkExistDimensionGroup({ + province: 'A', + city: 'A', + }), + ).toEqual(false); + expect( + dataSet.checkExistDimensionGroup({ + province: 'A', + city: 'A', + type: 'A', + }), + ).toEqual(false); + }); + test('should get correct boolean of dimensionValue is a query condition', () => { + expect( + dataSet.checkAccordQueryWithDimensionValue({ + dimensionValues: '浙江省[&]杭州市[&]家具[&]桌子', + query: { + province: '浙江省', + city: 'A', + type: 'Abc', + }, + dimensions: dataCfg.fields.rows, + field: 'province', + }), + ).toEqual(true); + expect( + dataSet.checkAccordQueryWithDimensionValue({ + dimensionValues: '浙江省[&]杭州市[&]家具[&]桌子', + query: { + province: '浙江省', + city: '杭州市', + type: '家具', + }, + dimensions: dataCfg.fields.rows, + field: 'sub_type', + }), + ).toEqual(true); + expect( + dataSet.checkAccordQueryWithDimensionValue({ + dimensionValues: '浙江省[&]杭州市[&]家具[&]桌子', + query: { + province: '浙江省', + city: '不是杭州市', + type: '家具', + }, + dimensions: dataCfg.fields.rows, + field: 'sub_type', + }), + ).toEqual(false); + expect( + dataSet.checkAccordQueryWithDimensionValue({ + dimensionValues: '浙江省[&]杭州市[&]家具[&]桌子', + query: { + province: '浙江省', + }, + dimensions: dataCfg.fields.rows, + field: 'sub_type', + }), + ).toEqual(true); + }); + test('get correct query list when query need to be processed', () => { + expect( + dataSet.getTotalGroupQueries(dataCfg.fields.rows, { + province: '浙江省', + sub_type: '桌子', + }), + ).toEqual([ + { province: '浙江省', sub_type: '桌子', type: '家具', city: '杭州市' }, + { province: '浙江省', sub_type: '桌子', type: '家具', city: '绍兴市' }, + { province: '浙江省', sub_type: '桌子', type: '家具', city: '宁波市' }, + { province: '浙江省', sub_type: '桌子', type: '家具', city: '舟山市' }, + ]); + }); + + test('get correct MultiData when query need to be processed', () => { + expect( + dataSet.getMultiData({ + province: '浙江省', + sub_type: '桌子', + }), + ).toMatchSnapshot(); + expect( + dataSet.getMultiData({ + province: '浙江省', + sub_type: '杭州市', + }), + ).toMatchSnapshot(); + expect( + dataSet.getMultiData({ + sub_type: '桌子', + }), + ).toMatchSnapshot(); + }); + }); }); diff --git a/packages/s2-core/__tests__/unit/utils/export/export-spec.ts b/packages/s2-core/__tests__/unit/utils/export/export-spec.ts index 8845f7511e..d160c70afe 100644 --- a/packages/s2-core/__tests__/unit/utils/export/export-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/export/export-spec.ts @@ -237,6 +237,77 @@ describe('PivotSheet Export Test', () => { }); }); + it('should export correct data in grid mode with grouped totals in col', () => { + const s2 = new PivotSheet( + getContainer(), + assembleDataCfg({ + fields: { + valueInCols: true, + columns: ['province', 'city', 'type', 'sub_type', 'number'], + }, + }), + assembleOptions({ + hierarchyType: 'grid', + totals: { + row: { + showGrandTotals: true, + showSubTotals: true, + subTotalsDimensions: ['province'], + }, + col: { + totalsGroupDimensions: ['city', 'type'], + subTotalsGroupDimensions: ['sub_type'], + showGrandTotals: true, + showSubTotals: true, + subTotalsDimensions: ['type'], + }, + }, + }), + ); + s2.render(); + const data = copyData(s2, '\t'); + const rows = data.split('\n'); + expect(rows).toHaveLength(17); + rows.forEach((e) => { + expect(e.split('\t')).toHaveLength(60); + }); + }); + + it('should export correct data in grid mode with grouped totals in row', () => { + const s2 = new PivotSheet( + getContainer(), + assembleDataCfg({ + fields: { + valueInCols: false, + columns: ['province', 'city', 'type', 'sub_type', 'number'], + }, + }), + assembleOptions({ + hierarchyType: 'grid', + totals: { + row: { + showGrandTotals: true, + showSubTotals: true, + subTotalsDimensions: ['province'], + }, + col: { + totalsGroupDimensions: ['city', 'sub_type', 'province'], + subTotalsGroupDimensions: ['sub_type'], + showGrandTotals: true, + showSubTotals: true, + subTotalsDimensions: ['type'], + }, + }, + }), + ); + s2.render(); + const data = copyData(s2, '\t'); + const rows = data.split('\n'); + expect(rows).toHaveLength(16); + rows.forEach((e) => { + expect(e.split('\t')).toHaveLength(63); + }); + }); it('should export correct data in grid mode with totals in row', () => { const s2 = new PivotSheet( getContainer(), diff --git a/packages/s2-core/__tests__/unit/utils/facet-spec.ts b/packages/s2-core/__tests__/unit/utils/facet-spec.ts index 6dc2ca2561..5c9d1aa44a 100644 --- a/packages/s2-core/__tests__/unit/utils/facet-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/facet-spec.ts @@ -1,29 +1,10 @@ import { - getSubTotalNodeWidthOrHeightByLevel, getIndexRangeWithOffsets, getAdjustedRowScrollX, getAdjustedScrollOffset, } from '@/utils/facet'; describe('Facet util test', () => { - test('should get correct width of subTotal node', () => { - const sampleNodesForAllLevels = [ - { - id: 'root[&]测试', - value: '测试', - isSubTotals: true, - width: 20, - level: 0, - }, - ]; - expect( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - getSubTotalNodeWidthOrHeightByLevel(sampleNodesForAllLevels, -1, 'width'), - ).toEqual(20); - expect(getSubTotalNodeWidthOrHeightByLevel([], -1, 'width')).toEqual(0); - }); - test('should get correct index range for given offsets', () => { const offsets = [0, 30, 60, 90, 120, 150, 160, 170, 190]; expect(getIndexRangeWithOffsets(offsets, 0, 31)).toStrictEqual({ diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index f60de70711..0bfebe0b5f 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -85,7 +85,7 @@ export abstract class HeaderCell extends BaseCell { } protected getFormattedFieldValue(): FormatResult { - const { label, isTotals, isGrandTotals } = this.meta; + const { label, isTotalRoot, isGrandTotals } = this.meta; const formatter = this.spreadsheet.dataSet.getFieldFormatter( this.meta.field, @@ -99,16 +99,15 @@ export abstract class HeaderCell extends BaseCell { if (this.spreadsheet.isTableMode()) { shouldFormat = false; } else if (this.spreadsheet.isHierarchyTreeType()) { - shouldFormat = !isGrandTotals; + shouldFormat = !(isGrandTotals && isTotalRoot); } else { - shouldFormat = !isTotals; + shouldFormat = !isTotalRoot; } const formattedValue = shouldFormat && formatter ? formatter(label, undefined, this.meta) : label; - return { formattedValue, value: label, diff --git a/packages/s2-core/src/common/interface/basic.ts b/packages/s2-core/src/common/interface/basic.ts index 62d5c0a57f..cc63caae50 100644 --- a/packages/s2-core/src/common/interface/basic.ts +++ b/packages/s2-core/src/common/interface/basic.ts @@ -151,6 +151,10 @@ export interface Total { label?: string; // sub label's display name, default = '小计' subLabel?: string; + /** 总计分组维度 */ + totalsGroupDimensions?: string[]; + /** 小计分组维度 */ + subTotalsGroupDimensions?: string[]; } /** diff --git a/packages/s2-core/src/data-set/base-data-set.ts b/packages/s2-core/src/data-set/base-data-set.ts index 49d0c36072..1c95f5e736 100644 --- a/packages/s2-core/src/data-set/base-data-set.ts +++ b/packages/s2-core/src/data-set/base-data-set.ts @@ -165,6 +165,28 @@ export abstract class BaseDataSet { */ public abstract getDimensionValues(field: string, query?: DataType): string[]; + /** + * province city type + * 辽宁省 + * 达州市 A + * B + * 芜湖市 C + * 浙江省 + * 杭州市 B + * D + * 宁波市 E + * query = {province: "浙江省"} + * field = 'type' + * * => [B,D,E] + * + * @param field current dimensions + * @param query dimension value query + */ + public abstract getTotalDimensionValues( + field: string, + query?: DataType, + ): string[]; + /** * In most cases, this function to get the specific * cross data cell data diff --git a/packages/s2-core/src/data-set/interface.ts b/packages/s2-core/src/data-set/interface.ts index c1570113d3..672766c8ef 100644 --- a/packages/s2-core/src/data-set/interface.ts +++ b/packages/s2-core/src/data-set/interface.ts @@ -47,6 +47,24 @@ export interface CellDataParams { rowNode?: Node; // mark row's cell isRow?: boolean; + // use with isTotals + totalStatus?: TotalStatus; +} + +export interface CheckAccordQueryParams { + // item of sortedDimensionValues,es: "浙江省[&]杭州市[&]家具[&]桌子" + dimensionValues: string; + query: DataType; + // rows or columns + dimensions: string[]; + field: string; +} + +export interface TotalStatus { + isRowTotal: boolean; + isRowSubTotal: boolean; + isColTotal: boolean; + isColSubTotal: boolean; } export interface SortActionParams { diff --git a/packages/s2-core/src/data-set/pivot-data-set.ts b/packages/s2-core/src/data-set/pivot-data-set.ts index af71751c04..545d9eebf8 100644 --- a/packages/s2-core/src/data-set/pivot-data-set.ts +++ b/packages/s2-core/src/data-set/pivot-data-set.ts @@ -1,5 +1,6 @@ import { compact, + concat, difference, each, every, @@ -14,6 +15,7 @@ import { isNumber, isUndefined, keys, + some, uniq, unset, values, @@ -61,9 +63,11 @@ import type { CellMeta } from '../common'; import { BaseDataSet } from './base-data-set'; import type { CellDataParams, + CheckAccordQueryParams, DataType, PivotMeta, SortedDimensionValues, + TotalStatus, } from './interface'; export class PivotDataSet extends BaseDataSet { @@ -200,6 +204,7 @@ export class PivotDataSet extends BaseDataSet { } // 2. 删除 rowPivotMeta 当前下钻层级对应 meta 信息 deleteMetaById(this.rowPivotMeta, rowNodeId); + // 3. 删除下钻缓存路径 idPathMap.delete(rowNodeId); @@ -317,6 +322,36 @@ export class PivotDataSet extends BaseDataSet { }; } + public getDimensionsByField(field: string): string[] { + const { rows = [], columns = [] } = this.fields || {}; + if (includes(rows, field)) { + return rows; + } + if (includes(columns, field)) { + return columns as string[]; + } + return []; + } + + // rows :['province','city','type'] + // query: ['浙江省',undefined] => return: ['文具','家具'] + public getTotalDimensionValues(field: string, query?: DataType): string[] { + const dimensions = this.getDimensionsByField(field); + const allCurrentFieldDimensionValues = ( + this.sortedDimensionValues[field] || [] + ).filter((dimValue) => + this.checkAccordQueryWithDimensionValue({ + dimensionValues: dimValue, + query, + dimensions, + field, + }), + ); + return filterUndefined( + uniq(getDimensionsWithoutPathPre([...allCurrentFieldDimensionValues])), + ); + } + public getDimensionValues(field: string, query?: DataType): string[] { const { rows = [], columns = [] } = this.fields || {}; let meta: PivotMeta = new Map(); @@ -328,7 +363,6 @@ export class PivotDataSet extends BaseDataSet { meta = this.colPivotMeta; dimensions = columns as string[]; } - if (!isEmpty(query)) { let sortedMeta = []; const dimensionValuePath = []; @@ -367,10 +401,12 @@ export class PivotDataSet extends BaseDataSet { return filterUndefined([...meta.keys()]); } - getTotalValue(query: DataType) { + getTotalValue(query: DataType, totalStatus?: TotalStatus) { + const effectiveStatus = some(totalStatus); + const status = effectiveStatus ? totalStatus : this.getTotalStatus(query); const { aggregation, calcFunc } = getAggregationAndCalcFuncByQuery( - this.getTotalStatus(query), + status, this.spreadsheet.options?.totals, ) || {}; const calcAction = calcActionByType[aggregation]; @@ -379,7 +415,6 @@ export class PivotDataSet extends BaseDataSet { if (calcAction || calcFunc) { const data = this.getMultiData(query); let totalValue: number; - if (calcFunc) { totalValue = calcFunc(query, data); } else if (calcAction) { @@ -395,7 +430,7 @@ export class PivotDataSet extends BaseDataSet { } public getCellData(params: CellDataParams): DataType { - const { query, rowNode, isTotals = false } = params || {}; + const { query, rowNode, isTotals = false, totalStatus } = params || {}; const { columns, rows: originRows } = this.fields; let rows = originRows; @@ -427,8 +462,7 @@ export class PivotDataSet extends BaseDataSet { // 如果已经有数据则取已有数据 return data; } - - return isTotals ? this.getTotalValue(query) : data; + return isTotals ? this.getTotalValue(query, totalStatus) : data; } getCustomData = (path: number[]) => { @@ -482,6 +516,147 @@ export class PivotDataSet extends BaseDataSet { }; }; + /** + * 检查是否属于需要填充中间汇总维度的场景 + * [undefined , '杭州市' , undefined , 'number'] => true + * ['浙江省' , '杭州市' , undefined , 'number'] => true + */ + checkExistDimensionGroup(query: DataType): boolean { + const { rows, columns } = this.fields; + const check = (dimensions: string[]) => { + let existDimensionValue = false; + for (let i = dimensions.length; i > 0; i--) { + const key = dimensions[i - 1]; + if (keys(query).includes(key)) { + if (key !== EXTRA_FIELD) { + existDimensionValue = true; + } + } else if (existDimensionValue) { + return true; + } + } + return false; + }; + return check(rows) || check(columns as string[]); + } + + /** + * 检查 DimensionValue 是否符合 query 条件 + * dimensions = ['province','city'] + * query = [province: '杭州市', type: '文具'] + * field = 'sub_type' + * DimensionValue: 浙江省[&]杭州市[&]家具[&]桌子 => true + * DimensionValue: 四川省[&]成都市[&]文具[&]笔 => false + */ + checkAccordQueryWithDimensionValue(params: CheckAccordQueryParams): boolean { + const { dimensionValues, query, dimensions, field } = params; + for (const [index, dimension] of dimensions.entries()) { + const queryValue = get(query, dimension); + if (queryValue) { + const arrTypeValue = dimensionValues.split(ID_SEPARATOR); + const dimensionValue = arrTypeValue[index]; + if (dimensionValue !== queryValue) { + return false; + } + } + if (field === dimension) { + break; + } + } + return true; + } + + /** + * 补足分组汇总场景的前置 undefined + * {undefined,'可乐','undefined','price'} + * => [ + * {'可口公司','可乐','undefined','price'}, + * {'百事公司','可乐','undefined','price'}, + * ] + */ + getTotalGroupQueries(dimensions: string[], originQuery: DataType) { + let queries = [originQuery]; + let existDimensionGroupKey = null; + for (let i = dimensions.length; i > 0; i--) { + const key = dimensions[i - 1]; + if (keys(originQuery).includes(key)) { + if (key !== EXTRA_FIELD) { + existDimensionGroupKey = key; + } + } else if (existDimensionGroupKey) { + const allCurrentFieldDimensionValues = + this.sortedDimensionValues[existDimensionGroupKey]; + let res = []; + const arrayLength = + allCurrentFieldDimensionValues[0].split(ID_SEPARATOR).length; + for (const query of queries) { + const resKeys = []; + for (const dimValue of allCurrentFieldDimensionValues) { + if ( + this.checkAccordQueryWithDimensionValue({ + dimensionValues: dimValue, + query, + dimensions, + field: existDimensionGroupKey, + }) + ) { + const arrTypeValue = dimValue.split(ID_SEPARATOR); + const currentKey = arrTypeValue[arrayLength - 2]; + if (currentKey !== 'undefined') { + resKeys.push(currentKey); + } + } + } + const queryList = uniq(resKeys).map((v) => { + return { ...query, [key]: v }; + }); + res = concat(res, queryList); + } + queries = res; + existDimensionGroupKey = key; + } + } + return queries; + } + + // 有中间维度汇总的分组场景,将有中间 undefined 值的 query 处理为一组合法 query 后查询数据再合并 + private getGroupTotalMultiData( + totalRows: string[], + originQuery: DataType, + ): DataType[] { + const { rows, columns } = this.fields; + let result = []; + const rowTotalGroupQueries = this.getTotalGroupQueries( + totalRows, + originQuery, + ); + let totalGroupQueries = []; + for (const query of rowTotalGroupQueries) { + totalGroupQueries = concat( + totalGroupQueries, + this.getTotalGroupQueries(columns as string[], query), + ); + } + + for (const query of totalGroupQueries) { + const rowDimensionValues = getQueryDimValues(totalRows, query); + const colDimensionValues = getQueryDimValues(columns as string[], query); + const path = getDataPath({ + rowDimensionValues, + colDimensionValues, + careUndefined: true, + isFirstCreate: true, + rowFields: rows, + colFields: columns as string[], + rowPivotMeta: this.rowPivotMeta, + colPivotMeta: this.colPivotMeta, + }); + const currentData = this.getCustomData(path); + result = concat(result, compact(customFlatten(currentData))); + } + return result; + } + public getMultiData( query: DataType, isTotals?: boolean, @@ -495,70 +670,77 @@ export class PivotDataSet extends BaseDataSet { const totalRows = !isEmpty(drillDownFields) ? rows.concat(drillDownFields) : rows; - const rowDimensionValues = getQueryDimValues(totalRows, query); - const colDimensionValues = getQueryDimValues(columns as string[], query); - const path = getDataPath({ - rowDimensionValues, - colDimensionValues, - careUndefined: true, - isFirstCreate: true, - rowFields: rows, - colFields: columns as string[], - rowPivotMeta: this.rowPivotMeta, - colPivotMeta: this.colPivotMeta, - }); - const currentData = this.getCustomData(path); - let result = compact(customFlatten(currentData)); - if (isTotals) { - // 总计/小计(行/列) - // need filter extra data - // grand total => {$$extra$$: 'price'} - // sub total => {$$extra$$: 'price', category: 'xxxx'} - // [undefined, undefined, "price"] => [category] - let fieldKeys = []; - const rowKeys = getFieldKeysByDimensionValues(rowDimensionValues, rows); - const colKeys = getFieldKeysByDimensionValues( + // existDimensionGroup:当 undefined 维度后面有非 undefined,为维度分组场景,将非 undefined 维度前的维度填充为所有可能的维度值。 + // 如 [undefined , '杭州市' , undefined , 'number'] + const existDimensionGroup = this.checkExistDimensionGroup(query); + let result = []; + if (existDimensionGroup) { + result = this.getGroupTotalMultiData(totalRows, query); + } else { + const rowDimensionValues = getQueryDimValues(totalRows, query); + const colDimensionValues = getQueryDimValues(columns as string[], query); + const path = getDataPath({ + rowDimensionValues, colDimensionValues, - columns as string[], - ); - if (isRow) { - // 行总计 - fieldKeys = rowKeys; - } else { - // 只有一个值,此时为列总计 - const isCol = keys(query)?.length === 1 && has(query, EXTRA_FIELD); - - if (isCol) { - fieldKeys = colKeys; + careUndefined: true, + isFirstCreate: true, + rowFields: rows, + colFields: columns as string[], + rowPivotMeta: this.rowPivotMeta, + colPivotMeta: this.colPivotMeta, + }); + const currentData = this.getCustomData(path); + result = compact(customFlatten(currentData)); + if (isTotals) { + // 总计/小计(行/列) + // need filter extra data + // grand total => {$$extra$$: 'price'} + // sub total => {$$extra$$: 'price', category: 'xxxx'} + // [undefined, undefined, "price"] => [category] + let fieldKeys = []; + const rowKeys = getFieldKeysByDimensionValues(rowDimensionValues, rows); + const colKeys = getFieldKeysByDimensionValues( + colDimensionValues, + columns as string[], + ); + if (isRow) { + // 行总计 + fieldKeys = rowKeys; } else { - const getTotalStatus = (dimensions: string[]) => { - return isEveryUndefined( - dimensions?.filter((item) => !valueList?.includes(item)), - ); - }; - const isRowTotal = getTotalStatus(colDimensionValues); - const isColTotal = getTotalStatus(rowDimensionValues); - - if (isRowTotal) { - // 行小计 - fieldKeys = rowKeys; - } else if (isColTotal) { - // 列小计 + // 只有一个值,此时为列总计 + const isCol = keys(query)?.length === 1 && has(query, EXTRA_FIELD); + + if (isCol) { fieldKeys = colKeys; } else { - // 行小计+列 or 列小计+行 - fieldKeys = [...rowKeys, ...colKeys]; + const getTotalStatus = (dimensions: string[]) => { + return isEveryUndefined( + dimensions?.filter((item) => !valueList?.includes(item)), + ); + }; + const isRowTotal = getTotalStatus(colDimensionValues); + const isColTotal = getTotalStatus(rowDimensionValues); + + if (isRowTotal) { + // 行小计 + fieldKeys = rowKeys; + } else if (isColTotal) { + // 列小计 + fieldKeys = colKeys; + } else { + // 行小计+列 or 列小计+行 + fieldKeys = [...rowKeys, ...colKeys]; + } } } + result = result.filter( + (r) => + !fieldKeys?.find( + (item) => item !== EXTRA_FIELD && keys(r)?.includes(item), + ), + ); } - result = result.filter( - (r) => - !fieldKeys?.find( - (item) => item !== EXTRA_FIELD && keys(r)?.includes(item), - ), - ); } - return result || []; } diff --git a/packages/s2-core/src/data-set/table-data-set.ts b/packages/s2-core/src/data-set/table-data-set.ts index 0d9c8e457f..619d04456a 100644 --- a/packages/s2-core/src/data-set/table-data-set.ts +++ b/packages/s2-core/src/data-set/table-data-set.ts @@ -150,6 +150,10 @@ export class TableDataSet extends BaseDataSet { }); }; + public getTotalDimensionValues(field: string, query?: DataType): string[] { + return []; + } + public getDimensionValues(field: string, query?: DataType): string[] { return []; } diff --git a/packages/s2-core/src/facet/README-adjustTotalNodesCoordinate.md b/packages/s2-core/src/facet/README-adjustTotalNodesCoordinate.md new file mode 100644 index 0000000000..3998b5fcf6 --- /dev/null +++ b/packages/s2-core/src/facet/README-adjustTotalNodesCoordinate.md @@ -0,0 +1,57 @@ +## 小计总计节点布局逻辑 + +1. 计算一个 multipleMap ,表示每个 level 的汇总节点实际占据一个单元格 +2. 遍历所有汇总单元格,根据 multipleMap 中的值,计算宽或高为合并多个单元格 + +🌰 例子: + +rows: [ 省份, 城市, 类别, 子类别 ] + +Total - row - totalDimensionGroup: [ 城市, 类别 ] + +Total - row - subTotalDimensionGroup: [ 子类别 ] + +row + +### multipleMap 如何计算 - getMultipleMap 函数 + +行总计 multipleMap: + +1. 初始化一个长度与 rows 相同的数组 [ 1, 1, 1 ,1 ] +2. 从后往前判断 totalDimensionGroup.includes(field) + - 第一个处理子类别,不做处理 + - 第二个处理 类别,判断为 false,将类别数值向前移动[ 1, 2, 0, 1] + - 第三个判断 城市,判断为 false,将城市数值向前移动[ 3, 0, 0, 1] + - 第四个 省份,首个维度不做遍历 +3. multipleMap 结果为 [ 3, 0, 0, 1] + +行小计 multipleMap: + +1. 初始化一个长度与 rows 相同的数组 [ 1, 1, 1 ,1 ] +2. 从后往前判断 subTotalDimensionGroup.includes(field) + - 第一个处理子类别,判断为 true,不做处理 + - 第二个处理 类别,判断为 false,将类别数值向前移动[ 1, 1, 2, 0] + - 第三个判断 城市,判断为 flase,不做处理 + - 第四个 省份,首个维度不做遍历 +3. multipleMap 结果为 [ 1, 1, 2, 0] + +### multipleMap 如何使用 - adjustTotalNodesCoordinate 函数 + +总计的 multipleMap = [ 1 , 1 ,2 ,0 ] + +multipleMap 表示: + +- row 的第一第二个维度占据一个单元格 +- 第三个维度合并两个单元格 +- 第四个维度没有节点 + +小计的 multipleMap = [ 3,0,0,1 ] + +multipleMap 表示: + +- 小计结点,row的第一个维度合并三个单元格 +- 小计结点的最后一个维度占据一个单元格 + +当小计节点出现在第二个维度,则合并量为‘最近的上一个不为0的值’ - level 差 + +即实际该小计节点实际的 multipleMap 为 [1,2,0,1] diff --git a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts index bec4de2b0d..eb8de392ec 100644 --- a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts @@ -1,36 +1,17 @@ import { isEmpty, isUndefined } from 'lodash'; import { EXTRA_FIELD } from '../../common/constant'; -import type { SpreadSheetFacetCfg } from '../../common/interface'; import { addTotals } from '../../utils/layout/add-totals'; import { generateHeaderNodes } from '../../utils/layout/generate-header-nodes'; import { getDimsCondition } from '../../utils/layout/get-dims-condition-by-node'; import type { FieldValue, GridHeaderParams } from '../layout/interface'; import { layoutArrange } from '../layout/layout-hooks'; import { TotalMeasure } from '../layout/total-measure'; +import { whetherLeafByLevel } from '../../utils/layout/whether-leaf-by-level'; +import { TotalClass } from './total-class'; -const hideMeasureColumn = ( - fieldValues: FieldValue[], - field: string, - cfg: SpreadSheetFacetCfg, -) => { - const hideMeasure = cfg.colCfg?.hideMeasureColumn ?? false; - const valueInCol = cfg.dataSet.fields.valueInCols; - for (const value of fieldValues) { - if (hideMeasure && valueInCol && field === EXTRA_FIELD) { - fieldValues.splice(fieldValues.indexOf(value), 1); - } - } -}; - -/** - * Build grid hierarchy in rows or columns - * - * @param params - */ -export const buildGridHierarchy = (params: GridHeaderParams) => { +const buildTotalGridHierarchy = (params: GridHeaderParams) => { const { addTotalMeasureInTotal, - addMeasureInTotalQuery, parentNode, currentField, fields, @@ -42,64 +23,118 @@ export const buildGridHierarchy = (params: GridHeaderParams) => { const { dataSet, values, spreadsheet } = facetCfg; const fieldValues: FieldValue[] = []; + const fieldName = dataSet.getFieldName(currentField); let query = {}; - if (parentNode.isTotals) { - // add total measures - if (addTotalMeasureInTotal) { - query = getDimsCondition(parentNode.parent, true); - // add total measures - fieldValues.push(...values.map((v) => new TotalMeasure(v))); + const totalsConfig = spreadsheet.getTotalsConfig(currentField); + const dimensionGroup = parentNode.isGrandTotals + ? totalsConfig.totalsGroupDimensions + : totalsConfig.subTotalsGroupDimensions; + if (dimensionGroup?.includes(currentField)) { + query = getDimsCondition(parentNode); + const dimValues = dataSet.getTotalDimensionValues(currentField, query); + fieldValues.push( + ...(dimValues || []).map( + (value) => + new TotalClass({ + label: value, + isSubTotals: parentNode.isSubTotals, + isGrandTotals: parentNode.isGrandTotals, + isTotalRoot: false, + }), + ), + ); + if (isEmpty(fieldValues)) { + fieldValues.push(fieldName); } + } else if (addTotalMeasureInTotal && currentField === EXTRA_FIELD) { + // add total measures + query = getDimsCondition(parentNode); + fieldValues.push(...values.map((v) => new TotalMeasure(v))); + } else if (whetherLeafByLevel({ facetCfg, level: index, fields })) { + // 如果最后一级没有分组维度,则将上一个结点设为叶子结点 + parentNode.isLeaf = true; + hierarchy.pushIndexNode(parentNode); + parentNode.rowIndex = hierarchy.getIndexNodes().length - 1; + return; } else { - // field(dimension)'s all values - query = getDimsCondition(parentNode, true); + // 如果是空维度,则跳转到下一级 level + buildTotalGridHierarchy({ ...params, currentField: fields[index + 1] }); + return; + } - const dimValues = dataSet.getDimensionValues(currentField, query); + const displayFieldValues = fieldValues.filter((value) => !isUndefined(value)); + generateHeaderNodes({ + ...params, + fieldValues: displayFieldValues, + level: index, + parentNode, + query, + }); +}; - const arrangedValues = layoutArrange( - dimValues, - facetCfg, - parentNode, - currentField, - ); - fieldValues.push(...(arrangedValues || [])); +const buildNormalGridHierarchy = (params: GridHeaderParams) => { + const { parentNode, currentField, fields, facetCfg } = params; - // add skeleton for empty data + const index = fields.indexOf(currentField); - const fieldName = dataSet.getFieldName(currentField); + const { dataSet, spreadsheet } = facetCfg; + const fieldValues: FieldValue[] = []; + const fieldName = dataSet.getFieldName(currentField); - if (isEmpty(fieldValues)) { - if (currentField === EXTRA_FIELD) { - fieldValues.push(...dataSet.fields?.values); - } else { - fieldValues.push(fieldName); - } + let query = {}; + + // field(dimension)'s all values + query = getDimsCondition(parentNode, true); + + const dimValues = dataSet.getDimensionValues(currentField, query); + + const arrangedValues = layoutArrange( + dimValues, + facetCfg, + parentNode, + currentField, + ); + fieldValues.push(...(arrangedValues || [])); + + // add skeleton for empty data + + if (isEmpty(fieldValues)) { + if (currentField === EXTRA_FIELD) { + fieldValues.push(...dataSet.fields?.values); + } else { + fieldValues.push(fieldName); } - // hide measure in columns - hideMeasureColumn(fieldValues, currentField, facetCfg); - // add totals if needed - addTotals({ - currentField, - lastField: fields[index - 1], - isFirstField: index === 0, - fieldValues, - spreadsheet, - }); } - const displayFieldValues = fieldValues.filter((value) => !isUndefined(value)); + // add totals if needed + addTotals({ + currentField, + lastField: fields[index - 1], + isFirstField: index === 0, + fieldValues, + spreadsheet, + }); + const displayFieldValues = fieldValues.filter((value) => !isUndefined(value)); generateHeaderNodes({ - currentField, - fields, + ...params, fieldValues: displayFieldValues, - facetCfg, - hierarchy, - parentNode, level: index, + parentNode, query, - addMeasureInTotalQuery, - addTotalMeasureInTotal, }); }; + +/** + * Build grid hierarchy in rows or columns + * + * @param params + */ +export const buildGridHierarchy = (params: GridHeaderParams) => { + if (params.parentNode.isTotals) { + buildTotalGridHierarchy(params); + } else { + buildNormalGridHierarchy(params); + } +}; diff --git a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts index 05afda5ffe..28697f9c11 100644 --- a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts @@ -20,7 +20,13 @@ const addTotals = ( // TODO valueInCol = false and one or more values if (totalsConfig.showGrandTotals) { const func = totalsConfig.reverseLayout ? 'unshift' : 'push'; - fieldValues[func](new TotalClass(totalsConfig.label, false, true)); + fieldValues[func]( + new TotalClass({ + label: totalsConfig.label, + isSubTotals: false, + isGrandTotals: true, + }), + ); } }; diff --git a/packages/s2-core/src/facet/layout/interface.ts b/packages/s2-core/src/facet/layout/interface.ts index ab051f243d..3e15a02f5d 100644 --- a/packages/s2-core/src/facet/layout/interface.ts +++ b/packages/s2-core/src/facet/layout/interface.ts @@ -42,15 +42,8 @@ export interface TotalParams { spreadsheet: SpreadSheet; } -export interface HeaderNodesParams { - currentField: string; - fields: string[]; +export interface HeaderNodesParams extends GridHeaderParams { fieldValues: FieldValue[]; - addTotalMeasureInTotal: boolean; - addMeasureInTotalQuery: boolean; - facetCfg: SpreadSheetFacetCfg; - hierarchy: Hierarchy; - parentNode: Node; level: number; query: Record; } @@ -93,3 +86,9 @@ export interface CustomTreeHeaderParams { hierarchy: Hierarchy; customTreeItems: CustomTreeItem[]; } + +export interface WhetherLeafParams { + facetCfg: SpreadSheetFacetCfg; + fields: string[]; + level: number; +} diff --git a/packages/s2-core/src/facet/layout/node.ts b/packages/s2-core/src/facet/layout/node.ts index 195bf0051a..cfc09d5466 100644 --- a/packages/s2-core/src/facet/layout/node.ts +++ b/packages/s2-core/src/facet/layout/node.ts @@ -21,6 +21,7 @@ export interface BaseNodeConfig { isSubTotals?: boolean; isCollapsed?: boolean; isGrandTotals?: boolean; + isTotalRoot?: boolean; hierarchy?: Hierarchy; isPivotMode?: boolean; seriesNumberWidth?: number; @@ -62,6 +63,7 @@ export class Node { isGrandTotals, isSubTotals, isCollapsed, + isTotalRoot, hierarchy, isPivotMode, seriesNumberWidth, @@ -95,6 +97,7 @@ export class Node { this.isLeaf = isLeaf; this.isGrandTotals = isGrandTotals; this.isSubTotals = isSubTotals; + this.isTotalRoot = isTotalRoot; this.extra = extra; } @@ -301,6 +304,8 @@ export class Node { public isSubTotals?: boolean; + public isTotalRoot?: boolean; + public hiddenChildNodeInfo?: HiddenColumnsInfo | null; public extra?: Record; diff --git a/packages/s2-core/src/facet/layout/total-class.ts b/packages/s2-core/src/facet/layout/total-class.ts index 51df2d35c1..36f47c995d 100644 --- a/packages/s2-core/src/facet/layout/total-class.ts +++ b/packages/s2-core/src/facet/layout/total-class.ts @@ -1,6 +1,16 @@ /** * Class to mark '小计' & '总计' */ + +export interface TotalClassConfig { + label: string; + // 是否属于小计汇总格 + isSubTotals: boolean; + // 是否属于总计汇总格 + isGrandTotals: boolean; + // 是否是”小计“、”总计“单元格本身 + isTotalRoot?: boolean; +} export class TotalClass { public label: string; @@ -8,13 +18,14 @@ export class TotalClass { public isGrandTotals: boolean; - public constructor( - label: string, - isSubTotals = false, - isGrandTotals = false, - ) { + // 是否为 小计/总计 根结点,即 value = “小计”,单元格,此类结点不参与 query + public isTotalRoot: boolean; + + public constructor(params: TotalClassConfig) { + const { label, isSubTotals, isGrandTotals, isTotalRoot } = params; this.label = label; this.isSubTotals = isSubTotals; this.isGrandTotals = isGrandTotals; + this.isTotalRoot = isTotalRoot; } } diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index 559a2a43a8..3b432f44b5 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -1,5 +1,5 @@ import { - find, + filter, forEach, get, isArray, @@ -26,11 +26,9 @@ import { DebuggerUtil } from '../common/debug'; import type { LayoutResult, ViewMeta } from '../common/interface'; import { getDataCellId, handleDataItem } from '../utils/cell/data-cell'; import { getActionIconConfig } from '../utils/cell/header-cell'; -import { - getIndexRangeWithOffsets, - getSubTotalNodeWidthOrHeightByLevel, -} from '../utils/facet'; +import { getIndexRangeWithOffsets } from '../utils/facet'; import { getCellWidth, safeJsonParse } from '../utils/text'; +import { getHeaderTotalStatus } from '../utils/dataset/pivot-data-set'; import { BaseFacet } from './base-facet'; import { buildHeaderHierarchy } from './layout/build-header-hierarchy'; import type { Hierarchy } from './layout/hierarchy'; @@ -97,10 +95,12 @@ export class PivotFacet extends BaseFacet { } : {}; const dataQuery = merge({}, rowQuery, colQuery, measureInfo); + const totalStatus = getHeaderTotalStatus(row, col); const data = dataSet.getCellData({ query: dataQuery, rowNode: row, isTotals, + totalStatus, }); let valueField: string; let fieldValue = null; @@ -182,9 +182,17 @@ export class PivotFacet extends BaseFacet { ) { let preLeafNode = Node.blankNode(); const allNodes = colsHierarchy.getNodes(); - for (const levelSample of colsHierarchy.sampleNodesForAllLevels) { + const sampleNodesForAllLevels = colsHierarchy.sampleNodesForAllLevels; + for (let level = 0; level < sampleNodesForAllLevels.length; level++) { + const levelSample = sampleNodesForAllLevels[level]; levelSample.height = this.getColNodeHeight(levelSample); colsHierarchy.height += levelSample.height; + if (levelSample.level === 0) { + levelSample.y = 0; + } else { + const preLevelSample = sampleNodesForAllLevels[level - 1]; + levelSample.y = preLevelSample?.y + preLevelSample?.height ?? 0; + } } let currentCollIndex = 0; for (let i = 0; i < allNodes.length; i++) { @@ -211,17 +219,21 @@ export class PivotFacet extends BaseFacet { ); currentNode.y = preLevelSample?.y + preLevelSample?.height ?? 0; } - // 数值置于行头时, 列头的总计即叶子节点, 此时应该用列高: https://github.com/antvis/S2/issues/1715 - currentNode.height = - currentNode.isGrandTotals && currentNode.isLeaf - ? colsHierarchy.height - : this.getColNodeHeight(currentNode); + currentNode.height = this.getColNodeHeight(currentNode); layoutCoordinate(this.cfg, null, currentNode); } this.autoCalculateColNodeWidthAndX(colLeafNodes); if (!isEmpty(this.spreadsheet.options.totals?.col)) { - this.adjustTotalNodesCoordinate(colsHierarchy); - this.adjustSubTotalNodesCoordinate(colsHierarchy); + this.adjustTotalNodesCoordinate({ + hierarchy: colsHierarchy, + isRowHeader: false, + isSubTotal: true, + }); + this.adjustTotalNodesCoordinate({ + hierarchy: colsHierarchy, + isRowHeader: false, + isSubTotal: false, + }); } } @@ -311,6 +323,7 @@ export class PivotFacet extends BaseFacet { col.isTotalMeasure || rowNode.isTotals || rowNode.isTotalMeasure, + totalStatus: getHeaderTotalStatus(rowNode, col), }); if (cellData) { @@ -487,8 +500,16 @@ export class PivotFacet extends BaseFacet { if (!isTree) { this.autoCalculateRowNodeHeightAndY(rowLeafNodes); if (!isEmpty(spreadsheet.options.totals?.row)) { - this.adjustTotalNodesCoordinate(rowsHierarchy, true); - this.adjustSubTotalNodesCoordinate(rowsHierarchy, true); + this.adjustTotalNodesCoordinate({ + hierarchy: rowsHierarchy, + isRowHeader: true, + isSubTotal: false, + }); + this.adjustTotalNodesCoordinate({ + hierarchy: rowsHierarchy, + isRowHeader: true, + isSubTotal: true, + }); } } } @@ -517,105 +538,64 @@ export class PivotFacet extends BaseFacet { } } - /** - * @description adjust the coordinate of total nodes and their children - * @param hierarchy Hierarchy - * @param isRowHeader boolean - */ - private adjustTotalNodesCoordinate( + // please read README-adjustTotalNodesCoordinate.md to understand this function + private getMultipleMap( hierarchy: Hierarchy, isRowHeader?: boolean, + isSubTotal?: boolean, ) { - const moreThanOneValue = this.cfg.dataSet.moreThanOneValue(); const { maxLevel } = hierarchy; - const grandTotalNode = find( - hierarchy.getNodes(0), - (node: Node) => node.isGrandTotals, - ); - if (!(grandTotalNode instanceof Node)) { - return; - } - const grandTotalChildren = grandTotalNode.children; - // 总计节点层级 (有且有两级) - if (isRowHeader) { - // 填充行总单元格宽度 - grandTotalNode.width = hierarchy.width; - // 调整其叶子节点位置和宽度 - forEach(grandTotalChildren, (node: Node) => { - const maxLevelNode = hierarchy.getNodes(maxLevel)[0]; - node.x = maxLevelNode.x; - node.width = maxLevelNode.width; - }); - } else if (maxLevel > 1 || (maxLevel <= 1 && !moreThanOneValue)) { - // 只有当列头总层级大于1级或列头为1级单指标时总计格高度才需要填充 - // 填充列总计单元格高度 - const grandTotalChildrenHeight = grandTotalChildren?.[0]?.height ?? 0; - grandTotalNode.height = hierarchy.height - grandTotalChildrenHeight; - // 调整其叶子结点位置, 以非小计行为准 - const positionY = find( - hierarchy.getNodes(maxLevel), - (node: Node) => !node.isTotalMeasure, - )?.y; - forEach(grandTotalChildren, (node: Node) => { - node.y = positionY; - }); + const { totals, dataSet } = this.cfg; + const moreThanOneValue = dataSet.moreThanOneValue(); + const { rows, columns } = dataSet.fields; + const fields = isRowHeader ? rows : columns; + const totalConfig = isRowHeader ? totals.row : totals.col; + const dimensionGroup = isSubTotal + ? totalConfig.subTotalsGroupDimensions || [] + : totalConfig.totalsGroupDimensions || []; + const multipleMap: number[] = Array.from({ length: maxLevel + 1 }, () => 1); + for (let level = maxLevel; level > 0; level--) { + const currentField = fields[level] as string; + // 若不符合【分组维度包含此维度】或【者指标维度下非单指标维度】,此表头单元格为空,将宽高合并到上级单元格 + const existValueField = currentField === EXTRA_FIELD && moreThanOneValue; + if (!(dimensionGroup.includes(currentField) || existValueField)) { + multipleMap[level - 1] += multipleMap[level]; + multipleMap[level] = 0; + } } + return multipleMap; } - /** - * @description adust the coordinate of subTotal nodes when there is just one value - * @param hierarchy Hierarchy - * @param isRowHeader boolean - */ - private adjustSubTotalNodesCoordinate( - hierarchy: Hierarchy, - isRowHeader?: boolean, - ) { - const subTotalNodes = hierarchy - .getNodes() - .filter((node: Node) => node.isSubTotals); - - if (isEmpty(subTotalNodes)) { - return; - } - const { maxLevel } = hierarchy; - forEach(subTotalNodes, (subTotalNode: Node) => { - const subTotalNodeChildren = subTotalNode.children; - if (isRowHeader) { - // 填充行总单元格宽度 - subTotalNode.width = getSubTotalNodeWidthOrHeightByLevel( - hierarchy.sampleNodesForAllLevels, - subTotalNode.level, - 'width', - ); - - // 调整其叶子结点位置 - forEach(subTotalNodeChildren, (node: Node) => { - node.x = hierarchy.getNodes(maxLevel)[0].x; - }); - } else { - // 填充列总单元格高度 - const totalHeight = getSubTotalNodeWidthOrHeightByLevel( - hierarchy.sampleNodesForAllLevels, - subTotalNode.level, - 'height', - ); - const subTotalNodeChildrenHeight = - subTotalNodeChildren?.[0]?.height ?? 0; - subTotalNode.height = totalHeight - subTotalNodeChildrenHeight; - // 调整其叶子结点位置,以非小计单元格为准 - forEach(subTotalNodeChildren, (node: Node) => { - node.y = hierarchy.getNodes(maxLevel)[0].y; - }); - // 调整其叶子结点位置, 以非小计行为准 - const positionY = find( - hierarchy.getNodes(maxLevel), - (node: Node) => !node.isTotalMeasure, - )?.y; - forEach(subTotalNodeChildren, (node: Node) => { - node.y = positionY; - }); + // please read README-adjustTotalNodesCoordinate.md to understand this function + private adjustTotalNodesCoordinate(params: { + hierarchy: Hierarchy; + isRowHeader?: boolean; + isSubTotal?: boolean; + }) { + const { hierarchy, isRowHeader, isSubTotal } = params; + const multipleMap = this.getMultipleMap(hierarchy, isRowHeader, isSubTotal); + const totalNodes = filter(hierarchy.getNodes(), (node: Node) => + isSubTotal ? node.isSubTotals : node.isGrandTotals, + ); + const key = isRowHeader ? 'width' : 'height'; + forEach(totalNodes, (node: Node) => { + let multiple = multipleMap[node.level]; + // 小计根节点若为 0,则改为最近上级倍数 - level 差 + if (!multiple && isSubTotal) { + let lowerLevelIndex = 1; + while (multiple < 1) { + multiple = + multipleMap[node.level - lowerLevelIndex] - lowerLevelIndex; + lowerLevelIndex++; + } + } + let res = 0; + for (let i = 0; i < multiple; i++) { + res += hierarchy.sampleNodesForAllLevels.find( + (sampleNode) => sampleNode.level === node.level + i, + )[key]; } + node[key] = res; }); } @@ -699,6 +679,7 @@ export class PivotFacet extends BaseFacet { col.isTotalMeasure || rowNode.isTotals || rowNode.isTotalMeasure, + totalStatus: getHeaderTotalStatus(rowNode, col), }); const cellDataKeys = keys(cellData); diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index 6fae391b5b..f3d48faad9 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -616,12 +616,12 @@ export abstract class SpreadSheet extends EE { ? totalConfig.showSubTotals : false; return { + label: i18n('总计'), + subLabel: i18n('小计'), + totalsGroupDimensions: [], + subTotalsGroupDimensions: [], + ...totalConfig, showSubTotals, - showGrandTotals: totalConfig.showGrandTotals, - reverseLayout: totalConfig.reverseLayout, - reverseSubLayout: totalConfig.reverseSubLayout, - label: totalConfig.label || i18n('总计'), - subLabel: totalConfig.subLabel || i18n('小计'), }; } diff --git a/packages/s2-core/src/utils/dataset/pivot-data-set.ts b/packages/s2-core/src/utils/dataset/pivot-data-set.ts index 85b3f7570f..02bcc254f3 100644 --- a/packages/s2-core/src/utils/dataset/pivot-data-set.ts +++ b/packages/s2-core/src/utils/dataset/pivot-data-set.ts @@ -8,15 +8,16 @@ import { reduce, set, } from 'lodash'; -import { i18n } from '../../common/i18n'; import { EXTRA_FIELD, ID_SEPARATOR, ROOT_ID } from '../../common/constant'; import type { DataPathParams, DataType, PivotMeta, SortedDimensionValues, + TotalStatus, } from '../../data-set/interface'; import type { Meta } from '../../common/interface/basic'; +import type { Node } from '../../facet/layout/node'; interface Param { rows: string[]; @@ -197,9 +198,7 @@ export function getDataPath(params: DataPathParams) { rowPivotMeta, colPivotMeta, ); - const result = rowPath.concat(...colPath); - - return result; + return rowPath.concat(...colPath); } /** @@ -344,3 +343,12 @@ export function generateExtraFieldMeta( return extraFieldMeta; } + +export function getHeaderTotalStatus(row: Node, col: Node): TotalStatus { + return { + isRowTotal: row.isGrandTotals, + isRowSubTotal: row.isSubTotals, + isColTotal: col.isGrandTotals, + isColSubTotal: col.isSubTotals, + }; +} diff --git a/packages/s2-core/src/utils/export/copy.ts b/packages/s2-core/src/utils/export/copy.ts index 0c691d353d..b2a11af36a 100644 --- a/packages/s2-core/src/utils/export/copy.ts +++ b/packages/s2-core/src/utils/export/copy.ts @@ -10,21 +10,20 @@ import { max, orderBy, reduce, - repeat, zip, } from 'lodash'; import type { ColCell, RowCell } from '../../cell'; import { + type CellMeta, CellTypes, CopyType, EMPTY_PLACEHOLDER, EXTRA_FIELD, ID_SEPARATOR, InteractionStateName, + type RowData, SERIES_NUMBER_FIELD, VALUE_FIELD, - type CellMeta, - type RowData, } from '../../common'; import type { DataType } from '../../data-set/interface'; import type { Node } from '../../facet/layout/node'; @@ -32,6 +31,7 @@ import type { SpreadSheet } from '../../sheet-type'; import { copyToClipboard } from '../../utils/export'; import { flattenDeep } from '../data-set-operate'; import { getEmptyPlaceholder } from '../text'; +import { getHeaderTotalStatus } from '../dataset/pivot-data-set'; export function keyEqualTo(key: string, compareKey: string) { if (!key || !compareKey) { @@ -115,6 +115,7 @@ const getValueFromMeta = ( rowNode.isTotalMeasure || colNode.isTotals || colNode.isTotalMeasure, + totalStatus: getHeaderTotalStatus(rowNode, colNode), }); return cell?.[VALUE_FIELD] ?? ''; } @@ -394,6 +395,7 @@ const getDataMatrix = ( rowNode.isTotalMeasure || colNode.isTotals || colNode.isTotalMeasure, + totalStatus: getHeaderTotalStatus(rowNode, colNode), }); return getFormat( colNode.colIndex, @@ -658,6 +660,27 @@ function getLastLevelCells( }); } +/** 处理有合并单元格的复制(小记总计格) + * 维度1 | 维度2 | 维度3 + * 总计 | 维度三 + * => 总计 总计 维度三 + */ +function getTotalCellMatrixId(meta: Node, maxLevel: number) { + let nextNode = meta; + let lastNode = { level: maxLevel }; + let cellId = nextNode.label; + while (nextNode.level >= 0) { + let repeatNumber = lastNode.level - nextNode.level; + while (repeatNumber > 0) { + cellId = `${nextNode.label}${ID_SEPARATOR}${cellId}`; + repeatNumber--; + } + lastNode = nextNode; + nextNode = nextNode.parent; + } + return cellId; +} + function getCellMatrix( lastLevelCells: Array, maxLevel: number, @@ -665,22 +688,19 @@ function getCellMatrix( ) { return map(lastLevelCells, (cell: RowCell | ColCell) => { const meta = cell.getMeta(); - const { id, label, isTotals, level } = meta; + const { id, label, isTotals } = meta; let cellId = id; - // 为总计小计补齐高度 - if (isTotals && level !== maxLevel) { - cellId = id + ID_SEPARATOR + repeat(label, maxLevel - level); + if (isTotals) { + cellId = getTotalCellMatrixId(meta, maxLevel); } // 将指标维度单元格的标签替换为实际文本 const actualText = cell.getActualText(); const headerList = getHeaderList(cellId, allLevel.size); - const formattedHeaderList = map(headerList, (header) => + return map(headerList, (header) => isEqual(header, label) ? actualText : header, ); - - return formattedHeaderList; }); } diff --git a/packages/s2-core/src/utils/facet.ts b/packages/s2-core/src/utils/facet.ts index 56377cdcf6..74db5444fd 100644 --- a/packages/s2-core/src/utils/facet.ts +++ b/packages/s2-core/src/utils/facet.ts @@ -1,16 +1,4 @@ import { findIndex } from 'lodash'; -import type { Node } from '../facet/layout/node'; - -export const getSubTotalNodeWidthOrHeightByLevel = ( - sampleNodesForAllLevels: Node[], - level: number, - key: 'width' | 'height', -) => { - return sampleNodesForAllLevels - .filter((node: Node) => node.level >= level) - .map((value) => value[key]) - .reduce((sum, current) => sum + current, 0); -}; /** * 根据视窗高度计算需要展示的数据数组下标 diff --git a/packages/s2-core/src/utils/layout/add-totals.ts b/packages/s2-core/src/utils/layout/add-totals.ts index c267da8c33..9b29207ca1 100644 --- a/packages/s2-core/src/utils/layout/add-totals.ts +++ b/packages/s2-core/src/utils/layout/add-totals.ts @@ -15,7 +15,12 @@ export const addTotals = (params: TotalParams) => { // check to see if grand total is added if (totalsConfig?.showGrandTotals) { action = totalsConfig.reverseLayout ? 'unshift' : 'push'; - totalValue = new TotalClass(totalsConfig.label, false, true); + totalValue = new TotalClass({ + label: totalsConfig.label, + isSubTotals: false, + isGrandTotals: true, + isTotalRoot: true, + }); } } else if ( /** @@ -29,7 +34,12 @@ export const addTotals = (params: TotalParams) => { currentField !== EXTRA_FIELD ) { action = totalsConfig.reverseSubLayout ? 'unshift' : 'push'; - totalValue = new TotalClass(totalsConfig.subLabel, true); + totalValue = new TotalClass({ + label: totalsConfig.subLabel, + isSubTotals: true, + isGrandTotals: false, + isTotalRoot: true, + }); } fieldValues[action]?.(totalValue); diff --git a/packages/s2-core/src/utils/layout/generate-header-nodes.ts b/packages/s2-core/src/utils/layout/generate-header-nodes.ts index 9a1db34d0f..402eb59120 100644 --- a/packages/s2-core/src/utils/layout/generate-header-nodes.ts +++ b/packages/s2-core/src/utils/layout/generate-header-nodes.ts @@ -1,4 +1,4 @@ -import { includes, isBoolean } from 'lodash'; +import { isBoolean } from 'lodash'; import { EXTRA_FIELD, SERIES_NUMBER_FIELD } from '../../common/constant'; import { i18n } from '../../common/i18n'; import { buildGridHierarchy } from '../../facet/layout/build-gird-hierarchy'; @@ -12,6 +12,7 @@ import { TotalClass } from '../../facet/layout/total-class'; import { TotalMeasure } from '../../facet/layout/total-measure'; import { generateId } from '../../utils/layout/generate-id'; import type { Columns } from '../../common'; +import { whetherLeafByLevel } from './whether-leaf-by-level'; export const generateHeaderNodes = (params: HeaderNodesParams) => { const { @@ -26,7 +27,7 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { addMeasureInTotalQuery, addTotalMeasureInTotal, } = params; - const { spreadsheet, collapsedCols, colCfg } = facetCfg; + const { spreadsheet, collapsedCols } = facetCfg; for (const [index, fieldValue] of fieldValues.entries()) { const isTotals = fieldValue instanceof TotalClass; @@ -36,32 +37,33 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { let isLeaf = false; let isGrandTotals = false; let isSubTotals = false; + let isTotalRoot = false; let adjustedField = currentField; if (isTotals) { const totalClass = fieldValue as TotalClass; isGrandTotals = totalClass.isGrandTotals; isSubTotals = totalClass.isSubTotals; + isTotalRoot = totalClass.isTotalRoot; value = i18n((fieldValue as TotalClass).label); - if (addMeasureInTotalQuery) { - // root[&]四川[&]总计 => {province: '四川', EXTRA_FIELD: 'price'} - nodeQuery = { - ...query, - [EXTRA_FIELD]: spreadsheet?.dataSet?.fields.values[0], - }; - isLeaf = true; + if (isTotalRoot) { + nodeQuery = query; } else { // root[&]四川[&]总计 => {province: '四川'} - nodeQuery = query; - if (!addTotalMeasureInTotal) { - isLeaf = true; - } + nodeQuery = { ...query, [currentField]: value }; } + if (addMeasureInTotalQuery) { + // root[&]四川[&]总计 => {province: '四川', EXTRA_FIELD: 'price'} + nodeQuery[EXTRA_FIELD] = spreadsheet?.dataSet?.fields.values[0]; + } + isLeaf = whetherLeafByLevel({ facetCfg, level, fields }); } else if (isTotalMeasure) { value = i18n((fieldValue as TotalMeasure).label); // root[&]四川[&]总计[&]price => {province: '四川',EXTRA_FIELD: 'price' } nodeQuery = { ...query, [EXTRA_FIELD]: value }; adjustedField = EXTRA_FIELD; - isLeaf = true; + isGrandTotals = parentNode.isGrandTotals; + isSubTotals = parentNode.isSubTotals; + isLeaf = whetherLeafByLevel({ facetCfg, level, fields }); } else if (spreadsheet.isTableMode()) { value = fieldValue; adjustedField = fields[index]; @@ -71,13 +73,7 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { value = fieldValue; // root[&]四川[&]成都 => {province: '四川', city: '成都' } nodeQuery = { ...query, [currentField]: value }; - const isValueInCols = spreadsheet.dataCfg.fields?.valueInCols ?? true; - const isHideMeasure = - colCfg?.hideMeasureColumn && - isValueInCols && - includes(fields, EXTRA_FIELD); - const extraSize = isHideMeasure ? 2 : 1; - isLeaf = level === fields.length - extraSize; + isLeaf = whetherLeafByLevel({ facetCfg, level, fields }); } const uniqueId = generateId(parentNode.id, value); if (!uniqueId) { @@ -95,11 +91,12 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { level, field: adjustedField, parent: parentNode, - isTotals, + isTotals: isTotals || isTotalMeasure, isGrandTotals, isSubTotals, isTotalMeasure, isCollapsed, + isTotalRoot, hierarchy, query: nodeQuery, spreadsheet, diff --git a/packages/s2-core/src/utils/layout/get-dims-condition-by-node.ts b/packages/s2-core/src/utils/layout/get-dims-condition-by-node.ts index 35befc837a..3bc44b3b31 100644 --- a/packages/s2-core/src/utils/layout/get-dims-condition-by-node.ts +++ b/packages/s2-core/src/utils/layout/get-dims-condition-by-node.ts @@ -8,7 +8,7 @@ export function getDimsCondition(parent: Node, force?: boolean) { * 当为表格布局时,小计行的内容是“小计”不需要作为筛选条件 * 当为树状布局时,force可以强行指定小计行,即父类目作为筛选条件 */ - if (!p.isTotals || force) { + if (!p.isTotalRoot || force) { cond[p.key] = p.value; } p = p.parent; diff --git a/packages/s2-core/src/utils/layout/whether-leaf-by-level.ts b/packages/s2-core/src/utils/layout/whether-leaf-by-level.ts new file mode 100644 index 0000000000..c03632b673 --- /dev/null +++ b/packages/s2-core/src/utils/layout/whether-leaf-by-level.ts @@ -0,0 +1,17 @@ +import { includes } from 'lodash'; +import type { WhetherLeafParams } from '../../facet/layout/interface'; +import { EXTRA_FIELD } from '../../common'; + +export const whetherLeafByLevel = (params: WhetherLeafParams) => { + const { facetCfg, level, fields } = params; + const { colCfg, spreadsheet, dataSet } = facetCfg; + const moreThanOneValue = dataSet.moreThanOneValue(); + const isValueInCols = spreadsheet.dataCfg.fields?.valueInCols ?? true; + const isHideMeasure = + colCfg?.hideMeasureColumn && + isValueInCols && + !moreThanOneValue && + includes(fields, EXTRA_FIELD); + const extraSize = isHideMeasure ? 2 : 1; + return level === fields.length - extraSize; +}; diff --git a/packages/s2-react/__tests__/unit/utils/__snapshots__/build-table-hierarchy-spec.tsx.snap b/packages/s2-react/__tests__/unit/utils/__snapshots__/build-table-hierarchy-spec.tsx.snap index fad5bec42a..36ce2b1f24 100644 --- a/packages/s2-react/__tests__/unit/utils/__snapshots__/build-table-hierarchy-spec.tsx.snap +++ b/packages/s2-react/__tests__/unit/utils/__snapshots__/build-table-hierarchy-spec.tsx.snap @@ -19,6 +19,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "$$series_number$$", "label": "序号", @@ -49,6 +50,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "province", "label": "省份", @@ -79,6 +81,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "city", "label": "城市", @@ -109,6 +112,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "type", "label": "类别", @@ -139,6 +143,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "sub_type", "label": "子类别", @@ -169,6 +174,7 @@ Object { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "number", "label": "数量", @@ -197,6 +203,7 @@ Object { "isPivotMode": undefined, "isSubTotals": undefined, "isTotalMeasure": undefined, + "isTotalRoot": undefined, "isTotals": undefined, "key": "", "label": "", @@ -230,6 +237,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "$$series_number$$", "label": "序号", @@ -260,6 +268,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "province", "label": "省份", @@ -290,6 +299,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "city", "label": "城市", @@ -320,6 +330,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "type", "label": "类别", @@ -350,6 +361,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "sub_type", "label": "子类别", @@ -380,6 +392,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "number", "label": "数量", @@ -413,6 +426,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "$$series_number$$", "label": "序号", @@ -443,6 +457,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "province", "label": "省份", @@ -473,6 +488,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "city", "label": "城市", @@ -503,6 +519,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "type", "label": "类别", @@ -533,6 +550,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "sub_type", "label": "子类别", @@ -563,6 +581,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "number", "label": "数量", @@ -595,6 +614,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "$$series_number$$", "label": "序号", @@ -626,6 +646,7 @@ Hierarchy { "isPivotMode": undefined, "isSubTotals": false, "isTotalMeasure": false, + "isTotalRoot": false, "isTotals": false, "key": "$$series_number$$", "label": "序号", diff --git a/s2-site/docs/api/basic-class/node.en.md b/s2-site/docs/api/basic-class/node.en.md index 374df5e90a..701f3ed5ea 100644 --- a/s2-site/docs/api/basic-class/node.en.md +++ b/s2-site/docs/api/basic-class/node.en.md @@ -5,7 +5,7 @@ order: 5 Function description: layout node. [details](https://github.com/antvis/S2/blob/master/packages/s2-core/src/facet/layout/node.ts) | parameter | illustrate | type | -| ----------------- | -------------------------------------- | --------------------------------------------------- | +|-------------------|----------------------------------------| --------------------------------------------------- | | id | node id | `string` | | key | node key | `string` | | value | node value | `string` | @@ -17,6 +17,7 @@ Function description: layout node. [details](https://github.com/antvis/S2/blob/m | isTotals | Is it summary | `boolean` | | isSubTotals | Is it a subtotal | `boolean` | | isGrandTotals | Is it total | `boolean` | +| isTotalRoot | Is it total root | `boolean` | | isCollapsed | Whether to expand | `boolean` | | hierarchy | hierarchical structure | [Hierarchy](#) | | isPivotMode | Is it a pivot table | `boolean` | diff --git a/s2-site/docs/api/basic-class/node.zh.md b/s2-site/docs/api/basic-class/node.zh.md index 31b90c3520..e1bc3044c8 100644 --- a/s2-site/docs/api/basic-class/node.zh.md +++ b/s2-site/docs/api/basic-class/node.zh.md @@ -9,34 +9,35 @@ order: 5 node.isTotals // false ``` -| 参数 | 说明 | 类型 | -| --- | --- | --- | -| id | 节点 id | `string` | -| key | 节点 key | `string` | -| value | 节点值 | `string` | -| label | 节点标题 | `string` | -| level | 节点等级 | `number` | -| rowIndex | 行头索引 | `number` | -| colIndex | 列头索引 | `number` | -| parent | 父节点 | [Node](/docs/api/basic-class/node) | -| isTotals | 是否是汇总 | `boolean` | -| isSubTotals | 是否是小计 | `boolean` | -| isGrandTotals | 是否是总计 | `boolean` | -| isCollapsed | 是否展开 | `boolean` | -| hierarchy | 层级结构 | [Hierarchy](#) | -| isPivotMode | 是否是透视表 | `boolean` | -| seriesNumberWidth | 序号宽度 | `number` | -| field | dataCfg 对应的 field | `string` | -| spreadsheet | 表格实例 | [SpreadSheet](/docs/api/basic-class/spreadsheet) | -| query | 当前节点对应的数据 | `Record` | -| belongsCell | 对应的单元格 | [S2CellType](/docs/api/basic-class/base-cell) | -| isTotalMeasure | 是否是数值小计 | `boolean` | -| inCollapseNode | 是否展开的节点 | `boolean` | -| isLeaf | 是否是叶子节点 | `boolean` | -| x | x 轴坐标 | `number` | -| y | y 轴坐标 | `number` | -| width | 宽度 | `number` | -| height | 高度 | `number` | -| padding | 间距 | `number` | -| hiddenChildNodeInfo | 隐藏的子节点信息 | [hiddenColumnsInfo](/api/basic-class/store#hiddencolumnsinfo) | -| children | 子节点 | [Node[]](/docs/api/basic-class/node) | +| 参数 | 说明 | 类型 | +|---------------------|-------------------| --- | +| id | 节点 id | `string` | +| key | 节点 key | `string` | +| value | 节点值 | `string` | +| label | 节点标题 | `string` | +| level | 节点等级 | `number` | +| rowIndex | 行头索引 | `number` | +| colIndex | 列头索引 | `number` | +| parent | 父节点 | [Node](/docs/api/basic-class/node) | +| isTotals | 是否是汇总 | `boolean` | +| isSubTotals | 是否是小计 | `boolean` | +| isGrandTotals | 是否是总计 | `boolean` | +| isCollapsed | 是否展开 | `boolean` | +| isTotalRoot | 是否是汇总结点的根节点 | `boolean` | +| hierarchy | 层级结构 | [Hierarchy](#) | +| isPivotMode | 是否是透视表 | `boolean` | +| seriesNumberWidth | 序号宽度 | `number` | +| field | dataCfg 对应的 field | `string` | +| spreadsheet | 表格实例 | [SpreadSheet](/docs/api/basic-class/spreadsheet) | +| query | 当前节点对应的数据 | `Record` | +| belongsCell | 对应的单元格 | [S2CellType](/docs/api/basic-class/base-cell) | +| isTotalMeasure | 是否是数值小计 | `boolean` | +| inCollapseNode | 是否展开的节点 | `boolean` | +| isLeaf | 是否是叶子节点 | `boolean` | +| x | x 轴坐标 | `number` | +| y | y 轴坐标 | `number` | +| width | 宽度 | `number` | +| height | 高度 | `number` | +| padding | 间距 | `number` | +| hiddenChildNodeInfo | 隐藏的子节点信息 | [hiddenColumnsInfo](/api/basic-class/store#hiddencolumnsinfo) | +| children | 子节点 | [Node[]](/docs/api/basic-class/node) | diff --git a/s2-site/docs/common/totals.en.md b/s2-site/docs/common/totals.en.md index e04c4f7bc0..d9b8df6b75 100644 --- a/s2-site/docs/common/totals.en.md +++ b/s2-site/docs/common/totals.en.md @@ -27,6 +27,8 @@ object is **required** , *default: null* Function description: subtotal total co | subLabel | subtotal alias | `string` | | | | calcTotals | Custom Calculated Totals | [CalcTotals](#calctotals) | | | | calcSubTotals | Custom Calculated Subtotals | [CalcTotals](#calctotals) | | | +| totalsGroupDimensions | grouping dimension of the total |`string[]` | | | +| subTotalsGroupDimensions | grouping dimension of the subtotal | `string[]` | | | ## CalcTotals diff --git a/s2-site/docs/common/totals.zh.md b/s2-site/docs/common/totals.zh.md index 6c27917856..6edacd116a 100644 --- a/s2-site/docs/common/totals.zh.md +++ b/s2-site/docs/common/totals.zh.md @@ -27,6 +27,8 @@ object **必选**,_default:null_ 功能描述: 小计总计配置 | subLabel | 小计别名 | `string` | | | | calcTotals | 自定义计算总计 | [CalcTotals](#calctotals) | | | | calcSubTotals | 自定义计算小计 | [CalcTotals](#calctotals) | | | +| totalsGroupDimensions | 总计的分组维度 |`string[]` | | | +| subTotalsGroupDimensions | 小计的分组维度 | `string[]` | | | ## CalcTotals diff --git a/s2-site/docs/manual/basic/totals.en.md b/s2-site/docs/manual/basic/totals.en.md index 37848725ab..805319532b 100644 --- a/s2-site/docs/manual/basic/totals.en.md +++ b/s2-site/docs/manual/basic/totals.en.md @@ -12,19 +12,21 @@ order: 5 object is **required** , *default: null* Function description: Subtotal calculation configuration -| parameter | illustrate | type | Defaults | required | | -| ------------------- |---------------------------| ------------ | --------------------- | -------- | - | -| showGrandTotals | Whether to display the total | `boolean` | false | ✓ | | -| showSubTotals | Whether to display subtotals. When configured as an object, always controls whether to always display -subtotals when there are less than 2 subdimensions, and does not display by default. | `boolean \| { always: boolean }` -| false | ✓ | | -| subTotalsDimensions | Summary Dimensions for Subtotals | `string[]` | [] | ✓ | | -| reverseLayout | total layout position, default bottom or right | `boolean` | false | ✓ | | -| reverseSubLayout | Subtotal layout position, default bottom or right | `boolean` | false | ✓ | | -| label | total alias | `string` | | | | -| subLabel | subtotal alias | `string` | | | | -| calcTotals | calculate the total | `CalcTotals` | | | | -| calcSubTotals | calculate subtotal | `CalcTotals` | | | | +| parameter | illustrate | type | Defaults | required | | +|--------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|--------------|----------|----------|-----| +| showGrandTotals | Whether to display the total | `boolean` | false | ✓ | | +| showSubTotals | Whether to display subtotals. When configured as an object, always controls whether to always display | | | | | +| subtotals when there are less than 2 subdimensions, and does not display by default. | `boolean \| { always: boolean }` | | | | | +| false | ✓ | | | | | +| subTotalsDimensions | Summary Dimensions for Subtotals | `string[]` | [] | ✓ | | +| reverseLayout | total layout position, default bottom or right | `boolean` | false | ✓ | | +| reverseSubLayout | Subtotal layout position, default bottom or right | `boolean` | false | ✓ | | +| label | total alias | `string` | | | | +| subLabel | subtotal alias | `string` | | | | +| calcTotals | calculate the total | `CalcTotals` | | | | +| calcSubTotals | calculate subtotal | `CalcTotals` | | | | +| totalsGroupDimensions | grouping dimension of the total |`string[]` | | | +| subTotalsGroupDimensions | grouping dimension of the subtotal | `string[]` | | | ```typescript const s2Options = { @@ -35,6 +37,8 @@ subtotals when there are less than 2 subdimensions, and does not display by defa reverseLayout: true, reverseSubLayout: true, subTotalsDimensions: [ 'province' ], + totalsGroupDimensions: ['city'], + subTotalsGroupDimensions: ['type', 'sub_type'], }, col: { showGrandTotals: true, diff --git a/s2-site/docs/manual/basic/totals.zh.md b/s2-site/docs/manual/basic/totals.zh.md index 0821574acb..cffaf101cb 100644 --- a/s2-site/docs/manual/basic/totals.zh.md +++ b/s2-site/docs/manual/basic/totals.zh.md @@ -47,6 +47,22 @@ order: 5 row +### 分组汇总 + +按维度进行 小计/总计 的汇总计算,用于进行某一维度的数据对比分析等。 + + + +#### 行总计小计分组 + +行总计按 “类别” 分组,行小计按 “类别”,“子类别” 分组: + +row + +#### 列总计小计分组 + +col + ## 使用 ### 1. 显示配置 @@ -66,17 +82,19 @@ object **必选**,_default:null_ 功能描述: 小计总计配置 object **必选**,_default:null_ 功能描述: 小计总计算配置 -| 参数 | 说明 | 类型 | 默认值 | 必选 | -| ------------------- | ------------------------ | ------------ | ------ | ---- | -| showGrandTotals | 是否显示总计 | `boolean` | false | ✓ | -| showSubTotals | 是否显示小计。当配置为对象时,always 控制是否在子维度不足 2 个时始终展示小计,默认不展示。 | `boolean | { always: boolean }` | false | ✓ | -| subTotalsDimensions | 小计的汇总维度 | `string[]` | [] | ✓ | -| reverseLayout | 总计布局位置,默认下或右 | `boolean` | false | ✓ | -| reverseSubLayout | 小计布局位置,默认下或右 | `boolean` | false | ✓ | -| label | 总计别名 | `string` | | | -| subLabel | 小计别名 | `string` | | | -| calcTotals | 计算总计 | `CalcTotals` | | | -| calcSubTotals | 计算小计 | `CalcTotals` | | | +| 参数 | 说明 | 类型 | 默认值 | 必选 | +|----------------------------------------|----------------------------------------------------|--------------|--------------------| ---- | +| showGrandTotals | 是否显示总计 | `boolean` | false | ✓ | +| showSubTotals | 是否显示小计。当配置为对象时,always 控制是否在子维度不足 2 个时始终展示小计,默认不展示。 | `boolean | { always: boolean }` | false | ✓ | +| subTotalsDimensions | 小计的汇总维度 | `string[]` | [] | ✓ | +| reverseLayout | 总计布局位置,默认下或右 | `boolean` | false | ✓ | +| reverseSubLayout | 小计布局位置,默认下或右 | `boolean` | false | ✓ | +| label | 总计别名 | `string` | | | +| subLabel | 小计别名 | `string` | | | +| calcTotals | 计算总计 | `CalcTotals` | | | +| calcSubTotals | 计算小计 | `CalcTotals` | | | +| totalsGroupDimensions | 总计的分组维度 |`string[]` | | | +| subTotalsGroupDimensions | 小计的分组维度 | `string[]` | | | ```ts const s2Options = { @@ -87,6 +105,8 @@ const s2Options = { reverseLayout: true, reverseSubLayout: true, subTotalsDimensions: ['province'], + totalsGroupDimensions: ['city'], + subTotalsGroupDimensions: ['type', 'sub_type'], }, col: { showGrandTotals: true, diff --git a/s2-site/examples/analysis/totals/demo/dimension-group-col.ts b/s2-site/examples/analysis/totals/demo/dimension-group-col.ts new file mode 100644 index 0000000000..89bd78473a --- /dev/null +++ b/s2-site/examples/analysis/totals/demo/dimension-group-col.ts @@ -0,0 +1,68 @@ +import { PivotSheet } from '@antv/s2'; + +fetch('https://gw.alipayobjects.com/os/bmw-prod/6eede6eb-8021-4da8-bb12-67891a5705b7.json') + .then((res) => res.json()) + .then((data) => { + const container = document.getElementById('container'); + const s2DataConfig = { + fields: { + rows: [], + columns: ['province', 'city', 'type'], + values: ['price' ,'cost'], + valueInCols: false, + }, + meta: [ + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '商品类别', + }, + { + field: 'price', + name: '价格', + }, + { + field: 'cost', + name: '成本', + }, + ], + data, + }; + + const s2Options = { + width: 600, + height: 480, + // 配置行小计总计显示,且按维度分组(列小计总计同理) + totals: { + col: { + showGrandTotals: true, + showSubTotals: true, + reverseLayout: true, + reverseSubLayout: true, + subTotalsDimensions: ['province'], + calcTotals: { + // 设置总计汇总计算方式为求和 + aggregation: 'SUM', + }, + calcSubTotals: { + // 设置小计汇总计算方式为求和 + aggregation: 'SUM', + }, + // 总计分组下,city 城市维度会出现分组 + totalsGroupDimensions: ['city'], + // 小计维度下,type 类别维度下会出现分组 + subTotalsGroupDimensions: ['type'], + }, + }, + }; + const s2 = new PivotSheet(container, s2DataConfig, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/analysis/totals/demo/dimension-group-row.ts b/s2-site/examples/analysis/totals/demo/dimension-group-row.ts new file mode 100644 index 0000000000..e12518827e --- /dev/null +++ b/s2-site/examples/analysis/totals/demo/dimension-group-row.ts @@ -0,0 +1,67 @@ +import { PivotSheet } from '@antv/s2'; + +fetch('https://gw.alipayobjects.com/os/bmw-prod/6eede6eb-8021-4da8-bb12-67891a5705b7.json') + .then((res) => res.json()) + .then((data) => { + const container = document.getElementById('container'); + const s2DataConfig = { + fields: { + rows: ['province', 'city', 'type'], + columns: [], + values: ['price' ,'cost'], + }, + meta: [ + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '商品类别', + }, + { + field: 'price', + name: '价格', + }, + { + field: 'cost', + name: '成本', + }, + ], + data, + }; + + const s2Options = { + width: 600, + height: 480, + // 配置行小计总计显示,且按维度分组(列小计总计同理) + totals: { + row: { + showGrandTotals: true, + showSubTotals: true, + reverseLayout: true, + reverseSubLayout: true, + subTotalsDimensions: ['province'], + calcTotals: { + // 设置总计汇总计算方式为求和 + aggregation: 'SUM', + }, + calcSubTotals: { + // 设置小计汇总计算方式为求和 + aggregation: 'SUM', + }, + // 总计分组下,city 城市维度会出现分组 + totalsGroupDimensions: ['city'], + // 小计维度下,type 类别维度下会出现分组 + subTotalsGroupDimensions: ['type'], + }, + }, + }; + const s2 = new PivotSheet(container, s2DataConfig, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/analysis/totals/demo/meta.json b/s2-site/examples/analysis/totals/demo/meta.json index d751e58992..12ee0b59ab 100644 --- a/s2-site/examples/analysis/totals/demo/meta.json +++ b/s2-site/examples/analysis/totals/demo/meta.json @@ -20,6 +20,22 @@ }, "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/3SuoQrkTsR/5657d02f-8e7f-4fbc-8159-7a0e8f772462.png" }, + { + "filename": "dimension-group-row.ts", + "title": { + "zh": "行总计小计按维度分组", + "en": "Total Of Rows Grouped By Dimension" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*1SDsRpTA_kQAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "dimension-group-col.ts", + "title": { + "zh": "列总计小计按维度分组", + "en": "Total Of Columns Grouped By Dimension" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*5PTqQpXXCcsAAAAAAAAAAAAADmJ7AQ/original" + }, { "filename": "tree.ts", "title": { From 406d17e4481f7a252f094da54997e16b833c24c8 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 22 Sep 2023 19:46:41 +0800 Subject: [PATCH 02/18] =?UTF-8?q?chore:=20=F0=9F=A4=96=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20changelog=20=E6=96=87=E4=BB=B6=20(#2356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/CHANGELOG.md | 8 ++++++++ packages/s2-core/package.json | 2 +- packages/s2-react/CHANGELOG.md | 8 ++++++++ packages/s2-react/package.json | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/s2-core/CHANGELOG.md b/packages/s2-core/CHANGELOG.md index b7c074f82d..c1c342e399 100644 --- a/packages/s2-core/CHANGELOG.md +++ b/packages/s2-core/CHANGELOG.md @@ -1,3 +1,11 @@ +# [@antv/s2-v1.51.0](https://github.com/antvis/S2/compare/@antv/s2-v1.50.0...@antv/s2-v1.51.0) (2023-09-22) + + +### Features + +* 对比值无波动时也显示灰色 ([#2351](https://github.com/antvis/S2/issues/2351)) ([12f2d02](https://github.com/antvis/S2/commit/12f2d0268d447ec99a1227ffedd5ed266d93e86b)) +* 小计/总计功能,支持按维度分组汇总 ([#2346](https://github.com/antvis/S2/issues/2346)) ([20e608f](https://github.com/antvis/S2/commit/20e608f2447e9ffdb135d98e4cc7f39f1cfb308d)) + # [@antv/s2-v1.50.0](https://github.com/antvis/S2/compare/@antv/s2-v1.49.2...@antv/s2-v1.50.0) (2023-09-09) diff --git a/packages/s2-core/package.json b/packages/s2-core/package.json index 6337125605..d818a75808 100644 --- a/packages/s2-core/package.json +++ b/packages/s2-core/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@antv/s2", - "version": "1.50.0", + "version": "1.51.0", "main": "lib/index.js", "unpkg": "dist/index.min.js", "module": "esm/index.js", diff --git a/packages/s2-react/CHANGELOG.md b/packages/s2-react/CHANGELOG.md index 7c49b535a6..b0a6bd9ed3 100644 --- a/packages/s2-react/CHANGELOG.md +++ b/packages/s2-react/CHANGELOG.md @@ -1,3 +1,11 @@ +# [@antv/s2-react-v1.44.0](https://github.com/antvis/S2/compare/@antv/s2-react-v1.43.0...@antv/s2-react-v1.44.0) (2023-09-22) + + +### Features + +* 对比值无波动时也显示灰色 ([#2351](https://github.com/antvis/S2/issues/2351)) ([12f2d02](https://github.com/antvis/S2/commit/12f2d0268d447ec99a1227ffedd5ed266d93e86b)) +* 小计/总计功能,支持按维度分组汇总 ([#2346](https://github.com/antvis/S2/issues/2346)) ([20e608f](https://github.com/antvis/S2/commit/20e608f2447e9ffdb135d98e4cc7f39f1cfb308d)) + # [@antv/s2-react-v1.43.0](https://github.com/antvis/S2/compare/@antv/s2-react-v1.42.1...@antv/s2-react-v1.43.0) (2023-09-09) diff --git a/packages/s2-react/package.json b/packages/s2-react/package.json index 2337ce8698..202ab32399 100644 --- a/packages/s2-react/package.json +++ b/packages/s2-react/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@antv/s2-react", - "version": "1.43.0", + "version": "1.44.0", "main": "lib/index.js", "unpkg": "dist/index.min.js", "module": "esm/index.js", From c8d2c94c21527ae52ece3485524d374d09b1cba7 Mon Sep 17 00:00:00 2001 From: NoobNot <56724970+NoobNotN@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:15:45 +0800 Subject: [PATCH 03/18] =?UTF-8?q?fix(layout):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E7=BB=93=E7=82=B9=E6=97=B6=E5=AF=B9=E7=88=B6?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=B8=83=E5=B1=80=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E9=94=99=E8=AF=AF=20close=20#2355=20(#2360)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(layout): 修复隐藏结点时对父节点的布局计算错误 close #2355 * chore: 增加废弃标记 --------- Co-authored-by: JuZe --- .../spreadsheet/hidden-columns-spec.ts | 28 +++++++++++++++++++ packages/s2-core/src/facet/layout/node.ts | 3 ++ packages/s2-core/src/facet/pivot-facet.ts | 4 +-- .../src/utils/layout/generate-header-nodes.ts | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/hidden-columns-spec.ts b/packages/s2-core/__tests__/spreadsheet/hidden-columns-spec.ts index b6c4a11dbd..7aecda2f1d 100644 --- a/packages/s2-core/__tests__/spreadsheet/hidden-columns-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/hidden-columns-spec.ts @@ -347,6 +347,34 @@ describe('SpreadSheet Hidden Columns Tests', () => { expect(hiddenColumnsInfo).toBeTruthy(); expect(parentNode.hiddenChildNodeInfo).toEqual(hiddenColumnsInfo); }); + // https://github.com/antvis/S2/issues/2355 + test('should render correctly x and width after hide columns when there is only one value for the higher-level dimension.', () => { + const nodeId = 'root[&]笔[&]义乌[&]price'; + + pivotSheet.setOptions({ + style: { + colCfg: { + width: 100, + }, + }, + }); + const data = pivotSheet.dataCfg.data.map((i) => ({ ...i, cost: 0 })); + pivotSheet.setDataCfg({ + data, + fields: { + values: ['cost', 'price'], + }, + }); + pivotSheet.render(); + + pivotSheet.interaction.hideColumns([nodeId]); + const rootNode = pivotSheet + .getColumnNodes() + .find((node) => node.id === 'root[&]笔'); + + expect(rootNode.width).toEqual(300); + expect(rootNode.x).toEqual(0); + }); // https://github.com/antvis/S2/issues/2194 test('should render correctly when always hidden last column', () => { diff --git a/packages/s2-core/src/facet/layout/node.ts b/packages/s2-core/src/facet/layout/node.ts index cfc09d5466..e9e7ee0c0f 100644 --- a/packages/s2-core/src/facet/layout/node.ts +++ b/packages/s2-core/src/facet/layout/node.ts @@ -306,6 +306,9 @@ export class Node { public isTotalRoot?: boolean; + /** + * @deprecated 已废弃, 该属性只记录相邻一级的隐藏信息,将会在未来版本中移除 + */ public hiddenChildNodeInfo?: HiddenColumnsInfo | null; public extra?: Record; diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index 3b432f44b5..c5da151310 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -252,9 +252,9 @@ export class PivotFacet extends BaseFacet { leafNodes.push(parentNode); const firstVisibleChildNode = parentNode.children?.find( - (childNode) => !childNode.hiddenChildNodeInfo, + (childNode) => childNode.width, ); - // 父节点 x 坐标 = 第一个未隐藏的子节点的 x 坐标 + // 父节点 x 坐标 = 第一个正常布局处理过的子节点 x 坐标(width 有值认为是正常布局过) const parentNodeX = firstVisibleChildNode?.x; // 父节点宽度 = 所有子节点宽度之和 const parentNodeWidth = sumBy(parentNode.children, 'width'); diff --git a/packages/s2-core/src/utils/layout/generate-header-nodes.ts b/packages/s2-core/src/utils/layout/generate-header-nodes.ts index 402eb59120..aa33a7bf42 100644 --- a/packages/s2-core/src/utils/layout/generate-header-nodes.ts +++ b/packages/s2-core/src/utils/layout/generate-header-nodes.ts @@ -113,6 +113,7 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { // 如果当前是隐藏节点, 给其父节点挂载相应信息 (兄弟节点, 当前哪个子节点隐藏了), 这样在 facet 层可以直接使用, 不用每次都去遍历 const hiddenColumnsInfo = spreadsheet?.facet?.getHiddenColumnsInfo(node); if (hiddenColumnsInfo && parentNode) { + // hiddenChildNodeInfo 属性在 S2 中没有用到,但是没删怕外部有使用,已标记为废弃 parentNode.hiddenChildNodeInfo = hiddenColumnsInfo; } From b719c8adbb2cbba8635686aa6d77ff0e52e15b56 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Wed, 11 Oct 2023 17:44:37 +0800 Subject: [PATCH 04/18] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=20headerAction?= =?UTF-8?q?Icons=20=E9=94=99=E8=AF=AF=E6=96=87=E6=A1=A3=20close=20#2362=20?= =?UTF-8?q?(#2363)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s2-site/docs/common/header-action-icon.zh.md | 18 +++++++++--------- s2-site/docs/common/icon.zh.md | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/s2-site/docs/common/header-action-icon.zh.md b/s2-site/docs/common/header-action-icon.zh.md index bac525cb2b..e192e145e8 100644 --- a/s2-site/docs/common/header-action-icon.zh.md +++ b/s2-site/docs/common/header-action-icon.zh.md @@ -7,12 +7,12 @@ | 参数 | 说明 | 类型 | 默认值 | 必选 | 取值 | 版本 | | ---------------- | ----------- | ----------- | ------ | ---- | ----------- | --- | | iconNames | 已经注册的 icon 名称,或用户通过 customSVGIcons 注册的 icon 名称 | `string[]` | | ✓ | | | -| belongsCell | 需要增加操作图标的单元格名称 | `string[]` | | ✓ | 角头:'cornerCell';
列头:'colCell';
行头:'rowCell' | | -| defaultHide | 控制是否 hover 才展示 icon | `boolean | (meta: Node, iconName: string) => boolean` | false | | true | `1.26.0` 支持配置为一个函数 | -| displayCondition | 展示的过滤条件,可以通过该回调函数用户自定义行列头哪些层级或单元格需要展示 icon。 所有返回值为 true 的 icon 会展示给用户。 | `(mete: Node, iconName: string) => boolean;` | | | | `1.26.0` 回传 `iconName` 并按单个 icon 控制显隐 | -| action | icon 点击之后的执行函数 | `(headerActionIconProps: HeaderActionIconProps) => void;` | | | | 已废弃,请使用 `onClick` | -| onClick | icon 点击之后的执行函数 | `(headerIconClickParams: HeaderIconClickParams) => void;` | | | | `1.26.0` | -| onHover | icon hover 开始及结束之后的执行函数 | `(headerIconHoverParams: HeaderIconHoverParams) => void;` | | | | `1.26.0` | +| belongsCell | 需要增加操作图标的单元格名称 | `string` | | ✓ | 角头:`cornerCell`;
列头:`colCell`;
行头:`rowCell` | | +| defaultHide | 控制是否 hover 在对应单元格时才展示 icon, 默认始终展示 | `boolean \| (meta: Node, iconName: string) => boolean` | false | | | `1.26.0` 支持配置为一个函数 | +| displayCondition | 自定义展示条件,可根据当前单元格信息动态控制 icon 是否展示 | `(mete: Node, iconName: string) => boolean` | | | | `1.26.0` 回传 `iconName` 并按单个 icon 控制显隐 | +| action | icon 点击之后的执行函数 (已废弃,请使用 `onClick`) | `(headerActionIconProps: HeaderActionIconProps) => void` | | | | | +| onClick | icon 点击之后的执行函数 | `(headerIconClickParams: HeaderIconClickParams) => void` | | | | `1.26.0` | +| onHover | icon hover 开始及结束之后的执行函数 | `(headerIconHoverParams: HeaderIconHoverParams) => void` | | | | `1.26.0` | ​ @@ -24,9 +24,9 @@ | 参数 | 功能描述 | 类型 | 默认值 | 必选 | | --- | --- | --- | --- | --- | -| iconName | 当前点击的 icon 名称 | string | | ✓ | -| meta |当前 cell 的 meta 信息| Node | | ✓ | -| event |当前点击事件信息| Event |false| ✓ | +| iconName | 当前 icon 名称 | string | | ✓ | +| meta |当前 cell 的 meta 信息| [Node](/api/basic-class/node) | | ✓ | +| event |当前点击事件信息| Event | false | ✓ | ## CustomSVGIcon diff --git a/s2-site/docs/common/icon.zh.md b/s2-site/docs/common/icon.zh.md index 58a8b63450..9838273b7a 100644 --- a/s2-site/docs/common/icon.zh.md +++ b/s2-site/docs/common/icon.zh.md @@ -7,12 +7,12 @@ order: 3 | icon 名称 | icon 图标 | 功能描述 | icon 名称 | icon 图标 | 功能描述 | | ------------- | --------------------- | ---------- | ---------------- | ----------- | ------------------ | -| CellDown | icon | 同环比下降 | ExpandColIcon | icon | 明细表隐藏展开 | +| CellDown | icon | 同环比下降 | ExpandColIcon | icon | 展开列头 | | CellUp | icon | 同环比上升 | Plus | icon | 树状表格展开 | | GlobalAsc | icon | 全局升序 | Minus | icon | 树状表格收起 | -| GlobalDesc | icon | 全局降序 | SortDown | icon | 明细表降序 | -| GroupAsc | icon | 组内升序 | SortDownSelected | icon | 明细表降序选择状态 | -| GroupDesc | icon | 组内降序 | SortUp | icon | 明细表升序 | -| Trend | icon | 趋势图 | SortUpSelected | icon | 明细表升序选择状态 | +| GlobalDesc | icon | 全局降序 | SortDown | icon | 降序 | +| GroupAsc | icon | 组内升序 | SortDownSelected | icon | 降序选中状态 | +| GroupDesc | icon | 组内降序 | SortUp | icon | 升序 | +| Trend | icon | 趋势图 | SortUpSelected | icon | 升序选中状态 | | ArrowUp | icon | 指标上升 |ArrowDown | icon | 指标下降 | | DrillDownIcon | icon | 下钻 | | | | From 7751c4968b69752243928cbcd2c50333b06b2c66 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Wed, 11 Oct 2023 20:04:59 +0800 Subject: [PATCH 05/18] =?UTF-8?q?fix(table-sheet):=20=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E8=A1=A8=E6=95=B0=E6=8D=AE=E4=B8=BA=E7=A9=BA=E6=97=B6=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E5=B1=95=E7=A4=BA=E4=B8=80=E8=A1=8C=E7=A9=BA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20close=20#2255=20(#2357)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(table-sheet): 明细表数据为空时错误的展示一行空数据 close #2255 * test: 修复测试 * test: 修复测试 * fix: 重命名 --------- Co-authored-by: Wenjun Xu <906626481@qq.com> --- .../unit/data-set/pivot-data-set-spec.ts | 4 ++++ .../unit/data-set/table-data-set-spec.ts | 4 ++++ .../__tests__/unit/facet/table-facet-spec.ts | 6 ++++++ packages/s2-core/src/cell/table-col-cell.ts | 1 + .../s2-core/src/data-set/base-data-set.ts | 5 +++++ packages/s2-core/src/facet/table-facet.ts | 21 +++++++++++-------- .../s2-core/src/sheet-type/table-sheet.ts | 1 + .../docs/api/basic-class/base-data-set.zh.md | 1 + 8 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/s2-core/__tests__/unit/data-set/pivot-data-set-spec.ts b/packages/s2-core/__tests__/unit/data-set/pivot-data-set-spec.ts index 2206b0f4b4..ab9f72ce28 100644 --- a/packages/s2-core/__tests__/unit/data-set/pivot-data-set-spec.ts +++ b/packages/s2-core/__tests__/unit/data-set/pivot-data-set-spec.ts @@ -158,6 +158,10 @@ describe('Pivot Dataset Test', () => { getDimensionsWithoutPathPre(sortedDimensionValues[EXTRA_FIELD]), ).toEqual(['number', 'number', 'number', 'number']); }); + + test('should get correctly empty dataset result', () => { + expect(dataSet.isEmpty()).toBeFalsy(); + }); }); describe('test for query data', () => { diff --git a/packages/s2-core/__tests__/unit/data-set/table-data-set-spec.ts b/packages/s2-core/__tests__/unit/data-set/table-data-set-spec.ts index 4da12e138b..8e990596c3 100644 --- a/packages/s2-core/__tests__/unit/data-set/table-data-set-spec.ts +++ b/packages/s2-core/__tests__/unit/data-set/table-data-set-spec.ts @@ -99,6 +99,10 @@ describe('Table Mode Dataset Test', () => { test('should get correct meta data', () => { expect(dataSet.meta).toEqual(expect.objectContaining([])); }); + + test('should get correctly empty dataset result', () => { + expect(dataSet.isEmpty()).toBeFalsy(); + }); }); describe('test for query data', () => { diff --git a/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts b/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts index 428e07a9fa..ad72e6226a 100644 --- a/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts +++ b/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts @@ -13,9 +13,11 @@ import { DataCell, DEFAULT_STYLE, type Fields, Node } from '@/index'; import { getFrozenLeafNodesCount } from '@/facet/utils'; import { SpreadSheet } from '@/sheet-type'; import { getTheme } from '@/theme'; + const actualDataSet = jest.requireActual( '@/data-set/base-data-set', ).BaseDataSet; + jest.mock('@/sheet-type', () => { const container = new Canvas({ width: 100, @@ -59,6 +61,9 @@ jest.mock('@/sheet-type', () => { interaction: { clearHoverTimer: jest.fn(), }, + dataSet: { + isEmpty: jest.fn(), + }, }; }), }; @@ -77,6 +82,7 @@ jest.mock('@/data-set/table-data-set', () => { getCellData: () => 1, getFieldMeta: jest.fn(), getFieldFormatter: actualDataSet.prototype.getFieldFormatter, + isEmpty: jest.fn(), }; }), }; diff --git a/packages/s2-core/src/cell/table-col-cell.ts b/packages/s2-core/src/cell/table-col-cell.ts index 3368f9ffd6..be1c6a04c4 100644 --- a/packages/s2-core/src/cell/table-col-cell.ts +++ b/packages/s2-core/src/cell/table-col-cell.ts @@ -77,6 +77,7 @@ export class TableColCell extends ColCell { y, }; } + return { x: position.x + x - scrollX, y: position.y + y, diff --git a/packages/s2-core/src/data-set/base-data-set.ts b/packages/s2-core/src/data-set/base-data-set.ts index 1c95f5e736..92ffb6ae8e 100644 --- a/packages/s2-core/src/data-set/base-data-set.ts +++ b/packages/s2-core/src/data-set/base-data-set.ts @@ -3,6 +3,7 @@ import { find, get, identity, + isEmpty, isNil, map, max, @@ -117,6 +118,10 @@ export abstract class BaseDataSet { return this.displayData; } + public isEmpty() { + return isEmpty(this.getDisplayDataSet()); + } + public getValueRangeByField(field: string): ValueRange { const cacheRange = getValueRangeState(this.spreadsheet, field); if (cacheRange) { diff --git a/packages/s2-core/src/facet/table-facet.ts b/packages/s2-core/src/facet/table-facet.ts index 0cd0f14ed2..e1608eb241 100644 --- a/packages/s2-core/src/facet/table-facet.ts +++ b/packages/s2-core/src/facet/table-facet.ts @@ -43,7 +43,7 @@ import { getFrozenRowsForGrid, getRowsForGrid, } from '../utils/grid'; -import type { PanelIndexes } from '../utils/indexes'; +import type { Indexes, PanelIndexes } from '../utils/indexes'; import { getValidFrozenOptions } from '../utils/layout/frozen'; import { BaseFacet } from './base-facet'; import { CornerBBox } from './bbox/cornerBBox'; @@ -1129,14 +1129,17 @@ export class TableFacet extends BaseFacet { } } - const indexes = calculateInViewIndexes( - scrollX, - scrollY, - this.viewCellWidths, - this.viewCellHeights, - finalViewport, - this.getRealScrollX(this.cornerBBox.width), - ); + // https://github.com/antvis/S2/issues/2255 + const indexes = this.spreadsheet.dataSet.isEmpty() + ? ([] as unknown as Indexes) + : calculateInViewIndexes( + scrollX, + scrollY, + this.viewCellWidths, + this.viewCellHeights, + finalViewport, + this.getRealScrollX(this.cornerBBox.width), + ); this.panelScrollGroupIndexes = indexes; diff --git a/packages/s2-core/src/sheet-type/table-sheet.ts b/packages/s2-core/src/sheet-type/table-sheet.ts index 3612f9d960..21f372f269 100644 --- a/packages/s2-core/src/sheet-type/table-sheet.ts +++ b/packages/s2-core/src/sheet-type/table-sheet.ts @@ -123,6 +123,7 @@ export class TableSheet extends SpreadSheet { } return new TableDataCell(facet, this); }; + return { ...this.options, ...fields, diff --git a/s2-site/docs/api/basic-class/base-data-set.zh.md b/s2-site/docs/api/basic-class/base-data-set.zh.md index 3b8523fc5a..b259e92ced 100644 --- a/s2-site/docs/api/basic-class/base-data-set.zh.md +++ b/s2-site/docs/api/basic-class/base-data-set.zh.md @@ -28,6 +28,7 @@ s2.dataSet.getFieldName('type') | getCellData | 获取单个的单元格数据 | (params: [CellDataParams](#celldataparams)) => [DataType[]](#datatype) | | | getMultiData | 获取批量的单元格数据 | (query: [DataType](#datatype), isTotals?: boolean, isRow?: boolean, drillDownFields?: string[]) => [DataType[]](#datatype) | | | moreThanOneValue | 是否超过 1 个数值 | () => [ViewMeta](#viewmeta) | | +| isEmpty | 是否为空数据集 | () => `boolean` | `@antv/s2-v1.51.1` | ### DataType From 4953b4e430f3c1857ce6648bcdc2493c37bb1092 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 13 Oct 2023 17:25:16 +0800 Subject: [PATCH 06/18] =?UTF-8?q?fix:=20=E5=88=97=E5=A4=B4=E7=BB=98?= =?UTF-8?q?=E5=88=B6=E5=A4=9A=E5=88=97=E6=96=87=E6=9C=AC=E6=97=B6=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E4=BD=BF=E7=94=A8=E4=BA=86=E6=95=B0=E5=80=BC?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=A0=BC=E7=9A=84=E6=A0=B7=E5=BC=8F=20close?= =?UTF-8?q?=20#2359=20(#2364)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-core/__tests__/bugs/issue-2359-spec.ts | 140 ++++++++++++++++++ packages/s2-core/src/utils/text.ts | 4 +- 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 packages/s2-core/__tests__/bugs/issue-2359-spec.ts diff --git a/packages/s2-core/__tests__/bugs/issue-2359-spec.ts b/packages/s2-core/__tests__/bugs/issue-2359-spec.ts new file mode 100644 index 0000000000..04079ebc2a --- /dev/null +++ b/packages/s2-core/__tests__/bugs/issue-2359-spec.ts @@ -0,0 +1,140 @@ +/** + * @description spec for issue #2359 + * https://github.com/antvis/S2/issues/2359 + * 明细表: 自定义列头误用 dataCell 样式 + */ +import { pick } from 'lodash'; +import { createTableSheet } from 'tests/util/helpers'; +import { TableColCell, drawObjectText } from '../../src'; +import type { S2CellType, S2Options } from '@/common/interface'; + +class TestColCell extends TableColCell { + drawTextShape() { + drawObjectText(this, { + values: [['A', 'B', 'C']], + }); + } +} + +const s2Options: S2Options = { + width: 300, + height: 480, + showSeriesNumber: true, + colCell: (...args) => new TestColCell(...args), +}; + +describe('Table Sheet Custom Multiple Values Tests', () => { + test('should use current cell text theme', () => { + const s2 = createTableSheet(s2Options); + + s2.setTheme({ + colCell: { + measureText: { + fontSize: 12, + }, + bolderText: { + fontSize: 14, + }, + text: { + fontSize: 20, + fill: 'red', + }, + }, + dataCell: { + text: { + fontSize: 30, + fill: 'green', + }, + }, + }); + s2.render(); + + const mapTheme = (cell: S2CellType) => { + return cell + .getTextShapes() + .map((shape) => pick(shape.attr(), ['fill', 'fontSize'])); + }; + + const colCellTexts = s2 + .getColumnNodes() + .map((node) => mapTheme(node.belongsCell)); + + const dataCellTexts = s2.interaction + .getPanelGroupAllDataCells() + .map(mapTheme); + + expect(colCellTexts).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "fill": "red", + "fontSize": 20, + }, + Object { + "fill": "red", + "fontSize": 20, + }, + Object { + "fill": "red", + "fontSize": 20, + }, + ], + Array [ + Object { + "fill": "red", + "fontSize": 20, + }, + Object { + "fill": "red", + "fontSize": 20, + }, + Object { + "fill": "red", + "fontSize": 20, + }, + ], + ] + `); + + expect(dataCellTexts).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "fill": "#000000", + "fontSize": 12, + }, + ], + Array [ + Object { + "fill": "#000000", + "fontSize": 12, + }, + ], + Array [ + Object { + "fill": "#000000", + "fontSize": 12, + }, + ], + Array [ + Object { + "fill": "green", + "fontSize": 30, + }, + ], + Array [ + Object { + "fill": "green", + "fontSize": 30, + }, + ], + Array [ + Object { + "fill": "green", + "fontSize": 30, + }, + ], + ] + `); + }); +}); diff --git a/packages/s2-core/src/utils/text.ts b/packages/s2-core/src/utils/text.ts index e0074ca635..833940b721 100644 --- a/packages/s2-core/src/utils/text.ts +++ b/packages/s2-core/src/utils/text.ts @@ -357,9 +357,7 @@ const calX = ( const getDrawStyle = (cell: S2CellType) => { const { isTotals } = cell.getMeta(); const isMeasureField = (cell as ColCell).isMeasureField?.(); - const cellStyle = cell.getStyle( - isMeasureField ? CellTypes.COL_CELL : CellTypes.DATA_CELL, - ); + const cellStyle = cell.getStyle(cell.cellType || CellTypes.DATA_CELL); let textStyle: TextTheme; if (isMeasureField) { From bd09e3d0f16bcf714e3f031e17feeeb6471d3295 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Mon, 16 Oct 2023 19:51:43 +0800 Subject: [PATCH 07/18] =?UTF-8?q?chore:=20=F0=9F=A4=96=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20changelog=20=E6=96=87=E4=BB=B6=20(#2369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/CHANGELOG.md | 9 +++++++++ packages/s2-core/package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/s2-core/CHANGELOG.md b/packages/s2-core/CHANGELOG.md index c1c342e399..de6e670e28 100644 --- a/packages/s2-core/CHANGELOG.md +++ b/packages/s2-core/CHANGELOG.md @@ -1,3 +1,12 @@ +# [@antv/s2-v1.51.1](https://github.com/antvis/S2/compare/@antv/s2-v1.51.0...@antv/s2-v1.51.1) (2023-10-13) + + +### Bug Fixes + +* **layout:** 修复隐藏结点时对父节点的布局计算错误 close [#2355](https://github.com/antvis/S2/issues/2355) ([#2360](https://github.com/antvis/S2/issues/2360)) ([c8d2c94](https://github.com/antvis/S2/commit/c8d2c94c21527ae52ece3485524d374d09b1cba7)) +* **table-sheet:** 明细表数据为空时错误的展示一行空数据 close [#2255](https://github.com/antvis/S2/issues/2255) ([#2357](https://github.com/antvis/S2/issues/2357)) ([7751c49](https://github.com/antvis/S2/commit/7751c4968b69752243928cbcd2c50333b06b2c66)) +* 列头绘制多列文本时错误的使用了数值单元格的样式 close [#2359](https://github.com/antvis/S2/issues/2359) ([#2364](https://github.com/antvis/S2/issues/2364)) ([4953b4e](https://github.com/antvis/S2/commit/4953b4e430f3c1857ce6648bcdc2493c37bb1092)) + # [@antv/s2-v1.51.0](https://github.com/antvis/S2/compare/@antv/s2-v1.50.0...@antv/s2-v1.51.0) (2023-09-22) diff --git a/packages/s2-core/package.json b/packages/s2-core/package.json index d818a75808..cbc465052a 100644 --- a/packages/s2-core/package.json +++ b/packages/s2-core/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@antv/s2", - "version": "1.51.0", + "version": "1.51.1", "main": "lib/index.js", "unpkg": "dist/index.min.js", "module": "esm/index.js", From 24437622e1b259288bf7f8f3505837088a6e1b9d Mon Sep 17 00:00:00 2001 From: NoobNot <56724970+NoobNotN@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:27:23 +0800 Subject: [PATCH 08/18] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=86?= =?UTF-8?q?=E7=BB=84=E6=B1=87=E6=80=BB=E6=97=B6=EF=BC=8C=E6=8C=89=E6=B1=87?= =?UTF-8?q?=E6=80=BB=E6=8E=92=E5=BA=8F=E8=8E=B7=E5=8F=96=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=BA=E7=A9=BA=20(#2370)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复指标不在最后一级配置与分组汇总同时使用时,获取数据为空 * fix: 修复指标不在最后一级配置与分组汇总同时使用时,获取数据为空 * test: 增加分组汇总时按汇总排序单测 * test: 增加分组汇总时按汇总排序单测 * chore: 优化代码可读,getMultiData 使用对象传参 * chore: 优化代码可读,getMultiData 使用对象传参 * chore: 优化代码可读,getMultiData 使用对象传参 * chore: getMultiData 使用传参格式改回 * chore: 改名为 includeTotalData 参数名以及文档 * docs: 加个空格 --------- Co-authored-by: JuZe --- .../__snapshots__/sort-action-spec.tsx.snap | 20 +++++++ .../__tests__/unit/utils/sort-action-spec.tsx | 52 +++++++++++++++++++ .../s2-core/src/data-set/base-data-set.ts | 2 + .../s2-core/src/data-set/pivot-data-set.ts | 12 ++--- packages/s2-core/src/utils/sort-action.ts | 8 ++- .../docs/api/basic-class/base-data-set.en.md | 38 +++++++------- .../docs/api/basic-class/base-data-set.zh.md | 2 +- 7 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 packages/s2-core/__tests__/unit/utils/__snapshots__/sort-action-spec.tsx.snap diff --git a/packages/s2-core/__tests__/unit/utils/__snapshots__/sort-action-spec.tsx.snap b/packages/s2-core/__tests__/unit/utils/__snapshots__/sort-action-spec.tsx.snap new file mode 100644 index 0000000000..a27042ad8a --- /dev/null +++ b/packages/s2-core/__tests__/unit/utils/__snapshots__/sort-action-spec.tsx.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GetSortByMeasureValues Total Fallback Tests should sort by col total whit group 1`] = ` +Array [ + Object { + "$$extra$$": "price", + "$$value$$": "666", + "city": "杭州", + "price": "666", + "type": "笔", + }, + Object { + "$$extra$$": "price", + "$$value$$": "999", + "city": "杭州", + "price": "999", + "type": "纸张", + }, +] +`; diff --git a/packages/s2-core/__tests__/unit/utils/sort-action-spec.tsx b/packages/s2-core/__tests__/unit/utils/sort-action-spec.tsx index 5b90bdb267..e9989b8847 100644 --- a/packages/s2-core/__tests__/unit/utils/sort-action-spec.tsx +++ b/packages/s2-core/__tests__/unit/utils/sort-action-spec.tsx @@ -714,4 +714,56 @@ describe('GetSortByMeasureValues Total Fallback Tests', () => { }, ]); }); + + test('should sort by col total whit group', () => { + const currentOptions = { + totals: { + col: { + totalsGroupDimensions: ['city'], + showGrandTotals: true, + }, + }, + } as S2Options; + + const dataConfig = { + ...sortData, + data: [ + ...sortData.data, + { + city: '杭州', + type: '纸张', + price: '999', + }, + { + city: '杭州', + type: '笔', + price: '666', + }, + ], + fields: { + rows: ['type'], + columns: ['province', 'city'], + values: ['price'], + }, + }; + sheet = new PivotSheet(getContainer(), dataConfig, currentOptions); + sheet.render(); + // 根据列(类别)的总和排序 + const sortParam: SortParam = { + sortFieldId: 'type', + sortByMeasure: TOTAL_VALUE, + sortMethod: 'desc', + query: { + [EXTRA_FIELD]: 'price', + city: '杭州', + }, + }; + + const params: SortActionParams = { + dataSet: sheet.dataSet, + sortParam, + }; + const measureValues = getSortByMeasureValues(params); + expect(measureValues).toMatchSnapshot(); + }); }); diff --git a/packages/s2-core/src/data-set/base-data-set.ts b/packages/s2-core/src/data-set/base-data-set.ts index 92ffb6ae8e..3697f895e3 100644 --- a/packages/s2-core/src/data-set/base-data-set.ts +++ b/packages/s2-core/src/data-set/base-data-set.ts @@ -206,12 +206,14 @@ export abstract class BaseDataSet { * @param isTotals * @param isRow * @param drillDownFields + * @param includeTotalData 用于标记是否包含汇总数据,例如在排序功能中需要汇总数据,在计算汇总值中只取明细数据 */ public abstract getMultiData( query: DataType, isTotals?: boolean, isRow?: boolean, drillDownFields?: string[], + includeTotalData?: boolean, ): DataType[]; public moreThanOneValue() { diff --git a/packages/s2-core/src/data-set/pivot-data-set.ts b/packages/s2-core/src/data-set/pivot-data-set.ts index 545d9eebf8..4f4766272c 100644 --- a/packages/s2-core/src/data-set/pivot-data-set.ts +++ b/packages/s2-core/src/data-set/pivot-data-set.ts @@ -577,8 +577,8 @@ export class PivotDataSet extends BaseDataSet { getTotalGroupQueries(dimensions: string[], originQuery: DataType) { let queries = [originQuery]; let existDimensionGroupKey = null; - for (let i = dimensions.length; i > 0; i--) { - const key = dimensions[i - 1]; + for (let i = dimensions.length - 1; i >= 0; i--) { + const key = dimensions[i]; if (keys(originQuery).includes(key)) { if (key !== EXTRA_FIELD) { existDimensionGroupKey = key; @@ -587,8 +587,6 @@ export class PivotDataSet extends BaseDataSet { const allCurrentFieldDimensionValues = this.sortedDimensionValues[existDimensionGroupKey]; let res = []; - const arrayLength = - allCurrentFieldDimensionValues[0].split(ID_SEPARATOR).length; for (const query of queries) { const resKeys = []; for (const dimValue of allCurrentFieldDimensionValues) { @@ -601,7 +599,7 @@ export class PivotDataSet extends BaseDataSet { }) ) { const arrTypeValue = dimValue.split(ID_SEPARATOR); - const currentKey = arrTypeValue[arrayLength - 2]; + const currentKey = arrTypeValue[i]; if (currentKey !== 'undefined') { resKeys.push(currentKey); } @@ -662,6 +660,7 @@ export class PivotDataSet extends BaseDataSet { isTotals?: boolean, isRow?: boolean, drillDownFields?: string[], + includeTotalData?: boolean, ): DataType[] { if (isEmpty(query)) { return compact(customFlattenDeep(this.indexesData)); @@ -672,7 +671,8 @@ export class PivotDataSet extends BaseDataSet { : rows; // existDimensionGroup:当 undefined 维度后面有非 undefined,为维度分组场景,将非 undefined 维度前的维度填充为所有可能的维度值。 // 如 [undefined , '杭州市' , undefined , 'number'] - const existDimensionGroup = this.checkExistDimensionGroup(query); + const existDimensionGroup = + !includeTotalData && this.checkExistDimensionGroup(query); let result = []; if (existDimensionGroup) { result = this.getGroupTotalMultiData(totalRows, query); diff --git a/packages/s2-core/src/utils/sort-action.ts b/packages/s2-core/src/utils/sort-action.ts index 921df04fb0..79bdf3e52d 100644 --- a/packages/s2-core/src/utils/sort-action.ts +++ b/packages/s2-core/src/utils/sort-action.ts @@ -252,7 +252,13 @@ export const getSortByMeasureValues = ( const { dataSet, sortParam, originValues } = params; const { fields } = dataSet; const { sortByMeasure, query, sortFieldId } = sortParam; - const dataList = dataSet.getMultiData(query); // 按 query 查出所有数据 + const dataList = dataSet.getMultiData( + query, + undefined, + undefined, + undefined, + true, + ); // 按 query 查出所有数据 const columns = getLeafColumnsWithKey(fields.columns); /** * 按明细数据 diff --git a/s2-site/docs/api/basic-class/base-data-set.en.md b/s2-site/docs/api/basic-class/base-data-set.en.md index 2f3b37a108..a6cba2cc77 100644 --- a/s2-site/docs/api/basic-class/base-data-set.en.md +++ b/s2-site/docs/api/basic-class/base-data-set.en.md @@ -8,25 +8,25 @@ Function description: tabular data set. [details](https://github.com/antvis/S2/b s2.dataSet.xx() ``` -| parameter | illustrate | type | Version | -| ------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | -| fields | field information | () => [Fields](/docs/api/general/S2DataConfig#fields) | | -| meta | Field meta information, including field name, formatting, etc. | () => [Meta\[\]](/docs/api/general/S2DataConfig#meta) | | -| originData | Raw data | () => [DataType\[\]](#datatype) | | -| totalData | summary data | () => [DataType\[\]](#datatype) | | -| indexesData | multidimensional index data | () => [DataType\[\]](#datatype) | | -| sortParams | sort configuration | () => [SortParams](/docs/api/general/S2DataConfig#sortparams) | | -| spreadsheet | Form example | () => [SpreadSheet](/docs/api/basic-class/spreadsheet) | | -| getFieldMeta | Get field metadata information | (field: string, meta?: [Meta\[\]](/docs/api/general/S2DataConfig#meta) ) => [Meta](/docs/api/general/S2DataConfig#meta) | | -| getFieldName | get field name | `() => string` | | -| getFieldFormatter | Get the field formatting function | `() => (v: string) => unknown` | | -| getFieldDescription | Get field description | `() => string` | | -| setDataCfg | Set data configuration | `(dataCfg: T extends true ?` [`S2DataConfig`](/docs/api/general/S2DataConfig) `: Partial<`[`S2DataConfig`](/docs/api/general/S2DataConfig)`>, reset?: T) => void` | The `reset` parameter needs to be used in `@antv/s2-v1.34.0` version | -| getDisplayDataSet | Get the currently displayed dataset | () => [DataType\[\]](#datatype) | | -| getDimensionValues | get dimension value | (filed: string, query?: [DataType](#datatype) ) => string\[] | | -| getCellData | Get a single cell data | (params: [CellDataParams](#celldataparams) ) => [DataType\[\]](#datatype) | | -| getMultiData | Get bulk cell data | (query: [DataType](#datatype) , isTotals?: boolean, isRow?: boolean, drillDownFields?: string\[]) => [DataType\[\]](#datatype) | | -| moreThanOneValue | Is there more than 1 value | () => [ViewMeta](#viewmeta) | | +| parameter | illustrate | type | Version | +| ------------------- | -------------------------------------------------------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------------------------------------------------------------------- | +| fields | field information | () => [Fields](/docs/api/general/S2DataConfig#fields) | | +| meta | Field meta information, including field name, formatting, etc. | () => [Meta\[\]](/docs/api/general/S2DataConfig#meta) | | +| originData | Raw data | () => [DataType\[\]](#datatype) | | +| totalData | summary data | () => [DataType\[\]](#datatype) | | +| indexesData | multidimensional index data | () => [DataType\[\]](#datatype) | | +| sortParams | sort configuration | () => [SortParams](/docs/api/general/S2DataConfig#sortparams) | | +| spreadsheet | Form example | () => [SpreadSheet](/docs/api/basic-class/spreadsheet) | | +| getFieldMeta | Get field metadata information | (field: string, meta?: [Meta\[\]](/docs/api/general/S2DataConfig#meta) ) => [Meta](/docs/api/general/S2DataConfig#meta) | | +| getFieldName | get field name | `() => string` | | +| getFieldFormatter | Get the field formatting function | `() => (v: string) => unknown` | | +| getFieldDescription | Get field description | `() => string` | | +| setDataCfg | Set data configuration | `(dataCfg: T extends true ?` [`S2DataConfig`](/docs/api/general/S2DataConfig) `: Partial<`[`S2DataConfig`](/docs/api/general/S2DataConfig)`>, reset?: T) => void` | The `reset` parameter needs to be used in `@antv/s2-v1.34.0` version | +| getDisplayDataSet | Get the currently displayed dataset | () => [DataType\[\]](#datatype) | | +| getDimensionValues | get dimension value | (filed: string, query?: [DataType](#datatype) ) => string\[] | | +| getCellData | Get a single cell data | (params: [CellDataParams](#celldataparams) ) => [DataType\[\]](#datatype) | | +| getMultiData | Get bulk cell data | (query: [DataType](#datatype) , isTotals?: boolean, isRow?: boolean, drillDownFields?: string\[], includeTotalData:boolean) => [DataType\[\]](#datatype) | | +| moreThanOneValue | Is there more than 1 value | () => [ViewMeta](#viewmeta) | | ### DataType diff --git a/s2-site/docs/api/basic-class/base-data-set.zh.md b/s2-site/docs/api/basic-class/base-data-set.zh.md index b259e92ced..115356e46a 100644 --- a/s2-site/docs/api/basic-class/base-data-set.zh.md +++ b/s2-site/docs/api/basic-class/base-data-set.zh.md @@ -26,7 +26,7 @@ s2.dataSet.getFieldName('type') | getDisplayDataSet | 获取当前显示的数据集 | () => [DataType[]](#datatype) | | | getDimensionValues | 获取维值 | (filed: string, query?: [DataType](#datatype) ) => string[] | | | getCellData | 获取单个的单元格数据 | (params: [CellDataParams](#celldataparams)) => [DataType[]](#datatype) | | -| getMultiData | 获取批量的单元格数据 | (query: [DataType](#datatype), isTotals?: boolean, isRow?: boolean, drillDownFields?: string[]) => [DataType[]](#datatype) | | +| getMultiData | 获取批量的单元格数据 | (query: [DataType](#datatype), isTotals?: boolean, isRow?: boolean, drillDownFields?: string[], includeTotalData:boolean) => [DataType[]](#datatype) | | | moreThanOneValue | 是否超过 1 个数值 | () => [ViewMeta](#viewmeta) | | | isEmpty | 是否为空数据集 | () => `boolean` | `@antv/s2-v1.51.1` | From 19497b34aaa9bd19ca5f410926f3035f1aa76d09 Mon Sep 17 00:00:00 2001 From: NoobNot <56724970+NoobNotN@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:57:09 +0800 Subject: [PATCH 09/18] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A0=91?= =?UTF-8?q?=E7=8A=B6=E8=A7=92=E5=A4=B4=EF=BC=8C=E5=BD=93=E6=9C=89=E5=AD=98?= =?UTF-8?q?=E5=9C=A8icon=E6=97=B6=EF=BC=8C=E5=86=85=E5=AE=B9=E4=B8=8Ebox?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E6=81=B0=E5=A5=BD=E7=9B=B8=E7=AD=89=EF=BC=8C?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=E6=8D=A2=E8=A1=8C=20close=20#2389=20(#2390)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 树状角头,当有两个icon时,内容与box宽度恰好相等,出现换行 * fix: 树状角头,当有两个icon时,内容与box宽度恰好相等,出现换行 close:2389 * fix: 树状角头,当有两个icon时,内容与box宽度恰好相等,出现换行 close:2389 * chore: 增加注释 --------- Co-authored-by: JuZe --- .../__snapshots__/corner-spec.ts.snap | 12 +++---- .../spread-sheet-tree-mode-spec.ts | 36 ++++++++++++++++++- .../unit/facet/layout/col-node-width-spec.ts | 4 +-- packages/s2-core/src/facet/pivot-facet.ts | 5 ++- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap b/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap index b3dc716fbd..f3430d0399 100644 --- a/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap +++ b/packages/s2-core/__tests__/spreadsheet/__snapshots__/corner-spec.ts.snap @@ -188,7 +188,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 0, "value": "province/city/数值", - "width": 150, + "width": 151, "x": 0, "y": 0, }, @@ -253,7 +253,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 80, "value": "province/city/数值", - "width": 150, + "width": 151, "x": 80, "y": 0, }, @@ -448,7 +448,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 0, "value": "province/city", - "width": 122, + "width": 123, "x": 0, "y": 0, }, @@ -513,7 +513,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 80, "value": "province/city", - "width": 122, + "width": 123, "x": 80, "y": 0, }, @@ -708,7 +708,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 0, "value": "province/city", - "width": 122, + "width": 123, "x": 0, "y": 0, }, @@ -773,7 +773,7 @@ Array [ "rowIndex": undefined, "seriesNumberWidth": 80, "value": "province/city", - "width": 122, + "width": 123, "x": 80, "y": 0, }, diff --git a/packages/s2-core/__tests__/spreadsheet/spread-sheet-tree-mode-spec.ts b/packages/s2-core/__tests__/spreadsheet/spread-sheet-tree-mode-spec.ts index 510c28565a..d7761e11b5 100644 --- a/packages/s2-core/__tests__/spreadsheet/spread-sheet-tree-mode-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/spread-sheet-tree-mode-spec.ts @@ -26,7 +26,7 @@ describe('SpreadSheet Tree Mode Tests', () => { s2.render(); const rowsHierarchyWidth = s2.facet.layoutResult.rowsHierarchy.width; - expect(Math.round(rowsHierarchyWidth)).toEqual(123); + expect(Math.round(rowsHierarchyWidth)).toEqual(124); // 行头维度均更改为较长的 name const newDataCfg: S2DataConfig = { @@ -49,5 +49,39 @@ describe('SpreadSheet Tree Mode Tests', () => { rowsHierarchyWidth, ); }); + + // https://github.com/antvis/S2/issues/2389 + test('the corner should only have one line with action icon', () => { + // 行头维度更改为较长的 name + const newDataCfg: S2DataConfig = { + ...mockDataConfig, + meta: [ + { + field: 'province', + name: '省1234567890份', + }, + { + field: 'city', + name: '城1234567890市', + }, + ], + }; + // 添加icon + const newS2Options: S2Options = { + ...s2Options, + headerActionIcons: [ + { + iconNames: ['SortDownSelected'], + belongsCell: 'cornerCell', + }, + ], + }; + const s2 = new PivotSheet(container, newDataCfg, newS2Options); + s2.render(); + + // 检查文本是否只有一行 + const textLen = s2.facet.cornerHeader.cfg.children[0].textShapes.length; + expect(textLen).toEqual(1); + }); }); }); diff --git a/packages/s2-core/__tests__/unit/facet/layout/col-node-width-spec.ts b/packages/s2-core/__tests__/unit/facet/layout/col-node-width-spec.ts index 49217dcd9f..f31e370a9b 100644 --- a/packages/s2-core/__tests__/unit/facet/layout/col-node-width-spec.ts +++ b/packages/s2-core/__tests__/unit/facet/layout/col-node-width-spec.ts @@ -53,7 +53,7 @@ describe('Col width Test', () => { }); s2.render(); const { colLeafNodes } = s2.facet.layoutResult; - expect(Math.round(colLeafNodes[0].width)).toBe(339); + expect(Math.round(colLeafNodes[0].width)).toBe(338); }); test('get correct width in layoutWidthType adaptive tree mode when enable seriesnumber', () => { @@ -63,7 +63,7 @@ describe('Col width Test', () => { }); s2.render(); const { colLeafNodes } = s2.facet.layoutResult; - expect(Math.round(colLeafNodes[0].width)).toBe(299); + expect(Math.round(colLeafNodes[0].width)).toBe(298); }); test('get correct width in layoutWidthType compact mode', () => { diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index c5da151310..eede09c795 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -745,8 +745,11 @@ export class PivotFacet extends BaseFacet { .join('/'); const { bolderText: cornerCellTextStyle, icon: cornerIconStyle } = this.spreadsheet.theme.cornerCell; - // 初始化角头时,保证其在树形模式下不换行,给与两个icon的宽度空余(tree icon 和 action icon),减少复杂的 action icon 判断 + // 初始化角头时,保证其在树形模式下不换行 + // 给与两个icon的宽度空余(tree icon 和 action icon),减少复杂的 action icon 判断 + // 额外增加 1,当内容和容器宽度恰好相等时会出现换行 const maxLabelWidth = + 1 + this.spreadsheet.measureTextWidth(treeHeaderLabel, cornerCellTextStyle) + cornerIconStyle.size * 2 + cornerIconStyle.margin?.left + From b2e97008122f5320342fd069a08f6e821a5c9ad6 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 27 Oct 2023 15:30:07 +0800 Subject: [PATCH 10/18] =?UTF-8?q?fix(interaction):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=8B=96=E5=8A=A8=E6=B0=B4=E5=B9=B3=E6=BB=9A=E5=8A=A8=E6=9D=A1?= =?UTF-8?q?=E5=90=8E=E5=8D=95=E5=85=83=E6=A0=BC=E9=80=89=E4=B8=AD=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E8=A2=AB=E9=87=8D=E7=BD=AE=20close=20#2376=20(#2380)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/scroll-spec.ts | 50 ++++++++++++++++++- .../src/interaction/event-controller.ts | 17 +++++-- packages/s2-react/playground/config.ts | 1 + 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/scroll-spec.ts b/packages/s2-core/__tests__/spreadsheet/scroll-spec.ts index bf4fad922e..b634849f5a 100644 --- a/packages/s2-core/__tests__/spreadsheet/scroll-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/scroll-spec.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/no-conditional-expect */ import * as mockDataConfig from 'tests/data/simple-data.json'; import { createMockCellInfo, getContainer, sleep } from 'tests/util/helpers'; -import { ScrollType } from '../../src/ui/scrollbar'; +import { ScrollBar, ScrollType } from '../../src/ui/scrollbar'; import type { CellScrollPosition } from './../../src/common/interface/scroll'; import { PivotSheet, SpreadSheet } from '@/sheet-type'; import type { @@ -880,4 +880,52 @@ describe('Scroll Tests', () => { expect(errorSpy).toBeCalledTimes(1); }); + + // https://github.com/antvis/S2/issues/2376 + test.each(['hScrollBar', 'hRowScrollBar'])( + 'should not reset interaction state after %s scrollbar thumb or track clicked', + (scrollbarName) => { + const containsMock = jest + .spyOn(HTMLElement.prototype, 'contains') + .mockImplementation(() => true); + + const reset = jest.fn(); + + const scrollbar = s2.facet[scrollbarName] as ScrollBar; + const colCell = s2.getColumnLeafNodes()[0].belongsCell; + + s2.on(S2Event.GLOBAL_RESET, reset); + s2.interaction.selectHeaderCell({ + cell: colCell, + }); + + expect(s2.interaction.isSelectedState()).toBeTruthy(); + + const { maxX, maxY } = s2.facet?.panelBBox || {}; + const { x, y } = canvas.getBoundingClientRect() || {}; + + // 滚动条 + window.dispatchEvent( + new MouseEvent('click', { + clientX: x + scrollbar.position.x, + // 在滚动条内点击 + clientY: y + scrollbar.position.y + scrollbar.theme.size - 2, + } as MouseEventInit), + ); + + // 滑动轨道 + window.dispatchEvent( + new MouseEvent('click', { + // 右下角滑道点击 + clientX: x + maxX - 2, + clientY: y + maxY + 2, + } as MouseEventInit), + ); + + expect(s2.interaction.isSelectedState()).toBeTruthy(); + expect(reset).not.toHaveBeenCalled(); + + containsMock.mockReset(); + }, + ); }); diff --git a/packages/s2-core/src/interaction/event-controller.ts b/packages/s2-core/src/interaction/event-controller.ts index f9d54c4851..a1258710b1 100644 --- a/packages/s2-core/src/interaction/event-controller.ts +++ b/packages/s2-core/src/interaction/event-controller.ts @@ -207,21 +207,32 @@ export class EventController { // 比如实际 400 * 300 => hd (800 * 600) // 从视觉来看, 虽然点击了空白处, 但其实还是处于 放大后的 canvas 区域, 所以还需要额外判断一下坐标 const { width, height } = this.getContainerRect(); + return ( canvas.contains(event.target as HTMLElement) && event.clientX <= x + width && event.clientY <= y + height ); } + return false; } private getContainerRect() { - const { maxX, maxY } = this.spreadsheet.facet?.panelBBox || {}; - const { width, height } = this.spreadsheet.options; + const { facet, options } = this.spreadsheet; + const scrollBar = facet.hRowScrollBar || facet.hScrollBar; + const { maxX, maxY } = facet?.panelBBox || {}; + const { width, height } = options; + + /** + * https://github.com/antvis/S2/issues/2376 + * 横向的滚动条在表格外 (Canvas 内), 点击滚动条(含滑道区域) 不应该重置交互 + */ + const trackHeight = scrollBar?.theme?.size || 0; + return { width: Math.min(width, maxX), - height: Math.min(height, maxY), + height: Math.min(height, maxY + trackHeight), }; } diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index 52719ad7ee..d7601cd540 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -110,6 +110,7 @@ export const s2Options: SheetComponentOptions = { }, cellCfg: { height: 50, + width: 200, }, }, }; From 2edd99c367116bad661a02893a303311787eb647 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 27 Oct 2023 15:30:22 +0800 Subject: [PATCH 11/18] =?UTF-8?q?fix(layout):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=9C=A8=E7=B4=A7=E5=87=91=E6=A8=A1=E5=BC=8F=E5=88=97=E5=A4=B4?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E6=9C=AA=E6=8C=89=E6=96=87=E6=9C=AC=E8=87=AA?= =?UTF-8?q?=E9=80=82=E5=BA=94=20close=20#2385=20(#2392)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(layout): 修复在紧凑模式列头宽度未按文本自适应 close #2385 * chore: 还原配置 --- .../s2-core/__tests__/bugs/issue-2385-spec.ts | 57 ++++ .../__tests__/data/data-issue-2385.json | 272 ++++++++++++++++++ packages/s2-core/src/facet/pivot-facet.ts | 25 +- .../s2-react/__tests__/data/mock-dataset.json | 2 +- packages/s2-react/playground/config.ts | 7 +- packages/s2-react/playground/index.tsx | 5 +- s2-site/docs/common/style.zh.md | 4 +- 7 files changed, 360 insertions(+), 12 deletions(-) create mode 100644 packages/s2-core/__tests__/bugs/issue-2385-spec.ts create mode 100644 packages/s2-core/__tests__/data/data-issue-2385.json diff --git a/packages/s2-core/__tests__/bugs/issue-2385-spec.ts b/packages/s2-core/__tests__/bugs/issue-2385-spec.ts new file mode 100644 index 0000000000..4e3e56430a --- /dev/null +++ b/packages/s2-core/__tests__/bugs/issue-2385-spec.ts @@ -0,0 +1,57 @@ +/** + * @description spec for issue #2385 + * https://github.com/antvis/S2/issues/2385 + */ +import { createPivotSheet, getContainer } from '../util/helpers'; +import * as mockDataConfig from '../data/data-issue-2385.json'; +import type { S2Options } from '../../src'; +import { PivotSheet, TableSheet } from '@/sheet-type'; + +const s2Options: S2Options = { + width: 800, + height: 600, + style: { + cellCfg: { + width: 200, + }, + layoutWidthType: 'compact', + }, +}; + +describe('Compare Layout Tests', () => { + test('should get max col width for pivot sheet', () => { + const s2 = new PivotSheet(getContainer(), mockDataConfig, s2Options); + s2.setTheme({ + dataCell: { + text: { + fontSize: 20, + }, + }, + }); + s2.render(); + + const colLeafNodes = s2.facet.layoutResult.colLeafNodes; + expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo(179); + expect(Math.floor(colLeafNodes[1].width)).toEqual(98); + }); + + test('should get max col width for table sheet', () => { + const s2 = new TableSheet(getContainer(), mockDataConfig, s2Options); + s2.setDataCfg({ + fields: { + columns: ['price'], + }, + }); + s2.setTheme({ + dataCell: { + text: { + fontSize: 20, + }, + }, + }); + s2.render(); + + const colLeafNodes = s2.facet.layoutResult.colLeafNodes; + expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo(165); + }); +}); diff --git a/packages/s2-core/__tests__/data/data-issue-2385.json b/packages/s2-core/__tests__/data/data-issue-2385.json new file mode 100644 index 0000000000..c8ad719d5c --- /dev/null +++ b/packages/s2-core/__tests__/data/data-issue-2385.json @@ -0,0 +1,272 @@ +{ + "data": [ + { + "province": "浙江", + "city": "杭州", + "type": "笔", + "price": "11111111" + }, + { + "province": "浙江", + "city": "杭州", + "type": "纸张", + "price": "2" + }, + { + "province": "浙江", + "city": "舟山", + "type": "笔", + "price": "2" + }, + { + "province": "浙江", + "city": "舟山", + "type": "纸张", + "price": "133.333" + }, + { + "province": "吉林", + "city": "长春", + "type": "笔", + "price": "3" + }, + { + "province": "吉林", + "city": "长春", + "type": "纸张", + "price": "2" + }, + { + "province": "吉林", + "city": "白山", + "type": "笔", + "price": "4" + }, + { + "province": "吉林", + "city": "白山", + "type": "纸张", + "price": "1" + }, + { + "province": "浙江", + "city": "杭州", + "type": "笔", + "price": "11111111111111111" + }, + { + "province": "浙江", + "city": "杭州", + "type": "纸张", + "price": "2" + }, + { + "province": "浙江", + "city": "舟山", + "type": "笔", + "price": "2" + }, + { + "province": "浙江", + "city": "舟山", + "type": "纸张", + "price": "666.333" + }, + { + "province": "吉林", + "city": "长春", + "type": "笔", + "price": "3" + }, + { + "province": "吉林", + "city": "长春", + "type": "纸张", + "price": "2" + }, + { + "province": "吉林", + "city": "白山", + "type": "笔", + "price": "4" + }, + { + "province": "吉林", + "city": "白山", + "type": "纸张", + "price": "1" + }, + { + "province": "浙江", + "city": "杭州", + "type": "笔", + "cost": "33.333" + }, + { + "province": "浙江", + "city": "杭州", + "type": "纸张", + "cost": "1.5" + }, + { + "province": "浙江", + "city": "舟山", + "type": "笔", + "cost": "1.5" + }, + { + "province": "浙江", + "city": "舟山", + "type": "纸张", + "cost": "0.2" + }, + { + "province": "吉林", + "city": "长春", + "type": "笔", + "cost": "2" + }, + { + "province": "吉林", + "city": "长春", + "type": "纸张", + "cost": "1" + }, + { + "province": "吉林", + "city": "白山", + "type": "笔", + "cost": "3" + }, + { + "province": "吉林", + "city": "白山", + "type": "纸张", + "cost": "33.333" + }, + { + "price": "15.5" + }, + { + "province": "浙江", + "price": "5.5" + }, + { + "province": "浙江", + "city": "杭州", + "price": "3" + }, + { + "province": "浙江", + "city": "舟山", + "price": "2.5" + }, + { + "province": "吉林", + "price": "10" + }, + { + "province": "吉林", + "city": "长春", + "price": "5" + }, + { + "province": "吉林", + "city": "白山", + "price": "5" + }, + { + "type": "笔", + "price": "10" + }, + { + "type": "笔", + "province": "浙江", + "price": "3" + }, + { + "type": "笔", + "province": "吉林", + "price": "7" + }, + { + "type": "纸张", + "price": "5.5" + }, + { + "type": "纸张", + "province": "浙江", + "price": "2.5" + }, + { + "type": "纸张", + "province": "吉林", + "price": "3" + }, + { + "cost": "10.2" + }, + { + "province": "浙江", + "cost": "3.7" + }, + { + "province": "浙江", + "city": "杭州", + "cost": "2" + }, + { + "province": "浙江", + "city": "舟山", + "cost": "1.7" + }, + { + "province": "吉林", + "cost": "6.5" + }, + { + "province": "吉林", + "city": "长春", + "cost": "3" + }, + { + "province": "吉林", + "city": "白山", + "cost": "3.5" + }, + { + "type": "笔", + "cost": "7" + }, + { + "type": "笔", + "province": "浙江", + "cost": "2" + }, + { + "type": "笔", + "province": "吉林", + "cost": "5" + }, + { + "type": "纸张", + "cost": "3.2" + }, + { + "type": "纸张", + "province": "浙江", + "cost": "1.7" + }, + { + "type": "纸张", + "province": "吉林", + "cost": "1.5" + } + ], + "fields": { + "rows": ["province", "city"], + "columns": ["type"], + "values": ["price"], + "valueInCols": true + } +} diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index eede09c795..b12e81320b 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -294,6 +294,7 @@ export class PivotFacet extends BaseFacet { cell: colCellStyle, icon: colIconStyle, } = this.spreadsheet.theme.colCell; + const { text: dataCellTextStyle } = this.spreadsheet.theme.dataCell; // leaf node rough width const cellFormatter = this.spreadsheet.dataSet.getFieldFormatter( @@ -307,7 +308,10 @@ export class PivotFacet extends BaseFacet { colIconStyle, ); const leafNodeRoughWidth = - this.spreadsheet.measureTextWidthRoughly(leafNodeLabel) + iconWidth; + this.spreadsheet.measureTextWidthRoughly( + leafNodeLabel, + colCellTextStyle, + ) + iconWidth; // 采样 50 个 label,逐个计算找出最长的 label let maxDataLabel: string; @@ -334,8 +338,10 @@ export class PivotFacet extends BaseFacet { cellData[EXTRA_FIELD], )?.(valueData) ?? valueData; const cellLabel = `${formattedValue}`; - const cellLabelWidth = - this.spreadsheet.measureTextWidthRoughly(cellLabel); + const cellLabelWidth = this.spreadsheet.measureTextWidthRoughly( + cellLabel, + dataCellTextStyle, + ); if (cellLabelWidth > maxDataLabelWidth) { maxDataLabel = cellLabel; @@ -345,7 +351,6 @@ export class PivotFacet extends BaseFacet { } } - // compare result const isLeafNodeWidthLonger = leafNodeRoughWidth > maxDataLabelWidth; const maxLabel = isLeafNodeWidthLonger ? leafNodeLabel : maxDataLabel; const appendedWidth = isLeafNodeWidthLonger ? iconWidth : 0; @@ -354,10 +359,20 @@ export class PivotFacet extends BaseFacet { 'Max Label In Col:', col.field, maxLabel, + maxDataLabelWidth, ); + // 取列头/数值字体最大的文本宽度 https://github.com/antvis/S2/issues/2385 + const maxTextWidth = this.spreadsheet.measureTextWidth(maxLabel, { + ...colCellTextStyle, + fontSize: Math.max( + dataCellTextStyle.fontSize, + colCellTextStyle.fontSize, + ), + }); + return ( - this.spreadsheet.measureTextWidth(maxLabel, colCellTextStyle) + + maxTextWidth + colCellStyle.padding?.left + colCellStyle.padding?.right + appendedWidth diff --git a/packages/s2-react/__tests__/data/mock-dataset.json b/packages/s2-react/__tests__/data/mock-dataset.json index d42360ebc8..c807f0b973 100644 --- a/packages/s2-react/__tests__/data/mock-dataset.json +++ b/packages/s2-react/__tests__/data/mock-dataset.json @@ -80,7 +80,7 @@ "sub_type": "沙发" }, { - "number": 632, + "number": 632632632, "province": "浙江省", "city": "绍兴市", "type": "家具", diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index d7601cd540..27bc560e14 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -1,5 +1,5 @@ import { isUpDataValue, type Columns } from '@antv/s2'; -import type { S2DataConfig } from '@antv/s2'; +import type { S2DataConfig, ThemeCfg } from '@antv/s2'; import { getBaseSheetComponentOptions } from '@antv/s2-shared'; import type { SliderSingleProps } from 'antd'; import { @@ -115,6 +115,11 @@ export const s2Options: SheetComponentOptions = { }, }; +export const s2ThemeConfig: ThemeCfg = { + name: 'default', + theme: {}, +}; + export const sliderOptions: SliderSingleProps = { min: 0, max: 10, diff --git a/packages/s2-react/playground/index.tsx b/packages/s2-react/playground/index.tsx index cbf99d085b..5d27b80834 100644 --- a/packages/s2-react/playground/index.tsx +++ b/packages/s2-react/playground/index.tsx @@ -67,6 +67,7 @@ import { tableSheetDataCfg, tableSheetMultipleColumns, tableSheetSingleColumns, + s2ThemeConfig, } from './config'; import './index.less'; import { ResizeConfig } from './resize'; @@ -172,9 +173,7 @@ function MainLayout() { const [showPagination, setShowPagination] = React.useState(false); const [showTotals, setShowTotals] = React.useState(false); - const [themeCfg, setThemeCfg] = React.useState({ - name: 'default', - }); + const [themeCfg, setThemeCfg] = React.useState(s2ThemeConfig); const [themeColor, setThemeColor] = React.useState('#FFF'); const [showCustomTooltip, setShowCustomTooltip] = React.useState(false); const [adaptive, setAdaptive] = React.useState(false); diff --git a/s2-site/docs/common/style.zh.md b/s2-site/docs/common/style.zh.md index c0709c5bbf..22f4c3e823 100644 --- a/s2-site/docs/common/style.zh.md +++ b/s2-site/docs/common/style.zh.md @@ -9,7 +9,7 @@ object **必选**,_default:null_ 功能描述:样式设置 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | --- | --- | --- | --- | --- | -| layoutWidthType | `adaptive` \| `colAdaptive` \| `compact` | | | 单元格宽度布局类型
`adaptive` : 行列等宽,均分整个 `Canvas` 画布宽度
`colAdaptive`:列等宽,行头紧凑布局,列等分画布宽度减去行头宽度的剩余宽度
`compact`:行列紧凑布局,指标维度少的时候无法布满整个画布 | +| layoutWidthType | `adaptive \| colAdaptive \| compact` | | | 单元格宽度布局类型
`adaptive` : 行列等宽,均分整个 `Canvas` 画布宽度
`colAdaptive`:列等宽,行头紧凑布局,列等分画布宽度减去行头宽度的剩余宽度
`compact`:行列紧凑布局,列头宽度为内容实际宽度 (采样列前 50 个数值), 指标维度少的时候无法布满整个画布 | | showTreeLeafNodeAlignDot | `boolean` | | false | 树状模式下叶子节点是否显示层级占位点 | | treeRowsWidth | `number` | | 120 | 树状模式行单元格宽度 (优先级大于 `rowCfg.width` 和 `rowCfg.treeRowsWidth (已废弃)`) | | hierarchyCollapse | `boolean` | | `false` | 在树状结构模式下行头是否默认展开。 | @@ -18,7 +18,7 @@ object **必选**,_default:null_ 功能描述:样式设置 | cellCfg | [CellCfg](#cellcfg) | | | 单元格配置 | | colCfg | [ColCfg](#colcfg) | | | 列样式配置 | | rowCfg | [RowCfg](#rowcfg) | | | 行样式配置 | -| device | `pc` \| `mobile` | | `pc` | 设备类型 | +| device | `pc \| mobile` | | `pc` | 设备类型 | ## CellCfg From 58508eac2887d7ef617ae2c5acc5e8215c3ab50e Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 27 Oct 2023 15:40:26 +0800 Subject: [PATCH 12/18] =?UTF-8?q?chore:=20=E4=B8=8D=E6=8C=89=20issue=20?= =?UTF-8?q?=E6=A8=A1=E7=89=88=E6=8F=90=E4=BA=A4=E8=87=AA=E5=8A=A8=E5=85=B3?= =?UTF-8?q?=E9=97=AD=20(#2393)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- .github/ISSUE_TEMPLATE/feature-request.md | 33 +++++++++++++++++++++++ .github/workflows/issue-labeled.yml | 2 +- .github/workflows/issue-opend.yml | 6 ++--- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 14a0fcaa81..916dadda53 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -49,7 +49,7 @@ eg. - + ### 🔗 Reproduce Link diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index bc72b546fd..518d3e947a 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -8,6 +8,39 @@ labels: 🙏feature request +### 🏷 Version + + + + + + +| Package | Version | +| -------------- | ------- | +| @antv/s2 | | +| @antv/s2-react | | +| @antv/s2-vue | | + +### Sheet Type + + + + +- [ ] PivotSheet +- [ ] TableSheet +- [ ] GridAnalysisSheet +- [ ] StrategySheet +- [ ] EditableSheet + ### 🖋 Description diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index 4eb12f78cc..b345097204 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -105,7 +105,7 @@ jobs: body: | 你好 @${{ github.event.issue.user.login }},经过我们的反复讨论, 你的需求现已被采纳, 我们会排期开发, 但人力资源有限, 短期内无法支持, 请关注后续发布日志。当然, 如果能贡献 PR 帮助我们改进, 不胜感激! - Hello, @${{ github.vent.issue.user.login }}, your feature request has been accepted after our repeated discussion. We will schedule the development. However, it could not be supported in the short term since limited time, please pay attention to the follow-up release logs. Of course, looking forward for your PR! + Hello, @${{ github.event.issue.user.login }}, your feature request has been accepted after our repeated discussion. We will schedule the development. However, it could not be supported in the short term since limited time, please pay attention to the follow-up release logs. Of course, looking forward for your PR! - name: Rejected if: github.event.label.name == '❌ won''t support' diff --git a/.github/workflows/issue-opend.yml b/.github/workflows/issue-opend.yml index 1cfb08c64c..ec973f8af4 100644 --- a/.github/workflows/issue-opend.yml +++ b/.github/workflows/issue-opend.yml @@ -20,9 +20,9 @@ jobs: Hello, @${{ github.event.issue.user.login }}, please edit your issue title. a concise issue title will save everyone time. please do not leave the title as the body or empty. - # 如果是 bug 的 issue, 但是基本的版本号,表格类型, 描述都没有, 直接关闭, 不多BB. - - name: check bug report issue body - if: contains(github.event.issue.title, '🐛') == true && contains(github.event.issue.body, 'Version') == false && contains(github.event.issue.body, 'Sheet Type') == false && contains(github.event.issue.body, 'Description') == false + # 如果 issue 的提交者无视模版, 连基本的版本号,表格类型, 描述都没有, 直接自动关闭, 不多BB. + - name: check issue body + if: contains(github.event.issue.body, 'Version') == false && contains(github.event.issue.body, 'Sheet Type') == false && contains(github.event.issue.body, 'Description') == false uses: actions-cool/issues-helper@main with: actions: 'create-comment,add-labels,close-issue' From ddbee015a89fc8ef7ceb324950e9fc9cf018bc74 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 27 Oct 2023 16:49:15 +0800 Subject: [PATCH 13/18] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=98=8E=E7=BB=86=E8=A1=A8=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=A0=BC=20&=20=E8=87=AA=E5=AE=9A=E4=B9=89=20mini=20=E5=9B=BE?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20(#2394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/manual/advanced/custom/hook.zh.md | 10 +- .../docs/manual/basic/analysis/strategy.zh.md | 8 +- s2-site/examples/case/art/demo/lost-text.tsx | 292 ++++++++++++++++++ s2-site/examples/case/art/demo/meta.json | 10 +- .../custom/custom-cell/demo/corner-header.ts | 5 +- .../custom-cell/demo/custom-specified-cell.ts | 95 +++++- .../custom-cell/demo/custom-table-cell.ts | 139 +++++++++ .../custom/custom-cell/demo/data-cell.ts | 36 +-- .../custom/custom-cell/demo/meta.json | 18 +- .../custom/custom-cell/demo/mini-chart.ts | 178 +++++++++++ .../custom/custom-cell/demo/totals-cell.ts | 2 +- .../custom-layout/demo/custom-coordinate.ts | 22 ++ .../demo/custom-data-position.ts | 22 ++ .../demo/custom-layout-arrange.ts | 22 ++ .../demo/custom-layout-hierarchy.ts | 22 ++ .../custom-layout/demo/custom-value-order.ts | 22 ++ .../sheet/demo/strategy-mini-chart.tsx | 5 + 17 files changed, 856 insertions(+), 52 deletions(-) create mode 100644 s2-site/examples/case/art/demo/lost-text.tsx create mode 100644 s2-site/examples/custom/custom-cell/demo/custom-table-cell.ts create mode 100644 s2-site/examples/custom/custom-cell/demo/mini-chart.ts diff --git a/s2-site/docs/manual/advanced/custom/hook.zh.md b/s2-site/docs/manual/advanced/custom/hook.zh.md index a395b2e60c..b527c4ce92 100644 --- a/s2-site/docs/manual/advanced/custom/hook.zh.md +++ b/s2-site/docs/manual/advanced/custom/hook.zh.md @@ -31,23 +31,23 @@ order: 1 ## dataCell -改变数据单元格的默认实现,需要继承自 [dataCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/data-cell.ts),覆盖某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#data-cell) +修改数据单元格的默认实现,需要继承自 [DataCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/data-cell.ts) (明细表对应 [TableDataCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/table-data-cell.ts)),复写某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#data-cell) ## rowCell -改变行头单元格的默认实现,需要继承自 [rowCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/row-cell.ts),覆盖某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#row-cell) +修改行头单元格的默认实现,需要继承自 [RowCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/row-cell.ts),复写某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#row-cell) ## colCell -改变列头单元格的默认实现,需要继承自 [colCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/col-cell.ts),覆盖某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#col-cell) +修改列头单元格的默认实现,需要继承自 [ColCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/col-cell.ts)(明细表对应 [TableColCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/table-col-cell.ts)),复写某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#col-cell) ## cornerCell -改变角头单元格的默认实现,需要继承自 [cornerCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/corner-cell.ts),覆盖某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#corner-cell) +修改角头单元格的默认实现,需要继承自 [TableCornerCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/corner-cell.ts)(明细表对应 [TableCornerCell](https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/table-corner-cell.ts)),复写某些方法,比如字体样式、背景样式等。[例子](/examples/custom/custom-cell#corner-cell) ## cornerHeader -改变角头的默认实现,需要继承自 [Group](https://g.antv.vision/zh/docs/api/group),覆盖某些方法,比如渲染内容更换等。[例子](/examples/custom/custom-cell#corner-cell) +修改角头的默认实现,需要继承自 [Group](https://g.antv.vision/zh/docs/api/group),复写某些方法,比如渲染内容更换等。[例子](/examples/custom/custom-cell#corner-header) ## frame diff --git a/s2-site/docs/manual/basic/analysis/strategy.zh.md b/s2-site/docs/manual/basic/analysis/strategy.zh.md index 39d1c72a64..0ca2bdf96f 100644 --- a/s2-site/docs/manual/basic/analysis/strategy.zh.md +++ b/s2-site/docs/manual/basic/analysis/strategy.zh.md @@ -176,7 +176,13 @@ const s2Options = { -配置如下: +### 在普通透视表中使用 + +如果不依赖 `React`, 想在 `@antv/s2` 普通的透视表中使用 mini 图,可以参考这个 [示例](/zh/examples/custom/custom-cell/#mini-chart) + + + +### API diff --git a/s2-site/examples/case/art/demo/lost-text.tsx b/s2-site/examples/case/art/demo/lost-text.tsx new file mode 100644 index 0000000000..898564f3e2 --- /dev/null +++ b/s2-site/examples/case/art/demo/lost-text.tsx @@ -0,0 +1,292 @@ +/* eslint-disable max-classes-per-file */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { SheetComponent } from '@antv/s2-react'; +import { Tag } from 'antd'; +import { + BaseEvent, + CellTypes, + getTheme, + InterceptType, + S2Event, + CornerCell, +} from '@antv/s2'; + +import '@antv/s2-react/dist/style.min.css'; + +const Theme = { + rowCell: { + text: { + opacity: 0, + }, + bolderText: { + opacity: 0, + }, + measureText: { + opacity: 0, + }, + }, + colCell: { + text: { + opacity: 0, + }, + bolderText: { + opacity: 0, + }, + measureText: { + opacity: 0, + }, + }, + dataCell: { + text: { + opacity: 0, + }, + }, +}; + +class CustomCornerCell extends CornerCell { + drawBackgroundShape() { + this.addShape('rect', { + attrs: { + ...this.getCellArea(), + fill: '#E0E9FD', + }, + }); + } + + getCornerText() { + return '👍🏻'; + } +} + +class CustomInteraction extends BaseEvent { + timer = null; + + count = 0; + + changeCell(cellType: CellTypes) { + this.count++; + + const defaultTheme = getTheme(null)?.[cellType]; + this.spreadsheet.setTheme({ + [cellType]: defaultTheme, + }); + this.spreadsheet.render(false); + + if (this.count >= 3) { + clearInterval(this.timer); + this.showSuccessTips(); + } + } + + resetCell() { + this.count = 0; + this.spreadsheet.setTheme(Theme); + this.spreadsheet.render(false); + } + + showSuccessTips() { + const rect = this.spreadsheet.getCanvasElement().getBoundingClientRect(); + + this.spreadsheet.showTooltip({ + position: { + x: rect.width / 2 + rect.left, + y: rect.height / 2 + rect.top, + }, + content: ( +
+

💐 通关啦 💐

+

+ S2 + 多维交叉分析表格是多维交叉分析领域的表格解决方案,数据驱动视图,提供底层核心库、基础组件库、业务场景库,具备自由扩展的能力,让开发者既能开箱即用,也能基于自身场景自由发挥。 +

+

+ + 前往官网 https://s2.antv.antgroup.com/ + +

+
+ ), + }); + this.spreadsheet.interaction.addIntercepts([InterceptType.HOVER]); + } + + bindEvents() { + // 角头: 一键三连 + this.addCornerCellInteraction(); + // 行头: 多选全部偶数行 + this.addRowCellInteraction(); + // 列头: 调整列宽/刷选全部 + this.addColCellInteraction(); + // 数值: 键盘方向键移动端选中单元格到右下角 + this.addDataCellInteraction(); + } + + addCornerCellInteraction() { + const countMap: Record = { + 0: CellTypes.ROW_CELL, + 1: CellTypes.COL_CELL, + 2: CellTypes.DATA_CELL, + }; + + this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_DOWN, () => { + clearInterval(this.timer); + this.resetCell(); + + this.timer = setInterval(() => { + this.changeCell(countMap[this.count]); + }, 1000); + }); + + this.spreadsheet.on(S2Event.CORNER_CELL_MOUSE_UP, () => { + clearInterval(this.timer); + + if (this.count < 3) { + this.resetCell(); + } + }); + } + + addDataCellInteraction() { + this.spreadsheet.on(S2Event.DATA_CELL_SELECT_MOVE, (cells) => { + const { colIndex, rowIndex } = cells[0]; + + const isLastCell = colIndex === 3 && rowIndex === 7; + if (isLastCell) { + this.changeCell(CellTypes.DATA_CELL); + } + }); + } + + addColCellInteraction() { + this.spreadsheet.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ({ info }) => { + const rules = [6, 66, 666]; + if (rules.includes(info.resizedWidth)) { + this.changeCell(CellTypes.COL_CELL); + } + }); + + this.spreadsheet.on(S2Event.COL_CELL_BRUSH_SELECTION, (colCells) => { + const isAllSelected = + colCells.length === this.spreadsheet.getColumnNodes().length; + if (isAllSelected) { + this.changeCell(CellTypes.COL_CELL); + } + }); + } + + addRowCellInteraction() { + this.spreadsheet.on(S2Event.GLOBAL_SELECTED, (cells) => { + const selectedOddRowCells = cells.filter((cell) => { + const meta = cell.getMeta(); + return cell.cellType === CellTypes.ROW_CELL && meta.rowIndex % 2 !== 0; + }); + + const isAllOddRowCellsSelected = selectedOddRowCells.length === 4; + if (isAllOddRowCellsSelected) { + this.changeCell(CellTypes.ROW_CELL); + } + }); + } +} + +export const s2Options = { + debug: true, + width: 600, + height: 400, + showSeriesNumber: false, + showDefaultHeaderActionIcon: false, + interaction: { + enableCopy: true, + // 防止 mac 触摸板横向滚动触发浏览器返回, 和移动端下拉刷新 + overscrollBehavior: 'none', + brushSelection: { + data: true, + col: true, + row: true, + }, + hoverFocus: false, + hoverHighlight: false, + customInteractions: [ + { + key: 'CustomInteraction', + interaction: CustomInteraction, + }, + ], + }, + tooltip: { + showTooltip: false, + }, + hierarchyType: 'grid', + style: { + rowCfg: { + width: 100, + }, + cellCfg: { + width: 50, + height: 30, + }, + }, + cornerCell: (...args) => new CustomCornerCell(...args), +}; + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/2a5dbbc8-d0a7-4d02-b7c9-34f6ca63cff6.json', +) + .then((res) => res.json()) + .then((dataCfg) => { + ReactDOM.render( + +

+ 单元格的文字都消失了, 想办法让文字全部显示出来. + + 查看代码 + +

+
    +
  • + + 列头可以 "调整" 成三个尺码: s (6px) M (66px) L (666px) + +
  • +
  • + 列头10个单元格可以 "圈" 在一起 +
  • +
  • + 行头多选, 让它显示斑马纹 +
  • +
  • + 有一个数值单元格喜欢待在角落 ↑ ↓ ← → +
  • +
  • + 搞不定, 试试看看 + + 基础交互 + + 章节或试试 长按一键三连 +
  • +
+ + ), + }} + />, + document.getElementById('container'), + ); + }); diff --git a/s2-site/examples/case/art/demo/meta.json b/s2-site/examples/case/art/demo/meta.json index 4472e98d6c..77f7574aa4 100644 --- a/s2-site/examples/case/art/demo/meta.json +++ b/s2-site/examples/case/art/demo/meta.json @@ -15,10 +15,18 @@ { "filename": "time-spend-abstract.tsx", "title": { - "zh": "想如何度过一天(抽象版)", + "zh": "想如何度过一天 (抽象版)", "en": "How to spend the day" }, "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/lN8USZvMEV/f6b5e06f-5def-4db8-a9c8-826e347d0fa1.png" + }, + { + "filename": "lost-text.tsx", + "title": { + "zh": "消失的文字 (交互小游戏)", + "en": "Lost text" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*3WDCR7PZY6MAAAAAAAAAAAAADmJ7AQ/original" } ] } diff --git a/s2-site/examples/custom/custom-cell/demo/corner-header.ts b/s2-site/examples/custom/custom-cell/demo/corner-header.ts index 1b40471866..4d65f8ea4f 100644 --- a/s2-site/examples/custom/custom-cell/demo/corner-header.ts +++ b/s2-site/examples/custom/custom-cell/demo/corner-header.ts @@ -8,13 +8,17 @@ import { get } from 'lodash'; */ class CustomCornerHeader extends Group { node; + backgroundShape; + textShape; + constructor(node) { super({}); this.node = node; this.initCornerHeader(); } + initCornerHeader() { this.initBg(); this.initText(); @@ -77,6 +81,5 @@ fetch( }; const s2 = new PivotSheet(container, s2DataConfig, s2Options); - // 使用 s2.render(); }); diff --git a/s2-site/examples/custom/custom-cell/demo/custom-specified-cell.ts b/s2-site/examples/custom/custom-cell/demo/custom-specified-cell.ts index 086cf0dec2..31f93077a4 100644 --- a/s2-site/examples/custom/custom-cell/demo/custom-specified-cell.ts +++ b/s2-site/examples/custom/custom-cell/demo/custom-specified-cell.ts @@ -1,8 +1,8 @@ /* eslint-disable max-classes-per-file */ -import { PivotSheet, DataCell, ColCell } from '@antv/s2'; +import { PivotSheet, DataCell, ColCell, CornerCell, RowCell } from '@antv/s2'; /** - * 自定义 DataCell,给特定单元格设置背景色, 文字大小, 颜色 + * 自定义 DataCell,通过复写基类方法, 给特定单元格设置背景色, 文字大小, 颜色等... * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/data-cell.ts */ class CustomDataCell extends DataCell { @@ -62,7 +62,7 @@ class CustomDataCell extends DataCell { } /** - * 自定义 ColCell, 给特定单元格设置文字大小, 颜色 + * 自定义 ColCell, 通过复写基类方法, 给特定单元格设置文字大小, 颜色等... * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/col-cell.ts */ class CustomColCell extends ColCell { @@ -70,7 +70,7 @@ class CustomColCell extends ColCell { const defaultTextStyle = super.getTextStyle(); // 指定列 - if (this.meta.rowIndex % 2 === 0) { + if (this.meta.colIndex % 2 === 0) { return { ...defaultTextStyle, fontSize: 16, @@ -102,6 +102,81 @@ class CustomColCell extends ColCell { } } +/** + * 自定义 CornerCell, 通过复写基类方法, 给特定单元格设置文字大小, 颜色等... + * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/corner-cell.ts + */ +class CustomCornerCell extends CornerCell { + getBackgroundColor() { + // 特定数据 + if (this.meta.field === 'province') { + return { + backgroundColor: 'red', + backgroundColorOpacity: 0.2, + }; + } + + return super.getBackgroundColor(); + } + + getTextStyle() { + const defaultTextStyle = super.getTextStyle(); + + if (this.meta.field === 'type') { + return { + ...defaultTextStyle, + fill: '#06a', + fontSize: 20, + fontWeight: 200, + }; + } + + return super.getTextStyle(); + } +} + +/** + * 自定义 RowCell, 通过复写基类方法, 给特定单元格设置文字大小, 颜色等... + * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/row-cell.ts + */ +class CustomRowCell extends RowCell { + getBackgroundColor() { + // 特定数据 + if (this.meta.field === 'province') { + return { + backgroundColor: 'red', + backgroundColorOpacity: 0.2, + }; + } + + return super.getBackgroundColor(); + } + + getTextStyle() { + const defaultTextStyle = super.getTextStyle(); + + if (this.meta.field === 'type') { + return { + ...defaultTextStyle, + fill: '#06a', + fontSize: 20, + fontWeight: 200, + }; + } + + if (this.meta.rowIndex >= 1) { + return { + ...defaultTextStyle, + fill: '#dcdcdc', + fontSize: 20, + fontWeight: 700, + }; + } + + return super.getTextStyle(); + } +} + fetch( 'https://gw.alipayobjects.com/os/bmw-prod/cd9814d0-6dfa-42a6-8455-5a6bd0ff93ca.json', ) @@ -120,16 +195,20 @@ fetch( const s2Options = { width: 600, height: 480, - dataCell: (viewMeta) => { - return new CustomDataCell(viewMeta, viewMeta?.spreadsheet); + cornerCell: (node, spreadsheet, headerConfig) => { + return new CustomCornerCell(node, spreadsheet, headerConfig); }, - // rowCell 同理, 请参考示例 colCell: (node, spreadsheet, headerConfig) => { return new CustomColCell(node, spreadsheet, headerConfig); }, + rowCell: (node, spreadsheet, headerConfig) => { + return new CustomRowCell(node, spreadsheet, headerConfig); + }, + dataCell: (viewMeta) => { + return new CustomDataCell(viewMeta, viewMeta?.spreadsheet); + }, }; const s2 = new PivotSheet(container, s2DataConfig, s2Options); - // 使用 s2.render(); }); diff --git a/s2-site/examples/custom/custom-cell/demo/custom-table-cell.ts b/s2-site/examples/custom/custom-cell/demo/custom-table-cell.ts new file mode 100644 index 0000000000..7a3e822182 --- /dev/null +++ b/s2-site/examples/custom/custom-cell/demo/custom-table-cell.ts @@ -0,0 +1,139 @@ +/* eslint-disable max-classes-per-file */ +import { TableColCell, TableDataCell, TableSheet } from '@antv/s2'; + +/** + * 自定义 DataCell,通过复写基类方法, 给特定单元格设置背景色, 文字大小, 颜色等... + * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/table-data-cell.ts + */ +class CustomDataCell extends TableDataCell { + getBackgroundColor() { + // 特定数据 + if (this.meta.fieldValue >= 6000) { + return { + backgroundColor: 'red', + backgroundColorOpacity: 0.2, + }; + } + + return super.getBackgroundColor(); + } + + getTextStyle() { + const defaultTextStyle = super.getTextStyle(); + // 序号 + + if (this.meta.colIndex === 0) { + return { + ...defaultTextStyle, + fontWeight: 600, + textAlign: 'center', + }; + } + + // 指定列 + if (this.meta.rowIndex % 2 === 0 && this.meta.colIndex > 0) { + return { + ...defaultTextStyle, + fontSize: 16, + fill: '#396', + textAlign: 'left', + }; + } + + // 指定数据 + if (this.meta.fieldValue >= 600 || this.meta.fieldValue === '沙发') { + return { + ...defaultTextStyle, + fontSize: 14, + fontWeight: 700, + fill: '#f63', + textAlign: 'center', + }; + } + + // 指定单元格 + if (this.meta.id === '7-root[&]省份') { + return { + ...defaultTextStyle, + fontSize: 12, + fontWeight: 200, + fill: '#dcdcdc', + opacity: 0.9, + textAlign: 'right', + }; + } + + // 使用默认处理 + return super.getTextStyle(); + } +} + +/** + * 自定义 ColCell, 通过复写基类方法, 给特定单元格设置文字大小, 颜色等... + * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/table-col-cell.ts + */ +class CustomColCell extends TableColCell { + getTextStyle() { + const defaultTextStyle = super.getTextStyle(); + + // 指定列 + if (this.meta.colIndex % 2 === 0) { + return { + ...defaultTextStyle, + fontSize: 16, + fill: '#396', + textAlign: 'left', + }; + } + + // 指定层级 + if (this.meta.level >= 0) { + return { + ...defaultTextStyle, + fill: 'pink', + textAlign: 'center', + }; + } + + // 指定文本 + if (this.meta.label === '子类别') { + return { + ...defaultTextStyle, + fontSize: 22, + textAlign: 'right', + }; + } + + // 使用默认处理 + return super.getTextStyle(); + } +} + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/cd9814d0-6dfa-42a6-8455-5a6bd0ff93ca.json', +) + .then((res) => res.json()) + .then((res) => { + const container = document.getElementById('container'); + const s2DataConfig = { + fields: { + columns: ['province', 'city', 'type', 'sub_type', 'number'], + }, + meta: res.meta, + data: res.data, + }; + const s2Options = { + width: 600, + height: 480, + showSeriesNumber: true, + colCell: (node, spreadsheet, headerConfig) => { + return new CustomColCell(node, spreadsheet, headerConfig); + }, + dataCell: (viewMeta) => { + return new CustomDataCell(viewMeta, viewMeta?.spreadsheet); + }, + }; + const s2 = new TableSheet(container, s2DataConfig, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/custom/custom-cell/demo/data-cell.ts b/s2-site/examples/custom/custom-cell/demo/data-cell.ts index 8a93ca9805..dc936cfeb0 100644 --- a/s2-site/examples/custom/custom-cell/demo/data-cell.ts +++ b/s2-site/examples/custom/custom-cell/demo/data-cell.ts @@ -1,5 +1,4 @@ -import { PivotSheet, DataCell, drawObjectText } from '@antv/s2'; -import { isObject } from 'lodash'; +import { DataCell, PivotSheet } from '@antv/s2'; /** * 自定义 DataCell,给数值单元格添加背景图 @@ -15,15 +14,6 @@ class CustomDataCell extends DataCell { }, }); } - - // 当配置对象时,完全接管绘制(实现趋势表的mini图功能) - protected drawTextShape() { - if (isObject(this.getMeta().fieldValue)) { - drawObjectText(this); - } else { - super.drawTextShape(); - } - } } fetch( @@ -39,28 +29,7 @@ fetch( values: ['number'], }, meta: res.meta, - data: [ - ...res.data, - // 用于绘制 mini 图的数据 - { - province: '海南省', - city: '三亚市', - type: '家具', - sub_type: '桌子', - number: { - values: { - type: 'line', - data: [ - { date: '周一', value: 110 }, - { date: '周二', value: 150 }, - { date: '周三', value: 90 }, - { date: '周三', value: 190 }, - ], - encode: { x: 'date', y: 'value' }, - }, - }, - }, - ], + data: res.data, }; const s2Options = { @@ -75,6 +44,5 @@ fetch( }; const s2 = new PivotSheet(container, s2DataConfig, s2Options); - // 使用 s2.render(); }); diff --git a/s2-site/examples/custom/custom-cell/demo/meta.json b/s2-site/examples/custom/custom-cell/demo/meta.json index cc500816a2..05f3f82822 100644 --- a/s2-site/examples/custom/custom-cell/demo/meta.json +++ b/s2-site/examples/custom/custom-cell/demo/meta.json @@ -58,7 +58,7 @@ "zh": "自定义特定单元格", "en": "custom specified cell" }, - "screenshot": "https://gw.alipayobjects.com/mdn/rms_56cbb2/afts/img/A*KJqYQKjxIwIAAAAAAAAAAAAAARQnAQ" + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*GSh4R6qdC5IAAAAAAAAAAAAADmJ7AQ/original" }, { "filename": "custom-merged-cell.ts", @@ -67,6 +67,22 @@ "en": "custom merged cell" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*B7mTTZYFjv8AAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "custom-table-cell.ts", + "title": { + "zh": "自定义明细表单元格", + "en": "custom table cell" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*-uLETb4gK9AAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "mini-chart.ts", + "title": { + "zh": "自定义 mini 图", + "en": "custom mini chart" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*apnIT4KXP3YAAAAAAAAAAAAADmJ7AQ/original" } ] } diff --git a/s2-site/examples/custom/custom-cell/demo/mini-chart.ts b/s2-site/examples/custom/custom-cell/demo/mini-chart.ts new file mode 100644 index 0000000000..ee007fa585 --- /dev/null +++ b/s2-site/examples/custom/custom-cell/demo/mini-chart.ts @@ -0,0 +1,178 @@ +import { PivotSheet, DataCell, drawObjectText } from '@antv/s2'; +import { isArray, isObject } from 'lodash'; + +/** + * 自定义 DataCell,使用 drawObjectText 绘制简易的 mini 图 + * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/data-cell.ts + */ +class CustomDataCell extends DataCell { + // 当数值为对象时,完全接管绘制, 使用内置的 `drawObjectText` 根据不同的数据结构 (见下方) 绘制不同的图形 + drawTextShape() { + const { fieldValue } = this.getMeta(); + + if (isObject(fieldValue) || isArray(fieldValue)) { + drawObjectText(this); + return; + } + + super.drawTextShape(); + } +} + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/cd9814d0-6dfa-42a6-8455-5a6bd0ff93ca.json', +) + .then((res) => res.json()) + .then((res) => { + const container = document.getElementById('container')!; + const s2DataConfig = { + fields: { + rows: ['province', 'city'], + columns: ['type', 'sub_type'], + values: ['number'], + }, + meta: res.meta, + data: [ + // 用于绘制 mini 图的数据, 数据结构请查阅: https://s2.antv.antgroup.com/manual/basic/analysis/strategy#%E9%85%8D%E7%BD%AE-mini-%E5%9B%BE + { + province: '海南省', + city: '三亚市', + type: '家具', + sub_type: '桌子', + number: { + // 折线图 + values: { + type: 'line', + data: [ + { + year: '2017', + value: -368, + }, + { + year: '2018', + value: 368, + }, + { + year: '2019', + value: 368, + }, + { + year: '2020', + value: 368, + }, + { + year: '2021', + value: 268, + }, + { + year: '2022', + value: 168, + }, + ], + encode: { x: 'year', y: 'value' }, + }, + }, + }, + { + province: '海南省', + city: '三亚市', + type: '家具', + sub_type: '沙发', + number: { + // 柱状图 + values: { + type: 'bar', + data: [ + { + year: '2017', + value: -368, + }, + { + year: '2018', + value: 328, + }, + { + year: '2019', + value: 38, + }, + { + year: '2020', + value: 168, + }, + { + year: '2021', + value: 268, + }, + { + year: '2022', + value: 368, + }, + ], + encode: { x: 'year', y: 'value' }, + }, + }, + }, + { + province: '海南省', + city: '三亚市', + type: '办公用品', + sub_type: '笔', + number: { + // 多列文本 + values: [ + [3877, -4324, '42%'], + [3877, 4324, '-42%'], + ], + }, + }, + { + province: '海南省', + city: '三亚市', + type: '办公用品', + sub_type: '纸张', + number: { + // 子弹图 + values: { + measure: 0.3, + target: 0.76, + }, + }, + }, + ...res.data, + ], + }; + + const s2Options = { + width: 1000, + height: 680, + style: { + cellCfg: { + height: 40, + }, + }, + conditions: { + text: [ + { + field: 'number', + mapping: (value, cellInfo) => { + const { meta, colIndex } = cellInfo || {}; + if (colIndex === 0 || !value || !meta?.fieldValue) { + return { + fill: '#000', + }; + } + return { + fill: value > 0 ? '#FF4D4F' : '#29A294', + }; + }, + }, + ], + }, + dataCell: (viewMeta) => { + return new CustomDataCell(viewMeta, viewMeta?.spreadsheet); + }, + }; + const s2 = new PivotSheet(container, s2DataConfig, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/custom/custom-cell/demo/totals-cell.ts b/s2-site/examples/custom/custom-cell/demo/totals-cell.ts index e716f275d2..5c140c1f7c 100644 --- a/s2-site/examples/custom/custom-cell/demo/totals-cell.ts +++ b/s2-site/examples/custom/custom-cell/demo/totals-cell.ts @@ -1,7 +1,7 @@ import { PivotSheet, RowCell, renderRect } from '@antv/s2'; /** - * 继承 RowCell, 单独修改小计/总计的背景色和文字颜色 + * 继承 RowCell, 单独修改行小计/行总计的背景色和文字颜色 (继承 ColCell, 列小计/列总计 同理) * 查看更多方法 https://github.com/antvis/S2/blob/master/packages/s2-core/src/cell/row-cell.ts */ class CustomTotalsRowCell extends RowCell { diff --git a/s2-site/examples/custom/custom-layout/demo/custom-coordinate.ts b/s2-site/examples/custom/custom-layout/demo/custom-coordinate.ts index a4e721a4f1..0df1e0189b 100644 --- a/s2-site/examples/custom/custom-layout/demo/custom-coordinate.ts +++ b/s2-site/examples/custom/custom-layout/demo/custom-coordinate.ts @@ -13,6 +13,28 @@ fetch( values: ['number'], }, data: res.data, + meta: [ + { + field: 'number', + name: '数量', + }, + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '类别', + }, + { + field: 'sub_type', + name: '子类别', + }, + ], }; const s2Options = { diff --git a/s2-site/examples/custom/custom-layout/demo/custom-data-position.ts b/s2-site/examples/custom/custom-layout/demo/custom-data-position.ts index 2ca7fa1f57..fa7c5460c0 100644 --- a/s2-site/examples/custom/custom-layout/demo/custom-data-position.ts +++ b/s2-site/examples/custom/custom-layout/demo/custom-data-position.ts @@ -13,6 +13,28 @@ fetch( values: ['number'], }, data: res.data, + meta: [ + { + field: 'number', + name: '数量', + }, + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '类别', + }, + { + field: 'sub_type', + name: '子类别', + }, + ], }; const s2Options = { diff --git a/s2-site/examples/custom/custom-layout/demo/custom-layout-arrange.ts b/s2-site/examples/custom/custom-layout/demo/custom-layout-arrange.ts index 26f8249c57..b233076ca5 100644 --- a/s2-site/examples/custom/custom-layout/demo/custom-layout-arrange.ts +++ b/s2-site/examples/custom/custom-layout/demo/custom-layout-arrange.ts @@ -13,6 +13,28 @@ fetch( values: ['number'], }, data: res.data, + meta: [ + { + field: 'number', + name: '数量', + }, + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '类别', + }, + { + field: 'sub_type', + name: '子类别', + }, + ], }; const s2Options = { diff --git a/s2-site/examples/custom/custom-layout/demo/custom-layout-hierarchy.ts b/s2-site/examples/custom/custom-layout/demo/custom-layout-hierarchy.ts index bd0fc3e70c..746ec39187 100644 --- a/s2-site/examples/custom/custom-layout/demo/custom-layout-hierarchy.ts +++ b/s2-site/examples/custom/custom-layout/demo/custom-layout-hierarchy.ts @@ -45,6 +45,28 @@ fetch( }, ], ], + meta: [ + { + field: 'number', + name: '数量', + }, + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '类别', + }, + { + field: 'sub_type', + name: '子类别', + }, + ], }; const s2Options = { diff --git a/s2-site/examples/custom/custom-layout/demo/custom-value-order.ts b/s2-site/examples/custom/custom-layout/demo/custom-value-order.ts index ba02eedb84..1bd014dc1c 100644 --- a/s2-site/examples/custom/custom-layout/demo/custom-value-order.ts +++ b/s2-site/examples/custom/custom-layout/demo/custom-value-order.ts @@ -16,6 +16,28 @@ fetch( customValueOrder: 1, }, data: res.data, + meta: [ + { + field: 'number', + name: '数量', + }, + { + field: 'province', + name: '省份', + }, + { + field: 'city', + name: '城市', + }, + { + field: 'type', + name: '类别', + }, + { + field: 'sub_type', + name: '子类别', + }, + ], }; const s2Options = { diff --git a/s2-site/examples/react-component/sheet/demo/strategy-mini-chart.tsx b/s2-site/examples/react-component/sheet/demo/strategy-mini-chart.tsx index 5de2358db1..f4b2632e37 100644 --- a/s2-site/examples/react-component/sheet/demo/strategy-mini-chart.tsx +++ b/s2-site/examples/react-component/sheet/demo/strategy-mini-chart.tsx @@ -3,6 +3,11 @@ import ReactDOM from 'react-dom'; import { SheetComponent, SheetComponentOptions } from '@antv/s2-react'; import '@antv/s2-react/dist/style.min.css'; +/** + * 该示例为 React 版本的趋势分析表 + * 如何在普通图表中使用, 请查看: https://s2.antv.antgroup.com/zh/examples/custom/custom-cell#mini-chart + */ + fetch( 'https://gw.alipayobjects.com/os/bmw-prod/b942d973-7364-4fad-a10a-369426a61376.json', ) From 3842c90b9f7df078465e67a961af6863871f3c9f Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Mon, 30 Oct 2023 10:47:38 +0800 Subject: [PATCH 14/18] =?UTF-8?q?chore:=20=F0=9F=A4=96=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20changelog=20=E6=96=87=E4=BB=B6=20(#2395)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/CHANGELOG.md | 10 ++++++++++ packages/s2-core/package.json | 2 +- packages/s2-react/CHANGELOG.md | 8 ++++++++ packages/s2-react/package.json | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/s2-core/CHANGELOG.md b/packages/s2-core/CHANGELOG.md index de6e670e28..ae568bc517 100644 --- a/packages/s2-core/CHANGELOG.md +++ b/packages/s2-core/CHANGELOG.md @@ -1,3 +1,13 @@ +# [@antv/s2-v1.51.2](https://github.com/antvis/S2/compare/@antv/s2-v1.51.1...@antv/s2-v1.51.2) (2023-10-27) + + +### Bug Fixes + +* **interaction:** 修复拖动水平滚动条后单元格选中状态被重置 close [#2376](https://github.com/antvis/S2/issues/2376) ([#2380](https://github.com/antvis/S2/issues/2380)) ([b2e9700](https://github.com/antvis/S2/commit/b2e97008122f5320342fd069a08f6e821a5c9ad6)) +* **layout:** 修复在紧凑模式列头宽度未按文本自适应 close [#2385](https://github.com/antvis/S2/issues/2385) ([#2392](https://github.com/antvis/S2/issues/2392)) ([2edd99c](https://github.com/antvis/S2/commit/2edd99c367116bad661a02893a303311787eb647)) +* 修复分组汇总时,按汇总排序获取排序数据为空 ([#2370](https://github.com/antvis/S2/issues/2370)) ([2443762](https://github.com/antvis/S2/commit/24437622e1b259288bf7f8f3505837088a6e1b9d)) +* 修复树状角头,当有存在icon时,内容与box宽度恰好相等,出现换行 close [#2389](https://github.com/antvis/S2/issues/2389) ([#2390](https://github.com/antvis/S2/issues/2390)) ([19497b3](https://github.com/antvis/S2/commit/19497b34aaa9bd19ca5f410926f3035f1aa76d09)) + # [@antv/s2-v1.51.1](https://github.com/antvis/S2/compare/@antv/s2-v1.51.0...@antv/s2-v1.51.1) (2023-10-13) diff --git a/packages/s2-core/package.json b/packages/s2-core/package.json index cbc465052a..3e28ed259f 100644 --- a/packages/s2-core/package.json +++ b/packages/s2-core/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@antv/s2", - "version": "1.51.1", + "version": "1.51.2", "main": "lib/index.js", "unpkg": "dist/index.min.js", "module": "esm/index.js", diff --git a/packages/s2-react/CHANGELOG.md b/packages/s2-react/CHANGELOG.md index b0a6bd9ed3..316f282506 100644 --- a/packages/s2-react/CHANGELOG.md +++ b/packages/s2-react/CHANGELOG.md @@ -1,3 +1,11 @@ +# [@antv/s2-react-v1.44.1](https://github.com/antvis/S2/compare/@antv/s2-react-v1.44.0...@antv/s2-react-v1.44.1) (2023-10-27) + + +### Bug Fixes + +* **interaction:** 修复拖动水平滚动条后单元格选中状态被重置 close [#2376](https://github.com/antvis/S2/issues/2376) ([#2380](https://github.com/antvis/S2/issues/2380)) ([b2e9700](https://github.com/antvis/S2/commit/b2e97008122f5320342fd069a08f6e821a5c9ad6)) +* **layout:** 修复在紧凑模式列头宽度未按文本自适应 close [#2385](https://github.com/antvis/S2/issues/2385) ([#2392](https://github.com/antvis/S2/issues/2392)) ([2edd99c](https://github.com/antvis/S2/commit/2edd99c367116bad661a02893a303311787eb647)) + # [@antv/s2-react-v1.44.0](https://github.com/antvis/S2/compare/@antv/s2-react-v1.43.0...@antv/s2-react-v1.44.0) (2023-09-22) diff --git a/packages/s2-react/package.json b/packages/s2-react/package.json index 202ab32399..4f5173f81a 100644 --- a/packages/s2-react/package.json +++ b/packages/s2-react/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@antv/s2-react", - "version": "1.44.0", + "version": "1.44.1", "main": "lib/index.js", "unpkg": "dist/index.min.js", "module": "esm/index.js", From 82a67378b0e17383ded27fdd9567752625400f0f Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Thu, 2 Nov 2023 16:32:58 +0800 Subject: [PATCH 15/18] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E5=AE=98?= =?UTF-8?q?=E7=BD=91=20S2=20=E7=89=88=E6=9C=AC=20&=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20(#2397)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: 更新官网 S2 版本 & 优化文档 * chore: 移动位置 --- README.en-US.md | 8 ++-- README.md | 10 ++-- packages/s2-react/playground/config.ts | 32 ++++++++++++- packages/s2-react/playground/index.tsx | 29 +++++++++++- .../docs/api/basic-class/spreadsheet.zh.md | 2 +- s2-site/docs/api/general/S2DataConfig.zh.md | 2 +- s2-site/docs/common/contact-us.en.md | 3 -- s2-site/docs/common/contact-us.zh.md | 3 -- s2-site/docs/common/development.zh.md | 10 +++- s2-site/docs/common/packages.zh.md | 15 ++++++ s2-site/docs/common/style.zh.md | 2 +- s2-site/docs/manual/faq.zh.md | 16 ++----- s2-site/docs/manual/getting-started.zh.md | 46 ++++++++++++------- s2-site/docs/manual/introduction.zh.md | 7 +-- .../layout/adaptive/demo/react-adaptive.tsx | 12 +++-- .../examples/layout/basic/demo/adaptive.ts | 21 +++++++++ .../examples/layout/basic/demo/colAdaptive.ts | 1 + s2-site/examples/layout/basic/demo/compact.ts | 18 +++++++- s2-site/examples/layout/basic/demo/meta.json | 10 +++- s2-site/package.json | 14 +++--- s2-site/public/site.css | 4 ++ 21 files changed, 195 insertions(+), 70 deletions(-) create mode 100644 s2-site/examples/layout/basic/demo/adaptive.ts diff --git a/README.en-US.md b/README.en-US.md index 166fd4efca..9f4e2fdd37 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -68,8 +68,9 @@ demo components and expansion capabilities, it allows developers to use it quick ## 📦 Installation ```bash -$ npm install @antv/s2 -# yarn add @antv/s2 +$ npm install @antv/s2 --save +# yarn add @antv/s2 --save +# pnpm install @antv/s2 --save ``` ## 🔨 Getting Started @@ -227,9 +228,6 @@ yarn site:start S2 - - S2 -

## 👬 Contributors diff --git a/README.md b/README.md index d636c4b73d..341116452c 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,9 @@ S2 是 AntV 在多维交叉分析表格领域的解决方案,完全基于数 ## 📦 安装 ```bash -$ npm install @antv/s2 -# yarn add @antv/s2 +$ npm install @antv/s2 --save +# yarn add @antv/s2 --save +# pnpm install @antv/s2 --save ``` ## 🔨 使用 @@ -73,7 +74,7 @@ $ npm install @antv/s2 ### 1. 数据准备
- s2DataConfig + s2DataConfig ```ts const s2DataConfig = { @@ -221,9 +222,6 @@ yarn site:start DingTalk - - qq -

## 👬 Contributors diff --git a/packages/s2-react/playground/config.ts b/packages/s2-react/playground/config.ts index 27bc560e14..ccb83ab7ac 100644 --- a/packages/s2-react/playground/config.ts +++ b/packages/s2-react/playground/config.ts @@ -1,4 +1,4 @@ -import { isUpDataValue, type Columns } from '@antv/s2'; +import { isUpDataValue, type Columns, customMerge } from '@antv/s2'; import type { S2DataConfig, ThemeCfg } from '@antv/s2'; import { getBaseSheetComponentOptions } from '@antv/s2-shared'; import type { SliderSingleProps } from 'antd'; @@ -46,6 +46,36 @@ export const pivotSheetDataCfg: S2DataConfig = { fields, }; +export const pivotSheetDataCfgForCompactMode = customMerge(pivotSheetDataCfg, { + data: [ + ...pivotSheetDataCfg.data, + { + province: '浙江', + city: '杭州', + type: '笔', + price: '11111111', + }, + { + province: '浙江', + city: '杭州', + type: '纸张', + price: '2', + }, + { + province: '浙江', + city: '舟山', + type: '笔', + price: '2', + }, + { + province: '浙江', + city: '舟山', + type: '纸张', + price: '133.333', + }, + ], +}); + export const s2Options: SheetComponentOptions = { debug: true, width: 600, diff --git a/packages/s2-react/playground/index.tsx b/packages/s2-react/playground/index.tsx index 5d27b80834..e9829dd31e 100644 --- a/packages/s2-react/playground/index.tsx +++ b/packages/s2-react/playground/index.tsx @@ -68,6 +68,7 @@ import { tableSheetMultipleColumns, tableSheetSingleColumns, s2ThemeConfig, + pivotSheetDataCfgForCompactMode, } from './config'; import './index.less'; import { ResizeConfig } from './resize'; @@ -324,6 +325,27 @@ function MainLayout() { ); }, [tableSheetColumnType]); + useUpdateEffect(() => { + switch (options.style.layoutWidthType) { + case 'compact': + updateOptions({ + style: { + cellCfg: { + width: 200, + }, + }, + }); + setDataCfg(pivotSheetDataCfgForCompactMode); + break; + + default: + updateOptions({ + style: DEFAULT_STYLE, + }); + setDataCfg(pivotSheetDataCfg); + } + }, [options.style.layoutWidthType]); + // ================== Config ======================== const mergedOptions: SheetComponentOptions = customMerge( @@ -442,7 +464,7 @@ function MainLayout() { 行列等宽 列等宽 @@ -450,7 +472,10 @@ function MainLayout() { - + 默认 简约灰 多彩蓝 diff --git a/s2-site/docs/api/basic-class/spreadsheet.zh.md b/s2-site/docs/api/basic-class/spreadsheet.zh.md index c52e37f00b..37d949e8c1 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.zh.md +++ b/s2-site/docs/api/basic-class/spreadsheet.zh.md @@ -58,7 +58,7 @@ s2.isPivotMode() | updatePagination | 更新分页 | (pagination: [Pagination](/docs/api/general/S2Options#pagination)) => void | | | getContentHeight | 获取当前表格实际内容高度 | `() => number` | | | changeSheetSize (别名:changeSize) | 修改表格画布大小,不用重新加载数据 | `(width?: number, height?: number) => void` | | -| getLayoutWidthType | 获取单元格宽度布局类型(LayoutWidthType: `adaptive(自适应)` \| `colAdaptive(列自适应)` \| `compact(紧凑)`) | () => `LayoutWidthType`| | +| getLayoutWidthType | 获取单元格宽度布局类型。[详情](/api/general/s2-options#style) | () => `adaptive \| colAdaptive \| compact` | | | getRowNodes | 获取行头节点 | (level: number) => [Node[]](/docs/api/basic-class/node/) | | | getRowLeafNodes | 获取行头叶子节点 | () => [Node[]](/docs/api/basic-class/node/) | | | getColumnNodes | 获取列头节点 | (level: number) => [Node[]](/docs/api/basic-class/node/) | | diff --git a/s2-site/docs/api/general/S2DataConfig.zh.md b/s2-site/docs/api/general/S2DataConfig.zh.md index f06b8effbe..55d5bee498 100644 --- a/s2-site/docs/api/general/S2DataConfig.zh.md +++ b/s2-site/docs/api/general/S2DataConfig.zh.md @@ -68,7 +68,7 @@ object **必选**,_default:null_ | columns | 列维度列表 | [Columns[]](#columns) | `[]` | | | values | 指标维度列表 | `string[]` | `[]` | | | valueInCols | 指标维度是否在列头 | `boolean` | `true` | | -| customValueOrder | 自定义指标维度在行列头中的层级顺序 (即 `values` 的 顺序,从 `0` 开始) [查看示例](/zh/examples/custom/custom-layout/#custom-value-order) | `number` | - | | +| customValueOrder | 自定义指标维度在行列头中的层级顺序 (即 `values` 的 顺序,从 `0` 开始,**仅支持单指标**) [查看示例](/zh/examples/custom/custom-layout/#custom-value-order) | `number` | - | | ### Meta diff --git a/s2-site/docs/common/contact-us.en.md b/s2-site/docs/common/contact-us.en.md index 192fd5b82e..397c40a4ad 100644 --- a/s2-site/docs/common/contact-us.en.md +++ b/s2-site/docs/common/contact-us.en.md @@ -7,7 +7,4 @@ order: 5 DingTalk - - qq -

diff --git a/s2-site/docs/common/contact-us.zh.md b/s2-site/docs/common/contact-us.zh.md index 59ae6f1dba..6281d0dc71 100644 --- a/s2-site/docs/common/contact-us.zh.md +++ b/s2-site/docs/common/contact-us.zh.md @@ -7,7 +7,4 @@ order: 5 DingTalk - - qq -

diff --git a/s2-site/docs/common/development.zh.md b/s2-site/docs/common/development.zh.md index 7e5bee0bc2..88b9a3135d 100644 --- a/s2-site/docs/common/development.zh.md +++ b/s2-site/docs/common/development.zh.md @@ -1,4 +1,12 @@ -跃跃欲试想贡献?[查看贡献指南](https://s2.antv.antgroup.com/manual/contribution) +跃跃欲试想贡献?[查看贡献指南](https://s2.antv.antgroup.com/manual/contribution) , 欢迎 [Pull Request](https://github.com/antvis/S2/pulls),或给我们 [报告 Bug](https://github.com/antvis/S2/issues/new?assignees=&labels=&projects=&template=bug-report.md&title=%F0%9F%90%9B). + +> 强烈建议花一点你的宝贵时间阅读: + +- [《提 Issue 前必读》](https://github.com/antvis/S2/issues/1904) +- [《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393) +- [《如何有效地报告 Bug》](https://www.chiark.greenend.org.uk/~sgtatham/bugs-cn.html) +- [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way) +- [《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) ```bash git clone git@github.com:antvis/S2.git diff --git a/s2-site/docs/common/packages.zh.md b/s2-site/docs/common/packages.zh.md index 3b0767f32b..01f71f584a 100644 --- a/s2-site/docs/common/packages.zh.md +++ b/s2-site/docs/common/packages.zh.md @@ -3,8 +3,23 @@ title: Packages order: 5 --- +- `@antv/s2`: 基于 `Canvas` 和 [AntV/G](https://g.antv.vision/zh/docs/guide/introduce) 开发,提供基本的表格展示/交互等能力。 +- `@antv/s2-react`: 基于 `React` 和 `@antv/s2` 封装,提供配套的分析组件,配置项和 `@antv/s2` 通用。 +- `@antv/s2-vue`: 基于 `Vue3` 和 `@antv/s2` 封装,配置项和 `@antv/s2` 通用,如果你想在 `Vue2` 中使用,请使用 `@antv/s2`。 + +也就是说 `@antv/s2` 和**框架无关**,你也可以在 `Vue`, `Angular` 等框架中直接使用。 + | 版本号 | 稳定版 | 测试版 | 预览版 | 先行版 | 包大小 | 下载量 | | -------- | ------ | --------- | ---------- | ---------- | ---------- | ------ | | [@antv/s2](https://github.com/antvis/S2/tree/master/packages/s2-core) | ![latest](https://img.shields.io/npm/v/@antv/s2/latest.svg) | ![beta](https://img.shields.io/npm/v/@antv/s2/beta.svg) | ![alpha](https://img.shields.io/npm/v/@antv/s2/alpha.svg) | ![next](https://img.shields.io/npm/v/@antv/s2/next.svg) | ![size](https://img.badgesize.io/https:/unpkg.com/@antv/s2@latest/dist/index.min.js?label=gzip%20size&compression=gzip) | ![download](https://img.shields.io/npm/dm/@antv/s2.svg) | | [@antv/s2-react](https://github.com/antvis/S2/tree/master/packages/s2-react) | ![latest](https://img.shields.io/npm/v/@antv/s2-react/latest.svg) | ![beta](https://img.shields.io/npm/v/@antv/s2-react/beta.svg) | ![alpha](https://img.shields.io/npm/v/@antv/s2-react/alpha.svg) | ![next](https://img.shields.io/npm/v/@antv/s2-react/next.svg)| ![size](https://img.badgesize.io/https:/unpkg.com/@antv/s2-react@latest/dist/index.min.js?label=gzip%20size&compression=gzip) | ![download](https://img.shields.io/npm/dm/@antv/s2-react.svg) | | [@antv/s2-vue](https://github.com/antvis/S2/tree/master/packages/s2-vue) | ![latest](https://img.shields.io/npm/v/@antv/s2-vue/latest.svg) | ![beta](https://img.shields.io/npm/v/@antv/s2-vue/beta.svg) | ![alpha](https://img.shields.io/npm/v/@antv/s2-vue/alpha.svg) | ![next](https://img.shields.io/npm/v/@antv/s2-vue/next.svg) | ![size](https://img.badgesize.io/https:/unpkg.com/@antv/s2-vue@latest/dist/index.min.js?label=gzip%20size&compression=gzip) | ![download](https://img.shields.io/npm/dm/@antv/s2-vue.svg) | + +:::info{title='如何获取新版本发布通知?'} + +- 订阅:[https://github.com/antvis/S2/releases.atom](https://github.com/antvis/S2/releases.atom) 来获得新版本发布的通知。 +- 加入钉钉交流群,新版本发布后,会通过🤖 群机器人推送。 +- `Watch` [S2 代码仓库](https://github.com/antvis/S2), 选择 `Custom - Releases` 来获取消息推送。 + +![preview](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*NKYFSKFV_scAAAAAAAAAAAAADmJ7AQ/original) +::: diff --git a/s2-site/docs/common/style.zh.md b/s2-site/docs/common/style.zh.md index 22f4c3e823..26b81349fd 100644 --- a/s2-site/docs/common/style.zh.md +++ b/s2-site/docs/common/style.zh.md @@ -9,7 +9,7 @@ object **必选**,_default:null_ 功能描述:样式设置 | 参数 | 类型 | 必选 | 默认值 | 功能描述 | | --- | --- | --- | --- | --- | -| layoutWidthType | `adaptive \| colAdaptive \| compact` | | | 单元格宽度布局类型
`adaptive` : 行列等宽,均分整个 `Canvas` 画布宽度
`colAdaptive`:列等宽,行头紧凑布局,列等分画布宽度减去行头宽度的剩余宽度
`compact`:行列紧凑布局,列头宽度为内容实际宽度 (采样列前 50 个数值), 指标维度少的时候无法布满整个画布 | +| layoutWidthType | `adaptive \| colAdaptive \| compact` | | | 单元格宽度布局类型
`adaptive` : 行列等宽,均分整个表格 (`Canvas`) 画布宽度
`colAdaptive`:列等宽,行头紧凑布局,列等分画布宽度减去行头宽度的剩余宽度
`compact`:行列紧凑布局,列头宽度为内容实际宽度 (采样列前 50 个数值), 指标维度少的时候无法布满整个画布 | | showTreeLeafNodeAlignDot | `boolean` | | false | 树状模式下叶子节点是否显示层级占位点 | | treeRowsWidth | `number` | | 120 | 树状模式行单元格宽度 (优先级大于 `rowCfg.width` 和 `rowCfg.treeRowsWidth (已废弃)`) | | hierarchyCollapse | `boolean` | | `false` | 在树状结构模式下行头是否默认展开。 | diff --git a/s2-site/docs/manual/faq.zh.md b/s2-site/docs/manual/faq.zh.md index 99d67ee8c9..706556faeb 100644 --- a/s2-site/docs/manual/faq.zh.md +++ b/s2-site/docs/manual/faq.zh.md @@ -4,7 +4,7 @@ order: 8 --- :::warning{title="一些建议"} -**在提出问题前,请确保你已经仔细阅读了一遍文档,查看了相关图表示例,并且已经查看了常见问题。** +**在提出问题前,请确保你已经仔细阅读了一遍文档,查看了相关图表示例,并且已经查看了常见问题和 Issues。** ::: ## 1. 使用问题 @@ -254,17 +254,7 @@ s2.setTheme({ 目前只有 React 版本 `@antv/s2-react` 支持编辑表格,其他版本暂不支持,需参考 [源码](https://github.com/antvis/S2/blob/2d85d5739f5a3a52e92df699a935df93aa2a6a73/packages/s2-react/src/components/sheets/editable-sheet/index.tsx#L10) 自行实现 -### S2 有对应的 `Vue` 或者 `Angular` 版本吗? - -目前,S2 由三个包构成 - -- `@antv/s2`: 基于 `canvas` 和 [AntV/G](https://g.antv.vision/zh/docs/guide/introduce) 开发,提供基本的表格展示/交互等能力 -- `@antv/s2-react`: 基于 `@antv/s2` 封装,提供配套的分析组件 -- `@antv/s2-vue`: 基于 `Vue3` 和 `@antv/s2` 封装,提供配套的分析组件 - -也就是说 `@antv/s2` 和**框架无关**,你可以在 `Vue`, `Angular` 等框架中使用。 - -以下是版本概览: +### S2 有对应的 `Vue` 或者 `Angular` 版本吗?如何获取新版本发布通知? @@ -323,6 +313,8 @@ s2.setTheme({ ### 有讨论群吗? +交流群不提供任何答疑,有任何问题请直接提交 [Issue](https://github.com/antvis/S2/issues/new/choose) 或者 [Discussion](https://github.com/antvis/S2/discussions/new?category=q-a), 当然,也期待你的 [Pull request](https://github.com/antvis/S2/pulls). + ## 2. 错误和警告 diff --git a/s2-site/docs/manual/getting-started.zh.md b/s2-site/docs/manual/getting-started.zh.md index 64705d1340..4f23674fd9 100644 --- a/s2-site/docs/manual/getting-started.zh.md +++ b/s2-site/docs/manual/getting-started.zh.md @@ -2,26 +2,30 @@ title: 快速上手 order: 1 --- + ## 📦 安装 -### npm | yarn 安装 +### 使用 npm 或 yarn 或 pnpm 安装 ```bash # npm -$ npm install @antv/s2 +$ npm install @antv/s2 --save # yarn -$ yarn add @antv/s2 +$ yarn add @antv/s2 --save + +# pnpm +$ pnpm install @antv/s2 --save ``` ### 使用 React 或 Vue3 版本 ```bash # React -$ yarn add @antv/s2 @antv/s2-react +$ yarn add @antv/s2 @antv/s2-react --save # Vue3 -$ yarn add @antv/s2 @antv/s2-vue +$ yarn add @antv/s2 @antv/s2-vue --save ``` ### 浏览器引入(不推荐) @@ -34,6 +38,8 @@ $ yarn add @antv/s2 @antv/s2-vue 创建 `S2` 表格有三种方式,基础类版本 `(s2-core)` 和 基于 `core` 层 封装的 `React` 和 `Vue3` 版本 +### 版本 + ### 基础类 @@ -184,7 +190,8 @@ s2.render(); ### `React` 版本 -`S2` 提供了开箱即用的 `React` 版本 [表格组件](/examples/gallery#category-表格组件), 还有丰富的配套 [分析组件](/examples/gallery#category-Tooltip), 帮助开发者快速满足业务看数分析需求。 +`S2` 提供了开箱即用的 `React` 版本 [表格组件](examples/gallery#category-表格组件) +, 还有丰富的配套 [分析组件](/examples/gallery#category-Tooltip), 帮助开发者快速满足业务看数分析需求。 #### 表格组件使用 @@ -194,8 +201,6 @@ import ReactDOM from 'react-dom'; import { SheetComponent } from '@antv/s2-react'; import '@antv/s2-react/dist/style.min.css'; -const container = document.getElementById('container'); - ReactDOM.render( diff --git a/s2-site/docs/manual/introduction.zh.md b/s2-site/docs/manual/introduction.zh.md index c577f4e01a..7e0c4361e5 100644 --- a/s2-site/docs/manual/introduction.zh.md +++ b/s2-site/docs/manual/introduction.zh.md @@ -27,7 +27,7 @@ redirect_from: ## ❓ 什么是 S2 -[S2](https://github.com/antvis/s2) 是一个面向可视分析领域的数据驱动的表可视化引擎。"S" 取自于 "SpreadSheet" 的两个 "S","2" 代表了透视表中的行列两个维度。旨在提供美观、易用、高性能、易扩展的多维表格。 +[S2](https://github.com/antvis/s2) 是一个面向可视分析领域的数据驱动的表可视化引擎。`S` 取自于 `SpreadSheet` 的两个 `S`,`2` 代表了透视表中的行列两个维度。旨在提供美观、易用、高性能、易扩展的多维表格。 ![demos](https://gw.alipayobjects.com/zos/antfincdn/6R5Koawk9L/huaban%2525202.png) @@ -42,8 +42,9 @@ redirect_from: ## 📦 安装 ```bash -npm install @antv/s2 -# yarn add @antv/s2 +npm install @antv/s2 --save +# yarn add @antv/s2 --save +# pnpm install @antv/s2 --save ``` ## 🔨 使用 diff --git a/s2-site/examples/layout/adaptive/demo/react-adaptive.tsx b/s2-site/examples/layout/adaptive/demo/react-adaptive.tsx index f89d279ec9..8133f86da9 100644 --- a/s2-site/examples/layout/adaptive/demo/react-adaptive.tsx +++ b/s2-site/examples/layout/adaptive/demo/react-adaptive.tsx @@ -1,7 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { SheetComponent } from '@antv/s2-react'; -import { concat, debounce, forEach, map } from 'lodash'; fetch( 'https://gw.alipayobjects.com/os/bmw-prod/2a5dbbc8-d0a7-4d02-b7c9-34f6ca63cff6.json', @@ -13,13 +12,16 @@ fetch( height: 480, }; - ReactDOM.render( - document.getElementById('container') - }} />, + getContainer: () => document.getElementById('container'), + }} + />, document.getElementById('container'), ); }); diff --git a/s2-site/examples/layout/basic/demo/adaptive.ts b/s2-site/examples/layout/basic/demo/adaptive.ts new file mode 100644 index 0000000000..e63fdb9be5 --- /dev/null +++ b/s2-site/examples/layout/basic/demo/adaptive.ts @@ -0,0 +1,21 @@ +import { PivotSheet } from '@antv/s2'; + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/2a5dbbc8-d0a7-4d02-b7c9-34f6ca63cff6.json', +) + .then((res) => res.json()) + .then((dataCfg) => { + const container = document.getElementById('container'); + + const s2Options = { + width: 600, + height: 480, + style: { + // 了解更多: https://s2.antv.antgroup.com/api/general/s2-options#style + layoutWidthType: 'adaptive', + }, + }; + const s2 = new PivotSheet(container, dataCfg, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/layout/basic/demo/colAdaptive.ts b/s2-site/examples/layout/basic/demo/colAdaptive.ts index a8c54becc5..6c37ca45da 100644 --- a/s2-site/examples/layout/basic/demo/colAdaptive.ts +++ b/s2-site/examples/layout/basic/demo/colAdaptive.ts @@ -11,6 +11,7 @@ fetch( width: 600, height: 480, style: { + // 了解更多: https://s2.antv.antgroup.com/api/general/s2-options#style layoutWidthType: 'colAdaptive', }, }; diff --git a/s2-site/examples/layout/basic/demo/compact.ts b/s2-site/examples/layout/basic/demo/compact.ts index bf1b9ce198..408058793c 100644 --- a/s2-site/examples/layout/basic/demo/compact.ts +++ b/s2-site/examples/layout/basic/demo/compact.ts @@ -5,16 +5,32 @@ fetch( ) .then((res) => res.json()) .then((dataCfg) => { + // 增加几条长度不一致的 mock 数据 + dataCfg.data.at(0).number = 11111111; + dataCfg.data.at(6).number = 7777; + dataCfg.data.at(-1).number = 666666; + const container = document.getElementById('container'); const s2Options = { - width: 400, + width: 600, height: 480, style: { + // 了解更多: https://s2.antv.antgroup.com/api/general/s2-options#style layoutWidthType: 'compact', }, }; + const s2 = new PivotSheet(container, dataCfg, s2Options); + // 紧凑模式下, 列头宽度为实际内容宽度 (取当前列最大值, 采样每一列前 50 条数据) + s2.setTheme({ + dataCell: { + text: { + fontSize: 16, + }, + }, + }); + s2.render(); }); diff --git a/s2-site/examples/layout/basic/demo/meta.json b/s2-site/examples/layout/basic/demo/meta.json index be75879383..7ac8996af1 100644 --- a/s2-site/examples/layout/basic/demo/meta.json +++ b/s2-site/examples/layout/basic/demo/meta.json @@ -4,13 +4,21 @@ "en": "Pivot" }, "demos": [ + { + "filename": "adaptive.ts", + "title": { + "zh": "行列等宽布局 (默认)", + "en": "Adaptive (Default)" + }, + "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/cEkDC%26g%24xj/df31b66d-4a76-4e69-be5a-f467af2d337d.png" + }, { "filename": "compact.ts", "title": { "zh": "紧凑布局", "en": "Compact" }, - "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/yzBFZaMMLH/55a2d580-1121-468e-9d0b-25f3e9ae25df.png" + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*NN_lTpAqhQkAAAAAAAAAAAAADmJ7AQ/original" }, { "filename": "colAdaptive.ts", diff --git a/s2-site/package.json b/s2-site/package.json index 0bde635b0f..0d5394b618 100644 --- a/s2-site/package.json +++ b/s2-site/package.json @@ -28,14 +28,14 @@ "sync:s2-lock": "yarn upgrade @antv/s2 @antv/s2-react --latest" }, "dependencies": { - "@ant-design/icons": "^4.7.0", - "@antv/dumi-theme-antv": "^0.3.5", + "@ant-design/icons": "^4.8.1", + "@antv/dumi-theme-antv": "^0.3.20", "@antv/g-canvas": "^0.5.12", - "@antv/s2": "^1.42.0", - "@antv/s2-react": "^1.37.0", - "antd": "^4.24.1", - "copy-to-clipboard": "^3.3.1", - "dumi": "^2.0.3", + "@antv/s2": "^1.51.2", + "@antv/s2-react": "^1.44.1", + "antd": "^4.24.14", + "copy-to-clipboard": "^3.3.3", + "dumi": "^2.2.14", "gh-pages": "^3.1.0", "lodash": "^4.17.21", "react-color": "^2.19.3" diff --git a/s2-site/public/site.css b/s2-site/public/site.css index 9c282610b2..bbe2cc2b58 100644 --- a/s2-site/public/site.css +++ b/s2-site/public/site.css @@ -18,6 +18,10 @@ margin: 20px 0; } +.dumi-default-table { + margin: 0; +} + img[alt='preview'] { margin: 10px 0; } From 0310c2f7f6ea054a79f0fc71b972ee1d3dd1c649 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Mon, 6 Nov 2023 19:31:55 +0800 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B6=8B?= =?UTF-8?q?=E5=8A=BF=E5=88=86=E6=9E=90=E8=A1=A8=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E5=88=97=E5=A4=B4=20tooltip=20=E5=90=8E=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E4=BD=BF=E7=94=A8=E8=A1=8C=E5=A4=B4=E7=9A=84=20toolti?= =?UTF-8?q?p=20(#2399)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sheets/strategy-sheet/index-spec.tsx | 41 ++++++++++++++++--- .../sheets/strategy-sheet/index.tsx | 13 +++--- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/s2-react/__tests__/unit/components/sheets/strategy-sheet/index-spec.tsx b/packages/s2-react/__tests__/unit/components/sheets/strategy-sheet/index-spec.tsx index 46facbf366..66cbf1a2ef 100644 --- a/packages/s2-react/__tests__/unit/components/sheets/strategy-sheet/index-spec.tsx +++ b/packages/s2-react/__tests__/unit/components/sheets/strategy-sheet/index-spec.tsx @@ -18,7 +18,7 @@ import { SheetComponent, SheetComponentOptions, } from '../../../../../src/components'; -import { getContainer } from '../../../../util/helpers'; +import { getContainer, sleep } from '../../../../util/helpers'; import { StrategyOptions, StrategySheetDataConfig, @@ -36,6 +36,7 @@ describe(' Tests', () => { ReactDOM.unmountComponentAtNode(container); container.remove(); }); + const renderStrategySheet = ( options: SheetComponentOptions | null, dataCfg?: S2DataConfig, @@ -74,7 +75,7 @@ describe(' Tests', () => { renderStrategySheet(s2Options); - expect(s2.options.tooltip.data.content).toEqual(content); + expect(s2.options.tooltip!.data!.content).toEqual(content); }); test('should replace hierarchyType with "customTree" when rows is empty and contains custom tree items', () => { @@ -114,16 +115,21 @@ describe(' Tests', () => { renderStrategySheet(s2Options, s2DataConfig); - expect(s2.options.style.colCfg.hideMeasureColumn).toBeTruthy(); + expect(s2.options.style!.colCfg!.hideMeasureColumn).toBeTruthy(); }); test('should enable hidden columns operation', () => { renderStrategySheet(null); - expect(s2.options.tooltip.operation.hiddenColumns).toBeTruthy(); + expect(s2.options.tooltip!.operation!.hiddenColumns).toBeTruthy(); }); - test.each([CellTypes.ROW_CELL, CellTypes.COL_CELL, CellTypes.DATA_CELL])( + test.each([ + CellTypes.ROW_CELL, + CellTypes.COL_CELL, + CellTypes.DATA_CELL, + CellTypes.CORNER_CELL, + ])( 'should overwrite strategy sheet default custom tooltip and render custom %s tooltip', (cellType) => { const content = `${cellType} test content`; @@ -131,6 +137,7 @@ describe(' Tests', () => { [CellTypes.ROW_CELL]: 'row', [CellTypes.COL_CELL]: 'col', [CellTypes.DATA_CELL]: 'data', + [CellTypes.CORNER_CELL]: 'corner', }[cellType]; renderStrategySheet({ @@ -150,6 +157,28 @@ describe(' Tests', () => { }, ); + test('should get current cell custom tooltip content', () => { + renderStrategySheet({ + tooltip: { + showTooltip: true, + row: { + content: () =>
{CellTypes.ROW_CELL}
, + }, + data: { + content: () =>
{CellTypes.DATA_CELL}
, + }, + }, + }); + + jest.spyOn(s2, 'getCellType').mockReturnValueOnce(CellTypes.COL_CELL); + + s2.showTooltipWithInfo({} as GEvent, []); + + [CellTypes.ROW_CELL, CellTypes.DATA_CELL].forEach((content) => { + expect(s2.tooltip.container.innerText).not.toEqual(content); + }); + }); + test('should render correctly KPI bullet column measure text', () => { renderStrategySheet( { @@ -209,7 +238,7 @@ describe(' Tests', () => { expect(textList).toEqual([cornerExtraFieldText, '日期']); - expect(cornerNode.label).toEqual(cornerExtraFieldText); + expect(cornerNode!.label).toEqual(cornerExtraFieldText); }); test('should format corner date field', () => { diff --git a/packages/s2-react/src/components/sheets/strategy-sheet/index.tsx b/packages/s2-react/src/components/sheets/strategy-sheet/index.tsx index fcced7995d..f7252e29e0 100644 --- a/packages/s2-react/src/components/sheets/strategy-sheet/index.tsx +++ b/packages/s2-react/src/components/sheets/strategy-sheet/index.tsx @@ -1,14 +1,13 @@ -import type { S2CellType } from '@antv/s2'; +import type { S2CellType, S2DataConfig } from '@antv/s2'; import { - type ColHeaderConfig, - customMerge, Node, - type S2DataConfig, - type S2Options, SpreadSheet, - type ViewMeta, + customMerge, + type ColHeaderConfig, type MultiData, + type S2Options, type TooltipShowOptions, + type ViewMeta, } from '@antv/s2'; import { isArray, isEmpty, isFunction, isNil, size } from 'lodash'; import React from 'react'; @@ -114,7 +113,7 @@ export const StrategySheet: React.FC = React.memo( }, col: { content: (cell, tooltipOptions) => - getContent('row')(cell, tooltipOptions) ?? ( + getContent('col')(cell, tooltipOptions) ?? ( ), }, From 432c0c766e993b2124f3cc372c0d4776fed690d1 Mon Sep 17 00:00:00 2001 From: Aimer <44153856+aimerthyr@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:48:26 -0600 Subject: [PATCH 17/18] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=80=8F?= =?UTF-8?q?=E8=A7=86=E8=A1=A8=E6=98=8E=E7=BB=86=E8=A1=A8=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=A0=BC=E8=99=9A=E7=BA=BF&=E5=88=86=E5=89=B2=E7=BA=BF?= =?UTF-8?q?=E8=99=9A=E7=BA=BF(#2400)=20(#2401)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 支持透视表明细表单元格虚线&分割线虚线(#2400) * docs: 修改contributing.md --- CONTRIBUTING.md | 2 +- .../spreadsheet/__snapshots__/theme-spec.ts.snap | 6 ++++++ packages/s2-core/src/cell/col-cell.ts | 2 ++ packages/s2-core/src/common/interface/theme.ts | 4 ++++ packages/s2-core/src/facet/header/frame.ts | 2 ++ packages/s2-core/src/facet/table-facet.ts | 2 ++ packages/s2-core/src/group/grid-group.ts | 2 ++ packages/s2-core/src/theme/index.ts | 9 +++++++++ packages/s2-core/src/utils/cell/cell.ts | 3 +++ s2-site/docs/api/general/S2Theme.en.md | 2 ++ s2-site/docs/api/general/S2Theme.zh.md | 2 ++ 11 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd6657e86c..730543c82d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ 2. 安装依赖:`yarn bootstrap` 或者 `yarn` 3. 提交你的改动,commit 请遵守 [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.uyo6cb12dt6w) 4. 如果你的改动是修复 bug, 还可以在提交信息后面加上 `close #issue 号`, 这样可以在 pr 合并后,可以自动关闭对应的 issue, 比如 `fix: render bug close #123` -5. 确保加上了对应的单元测试和文档 (如有必要) +5. 确保加上了对应的单元测试和文档 (如果有 `Snapshot` UI 快照 (.snap 文件)更新, 可以运行 `yarn core:test -- -u` 和 `yarn react:test -- -u` 自动更新, 并一起提交上来, 请勿手动编辑) 6. 所有 Lint 和 Test 检查通过后,并且 review 通过,我们会合并你的 pr. ![preview](https://gw.alipayobjects.com/zos/antfincdn/ssOxFrycD/86339514-5f9a-4101-8690-e47c97cd8af5.png) diff --git a/packages/s2-core/__tests__/spreadsheet/__snapshots__/theme-spec.ts.snap b/packages/s2-core/__tests__/spreadsheet/__snapshots__/theme-spec.ts.snap index f9b83c35d8..50d2c40be0 100644 --- a/packages/s2-core/__tests__/spreadsheet/__snapshots__/theme-spec.ts.snap +++ b/packages/s2-core/__tests__/spreadsheet/__snapshots__/theme-spec.ts.snap @@ -19,6 +19,7 @@ Object { "cell": Object { "backgroundColor": "#E0E9FD", "backgroundColorOpacity": 1, + "borderDash": Array [], "horizontalBorderColor": "#CCDBFC", "horizontalBorderColorOpacity": 1, "horizontalBorderWidth": 1, @@ -102,6 +103,7 @@ Object { "cell": Object { "backgroundColor": "#E0E9FD", "backgroundColorOpacity": 1, + "borderDash": Array [], "horizontalBorderColor": "#CCDBFC", "horizontalBorderColorOpacity": 1, "horizontalBorderWidth": 1, @@ -147,6 +149,7 @@ Object { "cell": Object { "backgroundColor": "#FFFFFF", "backgroundColorOpacity": 1, + "borderDash": Array [], "crossBackgroundColor": "#F5F8FE", "horizontalBorderColor": "#E0E9FD", "horizontalBorderColorOpacity": 1, @@ -273,6 +276,7 @@ Object { "cell": Object { "backgroundColor": "#FFFFFF", "backgroundColorOpacity": 1, + "borderDash": Array [], "crossBackgroundColor": "#F5F8FE", "horizontalBorderColor": "#E0E9FD", "horizontalBorderColorOpacity": 1, @@ -420,6 +424,7 @@ Object { "cell": Object { "backgroundColor": "#F5F8FE", "backgroundColorOpacity": 1, + "borderDash": Array [], "horizontalBorderColor": "#E0E9FD", "horizontalBorderColorOpacity": 1, "horizontalBorderWidth": 1, @@ -512,6 +517,7 @@ Object { "trackColor": "rgba(0,0,0,0.01)", }, "splitLine": Object { + "borderDash": Array [], "horizontalBorderColor": "#326EF4", "horizontalBorderColorOpacity": 0.2, "horizontalBorderWidth": 2, diff --git a/packages/s2-core/src/cell/col-cell.ts b/packages/s2-core/src/cell/col-cell.ts index bf73bd4095..fcab2334e3 100644 --- a/packages/s2-core/src/cell/col-cell.ts +++ b/packages/s2-core/src/cell/col-cell.ts @@ -460,6 +460,7 @@ export class ColCell extends HeaderCell { horizontalBorderColor, horizontalBorderWidth, horizontalBorderColorOpacity, + borderDash, } = this.theme.splitLine; const lineX = this.isLastColumn() ? x + width - horizontalBorderWidth : x; @@ -475,6 +476,7 @@ export class ColCell extends HeaderCell { stroke: horizontalBorderColor, lineWidth: horizontalBorderWidth, strokeOpacity: horizontalBorderColorOpacity, + lineDash: borderDash, }, ); } diff --git a/packages/s2-core/src/common/interface/theme.ts b/packages/s2-core/src/common/interface/theme.ts index acf8a63a0a..ef17aa4e3b 100644 --- a/packages/s2-core/src/common/interface/theme.ts +++ b/packages/s2-core/src/common/interface/theme.ts @@ -139,6 +139,8 @@ export interface CellTheme { miniBarChartHeight?: number; /* @deprecated 已废弃, 请用 miniChartTheme.interval.fill 代替 */ miniBarChartFillColor?: string; + /** 单元格边线虚线 */ + borderDash?: number[]; } export interface IconTheme { @@ -214,6 +216,8 @@ export interface SplitLine { /* 线性变化右侧颜色 */ right: string; }; + /** 分割线虚线 */ + borderDash?: number[]; } export interface DefaultCellTheme extends GridAnalysisCellTheme { /* 粗体文本样式 */ diff --git a/packages/s2-core/src/facet/header/frame.ts b/packages/s2-core/src/facet/header/frame.ts index 364cc9d88a..0c18d38b47 100644 --- a/packages/s2-core/src/facet/header/frame.ts +++ b/packages/s2-core/src/facet/header/frame.ts @@ -65,6 +65,7 @@ export class Frame extends Group { stroke: splitLine.verticalBorderColor, lineWidth: splitLine.verticalBorderWidth, opacity: splitLine.verticalBorderColorOpacity, + lineDash: splitLine.borderDash, }, }); } @@ -94,6 +95,7 @@ export class Frame extends Group { stroke: splitLine.horizontalBorderColor, lineWidth: splitLine.horizontalBorderWidth, opacity: splitLine.horizontalBorderColorOpacity, + lineDash: splitLine.borderDash, }, }); } diff --git a/packages/s2-core/src/facet/table-facet.ts b/packages/s2-core/src/facet/table-facet.ts index e1608eb241..23ab9e67e2 100644 --- a/packages/s2-core/src/facet/table-facet.ts +++ b/packages/s2-core/src/facet/table-facet.ts @@ -667,12 +667,14 @@ export class TableFacet extends BaseFacet { lineWidth: style?.verticalBorderWidth, stroke: style?.verticalBorderColor, opacity: style?.verticalBorderColorOpacity, + lineDash: style?.borderDash, }; const horizontalBorderStyle = { lineWidth: style?.horizontalBorderWidth, stroke: style?.horizontalBorderColor, opacity: style?.horizontalBorderColorOpacity, + lineDash: style?.borderDash, }; if (frozenColCount > 0) { diff --git a/packages/s2-core/src/group/grid-group.ts b/packages/s2-core/src/group/grid-group.ts index 4f92f9b085..f0a6b28c41 100644 --- a/packages/s2-core/src/group/grid-group.ts +++ b/packages/s2-core/src/group/grid-group.ts @@ -70,6 +70,7 @@ export class GridGroup extends Group { strokeOpacity: style.verticalBorderColorOpacity, lineWidth: verticalBorderWidth, lineCap: SQUARE_LINE_CAP, + lineDash: style.borderDash, }, ); }); @@ -90,6 +91,7 @@ export class GridGroup extends Group { strokeOpacity: style.horizontalBorderColorOpacity, lineWidth: horizontalBorderWidth, lineCap: SQUARE_LINE_CAP, + lineDash: style.borderDash, }, ); }); diff --git a/packages/s2-core/src/theme/index.ts b/packages/s2-core/src/theme/index.ts index 9bc4c7074b..21ac42bc90 100644 --- a/packages/s2-core/src/theme/index.ts +++ b/packages/s2-core/src/theme/index.ts @@ -55,6 +55,8 @@ export const getTheme = ( // ----------- border width -------------- horizontalBorderWidth: 1, verticalBorderWidth: 1, + // -------------- border dash ----------------- + borderDash: [], // -------------- layout ----------------- padding: { top: 8, @@ -198,6 +200,8 @@ export const getTheme = ( // ----------- border width -------------- horizontalBorderWidth: 1, verticalBorderWidth: 1, + // -------------- border dash ----------------- + borderDash: [], // -------------- layout ----------------- padding: { top: 0, @@ -268,6 +272,8 @@ export const getTheme = ( // ----------- bottom border width -------------- horizontalBorderWidth: 1, verticalBorderWidth: 1, + // -------------- border dash ----------------- + borderDash: [], // -------------- layout ----------------- padding: { top: 0, @@ -362,6 +368,8 @@ export const getTheme = ( // ----------- border width -------------- horizontalBorderWidth: 1, verticalBorderWidth: 1, + // -------------- border dash ----------------- + borderDash: [], // -------------- layout ----------------- padding: { top: 0, @@ -461,6 +469,7 @@ export const getTheme = ( left: 'rgba(0,0,0,0.1)', right: 'rgba(0,0,0,0)', }, + borderDash: [], }, // ------------- prepareSelectMask ----------------- prepareSelectMask: { diff --git a/packages/s2-core/src/utils/cell/cell.ts b/packages/s2-core/src/utils/cell/cell.ts index f49b25d3eb..42db386cca 100644 --- a/packages/s2-core/src/utils/cell/cell.ts +++ b/packages/s2-core/src/utils/cell/cell.ts @@ -266,6 +266,7 @@ export const getBorderPositionAndStyle = ( verticalBorderWidth, verticalBorderColor, verticalBorderColorOpacity, + borderDash, } = style; let x1; let y1; @@ -293,6 +294,7 @@ export const getBorderPositionAndStyle = ( lineWidth: horizontalBorderWidth, stroke: horizontalBorderColor, strokeOpacity: horizontalBorderColorOpacity, + lineDash: borderDash, }; } @@ -315,6 +317,7 @@ export const getBorderPositionAndStyle = ( lineWidth: verticalBorderWidth, stroke: verticalBorderColor, strokeOpacity: verticalBorderColorOpacity, + lineDash: borderDash, }; } diff --git a/s2-site/docs/api/general/S2Theme.en.md b/s2-site/docs/api/general/S2Theme.en.md index 34f7079343..2190018346 100644 --- a/s2-site/docs/api/general/S2Theme.en.md +++ b/s2-site/docs/api/general/S2Theme.en.md @@ -132,6 +132,7 @@ Function description: Split line style | showShadow | Whether to display the outer shadow of the dividing line (in the case of frozen rows and columns) | `boolean` | `true` | | | shadowWidth | shadow width | `number` | 10 | | | shadowColors | `left` : change the left color linearly `right` : change the color of the right side linearly | `{left: string,` `right: string}` | `{left: 'rgba(0,0,0,0.1)',` `right: 'rgba(0,0,0,0)'}` | | +| borderDash | Split line dash | `number[]` | `[]` | | #### TextTheme @@ -171,6 +172,7 @@ Function description: Cell general theme | interactionState | cell interaction state | [InteractionStateTheme](#interactionstatetheme) | - | | | miniBarChartHeight | In-Cell Conditional Formatting - Sparkline Bar Height | `number` | 12 | | | miniBarChartFillColor | In-cell conditional formatting - default fill color for mini bar charts | `string` | - | | +| borderDash | cell border dash | `number[]` | `[]` | | #### IconTheme diff --git a/s2-site/docs/api/general/S2Theme.zh.md b/s2-site/docs/api/general/S2Theme.zh.md index 892ef33676..35d551fe8d 100644 --- a/s2-site/docs/api/general/S2Theme.zh.md +++ b/s2-site/docs/api/general/S2Theme.zh.md @@ -134,6 +134,7 @@ s2.setTheme({ | showShadow | 分割线是否显示外阴影(行列冻结情况下) | `boolean` | `true` | | | shadowWidth | 阴影宽度 | `number` | 10 | | | shadowColors | `left` : 线性变化左侧颜色
`right` : 线性变化右侧颜色 | `{left: string,`
`right: string}` | `{left: 'rgba(0,0,0,0.1)',`
`right: 'rgba(0,0,0,0)'}` | | +| borderDash | 分割线虚线 | `number[]` | `[]` | | #### TextTheme @@ -173,6 +174,7 @@ s2.setTheme({ | interactionState | 单元格交互态 ([查看默认配置](https://github.com/antvis/S2/blob/master/packages/s2-core/src/theme/index.ts#L66-L107)) ([示例](/zh/examples/interaction/basic#state-theme)) | Record<[InteractionStateName](#interactionstatename), [InteractionStateTheme](#interactionstatetheme)> | - | | | miniBarChartHeight | 单元格内条件格式-迷你条形图高度 | `number` | 12 | | | miniBarChartFillColor | 单元格内条件格式-迷你条形图默认填充颜色 | `string` | - | | +| borderDash | 单元格边线虚线 | `number[]` | `[]` | | #### IconTheme From eb8adbc9fa93436944866f3b2407fb0940427ec6 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Fri, 10 Nov 2023 18:12:18 +0800 Subject: [PATCH 18/18] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=20fontStyle=20=E5=92=8C=20fontVariant=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20(#2407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-core/src/common/interface/theme.ts | 4 +- s2-site/docs/api/general/S2Theme.zh.md | 2 + s2-site/docs/api/graphic.zh.md | 2 + .../custom-cell/demo/data-cell-placeholder.ts | 42 +++++++++++++++++++ .../custom/custom-cell/demo/meta.json | 8 ++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 s2-site/examples/custom/custom-cell/demo/data-cell-placeholder.ts diff --git a/packages/s2-core/src/common/interface/theme.ts b/packages/s2-core/src/common/interface/theme.ts index ef17aa4e3b..1eb86691f3 100644 --- a/packages/s2-core/src/common/interface/theme.ts +++ b/packages/s2-core/src/common/interface/theme.ts @@ -97,7 +97,9 @@ export interface TextAlignCfg { textBaseline?: TextBaseline; } -export interface TextTheme extends TextAlignCfg { +export interface TextTheme + extends TextAlignCfg, + Pick { /* 字体 */ fontFamily?: string; /* 字体大小 */ diff --git a/s2-site/docs/api/general/S2Theme.zh.md b/s2-site/docs/api/general/S2Theme.zh.md index 35d551fe8d..ae1df30d2b 100644 --- a/s2-site/docs/api/general/S2Theme.zh.md +++ b/s2-site/docs/api/general/S2Theme.zh.md @@ -149,6 +149,8 @@ s2.setTheme({ | fontFamily | 字体 | `string` | `Roboto, PingFangSC,`
`BlinkMacSystemFont,`
`Microsoft YaHei,`
`Arial, sans-serif` | | | fontSize | 字体大小 | `number` | - | | | fontWeight | number
string: `normal`
`bold`
`bolder`
`lighter` 字体粗细 | `number` \| `string` | 粗体文本:Mobile:`520` PC: `bold`
普通文本:`normal` | | +| fontStyle | 字体样式 | `normal \| italic \| oblique` | `normal` | +| fontVariant | 字体变体 | `normal \| small-caps \| string` | `normal` | | fill | 字体颜色 | `string` | - | | | linkTextFill | 链接文本颜色 | `string` | - | | | opacity | 字体透明度 | `number` | 1 | | diff --git a/s2-site/docs/api/graphic.zh.md b/s2-site/docs/api/graphic.zh.md index 7c92aff545..8fb20a7e90 100644 --- a/s2-site/docs/api/graphic.zh.md +++ b/s2-site/docs/api/graphic.zh.md @@ -47,6 +47,8 @@ S2 使用 [AntV/G](https://g.antv.vision/zh/docs/guide/introduce) 作为绘图 | fontSize | `number` | 文字大小 | | fontFamily | `string` | 文字字体 | | fontWeight | `number` | 字体粗细 | +| fontStyle | `normal \| italic \| oblique` | 字体样式 | +| fontVariant | `normal \| small-caps \| string` | 字体变体 | | lineHeight | `number` | 文字的行高 | | textAlign | `center` \| `left` \| `right` \| `start` \| `end` | 设置文本内容的对齐方式 | | textBaseline | `top` \| `middle` \| `bottom` \| `alphabetic` \| `hanging` | 设置在绘制文本时使用的当前文本基线| diff --git a/s2-site/examples/custom/custom-cell/demo/data-cell-placeholder.ts b/s2-site/examples/custom/custom-cell/demo/data-cell-placeholder.ts new file mode 100644 index 0000000000..b61659a5d8 --- /dev/null +++ b/s2-site/examples/custom/custom-cell/demo/data-cell-placeholder.ts @@ -0,0 +1,42 @@ +import { PivotSheet, CornerCell } from '@antv/s2'; + +fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/cd9814d0-6dfa-42a6-8455-5a6bd0ff93ca.json', +) + .then((res) => res.json()) + .then((res) => { + const container = document.getElementById('container'); + const s2DataConfig = { + fields: { + rows: ['province', 'city'], + columns: ['type', 'sub_type'], + values: ['number'], + }, + meta: res.meta, + data: res.data.map((item, i) => { + return { + ...item, + number: i < 5 ? item.number : null, + }; + }), + }; + const s2Options = { + width: 600, + height: 480, + // 默认 "-" + // placeholder: '', + placeholder: (cell) => { + // 或者根据当前单元格动态设置 + console.log('cell: ', cell); + if (cell.cellType === 'dataCell') { + return '*****'; + } + + // 返回 null, 使用默认值 ("-") + return null; + }, + }; + const s2 = new PivotSheet(container, s2DataConfig, s2Options); + + s2.render(); + }); diff --git a/s2-site/examples/custom/custom-cell/demo/meta.json b/s2-site/examples/custom/custom-cell/demo/meta.json index 05f3f82822..0da90cfb49 100644 --- a/s2-site/examples/custom/custom-cell/demo/meta.json +++ b/s2-site/examples/custom/custom-cell/demo/meta.json @@ -83,6 +83,14 @@ "en": "custom mini chart" }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*apnIT4KXP3YAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "data-cell-placeholder.ts", + "title": { + "zh": "自定义空数据单元格占位符", + "en": "custom cell placeholder" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*B-VMT7kCWL0AAAAAAAAAAAAADmJ7AQ/original" } ] }