├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── auto-add-to-project.yml │ ├── auto-release.yml │ ├── codeql-analysis.yml │ ├── compressed-size.yml │ ├── disscustion.yml │ ├── emoji-helper.yml │ ├── issue-check-inactive.yml │ ├── issue-close-require.yml │ ├── issue-labeled.yml │ ├── issue-opend.yml │ ├── issue-remove-inactive.yml │ ├── issues-similarity-analysis.yml │ ├── lint.yml │ ├── pr-auto-assign-reviewer.yml │ ├── pr-auto-set-label.yml │ ├── pr-auto-set-next-label.yml │ ├── pr-closed.yml │ ├── pr-labeled.yml │ ├── pr-opend.yml │ ├── release-notify.yml │ ├── release-success.yml │ ├── test-s2-react-components.yml │ ├── test-s2-react.yml │ └── test-s2.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg ├── common.sh ├── pre-commit └── pre-push ├── .markdownlint.json ├── .markdownlintignore ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── .releaserc.base.js ├── .stylelintrc ├── CNAME ├── CONTRIBUTING.en-US.md ├── CONTRIBUTING.md ├── LICENSE ├── README.en-US.md ├── README.md ├── build.config.base.mjs ├── global.d.ts ├── jest.config.base.js ├── package.json ├── packages ├── s2-core │ ├── .releaserc.js │ ├── CHANGELOG.md │ ├── README.en-US.md │ ├── README.md │ ├── __tests__ │ │ ├── README.md │ │ ├── benchmark │ │ │ ├── pivot-sheet-spec.ts │ │ │ └── table-sheet-spec.ts │ │ ├── bugs │ │ │ ├── __snapshots__ │ │ │ │ ├── issue-2359-spec.ts.snap │ │ │ │ ├── issue-2684-spec.ts.snap │ │ │ │ ├── issue-2707-spec.ts.snap │ │ │ │ ├── issue-2808-spec.ts.snap │ │ │ │ ├── issue-446-spec.ts.snap │ │ │ │ └── issue-565-spec.ts.snap │ │ │ ├── issue-1014-spec.ts │ │ │ ├── issue-1191-spec.ts │ │ │ ├── issue-1201-spec.ts │ │ │ ├── issue-1337-spec.ts │ │ │ ├── issue-1520-spec.ts │ │ │ ├── issue-1539-spec.ts │ │ │ ├── issue-1561-spec.ts │ │ │ ├── issue-1587-spec.ts │ │ │ ├── issue-1624-spec.ts │ │ │ ├── issue-1668-spec.ts │ │ │ ├── issue-1715-spec.ts │ │ │ ├── issue-1764-spec.ts │ │ │ ├── issue-1781-spec.ts │ │ │ ├── issue-2140-spec.ts │ │ │ ├── issue-2164-spec.ts │ │ │ ├── issue-2195-spec.ts │ │ │ ├── issue-2199-spec.ts │ │ │ ├── issue-2322-spec.ts │ │ │ ├── issue-2340-spec.ts │ │ │ ├── issue-2359-spec.ts │ │ │ ├── issue-2501-spec.ts │ │ │ ├── issue-2528-spec.ts │ │ │ ├── issue-2582-spec.ts │ │ │ ├── issue-2684-spec.ts │ │ │ ├── issue-2707-spec.ts │ │ │ ├── issue-2726-spec.ts │ │ │ ├── issue-2804-spec.ts │ │ │ ├── issue-2808-spec.ts │ │ │ ├── issue-292-spec.ts │ │ │ ├── issue-2957-spec.ts │ │ │ ├── issue-3014-spec.ts │ │ │ ├── issue-3048-spec.ts │ │ │ ├── issue-368-spec.ts │ │ │ ├── issue-372-spec.ts │ │ │ ├── issue-446-spec.ts │ │ │ ├── issue-507-spec.ts │ │ │ ├── issue-511-spec.ts │ │ │ ├── issue-565-spec.ts │ │ │ ├── issue-720-spec.ts │ │ │ ├── issue-725-spec.ts │ │ │ ├── issue-836-spec.ts │ │ │ ├── issue-840-spec.ts │ │ │ ├── issue-860-spec.ts │ │ │ └── issue-868-spec.ts │ │ ├── data │ │ │ ├── custom-grid-fields.ts │ │ │ ├── custom-grid-simple-fields.ts │ │ │ ├── custom-table-col-fields.ts │ │ │ ├── custom-tree-nodes.ts │ │ │ ├── data-custom-grid.ts │ │ │ ├── data-custom-tree.ts │ │ │ ├── data-custom-trees.ts │ │ │ ├── data-issue-1520.json │ │ │ ├── data-issue-1668.json │ │ │ ├── data-issue-2199.json │ │ │ ├── data-issue-2322.json │ │ │ ├── data-issue-2385.json │ │ │ ├── data-issue-2684.json │ │ │ ├── data-issue-2707.json │ │ │ ├── data-issue-2804.json │ │ │ ├── data-issue-292.json │ │ │ ├── data-issue-368.json │ │ │ ├── data-issue-372.json │ │ │ ├── data-issue-446.json │ │ │ ├── data-issue-507.json │ │ │ ├── data-issue-511.json │ │ │ ├── data-issue-565.json │ │ │ ├── data-issue-725.json │ │ │ ├── data-issue-836.json │ │ │ ├── data-issue-860.json │ │ │ ├── data-issue-868.json │ │ │ ├── data-multi-line-text.ts │ │ │ ├── mock-dataset-multi-measure.json │ │ │ ├── mock-dataset.json │ │ │ ├── mock-drill-down-dataset.json │ │ │ ├── simple-data.json │ │ │ ├── simple-table-data.json │ │ │ ├── single-measure-total.json │ │ │ ├── sort-advanced.ts │ │ │ └── total-group-data.ts │ │ ├── matchers │ │ │ ├── colorMatcher.js │ │ │ ├── index.js │ │ │ └── matcher.d.ts │ │ ├── setup.js │ │ ├── spreadsheet │ │ │ ├── __snapshots__ │ │ │ │ ├── corner-spec.ts.snap │ │ │ │ ├── custom-cell-style-spec.ts.snap │ │ │ │ ├── custom-dataset-spec.ts.snap │ │ │ │ ├── custom-grid-spec.ts.snap │ │ │ │ ├── custom-table-col-spec.ts.snap │ │ │ │ ├── custom-tree-spec.ts.snap │ │ │ │ ├── custom-value-order-spec.ts.snap │ │ │ │ ├── header-action-icons-spec.ts.snap │ │ │ │ ├── hidden-columns-spec.ts.snap │ │ │ │ ├── layout-hooks-spec.ts.snap │ │ │ │ ├── miss-dimension-values-spec.ts.snap │ │ │ │ ├── multi-line-text-spec.ts.snap │ │ │ │ ├── pivot-chart-sheet-spec.ts.snap │ │ │ │ ├── sort-by-order-spec.ts.snap │ │ │ │ ├── spread-sheet-collapse-spec.ts.snap │ │ │ │ ├── spread-sheet-facet-layout-api-spec.ts.snap │ │ │ │ ├── spread-sheet-frozen-spec.ts.snap │ │ │ │ ├── spread-sheet-resize-spec.ts.snap │ │ │ │ ├── spread-sheet-spec.ts.snap │ │ │ │ ├── table-sheet-spec.ts.snap │ │ │ │ └── theme-spec.ts.snap │ │ │ ├── compare-layout-spec.ts │ │ │ ├── corner-spec.ts │ │ │ ├── custom-cell-style-spec.ts │ │ │ ├── custom-dataset-spec.ts │ │ │ ├── custom-facet-spec.ts │ │ │ ├── custom-grid-spec.ts │ │ │ ├── custom-table-col-spec.ts │ │ │ ├── custom-tree-spec.ts │ │ │ ├── custom-value-order-spec.ts │ │ │ ├── empty-dataset-spec.ts │ │ │ ├── empty-string-values-spec.ts │ │ │ ├── header-action-icons-spec.ts │ │ │ ├── header-cell-link-click-spec.ts │ │ │ ├── hidden-columns-spec.ts │ │ │ ├── interaction-brush-selection-scroll-spec.ts │ │ │ ├── interaction-cell-selected-event-spec.ts │ │ │ ├── interaction-corner-click-spec.ts │ │ │ ├── interaction-multi-selection-spec.ts │ │ │ ├── interaction-spotlight-spec.ts │ │ │ ├── interaction-tooltip-spec.ts │ │ │ ├── layout-hooks-spec.ts │ │ │ ├── miss-dimension-values-spec.ts │ │ │ ├── multi-line-text-spec.ts │ │ │ ├── pivot-chart-sheet-spec.ts │ │ │ ├── scroll-spec.ts │ │ │ ├── sort-by-order-spec.ts │ │ │ ├── spread-sheet-collapse-spec.ts │ │ │ ├── spread-sheet-facet-layout-api-spec.ts │ │ │ ├── spread-sheet-frozen-spec.ts │ │ │ ├── spread-sheet-resize-spec.ts │ │ │ ├── spread-sheet-series-number-spec.ts │ │ │ ├── spread-sheet-spec.ts │ │ │ ├── spread-sheet-totals-spec.ts │ │ │ ├── spread-sheet-tree-mode-spec.ts │ │ │ ├── table-resize-spec.ts │ │ │ ├── table-sheet-spec.ts │ │ │ ├── theme-spec.ts │ │ │ ├── tooltip-spec.ts │ │ │ └── total-group-spec.ts │ │ ├── unit │ │ │ ├── cell │ │ │ │ ├── col-cell-spec.ts │ │ │ │ ├── corner-cell-spec.ts │ │ │ │ ├── custom-tree-corner-cell-spec.ts │ │ │ │ ├── data-cell-spec.ts │ │ │ │ ├── header-cell-spec.ts │ │ │ │ ├── merged-cell-spec.ts │ │ │ │ ├── row-cell-spec.ts │ │ │ │ └── series-number-cell-spec.ts │ │ │ ├── common │ │ │ │ ├── i18n │ │ │ │ │ └── index-spec.ts │ │ │ │ ├── icons │ │ │ │ │ ├── factory-spec.ts │ │ │ │ │ └── gui-icon-spec.ts │ │ │ │ └── store │ │ │ │ │ └── index-spec.ts │ │ │ ├── data-process │ │ │ │ ├── README_zh.md │ │ │ │ ├── pivot-spec.tsx │ │ │ │ └── table-spec.tsx │ │ │ ├── data-set │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── pivot-data-set-total-spec.ts.snap │ │ │ │ │ └── table-data-set-spec.ts.snap │ │ │ │ ├── custom-grid-data-set-spec.ts │ │ │ │ ├── pivot-data-set-row-value-spec.ts │ │ │ │ ├── pivot-data-set-spec.ts │ │ │ │ ├── pivot-data-set-total-spec.ts │ │ │ │ └── table-data-set-spec.ts │ │ │ ├── dataset │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── table-dataset-spec.ts.snap │ │ │ │ └── table-dataset-spec.ts │ │ │ ├── facet │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── table-facet-spec.ts.snap │ │ │ │ ├── bbox │ │ │ │ │ ├── corner-bbox-spec.ts │ │ │ │ │ └── panel-bbox-spec.ts │ │ │ │ ├── header │ │ │ │ │ ├── corner-spec.ts │ │ │ │ │ └── frozen-row-spec.ts │ │ │ │ ├── layout │ │ │ │ │ ├── build-row-tree-hierarchy-spec.ts │ │ │ │ │ ├── col-node-width-spec.ts │ │ │ │ │ ├── layout-hooks-spec.ts │ │ │ │ │ ├── node-spec.ts │ │ │ │ │ └── row-node-width-spec.ts │ │ │ │ ├── pivot-facet-spec.ts │ │ │ │ ├── table-facet-spec.ts │ │ │ │ └── util.ts │ │ │ ├── interaction │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── root-spec.ts.snap │ │ │ │ │ └── row-column-resize-spec.ts.snap │ │ │ │ ├── base-event-spec.ts │ │ │ │ ├── base-interaction │ │ │ │ │ ├── click │ │ │ │ │ │ ├── corner-cell-click-spec.ts │ │ │ │ │ │ ├── data-cell-click-spec.ts │ │ │ │ │ │ ├── header-cell-link-click-spec.ts │ │ │ │ │ │ ├── preview-click-spec.ts │ │ │ │ │ │ └── row-column-click-spec.ts │ │ │ │ │ └── hover-spec.ts │ │ │ │ ├── brush-selection │ │ │ │ │ ├── base-brush-selection-spec.ts │ │ │ │ │ ├── col-brush-selection-spec.ts │ │ │ │ │ ├── data-brush-selection-spec.ts │ │ │ │ │ └── row-brush-selection-spec.ts │ │ │ │ ├── data-cell-multi-selection-spec.ts │ │ │ │ ├── event-controller-spec.ts │ │ │ │ ├── range-selection-spec.ts │ │ │ │ ├── root-spec.ts │ │ │ │ ├── row-column-resize-spec.ts │ │ │ │ └── selected-cell-move-spec.ts │ │ │ ├── renderer │ │ │ │ └── renderer-factory-spec.ts │ │ │ ├── shared │ │ │ │ └── utils │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── drill-down-spec.ts.snap │ │ │ │ │ └── options-spec.ts.snap │ │ │ │ │ ├── classnames-spec.ts │ │ │ │ │ ├── drill-down-spec.ts │ │ │ │ │ ├── options-spec.ts │ │ │ │ │ └── resize-spec.ts │ │ │ ├── sheet-type │ │ │ │ ├── pivot-sheet-spec.ts │ │ │ │ └── table-sheet-spec.ts │ │ │ ├── ui │ │ │ │ ├── hd-adapter │ │ │ │ │ └── index-spec.ts │ │ │ │ ├── scrollbar │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── index-spec.ts.snap │ │ │ │ │ └── index-spec.ts │ │ │ │ └── tooltip │ │ │ │ │ └── index-spec.ts │ │ │ └── utils │ │ │ │ ├── __snapshots__ │ │ │ │ ├── g-mini-charts-spec.ts.snap │ │ │ │ ├── hide-columns-spec.ts.snap │ │ │ │ ├── indexes-spec.ts.snap │ │ │ │ ├── merge-spec.ts.snap │ │ │ │ ├── sort-action-spec.ts.snap │ │ │ │ ├── theme-spec.ts.snap │ │ │ │ └── tooltip-spec.ts.snap │ │ │ │ ├── canvas-spec.ts │ │ │ │ ├── cell │ │ │ │ ├── cell-spec.ts │ │ │ │ ├── custom-renderer-spec.ts │ │ │ │ ├── header-cell-spec.ts │ │ │ │ ├── table-col-cell-spec.ts │ │ │ │ └── text-scrolling-spec.ts │ │ │ │ ├── color-spec.ts │ │ │ │ ├── condition │ │ │ │ ├── condition-spec.ts │ │ │ │ └── state-controller-spec.ts │ │ │ │ ├── data-item-type-checker-spec.ts │ │ │ │ ├── data-set-operate-spec.ts │ │ │ │ ├── dataset │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── pivot-data-set-spec.ts.snap │ │ │ │ └── pivot-data-set-spec.ts │ │ │ │ ├── export │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── copy-spec.ts.snap │ │ │ │ │ ├── export-pivot-spec.ts.snap │ │ │ │ │ ├── export-spec.ts.snap │ │ │ │ │ └── export-table-spec.ts.snap │ │ │ │ ├── copy-spec.ts │ │ │ │ ├── export-pivot-spec.ts │ │ │ │ ├── export-spec.ts │ │ │ │ ├── export-table-spec.ts │ │ │ │ ├── method-spec.ts │ │ │ │ └── utils-spec.ts │ │ │ │ ├── facet-spec.ts │ │ │ │ ├── formatter-spec.ts │ │ │ │ ├── frozen-util-spec.ts │ │ │ │ ├── g-mini-charts-spec.ts │ │ │ │ ├── get-all-child-cells-spec.ts │ │ │ │ ├── get-all-children-node-height-spec.ts │ │ │ │ ├── get-classnames-spec.ts │ │ │ │ ├── grid-spec.ts │ │ │ │ ├── hide-columns-spec.ts │ │ │ │ ├── indexes-spec.ts │ │ │ │ ├── interaction │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── formatter-spec.ts.snap │ │ │ │ ├── formatter-spec.ts │ │ │ │ ├── hover-event-spec.ts │ │ │ │ ├── resize-spec.ts │ │ │ │ ├── select-event-spec.ts │ │ │ │ └── state-controller-spec.ts │ │ │ │ ├── is-mobile-spec.ts │ │ │ │ ├── layout │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── frozen-spec.ts.snap │ │ │ │ ├── frozen-spec.ts │ │ │ │ ├── generate-id-spec.ts │ │ │ │ ├── get-dims-condition-by-node-spec.ts │ │ │ │ └── whether-leaf-by-level-spec.ts │ │ │ │ ├── math-spec.ts │ │ │ │ ├── merge-cell-spec.ts │ │ │ │ ├── merge-spec.ts │ │ │ │ ├── number-calculate-spec.ts │ │ │ │ ├── sort-action-spec.ts │ │ │ │ ├── text-spec.ts │ │ │ │ ├── theme-spec.ts │ │ │ │ ├── tooltip-spec.ts │ │ │ │ └── veen-arr-spec.ts │ │ └── util │ │ │ ├── fp.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ └── interaction.ts │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.mjs │ ├── scripts │ │ ├── sync-event.mjs │ │ └── test-live.mjs │ ├── src │ │ ├── cell │ │ │ ├── base-cell.ts │ │ │ ├── col-cell.ts │ │ │ ├── corner-cell.ts │ │ │ ├── data-cell.ts │ │ │ ├── header-cell.ts │ │ │ ├── index.ts │ │ │ ├── merged-cell.ts │ │ │ ├── row-cell.ts │ │ │ ├── series-number-cell.ts │ │ │ ├── table-col-cell.ts │ │ │ ├── table-corner-cell.ts │ │ │ ├── table-data-cell.ts │ │ │ └── table-series-number-cell.ts │ │ ├── common │ │ │ ├── constant │ │ │ │ ├── basic.ts │ │ │ │ ├── classnames.ts │ │ │ │ ├── condition.ts │ │ │ │ ├── copy.ts │ │ │ │ ├── dataConfig.ts │ │ │ │ ├── events │ │ │ │ │ ├── basic.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interaction.ts │ │ │ │ │ └── origin.ts │ │ │ │ ├── field.ts │ │ │ │ ├── frozen.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interaction.ts │ │ │ │ ├── node.ts │ │ │ │ ├── options.ts │ │ │ │ ├── pagination.ts │ │ │ │ ├── query.ts │ │ │ │ ├── renderer.ts │ │ │ │ ├── resize.ts │ │ │ │ ├── theme.ts │ │ │ │ └── tooltip.ts │ │ │ ├── debug │ │ │ │ └── index.ts │ │ │ ├── i18n │ │ │ │ ├── en_US.ts │ │ │ │ ├── index.ts │ │ │ │ ├── ru_RU.ts │ │ │ │ └── zh_CN.ts │ │ │ ├── icons │ │ │ │ ├── factory.ts │ │ │ │ ├── gui-icon.ts │ │ │ │ ├── index.ts │ │ │ │ └── svg │ │ │ │ │ ├── index.ts │ │ │ │ │ └── svgs.ts │ │ │ ├── index.ts │ │ │ ├── interface │ │ │ │ ├── basic.ts │ │ │ │ ├── collapse.ts │ │ │ │ ├── condition.ts │ │ │ │ ├── emitter.ts │ │ │ │ ├── events.ts │ │ │ │ ├── export.ts │ │ │ │ ├── facet.ts │ │ │ │ ├── frame.ts │ │ │ │ ├── frozen.ts │ │ │ │ ├── group.ts │ │ │ │ ├── hooks.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interaction.ts │ │ │ │ ├── node.ts │ │ │ │ ├── renderer.ts │ │ │ │ ├── resize.ts │ │ │ │ ├── s2DataConfig.ts │ │ │ │ ├── s2Options.ts │ │ │ │ ├── scroll.ts │ │ │ │ ├── store.ts │ │ │ │ ├── style.ts │ │ │ │ ├── text.ts │ │ │ │ ├── theme.ts │ │ │ │ ├── tooltip.ts │ │ │ │ └── type-utils.ts │ │ │ └── store │ │ │ │ └── index.ts │ │ ├── data-set │ │ │ ├── base-data-set.ts │ │ │ ├── cell-data.ts │ │ │ ├── custom-grid-pivot-data-set.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── pivot-data-set.ts │ │ │ └── table-data-set.ts │ │ ├── engine │ │ │ ├── CustomImage.ts │ │ │ ├── CustomRect.ts │ │ │ ├── CustomText.ts │ │ │ ├── index.ts │ │ │ └── interface.ts │ │ ├── extends │ │ │ ├── index.ts │ │ │ └── pivot-chart │ │ │ │ ├── cell │ │ │ │ ├── axis-col-cell.ts │ │ │ │ ├── axis-corner-cell.ts │ │ │ │ ├── axis-row-cell.ts │ │ │ │ ├── cell-type.ts │ │ │ │ ├── chart-data-cell.ts │ │ │ │ └── pivot-chart-data-cell.ts │ │ │ │ ├── constant.ts │ │ │ │ ├── facet │ │ │ │ ├── corner-bbox.ts │ │ │ │ ├── frame.ts │ │ │ │ ├── panel-bbox.ts │ │ │ │ └── pivot-chart-facet.ts │ │ │ │ ├── header │ │ │ │ ├── axis-col.ts │ │ │ │ ├── axis-corner.ts │ │ │ │ ├── axis-row.ts │ │ │ │ └── corner.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interaction │ │ │ │ ├── axis-click.ts │ │ │ │ ├── axis-hover.ts │ │ │ │ └── root.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── pivot-chart-sheet.ts │ │ │ │ └── utils │ │ │ │ ├── chart-options.ts │ │ │ │ ├── frozen.ts │ │ │ │ ├── handle-interaction.ts │ │ │ │ ├── separate-axis.ts │ │ │ │ └── theme.ts │ │ ├── facet │ │ │ ├── README-adjustTotalNodesCoordinate.md │ │ │ ├── base-facet.ts │ │ │ ├── bbox │ │ │ │ ├── base-bbox.ts │ │ │ │ ├── corner-bbox.ts │ │ │ │ └── panel-bbox.ts │ │ │ ├── frozen-facet.ts │ │ │ ├── header │ │ │ │ ├── base.ts │ │ │ │ ├── col.ts │ │ │ │ ├── corner.ts │ │ │ │ ├── frame.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── row.ts │ │ │ │ ├── series-number.ts │ │ │ │ ├── table-col.ts │ │ │ │ └── util.ts │ │ │ ├── index.ts │ │ │ ├── layout │ │ │ │ ├── build-gird-hierarchy.ts │ │ │ │ ├── build-header-hierarchy.ts │ │ │ │ ├── build-row-custom-tree-hierarchy.ts │ │ │ │ ├── build-row-tree-hierarchy.ts │ │ │ │ ├── build-table-hierarchy.ts │ │ │ │ ├── hierarchy.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── layout-hooks.ts │ │ │ │ ├── node.ts │ │ │ │ ├── total-class.ts │ │ │ │ └── total-measure.ts │ │ │ ├── mobile │ │ │ │ └── wheelEvent.ts │ │ │ ├── pivot-facet.ts │ │ │ ├── table-facet.ts │ │ │ └── utils.ts │ │ ├── group │ │ │ ├── frozen-group.ts │ │ │ ├── grid-group.ts │ │ │ └── panel-scroll-group.ts │ │ ├── index.ts │ │ ├── interaction │ │ │ ├── base-event.ts │ │ │ ├── base-interaction │ │ │ │ ├── click │ │ │ │ │ ├── corner-cell-click.ts │ │ │ │ │ ├── data-cell-click.ts │ │ │ │ │ ├── header-cell-link-click.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── merged-cell-click.ts │ │ │ │ │ ├── preview-click.ts │ │ │ │ │ └── row-column-click.ts │ │ │ │ ├── hover.ts │ │ │ │ └── index.ts │ │ │ ├── brush-selection │ │ │ │ ├── base-brush-selection.ts │ │ │ │ ├── col-brush-selection.ts │ │ │ │ ├── data-cell-brush-selection.ts │ │ │ │ └── row-brush-selection.ts │ │ │ ├── data-cell-multi-selection.ts │ │ │ ├── event-controller.ts │ │ │ ├── index.ts │ │ │ ├── range-selection.ts │ │ │ ├── root.ts │ │ │ ├── row-column-resize.ts │ │ │ └── selected-cell-move.ts │ │ ├── renderer │ │ │ ├── BaseRenderer.ts │ │ │ ├── ImageRenderer.ts │ │ │ ├── RendererFactory.ts │ │ │ ├── VideoRenderer.ts │ │ │ └── index.ts │ │ ├── shared │ │ │ ├── constant │ │ │ │ ├── classnames.ts │ │ │ │ ├── i18n │ │ │ │ │ ├── en_US.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── ru_RU.ts │ │ │ │ │ └── zh_CN.ts │ │ │ │ ├── index.ts │ │ │ │ ├── option.ts │ │ │ │ ├── resize.ts │ │ │ │ └── sort.ts │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── styles │ │ │ │ ├── drill-down.less │ │ │ │ ├── tooltip │ │ │ │ │ ├── index.less │ │ │ │ │ └── operator.less │ │ │ │ └── variables.less │ │ │ └── utils │ │ │ │ ├── classnames.ts │ │ │ │ ├── drill-down.ts │ │ │ │ ├── index.ts │ │ │ │ ├── options.ts │ │ │ │ └── resize.ts │ │ ├── sheet-type │ │ │ ├── index.ts │ │ │ ├── pivot-sheet.ts │ │ │ ├── spread-sheet.ts │ │ │ └── table-sheet.ts │ │ ├── styles │ │ │ ├── theme │ │ │ │ └── dark.less │ │ │ └── variables.less │ │ ├── theme │ │ │ ├── index.ts │ │ │ └── palette │ │ │ │ ├── colorful.ts │ │ │ │ ├── dark.ts │ │ │ │ ├── default.ts │ │ │ │ └── gray.ts │ │ ├── ui │ │ │ ├── hd-adapter │ │ │ │ └── index.ts │ │ │ ├── scrollbar │ │ │ │ ├── index.ts │ │ │ │ └── interface.ts │ │ │ └── tooltip │ │ │ │ ├── index.less │ │ │ │ └── index.ts │ │ └── utils │ │ │ ├── canvas.ts │ │ │ ├── cell │ │ │ ├── cell.ts │ │ │ ├── customRenderer.ts │ │ │ ├── data-cell.ts │ │ │ ├── header-cell.ts │ │ │ ├── index.ts │ │ │ ├── merged-cell.ts │ │ │ ├── table-col-cell.ts │ │ │ └── text-scrolling.ts │ │ │ ├── color.ts │ │ │ ├── common.ts │ │ │ ├── condition │ │ │ ├── condition.ts │ │ │ └── state-controller.ts │ │ │ ├── data-item-type-checker.ts │ │ │ ├── data-set-operate.ts │ │ │ ├── dataset │ │ │ └── pivot-data-set.ts │ │ │ ├── export │ │ │ ├── copy │ │ │ │ ├── base-data-cell-copy.ts │ │ │ │ ├── common.ts │ │ │ │ ├── core.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pivot-data-cell-copy.ts │ │ │ │ ├── pivot-header-copy.ts │ │ │ │ ├── strategy-copy.ts │ │ │ │ └── table-copy.ts │ │ │ ├── index.ts │ │ │ ├── method.ts │ │ │ └── utils.ts │ │ │ ├── facet.ts │ │ │ ├── formatter.ts │ │ │ ├── g-mini-charts.ts │ │ │ ├── g-renders.ts │ │ │ ├── get-all-child-cells.ts │ │ │ ├── get-all-children-node-height.ts │ │ │ ├── get-classnames.ts │ │ │ ├── grid.ts │ │ │ ├── hide-columns.ts │ │ │ ├── index.ts │ │ │ ├── indexes.ts │ │ │ ├── inject-css-text.ts │ │ │ ├── interaction │ │ │ ├── common.ts │ │ │ ├── formatter.ts │ │ │ ├── hover-event.ts │ │ │ ├── index.ts │ │ │ ├── link-field.ts │ │ │ ├── merge-cell.ts │ │ │ ├── resize.ts │ │ │ ├── scroll.ts │ │ │ ├── select-event.ts │ │ │ └── state-controller.ts │ │ │ ├── is-mobile.ts │ │ │ ├── layout │ │ │ ├── add-totals.ts │ │ │ ├── frozen.ts │ │ │ ├── generate-header-nodes.ts │ │ │ ├── generate-id.ts │ │ │ ├── get-dims-condition-by-node.ts │ │ │ ├── index.ts │ │ │ └── whether-leaf-by-level.ts │ │ │ ├── math.ts │ │ │ ├── merge.ts │ │ │ ├── normalize.ts │ │ │ ├── number-calculate.ts │ │ │ ├── schedule.ts │ │ │ ├── sort-action.ts │ │ │ ├── text.ts │ │ │ ├── theme.ts │ │ │ ├── tooltip.ts │ │ │ └── veen-arr.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── s2-react-components │ ├── .releaserc.js │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── __mocks__ │ │ │ └── svg.ts │ │ ├── bugs │ │ │ └── issue-1736-spec.tsx │ │ ├── setup.js │ │ └── unit │ │ │ └── components │ │ │ ├── advanced-sort │ │ │ ├── __snapshots__ │ │ │ │ ├── advanced-sort-spec.tsx.snap │ │ │ │ └── custom-sort-spec.tsx.snap │ │ │ ├── advanced-sort-spec.tsx │ │ │ └── custom-sort-spec.tsx │ │ │ ├── common │ │ │ ├── radio-group │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ ├── reset-button │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ ├── reset-group │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ └── tooltip-wrapper │ │ │ │ ├── __snapshots__ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ ├── drill-down │ │ │ ├── __snapshots__ │ │ │ │ └── drill-down-spec.tsx.snap │ │ │ └── drill-down-spec.tsx │ │ │ ├── export │ │ │ ├── __snapshots__ │ │ │ │ └── export-spec.tsx.snap │ │ │ └── export-spec.tsx │ │ │ ├── frozen-panel │ │ │ ├── __snapshots__ │ │ │ │ └── index-spec.tsx.snap │ │ │ ├── frozen-input-number │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ └── index-spec.tsx │ │ │ ├── switcher │ │ │ ├── __snapshots__ │ │ │ │ └── switcher-spec.tsx.snap │ │ │ ├── content │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ └── index-spec.tsx │ │ │ ├── switcher-spec.tsx │ │ │ └── util-spec.ts │ │ │ ├── text-align-panel │ │ │ ├── __snapshots__ │ │ │ │ └── index-spec.tsx.snap │ │ │ └── index-spec.tsx │ │ │ └── theme-panel │ │ │ ├── __snapshots__ │ │ │ ├── index-spec.tsx.snap │ │ │ └── utils-spec.ts.snap │ │ │ ├── color-box │ │ │ ├── __snapshots__ │ │ │ │ └── index-spec.tsx.snap │ │ │ └── index-spec.tsx │ │ │ ├── color-picker-panel │ │ │ ├── __snapshots__ │ │ │ │ └── index-spec.tsx.snap │ │ │ └── index-spec.tsx │ │ │ ├── hooks │ │ │ └── useHistoryColorList-spec.ts │ │ │ ├── index-spec.tsx │ │ │ └── utils-spec.ts │ ├── jest.config.js │ ├── package.json │ ├── playground │ │ ├── Header │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── README.md │ │ ├── config.tsx │ │ ├── index.html │ │ ├── index.less │ │ ├── index.tsx │ │ └── utils.ts │ ├── src │ │ ├── common │ │ │ ├── constants │ │ │ │ ├── index.ts │ │ │ │ └── theme.ts │ │ │ ├── index.ts │ │ │ └── interface │ │ │ │ ├── components.ts │ │ │ │ ├── icon.ts │ │ │ │ └── index.ts │ │ ├── components │ │ │ ├── advanced-sort │ │ │ │ ├── advanced-sort.tsx │ │ │ │ ├── custom-sort.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ └── interface.ts │ │ │ ├── common │ │ │ │ ├── icons │ │ │ │ │ ├── calendar-icon.tsx │ │ │ │ │ ├── col-icon.tsx │ │ │ │ │ ├── index.less │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── location-icon.tsx │ │ │ │ │ ├── row-icon.tsx │ │ │ │ │ ├── text-icon.tsx │ │ │ │ │ └── value-icon.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── radio-group │ │ │ │ │ ├── index.less │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── radio-group.tsx │ │ │ │ ├── reset-button │ │ │ │ │ ├── index.less │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reset-button.tsx │ │ │ │ ├── reset-group │ │ │ │ │ ├── index.less │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── reset-group.tsx │ │ │ │ └── tooltip-wrapper │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── tooltip-wrapper.tsx │ │ │ ├── drill-down │ │ │ │ ├── drill-down.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ └── interface.ts │ │ │ ├── export │ │ │ │ ├── export.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ └── strategy-export.tsx │ │ │ ├── frozen-panel │ │ │ │ ├── frozen-input-number │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── interface.ts │ │ │ │ ├── frozen-panel.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ └── interface.ts │ │ │ ├── index.ts │ │ │ ├── switcher │ │ │ │ ├── constant.ts │ │ │ │ ├── content │ │ │ │ │ ├── index.less │ │ │ │ │ └── index.tsx │ │ │ │ ├── dimension │ │ │ │ │ ├── index.less │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── item │ │ │ │ │ ├── index.less │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── single-item.tsx │ │ │ │ ├── switcher.tsx │ │ │ │ └── util.ts │ │ │ ├── text-align-panel │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── text-align-panel.tsx │ │ │ │ └── utils.ts │ │ │ └── theme-panel │ │ │ │ ├── color-box │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── interface.ts │ │ │ │ ├── color-picker-panel │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── interface.ts │ │ │ │ ├── hooks │ │ │ │ └── useHistoryColorList.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── theme-panel.tsx │ │ │ │ └── utils.ts │ │ └── index.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── typings.d.ts │ └── vite.config.ts ├── s2-react │ ├── .releaserc.js │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ │ ├── README.md │ │ ├── __mocks__ │ │ │ └── svg.ts │ │ ├── bugs │ │ │ ├── issue-522-spec.tsx │ │ │ ├── issue-594-spec.tsx │ │ │ └── issue-652-spec.tsx │ │ ├── data │ │ │ ├── custom-tree-fields.ts │ │ │ ├── data-accuracy.ts │ │ │ ├── data-custom-trees.ts │ │ │ ├── data-g2-chart.ts │ │ │ ├── data-issue-285 │ │ │ │ ├── data-col-header.json │ │ │ │ └── data-without-col-header.json │ │ │ ├── data-issue-522.json │ │ │ ├── data-sort.json │ │ │ ├── grid-analysis-data.ts │ │ │ ├── mock-dataset.json │ │ │ ├── mock-drill-down-dataset.json │ │ │ ├── multiple-values-cell-mock-data.ts │ │ │ ├── simple-data.json │ │ │ ├── strategy-data.ts │ │ │ └── tableau-supermarket.csv │ │ ├── setup.js │ │ ├── spreadsheet │ │ │ ├── adaptive-spec.tsx │ │ │ ├── custom │ │ │ │ └── custom-interaction.tsx │ │ │ ├── data-accuracy-one-measure-spec.tsx │ │ │ ├── data-accuracy-two-measures-spec.tsx │ │ │ ├── drill-down-spec.tsx │ │ │ ├── filter-sheet-spec.tsx │ │ │ ├── less │ │ │ │ └── sort-sheet-spec.less │ │ │ ├── multiple-values-cell-spec.tsx │ │ │ ├── pagination-spec.tsx │ │ │ ├── pressure-test-spec.tsx │ │ │ ├── spread-sheet-spec.tsx │ │ │ ├── table-sheet-spec.tsx │ │ │ └── tooltip-spec.tsx │ │ ├── unit │ │ │ ├── components │ │ │ │ ├── sheets │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ │ ├── chart-sheet │ │ │ │ │ │ └── index-spec.tsx │ │ │ │ │ ├── index-spec.tsx │ │ │ │ │ └── strategy-sheet │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ │ │ ├── custom-tooltip │ │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ │ │ └── index-spec.tsx │ │ │ │ │ │ └── index-spec.tsx │ │ │ │ └── tooltip │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index-spec.tsx.snap │ │ │ │ │ └── index-spec.tsx │ │ │ ├── hooks │ │ │ │ ├── useEvents-spec.ts │ │ │ │ ├── useLoading-spec.ts │ │ │ │ ├── usePagination-spec.ts │ │ │ │ ├── usePivotSheetUpdate-spec.ts │ │ │ │ ├── useResize-spec.ts │ │ │ │ └── useSpreadSheet-spec.ts │ │ │ └── utils │ │ │ │ ├── drill-down-spec.ts │ │ │ │ ├── react-render-16-spec.tsx │ │ │ │ └── react-render-18-spec.tsx │ │ └── util │ │ │ └── helpers.ts │ ├── jest.config.js │ ├── package.json │ ├── playground │ │ ├── README.md │ │ ├── components │ │ │ ├── BigDataSheet.tsx │ │ │ ├── ChartSheet.tsx │ │ │ ├── ConfigProvider │ │ │ │ └── index.tsx │ │ │ ├── CustomGrid.tsx │ │ │ ├── CustomTree.tsx │ │ │ ├── EditableSheet.tsx │ │ │ ├── GridAnalysisSheet.tsx │ │ │ ├── Header │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── LinkGroup.tsx │ │ │ ├── PivotChartSheet.tsx │ │ │ ├── Plugins.tsx │ │ │ ├── ResizeConfig.tsx │ │ │ └── StrategySheet.tsx │ │ ├── config.tsx │ │ ├── context │ │ │ └── playground.context.ts │ │ ├── drill-down.tsx │ │ ├── index.html │ │ ├── index.less │ │ ├── index.tsx │ │ └── utils.ts │ ├── scripts │ │ └── test-live.mjs │ ├── src │ │ ├── common │ │ │ ├── constant │ │ │ │ ├── index.ts │ │ │ │ └── options.ts │ │ │ ├── icons │ │ │ │ ├── html-icon.less │ │ │ │ ├── html-icon.tsx │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── react-element.tsx │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── sheets │ │ │ │ ├── base-sheet │ │ │ │ │ ├── index.less │ │ │ │ │ └── index.tsx │ │ │ │ ├── chart-sheet │ │ │ │ │ └── index.tsx │ │ │ │ ├── editable-sheet │ │ │ │ │ ├── custom-cell │ │ │ │ │ │ ├── edit-cell │ │ │ │ │ │ │ ├── index.less │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── drag-copy │ │ │ │ │ │ ├── drag-copy-mask.less │ │ │ │ │ │ ├── drag-copy-mask.tsx │ │ │ │ │ │ ├── drag-copy-point.less │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── grid-analysis-sheet │ │ │ │ │ ├── custom-cell │ │ │ │ │ │ ├── data-cell.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── theme.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── interface.ts │ │ │ │ ├── pivot-chart-sheet │ │ │ │ │ └── index.tsx │ │ │ │ ├── pivot-sheet │ │ │ │ │ └── index.tsx │ │ │ │ ├── strategy-sheet │ │ │ │ │ ├── custom-cell │ │ │ │ │ │ ├── col-cell.ts │ │ │ │ │ │ ├── data-cell.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── custom-data-set.ts │ │ │ │ │ ├── custom-tooltip │ │ │ │ │ │ ├── col-cell-tooltip.tsx │ │ │ │ │ │ ├── data-cell-tooltip.tsx │ │ │ │ │ │ ├── index.less │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── interface.ts │ │ │ │ │ │ └── row-cell-tooltip.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── table-sheet │ │ │ │ │ └── index.tsx │ │ │ └── tooltip │ │ │ │ ├── components │ │ │ │ ├── description.tsx │ │ │ │ ├── detail.tsx │ │ │ │ ├── head-info.tsx │ │ │ │ ├── icon.tsx │ │ │ │ ├── infos.tsx │ │ │ │ ├── interpretation.tsx │ │ │ │ ├── operator.tsx │ │ │ │ ├── simple-tips.tsx │ │ │ │ └── summary.tsx │ │ │ │ ├── context.ts │ │ │ │ ├── custom-tooltip.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── interface.ts │ │ ├── context │ │ │ └── SpreadSheetContext.tsx │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useEvents.ts │ │ │ ├── useLoading.ts │ │ │ ├── usePagination.ts │ │ │ ├── usePivotSheetUpdate.ts │ │ │ ├── useResize.ts │ │ │ └── useSpreadSheet.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── extendLocale.ts │ │ │ ├── index.ts │ │ │ ├── invokeComponent.tsx │ │ │ ├── options.ts │ │ │ └── reactRender.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── typings.d.ts │ └── vite.config.ts └── s2-vue │ ├── .releaserc.js │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ └── setup.js │ ├── jest.config.js │ ├── package.json │ ├── playground │ ├── App.vue │ ├── index.html │ └── index.ts │ ├── src │ ├── common │ │ └── constant │ │ │ ├── index.ts │ │ │ └── options.ts │ ├── components │ │ ├── drill-down │ │ │ └── index.vue │ │ ├── index.ts │ │ ├── pagination │ │ │ └── index.vue │ │ ├── sheets │ │ │ ├── base-sheet.vue │ │ │ ├── editable-sheet.vue │ │ │ ├── index.vue │ │ │ ├── pivot-sheet.vue │ │ │ └── table-sheet.vue │ │ └── tooltip │ │ │ ├── components │ │ │ ├── detail.vue │ │ │ ├── head-info.vue │ │ │ ├── infos.vue │ │ │ ├── operator │ │ │ │ ├── index.ts │ │ │ │ ├── index.vue │ │ │ │ ├── menu.vue │ │ │ │ └── title.vue │ │ │ ├── simple-tips.vue │ │ │ └── summary.vue │ │ │ ├── custom-tooltip.ts │ │ │ ├── index.vue │ │ │ └── interface.ts │ ├── hooks │ │ ├── useEvents.ts │ │ ├── useExpose.ts │ │ ├── useLoading.ts │ │ ├── usePagination.ts │ │ ├── useResize.ts │ │ ├── useSheetUpdate.ts │ │ └── useSpreadSheet.ts │ ├── icons │ │ ├── calendar-icon.vue │ │ ├── location-icon.vue │ │ └── text-icon.vue │ ├── index.ts │ ├── interface.ts │ └── utils │ │ ├── extendLocale.ts │ │ ├── initPropAndEmits.ts │ │ └── options.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── typings.d.ts │ └── vite.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── s2-site ├── .dumi │ ├── global.less │ ├── global.ts │ ├── pages │ │ ├── playground.en.tsx │ │ └── playground.zh.tsx │ └── theme │ │ └── plugin.ts ├── .dumirc.ts ├── .gitignore ├── .npmrc ├── .yarnrc ├── CNAME ├── LEGAL.md ├── README.md ├── docs │ ├── api │ │ ├── basic-class │ │ │ ├── base-bbox.en.md │ │ │ ├── base-bbox.zh.md │ │ │ ├── base-cell.en.md │ │ │ ├── base-cell.zh.md │ │ │ ├── base-data-set.en.md │ │ │ ├── base-data-set.zh.md │ │ │ ├── base-facet.en.md │ │ │ ├── base-facet.zh.md │ │ │ ├── base-tooltip.en.md │ │ │ ├── base-tooltip.zh.md │ │ │ ├── cell-data.en.md │ │ │ ├── cell-data.zh.md │ │ │ ├── hierarchy.en.md │ │ │ ├── hierarchy.zh.md │ │ │ ├── interaction.en.md │ │ │ ├── interaction.zh.md │ │ │ ├── node.en.md │ │ │ ├── node.zh.md │ │ │ ├── spreadsheet.en.md │ │ │ ├── spreadsheet.zh.md │ │ │ ├── store.en.md │ │ │ └── store.zh.md │ │ ├── components │ │ │ ├── advanced-sort.en.md │ │ │ ├── advanced-sort.zh.md │ │ │ ├── drill-down.en.md │ │ │ ├── drill-down.zh.md │ │ │ ├── export.en.md │ │ │ ├── export.zh.md │ │ │ ├── sheet-component.en.md │ │ │ ├── sheet-component.zh.md │ │ │ ├── switcher.en.md │ │ │ └── switcher.zh.md │ │ ├── general │ │ │ ├── s2-data-config.en.md │ │ │ ├── s2-data-config.zh.md │ │ │ ├── s2-event.en.md │ │ │ ├── s2-event.zh.md │ │ │ ├── s2-options.en.md │ │ │ ├── s2-options.zh.md │ │ │ ├── s2-theme.en.md │ │ │ └── s2-theme.zh.md │ │ ├── graphic.en.md │ │ ├── graphic.zh.md │ │ ├── pivot-chart.en.md │ │ └── pivot-chart.zh.md │ ├── common │ │ ├── browser.en.md │ │ ├── browser.zh.md │ │ ├── conditions.en.md │ │ ├── conditions.zh.md │ │ ├── contact-us.en.md │ │ ├── contact-us.zh.md │ │ ├── copy-export.en.md │ │ ├── copy-export.zh.md │ │ ├── custom-tooltip.en.md │ │ ├── custom-tooltip.zh.md │ │ ├── custom │ │ │ ├── cellCallBack.en.md │ │ │ ├── cellCallBack.zh.md │ │ │ ├── customSvgIcons.en.md │ │ │ ├── customSvgIcons.zh.md │ │ │ ├── customTreeNode.en.md │ │ │ ├── customTreeNode.zh.md │ │ │ ├── dataCellCallback.en.md │ │ │ ├── dataCellCallback.zh.md │ │ │ ├── headerActionIcons.en.md │ │ │ ├── headerActionIcons.zh.md │ │ │ ├── layoutArrange.en.md │ │ │ ├── layoutArrange.zh.md │ │ │ ├── layoutCellMeta.en.md │ │ │ ├── layoutCellMeta.zh.md │ │ │ ├── layoutCoordinate.en.md │ │ │ ├── layoutCoordinate.zh.md │ │ │ ├── layoutHierarchy.en.md │ │ │ ├── layoutHierarchy.zh.md │ │ │ ├── layoutSeriesNumberNodes.en.md │ │ │ └── layoutSeriesNumberNodes.zh.md │ │ ├── development.en.md │ │ ├── development.zh.md │ │ ├── env.en.md │ │ ├── env.zh.md │ │ ├── frozen.en.md │ │ ├── frozen.zh.md │ │ ├── header-action-icon.en.md │ │ ├── header-action-icon.zh.md │ │ ├── icon.en.md │ │ ├── icon.zh.md │ │ ├── install.en.md │ │ ├── install.zh.md │ │ ├── interaction.en.md │ │ ├── interaction.zh.md │ │ ├── merged-cell.en.md │ │ ├── merged-cell.zh.md │ │ ├── mini-chart.en.md │ │ ├── mini-chart.zh.md │ │ ├── packages.en.md │ │ ├── packages.zh.md │ │ ├── pagination.en.md │ │ ├── pagination.zh.md │ │ ├── series-number.en.md │ │ ├── series-number.zh.md │ │ ├── sort-param.en.md │ │ ├── sort-param.zh.md │ │ ├── style.en.md │ │ ├── style.zh.md │ │ ├── tooltip.en.md │ │ ├── tooltip.zh.md │ │ ├── totals.en.md │ │ ├── totals.zh.md │ │ ├── view-meta.en.md │ │ └── view-meta.zh.md │ └── manual │ │ ├── advanced │ │ ├── adaptive.en.md │ │ ├── adaptive.zh.md │ │ ├── analysis │ │ │ ├── advanced.en.md │ │ │ ├── advanced.zh.md │ │ │ ├── drill-down.en.md │ │ │ ├── drill-down.zh.md │ │ │ ├── export.en.md │ │ │ ├── export.zh.md │ │ │ ├── introduction.en.md │ │ │ ├── introduction.zh.md │ │ │ ├── pagination.en.md │ │ │ ├── pagination.zh.md │ │ │ ├── switcher.en.md │ │ │ └── switcher.zh.md │ │ ├── cell-render │ │ │ ├── chart-in-cell.en.md │ │ │ ├── chart-in-cell.zh.md │ │ │ ├── chart-with-g2.en.md │ │ │ ├── chart-with-g2.zh.md │ │ │ ├── custom.zh.md │ │ │ ├── image.zh.md │ │ │ ├── link-jump.en.md │ │ │ ├── link-jump.zh.md │ │ │ └── video.zh.md │ │ ├── custom │ │ │ ├── cell-align.en.md │ │ │ ├── cell-align.zh.md │ │ │ ├── cell-size.en.md │ │ │ ├── cell-size.zh.md │ │ │ ├── custom-collapse-nodes.en.md │ │ │ ├── custom-collapse-nodes.zh.md │ │ │ ├── custom-header.en.md │ │ │ ├── custom-header.zh.md │ │ │ ├── custom-icon.en.md │ │ │ ├── custom-icon.zh.md │ │ │ ├── custom-order.en.md │ │ │ ├── custom-order.zh.md │ │ │ ├── hook.en.md │ │ │ └── hook.zh.md │ │ ├── g-plugins.en.md │ │ ├── g-plugins.zh.md │ │ ├── get-cell-data.en.md │ │ ├── get-cell-data.zh.md │ │ ├── get-instance.en.md │ │ ├── get-instance.zh.md │ │ ├── hd-adapter.en.md │ │ ├── hd-adapter.zh.md │ │ ├── interaction │ │ │ ├── basic.en.md │ │ │ ├── basic.zh.md │ │ │ ├── copy.en.md │ │ │ ├── copy.zh.md │ │ │ ├── custom.en.md │ │ │ ├── custom.zh.md │ │ │ ├── hide-columns.en.md │ │ │ ├── hide-columns.zh.md │ │ │ ├── highlight-and-select-cell.en.md │ │ │ ├── highlight-and-select-cell.zh.md │ │ │ ├── merge-cell.en.md │ │ │ ├── merge-cell.zh.md │ │ │ ├── resize.en.md │ │ │ ├── resize.zh.md │ │ │ ├── scroll.en.md │ │ │ └── scroll.zh.md │ │ ├── pivot-chart.en.md │ │ ├── pivot-chart.zh.md │ │ ├── sheet │ │ │ ├── editable-mode.en.md │ │ │ ├── editable-mode.zh.md │ │ │ ├── strategy.en.md │ │ │ └── strategy.zh.md │ │ ├── vue.en.md │ │ └── vue.zh.md │ │ ├── basic │ │ ├── analysis │ │ │ └── editable-mode.zh.md │ │ ├── base-concept.en.md │ │ ├── base-concept.zh.md │ │ ├── conditions.en.md │ │ ├── conditions.zh.md │ │ ├── formatter.en.md │ │ ├── formatter.zh.md │ │ ├── i18n.en.md │ │ ├── i18n.zh.md │ │ ├── multi-line-text.en.md │ │ ├── multi-line-text.zh.md │ │ ├── pagination.en.md │ │ ├── pagination.zh.md │ │ ├── sheet-type │ │ │ ├── pivot-mode.en.md │ │ │ ├── pivot-mode.zh.md │ │ │ ├── table-mode.en.md │ │ │ └── table-mode.zh.md │ │ ├── sort │ │ │ ├── basic.en.md │ │ │ ├── basic.zh.md │ │ │ ├── group.en.md │ │ │ └── group.zh.md │ │ ├── theme.en.md │ │ ├── theme.zh.md │ │ ├── tooltip.en.md │ │ ├── tooltip.zh.md │ │ ├── totals.en.md │ │ └── totals.zh.md │ │ ├── contribution.en.md │ │ ├── contribution.zh.md │ │ ├── extended-reading │ │ ├── data-process │ │ │ ├── pivot.en.md │ │ │ ├── pivot.zh.md │ │ │ ├── table.en.md │ │ │ └── table.zh.md │ │ ├── layout │ │ │ ├── pivot.en.md │ │ │ ├── pivot.zh.md │ │ │ ├── table.en.md │ │ │ └── table.zh.md │ │ ├── performance.en.md │ │ └── performance.zh.md │ │ ├── faq.en.md │ │ ├── faq.zh.md │ │ ├── getting-started.en.md │ │ ├── getting-started.zh.md │ │ ├── introduction.en.md │ │ ├── introduction.zh.md │ │ ├── migration-v2.en.md │ │ └── migration-v2.zh.md ├── examples │ ├── analysis │ │ ├── conditions │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── background.ts │ │ │ │ ├── bidirectional-interval.ts │ │ │ │ ├── distinguish-cell.ts │ │ │ │ ├── gradient-interval.ts │ │ │ │ ├── icon-with-action.ts │ │ │ │ ├── icon.ts │ │ │ │ ├── intelligent-background.ts │ │ │ │ ├── interval.ts │ │ │ │ ├── meta.json │ │ │ │ ├── multi-background.ts │ │ │ │ ├── multi-conditions.ts │ │ │ │ ├── table-text.ts │ │ │ │ └── text.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── get-data │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── get-cell-data.ts │ │ │ │ ├── get-multi-cell-data.ts │ │ │ │ ├── get-single-cell-data.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── sort │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── advanced.tsx │ │ │ │ ├── custom-list.ts │ │ │ │ ├── custom-measure.ts │ │ │ │ ├── custom-method.ts │ │ │ │ ├── custom-sort-func.ts │ │ │ │ ├── custom-totals.ts │ │ │ │ ├── group-sort-base.ts │ │ │ │ ├── group-sort.tsx │ │ │ │ ├── meta.json │ │ │ │ └── table-sort.tsx │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── totals │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── basic.ts │ │ │ ├── calculate.ts │ │ │ ├── custom.ts │ │ │ ├── dimension-group-col.ts │ │ │ ├── dimension-group-row.ts │ │ │ ├── meta.json │ │ │ ├── multiple-values.ts │ │ │ └── tree.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── basic │ │ ├── pivot │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── grid.ts │ │ │ │ ├── meta.json │ │ │ │ └── tree.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── table │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── meta.json │ │ │ └── table.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── case │ │ ├── art │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── lost-text.tsx │ │ │ │ ├── meta.json │ │ │ │ ├── mosaic.tsx │ │ │ │ └── time-spend-abstract.tsx │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── comparison │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── measure-comparison.tsx │ │ │ │ ├── meta.json │ │ │ │ ├── multiple-people-comparison.tsx │ │ │ │ ├── philosophers.tsx │ │ │ │ └── time-spend.tsx │ │ │ ├── design.en.md │ │ │ ├── design.zh.md │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── data-preview │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── excel.tsx │ │ │ │ ├── house.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── kpi-strategy │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── basic.tsx │ │ │ │ ├── covid-trend.tsx │ │ │ │ └── meta.json │ │ │ ├── design.en.md │ │ │ ├── design.zh.md │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── performance-compare │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── basic.tsx │ │ │ │ ├── meta.json │ │ │ │ ├── pivot.tsx │ │ │ │ └── table.tsx │ │ │ ├── design.en.md │ │ │ ├── design.zh.md │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── proportion │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── group-drill-down.tsx │ │ │ ├── meta.json │ │ │ └── single-population-proportion.tsx │ │ │ ├── design.en.md │ │ │ ├── design.zh.md │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── custom │ │ ├── custom-cell │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── col-cell.ts │ │ │ │ ├── corner-cell.ts │ │ │ │ ├── corner-header.ts │ │ │ │ ├── custom-merged-cell.ts │ │ │ │ ├── custom-specified-cell.ts │ │ │ │ ├── custom-table-cell.ts │ │ │ │ ├── data-cell-placeholder.ts │ │ │ │ ├── data-cell.ts │ │ │ │ ├── empty-placeholder.ts │ │ │ │ ├── meta.json │ │ │ │ ├── mini-chart.ts │ │ │ │ ├── row-cell.ts │ │ │ │ ├── series-number-cell.ts │ │ │ │ └── totals-cell.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-dataset │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-strategy-sheet-dataset.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-icon │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-data-cell-icon.ts │ │ │ │ ├── custom-header-action-icon.ts │ │ │ │ ├── custom-svg-icon.ts │ │ │ │ ├── custom-tree-icon.ts │ │ │ │ ├── display-condition.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-layout │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-facet.ts │ │ │ │ ├── custom-layout-arrange.ts │ │ │ │ ├── custom-layout-cell-meta.ts │ │ │ │ ├── custom-layout-coordinate.ts │ │ │ │ ├── custom-layout-hierarchy.ts │ │ │ │ ├── custom-value-order.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-order │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-order-base.ts │ │ │ │ ├── custom-order.tsx │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-plugins │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── a11y.ts │ │ │ │ ├── meta.json │ │ │ │ └── rough.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-renderer │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── image.ts │ │ │ │ ├── meta.json │ │ │ │ ├── qrcode.ts │ │ │ │ └── video.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── custom-shape-and-chart │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── custom-g-shape.ts │ │ │ ├── custom-g2-chart.ts │ │ │ ├── meta.json │ │ │ ├── pivot-chart-cell.ts │ │ │ ├── pivot-chart-polar.ts │ │ │ ├── pivot-chart-spec.ts │ │ │ ├── pivot-chart.ts │ │ │ └── pivot-tree-line.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── gallery │ │ ├── index.en.md │ │ └── index.zh.md │ ├── interaction │ │ ├── advanced │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-link-jump.ts │ │ │ │ ├── custom-tree-link-jump.ts │ │ │ │ ├── intercepts.ts │ │ │ │ ├── merge-cell.ts │ │ │ │ ├── meta.json │ │ │ │ ├── overscroll-behavior.ts │ │ │ │ ├── pivot-hide-columns.ts │ │ │ │ ├── pivot-link-jump.ts │ │ │ │ ├── resize-active.ts │ │ │ │ ├── resize-disable.ts │ │ │ │ ├── row-expand-depth.ts │ │ │ │ ├── scroll-loop.ts │ │ │ │ ├── scroll-speed-ratio.ts │ │ │ │ ├── scroll-to-cell.ts │ │ │ │ ├── table-hide-columns.ts │ │ │ │ └── table-link-jump.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── basic │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── auto-reset-sheet-style.ts │ │ │ │ ├── brush-header.ts │ │ │ │ ├── brush-selection.ts │ │ │ │ ├── copy-export.ts │ │ │ │ ├── corner-cell-click-selection.ts │ │ │ │ ├── data-cell-click-selection.ts │ │ │ │ ├── data-cell-range-selection.ts │ │ │ │ ├── event.ts │ │ │ │ ├── header-cell-click-selection.ts │ │ │ │ ├── hover-after-scroll.ts │ │ │ │ ├── hover.ts │ │ │ │ ├── meta.json │ │ │ │ ├── resize-selected.ts │ │ │ │ ├── resize.ts │ │ │ │ ├── selected-cell-highlight.ts │ │ │ │ ├── selected-cell-move.ts │ │ │ │ └── state-theme.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── custom │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── disable-context-menu.ts │ │ │ ├── double-click-hide-columns.ts │ │ │ ├── meta.json │ │ │ └── row-col-hover-tooltip.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── layout │ │ ├── adaptive │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── container-adaptation.ts │ │ │ │ ├── meta.json │ │ │ │ ├── react-adaptive.tsx │ │ │ │ └── window-adaptation.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── basic │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── adaptive.ts │ │ │ │ ├── colAdaptive.ts │ │ │ │ ├── compact.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom-header-group │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-pivot-col-header.ts │ │ │ │ ├── custom-pivot-row-header.ts │ │ │ │ ├── custom-table-col-header.ts │ │ │ │ ├── custom-tree.ts │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── custom │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── custom-pivot-size.ts │ │ │ │ ├── custom-row-col-cell-width.ts │ │ │ │ ├── custom-table-size.ts │ │ │ │ ├── custom-tree-collapse-nodes.ts │ │ │ │ ├── custom-tree-row-width.ts │ │ │ │ ├── hide-pivot-columns.ts │ │ │ │ ├── hide-table-columns.ts │ │ │ │ ├── hide-value.ts │ │ │ │ ├── meta.json │ │ │ │ └── only-show-row-header.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── frozen │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── frozen-pivot-grid.ts │ │ │ │ ├── frozen-pivot-series.ts │ │ │ │ ├── frozen-pivot-tree.ts │ │ │ │ ├── meta.json │ │ │ │ ├── pivot-frozen-row-header.ts │ │ │ │ └── table-frozen.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── multi-line-text │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── line-break.ts │ │ │ ├── meta.json │ │ │ ├── pivot.ts │ │ │ └── table.ts │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ ├── react-component │ │ ├── drill-down │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── basic-panel.tsx │ │ │ │ ├── for-pivot.tsx │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── export │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── export-strategy.tsx │ │ │ │ ├── export.tsx │ │ │ │ └── meta.json │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── pagination │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── meta.json │ │ │ │ ├── pivot.tsx │ │ │ │ └── table.tsx │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── sheet │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── chart.tsx │ │ │ │ ├── editable.tsx │ │ │ │ ├── meta.json │ │ │ │ ├── pivot.tsx │ │ │ │ ├── strategy-mini-chart.tsx │ │ │ │ ├── strategy.tsx │ │ │ │ └── table.tsx │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ ├── switcher │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ │ ├── meta.json │ │ │ │ ├── pivot-header.tsx │ │ │ │ ├── pivot-with-children.tsx │ │ │ │ ├── pivot.tsx │ │ │ │ ├── pure-switcher.tsx │ │ │ │ └── table.tsx │ │ │ ├── design.en.md │ │ │ ├── design.zh.md │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ │ └── tooltip │ │ │ ├── API.en.md │ │ │ ├── API.zh.md │ │ │ ├── demo │ │ │ ├── basic.tsx │ │ │ ├── custom-click-show-tooltip.tsx │ │ │ ├── custom-content-base.ts │ │ │ ├── custom-content-react.tsx │ │ │ ├── custom-description.tsx │ │ │ ├── custom-hover-show-tooltip.tsx │ │ │ ├── custom-operation.tsx │ │ │ ├── custom-show-tooltip.tsx │ │ │ ├── custom-tooltip.tsx │ │ │ ├── meta.json │ │ │ └── operation.tsx │ │ │ ├── index.en.md │ │ │ └── index.zh.md │ └── theme │ │ ├── custom │ │ ├── API.en.md │ │ ├── API.zh.md │ │ ├── demo │ │ │ ├── custom-generate-palette.tsx │ │ │ ├── custom-manual-palette.tsx │ │ │ ├── custom-palette.ts │ │ │ ├── custom-schema.ts │ │ │ ├── custom-scrollbar.ts │ │ │ ├── custom-transparent-background.ts │ │ │ └── meta.json │ │ ├── design.en.md │ │ ├── design.zh.md │ │ ├── index.en.md │ │ └── index.zh.md │ │ └── default │ │ ├── API.en.md │ │ ├── API.zh.md │ │ ├── demo │ │ ├── colorful.ts │ │ ├── dark.ts │ │ ├── default.ts │ │ ├── gray.ts │ │ └── meta.json │ │ ├── index.en.md │ │ └── index.zh.md ├── externals.d.ts ├── package.json ├── playground │ ├── config-panel │ │ ├── attribute-tree.tsx │ │ ├── config-component │ │ │ ├── base │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── collapse-panel │ │ │ │ └── index.tsx │ │ │ ├── collapse │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── color-picker │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── dragger-upload │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ ├── input-number │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── radio │ │ │ │ └── index.tsx │ │ │ ├── select │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── switcher │ │ │ │ └── index.tsx │ │ │ ├── tab-pane │ │ │ │ └── index.tsx │ │ │ └── tab │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ ├── config-token │ │ │ └── index.ts │ │ ├── index.tsx │ │ └── types.ts │ ├── dataset │ │ └── mock-dataset.json │ ├── layouts │ │ ├── index.less │ │ └── index.tsx │ └── sheet-component │ │ ├── config.ts │ │ ├── index.less │ │ └── index.tsx ├── public │ └── site.css ├── s2.drawio └── script │ └── translate.mjs ├── scripts ├── add-version.js ├── api-extractor.json ├── bump-version.js ├── dts.js ├── rewriter.js └── util.js ├── tsconfig.base.json └── vetur.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | s2-site 2 | node_modules 3 | .cache 4 | .history 5 | esm 6 | lib 7 | dist 8 | temp 9 | .idea 10 | *.d.ts 11 | .eslintrc* 12 | /scripts/* 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # Ref: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: 🤔 Question / 问题咨询 5 | url: https://github.com/antvis/S2/discussions/new?category=q-a 6 | about: Discuss S2 usage / 讨论 S2 使用问题 7 | -------------------------------------------------------------------------------- /.github/workflows/auto-add-to-project.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Auto Add Issue And PR To Project 2 | on: 3 | issues: 4 | types: 5 | - opened 6 | pull_request_target: 7 | types: 8 | - opened 9 | 10 | jobs: 11 | add-to-project: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/add-to-project@v0.4.1 15 | with: 16 | project-url: https://github.com/orgs/antvis/projects/17 17 | github-token: ${{ secrets.GH_PROJECT_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/emoji-helper.yml: -------------------------------------------------------------------------------- 1 | name: 😄 Emoji Helper 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | emoji: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions-cool/emoji-helper@v1.0.0 12 | with: 13 | type: 'release' 14 | emoji: '+1, laugh, heart, hooray, rocket, eyes' 15 | -------------------------------------------------------------------------------- /.github/workflows/issue-check-inactive.yml: -------------------------------------------------------------------------------- 1 | name: Issue Check Inactive 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 */15 * *" 6 | 7 | 8 | jobs: 9 | issue-check-inactive: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check Inactive 13 | uses: actions-cool/issues-helper@main 14 | with: 15 | actions: 'check-inactive' 16 | inactive-label: '💤 inactive' 17 | inactive-day: 30 18 | -------------------------------------------------------------------------------- /.github/workflows/issue-remove-inactive.yml: -------------------------------------------------------------------------------- 1 | name: Issue Remove Inactive 2 | 3 | on: 4 | issues: 5 | types: [edited] 6 | issue_comment: 7 | types: [created, edited] 8 | 9 | jobs: 10 | issue-remove-inactive: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: remove inactive 14 | if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login 15 | uses: actions-cool/issues-helper@v3 16 | with: 17 | actions: 'remove-labels' 18 | issue-number: ${{ github.event.issue.number }} 19 | labels: '💤 inactive,🤔 need reproduce,👀 need more info' 20 | -------------------------------------------------------------------------------- /.github/workflows/issues-similarity-analysis.yml: -------------------------------------------------------------------------------- 1 | name: Issues Similarity Analysis 2 | 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | 7 | jobs: 8 | similarity-analysis: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: analysis 12 | uses: actions-cool/issues-similarity-analysis@main 13 | with: 14 | filter-threshold: 0.7 15 | title-excludes: '' 16 | comment-title: '### 你要找的是不是 (You may look for issues):' 17 | comment-body: '${index}. ${similarity} #${number}' 18 | show-footer: false 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-auto-set-label.yml: -------------------------------------------------------------------------------- 1 | name: 🏷 PR Auto Set Label 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited] 6 | 7 | jobs: 8 | auto-set-labels: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: pr-auto-label 12 | uses: actions-cool/pr-auto-label@v1 13 | with: 14 | enum: 'fix, chore, feat, refactor, test, docs' 15 | extra: 'feat/feature, docs/documentation' 16 | -------------------------------------------------------------------------------- /.github/workflows/pr-auto-set-next-label.yml: -------------------------------------------------------------------------------- 1 | name: 🏷 PR Auto Set Next Label 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited] 6 | branches: 'next' 7 | 8 | jobs: 9 | auto-set-next-label: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: pr-auto-set-next-label 13 | uses: actions-cool/issues-helper@main 14 | with: 15 | actions: 'add-labels' 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | issue-number: ${{ github.event.pull_request.number }} 18 | labels: 'next' 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-closed.yml: -------------------------------------------------------------------------------- 1 | name: PR Closed 2 | 3 | on: 4 | pull_request_target: 5 | types: [closed] 6 | 7 | jobs: 8 | pr-closed: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Remove lint and test failed labels 12 | uses: actions-cool/issues-helper@main 13 | with: 14 | actions: 'remove-labels' 15 | token: ${{ secrets.GITHUB_TOKEN }} 16 | issue-number: ${{ github.event.pull_request.number }} 17 | labels: '🚨 lint failed,🚨 test failed' 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | *node_modules/ 3 | .vscode 4 | .idea 5 | .history 6 | .changelog 7 | *.log 8 | *.zip 9 | coverage 10 | yarn-error.log 11 | 12 | # Mac files 13 | .DS_Store 14 | 15 | # site 16 | s2-site/dist 17 | s2-site/node_modules/ 18 | s2-site/.cache/ 19 | 20 | # packages 21 | packages/s2-*/lib/ 22 | packages/s2-*/esm/ 23 | packages/s2-*/dist/ 24 | packages/s2-*/temp/ 25 | packages/s2-*/coverage/ 26 | packages/s2-*/stats.html 27 | packages/**/*.css 28 | 29 | .swc 30 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/common.sh: -------------------------------------------------------------------------------- 1 | command_exists() { 2 | command -v "$1" >/dev/null 2>&1 3 | } 4 | 5 | # Workaround for Windows 10, Git Bash and Yarn 6 | if command_exists winpty && test -t 1; then 7 | exec 简体中文 | [English](./CONTRIBUTING.en-US.md) 3 | 4 | 请阅读 [贡献指南](https://s2.antv.antgroup.com/manual/contribution) 5 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface HTMLCanvasElement { 3 | __s2_instance__?: any; 4 | } 5 | } 6 | export {}; 7 | -------------------------------------------------------------------------------- /packages/s2-core/.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../.releaserc.base.js'); 2 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/README.md: -------------------------------------------------------------------------------- 1 | ### 调试单测 2 | 3 | 如果你想查看单测的运行结果,除了常规的 `pnpm core:test` 和 `pnpm react:test` 来运行测试之外,还可以 `可视化的调试单测(基于 jest-electron)`, 可以更快的发现单测的问题。 4 | 5 | 1. 选择单测 6 | 7 | 命令行运行 `pnpm core:start` 或者 `pnpm react:start` 8 | 9 | preview 10 | 11 | 2. 查看结果 12 | 13 | 因为本质上就是一个浏览器,如果单测结果不符合预期,可以正常打断点进行调试,快速分析原因。 14 | 15 | preview 16 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/bugs/__snapshots__/issue-446-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`export should export correct data with show seriesNumber 1`] = ` 4 | "序号 col0 col1 col2 5 | 1 col0-0 col1-0 col2-0 6 | 2 col0-1 col1-1 col2-1" 7 | `; 8 | 9 | exports[`export should export correct data without show seriesNumber 1`] = ` 10 | "col0 col1 col2 11 | col0-0 col1-0 col2-0 12 | col0-1 col1-1 col2-1" 13 | `; 14 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/bugs/__snapshots__/issue-565-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Export data in pivot tree mode should export correct col header in pivot tree mode 1`] = ` 4 | " col0 col0-0 col0-0 5 | col1 col1-0 col1-1 6 | col2 col2-0 col2-0 7 | row0 row1 row2 number number 8 | row0 9 | row0 row1-0 10 | row0 row1-0 row2-0 3 11 | row0 row1-1 12 | row0 row1-1 row2-0 2 4" 13 | `; 14 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-custom-grid.ts: -------------------------------------------------------------------------------- 1 | export const CustomGridData = [ 2 | { 3 | 'measure-1': 13, 4 | 'measure-2': 2, 5 | 'measure-3': 3, 6 | 'measure-4': 4, 7 | 'measure-5': 5, 8 | 'measure-6': 6, 9 | type: '家具', 10 | sub_type: '桌子', 11 | }, 12 | { 13 | 'measure-1': 11, 14 | 'measure-2': 8, 15 | 'measure-3': 32, 16 | 'measure-4': 43, 17 | 'measure-5': 45, 18 | 'measure-6': 65, 19 | type: '家具', 20 | sub_type: '椅子', 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-custom-tree.ts: -------------------------------------------------------------------------------- 1 | export const CustomTreeData = [ 2 | { 3 | type: '家具', 4 | sub_type: '桌子', 5 | 'measure-a': 1, 6 | 'measure-b': 2, 7 | 'measure-c': 3, 8 | 'measure-d': 4, 9 | 'measure-e': 5, 10 | 'measure-f': 6, 11 | }, 12 | { 13 | type: '家具', 14 | sub_type: '椅子', 15 | 'measure-a': 11, 16 | 'measure-b': 22, 17 | 'measure-c': 33, 18 | 'measure-d': 44, 19 | 'measure-e': 55, 20 | 'measure-f': 66, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-custom-trees.ts: -------------------------------------------------------------------------------- 1 | export const dataCustomTrees = [ 2 | { 3 | type: '家具', 4 | sub_type: '桌子', 5 | 'measure-a': 1, 6 | 'measure-b': 2, 7 | 'measure-c': 3, 8 | 'measure-d': 4, 9 | 'measure-e': 5, 10 | 'measure-f': 6, 11 | }, 12 | { 13 | type: '家具', 14 | sub_type: '椅子', 15 | 'measure-a': 11, 16 | 'measure-b': 22, 17 | 'measure-c': 33, 18 | 'measure-d': 44, 19 | 'measure-e': 55, 20 | 'measure-f': 66, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-1520.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["province", "city"], 4 | "columns": ["type"], 5 | "values": ["cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "province": "浙江", 11 | "city": "义乌", 12 | "cost": -10 13 | }, 14 | { 15 | "province": "浙江", 16 | "city": "义乌", 17 | "type": "笔", 18 | "cost": 10 19 | }, 20 | { 21 | "province": "浙江", 22 | "city": "杭州", 23 | "type": "笔", 24 | "cost": 0 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-2804.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["row0", "row1"], 4 | "columns": ["col0"], 5 | "values": ["cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "row0": "a-1", 11 | "col0": "b-1", 12 | "cost": 2 13 | }, 14 | { 15 | "row0": "a-2", 16 | "row1": "a-2-1", 17 | "col0": "b-2", 18 | "cost": 3 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-292.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["province", "city"], 4 | "columns": ["type"], 5 | "values": ["price", "cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "province": "浙江", 11 | "city": "义乌", 12 | "type": "笔", 13 | "cost": "2", 14 | "price": "8" 15 | }, 16 | { 17 | "province": "浙江", 18 | "city": "杭州", 19 | "type": "笔", 20 | "price": "", 21 | "cost": 10 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-372.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["row0"], 4 | "columns": ["col0"], 5 | "values": ["price", "cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "row0": "row0", 11 | "col0": "col0", 12 | "cost": 2 13 | }, 14 | { 15 | "row0": "row0", 16 | "col0": "col0", 17 | "price": 1 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-446.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "columns": ["col0", "col1", "col2"] 4 | }, 5 | "data": [ 6 | { 7 | "col0": "col0-0", 8 | "col1": "col1-0", 9 | "col2": "col2-0" 10 | }, 11 | { 12 | "col0": "col0-1", 13 | "col1": "col1-1", 14 | "col2": "col2-1" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-507.json: -------------------------------------------------------------------------------- 1 | { 2 | "valueInCols": { 3 | "fields": { 4 | "rows": ["row0", "row1"], 5 | "columns": ["col0", "col1"], 6 | "values": ["value2", "value0", "value1"], 7 | "valueInCols": true 8 | }, 9 | "meta": [ 10 | { 11 | "field": "row0", 12 | "name": "行0" 13 | }, 14 | { 15 | "field": "value0", 16 | "name": "指标0" 17 | }, 18 | { 19 | "field": "value1", 20 | "name": "指标1" 21 | } 22 | ], 23 | "data": [{}] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-511.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["row0", "row1"], 4 | "columns": [], 5 | "values": ["value"], 6 | "valueInCols": true 7 | }, 8 | "meta": [ 9 | { 10 | "field": "row0", 11 | "name": "销售渠道" 12 | }, 13 | { 14 | "field": "row1", 15 | "name": "客户姓名" 16 | }, 17 | { 18 | "field": "value", 19 | "name": "数量" 20 | } 21 | ], 22 | "data": [ 23 | { 24 | "value": 1354, 25 | "row0": "欧尚", 26 | "row1": "张三" 27 | }, 28 | { 29 | "value": 1460, 30 | "row0": "物美", 31 | "row1": "李四" 32 | }, 33 | { 34 | "value": 1339, 35 | "row0": "物美", 36 | "row1": "张三" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/data-issue-868.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["row0"], 4 | "columns": ["col0"], 5 | "values": ["cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "row0": 0, 11 | "col0": 0, 12 | "cost": 2 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/simple-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["province", "city"], 4 | "columns": ["type"], 5 | "values": ["price", "cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "province": "浙江", 11 | "city": "义乌", 12 | "type": "笔", 13 | "price": 1, 14 | "cost": 2 15 | }, 16 | { 17 | "province": "浙江", 18 | "city": "义乌", 19 | "type": "笔", 20 | "price": 1, 21 | "cost": 2 22 | }, 23 | { 24 | "province": "浙江", 25 | "city": "杭州", 26 | "type": "笔", 27 | "price": 1, 28 | "cost": 2 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/data/simple-table-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "columns": ["price", "cost", "province", "city", "type"] 4 | }, 5 | "data": [ 6 | { 7 | "province": "浙江", 8 | "city": "义乌", 9 | "type": "笔", 10 | "price": 1, 11 | "cost": 2 12 | }, 13 | { 14 | "province": "浙江", 15 | "city": "义乌", 16 | "type": "笔", 17 | "price": 1, 18 | "cost": 2 19 | }, 20 | { 21 | "province": "浙江", 22 | "city": "杭州", 23 | "type": "笔", 24 | "price": 1, 25 | "cost": 2 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/matchers/index.js: -------------------------------------------------------------------------------- 1 | require('./colorMatcher'); 2 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/matchers/matcher.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace jest { 3 | interface Matchers { 4 | toBeColor(hexString: string): R; 5 | } 6 | } 7 | } 8 | 9 | export {}; 10 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | require('./matchers'); 2 | 3 | ['time', 'info', 'warn'].forEach((type) => { 4 | jest.spyOn(console, type).mockImplementation(() => {}); 5 | }); 6 | 7 | jest.mock('@/ui/hd-adapter', () => { 8 | return { 9 | HdAdapter: jest.fn().mockImplementation(() => { 10 | return { 11 | init: jest.fn(), 12 | destroy: jest.fn(), 13 | }; 14 | }), 15 | }; 16 | }); 17 | 18 | jest.setTimeout(60 * 1000); 19 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-dataset-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SpreadSheet Custom Dataset Tests should render custom data set 1`] = ` 4 | Array [ 5 | Object { 6 | "id": "province", 7 | }, 8 | Object { 9 | "id": "city", 10 | }, 11 | Object { 12 | "id": "type", 13 | }, 14 | Object { 15 | "id": "root[&]浙江", 16 | }, 17 | Object { 18 | "id": "root[&]浙江[&]义乌", 19 | }, 20 | Object { 21 | "id": "root[&]浙江[&]杭州", 22 | }, 23 | Object { 24 | "id": "root[&]笔", 25 | }, 26 | Object { 27 | "id": "root[&]笔[&]price", 28 | }, 29 | Object { 30 | "id": "root[&]笔[&]cost", 31 | }, 32 | ] 33 | `; 34 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/__snapshots__/hidden-columns-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SpreadSheet Hidden Columns Tests PivotSheet Multiple Values Tests should render correct row corner after hide measure node 1`] = ` 4 | Array [ 5 | Object { 6 | "height": 30, 7 | "width": 119, 8 | "x": 0, 9 | "y": 60, 10 | }, 11 | Object { 12 | "height": 30, 13 | "width": 119, 14 | "x": 119, 15 | "y": 60, 16 | }, 17 | Object { 18 | "height": 30, 19 | "width": 238, 20 | "x": 0, 21 | "y": 0, 22 | }, 23 | Object { 24 | "height": 30, 25 | "width": 238, 26 | "x": 0, 27 | "y": 30, 28 | }, 29 | ] 30 | `; 31 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/__snapshots__/layout-hooks-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`layout hooks spec layout cell meta hook 1`] = ` 4 | Object { 5 | "$$extra$$": "profit", 6 | "$$value$$": 999, 7 | "area": "中南", 8 | "sub_type": "系固件", 9 | "type": "办公用品", 10 | } 11 | `; 12 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/__snapshots__/sort-by-order-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Fallback Calc Sub Totals Sort Tests should sort by ASC sort method 1`] = ` 4 | Array [ 5 | "2024-05-08", 6 | "2024-05-07", 7 | "2024-05-06", 8 | "2024-05-05", 9 | "2024-05-04", 10 | "2024-05-03", 11 | "2024-05-02", 12 | ] 13 | `; 14 | 15 | exports[`Fallback Calc Sub Totals Sort Tests should sort by DESC sort method 1`] = ` 16 | Array [ 17 | "2024-05-02", 18 | "2024-05-03", 19 | "2024-05-04", 20 | "2024-05-05", 21 | "2024-05-06", 22 | "2024-05-07", 23 | "2024-05-08", 24 | ] 25 | `; 26 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/__snapshots__/spread-sheet-resize-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SpreadSheet Resize Active Tests should render correctly layout when tree row width is invalid number 1`] = ` 4 | Array [ 5 | Object { 6 | "height": 30, 7 | "id": "root[&]浙江", 8 | "width": 120, 9 | }, 10 | Object { 11 | "height": 30, 12 | "id": "root[&]浙江[&]义乌", 13 | "width": 120, 14 | }, 15 | Object { 16 | "height": 30, 17 | "id": "root[&]浙江[&]杭州", 18 | "width": 120, 19 | }, 20 | ] 21 | `; 22 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/spreadsheet/custom-dataset-spec.ts: -------------------------------------------------------------------------------- 1 | import { createPivotSheet } from 'tests/util/helpers'; 2 | import { PivotDataSet } from '../../src'; 3 | import { pickMap } from '../util/fp'; 4 | 5 | describe('SpreadSheet Custom Dataset Tests', () => { 6 | class CustomDataSet extends PivotDataSet { 7 | public getFieldName() { 8 | return 'test'; 9 | } 10 | } 11 | 12 | test('should render custom data set', async () => { 13 | const s2 = createPivotSheet({ 14 | dataSet: (spreadsheet) => new CustomDataSet(spreadsheet), 15 | }); 16 | 17 | await s2.render(); 18 | 19 | const headerNodes = pickMap(['id', 'values'])(s2.facet.getHeaderNodes()); 20 | 21 | expect(headerNodes).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/data-process/README_zh.md: -------------------------------------------------------------------------------- 1 | # 本文件夹测试用例用于保证「表」核心渲染流程 2 | 3 | ## 数据转换核心流程 4 | 5 | 1. 通过配置信息将原始数组转换成以行列维度为 path 的多维数组 6 | 2. 通过 hierarchy 生成的带层级的数据结构 7 | 3. 计算行列头格子的坐标 8 | 4. 通过行列头格子的坐标计算交叉结果单元格坐标和数据 9 | 10 | > 需要保证「透视表」、「明细表」等各自的核心数据流程 11 | 12 | ## UI 渲染核心流程 13 | 14 | 1. 角头、行头、列头、数据区域 出现 15 | 2. 分页 16 | 3. 交互行为 17 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/shared/utils/classnames-spec.ts: -------------------------------------------------------------------------------- 1 | import { getStrategySheetTooltipClsName } from '../../../../src/shared'; 2 | 3 | describe('classnames test', () => { 4 | test('#getStrategySheetTooltipClsName()', () => { 5 | expect(getStrategySheetTooltipClsName()).toEqual( 6 | 'antv-s2-strategy-sheet-tooltip', 7 | ); 8 | expect(getStrategySheetTooltipClsName('test')).toEqual( 9 | 'antv-s2-strategy-sheet-tooltip-test', 10 | ); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/__snapshots__/sort-action-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`total group dimension sort test should sort by col total with group 1`] = ` 4 | Array [ 5 | CellData { 6 | "extraField": "price", 7 | "raw": Object { 8 | "city": "杭州", 9 | "price": "666", 10 | "type": "笔", 11 | }, 12 | }, 13 | CellData { 14 | "extraField": "price", 15 | "raw": Object { 16 | "city": "杭州", 17 | "price": "999", 18 | "type": "纸张", 19 | }, 20 | }, 21 | ] 22 | `; 23 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/canvas-spec.ts: -------------------------------------------------------------------------------- 1 | import { getOffscreenCanvas, removeOffscreenCanvas } from '@/utils'; 2 | import { sleep } from './../../util/helpers'; 3 | 4 | describe('Canvas Utils Tests', () => { 5 | const ID = 'antv-s2-offscreen-canvas'; 6 | 7 | test('should get offscreen canvas', () => { 8 | const canvas = getOffscreenCanvas(); 9 | 10 | expect(canvas.id).toEqual(ID); 11 | expect(document.getElementById(ID)).toBeTruthy(); 12 | }); 13 | 14 | test('should remove offscreen canvas', async () => { 15 | getOffscreenCanvas(); 16 | 17 | await sleep(500); 18 | 19 | removeOffscreenCanvas(); 20 | expect(document.getElementById(ID)).toBeFalsy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/data-item-type-checker-spec.ts: -------------------------------------------------------------------------------- 1 | import { isMultiDataItem } from '@/utils/data-item-type-checker'; 2 | 3 | describe('data-item-type-checker test', () => { 4 | test(`should return true when it's multi data item`, () => { 5 | expect(isMultiDataItem({ values: [[1], [2]] })).toBeTrue(); 6 | }); 7 | 8 | test(`should return false when it's single data item`, () => { 9 | expect(isMultiDataItem(123)).toBeFalse(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/get-classnames-spec.ts: -------------------------------------------------------------------------------- 1 | import { getClassNameWithPrefix } from '../../../src/utils/get-classnames'; 2 | 3 | describe('get-classnames test', () => { 4 | test('should get className with prefix', () => { 5 | expect(getClassNameWithPrefix()).toEqual('antv-s2-'); 6 | expect(getClassNameWithPrefix('a')).toEqual('antv-s2-a'); 7 | expect(getClassNameWithPrefix('a', 'b')).toEqual('antv-s2-a-b'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/layout/__snapshots__/frozen-spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`frozen test should get valid frozen options 1`] = ` 4 | Object { 5 | "colCount": 0, 6 | "rowCount": 0, 7 | "trailingColCount": 0, 8 | "trailingRowCount": 0, 9 | } 10 | `; 11 | 12 | exports[`frozen test should get valid frozen options 2`] = ` 13 | Object { 14 | "colCount": 1, 15 | "rowCount": 2, 16 | "trailingColCount": 3, 17 | "trailingRowCount": 4, 18 | } 19 | `; 20 | 21 | exports[`frozen test should get valid frozen options 3`] = ` 22 | Object { 23 | "colCount": 10, 24 | "rowCount": 10, 25 | "trailingColCount": 0, 26 | "trailingRowCount": 0, 27 | } 28 | `; 29 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/layout/whether-leaf-by-level-spec.ts: -------------------------------------------------------------------------------- 1 | import { whetherLeafByLevel } from '../../../../src/utils/layout/whether-leaf-by-level'; 2 | import { createFakeSpreadSheet } from '../../../util/helpers'; 3 | 4 | describe('whether-leaf-by-level test', () => { 5 | test('should whether leaf by level', () => { 6 | const s2 = createFakeSpreadSheet(); 7 | 8 | expect( 9 | whetherLeafByLevel({ 10 | spreadsheet: s2, 11 | level: 0, 12 | fields: [], 13 | }), 14 | ).toEqual(false); 15 | 16 | expect( 17 | whetherLeafByLevel({ 18 | spreadsheet: s2, 19 | level: 1, 20 | fields: ['a', 'b'], 21 | }), 22 | ).toEqual(true); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/math-spec.ts: -------------------------------------------------------------------------------- 1 | import { floor, round } from '../../../src/utils/math'; 2 | 3 | describe('math test', () => { 4 | test('#floor()', () => { 5 | expect(floor(1.234)).toBe(1.23); 6 | expect(floor(1.230123)).toBe(1.23); 7 | expect(floor(1.234, 3)).toBe(1.234); 8 | }); 9 | 10 | test('#round()', () => { 11 | expect(round(1.234)).toBe(1); 12 | expect(round(1.9)).toBe(1); 13 | expect(round(12)).toBe(12); 14 | expect(round(null as unknown as number)).toBe(null); 15 | expect(round(undefined as unknown as number)).toBe(undefined); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/theme-spec.ts: -------------------------------------------------------------------------------- 1 | import { getPalette } from '../../../src/utils/theme'; 2 | 3 | describe('Theme Utils Tests', () => { 4 | test('should get correctly palette', () => { 5 | expect(getPalette()).toMatchSnapshot(); 6 | expect(getPalette('default')).toMatchSnapshot(); 7 | expect(getPalette('colorful')).toMatchSnapshot(); 8 | expect(getPalette('gray')).toMatchSnapshot(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/unit/utils/veen-arr-spec.ts: -------------------------------------------------------------------------------- 1 | import { vennArr } from '@/utils/veen-arr'; 2 | 3 | describe('veen-arr test', () => { 4 | test('should return intersection range', () => { 5 | expect(vennArr([1, 4], [3, 5])).toEqual([3, 4]); 6 | }); 7 | 8 | test('should return empty array when given arrs have no overlap', () => { 9 | expect(vennArr([1, 4], [5, 10])).toEqual([]); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/s2-core/__tests__/util/fp.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-restricted-imports 2 | import fp from 'lodash/fp'; 3 | 4 | export const pickMap = (values: string[]) => fp.map(fp.pick(values)); 5 | -------------------------------------------------------------------------------- /packages/s2-core/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../jest.config.base'); 2 | -------------------------------------------------------------------------------- /packages/s2-core/src/cell/table-corner-cell.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'lodash'; 2 | import { TableColCell } from './table-col-cell'; 3 | 4 | export class TableCornerCell extends TableColCell { 5 | public getStyle(name?: string) { 6 | return name ? get(this.theme, name) : this.theme?.cornerCell; 7 | } 8 | 9 | protected showSortIcon() { 10 | return false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/classnames.ts: -------------------------------------------------------------------------------- 1 | export const S2_PREFIX_CLS = 'antv-s2'; 2 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/condition.ts: -------------------------------------------------------------------------------- 1 | export const VALUE_RANGES_KEY = 'valueRanges'; 2 | export const DEFAULT_VALUE_RANGES = {}; 3 | 4 | export const DEFAULT_FONT_COLOR = '#000000'; 5 | export const REVERSE_FONT_COLOR = '#FFFFFF'; 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/copy.ts: -------------------------------------------------------------------------------- 1 | export enum CopyType { 2 | ALL, 3 | COL, 4 | ROW, 5 | } 6 | 7 | export const LINE_SEPARATOR = '\r\n'; 8 | 9 | export const TAB_SEPARATOR = '\t'; 10 | 11 | export const CSV_SEPARATOR = ','; 12 | 13 | // 每次异步渲染数据的阈值 14 | export const AsyncRenderThreshold = 5000; 15 | 16 | // 选择异步请求的阈值 17 | export const AsyncRequestThreshold = 100000; 18 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/dataConfig.ts: -------------------------------------------------------------------------------- 1 | import type { S2DataConfig } from '../../common/interface/s2DataConfig'; 2 | 3 | export const DEFAULT_DATA_CONFIG: Readonly = { 4 | data: [], 5 | fields: { 6 | rows: [], 7 | columns: [], 8 | values: [], 9 | valueInCols: true, 10 | }, 11 | meta: [], 12 | sortParams: [], 13 | filterParams: [], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/events/index.ts: -------------------------------------------------------------------------------- 1 | export * from './basic'; 2 | export * from './interaction'; 3 | export * from './origin'; 4 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/events/interaction.ts: -------------------------------------------------------------------------------- 1 | export enum InteractionEvent { 2 | DATA_CELL_CLICK_EVENT = 'interaction:data-cell-click', 3 | MERGED_CELLS_CLICK_EVENT = 'interaction:merged-cells-click', 4 | CORNER_TEXT_CLICK_EVENT = 'interaction:corner-cell-text-click', 5 | ROW_COLUMN_CLICK_EVENT = 'interaction:row-column-click', 6 | ROW_TEXT_CLICK_EVENT = 'interaction:row-text-click', 7 | HOVER_EVENT = 'interaction:hover', 8 | } 9 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/field.ts: -------------------------------------------------------------------------------- 1 | // 值字段的 id 是固定的! 2 | export const VALUE_FIELD = '$$value$$'; 3 | export const EXTRA_FIELD = '$$extra$$'; 4 | export const ORIGIN_FIELD = '$$origin$$'; 5 | export const EXTRA_COLUMN_FIELD = '$$extra_column$$'; 6 | export const TOTAL_VALUE = '$$total$$'; 7 | export const MULTI_VALUE = '$$multi$$'; 8 | export const SERIES_NUMBER_FIELD = '$$series_number$$'; 9 | export const EMPTY_FIELD_VALUE = '$$empty_field_value$$'; 10 | export const EMPTY_EXTRA_FIELD_PLACEHOLDER = '$$empty_extra_placeholder$$'; 11 | export const NULL_SYMBOL_ID = '$$null$$'; 12 | export const UNDEFINED_SYMBOL_ID = '$$undefined$$'; 13 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/index.ts: -------------------------------------------------------------------------------- 1 | export * from './basic'; 2 | export * from './classnames'; 3 | export * from './condition'; 4 | export * from './copy'; 5 | export * from './dataConfig'; 6 | export * from './events'; 7 | export * from './field'; 8 | export * from './frozen'; 9 | export * from './interaction'; 10 | export * from './node'; 11 | export * from './options'; 12 | export * from './pagination'; 13 | export * from './query'; 14 | export * from './resize'; 15 | export * from './theme'; 16 | export * from './tooltip'; 17 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/node.ts: -------------------------------------------------------------------------------- 1 | export const ROOT_NODE_ID = 'root'; 2 | export const NODE_ID_SEPARATOR = '[&]'; 3 | export const ROOT_BEGINNING_REGEX = /^root\[&\]*/; 4 | export const DATA_CELL_ID_SEPARATOR = '-'; 5 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/pagination.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PAGE_INDEX = 1; 2 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/query.ts: -------------------------------------------------------------------------------- 1 | export enum QueryDataType { 2 | /* 获取所有的数据 */ 3 | All = 'all', 4 | /* 只需要明细数据 */ 5 | DetailOnly = 'detailOnly', 6 | } 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/constant/renderer.ts: -------------------------------------------------------------------------------- 1 | export enum CellRendererType { 2 | VIDEO = 'VIDEO', 3 | IMAGE = 'IMAGE', 4 | } 5 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/i18n/en_US.ts: -------------------------------------------------------------------------------- 1 | export const EN_US = { 2 | 小计: 'Total', 3 | 总计: 'Total', 4 | 总和: '(SUM)', 5 | 项: 'items', 6 | 已选择: 'selected', 7 | 序号: 'Index', 8 | 度量: 'Measure', 9 | 数值: 'Measure', 10 | 共计: 'Total', 11 | 条: '', 12 | 隐藏: 'Hide', 13 | 组内升序: 'Group ASC', 14 | 组内降序: 'Group DESC', 15 | 升序: 'ASC', 16 | 降序: 'DESC', 17 | 不排序: 'No order', 18 | 暂无数据: 'No Data', 19 | ',': ', ', 20 | }; 21 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/i18n/ru_RU.ts: -------------------------------------------------------------------------------- 1 | export const RU = { 2 | 小计: 'Промежуточный итог', 3 | 总计: 'Общий итог', 4 | 总和: '(СУММА)', 5 | 项: 'элемент(-а/-ов)', 6 | 已选择: 'выбрано', 7 | 序号: 'Индекс', 8 | 度量: 'Мера', 9 | 数值: 'Численная величина', 10 | 共计: 'Общее количество', 11 | 条: '', 12 | 隐藏: 'Скрыть', 13 | 趋势: 'Курс', 14 | 组内升序: 'Группировать по возрастанию', 15 | 组内降序: 'Группировать по убыванию', 16 | 升序: 'По возрастанию', 17 | 降序: 'По убыванию', 18 | 不排序: 'Не отсортировано', 19 | ',': ', ', 20 | }; 21 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/i18n/zh_CN.ts: -------------------------------------------------------------------------------- 1 | export const ZH_CN = { 2 | 小计: '小计', 3 | 总计: '总计', 4 | 总和: '(总和)', 5 | 项: '项', 6 | 已选择: '已选择', 7 | 序号: '序号', 8 | 度量: '度量', 9 | 数值: '数值', 10 | 共计: '共计', 11 | 条: '条', 12 | 趋势: '趋势', 13 | 隐藏: '隐藏', 14 | 组内升序: '组内升序', 15 | 升序: '升序', 16 | 降序: '降序', 17 | 组内降序: '组内降序', 18 | 不排序: '不排序', 19 | 暂无数据: '暂无数据', 20 | ',': ',', 21 | }; 22 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/icons/factory.ts: -------------------------------------------------------------------------------- 1 | import { get, keys, lowerCase } from 'lodash'; 2 | import * as InternalSvgIcons from './svg'; 3 | 4 | const SVGCache: Record = {}; 5 | 6 | export const registerIcon = (name: string, src: string) => { 7 | SVGCache[lowerCase(name)] = src; 8 | }; 9 | 10 | export const getIcon = (name: string): string => SVGCache[lowerCase(name)]; 11 | 12 | // 缓存内置 Icon 信息 13 | keys(InternalSvgIcons).forEach((name) => { 14 | const icon = get(InternalSvgIcons, name); 15 | 16 | registerIcon(name, icon); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/icons/index.ts: -------------------------------------------------------------------------------- 1 | export { getIcon, registerIcon } from './factory'; 2 | export { GuiIcon } from './gui-icon'; 3 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/icons/svg/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ArrowDown, 3 | ArrowUp, 4 | CellDown, 5 | CellUp, 6 | DrillDownIcon, 7 | Empty, 8 | ExpandColIcon, 9 | EyeOutlined, 10 | GlobalAsc, 11 | GlobalDesc, 12 | GroupAsc, 13 | GroupDesc, 14 | GroupNone, 15 | InfoCircle, 16 | Minus, 17 | Play, 18 | Plus, 19 | SortDown, 20 | SortDownSelected, 21 | SortUp, 22 | SortUpSelected, 23 | Trend, 24 | } from './svgs'; 25 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constant'; 2 | export * from './debug'; 3 | export * from './i18n'; 4 | export * from './icons'; 5 | export * from './interface'; 6 | export * from './store'; 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/collapse.ts: -------------------------------------------------------------------------------- 1 | import type { Node } from '../../facet/layout/node'; 2 | import type { RowCellStyle } from './style'; 3 | 4 | export type RowCellCollapsedParams = { 5 | isCollapsed: boolean; 6 | node: Node; 7 | collapseFields?: RowCellStyle['collapseFields']; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/events.ts: -------------------------------------------------------------------------------- 1 | // 这里存放 emit 事件 透出的信息 2 | 3 | import type { FederatedPointerEvent as GEvent, IEventTarget } from '@antv/g'; 4 | import type { Node } from '../../facet/layout/node'; 5 | import type { ViewMeta } from './basic'; 6 | import type { S2CellType } from './interaction'; 7 | 8 | export interface TargetCellInfo { 9 | target: S2CellType; 10 | event: GEvent; 11 | viewMeta: ViewMeta | Node; 12 | } 13 | 14 | export type CellEventTarget = 15 | | GEvent['target'] 16 | | null 17 | | undefined 18 | | EventTarget 19 | | IEventTarget; 20 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/frame.ts: -------------------------------------------------------------------------------- 1 | import type { SpreadSheet } from '../../sheet-type'; 2 | 3 | export interface FrameConfig { 4 | position: { 5 | x: number; 6 | y: number; 7 | }; 8 | scrollX?: number; 9 | cornerWidth: number; 10 | cornerHeight: number; 11 | viewportWidth: number; 12 | viewportHeight: number; 13 | showViewportLeftShadow: boolean; 14 | showViewportRightShadow: boolean; 15 | spreadsheet: SpreadSheet; 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/frozen.ts: -------------------------------------------------------------------------------- 1 | import type { FrozenGroup } from '../../group/frozen-group'; 2 | import type { FrozenGroupArea, FrozenGroupType } from '../constant/frozen'; 3 | 4 | export interface AreaBBox { 5 | width?: number; 6 | height?: number; 7 | x?: number; 8 | y?: number; 9 | range: number[]; 10 | } 11 | 12 | export type FrozenGroupAreas = Record; 13 | 14 | export type FrozenGroups = Record; 15 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/group.ts: -------------------------------------------------------------------------------- 1 | import type { SpreadSheet } from '../../sheet-type'; 2 | 3 | export type GridGroupConstructorParameters = { 4 | s2: SpreadSheet; 5 | name?: string; 6 | zIndex?: number; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/index.ts: -------------------------------------------------------------------------------- 1 | export * from './basic'; 2 | export * from './collapse'; 3 | export * from './condition'; 4 | export * from './emitter'; 5 | export * from './events'; 6 | export * from './export'; 7 | export * from './facet'; 8 | export * from './frame'; 9 | export * from './hooks'; 10 | export * from './interaction'; 11 | export * from './node'; 12 | export * from './renderer'; 13 | export * from './resize'; 14 | export * from './s2DataConfig'; 15 | export * from './s2Options'; 16 | export * from './scroll'; 17 | export * from './store'; 18 | export * from './style'; 19 | export * from './text'; 20 | export * from './theme'; 21 | export * from './tooltip'; 22 | export * from './type-utils'; 23 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/node.ts: -------------------------------------------------------------------------------- 1 | // 角头 node 类型 2 | export enum CornerNodeType { 3 | Row = 'row', 4 | Col = 'col', 5 | Series = 'series', 6 | } 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/scroll.ts: -------------------------------------------------------------------------------- 1 | export interface ScrollOffset { 2 | scrollX?: number; 3 | scrollY?: number; 4 | rowHeaderScrollX?: number; 5 | } 6 | 7 | export interface AreaRange { 8 | start: number; 9 | size: number; 10 | } 11 | 12 | export type CellScrollPosition = Required; 13 | 14 | export interface CellScrollOffset { 15 | deltaX?: number; 16 | deltaY?: number; 17 | offset?: number; 18 | offsetX: number; 19 | offsetY: number; 20 | } 21 | 22 | export interface CellScrollToOptions { 23 | /** 24 | * 是否展示滚动动画 25 | */ 26 | animate?: boolean; 27 | 28 | /** 29 | * 是否触发滚动事件 30 | */ 31 | skipScrollEvent?: boolean; 32 | } 33 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/text.ts: -------------------------------------------------------------------------------- 1 | export interface RenderTextShapeOptions { 2 | /** 3 | * 只渲染文本, 不记录 textShape 信息 4 | */ 5 | shallowRender: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/common/interface/type-utils.ts: -------------------------------------------------------------------------------- 1 | export type PickEssential = { 2 | [K in keyof O as Pick, K> extends Pick ? never : K]: O[K]; 3 | }; 4 | 5 | export type DeepRequired> = { 6 | [K in keyof T]-?: NonNullable extends Record 7 | ? DeepRequired> 8 | : NonNullable; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/s2-core/src/data-set/index.ts: -------------------------------------------------------------------------------- 1 | import { BaseDataSet } from './base-data-set'; 2 | import { CustomGridPivotDataSet } from './custom-grid-pivot-data-set'; 3 | import { PivotDataSet } from './pivot-data-set'; 4 | import { TableDataSet } from './table-data-set'; 5 | 6 | export { CellData } from './cell-data'; 7 | 8 | export { BaseDataSet, CustomGridPivotDataSet, PivotDataSet, TableDataSet }; 9 | 10 | export * from './interface'; 11 | -------------------------------------------------------------------------------- /packages/s2-core/src/engine/CustomImage.ts: -------------------------------------------------------------------------------- 1 | import { Image, type DisplayObjectConfig, type ImageStyleProps } from '@antv/g'; 2 | 3 | /** 4 | * 自定义 g Image 图形 5 | */ 6 | export class CustomImage extends Image { 7 | /** 自定义 imgType (不能命名为 type,影响 g5.0 渲染) */ 8 | public imgType: string; 9 | 10 | constructor(imgType: string, options?: DisplayObjectConfig) { 11 | super(options); 12 | 13 | this.imgType = imgType; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-core/src/engine/CustomRect.ts: -------------------------------------------------------------------------------- 1 | import { Rect, type DisplayObjectConfig, type RectStyleProps } from '@antv/g'; 2 | 3 | /** 4 | * 自定义 rect 图形 5 | * - 有 appendInfo 属性 6 | */ 7 | export class CustomRect> extends Rect { 8 | public appendInfo: T; 9 | 10 | constructor(options: DisplayObjectConfig, appendInfo: T) { 11 | super(options); 12 | 13 | this.appendInfo = appendInfo; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-core/src/engine/CustomText.ts: -------------------------------------------------------------------------------- 1 | import { Text, type DisplayObjectConfig, type TextStyleProps } from '@antv/g'; 2 | 3 | /** 4 | * 自定义 text 图形 5 | * - 有 appendInfo 属性 6 | */ 7 | export class CustomText> extends Text { 8 | public declare appendInfo: T; 9 | 10 | constructor(options: DisplayObjectConfig, appendInfo: T) { 11 | super(options); 12 | 13 | this.appendInfo = appendInfo; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-core/src/engine/index.ts: -------------------------------------------------------------------------------- 1 | // 类型定义 2 | export * from './interface'; 3 | 4 | // 自定义图形 5 | export * from './CustomImage'; 6 | export * from './CustomRect'; 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/engine/interface.ts: -------------------------------------------------------------------------------- 1 | export interface SimpleBBox { 2 | x: number; 3 | y: number; 4 | width: number; 5 | height: number; 6 | } 7 | 8 | export interface BBox { 9 | x: number; 10 | y: number; 11 | minX: number; 12 | minY: number; 13 | maxX: number; 14 | maxY: number; 15 | width: number; 16 | height: number; 17 | } 18 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pivot-chart'; 2 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/pivot-chart/cell/cell-type.ts: -------------------------------------------------------------------------------- 1 | export enum AxisCellType { 2 | AXIS_ROW_CELL = 'axisRowCell', 3 | AXIS_COL_CELL = 'axisColCell', 4 | AXIS_CORNER_CELL = 'axisCornerCell', 5 | } 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/pivot-chart/facet/corner-bbox.ts: -------------------------------------------------------------------------------- 1 | import { CornerBBox as OriginCornerBBox, floor } from '@antv/s2'; 2 | 3 | export class CornerBBox extends OriginCornerBBox { 4 | protected calculateOriginWidth(): void { 5 | const { rowsHierarchy, axisRowsHierarchy } = this.layoutResult; 6 | 7 | const rowAxisWidth = axisRowsHierarchy?.width ?? 0; 8 | 9 | this.originalWidth = floor( 10 | this.facet.getSeriesNumberWidth() + rowsHierarchy.width + rowAxisWidth, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/pivot-chart/facet/panel-bbox.ts: -------------------------------------------------------------------------------- 1 | import { PanelBBox as OriginPanelBBox } from '@antv/s2'; 2 | 3 | export class PanelBBox extends OriginPanelBBox { 4 | protected override getPanelHeight(): number { 5 | const scrollBarSize = this.spreadsheet.theme.scrollBar!.size; 6 | const { height: canvasHeight } = this.spreadsheet.options; 7 | 8 | const { axisColsHierarchy } = this.layoutResult; 9 | const colAxisHeight = axisColsHierarchy?.height ?? 0; 10 | 11 | const panelHeight = Math.max( 12 | 0, 13 | canvasHeight! - this.y - scrollBarSize! - colAxisHeight, 14 | ); 15 | 16 | return panelHeight; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/pivot-chart/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cell/axis-col-cell'; 2 | export * from './cell/axis-corner-cell'; 3 | export * from './cell/axis-row-cell'; 4 | export * from './cell/chart-data-cell'; 5 | export * from './cell/pivot-chart-data-cell'; 6 | export * from './facet/pivot-chart-facet'; 7 | export * from './header/axis-col'; 8 | export * from './header/axis-corner'; 9 | export * from './header/axis-row'; 10 | export * from './interface'; 11 | export * from './pivot-chart-sheet'; 12 | -------------------------------------------------------------------------------- /packages/s2-core/src/extends/pivot-chart/utils/theme.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SpreadSheet, 3 | getColCellTheme, 4 | getCornerCellTheme, 5 | getRowCellTheme, 6 | type S2Theme, 7 | type SimplePalette, 8 | } from '@antv/s2'; 9 | import { merge } from 'lodash'; 10 | import { AxisCellType } from '../cell/cell-type'; 11 | 12 | export const getCustomTheme = ( 13 | palette: SimplePalette, 14 | spreadsheet?: SpreadSheet, 15 | ): S2Theme => { 16 | return { 17 | [AxisCellType.AXIS_CORNER_CELL]: getCornerCellTheme(palette), 18 | [AxisCellType.AXIS_ROW_CELL]: getRowCellTheme(palette, spreadsheet), 19 | [AxisCellType.AXIS_COL_CELL]: merge(getColCellTheme(palette), { 20 | measureText: { 21 | textAlign: 'center', 22 | }, 23 | }), 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/s2-core/src/facet/header/index.ts: -------------------------------------------------------------------------------- 1 | export { ColHeader } from './col'; 2 | export { CornerHeader } from './corner'; 3 | export { Frame } from './frame'; 4 | export { RowHeader } from './row'; 5 | export { SeriesNumberHeader } from './series-number'; 6 | 7 | export * from './interface'; 8 | export * from './util'; 9 | -------------------------------------------------------------------------------- /packages/s2-core/src/facet/index.ts: -------------------------------------------------------------------------------- 1 | export { BaseFacet } from './base-facet'; 2 | export { FrozenFacet } from './frozen-facet'; 3 | export { PivotFacet } from './pivot-facet'; 4 | export { TableFacet } from './table-facet'; 5 | 6 | export * from './bbox/corner-bbox'; 7 | export * from './bbox/panel-bbox'; 8 | 9 | export * from './header'; 10 | export * from './layout'; 11 | export * from './utils'; 12 | -------------------------------------------------------------------------------- /packages/s2-core/src/facet/layout/index.ts: -------------------------------------------------------------------------------- 1 | export { buildGridHierarchy } from './build-gird-hierarchy'; 2 | export { buildTableHierarchy } from './build-table-hierarchy'; 3 | export { Hierarchy } from './hierarchy'; 4 | export { Node } from './node'; 5 | -------------------------------------------------------------------------------- /packages/s2-core/src/facet/layout/total-measure.ts: -------------------------------------------------------------------------------- 1 | export class TotalMeasure { 2 | public label: string; 3 | 4 | public constructor(label: string) { 5 | this.label = label; 6 | } 7 | 8 | static isTotalMeasureInstance(value: unknown): value is TotalMeasure { 9 | return value instanceof TotalMeasure; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-core/src/group/frozen-group.ts: -------------------------------------------------------------------------------- 1 | import { GridGroup } from './grid-group'; 2 | 3 | export class FrozenGroup extends GridGroup {} 4 | -------------------------------------------------------------------------------- /packages/s2-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { FederatedPointerEvent as GEvent } from '@antv/g'; 2 | export { buildTableHierarchy } from './facet/layout/build-table-hierarchy'; 3 | export { Hierarchy } from './facet/layout/hierarchy'; 4 | export { Node, type NodeProperties } from './facet/layout/node'; 5 | 6 | export * from './cell'; 7 | export * from './common'; 8 | export * from './data-set'; 9 | export * from './facet'; 10 | export * from './interaction'; 11 | export * from './shared'; 12 | export * from './sheet-type'; 13 | export * from './theme'; 14 | export * from './ui/scrollbar'; 15 | export * from './ui/tooltip'; 16 | export * from './utils'; 17 | -------------------------------------------------------------------------------- /packages/s2-core/src/interaction/base-interaction/click/index.ts: -------------------------------------------------------------------------------- 1 | export { CornerCellClick } from './corner-cell-click'; 2 | export { DataCellClick } from './data-cell-click'; 3 | export { HeaderCellLinkClick } from './header-cell-link-click'; 4 | export { MergedCellClick } from './merged-cell-click'; 5 | export { PreviewClick } from './preview-click'; 6 | export { RowColumnClick } from './row-column-click'; 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/interaction/base-interaction/index.ts: -------------------------------------------------------------------------------- 1 | export { BaseEvent, type BaseEventImplement } from '../base-event'; 2 | export * from './click'; 3 | export { HoverEvent } from './hover'; 4 | -------------------------------------------------------------------------------- /packages/s2-core/src/interaction/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base-event'; 2 | export * from './base-interaction'; 3 | export * from './brush-selection/base-brush-selection'; 4 | export * from './brush-selection/col-brush-selection'; 5 | export * from './brush-selection/data-cell-brush-selection'; 6 | export * from './brush-selection/row-brush-selection'; 7 | export * from './data-cell-multi-selection'; 8 | export * from './event-controller'; 9 | export * from './range-selection'; 10 | export * from './root'; 11 | export * from './row-column-resize'; 12 | export * from './selected-cell-move'; 13 | -------------------------------------------------------------------------------- /packages/s2-core/src/renderer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RendererFactory'; 2 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/classnames.ts: -------------------------------------------------------------------------------- 1 | import { S2_PREFIX_CLS } from '../../common/constant/classnames'; 2 | 3 | export const DRILL_DOWN_PRE_CLASS = `${S2_PREFIX_CLS}-drill-down`; 4 | 5 | export const STRATEGY_SHEET_TOOLTIP_PRE_CLASS = `${S2_PREFIX_CLS}-strategy-sheet-tooltip`; 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import type { LocaleType } from '../../../common/i18n'; 2 | import { EN_US } from './en_US'; 3 | import { RU } from './ru_RU'; 4 | import { ZH_CN } from './zh_CN'; 5 | 6 | export const Locale: LocaleType = { 7 | zh_CN: ZH_CN, 8 | en_US: EN_US, 9 | ru_RU: RU, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/index.ts: -------------------------------------------------------------------------------- 1 | export * from './classnames'; 2 | export * from './i18n'; 3 | export * from './option'; 4 | export * from './resize'; 5 | export * from './sort'; 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/option.ts: -------------------------------------------------------------------------------- 1 | import type { S2Options } from '../../common'; 2 | 3 | export const SHEET_COMPONENT_DEFAULT_OPTIONS: S2Options = { 4 | tooltip: { 5 | enable: true, 6 | autoAdjustBoundary: 'body', 7 | operation: { 8 | hiddenColumns: true, 9 | sort: true, 10 | }, 11 | }, 12 | showDefaultHeaderActionIcon: true, 13 | } as const; 14 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/resize.ts: -------------------------------------------------------------------------------- 1 | // 渲染延迟 (ms) 2 | export const RESIZE_RENDER_DELAY = 200; 3 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/constant/sort.ts: -------------------------------------------------------------------------------- 1 | import { i18n, S2_PREFIX_CLS } from '../../common'; 2 | 3 | export const getSortMethod = () => [ 4 | { 5 | name: i18n('升序'), 6 | value: 'ASC', 7 | }, 8 | { 9 | name: i18n('降序'), 10 | value: 'DESC', 11 | }, 12 | ]; 13 | 14 | export const getSortRuleOptions = () => [ 15 | { 16 | label: i18n('首字母'), 17 | value: 'sortMethod', 18 | }, 19 | { 20 | label: i18n('手动排序'), 21 | value: 'sortBy', 22 | }, 23 | { 24 | label: i18n('其他字段'), 25 | value: 'sortByMeasure', 26 | children: [], 27 | }, 28 | ]; 29 | 30 | export const ADVANCED_SORT_PRE_CLS = `${S2_PREFIX_CLS}-advanced-sort`; 31 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constant'; 2 | export * from './interface'; 3 | export * from './utils'; 4 | 5 | export * from './interface'; 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/styles/variables.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/variables.less'; 2 | 3 | @tooltip-operator-cls-prefix: ~'@{s2-cls-prefix}-tooltip-operator'; 4 | @tooltip-menu-item-text-color: rgba(0, 0, 0, 0.65); 5 | @tooltip-menu-item-height: 36px; 6 | @tooltip-menu-font-size: 12px; 7 | 8 | // 下钻 9 | @drill-down-cls-prefix: ~'@{s2-cls-prefix}-drill-down'; 10 | 11 | // 趋势表 tooltip 12 | @strategy-sheet-tooltip-cls-prefix: ~'@{s2-cls-prefix}-strategy-sheet-tooltip'; 13 | 14 | // 高级排序 15 | @advanced-sort-cls-prefix: ~'@{s2-cls-prefix}-advanced-sort'; 16 | 17 | // 指标切换 18 | @switcher-cls-prefix: ~'@{s2-cls-prefix}-switcher'; 19 | 20 | // 头部组件 21 | @header-cls-prefix: ~'@{s2-cls-prefix}-header'; 22 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/utils/classnames.ts: -------------------------------------------------------------------------------- 1 | import { STRATEGY_SHEET_TOOLTIP_PRE_CLASS } from '../constant'; 2 | 3 | /** 获取 tooltip css class name */ 4 | export const getStrategySheetTooltipClsName = (name?: string) => 5 | name 6 | ? `${STRATEGY_SHEET_TOOLTIP_PRE_CLASS}-${name}` 7 | : STRATEGY_SHEET_TOOLTIP_PRE_CLASS; 8 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './classnames'; 2 | export * from './drill-down'; 3 | export * from './options'; 4 | export * from './resize'; 5 | -------------------------------------------------------------------------------- /packages/s2-core/src/shared/utils/options.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_OPTIONS, type S2Options } from '../../common'; 2 | import { customMerge } from '../../utils'; 3 | import { SHEET_COMPONENT_DEFAULT_OPTIONS } from '../constant/option'; 4 | 5 | export const getBaseSheetComponentOptions = ( 6 | ...options: Partial[] 7 | ): Options => 8 | customMerge( 9 | DEFAULT_OPTIONS, 10 | SHEET_COMPONENT_DEFAULT_OPTIONS, 11 | ...options, 12 | ); 13 | -------------------------------------------------------------------------------- /packages/s2-core/src/sheet-type/index.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1270716220 2 | import type {} from '@antv/g-lite'; 3 | 4 | import { PivotSheet } from './pivot-sheet'; 5 | import { SpreadSheet } from './spread-sheet'; 6 | import { TableSheet } from './table-sheet'; 7 | 8 | export { PivotSheet, SpreadSheet, TableSheet }; 9 | -------------------------------------------------------------------------------- /packages/s2-core/src/styles/variables.less: -------------------------------------------------------------------------------- 1 | // s2 类名前缀 2 | @s2-cls-prefix: antv-s2; 3 | 4 | // css 变量前缀 5 | @css-var-prefix: ~'--@{s2-cls-prefix}'; 6 | 7 | // Tooltip 8 | @tooltip-cls-prefix: ~'@{s2-cls-prefix}-tooltip'; 9 | 10 | // 图标 11 | @icon-cls-prefix: ~'@{s2-cls-prefix}-icon'; 12 | -------------------------------------------------------------------------------- /packages/s2-core/src/theme/palette/dark.ts: -------------------------------------------------------------------------------- 1 | import type { Palette } from '../../common'; 2 | 3 | export const paletteDark: Palette = { 4 | brandColor: '#255dff', 5 | semanticColors: { 6 | red: '#FF4D4F', 7 | green: '#29A294', 8 | yellow: '#FAAD14', 9 | }, 10 | basicColors: [ 11 | '#ffffff', 12 | '#151a27', 13 | '#213f94', 14 | '#133aad', 15 | '#213f94', 16 | '#255dff', 17 | '#4b91ff', 18 | '#4b91ff', 19 | '#191919', 20 | '#1e2436', 21 | '#0647b1', 22 | '#7899ff', 23 | '#7899ff', 24 | '#f0f0f0', 25 | '#dcdcdc', 26 | ], 27 | basicColorRelations: [], 28 | }; 29 | -------------------------------------------------------------------------------- /packages/s2-core/src/ui/scrollbar/interface.ts: -------------------------------------------------------------------------------- 1 | import type { ScrollBarTheme } from '../../common/interface/theme'; 2 | 3 | export interface ScrollBarCfg { 4 | // 布局 横向(horizontal) | 纵向(vertical) 5 | readonly isHorizontal?: boolean; 6 | // 滑道长度 7 | readonly trackLen: number; 8 | // 滑块长度 9 | readonly thumbLen: number; 10 | // scrollBar 的位置 11 | readonly position: PointObject; 12 | // 滑块相对滑道的偏移量 13 | readonly thumbOffset?: number; 14 | // 滚动对象的长度 15 | readonly scrollTargetMaxOffset: number; 16 | 17 | // 滚动条样式 18 | readonly theme?: ScrollBarTheme | undefined; 19 | } 20 | 21 | export interface PointObject { 22 | x: number; 23 | y: number; 24 | } 25 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/cell/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cell'; 2 | export * from './data-cell'; 3 | export * from './header-cell'; 4 | export * from './merged-cell'; 5 | export * from './table-col-cell'; 6 | export * from './text-scrolling'; 7 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/common.ts: -------------------------------------------------------------------------------- 1 | export const safeJsonParse = (val: string): T | null => { 2 | try { 3 | return JSON.parse(val); 4 | } catch (err) { 5 | return null; 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/data-item-type-checker.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from 'lodash'; 2 | import type { DataItem, MultiData } from '../common/interface'; 3 | 4 | export const isMultiDataItem = (value: DataItem): value is MultiData => 5 | isObject(value) && 'values' in value; 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/export/copy/index.ts: -------------------------------------------------------------------------------- 1 | export type { SheetCopyConstructorParams } from '../../../common/interface/export'; 2 | export { 3 | asyncGetAllData, 4 | asyncGetAllHtmlData, 5 | asyncGetAllPlainData, 6 | getSelectedData, 7 | } from './core'; 8 | export { PivotDataCellCopy } from './pivot-data-cell-copy'; 9 | export { strategyCopy } from './strategy-copy'; 10 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/export/index.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | CopyableList, 3 | FormatOptions, 4 | } from '../../common/interface/export'; 5 | 6 | export { assembleMatrix, getMaxRowLen, getNodeFormatData } from './copy/common'; 7 | export { asyncGetAllPlainData } from './copy/core'; 8 | export { getHeaderList } from './method'; 9 | 10 | export * from './copy'; 11 | export * from './utils'; 12 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/get-all-children-node-height.ts: -------------------------------------------------------------------------------- 1 | import type { Node } from '../facet/layout/node'; 2 | 3 | /** 4 | * 获取当前node所有children的总高度 5 | * @param node 6 | */ 7 | export const getAllChildrenNodeHeight = (node: Node) => { 8 | let nodeAllCellHeight = 0; 9 | const nodes = node.children; 10 | 11 | nodes?.forEach((item) => { 12 | nodeAllCellHeight += item.height || 0; 13 | }); 14 | 15 | return nodeAllCellHeight; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/get-classnames.ts: -------------------------------------------------------------------------------- 1 | import { S2_PREFIX_CLS } from '../common/constant/classnames'; 2 | 3 | export const getClassNameWithPrefix = (...classNames: string[]) => 4 | `${S2_PREFIX_CLS}-${classNames.join('-')}`; 5 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/inject-css-text.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 注入 css 3 | * 反复注入到同一个 block 4 | */ 5 | export const injectCssText = ( 6 | elementId: string, 7 | cssText = '', 8 | ): HTMLStyleElement => { 9 | let element = document.getElementById(elementId) as HTMLStyleElement; 10 | 11 | if (!element) { 12 | element = document.createElement('style'); 13 | element.id = elementId; 14 | document.head.appendChild(element); 15 | } 16 | 17 | element.innerHTML = cssText; 18 | 19 | return element; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/interaction/common.ts: -------------------------------------------------------------------------------- 1 | import type { DisplayObject } from '@antv/g'; 2 | import type { CellAppendInfo } from '../../common'; 3 | 4 | export const getAppendInfo = = CellAppendInfo>( 5 | eventTarget: DisplayObject, 6 | ) => { 7 | if (!eventTarget || !('appendInfo' in eventTarget)) { 8 | return {} as T; 9 | } 10 | 11 | return eventTarget.appendInfo as T; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/interaction/index.ts: -------------------------------------------------------------------------------- 1 | export * from './formatter'; 2 | export * from './hover-event'; 3 | export * from './link-field'; 4 | export * from './merge-cell'; 5 | export * from './resize'; 6 | export * from './scroll'; 7 | export * from './select-event'; 8 | export * from './state-controller'; 9 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/interaction/link-field.ts: -------------------------------------------------------------------------------- 1 | import { isFunction } from 'lodash'; 2 | import type { ViewMeta } from '../../common/interface/basic'; 3 | import type { Node } from '../../facet/layout/node'; 4 | 5 | export const isLinkFieldNode = ( 6 | linkFields: string[] | ((meta: Node | ViewMeta) => boolean), 7 | meta: Node | ViewMeta, 8 | ): boolean => { 9 | if (isFunction(linkFields)) { 10 | return linkFields(meta); 11 | } 12 | 13 | return linkFields.some( 14 | (field) => 15 | field === meta.field || field === meta.id || field === meta.valueField, 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/is-mobile.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 判断是否是移动端。 3 | * 兼容场景:pc端但是使用mobile配置。 4 | */ 5 | import { DeviceType } from '../common'; 6 | 7 | export function isMobile(device?: string) { 8 | if (device === DeviceType.MOBILE) { 9 | return true; 10 | } 11 | 12 | return /(?:iPhone|iPad|SymbianOS|Windows Phone|iPod|iOS|Android|Mobile|Phone|Tablet)/i.test( 13 | navigator.userAgent, 14 | ); 15 | } 16 | 17 | export function isIPhoneX() { 18 | // eslint-disable-next-line no-restricted-globals 19 | return ( 20 | /iPhone/gi.test(navigator.userAgent) && 21 | window.screen.height === 812 && 22 | window.screen.width === 375 23 | ); 24 | } 25 | 26 | export function isWindows() { 27 | return /windows/i.test(navigator.userAgent); 28 | } 29 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/layout/get-dims-condition-by-node.ts: -------------------------------------------------------------------------------- 1 | import { EMPTY_FIELD_VALUE } from '../../common/constant'; 2 | import type { Query } from '../../data-set'; 3 | import type { Node } from '../../facet/layout/node'; 4 | 5 | export function getDimsCondition(parent: Node, force?: boolean) { 6 | const cond: Query = {}; 7 | let p: Node | undefined = parent; 8 | 9 | while (p && p.field) { 10 | /** 11 | * 当为表格布局时,小计行的内容是“小计”不需要作为筛选条件 12 | * 当为树状布局时,force可以强行指定小计行,即父类目作为筛选条件 13 | */ 14 | if ((!p.isTotalRoot || force) && p.value !== EMPTY_FIELD_VALUE) { 15 | cond[p.field] = p.value; 16 | } 17 | 18 | p = p.parent; 19 | } 20 | 21 | return cond; 22 | } 23 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from './frozen'; 2 | export { 3 | generateId, 4 | generateNillString, 5 | resolveId, 6 | resolveNillString, 7 | } from './generate-id'; 8 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/math.ts: -------------------------------------------------------------------------------- 1 | import { floor as innerFloor, isNumber } from 'lodash'; 2 | 3 | export function floor(value: number, precision = 2) { 4 | return innerFloor(value, precision); 5 | } 6 | 7 | /** 8 | * 向下取整, 避免向上取整后宽度超过实际宽度, 出现滚动条 9 | */ 10 | export function round(value: number) { 11 | if (!isNumber(value)) { 12 | return value; 13 | } 14 | 15 | return floor(value, 0); 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/normalize.ts: -------------------------------------------------------------------------------- 1 | import type { TextAlign, TextBaseline } from '../common/interface'; 2 | 3 | export enum NormalizedAlign { 4 | Start, 5 | Center, 6 | End, 7 | } 8 | 9 | export const normalizeTextAlign = (align: TextAlign | TextBaseline) => { 10 | if (['left', 'top'].includes(align)) { 11 | return NormalizedAlign.Start; 12 | } 13 | 14 | if (['center', 'middle'].includes(align)) { 15 | return NormalizedAlign.Center; 16 | } 17 | 18 | return NormalizedAlign.End; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/schedule.ts: -------------------------------------------------------------------------------- 1 | export function waitForCellMounted(cb: () => void) { 2 | Promise.resolve().then(() => { 3 | cb(); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/theme.ts: -------------------------------------------------------------------------------- 1 | import { PALETTE_MAP } from '../common/constant'; 2 | import type { Palette, ThemeName } from '../common/interface/theme'; 3 | 4 | /** 5 | * 获取当前的主题色板 6 | */ 7 | export const getPalette = (themeName?: ThemeName): Palette => { 8 | return PALETTE_MAP[themeName!] || PALETTE_MAP['default']; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/s2-core/src/utils/veen-arr.ts: -------------------------------------------------------------------------------- 1 | type ArrayRangeType = [number, number]; 2 | 3 | /* 4 | * 数组范围交集,如果没有交集,则返回空数组 5 | * 例如:[1,5], [2,6] -> [2,5] 6 | */ 7 | export function vennArr(arr1: ArrayRangeType, arr2: ArrayRangeType) { 8 | const min = Math.max(arr1[0], arr2[0]); 9 | const max = Math.min(arr1[1], arr2[1]); 10 | 11 | return min <= max ? [min, max] : []; 12 | } 13 | -------------------------------------------------------------------------------- /packages/s2-core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*", "./typings.d.ts", "../../global.d.ts"], 4 | "compilerOptions": { 5 | "target": "es2015", 6 | "paths": { 7 | "@antv/s2": ["s2-core/src/index.ts"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/s2-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "@antv/s2": ["s2-core/src/index.ts"], 6 | "@antv/s2/*":["s2-core/src/*"], 7 | "@/*": ["s2-core/src/*"], 8 | "tests/*": ["s2-core/__tests__/*"] 9 | }, 10 | }, 11 | "exclude": ["node_modules", "coverage", "esm", "lib", "dist", "temp"], 12 | "include": ["src", "../../global.d.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/s2-react-components/.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../.releaserc.base.js'); 2 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/__mocks__/svg.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-default-export 2 | export default 'SvgrURL'; 3 | 4 | export const ReactComponent = 'div'; 5 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | ['time', 'info', 'warn'].forEach((type) => { 2 | jest.spyOn(console, type).mockImplementation(() => {}); 3 | }); 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/unit/components/advanced-sort/custom-sort-spec.tsx: -------------------------------------------------------------------------------- 1 | import { CustomSort } from '@/components'; 2 | import { render } from '@testing-library/react'; 3 | import React from 'react'; 4 | 5 | describe('CustomSort Component Tests', () => { 6 | test('should render component', () => { 7 | const { asFragment } = render( 8 | , 20 | ); 21 | 22 | expect(asFragment()).toMatchSnapshot(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/unit/components/common/tooltip-wrapper/__snapshots__/index-spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Tooltip Wrapper Component Tests should render correctly 1`] = ` 4 | 5 | 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/unit/components/switcher/content/index-spec.tsx: -------------------------------------------------------------------------------- 1 | import { SwitcherContent } from '@/components/switcher/content'; 2 | import { render } from '@testing-library/react'; 3 | import React from 'react'; 4 | 5 | describe('switcher component content test', () => { 6 | test('should render switcher content', () => { 7 | const { asFragment } = render(); 8 | 9 | expect(asFragment()).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/s2-react-components/__tests__/unit/components/theme-panel/color-box/__snapshots__/index-spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Theme Panel Color Box Component Tests should render correctly 1`] = ` 4 | 5 |
8 | 9 | `; 10 | -------------------------------------------------------------------------------- /packages/s2-react-components/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../jest.config.base'); 2 | -------------------------------------------------------------------------------- /packages/s2-react-components/playground/README.md: -------------------------------------------------------------------------------- 1 | ### 开发与调试 2 | 3 | 根目录运行 `pnpm react-components:playground` 来运行 `S2`, 可用于调试 `@antv/s2-react-components`, 提供了一些常用的图表场景和配置。 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | S2 React Components Playground 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/s2-react-components/playground/index.less: -------------------------------------------------------------------------------- 1 | .playground { 2 | padding: 40px; 3 | background-color: var(--antv-s2-background, #fff); 4 | color: var(--antv-s2-font, #fff); 5 | 6 | .config { 7 | display: flex; 8 | align-items: flex-start; 9 | margin-bottom: 20px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-react-components/playground/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-underscore-dangle */ 2 | /* eslint-disable no-restricted-imports */ 3 | /* eslint-disable no-console */ 4 | import type { SpreadSheet } from '@antv/s2'; 5 | import _ from 'lodash'; 6 | 7 | export const onSheetMounted = (s2: SpreadSheet) => { 8 | console.log('onSheetMounted: ', s2); 9 | // @ts-ignore 10 | window.s2 = s2; 11 | // @ts-ignore 12 | window.g_instances = [s2.container]; 13 | // @ts-ignore 14 | window.__g_instances__ = [s2.container]; 15 | window._ = _; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/common/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export * from './interface'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/common/interface/components.ts: -------------------------------------------------------------------------------- 1 | export interface BaseComponentProps { 2 | /** 3 | * 标题 4 | * @default "文字对齐" 5 | */ 6 | title?: React.ReactNode; 7 | 8 | /** 9 | * 默认是否折叠 10 | * @default false 11 | */ 12 | defaultCollapsed?: boolean; 13 | 14 | /** 15 | * 默认配置 16 | */ 17 | defaultOptions?: Partial; 18 | 19 | /** 20 | * 子节点 21 | */ 22 | children?: React.ReactNode; 23 | } 24 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/common/interface/icon.ts: -------------------------------------------------------------------------------- 1 | export interface RadioIconProps { 2 | active: boolean; 3 | } 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/common/interface/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export * from './icon'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/advanced-sort/index.ts: -------------------------------------------------------------------------------- 1 | export { AdvancedSort } from './advanced-sort'; 2 | export { CustomSort } from './custom-sort'; 3 | 4 | export type { 5 | AdvancedSortBaseProps, 6 | AdvancedSortProps, 7 | CustomSortProps, 8 | Dimension, 9 | RuleItem, 10 | RuleOption, 11 | RuleValue, 12 | } from './interface'; 13 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/icons/col-icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function ColIcon(props: React.SVGProps) { 4 | return ( 5 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/icons/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{icon-cls-prefix} { 4 | color: inherit; 5 | } 6 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/icons/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | 3 | export * from './calendar-icon'; 4 | export * from './col-icon'; 5 | export * from './location-icon'; 6 | export * from './row-icon'; 7 | export * from './text-icon'; 8 | export * from './value-icon'; 9 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/icons/row-icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function RowIcon(props: React.SVGProps) { 4 | return ( 5 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/icons/value-icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function ValueIcon(props: React.SVGProps) { 4 | return ( 5 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './icons'; 2 | export * from './radio-group'; 3 | export * from './reset-button'; 4 | export * from './reset-group'; 5 | export * from './tooltip-wrapper'; 6 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/radio-group/index.ts: -------------------------------------------------------------------------------- 1 | export type { RadioGroupProps } from './interface'; 2 | export { RadioGroup } from './radio-group'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/radio-group/interface.ts: -------------------------------------------------------------------------------- 1 | import { type RadioGroupProps as AntdRadioGroupProps } from 'antd'; 2 | 3 | export interface RadioGroupProps extends AntdRadioGroupProps { 4 | label: React.ReactNode; 5 | onlyIcon?: boolean; 6 | extra?: React.ReactNode; 7 | } 8 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-button/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-reset-btn { 4 | color: #3572f9; 5 | cursor: pointer; 6 | padding-left: 8px; 7 | 8 | &-text { 9 | margin-left: 4px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-button/index.ts: -------------------------------------------------------------------------------- 1 | export type { ResetButtonProps } from './interface'; 2 | export { ResetButton } from './reset-button'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-button/interface.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export interface ResetButtonProps { 4 | title?: React.ReactNode; 5 | onClick?: (e: React.MouseEvent) => void; 6 | } 7 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-group/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-reset-group { 4 | width: 340px; 5 | } 6 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-group/index.ts: -------------------------------------------------------------------------------- 1 | export type { ResetGroupProps } from './interface'; 2 | export { ResetGroup } from './reset-group'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/reset-group/interface.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | 3 | export interface ResetGroupProps { 4 | style?: React.CSSProperties; 5 | className?: string; 6 | title?: React.ReactNode; 7 | defaultCollapsed?: boolean; 8 | onResetClick?: () => void; 9 | children?: React.ReactNode; 10 | } 11 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/tooltip-wrapper/index.ts: -------------------------------------------------------------------------------- 1 | export type { TooltipWrapperProps } from './interface'; 2 | export { TooltipWrapper } from './tooltip-wrapper'; 3 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/common/tooltip-wrapper/interface.ts: -------------------------------------------------------------------------------- 1 | import type { TooltipPropsWithTitle } from 'antd/es/tooltip'; 2 | 3 | export interface TooltipWrapperProps extends TooltipPropsWithTitle {} 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/drill-down/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/shared/styles/drill-down.less'; 2 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/drill-down/index.ts: -------------------------------------------------------------------------------- 1 | export { DrillDown } from './drill-down'; 2 | 3 | export type { DrillDownDataSet, DrillDownProps } from './interface'; 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/drill-down/interface.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | BaseDrillDownComponentProps, 3 | BaseDrillDownDataSet, 4 | } from '@antv/s2'; 5 | import type { MenuProps } from 'antd'; 6 | 7 | export interface DrillDownDataSet extends BaseDrillDownDataSet { 8 | icon?: React.ReactNode; 9 | } 10 | 11 | export interface DrillDownProps 12 | extends BaseDrillDownComponentProps { 13 | extra?: React.ReactNode; 14 | renderMenu?: (props: MenuProps) => React.ReactNode; 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/export/index.ts: -------------------------------------------------------------------------------- 1 | export { Export } from './export'; 2 | export { StrategyExport } from './strategy-export'; 3 | 4 | export type { ExportBaseProps, ExportProps } from './interface'; 5 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/export/strategy-export.tsx: -------------------------------------------------------------------------------- 1 | import { strategyCopy } from '@antv/s2'; 2 | import React from 'react'; 3 | import { Export } from './export'; 4 | import type { ExportProps } from './interface'; 5 | 6 | export const StrategyExport: React.FC = React.memo((props) => { 7 | return ; 8 | }); 9 | 10 | StrategyExport.displayName = 'StrategyExport'; 11 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/frozen-panel/frozen-input-number/interface.ts: -------------------------------------------------------------------------------- 1 | import type { InputNumberProps } from 'antd'; 2 | 3 | export interface FrozenInputNumberProps 4 | extends Omit { 5 | value: number | null; 6 | onChange?: (value: number) => void; 7 | } 8 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/frozen-panel/index.ts: -------------------------------------------------------------------------------- 1 | export { FrozenInputNumber } from './frozen-input-number'; 2 | export { FrozenPanel } from './frozen-panel'; 3 | 4 | export type { FrozenPanelOptions, FrozenPanelProps } from './interface'; 5 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | 3 | export * from './advanced-sort'; 4 | export * from './drill-down'; 5 | export * from './export'; 6 | export * from './switcher'; 7 | 8 | export * from './frozen-panel'; 9 | export * from './text-align-panel'; 10 | export * from './theme-panel'; 11 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/switcher/constant.ts: -------------------------------------------------------------------------------- 1 | export const SWITCHER_PREFIX_CLS = 'switcher'; 2 | 3 | export enum FieldType { 4 | Rows = 'rows', 5 | Cols = 'columns', 6 | Values = 'values', 7 | } 8 | 9 | export enum DroppableType { 10 | Dimensions = 'dimensions', 11 | Measures = 'measures', 12 | Rows = 'rows', 13 | Cols = 'cols', 14 | } 15 | 16 | export const SWITCHER_FIELDS = [ 17 | FieldType.Rows, 18 | FieldType.Cols, 19 | FieldType.Values, 20 | ]; 21 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/switcher/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/shared/styles/variables.less'; 2 | 3 | .@{switcher-cls-prefix}-switcher-overlay { 4 | .ant-popover-inner, 5 | .ant-popover-arrow-content::before { 6 | background: var(~'@{css-var-prefix}-container-background', #fff); 7 | } 8 | } 9 | 10 | .@{switcher-cls-prefix}-entry-button { 11 | &.ant-btn { 12 | display: inline-flex; 13 | align-items: center; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/switcher/index.ts: -------------------------------------------------------------------------------- 1 | export { Switcher } from './switcher'; 2 | 3 | export type { 4 | SwitcherField, 5 | SwitcherFields, 6 | SwitcherItem, 7 | SwitcherProps, 8 | } from './interface'; 9 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/text-align-panel/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-theme-panel-custom-color { 4 | display: flex; 5 | align-items: center; 6 | padding-top: 10px; 7 | 8 | &::before { 9 | position: relative; 10 | display: block; 11 | flex: 1; 12 | content: ''; 13 | } 14 | 15 | > div { 16 | display: flex; 17 | flex: 2; 18 | align-items: center; 19 | } 20 | 21 | &-title { 22 | margin-right: 20px; 23 | color: rgba(0, 0, 0, 0.45); 24 | font-size: 12px; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/text-align-panel/index.ts: -------------------------------------------------------------------------------- 1 | export { TextAlignPanel } from './text-align-panel'; 2 | 3 | export type { TextAlignPanelOptions, TextAlignPanelProps } from './interface'; 4 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/text-align-panel/interface.ts: -------------------------------------------------------------------------------- 1 | import type { S2Theme, TextAlign } from '@antv/s2'; 2 | import type { BaseComponentProps } from '../../common/interface/components'; 3 | 4 | export interface TextAlignPanelOptions { 5 | rowCellTextAlign?: TextAlign; 6 | colCellTextAlign?: TextAlign; 7 | dataCellTextAlign?: TextAlign; 8 | } 9 | 10 | export interface TextAlignPanelProps 11 | extends BaseComponentProps { 12 | /** 13 | * 选择 14 | */ 15 | onChange?: (options: TextAlignPanelOptions, theme: S2Theme) => void; 16 | 17 | /** 18 | * 重置 19 | */ 20 | onReset?: ( 21 | options: TextAlignPanelOptions, 22 | prevOptions: TextAlignPanelOptions, 23 | theme: S2Theme, 24 | ) => void; 25 | } 26 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/color-box/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-color-box { 4 | display: inline-flex; 5 | box-sizing: border-box; 6 | width: 20px; 7 | height: 20px; 8 | border: 1px solid rgba(0, 0, 0, 0.06); 9 | border-radius: 2px; 10 | cursor: pointer; 11 | transition: all 0.3s; 12 | 13 | &:hover { 14 | transform: scale(1.1); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/color-box/index.tsx: -------------------------------------------------------------------------------- 1 | import { S2_PREFIX_CLS } from '@antv/s2'; 2 | import cls from 'classnames'; 3 | import React from 'react'; 4 | import './index.less'; 5 | import type { ColorBoxProps } from './interface'; 6 | 7 | const PRE_CLASS = `${S2_PREFIX_CLS}-color-box`; 8 | 9 | export const ColorBox: React.FC = React.memo((props) => { 10 | const { color, className, onClick, ...restProps } = props; 11 | 12 | return ( 13 |
21 | ); 22 | }); 23 | 24 | ColorBox.displayName = 'ColorBox'; 25 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/color-box/interface.ts: -------------------------------------------------------------------------------- 1 | export interface ColorBoxProps { 2 | className?: string; 3 | /** 色值 */ 4 | color?: string; 5 | onClick?: () => void; 6 | } 7 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/color-picker-panel/interface.ts: -------------------------------------------------------------------------------- 1 | export interface ColorPickerPanelProps { 2 | /** 3 | * 自定义主色系颜色 4 | */ 5 | primaryColor?: string; 6 | 7 | /** 8 | * 历史自定义颜色记录上限 9 | */ 10 | maxHistoryColorCount?: number; 11 | 12 | /** 13 | * 颜色选择回调 14 | */ 15 | onChange?: (color: string) => void; 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-theme-panel-custom-color { 4 | display: flex; 5 | align-items: center; 6 | padding-top: 10px; 7 | 8 | &::before { 9 | position: relative; 10 | display: block; 11 | flex: 1; 12 | content: ''; 13 | } 14 | 15 | > div { 16 | display: flex; 17 | flex: 2; 18 | align-items: center; 19 | } 20 | 21 | &-title { 22 | margin-right: 20px; 23 | color: rgba(0, 0, 0, 0.45); 24 | font-size: 12px; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/components/theme-panel/index.ts: -------------------------------------------------------------------------------- 1 | export { ColorBox } from './color-box'; 2 | export { ColorPickerPanel } from './color-picker-panel'; 3 | export { ThemePanel } from './theme-panel'; 4 | 5 | export type { ThemePanelOptions, ThemePanelProps } from './interface'; 6 | -------------------------------------------------------------------------------- /packages/s2-react-components/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | -------------------------------------------------------------------------------- /packages/s2-react-components/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*", "./typings.d.ts", "../../global.d.ts"], 4 | "compilerOptions": { 5 | "target": "es2015", 6 | "paths": { 7 | "@antv/s2": ["s2-react-components/node_modules/@antv/s2/esm/index.d.ts"], 8 | "@antv/s2/*": ["s2-react-components/node_modules/@antv/s2/esm/*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-react-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "jsx": "react", 6 | "paths": { 7 | "@antv/s2":["s2-core/src/index.ts"], 8 | "@antv/s2/*":["s2-core/src/*"], 9 | "@antv/s2/esm/shared": ["s2-core/src/shared/index.ts"], 10 | "@antv/s2/esm/shared/*": ["s2-core/src/shared/*"], 11 | "@antv/s2-react":["s2-react/src/index.ts"], 12 | "@/*":["s2-react-components/src/*"], 13 | "tests/*":["s2-react-components/__tests__/*"] 14 | } 15 | }, 16 | "exclude": ["node_modules", "coverage", "esm", "lib", "dist", "temp"], 17 | "include": ["src", "./typings.d.ts", "playground", "../../global.d.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react-components/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.svg' { 4 | import * as React from 'react'; 5 | 6 | export const ReactComponent: React.FunctionComponent< 7 | React.SVGProps & { title?: string } 8 | >; 9 | } 10 | -------------------------------------------------------------------------------- /packages/s2-react/.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../.releaserc.base.js'); 2 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/README.md: -------------------------------------------------------------------------------- 1 | ### 调试单测 2 | 3 | 如果你想查看单测的运行结果,除了常规的 `pnpm core:test` 和 `pnpm react:test` 来运行测试之外,还可以 `可视化的调试单测(基于 jest-electron)`, 可以更快的发现单测的问题。 4 | 5 | 1. 选择单测 6 | 7 | 命令行运行 `pnpm core:start` 或者 `pnpm react:start` 8 | 9 | preview 10 | 11 | 2. 查看结果 12 | 13 | 因为本质上就是一个浏览器,如果单测结果不符合预期,可以正常打断点进行调试,快速分析原因。 14 | 15 | preview 16 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/__mocks__/svg.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-default-export 2 | export default 'SvgrURL'; 3 | 4 | export const ReactComponent = 'div'; 5 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/data/data-custom-trees.ts: -------------------------------------------------------------------------------- 1 | export const customTreeData = [ 2 | { 3 | 'measure-a': 13, 4 | 'measure-b': 2, 5 | 'measure-c': 3, 6 | 'measure-d': 4, 7 | 'measure-e': 5, 8 | 'measure-f': 6, 9 | type: '家具', 10 | sub_type: '桌子', 11 | }, 12 | { 13 | 'measure-a': 11, 14 | 'measure-b': 22, 15 | 'measure-c': 32, 16 | 'measure-d': 43, 17 | 'measure-e': 45, 18 | 'measure-f': 65, 19 | type: '家具', 20 | sub_type: '椅子', 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/data/data-issue-285/data-col-header.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["month"], 4 | "columns": ["type"], 5 | "values": ["price"] 6 | }, 7 | "meta": [ 8 | { 9 | "field": "month", 10 | "name": "订单日期" 11 | }, 12 | { 13 | "field": "type", 14 | "name": "种类" 15 | }, 16 | { 17 | "field": "price", 18 | "name": "价格" 19 | } 20 | ], 21 | "data": [ 22 | { 23 | "month": "2021-09", 24 | "price": 12, 25 | "type": "纸" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/data/data-issue-285/data-without-col-header.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["month"], 4 | "columns": [], 5 | "values": ["price", "number"] 6 | }, 7 | "meta": [ 8 | { 9 | "field": "month", 10 | "name": "订单日期" 11 | }, 12 | { 13 | "field": "number", 14 | "name": "数量" 15 | }, 16 | { 17 | "field": "price", 18 | "name": "价格" 19 | } 20 | ], 21 | "data": [ 22 | { 23 | "month": "2021-09", 24 | "price": 12 25 | }, 26 | { 27 | "month": "2021-09", 28 | "number": 2 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/data/simple-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields": { 3 | "rows": ["province", "city"], 4 | "columns": ["type"], 5 | "values": ["price", "cost"], 6 | "valueInCols": true 7 | }, 8 | "data": [ 9 | { 10 | "province": "浙江", 11 | "city": "义乌", 12 | "type": "笔", 13 | "cost": "2" 14 | }, 15 | { 16 | "province": "浙江", 17 | "city": "义乌", 18 | "type": "笔", 19 | "price": "8" 20 | }, 21 | { 22 | "province": "浙江", 23 | "city": "杭州", 24 | "type": "笔", 25 | "price": "", 26 | "cost": 10 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/s2-react/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | ['time', 'info', 'warn'].forEach((type) => { 4 | jest.spyOn(console, type).mockImplementation(() => {}); 5 | }); 6 | 7 | const originalErrorLog = console.error; 8 | 9 | jest.spyOn(console, 'error').mockImplementation((msg) => { 10 | // act 错误堆栈太长了, CI 上面影响观看,简化一下 11 | if (msg.includes('act(...)')) { 12 | return originalErrorLog( 13 | '[@antv/s2-react setup] act error, see: https://reactjs.org/docs/test-utils.html#act', 14 | ); 15 | } 16 | 17 | originalErrorLog(msg); 18 | }); 19 | 20 | // https://react.dev/blog/2022/03/08/react-18-upgrade-guide#configuring-your-testing-environment 21 | // eslint-disable-next-line no-undef 22 | globalThis.IS_REACT_ACT_ENVIRONMENT = true; 23 | -------------------------------------------------------------------------------- /packages/s2-react/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../jest.config.base'); 2 | -------------------------------------------------------------------------------- /packages/s2-react/playground/README.md: -------------------------------------------------------------------------------- 1 | ### 开发与调试 2 | 3 | 根目录运行 `pnpm react:playground` 来运行 `S2`, 可用于调试 `@antv/s2` 和 `@antv/s2-react`, 提供了一些常用的图表场景和配置。 4 | 5 | ![playground](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*6t8RRbg5x_kAAAAAAAAAAAAADmJ7AQ/original) 6 | 7 | S2 基于 `AntV/G` 渲染引擎绘制,如果想像 DOM 一样调试的话,可以安装 [G 开发者工具](https://g.antv.antgroup.com/api/devtools/g-devtools) 8 | 9 | 1. 访问 `chrome://extensions/` 安装后 10 | 11 | ![extensions](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*RTYXTpb3WuIAAAAAAAAAAAAADmJ7AQ/original) 12 | 13 | 2. 开始调试 14 | 15 | ![dev-tool](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*sP9eQaWxDpcAAAAAAAAAAAAADmJ7AQ/original) 16 | -------------------------------------------------------------------------------- /packages/s2-react/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | S2 React Playground 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/s2-react/playground/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-underscore-dangle */ 2 | /* eslint-disable no-restricted-imports */ 3 | /* eslint-disable no-console */ 4 | import type { SpreadSheet } from '@antv/s2'; 5 | import _ from 'lodash'; 6 | 7 | export const onSheetMounted = (s2: SpreadSheet) => { 8 | console.log('onSheetMounted: ', s2); 9 | // @ts-ignore 10 | window.s2 = s2; 11 | // @ts-ignore 12 | window.g_instances = [s2.container]; 13 | // @ts-ignore 14 | window.__g_instances__ = [s2.container]; 15 | window._ = _; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/s2-react/src/common/constant/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/common/constant/options.ts: -------------------------------------------------------------------------------- 1 | import type { SpreadSheet } from '@antv/s2'; 2 | import type { SheetComponentOptions } from '../../components'; 3 | import { CustomTooltip } from '../../components/tooltip/custom-tooltip'; 4 | 5 | export const RENDER_TOOLTIP_OPTIONS: SheetComponentOptions = { 6 | tooltip: { 7 | render: (spreadsheet: SpreadSheet) => new CustomTooltip(spreadsheet), 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/s2-react/src/common/icons/html-icon.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-html-icon { 4 | display: inline-block; 5 | 6 | svg { 7 | width: 12px; 8 | height: 12px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/s2-react/src/common/icons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './html-icon'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constant'; 2 | export * from './icons'; 3 | export * from './react-element'; 4 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/base-sheet/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix} { 4 | &-wrapper { 5 | padding: 0; 6 | margin: 0; 7 | display: flex; 8 | flex-direction: column; 9 | height: 100%; 10 | } 11 | 12 | &-container { 13 | overflow: auto; 14 | flex: 1 1 auto; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/editable-sheet/custom-cell/index.ts: -------------------------------------------------------------------------------- 1 | export { EditCell } from './edit-cell'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-mask.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-drag-copy-mask { 4 | position: absolute; 5 | top: -4px; 6 | left: -4px; 7 | z-index: 9; 8 | background-color: rgba(#c2d5fe, 0.5); 9 | cursor: crosshair; 10 | } 11 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/editable-sheet/drag-copy/drag-copy-point.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/styles/variables.less'; 2 | 3 | .@{s2-cls-prefix}-drag-copy-point { 4 | position: absolute; 5 | z-index: 9; 6 | width: 8px; 7 | height: 8px; 8 | background-color: #c2d5fe; 9 | cursor: crosshair; 10 | user-select: none; 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/editable-sheet/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BaseSheet } from '../base-sheet'; 3 | import type { SheetComponentProps } from '../interface'; 4 | import { EditCell } from './custom-cell'; 5 | import { DragCopyPoint } from './drag-copy'; 6 | 7 | export const EditableSheet: React.FC = React.memo( 8 | (props) => { 9 | return ( 10 | 11 | 15 | 16 | 17 | ); 18 | }, 19 | ); 20 | 21 | EditableSheet.displayName = 'EditableSheet'; 22 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/grid-analysis-sheet/custom-cell/data-cell.ts: -------------------------------------------------------------------------------- 1 | import { DataCell, drawCustomContent } from '@antv/s2'; 2 | 3 | /** 4 | * Cell for panelGroup area 5 | * ------------------------------------- 6 | * | label | 7 | * | measureLabel | measure | measure | 8 | * | measureLabel | measure | measure | 9 | * -------------------------------------- 10 | */ 11 | export class GridAnalysisSheetDataCell extends DataCell { 12 | public drawTextShape() { 13 | if (this.isMultiData()) { 14 | return drawCustomContent(this); 15 | } 16 | 17 | super.drawTextShape(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/grid-analysis-sheet/custom-cell/index.ts: -------------------------------------------------------------------------------- 1 | export { GridAnalysisSheetDataCell } from './data-cell'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/pivot-chart-sheet/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BaseSheet } from '../base-sheet'; 3 | import type { SheetComponentProps } from '../interface'; 4 | 5 | export const PivotChartSheet: React.FC = React.memo( 6 | (props) => { 7 | return ; 8 | }, 9 | ); 10 | 11 | PivotChartSheet.displayName = 'PivotChartSheet'; 12 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/strategy-sheet/custom-cell/data-cell.ts: -------------------------------------------------------------------------------- 1 | import { DataCell, drawCustomContent } from '@antv/s2'; 2 | 3 | /** 4 | * Cell for panelGroup area 5 | * ------------------------------------- 6 | * | measure1 measure2 measure3 | 7 | * | measure1 measure2 measure3 | 8 | * -------------------------------------- 9 | */ 10 | export class StrategySheetDataCell extends DataCell { 11 | public drawTextShape() { 12 | if (this.isMultiData()) { 13 | return drawCustomContent(this); 14 | } 15 | 16 | super.drawTextShape(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/strategy-sheet/custom-cell/index.ts: -------------------------------------------------------------------------------- 1 | export { StrategySheetColCell } from './col-cell'; 2 | export { StrategySheetDataCell } from './data-cell'; 3 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/strategy-sheet/custom-tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export * from './col-cell-tooltip'; 2 | export * from './data-cell-tooltip'; 3 | export * from './row-cell-tooltip'; 4 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/sheets/table-sheet/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BaseSheet } from '../base-sheet'; 3 | import type { SheetComponentProps } from '../interface'; 4 | 5 | export const TableSheet: React.FC = React.memo((props) => { 6 | return ; 7 | }); 8 | 9 | TableSheet.displayName = 'TableSheet'; 10 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/components/description.tsx: -------------------------------------------------------------------------------- 1 | import { i18n, TOOLTIP_PREFIX_CLS } from '@antv/s2'; 2 | import React from 'react'; 3 | 4 | interface TooltipDescriptionProps { 5 | description: React.ReactNode; 6 | } 7 | 8 | export const TooltipDescription: React.FC = React.memo( 9 | ({ description }) => ( 10 | <> 11 | {description && ( 12 |
13 | {i18n('说明')} 14 | {description} 15 |
16 | )} 17 | 18 | ), 19 | ); 20 | 21 | TooltipDescription.displayName = 'TooltipDescription'; 22 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/components/head-info.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | TOOLTIP_PREFIX_CLS, 3 | i18n, 4 | type TooltipDetailListItem, 5 | type TooltipHeadInfo, 6 | } from '@antv/s2'; 7 | import React from 'react'; 8 | 9 | export const TooltipHead: React.FC = React.memo((props) => { 10 | const { rows = [], cols = [] } = props; 11 | 12 | return ( 13 |
14 | {cols.map((item: TooltipDetailListItem) => item.value)?.join('/')} 15 | {cols.length > 0 && rows.length > 0 && i18n(',')} 16 | {rows.map((item: TooltipDetailListItem) => item.value)?.join('/')} 17 |
18 | ); 19 | }); 20 | 21 | TooltipHead.displayName = 'TooltipHead'; 22 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/components/icon.tsx: -------------------------------------------------------------------------------- 1 | import { getIcon } from '@antv/s2'; 2 | import React from 'react'; 3 | import { HtmlIcon } from '../../../common/icons'; 4 | import { ReactElement } from '../../../common/react-element'; 5 | import type { TooltipIconProps } from '../interface'; 6 | 7 | export const TooltipIcon: React.FC = React.memo((props) => { 8 | const { icon, ...attrs } = props; 9 | 10 | if (!icon) { 11 | return null; 12 | } 13 | 14 | if (getIcon(icon as string)) { 15 | const name = icon as string; 16 | 17 | return ; 18 | } 19 | 20 | return ; 21 | }); 22 | 23 | TooltipIcon.displayName = 'TooltipIcon'; 24 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/components/infos.tsx: -------------------------------------------------------------------------------- 1 | import { TOOLTIP_PREFIX_CLS } from '@antv/s2'; 2 | import React from 'react'; 3 | import type { TooltipInfosProps } from '../interface'; 4 | 5 | export const TooltipInfos: React.FC = React.memo((props) => { 6 | const { infos = '' } = props; 7 | 8 | return
{infos}
; 9 | }); 10 | 11 | TooltipInfos.displayName = 'TooltipInfos'; 12 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/components/simple-tips.tsx: -------------------------------------------------------------------------------- 1 | import { TOOLTIP_PREFIX_CLS, type TooltipNameTipsOptions } from '@antv/s2'; 2 | import React from 'react'; 3 | 4 | export const TooltipSimpleTips: React.FC = React.memo( 5 | (props) => { 6 | const { tips = '', name = '' } = props; 7 | 8 | return ( 9 | <> 10 | {name &&
{name}
} 11 | {tips &&
{tips}
} 12 | 13 | ); 14 | }, 15 | ); 16 | 17 | TooltipSimpleTips.displayName = 'TooltipSimpleTips'; 18 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/context.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const TooltipContext = React.createContext(false); 4 | -------------------------------------------------------------------------------- /packages/s2-react/src/components/tooltip/index.less: -------------------------------------------------------------------------------- 1 | @import '@antv/s2/esm/shared/styles/tooltip/index.less'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/context/SpreadSheetContext.tsx: -------------------------------------------------------------------------------- 1 | import type { SpreadSheet } from '@antv/s2'; 2 | import React from 'react'; 3 | 4 | export const SpreadSheetContext = React.createContext( 5 | null as unknown as SpreadSheet, 6 | ); 7 | 8 | export function useSpreadSheetInstance() { 9 | return React.useContext(SpreadSheetContext); 10 | } 11 | -------------------------------------------------------------------------------- /packages/s2-react/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useEvents'; 2 | export * from './useLoading'; 3 | export * from './usePagination'; 4 | export * from './usePivotSheetUpdate'; 5 | export * from './useResize'; 6 | export * from './useSpreadSheet'; 7 | -------------------------------------------------------------------------------- /packages/s2-react/src/hooks/useLoading.ts: -------------------------------------------------------------------------------- 1 | import { S2Event, SpreadSheet } from '@antv/s2'; 2 | import React from 'react'; 3 | 4 | export const useLoading = (s2: SpreadSheet, loadingFromProps?: boolean) => { 5 | const [loading, setLoading] = React.useState( 6 | loadingFromProps ?? false, 7 | ); 8 | 9 | React.useEffect(() => { 10 | s2?.on(S2Event.LAYOUT_BEFORE_RENDER, () => { 11 | setLoading(true); 12 | }); 13 | 14 | s2?.on(S2Event.LAYOUT_AFTER_RENDER, () => { 15 | setLoading(false); 16 | }); 17 | }, [s2]); 18 | 19 | return { 20 | loading: loadingFromProps ?? loading, 21 | setLoading, 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/s2-react/src/hooks/useResize.ts: -------------------------------------------------------------------------------- 1 | import { createResizeObserver, type ResizeEffectParams } from '@antv/s2'; 2 | import React from 'react'; 3 | 4 | export const useResize = (params: ResizeEffectParams) => { 5 | const { s2, adaptive, container, wrapper } = params; 6 | 7 | React.useLayoutEffect( 8 | () => createResizeObserver({ s2, adaptive, wrapper, container }), 9 | [s2, wrapper, container, adaptive], 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/s2-react/src/index.ts: -------------------------------------------------------------------------------- 1 | import './utils/extendLocale'; 2 | 3 | export * from './components'; 4 | -------------------------------------------------------------------------------- /packages/s2-react/src/utils/extendLocale.ts: -------------------------------------------------------------------------------- 1 | import { Locale, extendLocale } from '@antv/s2'; 2 | 3 | extendLocale(Locale); 4 | -------------------------------------------------------------------------------- /packages/s2-react/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | -------------------------------------------------------------------------------- /packages/s2-react/src/utils/options.ts: -------------------------------------------------------------------------------- 1 | import { getBaseSheetComponentOptions } from '@antv/s2'; 2 | import { RENDER_TOOLTIP_OPTIONS } from '../common'; 3 | import type { SheetComponentOptions } from '../components'; 4 | 5 | export const getSheetComponentOptions = ( 6 | ...options: Partial[] 7 | ) => 8 | getBaseSheetComponentOptions( 9 | RENDER_TOOLTIP_OPTIONS, 10 | ...options, 11 | ); 12 | -------------------------------------------------------------------------------- /packages/s2-react/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*", "./typings.d.ts", "../../global.d.ts"], 4 | "compilerOptions": { 5 | "target": "es2015", 6 | "paths": { 7 | "@antv/s2": ["s2-react/node_modules/@antv/s2/esm/index.d.ts"], 8 | "@antv/s2/*": ["s2-react/node_modules/@antv/s2/esm/*"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/s2-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "jsx": "react", 6 | "paths": { 7 | "@antv/s2": ["s2-core/src/index.ts"], 8 | "@antv/s2/*":["s2-core/src/*"], 9 | "@antv/s2/esm/shared": ["s2-core/src/shared/index.ts"], 10 | "@antv/s2/esm/shared/*": ["s2-core/src/shared/*"], 11 | "@antv/s2-react-components": ["s2-react-components/src/index.ts"], 12 | "@/*": ["s2-react/src/*"], 13 | "tests/*": ["s2-react/__tests__/*"] 14 | } 15 | }, 16 | "exclude": ["node_modules", "coverage", "esm", "lib", "dist", "temp"], 17 | "include": ["src", "./typings.d.ts", "playground", "../../global.d.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /packages/s2-react/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.svg' { 4 | import * as React from 'react'; 5 | 6 | export const ReactComponent: React.FunctionComponent< 7 | React.SVGProps & { title?: string } 8 | >; 9 | } 10 | -------------------------------------------------------------------------------- /packages/s2-vue/.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../.releaserc.base.js'); 2 | -------------------------------------------------------------------------------- /packages/s2-vue/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | ['time', 'info', 'warn'].forEach((type) => { 2 | jest.spyOn(console, type).mockImplementation(() => {}); 3 | }); 4 | -------------------------------------------------------------------------------- /packages/s2-vue/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../../jest.config.base'); 2 | -------------------------------------------------------------------------------- /packages/s2-vue/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | S2 Vue Playground 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/s2-vue/playground/index.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | 4 | createApp(App).mount('#app'); 5 | -------------------------------------------------------------------------------- /packages/s2-vue/src/common/constant/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options'; 2 | -------------------------------------------------------------------------------- /packages/s2-vue/src/common/constant/options.ts: -------------------------------------------------------------------------------- 1 | import type { S2Options, SpreadSheet } from '@antv/s2'; 2 | import { CustomTooltip } from '../../components/tooltip/custom-tooltip'; 3 | 4 | export const RENDER_TOOLTIP_OPTION: Partial = { 5 | tooltip: { 6 | render: (spreadsheet: SpreadSheet) => new CustomTooltip(spreadsheet), 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/s2-vue/src/components/index.ts: -------------------------------------------------------------------------------- 1 | // organize-imports-ignore 2 | export { CustomTooltip } from './tooltip/custom-tooltip'; 3 | export { default as BaseSheet } from './sheets/base-sheet.vue'; 4 | export { default as SheetComponent } from './sheets/index.vue'; 5 | export { default as PivotSheet } from './sheets/pivot-sheet.vue'; 6 | export { default as TableSheet } from './sheets/table-sheet.vue'; 7 | export { default as EditableSheet } from './sheets/editable-sheet.vue'; 8 | -------------------------------------------------------------------------------- /packages/s2-vue/src/components/sheets/table-sheet.vue: -------------------------------------------------------------------------------- 1 | 21 | 24 | -------------------------------------------------------------------------------- /packages/s2-vue/src/components/tooltip/components/infos.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /packages/s2-vue/src/components/tooltip/components/operator/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TooltipOperator } from './index.vue'; 2 | export { default as TooltipOperatorMenu } from './menu.vue'; 3 | export { default as TooltipOperatorTitle } from './title.vue'; 4 | -------------------------------------------------------------------------------- /packages/s2-vue/src/components/tooltip/interface.ts: -------------------------------------------------------------------------------- 1 | import type { S2CellType, TooltipShowOptions } from '@antv/s2'; 2 | 3 | export interface TooltipRenderProps 4 | extends TooltipShowOptions { 5 | readonly content?: T; 6 | readonly cell: S2CellType; 7 | } 8 | 9 | export type TooltipInfosProps = { 10 | infos: string; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/s2-vue/src/hooks/useExpose.ts: -------------------------------------------------------------------------------- 1 | import type { SpreadSheet } from '@antv/s2'; 2 | import { shallowRef } from 'vue'; 3 | 4 | export interface SheetExpose { 5 | instance: SpreadSheet | undefined; 6 | } 7 | 8 | export const useExpose = (expose: (exposed?: Record) => void) => { 9 | const s2Ref = shallowRef(); 10 | 11 | expose({ 12 | get instance() { 13 | return s2Ref.value?.instance; 14 | }, 15 | }); 16 | 17 | return s2Ref; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/s2-vue/src/hooks/useLoading.ts: -------------------------------------------------------------------------------- 1 | import { S2Event, type SpreadSheet } from '@antv/s2'; 2 | import { ref, watch, type ShallowRef } from 'vue'; 3 | 4 | export const useLoading = ( 5 | s2Ref: ShallowRef, 6 | loadingProp = false, 7 | ) => { 8 | const loading = ref(loadingProp); 9 | const setLoading = (updated: boolean) => { 10 | loading.value = updated; 11 | }; 12 | 13 | watch(s2Ref, (s2) => { 14 | s2?.on(S2Event.LAYOUT_BEFORE_RENDER, () => { 15 | setLoading(true); 16 | }); 17 | 18 | s2?.on(S2Event.LAYOUT_AFTER_RENDER, () => { 19 | setLoading(false); 20 | }); 21 | }); 22 | 23 | return { loading, setLoading }; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/s2-vue/src/index.ts: -------------------------------------------------------------------------------- 1 | import './utils/extendLocale'; 2 | 3 | export * from './components'; 4 | export * from './hooks/useExpose'; 5 | -------------------------------------------------------------------------------- /packages/s2-vue/src/utils/extendLocale.ts: -------------------------------------------------------------------------------- 1 | import { Locale, extendLocale } from '@antv/s2'; 2 | 3 | extendLocale(Locale); 4 | -------------------------------------------------------------------------------- /packages/s2-vue/src/utils/options.ts: -------------------------------------------------------------------------------- 1 | import type { S2Options } from '@antv/s2'; 2 | import { getBaseSheetComponentOptions } from '@antv/s2'; 3 | import { RENDER_TOOLTIP_OPTION } from '../common/constant'; 4 | 5 | export const getSheetComponentOptions = ( 6 | ...options: Partial[] 7 | ): S2Options => getBaseSheetComponentOptions(RENDER_TOOLTIP_OPTION, ...options); 8 | -------------------------------------------------------------------------------- /packages/s2-vue/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*", "./typings.d.ts", "../../global.d.ts"], 4 | "compilerOptions": { 5 | "target": "es2015", 6 | "paths": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/s2-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "jsx": "preserve", 6 | "strict": false, 7 | "declaration": true, 8 | "noImplicitAny": false, 9 | "paths": { 10 | "@antv/s2": ["s2-core/src/index.ts"], 11 | "@antv/s2/esm/shared": ["s2-core/src/shared/index.ts"], 12 | }, 13 | }, 14 | "exclude": ["node_modules", "coverage", "esm", "lib", "dist", "temp"], 15 | "include": ["src", "./typings.d.ts", "../../global.d.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /packages/s2-vue/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.svg' { 4 | import { FunctionalComponent, SVGAttributes } from 'vue' 5 | const src: FunctionalComponent 6 | export default src 7 | } 8 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 's2-site' 4 | -------------------------------------------------------------------------------- /s2-site/.dumi/global.less: -------------------------------------------------------------------------------- 1 | .dumi-default-table { 2 | margin: 0 !important; 3 | } 4 | 5 | // h3 { 6 | // border-bottom: none !important; 7 | // } 8 | 9 | body { 10 | overflow-x: hidden; 11 | } 12 | 13 | .ant-layout-content { 14 | ul { 15 | margin-bottom: 16px !important; 16 | } 17 | } 18 | 19 | details { 20 | cursor: pointer; 21 | } 22 | 23 | .dumi-default-source-code { 24 | margin: 24px 0; 25 | 26 | .prism-code { 27 | background-color: #f6f8fa !important; 28 | } 29 | } 30 | 31 | .demo-card-screenshot { 32 | width: 100% !important; 33 | } 34 | -------------------------------------------------------------------------------- /s2-site/.dumi/pages/playground.en.tsx: -------------------------------------------------------------------------------- 1 | import Playground from './playground.zh'; 2 | 3 | export default Playground; 4 | -------------------------------------------------------------------------------- /s2-site/.dumi/pages/playground.zh.tsx: -------------------------------------------------------------------------------- 1 | import ClientOnly from '@antv/dumi-theme-antv/dist/common/ClientOnly'; 2 | import Footer from '@antv/dumi-theme-antv/dist/slots/Footer'; 3 | import Header from '@antv/dumi-theme-antv/dist/slots/Header'; 4 | import React from 'react'; 5 | 6 | const Page = React.lazy(() => import('../../playground/layouts')); 7 | 8 | const Playground: React.FC = () => { 9 | return ( 10 | <> 11 |
12 | 13 | 14 | 15 |