diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 156d9625e..2b8ada8ae 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -292,6 +292,7 @@ importers: '@visactor/vscale': ~0.18.1 '@visactor/vtable-editors': workspace:* '@visactor/vutils': ~0.18.9 + '@visactor/vutils-extension': ~1.11.5 '@vitejs/plugin-react': 3.1.0 axios: ^1.4.0 chai: 4.3.4 @@ -336,6 +337,7 @@ importers: '@visactor/vscale': 0.18.9 '@visactor/vtable-editors': link:../vtable-editors '@visactor/vutils': 0.18.9 + '@visactor/vutils-extension': 1.11.5 cssfontparser: 1.2.1 devDependencies: '@babel/core': 7.20.12 @@ -3819,6 +3821,13 @@ packages: '@visactor/vdataset': 0.18.9 '@visactor/vutils': 0.18.9 + /@visactor/vutils-extension/1.11.5: + resolution: {integrity: sha512-FDTFOsEB3AIsXJQJH8reH3FWfNpXkhvTPiaGez4OFy0i7iWBzN74PDvKZmSDImQCWgfVeo4KWz9Jvl3qZO1tSw==} + dependencies: + '@visactor/vdataset': 0.18.9 + '@visactor/vutils': 0.18.9 + dev: false + /@visactor/vutils/0.18.9: resolution: {integrity: sha512-+CPwBATTQUPtXQ0KVXFRz8SCwAY9m5aR9QmtsVqya+mgaay3moFaAPNTbdkLBuZM5ewRYVcv/3fsDxuH+NXfFg==} dependencies: diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index d59a4eeae..37b7a8722 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.4.2","mainProject":"@visactor/vtable","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.5.0","mainProject":"@visactor/vtable","nextBump":"minor"}] diff --git a/docs/assets/api/en/methods.md b/docs/assets/api/en/methods.md index 9aeb62807..ce197a838 100644 --- a/docs/assets/api/en/methods.md +++ b/docs/assets/api/en/methods.md @@ -1165,3 +1165,46 @@ Determines whether the cell is in the visible area of the cell. If the cell is c ``` cellIsInVisualView(col: number, row: number) ``` + +## getCellAtRelativePosition(Function) + +Gets the cell position relative to the upper left corner of the table. + +In the case of scrolling, the cells obtained are those after scrolling. For example, if the currently displayed rows are 100-120, the cell position relative to the upper left corner of the table (10,100) is (first column, 103rd row), assuming the row height is 40px. + +``` +/** +* Get the cell information corresponding to the screen coordinates, taking scrolling into account +* @param this +* @param relativeX The left x value, relative to the upper left corner of the container, taking into account the scrolling of the grid +* @param relativeY The left y value, relative to the upper left corner of the container, taking into account the scrolling of the grid +* @returns +*/ +getCellAtRelativePosition(relativeX: number, relativeY: number): CellAddressWithBound +``` + +## showMoverLine(Function) + +Displays a highlighted line for moving columns or rows + +``` +/** +* Display the highlight line of the moving column or row If the (col, row) cell is the column header, the highlight column line is displayed; If the (col, row) cell is the row header, the highlight row line is displayed +* @param col Which column in the table header should be highlighted after? +* @param row The row after which the highlighted line is displayed +*/ +showMoverLine(col: number, row: number) +``` + +## hideMoverLine(Function) + +Hide the highlight line of the moved column or row + +``` +/** +* Hide the highlight line of the moved column or row +* @param col +* @param row +*/ +hideMoverLine(col: number, row: number) +``` diff --git a/docs/assets/api/zh/methods.md b/docs/assets/api/zh/methods.md index 1109e4fe4..a93ddc064 100644 --- a/docs/assets/api/zh/methods.md +++ b/docs/assets/api/zh/methods.md @@ -1163,3 +1163,46 @@ interface ISortedMapItem { ``` cellIsInVisualView(col: number, row: number) ``` + +## getCellAtRelativePosition(Function) + +获取相对于表格左上角的坐标对应的单元格位置。 + +有滚动的情况下,获取的单元格是滚动后的,如当前显示的行是 100-120 行,获取相对于表格左上角(10,100)位置的单元格位置是(第一列,第 103 行),假设行高 40px。 + +``` + /** + * 获取屏幕坐标对应的单元格信息,考虑滚动 + * @param this + * @param relativeX 左边x值,相对于容器左上角,已考虑格滚动情况 + * @param relativeY 左边y值,相对于容器左上角,已考虑格滚动情况 + * @returns + */ + getCellAtRelativePosition(relativeX: number, relativeY: number): CellAddressWithBound +``` + +## showMoverLine(Function) + +显示移动列或移动行的高亮标记线 + +``` + /** + * 显示移动列或移动行的高亮线 如果(col,row)单元格是列头 则显示高亮列线; 如果(col,row)单元格是行头 则显示高亮行线 + * @param col 在表头哪一列后显示高亮线 + * @param row 在表头哪一行后显示高亮线 + */ + showMoverLine(col: number, row: number) +``` + +## hideMoverLine(Function) + +隐藏掉移动列或移动行的高亮线 + +``` + /** + * 隐藏掉移动列或移动行的高亮线 + * @param col + * @param row + */ + hideMoverLine(col: number, row: number) +``` diff --git a/docs/assets/changelog/en/release.md b/docs/assets/changelog/en/release.md index 50c50b5de..9f12eab86 100644 --- a/docs/assets/changelog/en/release.md +++ b/docs/assets/changelog/en/release.md @@ -1,3 +1,33 @@ +# v1.4.2 + +2024-07-05 + + +**🆕 New feature** + +- **@visactor/vtable**: corner title can display row and column diemensionTitle [#1926](https://github.com/VisActor/VTable/issues/1926) +- **@visactor/vtable**: add column hide config [#1991](https://github.com/VisActor/VTable/issues/1991) +- **@visactor/vtable**: add getCellAtRelativePosition api + +**🐛 Bug fix** + +- **@visactor/vtable**: when not exit edit state then can not select other cells [#1974](https://github.com/VisActor/VTable/issues/1974) +- **@visactor/vtable**: selected_clear event trigger [#1981](https://github.com/VisActor/VTable/issues/1981) +- **@visactor/vtable**: pivotTable virtual node edit value not work [#2002](https://github.com/VisActor/VTable/issues/2002) +- **@visactor/vtable**: tooltip content can not be selected [#2003](https://github.com/VisActor/VTable/issues/2003) +- **@visactor/vtable**: fix vrender export module +- **@visactor/vtable**: fix merge cell update performance problem [#1972](https://github.com/VisActor/VTable/issues/1972) +- **@visactor/vtable**: fix regexp format for webpack 3 [#2005](https://github.com/VisActor/VTable/issues/2005) +- **@visactor/vtable**: fix width computation in shrinkSparklineFirst mode + +**🔨 Refactor** + +- **@visactor/vtable**: sparkline cellType set aggregationType None automatically [#1999](https://github.com/VisActor/VTable/issues/1999) + + + +[more detail about v1.4.2](https://github.com/VisActor/VTable/releases/tag/v1.4.2) + # v1.4.0 2024-06-21 diff --git a/docs/assets/changelog/zh/release.md b/docs/assets/changelog/zh/release.md index 97469de96..204a29d05 100644 --- a/docs/assets/changelog/zh/release.md +++ b/docs/assets/changelog/zh/release.md @@ -1,3 +1,33 @@ +# v1.4.2 + +2024-07-05 + + +**🆕 新增功能** + +- **@visactor/vtable**: corner title can display row and column diemensionTitle [#1926](https://github.com/VisActor/VTable/issues/1926) +- **@visactor/vtable**: add column hide config [#1991](https://github.com/VisActor/VTable/issues/1991) +- **@visactor/vtable**: add getCellAtRelativePosition api + +**🐛 功能修复** + +- **@visactor/vtable**: when not exit edit state then can not select other cells [#1974](https://github.com/VisActor/VTable/issues/1974) +- **@visactor/vtable**: selected_clear event trigger [#1981](https://github.com/VisActor/VTable/issues/1981) +- **@visactor/vtable**: pivotTable virtual node edit value not work [#2002](https://github.com/VisActor/VTable/issues/2002) +- **@visactor/vtable**: tooltip content can not be selected [#2003](https://github.com/VisActor/VTable/issues/2003) +- **@visactor/vtable**: fix vrender export module +- **@visactor/vtable**: fix merge cell update performance problem [#1972](https://github.com/VisActor/VTable/issues/1972) +- **@visactor/vtable**: fix regexp format for webpack 3 [#2005](https://github.com/VisActor/VTable/issues/2005) +- **@visactor/vtable**: fix width computation in shrinkSparklineFirst mode + +**🔨 功能重构** + +- **@visactor/vtable**: sparkline cellType set aggregationType None automatically [#1999](https://github.com/VisActor/VTable/issues/1999) + + + +[更多详情请查看 v1.4.2](https://github.com/VisActor/VTable/releases/tag/v1.4.2) + # v1.4.0 2024-06-21 diff --git a/docs/assets/demo-react/en/custom-layout/cell-custom-dom.md b/docs/assets/demo-react/en/custom-layout/cell-custom-dom.md index 4745ab3d4..0d649a370 100644 --- a/docs/assets/demo-react/en/custom-layout/cell-custom-dom.md +++ b/docs/assets/demo-react/en/custom-layout/cell-custom-dom.md @@ -4,7 +4,7 @@ group: component title: cell custom dom component cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/react-vtable-cell-dom-component.jpeg order: 1-1 -link: '../guide/Developer_Ecology/react' +link: '../../guide/Developer_Ecology/react' --- # cell custom dom component diff --git a/docs/assets/demo-react/zh/custom-layout/cell-custom-dom.md b/docs/assets/demo-react/zh/custom-layout/cell-custom-dom.md index b08666947..0cc54de03 100644 --- a/docs/assets/demo-react/zh/custom-layout/cell-custom-dom.md +++ b/docs/assets/demo-react/zh/custom-layout/cell-custom-dom.md @@ -4,7 +4,7 @@ group: component title: 单元格内dom组件 cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/react-vtable-cell-dom-component.jpeg order: 1-1 -link: '../guide/Developer_Ecology/react' +link: '../../guide/Developer_Ecology/react' --- # 单元格内dom组件 diff --git a/docs/assets/demo/en/basic-functionality/frozen-col.md b/docs/assets/demo/en/basic-functionality/frozen-col.md index 07d7715ab..e850d75ac 100644 --- a/docs/assets/demo/en/basic-functionality/frozen-col.md +++ b/docs/assets/demo/en/basic-functionality/frozen-col.md @@ -3,7 +3,7 @@ category: examples group: Basic Features title: frozen column cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-col.gif -link: '../guide/basic_function/frozen_column' +link: '../guide/basic_function/frozen_column_row' option: ListTable#frozenColCount --- diff --git a/docs/assets/demo/en/basic-functionality/frozen-row.md b/docs/assets/demo/en/basic-functionality/frozen-row.md new file mode 100644 index 000000000..ad90db4e0 --- /dev/null +++ b/docs/assets/demo/en/basic-functionality/frozen-row.md @@ -0,0 +1,154 @@ +--- +category: examples +group: Basic Features +title: Freeze Row +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-row.gif +link: '../guide/basic_function/frozen_column_row' +option: ListTable#frozenRowCount +--- + +# Freeze Row + +In order to keep these key information rows visible throughout the horizontal scroll, we need to "freeze" these rows. + +## Key Configurations + + - `frozenRowCount` The number of frozen rows (including header), default is the number of header rows + +## Code demo + +```javascript livedemo template=vtable + + let tableInstance; + fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'City', + title: 'City', + headerStyle: { + textStick: true, + color: (args) => { + if (args.row < 4) { + return 'red'; + } + return '#000'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + }, + width: 'auto' + } + ], + columns: [ + { + dimensionKey: 'Category', + title: 'Category', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + indicators: [ + { + indicatorKey: 'Quantity', + title: 'Quantity', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + }, + { + indicatorKey: 'Sales', + title: 'Sales', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + }, + { + indicatorKey: 'Profit', + title: 'Profit', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + dataConfig: { + sortRules: [ + { + sortField: 'Category', + sortBy: ['Office Supplies', 'Technology', 'Furniture'] + } + ] + }, + widthMode: 'standard', + frozenRowCount: 4, + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index f75067a33..95d5b3bf4 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -392,7 +392,23 @@ "category": "demo", "group": "Basic Features", "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-col.gif", - "link": "'../../guide/basic_function/frozen_column'", + "link": "'../../guide/basic_function/frozen_column_row'", + "option": "" + } + }, + { + "path": "frozen-row", + "title": { + "zh": "冻结行", + "en": "frozen row" + }, + "meta": { + "title": "frozen row", + "keywords": "", + "category": "demo", + "group": "Basic Features", + "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-row.gif", + "link": "'../../guide/basic_function/frozen_column_row'", "option": "" } }, diff --git a/docs/assets/demo/zh/basic-functionality/frozen-col.md b/docs/assets/demo/zh/basic-functionality/frozen-col.md index b1ebdbfdb..234bc3f34 100644 --- a/docs/assets/demo/zh/basic-functionality/frozen-col.md +++ b/docs/assets/demo/zh/basic-functionality/frozen-col.md @@ -3,7 +3,7 @@ category: examples group: Basic Features title: 冻结列 cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-col.gif -link: '../guide/basic_function/frozen_column' +link: '../guide/basic_function/frozen_column_row' option: ListTable#frozenColCount --- diff --git a/docs/assets/demo/zh/basic-functionality/frozen-row.md b/docs/assets/demo/zh/basic-functionality/frozen-row.md new file mode 100644 index 000000000..f21fbab68 --- /dev/null +++ b/docs/assets/demo/zh/basic-functionality/frozen-row.md @@ -0,0 +1,154 @@ +--- +category: examples +group: Basic Features +title: 冻结行 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/frozen-row.gif +link: '../guide/basic_function/frozen_column_row' +option: ListTable#frozenRowCount +--- + +# 冻结行 + +为了在纵向滚动过程中,始终保持这些关键信息列可见,我们需要将这些行进行“冻结”。 + +## 关键配置 + + - `frozenRowCount` 冻结行数(包含表头) 默认表头行数 + +## 代码演示 + +```javascript livedemo template=vtable + + let tableInstance; + fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'City', + title: 'City', + headerStyle: { + textStick: true, + color: (args) => { + if (args.row < 4) { + return 'red'; + } + return '#000'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + }, + width: 'auto' + } + ], + columns: [ + { + dimensionKey: 'Category', + title: 'Category', + headerStyle: { + textStick: true + }, + width: 'auto' + } + ], + indicators: [ + { + indicatorKey: 'Quantity', + title: 'Quantity', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + }, + { + indicatorKey: 'Sales', + title: 'Sales', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + }, + { + indicatorKey: 'Profit', + title: 'Profit', + width: 'auto', + showSort: false, + headerStyle: { + fontWeight: 'normal' + }, + format: rec => { + return '$' + Number(rec).toFixed(2); + }, + style: { + padding: [16, 28, 16, 28], + color(args) { + if (args.dataValue >= 0) return 'black'; + return 'red'; + }, + bgColor: (args) => { + if (args.row < 4) { + return 'rgba(255, 0, 0, 0.1)'; + } + return '#fff'; + } + } + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + dataConfig: { + sortRules: [ + { + sortField: 'Category', + sortBy: ['Office Supplies', 'Technology', 'Furniture'] + } + ] + }, + widthMode: 'standard', + frozenRowCount: 4, + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` diff --git a/docs/assets/guide/en/Load_on_Demand.md b/docs/assets/guide/en/Load_on_Demand.md new file mode 100644 index 000000000..c004ec40e --- /dev/null +++ b/docs/assets/guide/en/Load_on_Demand.md @@ -0,0 +1,38 @@ +# VTable on-demand loading + +By default, `ListTable`, `PivotTable` and `PivotChart` introduced from `@visactor/vtable` package contain all table-related components, which is a complete table component library. + +In order to meet the needs of package size optimization, VTable provides two types, `ListTableSimple` and `PivotTableSimple`, which are the most simplified lists and pivot tables, respectively. They only support text display and do not contain external components such as menus and titles. If you need some functions, you can load them on demand. The usage is as follows: + +```js +// ListTableSimple, PivotTableSimple are the simplest list and pivot table components, which do not include cell types and any components other than text +import {ListTableSimple, PivotTableSimple, registerTitle, registerTooltip} from '@visactor/vtable'; + +// Register title component +registerTitle(); + +// Register tooltip component +registerTooltip(); +``` + +## Load functions on demand + +### Functional components + +* registerAxis: axis component +* registerEmptyTip: empty prompt component +* registerLegend: legend component +* registerMenu: menu component +* registerTitle: title component +* registerTooltip: tooltip component + +### Cell type + +* registerChartCell: chart cell +* registerCheckboxCell: checkbox cell +* registerImageCell: Image cell +* registerProgressBarCell: Progress bar cell +* registerRadioCell: Radio button cell +* registerSparkLineCell: Sparkline cell +* registerTextCell: Text cell +* registerVideoCell: Video cell \ No newline at end of file diff --git a/docs/assets/guide/en/basic_function/frozen_column.md b/docs/assets/guide/en/basic_function/frozen_column_row.md similarity index 100% rename from docs/assets/guide/en/basic_function/frozen_column.md rename to docs/assets/guide/en/basic_function/frozen_column_row.md diff --git a/docs/assets/guide/en/export/excel.md b/docs/assets/guide/en/export/excel.md index 5f0135e27..b5f01addf 100644 --- a/docs/assets/guide/en/export/excel.md +++ b/docs/assets/guide/en/export/excel.md @@ -76,4 +76,36 @@ const excelOption = { } }; downloadExcel(await exportVTableToExcel(tableInstance, excelOption)); -``` \ No newline at end of file +``` + + +### formatExcelJSCell + +If you need to further customize the export style, you can set `formatExcelJSCell` to a function. The function parameters are cell information and ExcelJS cell objects. The function return value is the ExcelJS cell object. If `undefined` is returned, the default export logic is used. You can automatically set ExcelJS cell properties in the function. For details, please refer to https://github.com/exceljs/exceljs?tab=readme-ov-file#styles + +```ts +type CellInfo = { + cellType: string; + cellValue: string; + table: IVTable; + col: number; + row: number; +}; + +type ExportVTableToExcelOptions = { + // ...... + formatExceljsCell?: (cellInfo: CellInfo, cellInExceljs: ExcelJS.Cell) => ExcelJS.Cell; +}; +``` + +```js +const excelOption = { + formatExcelJSCell: (cellInfo, cell) => { + if (cellInfo.col === 1) { + cell.numFmt = '0.00%'; + } + return cell; + } +}; +downloadExcel(await exportVTableToExcel(tableInstance, excelOption)); +``` diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json index 09a36a953..83d1b12f0 100644 --- a/docs/assets/guide/menu.json +++ b/docs/assets/guide/menu.json @@ -245,10 +245,10 @@ ] }, { - "path": "frozen_column", + "path": "frozen_column_row", "title": { "zh": "冻结", - "en": "frozen column" + "en": "frozen column & row" } }, { @@ -551,6 +551,13 @@ } ] }, + { + "path": "Load_on_Demand", + "title": { + "zh": "按需加载", + "en": "Load on Demand" + } + }, { "path": "search", "title": { diff --git a/docs/assets/guide/zh/Load_on_Demand.md b/docs/assets/guide/zh/Load_on_Demand.md new file mode 100644 index 000000000..3f834daa3 --- /dev/null +++ b/docs/assets/guide/zh/Load_on_Demand.md @@ -0,0 +1,38 @@ +# VTable 按需加载 + +默认从 `@visactor/vtable` 包中引入的 `ListTable` 、 `PivotTable` 和 `PivotChart` 包含所有的表格相关的组件,是一个完整的表格组件库。 + +为了满足包体积优化的需求,VTable提供了 `ListTableSimple` 和 `PivotTableSimple` 两个类型,分别是最简化的列表和透视表,只支持文字类型的显示,不包含菜单、标题等外部组件。如果需要部分功能,可以进行按需加载,使用方法如下: + +```js +// ListTableSimple, PivotTableSimple 是最简单的列表和透视表组件,不包除了文字之外的单元格类型和任何组件 +import {ListTableSimple, PivotTableSimple, registerTitle, registerTooltip} from '@visactor/vtable'; + +// 注册标题组件 +registerTitle(); + +// 注册tooltip组件 +registerTooltip(); +``` + +## 按需加载功能 + +### 功能组件 + +* registerAxis: 坐标轴组件 +* registerEmptyTip: 空白提示组件 +* registerLegend: 图例组件 +* registerMenu: 菜单组件 +* registerTitle: 标题组件 +* registerTooltip: tooltip组件 + +### 单元格类型 + +* registerChartCell: 图表单元格 +* registerCheckboxCell: 复选框单元格 +* registerImageCell: 图片单元格 +* registerProgressBarCell: 进度条单元格 +* registerRadioCell: 单选框单元格 +* registerSparkLineCell: 迷你图单元格 +* registerTextCell: 文字单元格 +* registerVideoCell: 视频单元格 \ No newline at end of file diff --git a/docs/assets/guide/zh/basic_function/frozen_column.md b/docs/assets/guide/zh/basic_function/frozen_column_row.md similarity index 100% rename from docs/assets/guide/zh/basic_function/frozen_column.md rename to docs/assets/guide/zh/basic_function/frozen_column_row.md diff --git a/docs/assets/guide/zh/export/excel.md b/docs/assets/guide/zh/export/excel.md index 52c5b1e69..e8b67640b 100644 --- a/docs/assets/guide/zh/export/excel.md +++ b/docs/assets/guide/zh/export/excel.md @@ -62,7 +62,7 @@ type CellInfo = { }; type ExportVTableToExcelOptions = { - ignoreIcon?: boolean; + // ...... formatExportOutput?: (cellInfo: CellInfo) => string | undefined; }; ``` @@ -76,4 +76,35 @@ const excelOption = { } }; downloadExcel(await exportVTableToExcel(tableInstance, excelOption)); +``` + +### formatExcelJSCell + +对于导出样式有进一步的定制化需求的话,可以设置`formatExcelJSCell`为一个函数,函数的参数为单元格信息和ExcelJS的单元格对象,函数的返回值为ExcelJS的单元格对象,如果返回`undefined`,则按照默认导出逻辑处理。可以在函数中自动设置ExcelJS的单元格属性,具体可以参考 https://github.com/exceljs/exceljs?tab=readme-ov-file#styles + +```ts +type CellInfo = { + cellType: string; + cellValue: string; + table: IVTable; + col: number; + row: number; +}; + +type ExportVTableToExcelOptions = { + // ...... + formatExceljsCell?: (cellInfo: CellInfo, cellInExceljs: ExcelJS.Cell) => ExcelJS.Cell; +}; +``` + +```js +const excelOption = { + formatExcelJSCell: (cellInfo, cell) => { + if (cellInfo.col === 1) { + cell.numFmt = '0.00%'; + } + return cell; + } +}; +downloadExcel(await exportVTableToExcel(tableInstance, excelOption)); ``` \ No newline at end of file diff --git a/docs/assets/option/en/common/option-secondary.md b/docs/assets/option/en/common/option-secondary.md index 7e73ffd4c..b5663dc8d 100644 --- a/docs/assets/option/en/common/option-secondary.md +++ b/docs/assets/option/en/common/option-secondary.md @@ -68,6 +68,10 @@ Minimum column width limit. If set to true, the column width will be limited to The number of frozen columns +#${prefix} frozenRowCount(number) = 0 + +The number of frozen columns(including the header) + #${prefix} rightFrozenColCount(number) = 0 Freeze Columns Right diff --git a/docs/assets/option/zh/common/option-secondary.md b/docs/assets/option/zh/common/option-secondary.md index 660fe7d7f..20eeec1d9 100644 --- a/docs/assets/option/zh/common/option-secondary.md +++ b/docs/assets/option/zh/common/option-secondary.md @@ -68,6 +68,10 @@ adaptive 模式下高度的适应策略,默认为 'only-body'。 冻结列数 +#${prefix} frozenRowCount(number) = 0 + +冻结行数(包含表头) + #${prefix} rightFrozenColCount(number) = 0 右侧冻结列数 diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index f5df8e134..350074c17 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.4.2", + "version": "1.5.0", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index bb5fd4634..a6af03740 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.4.2", + "version": "1.5.0", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index 0fbedcc65..fd6739732 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.4.2", + "version": "1.5.0", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index 6ce572554..1993710b2 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.4.2", + "version": "1.5.0", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-export/src/excel/index.ts b/packages/vtable-export/src/excel/index.ts index 371022296..e47e48348 100644 --- a/packages/vtable-export/src/excel/index.ts +++ b/packages/vtable-export/src/excel/index.ts @@ -18,6 +18,7 @@ export type CellInfo = { export type ExportVTableToExcelOptions = { ignoreIcon?: boolean; formatExportOutput?: (cellInfo: CellInfo) => string | undefined; + formatExcelJSCell?: (cellInfo: CellInfo, cellInExcelJS: ExcelJS.Cell) => ExcelJS.Cell; }; export async function exportVTableToExcel(tableInstance: IVTable, options?: ExportVTableToExcelOptions) { @@ -132,17 +133,25 @@ function addCell( const cellInfo = { cellType, cellValue, table: tableInstance, col, row }; const formattedValue = options.formatExportOutput(cellInfo); if (formattedValue !== undefined) { - const cell = worksheet.getCell(encodeCellAddress(col, row)); + let cell = worksheet.getCell(encodeCellAddress(col, row)); cell.value = formattedValue; cell.font = getCellFont(cellStyle, cellType); cell.fill = getCellFill(cellStyle); cell.border = getCellBorder(cellStyle); const offset = getHierarchyOffset(col, row, tableInstance as any); cell.alignment = getCellAlignment(cellStyle, Math.ceil(offset / cell.font.size)); - return; + + if (cell && options.formatExcelJSCell) { + const formatedCell = options.formatExcelJSCell({ cellType, cellValue, table: tableInstance, col, row }, cell); + if (formatedCell) { + cell = formatedCell; + } + } + return cell; } } + let cell; if ( cellType === 'image' || cellType === 'video' || @@ -165,7 +174,7 @@ function addCell( // ext: { width: tableInstance.getColWidth(col), height: tableInstance.getRowHeight(row) } }); } else if (cellType === 'text' || cellType === 'link') { - const cell = worksheet.getCell(encodeCellAddress(col, row)); + cell = worksheet.getCell(encodeCellAddress(col, row)); cell.value = getCellValue(cellValue, cellType); cell.font = getCellFont(cellStyle, cellType); cell.fill = getCellFill(cellStyle); @@ -188,6 +197,14 @@ function addCell( }); tableInstance.scenegraph.updateNextFrame(); // rerender chart to avoid display error } + + if (cell && options.formatExcelJSCell) { + const formatedCell = options.formatExcelJSCell({ cellType, cellValue, table: tableInstance, col, row }, cell); + if (formatedCell) { + cell = formatedCell; + } + } + return cell; } function getCellValue(cellValue: string, cellType: CellType) { diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index 72f4d188b..54c09e4ce 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.4.2", + "version": "1.5.0", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index f024bba78..ab478d672 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,47 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.5.0", + "tag": "@visactor/vtable_v1.5.0", + "date": "Fri, 05 Jul 2024 10:43:51 GMT", + "comments": { + "none": [ + { + "comment": "feat: add showMoverLine and hideMoverLine api #2009\n\n" + }, + { + "comment": "fix: pivot chart select state #2017\n\n" + }, + { + "comment": "fix: disable select and edit input should move when input is outside of table #2039\n\n" + }, + { + "comment": "fix: last column resize width error #2040\n\n" + }, + { + "comment": "fix: fix test judgement in customMergeCell #2031" + }, + { + "comment": "fix: fix selected highlight update when scrolling #2028" + }, + { + "comment": "fix: fix select-rect update when scroll #2015" + }, + { + "comment": "fix: fix frozen cell update problem in sort #1997" + } + ], + "minor": [ + { + "comment": "feat: add formatExcelJSCell config in vtable-export #1989" + }, + { + "comment": "feat: optimize package size & add load on demand feature" + } + ] + } + }, { "version": "1.4.2", "tag": "@visactor/vtable_v1.4.2", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index aeab6d496..b145187a7 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,33 @@ # Change Log - @visactor/vtable -This log was last generated on Tue, 02 Jul 2024 12:48:08 GMT and should not be manually modified. +This log was last generated on Fri, 05 Jul 2024 10:43:51 GMT and should not be manually modified. + +## 1.5.0 +Fri, 05 Jul 2024 10:43:51 GMT + +### Minor changes + +- feat: add formatExcelJSCell config in vtable-export #1989 +- feat: optimize package size & add load on demand feature + +### Updates + +- feat: add showMoverLine and hideMoverLine api #2009 + + +- fix: pivot chart select state #2017 + + +- fix: disable select and edit input should move when input is outside of table #2039 + + +- fix: last column resize width error #2040 + + +- fix: fix test judgement in customMergeCell #2031 +- fix: fix selected highlight update when scrolling #2028 +- fix: fix select-rect update when scroll #2015 +- fix: fix frozen cell update problem in sort #1997 ## 1.4.2 Tue, 02 Jul 2024 12:48:08 GMT diff --git a/packages/vtable/__tests__/api/listTable-getCellRect.test.ts b/packages/vtable/__tests__/api/listTable-getCellRect.test.ts index e304c94d5..dad3cca5d 100644 --- a/packages/vtable/__tests__/api/listTable-getCellRect.test.ts +++ b/packages/vtable/__tests__/api/listTable-getCellRect.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable getCellRect test', () => { diff --git a/packages/vtable/__tests__/api/listTable-getCellRelativePosition.test.ts b/packages/vtable/__tests__/api/listTable-getCellRelativePosition.test.ts new file mode 100644 index 000000000..900f3ebbf --- /dev/null +++ b/packages/vtable/__tests__/api/listTable-getCellRelativePosition.test.ts @@ -0,0 +1,253 @@ +// @ts-nocheck +// 有问题可对照demo unitTestListTable +import records from '../data/marketsales.json'; +import { ListTable } from '../../src'; +import { createDiv } from '../dom'; +global.__VERSION__ = 'none'; +describe('listTable getCellRect test', () => { + const containerDom: HTMLElement = createDiv(); + containerDom.style.position = 'relative'; + containerDom.style.width = '1000px'; + containerDom.style.height = '800px'; + const columns = [ + { + field: '订单 ID', + caption: '订单 ID', + sort: true, + width: 'auto', + description: '这是订单的描述信息', + style: { + fontFamily: 'Arial', + fontSize: 14 + } + }, + { + field: '订单日期', + caption: '订单日期' + }, + { + field: '发货日期', + caption: '发货日期' + }, + { + field: '客户名称', + caption: '客户名称', + style: { + padding: [10, 0, 10, 60] + } + } + ]; + const option = { + columns: [...columns, ...columns, ...columns, ...columns], + defaultColWidth: 150, + allowFrozenColCount: 5, + frozenColCount: 1, + bottomFrozenRowCount: 2, + rightFrozenColCount: 2 + }; + + option.container = containerDom; + option.records = records; + const listTable = new ListTable(option); + test('listTable getCellAtRelativePosition init', () => { + expect(listTable.getCellAtRelativePosition(100, 220)).toEqual({ + row: 5, + col: 0, + rect: { + left: 0, + right: 151, + top: 200, + bottom: 240, + width: 151, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 220)).toEqual({ + row: 5, + col: 14, + rect: { + top: 200, + bottom: 240, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(400, 220)).toEqual({ + row: 5, + col: 2, + rect: { + left: 301, + right: 451, + top: 200, + bottom: 240, + width: 150, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(100, 740)).toEqual({ + row: 38, + col: 0, + rect: { + left: 0, + right: 151, + width: 151 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 740)).toEqual({ + row: 38, + col: 14, + rect: {} + }); + + expect(listTable.getCellAtRelativePosition(400, 740)).toEqual({ + row: 38, + col: 2, + rect: { + left: 301, + right: 451, + width: 150 + } + }); + + expect(listTable.getCellAtRelativePosition(100, 20)).toEqual({ + row: 0, + col: 0, + rect: { + left: 0, + right: 151, + top: 0, + bottom: 40, + width: 151, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 20)).toEqual({ + row: 0, + col: 14, + rect: { + top: 0, + bottom: 40, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(400, 20)).toEqual({ + row: 0, + col: 2, + rect: { + left: 301, + right: 451, + top: 0, + bottom: 40, + width: 150, + height: 40 + } + }); + }); + + test('listTable getCellAtRelativePosition scroll', () => { + listTable.scrollLeft = 500; + listTable.scrollTop = 500; + expect(listTable.getCellAtRelativePosition(100, 220)).toEqual({ + row: 17, + col: 0, + rect: { + left: 0, + right: 151, + top: 680, + bottom: 720, + width: 151, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 220)).toEqual({ + row: 17, + col: 14, + rect: { + top: 680, + bottom: 720, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(400, 220)).toEqual({ + row: 17, + col: 5, + rect: { + left: 752, + right: 902, + top: 680, + bottom: 720, + width: 150, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(100, 740)).toEqual({ + row: 38, + col: 0, + rect: { + left: 0, + right: 151, + width: 151 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 740)).toEqual({ + row: 38, + col: 14, + rect: {} + }); + + expect(listTable.getCellAtRelativePosition(400, 740)).toEqual({ + row: 38, + col: 5, + rect: { + left: 752, + right: 902, + width: 150 + } + }); + + expect(listTable.getCellAtRelativePosition(100, 20)).toEqual({ + row: 0, + col: 0, + rect: { + left: 0, + right: 151, + top: 0, + bottom: 40, + width: 151, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(800, 20)).toEqual({ + row: 0, + col: 14, + rect: { + top: 0, + bottom: 40, + height: 40 + } + }); + + expect(listTable.getCellAtRelativePosition(400, 20)).toEqual({ + row: 0, + col: 5, + rect: { + left: 752, + right: 902, + top: 0, + bottom: 40, + width: 150, + height: 40 + } + }); + }); +}); diff --git a/packages/vtable/__tests__/columns/listTable-cellType-function.test.ts b/packages/vtable/__tests__/columns/listTable-cellType-function.test.ts index f72000e6f..6d6012798 100644 --- a/packages/vtable/__tests__/columns/listTable-cellType-function.test.ts +++ b/packages/vtable/__tests__/columns/listTable-cellType-function.test.ts @@ -1,6 +1,6 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-cellType-function init test', () => { diff --git a/packages/vtable/__tests__/columns/listTable-cellType.test.ts b/packages/vtable/__tests__/columns/listTable-cellType.test.ts index 66e41abc1..f1629a7e9 100644 --- a/packages/vtable/__tests__/columns/listTable-cellType.test.ts +++ b/packages/vtable/__tests__/columns/listTable-cellType.test.ts @@ -1,6 +1,6 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-cellType init test', () => { diff --git a/packages/vtable/__tests__/columns/listTable-checkbox.test.ts b/packages/vtable/__tests__/columns/listTable-checkbox.test.ts index f8dea8c89..246245068 100644 --- a/packages/vtable/__tests__/columns/listTable-checkbox.test.ts +++ b/packages/vtable/__tests__/columns/listTable-checkbox.test.ts @@ -1,6 +1,6 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-checkbox init test', () => { diff --git a/packages/vtable/__tests__/columns/listTable-custom-layout.test.ts b/packages/vtable/__tests__/columns/listTable-custom-layout.test.ts index ded17c8c0..5f39d66d4 100644 --- a/packages/vtable/__tests__/columns/listTable-custom-layout.test.ts +++ b/packages/vtable/__tests__/columns/listTable-custom-layout.test.ts @@ -1,7 +1,7 @@ /* eslint-disable max-len */ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import * as VTable from '../../src/index'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; diff --git a/packages/vtable/__tests__/columns/listTable-dragHeader.test.ts b/packages/vtable/__tests__/columns/listTable-dragHeader.test.ts index 03476fb4a..f7249a9e5 100644 --- a/packages/vtable/__tests__/columns/listTable-dragHeader.test.ts +++ b/packages/vtable/__tests__/columns/listTable-dragHeader.test.ts @@ -1,6 +1,6 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; const generatePersons = count => { diff --git a/packages/vtable/__tests__/components/listTable-menu.test.ts b/packages/vtable/__tests__/components/listTable-menu.test.ts index 9c3f94cec..16eba25e5 100644 --- a/packages/vtable/__tests__/components/listTable-menu.test.ts +++ b/packages/vtable/__tests__/components/listTable-menu.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-menu init test', () => { diff --git a/packages/vtable/__tests__/components/listTable-title.test.ts b/packages/vtable/__tests__/components/listTable-title.test.ts index d90742892..aa0f73f55 100644 --- a/packages/vtable/__tests__/components/listTable-title.test.ts +++ b/packages/vtable/__tests__/components/listTable-title.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-title init test', () => { diff --git a/packages/vtable/__tests__/components/listTable-tooltip.test.ts b/packages/vtable/__tests__/components/listTable-tooltip.test.ts index 057c4d0a0..f378fd162 100644 --- a/packages/vtable/__tests__/components/listTable-tooltip.test.ts +++ b/packages/vtable/__tests__/components/listTable-tooltip.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import * as VTable from '../../src/index'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; diff --git a/packages/vtable/__tests__/edit/pivotTable.test.ts b/packages/vtable/__tests__/edit/pivotTable.test.ts index 29042df2b..e4820e3f2 100644 --- a/packages/vtable/__tests__/edit/pivotTable.test.ts +++ b/packages/vtable/__tests__/edit/pivotTable.test.ts @@ -2,7 +2,7 @@ // @ts-nocheck // 有问题可对照demo unitTestPivotTable import records from '../data/marketsales.json'; -import { PivotTable } from '../../src/PivotTable'; +import { PivotTable } from '../../src'; import { register } from '../../src'; import { InputEditor } from '@visactor/vtable-editors'; import { createDiv } from '../dom'; diff --git a/packages/vtable/__tests__/listTable-1W.test.ts b/packages/vtable/__tests__/listTable-1W.test.ts index c021d39e1..9c018758e 100644 --- a/packages/vtable/__tests__/listTable-1W.test.ts +++ b/packages/vtable/__tests__/listTable-1W.test.ts @@ -1,6 +1,6 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable -import { ListTable } from '../src/ListTable'; +import { ListTable } from '../src'; import * as VTable from '../src/index'; import { createDiv } from './dom'; global.__VERSION__ = 'none'; diff --git a/packages/vtable/__tests__/listTable.test.ts b/packages/vtable/__tests__/listTable.test.ts index b4215bcaf..d76fc9508 100644 --- a/packages/vtable/__tests__/listTable.test.ts +++ b/packages/vtable/__tests__/listTable.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from './data/marketsales.json'; -import { ListTable } from '../src/ListTable'; +import { ListTable } from '../src'; import { createDiv } from './dom'; global.__VERSION__ = 'none'; describe('listTable init test', () => { diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index b4f5d8240..efc31f54c 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable init test', () => { diff --git a/packages/vtable/__tests__/options/listTable-autoRowHeight.test.ts b/packages/vtable/__tests__/options/listTable-autoRowHeight.test.ts index 58d100e07..a65b85c7d 100644 --- a/packages/vtable/__tests__/options/listTable-autoRowHeight.test.ts +++ b/packages/vtable/__tests__/options/listTable-autoRowHeight.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv, removeDom } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-autoRowHeight init test', () => { diff --git a/packages/vtable/__tests__/options/listTable-frozen.test.ts b/packages/vtable/__tests__/options/listTable-frozen.test.ts index b11506591..47ed49543 100644 --- a/packages/vtable/__tests__/options/listTable-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-frozen.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable-frozen init test', () => { diff --git a/packages/vtable/__tests__/options/listTable-sort.test.ts b/packages/vtable/__tests__/options/listTable-sort.test.ts index 8f956ac64..fcf83b756 100644 --- a/packages/vtable/__tests__/options/listTable-sort.test.ts +++ b/packages/vtable/__tests__/options/listTable-sort.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestListTable import records from '../data/marketsales.json'; -import { ListTable } from '../../src/ListTable'; +import { ListTable } from '../../src'; import { createDiv } from '../dom'; global.__VERSION__ = 'none'; describe('listTable init test', () => { diff --git a/packages/vtable/__tests__/pivotTable-analysis.test.ts b/packages/vtable/__tests__/pivotTable-analysis.test.ts index 37ceeaaba..ffff4f483 100644 --- a/packages/vtable/__tests__/pivotTable-analysis.test.ts +++ b/packages/vtable/__tests__/pivotTable-analysis.test.ts @@ -1,7 +1,7 @@ // @ts-nocheck // 有问题可对照demo unitTestPivotTable import records from './data/marketsales.json'; -import { PivotTable } from '../src/PivotTable'; +import { PivotTable } from '../src'; import * as VTable from '../src/index'; import { createDiv } from './dom'; global.__VERSION__ = 'none'; diff --git a/packages/vtable/__tests__/pivotTable-tree.test.ts b/packages/vtable/__tests__/pivotTable-tree.test.ts index be6143c2f..38531f7c2 100644 --- a/packages/vtable/__tests__/pivotTable-tree.test.ts +++ b/packages/vtable/__tests__/pivotTable-tree.test.ts @@ -2,7 +2,7 @@ // @ts-nocheck // 有问题可对照demo unitTestPivotTable import records from './data/North_American_Superstore_pivot_extension_rows.json'; -import { PivotTable } from '../src/PivotTable'; +import { PivotTable } from '../src'; import { createDiv } from './dom'; global.__VERSION__ = 'none'; describe('pivotTableTree init test', () => { diff --git a/packages/vtable/__tests__/pivotTable.test.ts b/packages/vtable/__tests__/pivotTable.test.ts index 8f0dde9ee..9fcf5b10c 100644 --- a/packages/vtable/__tests__/pivotTable.test.ts +++ b/packages/vtable/__tests__/pivotTable.test.ts @@ -2,7 +2,7 @@ // @ts-nocheck // 有问题可对照demo unitTestPivotTable import records from './data/marketsales.json'; -import { PivotTable } from '../src/PivotTable'; +import { PivotTable } from '../src'; import { createDiv } from './dom'; global.__VERSION__ = 'none'; describe('pivotTable init test', () => { diff --git a/packages/vtable/examples/editor/input-editor.ts b/packages/vtable/examples/editor/input-editor.ts index 09754094d..3413d2cad 100644 --- a/packages/vtable/examples/editor/input-editor.ts +++ b/packages/vtable/examples/editor/input-editor.ts @@ -171,10 +171,10 @@ export function createTable() { const instance = new ListTable(option); //设置表格数据 - // instance.setRecords(personsDataSource, { - // field: 'progress', - // order: 'desc' - // }); + instance.setRecords(personsDataSource, { + field: 'progress', + order: 'desc' + }); instance.on('change_cell_value', arg => { console.log(arg); diff --git a/packages/vtable/examples/list/list.ts b/packages/vtable/examples/list/list.ts index 144bf154d..06fb2e665 100644 --- a/packages/vtable/examples/list/list.ts +++ b/packages/vtable/examples/list/list.ts @@ -214,6 +214,9 @@ export function createTable() { }; const tableInstance = new VTable.ListTable(option); window.tableInstance = tableInstance; + bindDebugTool(tableInstance.scenegraph.stage, { + customGrapicKeys: ['col', 'row'] + }); tableInstance.on('change_cell_value', arg => { console.log(arg); }); diff --git a/packages/vtable/examples/type/checkbox.ts b/packages/vtable/examples/type/checkbox.ts index 4a66be9c7..89c388c37 100644 --- a/packages/vtable/examples/type/checkbox.ts +++ b/packages/vtable/examples/type/checkbox.ts @@ -9,7 +9,7 @@ export function createTable() { columns: [ { field: '', - // headerType: 'checkbox', + headerType: 'checkbox', cellType: 'checkbox', width: 'auto', checked(args) { @@ -72,6 +72,7 @@ export function createTable() { showFrozenIcon: true, //显示VTable内置冻结列图标 widthMode: 'standard', heightMode: 'autoHeight', + allowFrozenColCount: 3, // transpose: true theme: VTable.themes.DEFAULT.extends({ checkboxStyle: { diff --git a/packages/vtable/package.json b/packages/vtable/package.json index 0b5984752..2c84e1712 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.4.2", + "version": "1.5.0", "description": "canvas table width high performance", "keywords": [ "grid", @@ -19,7 +19,15 @@ "url": "https://VisActor.io/" }, "license": "MIT", - "sideEffects": true, + "sideEffects": [ + "./src/ListTable-all.js", + "./src/ListTable-simple.js", + "./src/PivotTable-all.js", + "./src/PivotTable-simple.js", + "./src/PivotChart.js", + "./src/index.js", + "./src/scenegraph/scenegraph.js" + ], "main": "cjs/index.js", "module": "es/index.js", "types": "es/index.d.ts", @@ -56,6 +64,7 @@ "@visactor/vutils": "~0.18.9", "@visactor/vscale": "~0.18.1", "@visactor/vdataset": "~0.18.1", + "@visactor/vutils-extension": "~1.11.5", "cssfontparser": "^1.2.1" }, "devDependencies": { diff --git a/packages/vtable/src/ListTable-all.ts b/packages/vtable/src/ListTable-all.ts new file mode 100644 index 000000000..2d497ce21 --- /dev/null +++ b/packages/vtable/src/ListTable-all.ts @@ -0,0 +1,36 @@ +import { ListTable } from './ListTable'; +import { + registerAxis, + registerEmptyTip, + registerLegend, + registerMenu, + registerTitle, + registerTooltip +} from './components'; +import { + registerChartCell, + registerCheckboxCell, + registerImageCell, + registerProgressBarCell, + registerRadioCell, + registerSparkLineCell, + registerTextCell, + registerVideoCell +} from './scenegraph/group-creater/cell-type'; + +registerAxis(); +registerEmptyTip(); +registerLegend(); +registerMenu(); +registerTitle(); +registerTooltip(); + +registerChartCell(); +registerCheckboxCell(); +registerImageCell(); +registerProgressBarCell(); +registerRadioCell(); +registerSparkLineCell(); +registerTextCell(); +registerVideoCell(); +export class ListTableAll extends ListTable {} diff --git a/packages/vtable/src/ListTable-simple.ts b/packages/vtable/src/ListTable-simple.ts new file mode 100644 index 000000000..77d2e735e --- /dev/null +++ b/packages/vtable/src/ListTable-simple.ts @@ -0,0 +1,5 @@ +import { ListTable } from './ListTable'; +import { registerTextCell } from './scenegraph/group-creater/cell-type'; + +registerTextCell(); +export class ListTableSimple extends ListTable {} diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index af30270a6..b28f24b50 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -18,15 +18,13 @@ import type { } from './ts-types'; import { HierarchyState } from './ts-types'; import { SimpleHeaderLayoutMap } from './layout'; -import { isNumber, isObject, isValid } from '@visactor/vutils'; +import { isValid } from '@visactor/vutils'; import { _setDataSource, _setRecords, sortRecords } from './core/tableHelper'; import { BaseTable } from './core'; import type { BaseTableAPI, ListTableProtected } from './ts-types/base-table'; import { TABLE_EVENT_TYPE } from './core/TABLE_EVENT_TYPE'; -import { Title } from './components/title/title'; -import { cloneDeep } from '@visactor/vutils'; +import type { ITitleComponent } from './components/title/title'; import { Env } from './tools/env'; -import { editor } from './register'; import * as editors from './edit/editors'; import { EditManeger } from './edit/edit-manager'; import { computeColWidth } from './scenegraph/layout/compute-col-width'; @@ -35,9 +33,44 @@ import { defaultOrderFn } from './tools/util'; import type { IEditor } from '@visactor/vtable-editors'; import type { ColumnData, ColumnDefine } from './ts-types/list-table/layout-map/api'; import { getCellRadioState, setCellRadioState } from './state/radio/radio'; -import { cloneDeepSpec } from '@vutils-extension'; +import { cloneDeepSpec } from '@visactor/vutils-extension'; import { setCellCheckboxState } from './state/checkbox/checkbox'; -import { EmptyTip } from './components/empty-tip/empty-tip'; +import type { IEmptyTipComponent } from './components/empty-tip/empty-tip'; +import { Factory } from './core/factory'; +// import { +// registerAxis, +// registerEmptyTip, +// registerLegend, +// registerMenu, +// registerTitle, +// registerTooltip +// } from './components'; +// import { +// registerChartCell, +// registerCheckboxCell, +// registerImageCell, +// registerProgressBarCell, +// registerRadioCell, +// registerSparkLineCell, +// registerTextCell, +// registerVideoCell +// } from './scenegraph/group-creater/cell-type'; + +// registerAxis(); +// registerEmptyTip(); +// registerLegend(); +// registerMenu(); +// registerTitle(); +// registerTooltip(); + +// registerChartCell(); +// registerCheckboxCell(); +// registerImageCell(); +// registerProgressBarCell(); +// registerRadioCell(); +// registerSparkLineCell(); +// registerTextCell(); +// registerVideoCell(); export class ListTable extends BaseTable implements ListTableAPI { declare internalProps: ListTableProtected; @@ -98,6 +131,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.setRecords([]); } if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -105,6 +139,7 @@ export class ListTable extends BaseTable implements ListTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -436,6 +471,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.render(); } if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -443,6 +479,7 @@ export class ListTable extends BaseTable implements ListTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -528,7 +565,7 @@ export class ListTable extends BaseTable implements ListTableAPI { table.rowCount = layoutMap.recordsCount * layoutMap.bodyRowSpanCount + layoutMap.headerLevelCount; // table.frozenColCount = table.options.frozenColCount ?? 0; //这里不要这样写 这个setter会检查扁头宽度 可能将frozenColCount置为0 this.internalProps.frozenColCount = this.options.frozenColCount ?? 0; - table.frozenRowCount = layoutMap.headerLevelCount; + table.frozenRowCount = Math.max(layoutMap.headerLevelCount, this.options.frozenRowCount ?? 0); if (table.bottomFrozenRowCount !== (this.options.bottomFrozenRowCount ?? 0)) { table.bottomFrozenRowCount = this.options.bottomFrozenRowCount ?? 0; @@ -1037,6 +1074,7 @@ export class ListTable extends BaseTable implements ListTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } diff --git a/packages/vtable/src/PivotChart.ts b/packages/vtable/src/PivotChart.ts index 30b5d7b9a..8973569dc 100644 --- a/packages/vtable/src/PivotChart.ts +++ b/packages/vtable/src/PivotChart.ts @@ -38,17 +38,53 @@ import { clearChartCacheImage, updateChartData } from './scenegraph/refresh-node import type { ITableAxisOption } from './ts-types/component/axis'; import { cloneDeep, isArray } from '@visactor/vutils'; import type { DiscreteLegend } from '@visactor/vrender-components'; -import { Title } from './components/title/title'; +import type { ITitleComponent } from './components/title/title'; import { Env } from './tools/env'; import { TABLE_EVENT_TYPE } from './core/TABLE_EVENT_TYPE'; import type { IndicatorData } from './ts-types/list-table/layout-map/api'; -import { cloneDeepSpec } from '@vutils-extension'; +import { cloneDeepSpec } from '@visactor/vutils-extension'; import type { ITreeLayoutHeadNode } from './layout/tree-helper'; import { DimensionTree, type LayouTreeNode } from './layout/tree-helper'; import { IndicatorDimensionKeyPlaceholder } from './tools/global'; import { checkHasCartesianChart } from './layout/chart-helper/get-chart-spec'; import { supplementIndicatorNodesForCustomTree } from './layout/layout-helper'; -import { EmptyTip } from './components/empty-tip/empty-tip'; +import type { IEmptyTipComponent } from './components/empty-tip/empty-tip'; +import { Factory } from './core/factory'; +import { + registerAxis, + registerEmptyTip, + registerLegend, + registerMenu, + registerTitle, + registerTooltip +} from './components'; +import { + registerChartCell, + registerCheckboxCell, + registerImageCell, + registerProgressBarCell, + registerRadioCell, + registerSparkLineCell, + registerTextCell, + registerVideoCell +} from './scenegraph/group-creater/cell-type'; + +registerAxis(); +registerEmptyTip(); +registerLegend(); +registerMenu(); +registerTitle(); +registerTooltip(); + +registerChartCell(); +registerCheckboxCell(); +registerImageCell(); +registerProgressBarCell(); +registerRadioCell(); +registerSparkLineCell(); +registerTextCell(); +registerVideoCell(); + export class PivotChart extends BaseTable implements PivotChartAPI { layoutNodeId: { seqId: number } = { seqId: 0 }; declare internalProps: PivotChartProtected; @@ -228,6 +264,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { // 生成单元格场景树 this.scenegraph.createSceneGraph(); if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; this.internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -235,6 +272,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -460,6 +498,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { // 生成单元格场景树 this.scenegraph.createSceneGraph(); if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; this.internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -467,6 +506,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -493,7 +533,8 @@ export class PivotChart extends BaseTable implements PivotChartAPI { table.rowCount = layoutMap.rowCount ?? 0; // table.frozenColCount = layoutMap.rowHeaderLevelCount; //这里不要这样写 这个setter会检查扁头宽度 可能将frozenColCount置为0 table.internalProps.frozenColCount = layoutMap.rowHeaderLevelCount ?? 0; - table.frozenRowCount = layoutMap.headerLevelCount; + // table.frozenRowCount = layoutMap.headerLevelCount; + table.frozenRowCount = Math.max(layoutMap.headerLevelCount, this.options.frozenRowCount ?? 0); if (table.bottomFrozenRowCount !== (layoutMap?.bottomFrozenRowCount ?? 0)) { table.bottomFrozenRowCount = layoutMap?.bottomFrozenRowCount ?? 0; } diff --git a/packages/vtable/src/PivotTable-all.ts b/packages/vtable/src/PivotTable-all.ts new file mode 100644 index 000000000..caafdc0ed --- /dev/null +++ b/packages/vtable/src/PivotTable-all.ts @@ -0,0 +1,37 @@ +import { PivotTable } from './PivotTable'; +import { + registerAxis, + registerEmptyTip, + registerLegend, + registerMenu, + registerTitle, + registerTooltip +} from './components'; +import { + registerChartCell, + registerCheckboxCell, + registerImageCell, + registerProgressBarCell, + registerRadioCell, + registerSparkLineCell, + registerTextCell, + registerVideoCell +} from './scenegraph/group-creater/cell-type'; + +registerAxis(); +registerEmptyTip(); +registerLegend(); +registerMenu(); +registerTitle(); +registerTooltip(); + +registerChartCell(); +registerCheckboxCell(); +registerImageCell(); +registerProgressBarCell(); +registerRadioCell(); +registerSparkLineCell(); +registerTextCell(); +registerVideoCell(); + +export class PivotTableAll extends PivotTable {} diff --git a/packages/vtable/src/PivotTable-simple.ts b/packages/vtable/src/PivotTable-simple.ts new file mode 100644 index 000000000..f95c2e2a1 --- /dev/null +++ b/packages/vtable/src/PivotTable-simple.ts @@ -0,0 +1,6 @@ +import { PivotTable } from './PivotTable'; +import { registerTextCell } from './scenegraph/group-creater/cell-type'; + +registerTextCell(); + +export class PivotTableSimple extends PivotTable {} diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index 8b1345de3..44fa6e68d 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -28,7 +28,7 @@ import { cellInRange, emptyFn } from './tools/helper'; import { Dataset } from './dataset/dataset'; import { BaseTable } from './core/BaseTable'; import type { BaseTableAPI, HeaderData, PivotTableProtected } from './ts-types/base-table'; -import { Title } from './components/title/title'; +import type { ITitleComponent } from './components/title/title'; import { cloneDeep, isNumber, isValid } from '@visactor/vutils'; import { Env } from './tools/env'; import type { ITreeLayoutHeadNode } from './layout/tree-helper'; @@ -41,9 +41,11 @@ import { computeColWidth } from './scenegraph/layout/compute-col-width'; import { computeRowHeight } from './scenegraph/layout/compute-row-height'; import { isAllDigits } from './tools/util'; import type { IndicatorData } from './ts-types/list-table/layout-map/api'; -import { cloneDeepSpec } from '@vutils-extension'; +import { cloneDeepSpec } from '@visactor/vutils-extension'; import { parseColKeyRowKeyForPivotTable, supplementIndicatorNodesForCustomTree } from './layout/layout-helper'; -import { EmptyTip } from './components/empty-tip/empty-tip'; +import type { IEmptyTipComponent } from './components/empty-tip/empty-tip'; +import { Factory } from './core/factory'; + export class PivotTable extends BaseTable implements PivotTableAPI { layoutNodeId: { seqId: number } = { seqId: 0 }; declare internalProps: PivotTableProtected; @@ -207,6 +209,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { // this.render(); if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; this.internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -214,6 +217,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -417,6 +421,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { // this.scenegraph.resize(); // } if (options.title) { + const Title = Factory.getComponent('title') as ITitleComponent; this.internalProps.title = new Title(options.title, this); this.scenegraph.resize(); } @@ -424,6 +429,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } @@ -495,7 +501,8 @@ export class PivotTable extends BaseTable implements PivotTableAPI { (layoutMap.rowHeaderLevelCount ?? 0) + layoutMap.leftRowSeriesNumberColumnCount, this.options.frozenColCount ?? 0 ); - table.frozenRowCount = layoutMap.headerLevelCount; + // table.frozenRowCount = layoutMap.headerLevelCount; + table.frozenRowCount = Math.max(layoutMap.headerLevelCount, this.options.frozenRowCount ?? 0); if (table.bottomFrozenRowCount !== (this.options.bottomFrozenRowCount ?? 0)) { table.bottomFrozenRowCount = this.options.bottomFrozenRowCount ?? 0; @@ -1559,6 +1566,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { if (this.internalProps.emptyTip) { this.internalProps.emptyTip.resetVisible(); } else { + const EmptyTip = Factory.getComponent('emptyTip') as IEmptyTipComponent; this.internalProps.emptyTip = new EmptyTip(this.options.emptyTip, this); this.internalProps.emptyTip.resetVisible(); } diff --git a/packages/vtable/src/components/axis/axis.ts b/packages/vtable/src/components/axis/axis.ts index 3dcf9b23e..2cd5bd4ca 100644 --- a/packages/vtable/src/components/axis/axis.ts +++ b/packages/vtable/src/components/axis/axis.ts @@ -2,15 +2,15 @@ import { degreeToRadian, isNil, isValidNumber, merge } from '@visactor/vutils'; import type { BaseTableAPI } from '../../ts-types/base-table'; import type { ICellAxisOption } from '../../ts-types/component/axis'; import { LineAxis, type LineAxisAttributes } from '@visactor/vrender-components'; -import { commonAxis, getAxisAttributes, getCommonAxis } from './get-axis-attributes'; +import { getAxisAttributes, getCommonAxis } from './get-axis-attributes'; import { isXAxis, isYAxis } from '../util/orient'; import type { IOrientType } from '../../ts-types/component/util'; import { BandAxisScale } from './band-scale'; import { registerDataSetInstanceParser, registerDataSetInstanceTransform } from '../util/register'; import type { Parser } from '@visactor/vdataset'; -import { DataView } from '@visactor/vdataset'; +import { DataSet, DataView } from '@visactor/vdataset'; import type { IBaseScale } from '@visactor/vscale'; -import { ticks } from '@vutils-extension'; +import { ticks } from '@src/vrender'; import { LinearAxisScale } from './linear-scale'; import { doOverlap } from './label-overlap'; import type { TableTheme } from '../../themes/theme'; @@ -21,6 +21,16 @@ const scaleParser: Parser = (scale: IBaseScale) => { return scale; }; +export interface ICartesianAxis { + new ( + option: ICellAxisOption, + width: number, + height: number, + padding: [number, number, number, number], + table: BaseTableAPI + ): CartesianAxis; +} + export class CartesianAxis { width: number; height: number; @@ -113,11 +123,16 @@ export class CartesianAxis { } initData() { + if (!this.table._vDataSet) { + this.table._vDataSet = new DataSet(); + } + registerDataSetInstanceParser(this.table._vDataSet, 'scale', scaleParser); registerDataSetInstanceTransform(this.table._vDataSet, 'ticks', ticks); const label = this.option.label || {}; const tick = this.option.tick || {}; + const tickData = new DataView(this.table._vDataSet) .parse(this.scale._scale, { type: 'scale' diff --git a/packages/vtable/src/components/axis/get-axis-component-size.ts b/packages/vtable/src/components/axis/get-axis-component-size.ts index 7e27f917f..3c11cac29 100644 --- a/packages/vtable/src/components/axis/get-axis-component-size.ts +++ b/packages/vtable/src/components/axis/get-axis-component-size.ts @@ -3,6 +3,8 @@ import type { BaseTableAPI } from '../../ts-types/base-table'; import type { ICellAxisOption } from '../../ts-types/component/axis'; import { DEFAULT_TEXT_FONT_FAMILY, DEFAULT_TEXT_FONT_SIZE, commonAxis } from './get-axis-attributes'; +export type ComputeAxisComponentWidth = (config: ICellAxisOption, table: BaseTableAPI) => number; +export type ComputeAxisComponentHeight = (config: ICellAxisOption, table: BaseTableAPI) => number; /** * @description: compuational vertical axis width * @param {ICellAxisOption} config diff --git a/packages/vtable/src/components/empty-tip/empty-tip.ts b/packages/vtable/src/components/empty-tip/empty-tip.ts index 971b61d8a..f5a0a55c6 100644 --- a/packages/vtable/src/components/empty-tip/empty-tip.ts +++ b/packages/vtable/src/components/empty-tip/empty-tip.ts @@ -9,6 +9,10 @@ import type { PivotTable } from '../../PivotTable'; const emptyTipSvg = ''; +export interface IEmptyTipComponent { + new (emptyTipOption: IEmptyTip | true, table: BaseTableAPI): EmptyTip; +} + export class EmptyTip { table: BaseTableAPI; _emptyTipOption: IEmptyTip = { diff --git a/packages/vtable/src/components/index.ts b/packages/vtable/src/components/index.ts new file mode 100644 index 000000000..546696748 --- /dev/null +++ b/packages/vtable/src/components/index.ts @@ -0,0 +1,36 @@ +import { Factory } from '../core/factory'; +import { getAxisConfigInPivotChart } from '../layout/chart-helper/get-axis-config'; +import { CartesianAxis } from './axis/axis'; +import { computeAxisComponentHeight, computeAxisComponentWidth } from './axis/get-axis-component-size'; +import { EmptyTip } from './empty-tip/empty-tip'; +import { createLegend } from './legend/create-legend'; +import { MenuHandler } from './menu/dom/MenuHandler'; +import { Title } from './title/title'; +import { TooltipHandler } from './tooltip/TooltipHandler'; + +export const registerAxis = () => { + Factory.registerComponent('axis', CartesianAxis); + Factory.registerFunction('computeAxisComponentWidth', computeAxisComponentWidth); + Factory.registerFunction('computeAxisComponentHeight', computeAxisComponentHeight); + Factory.registerFunction('getAxisConfigInPivotChart', getAxisConfigInPivotChart); +}; + +export const registerEmptyTip = () => { + Factory.registerComponent('emptyTip', EmptyTip); +}; + +export const registerLegend = () => { + Factory.registerFunction('createLegend', createLegend); +}; + +export const registerMenu = () => { + Factory.registerComponent('menuHandler', MenuHandler); +}; + +export const registerTitle = () => { + Factory.registerComponent('title', Title); +}; + +export const registerTooltip = () => { + Factory.registerComponent('tooltipHandler', TooltipHandler); +}; diff --git a/packages/vtable/src/components/legend/create-legend.ts b/packages/vtable/src/components/legend/create-legend.ts index 25bc710c6..4665f3aec 100644 --- a/packages/vtable/src/components/legend/create-legend.ts +++ b/packages/vtable/src/components/legend/create-legend.ts @@ -3,6 +3,10 @@ import { DiscreteTableLegend } from './discrete-legend/discrete-legend'; import type { BaseTableAPI } from '../../ts-types/base-table'; import { ContinueTableLegend } from './continue-legend/continue-legend'; +export type CreateLegend = ( + option: ITableLegendOption, + table: BaseTableAPI +) => DiscreteTableLegend | ContinueTableLegend; export function createLegend(option: ITableLegendOption, table: BaseTableAPI) { if (option.type === 'color' || option.type === 'size') { return new ContinueTableLegend(option, table); diff --git a/packages/vtable/src/components/menu/dom/MenuHandler.ts b/packages/vtable/src/components/menu/dom/MenuHandler.ts index 70dafeb62..5e4cbb349 100644 --- a/packages/vtable/src/components/menu/dom/MenuHandler.ts +++ b/packages/vtable/src/components/menu/dom/MenuHandler.ts @@ -115,6 +115,9 @@ type AttachInfo = { range: CellRange; }; +export interface IMenuHandler { + new (table: BaseTableAPI): MenuHandler; +} export class MenuHandler { private _table: BaseTableAPI; private _menuInstances?: { [type: string]: BaseMenu }; diff --git a/packages/vtable/src/components/title/title.ts b/packages/vtable/src/components/title/title.ts index a0fcd07c7..1930a72ea 100644 --- a/packages/vtable/src/components/title/title.ts +++ b/packages/vtable/src/components/title/title.ts @@ -5,6 +5,10 @@ import type { ITitle } from '../../ts-types/component/title'; import { getQuadProps } from '../../scenegraph/utils/padding'; import type { BaseTableAPI } from '../../ts-types/base-table'; import { isEqual } from '@visactor/vutils'; + +export interface ITitleComponent { + new (titleOption: ITitle, table: BaseTableAPI): Title; +} export class Title { table: BaseTableAPI; _titleOption: ITitle; diff --git a/packages/vtable/src/components/tooltip/TooltipHandler.ts b/packages/vtable/src/components/tooltip/TooltipHandler.ts index 89afbf690..6ba0568ca 100644 --- a/packages/vtable/src/components/tooltip/TooltipHandler.ts +++ b/packages/vtable/src/components/tooltip/TooltipHandler.ts @@ -21,6 +21,9 @@ type AttachInfo = { range: CellRange; tooltipOptions: TooltipOptions; }; +export interface ITooltipHandler { + new (table: BaseTableAPI, confine: boolean): TooltipHandler; +} export class TooltipHandler { private _table: BaseTableAPI; diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 3edb9108f..04a5f6fc9 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -70,7 +70,7 @@ import { EventManager } from '../event/event'; import { BodyHelper } from '../body-helper/body-helper'; import { HeaderHelper } from '../header-helper/header-helper'; import type { PivotHeaderLayoutMap } from '../layout/pivot-header-layout'; -import { TooltipHandler } from '../components/tooltip/TooltipHandler'; +import type { ITooltipHandler } from '../components/tooltip/TooltipHandler'; import type { CachedDataSource, DataSource } from '../data'; import { AABBBounds, @@ -104,7 +104,7 @@ import { getStyleTheme, updateRootElementPadding } from './tableHelper'; -import { MenuHandler } from '../components/menu/dom/MenuHandler'; +import type { IMenuHandler } from '../components/menu/dom/MenuHandler'; import type { BaseTableAPI, BaseTableConstructorOptions, @@ -113,8 +113,8 @@ import type { } from '../ts-types/base-table'; import { FocusInput } from './FouseInput'; import { defaultPixelRatio } from '../tools/pixel-ratio'; -import { createLegend } from '../components/legend/create-legend'; -import { DataSet } from '@visactor/vdataset'; +import type { CreateLegend } from '../components/legend/create-legend'; +import type { DataSet } from '@visactor/vdataset'; import { Title } from '../components/title/title'; import type { Chart } from '../scenegraph/graphic/chart'; import { setBatchRenderChartCount } from '../scenegraph/graphic/contributions/chart-render-helper'; @@ -129,6 +129,7 @@ import type { ITextGraphicAttribute } from '@src/vrender'; import { ReactCustomLayout } from '../components/react/react-custom-layout'; import type { ISortedMapItem } from '../data/DataSource'; import { hasAutoImageColumn } from '../layout/layout-helper'; +import { Factory } from './factory'; import { getCellAt, getCellAtRelativePosition, @@ -167,7 +168,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { canvasWidth?: number; canvasHeight?: number; - _vDataSet: DataSet; + _vDataSet?: DataSet; scenegraph: Scenegraph; stateManager: StateManager; eventManager: EventManager; @@ -220,7 +221,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { // rowCount = 0, // colCount = 0, frozenColCount = 0, - // frozenRowCount = 0, + frozenRowCount, defaultRowHeight = 40, defaultHeaderRowHeight, defaultColWidth = 80, @@ -327,6 +328,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.pixelRatio = pixelRatio; internalProps.frozenColCount = frozenColCount; + internalProps.frozenRowCount = frozenRowCount; internalProps.defaultRowHeight = defaultRowHeight; internalProps.defaultHeaderRowHeight = defaultHeaderRowHeight ?? defaultRowHeight; // defaultHeaderRowHeight没有设置取defaultRowHeight @@ -413,13 +415,14 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { : 0 : 10; // 生成scenegraph - this._vDataSet = new DataSet(); + // this._vDataSet = new DataSet(); this.scenegraph = new Scenegraph(this); this.stateManager = new StateManager(this); this.eventManager = new EventManager(this); if (options.legends) { internalProps.legends = []; + const createLegend = Factory.getFunction('createLegend') as CreateLegend; if (Array.isArray(options.legends)) { for (let i = 0; i < options.legends.length; i++) { internalProps.legends.push(createLegend(options.legends[i], this)); @@ -447,6 +450,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { options.tooltip ); if (internalProps.tooltip.renderMode === 'html') { + const TooltipHandler = Factory.getComponent('tooltipHandler') as ITooltipHandler; internalProps.tooltipHandler = new TooltipHandler(this, internalProps.tooltip.confine); } internalProps.menu = Object.assign( @@ -463,6 +467,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { (this.globalDropDownMenu = options.menu.defaultHeaderMenuItems); if (internalProps.menu.renderMode === 'html') { + const MenuHandler = Factory.getComponent('menuHandler') as IMenuHandler; internalProps.menuHandler = new MenuHandler(this); } @@ -1803,8 +1808,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { /** * 获取屏幕坐标对应的单元格信息,考虑滚动 * @param this - * @param relativeX 左边x值,相对于容器左上角,考虑表格滚动 - * @param relativeY 左边y值,相对于容器左上角,考虑表格滚动 + * @param relativeX 左边x值,相对于容器左上角,已考虑格滚动情况 + * @param relativeY 左边y值,相对于容器左上角,已考虑格滚动情况 * @returns */ getCellAtRelativePosition(relativeX: number, relativeY: number): CellAddressWithBound { @@ -2260,7 +2265,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { : 0 : 10; // 生成scenegraph - this._vDataSet = new DataSet(); + // this._vDataSet = new DataSet(); internalProps.legends?.forEach(legend => { legend?.release(); }); @@ -2279,6 +2284,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { this.eventManager.updateEventBinder(); if (options.legends) { internalProps.legends = []; + const createLegend = Factory.getFunction('createLegend') as CreateLegend; if (Array.isArray(options.legends)) { for (let i = 0; i < options.legends.length; i++) { internalProps.legends.push(createLegend(options.legends[i], this)); @@ -2311,6 +2317,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { options.tooltip ); if (internalProps.tooltip.renderMode === 'html' && !internalProps.tooltipHandler) { + const TooltipHandler = Factory.getComponent('tooltipHandler') as ITooltipHandler; internalProps.tooltipHandler = new TooltipHandler(this, internalProps.tooltip.confine); } @@ -2329,6 +2336,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { (this.globalDropDownMenu = options.menu.defaultHeaderMenuItems); if (internalProps.menu.renderMode === 'html' && !internalProps.menuHandler) { + const MenuHandler = Factory.getComponent('menuHandler') as IMenuHandler; internalProps.menuHandler = new MenuHandler(this); } this.clearCellStyleCache(); @@ -2864,7 +2872,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { if ( customMerge && customMerge.range && - (customMerge.text || customMerge.customLayout || customMerge.customRender) + (isValid(customMerge.text) || customMerge.customLayout || customMerge.customRender) ) { return customMerge.range; } @@ -2879,7 +2887,11 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { getCustomMerge(col: number, row: number) { if (this.internalProps.customMergeCell) { const customMerge = this.internalProps.customMergeCell(col, row, this); - if (customMerge && customMerge.range && (customMerge.text || customMerge.customLayout || this.customRender)) { + if ( + customMerge && + customMerge.range && + (isValid(customMerge.text) || customMerge.customLayout || this.customRender) + ) { if (customMerge.style) { const styleClass = this.internalProps.bodyHelper.getStyleClass('text'); const style = customMerge.style; @@ -4281,4 +4293,22 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { get headerDomContainer() { return this.internalProps.headerDomContainer; } + /** + * 显示移动列或移动行的高亮线 如果(col,row)单元格是列头 则显示高亮列线; 如果(col,row)单元格是行头 则显示高亮行线 + * @param col 在表头哪一列后显示高亮线 + * @param row 在表头哪一行后显示高亮线 + */ + showMoverLine(col: number, row: number) { + this.scenegraph.component.showMoveCol(col, row, 0); + this.scenegraph.renderSceneGraph(); + } + /** + * 隐藏掉移动列或移动行的高亮线 + * @param col + * @param row + */ + hideMoverLine(col: number, row: number) { + this.scenegraph.component.hideMoveCol(); + this.scenegraph.renderSceneGraph(); + } } diff --git a/packages/vtable/src/core/factory.ts b/packages/vtable/src/core/factory.ts new file mode 100644 index 000000000..91fa95eb7 --- /dev/null +++ b/packages/vtable/src/core/factory.ts @@ -0,0 +1,29 @@ +export class Factory { + private static _components: { [key: string]: any } = {}; + private static _functions: { [key: string]: any } = {}; + private static _cellTypes: { [key: string]: any } = {}; + + static registerComponent(key: string, component: any) { + Factory._components[key] = component; + } + + static getComponent(key: string) { + return Factory._components[key]; + } + + static registerFunction(key: string, func: any) { + Factory._functions[key] = func; + } + + static getFunction(key: string) { + return Factory._functions[key]; + } + + static registerCellType(key: string, cellType: any) { + Factory._cellTypes[key] = cellType; + } + + static getCellType(key: string) { + return Factory._cellTypes[key]; + } +} diff --git a/packages/vtable/src/core/utils/get-cell-position.ts b/packages/vtable/src/core/utils/get-cell-position.ts index c40b720ea..a176b7631 100644 --- a/packages/vtable/src/core/utils/get-cell-position.ts +++ b/packages/vtable/src/core/utils/get-cell-position.ts @@ -363,6 +363,18 @@ export function getCellAtRelativePosition(x: number, y: number, _this: BaseTable x -= _this.tableX; y -= _this.tableY; + // top frozen + let topFrozen = false; + if (y > 0 && y < _this.getFrozenRowsHeight()) { + topFrozen = true; + } + + // left frozen + let leftFrozen = false; + if (x > 0 && x < _this.getFrozenColsWidth()) { + leftFrozen = true; + } + // bottom frozen let bottomFrozen = false; if (y > _this.tableNoFrameHeight - _this.getBottomFrozenRowsHeight() && y < _this.tableNoFrameHeight) { @@ -374,8 +386,16 @@ export function getCellAtRelativePosition(x: number, y: number, _this: BaseTable rightFrozen = true; } - const colInfo = getTargetColAtConsiderRightFrozen(rightFrozen ? x : x + _this.scrollLeft, rightFrozen, _this); - const rowInfo = getTargetRowAtConsiderBottomFrozen(bottomFrozen ? y : y + _this.scrollTop, bottomFrozen, _this); + const colInfo = getTargetColAtConsiderRightFrozen( + leftFrozen || rightFrozen ? x : x + _this.scrollLeft, + rightFrozen, + _this + ); + const rowInfo = getTargetRowAtConsiderBottomFrozen( + topFrozen || bottomFrozen ? y : y + _this.scrollTop, + bottomFrozen, + _this + ); const { row, top, bottom, height } = rowInfo; const { col, left, right, width } = colInfo; diff --git a/packages/vtable/src/event/listener/container-dom.ts b/packages/vtable/src/event/listener/container-dom.ts index 85c9a7981..136ce607f 100644 --- a/packages/vtable/src/event/listener/container-dom.ts +++ b/packages/vtable/src/event/listener/container-dom.ts @@ -6,6 +6,7 @@ import { handleWhell } from '../scroll'; import { browser } from '../../tools/helper'; import type { EventManager } from '../event'; import { getPixelRatio } from '../../tools/pixel-ratio'; +import { endResizeCol, endResizeRow } from './table-group'; export function bindContainerDomListener(eventManager: EventManager) { const table = eventManager.table; @@ -512,6 +513,11 @@ export function bindContainerDomListener(eventManager: EventManager) { table.eventManager.isDown = false; table.eventManager.isDraging = false; table.eventManager.inertiaScroll.endInertia(); + if (stateManager.isResizeCol()) { + endResizeCol(table); + } else if (stateManager.isResizeRow()) { + endResizeRow(table); + } }; eventManager.globalEventListeners.push({ name: 'pointerup', diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index a84d1c3ef..b60b4023f 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -957,7 +957,7 @@ export function bindGesture(eventManager: EventManager) { dblclickHandler(e, table); }); } -function endResizeCol(table: BaseTableAPI) { +export function endResizeCol(table: BaseTableAPI) { table.stateManager.endResizeCol(); // textStick 依赖了这个事件 所以一定要触发RESIZE_COLUMN_END // if ((table as any).hasListeners(TABLE_EVENT_TYPE.RESIZE_COLUMN_END)) { @@ -974,7 +974,7 @@ function endResizeCol(table: BaseTableAPI) { // } } -function endResizeRow(table: BaseTableAPI) { +export function endResizeRow(table: BaseTableAPI) { table.stateManager.endResizeRow(); table.fireListeners(TABLE_EVENT_TYPE.RESIZE_ROW_END, { diff --git a/packages/vtable/src/event/media-click.ts b/packages/vtable/src/event/media-click.ts index 334796e5b..4a5c498eb 100644 --- a/packages/vtable/src/event/media-click.ts +++ b/packages/vtable/src/event/media-click.ts @@ -12,6 +12,11 @@ export function bindMediaClick(table: BaseTableAPI): void { table.on(TABLE_EVENT_TYPE.CLICK_CELL, (e: MousePointerCellEvent) => { //如果目前是在某个icon上,如收起展开按钮 则不进行其他点击逻辑 const { col, row } = e; + + if (e.target.type === 'image' && (e.target as any).role && (e.target as any).role.startsWith('icon')) { + // click icon + return; + } let cellType; if (table.internalProps.layoutMap.isHeader(col, row)) { cellType = table.isPivotTable() diff --git a/packages/vtable/src/index.ts b/packages/vtable/src/index.ts index de9582877..2928eca9e 100644 --- a/packages/vtable/src/index.ts +++ b/packages/vtable/src/index.ts @@ -30,8 +30,11 @@ import type { TextAlignType, TextBaselineType } from './ts-types'; -import { ListTable } from './ListTable'; -import { PivotTable } from './PivotTable'; +import { ListTableAll as ListTable } from './ListTable-all'; +import { ListTableSimple } from './ListTable-simple'; +// import { PivotTable } from './PivotTable'; +import { PivotTableAll as PivotTable } from './PivotTable-all'; +import { PivotTableSimple } from './PivotTable-simple'; import { PivotChart } from './PivotChart'; import type { MousePointerCellEvent } from './ts-types/events'; import * as CustomLayout from './render/layout'; @@ -62,8 +65,10 @@ export { TYPES, core, ListTable, + ListTableSimple, ListTableConstructorOptions, PivotTable, + PivotTableSimple, PivotTableConstructorOptions, PivotChartConstructorOptions, PivotChart, @@ -116,3 +121,6 @@ function clearGlobal() { // columns.type.clearGlobal(); } TYPES.AggregationType; + +export * from './components'; +export * from './scenegraph/group-creater/cell-type'; diff --git a/packages/vtable/src/layout/chart-helper/get-axis-config.ts b/packages/vtable/src/layout/chart-helper/get-axis-config.ts index 6bf67ab11..d3d5439c4 100644 --- a/packages/vtable/src/layout/chart-helper/get-axis-config.ts +++ b/packages/vtable/src/layout/chart-helper/get-axis-config.ts @@ -2,10 +2,12 @@ import { isArray, isNumber, isValid, merge } from '@visactor/vutils'; import type { PivotHeaderLayoutMap } from '../pivot-header-layout'; import type { ITableAxisOption } from '../../ts-types/component/axis'; import type { PivotChart } from '../../PivotChart'; -import { getAxisDomainRangeAndLabels } from './get-axis-domain'; import type { CollectedValue } from '../../ts-types'; import { getNewRangeToAlign } from './zero-align'; +import { Factory } from '../../core/factory'; +import type { GetAxisDomainRangeAndLabels } from './get-axis-domain'; +export type GetAxisConfigInPivotChart = (col: number, row: number, layout: PivotHeaderLayoutMap) => any; export function getAxisConfigInPivotChart(col: number, row: number, layout: PivotHeaderLayoutMap): any { if (!layout._table.isPivotChart()) { return undefined; @@ -422,7 +424,7 @@ export function getAxisOption(col: number, row: number, orient: string, layout: }; } -export function checkZeroAlign(spec: any, orient: string, layout: PivotHeaderLayoutMap) { +function checkZeroAlign(spec: any, orient: string, layout: PivotHeaderLayoutMap) { // check condition: // 1. two axes and one set sync // 2. axisId in sync is another @@ -558,6 +560,7 @@ function getRange( range.min = range.min < 0 ? -1 : 0; range.max = range.max > 0 ? 1 : 0; } + const getAxisDomainRangeAndLabels = Factory.getFunction('getAxisDomainRangeAndLabels') as GetAxisDomainRangeAndLabels; const { range: niceRange, ticks } = getAxisDomainRangeAndLabels( range.min, range.max, diff --git a/packages/vtable/src/layout/chart-helper/get-axis-domain.ts b/packages/vtable/src/layout/chart-helper/get-axis-domain.ts index 0dbe96002..2e5e6cf23 100644 --- a/packages/vtable/src/layout/chart-helper/get-axis-domain.ts +++ b/packages/vtable/src/layout/chart-helper/get-axis-domain.ts @@ -89,3 +89,5 @@ export function getAxisDomainRangeAndLabels( ticks: scaleTicks }; } + +export type GetAxisDomainRangeAndLabels = typeof getAxisDomainRangeAndLabels; diff --git a/packages/vtable/src/layout/chart-helper/get-chart-spec.ts b/packages/vtable/src/layout/chart-helper/get-chart-spec.ts index ef8c37c08..91c012394 100644 --- a/packages/vtable/src/layout/chart-helper/get-chart-spec.ts +++ b/packages/vtable/src/layout/chart-helper/get-chart-spec.ts @@ -2,10 +2,12 @@ import { cloneDeep, isArray, isNumber, merge } from '@visactor/vutils'; import type { PivotHeaderLayoutMap } from '../pivot-header-layout'; import type { SimpleHeaderLayoutMap } from '../simple-header-layout'; import { getAxisOption, getAxisRange } from './get-axis-config'; -import { getAxisDomainRangeAndLabels } from './get-axis-domain'; import { getNewRangeToAlign } from './zero-align'; import type { IChartIndicator, IIndicator } from '../../ts-types'; -import { cloneDeepSpec } from '@vutils-extension'; +import { cloneDeepSpec } from '@visactor/vutils-extension'; +import { Factory } from '../../core/factory'; +import type { GetAxisDomainRangeAndLabels } from './get-axis-domain'; +import { DEFAULT_TEXT_FONT_SIZE } from '../../components/axis/get-axis-attributes'; const NO_AXISID_FRO_VTABLE = 'NO_AXISID_FRO_VTABLE'; @@ -211,14 +213,15 @@ export function getChartAxes(col: number, row: number, layout: PivotHeaderLayout axes.push( merge( { - range + range, + label: { style: { fontSize: DEFAULT_TEXT_FONT_SIZE } } }, axisOption, { type: axisOption?.type || 'linear', orient: index === 0 ? 'bottom' : 'top', // visible: true, - label: { visible: false }, + label: { visible: false, flush: true }, // label: { flush: true }, title: { visible: false }, domainLine: { visible: false }, @@ -246,7 +249,8 @@ export function getChartAxes(col: number, row: number, layout: PivotHeaderLayout merge( { domain: chartType === 'scatter' && !Array.isArray(domain) ? undefined : Array.from(domain ?? []), - range: chartType === 'scatter' && !Array.isArray(domain) ? domain : undefined + range: chartType === 'scatter' && !Array.isArray(domain) ? domain : undefined, + label: { style: { fontSize: DEFAULT_TEXT_FONT_SIZE } } }, axisOption, { @@ -308,14 +312,15 @@ export function getChartAxes(col: number, row: number, layout: PivotHeaderLayout axes.push( merge( { - range + range, + label: { style: { fontSize: DEFAULT_TEXT_FONT_SIZE } } }, axisOption, { type: axisOption?.type || 'linear', orient: index === 0 ? 'left' : 'right', // visible: true, - label: { visible: false }, + label: { visible: false, flush: true }, // label: { flush: true }, title: { visible: false }, domainLine: { visible: false }, @@ -345,7 +350,8 @@ export function getChartAxes(col: number, row: number, layout: PivotHeaderLayout merge( { domain: chartType === 'scatter' && !Array.isArray(domain) ? undefined : Array.from(domain ?? []), - range: chartType === 'scatter' && !Array.isArray(domain) ? domain : undefined + range: chartType === 'scatter' && !Array.isArray(domain) ? domain : undefined, + label: { style: { fontSize: DEFAULT_TEXT_FONT_SIZE } } }, axisOption, { @@ -424,6 +430,9 @@ function getRange( range.max = Math.max(range.max, 0); } if (axisOption?.nice) { + const getAxisDomainRangeAndLabels = Factory.getFunction( + 'getAxisDomainRangeAndLabels' + ) as GetAxisDomainRangeAndLabels; const { range: axisRange } = getAxisDomainRangeAndLabels( range.min, range.max, diff --git a/packages/vtable/src/layout/pivot-header-layout.ts b/packages/vtable/src/layout/pivot-header-layout.ts index 3b3e05641..1def71326 100644 --- a/packages/vtable/src/layout/pivot-header-layout.ts +++ b/packages/vtable/src/layout/pivot-header-layout.ts @@ -34,8 +34,6 @@ import type { PivotTable } from '../PivotTable'; import type { PivotChart } from '../PivotChart'; import { IndicatorDimensionKeyPlaceholder } from '../tools/global'; import { diffCellAddress } from '../tools/diff-cell'; -import type { ILinkDimension } from '../ts-types/pivot-table/dimension/link-dimension'; -import type { IImageDimension } from '../ts-types/pivot-table/dimension/image-dimension'; import { checkHasCartesianChart, checkHasChart, @@ -54,7 +52,8 @@ import { cloneDeep, isArray, isValid } from '@visactor/vutils'; import type { TextStyle } from '../body-helper/style'; import type { ITableAxisOption } from '../ts-types/component/axis'; import { getQuadProps } from '../scenegraph/utils/padding'; -import { getAxisConfigInPivotChart } from './chart-helper/get-axis-config'; +import type { GetAxisConfigInPivotChart } from './chart-helper/get-axis-config'; +import { Factory } from '../core/factory'; // export const sharedVar = { seqId: 0 }; // let colIndex = 0; @@ -2913,6 +2912,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { ((this.isFrozenRow(col, row) || this.isBottomFrozenRow(col, row)) && isHasCartesianChartInline(col, row, 'col', this)) ) { + const getAxisConfigInPivotChart = Factory.getFunction('getAxisConfigInPivotChart') as GetAxisConfigInPivotChart; return getAxisConfigInPivotChart(col, row, this); } return undefined; @@ -3159,7 +3159,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if ((this._table as PivotChart)._selectedDataItemsInChart.length >= 1) { const match = (this._table as PivotChart)._selectedDataItemsInChart.find(item => { for (const itemKey in item) { - if (item[itemKey] !== datum[itemKey]) { + if (typeof item[itemKey] !== 'object' && item[itemKey] !== datum[itemKey]) { return false; } } @@ -3169,7 +3169,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } else if ((this._table as PivotChart)._selectedDimensionInChart?.length) { // 判断维度点击 const match = (this._table as PivotChart)._selectedDimensionInChart.every(item => { - if (datum[item.key] !== item.value) { + if (typeof item.value !== 'object' && datum[item.key] !== item.value) { return false; } return true; @@ -3184,7 +3184,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if ((this._table as PivotChart)._selectedDataItemsInChart.length >= 1) { const match = (this._table as PivotChart)._selectedDataItemsInChart.find(item => { for (const itemKey in item) { - if (item[itemKey] !== datum[itemKey]) { + if (typeof item[itemKey] !== 'object' && item[itemKey] !== datum[itemKey]) { return false; } } @@ -3194,7 +3194,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } else if ((this._table as PivotChart)._selectedDimensionInChart?.length) { // 判断维度点击 const match = (this._table as PivotChart)._selectedDimensionInChart.every(item => { - if (datum[item.key] !== item.value) { + if (typeof item.value !== 'object' && datum[item.key] !== item.value) { return false; } return true; diff --git a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts index 33699feb9..370056650 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts @@ -827,11 +827,12 @@ export class AdjustColorGroupBeforeRenderContribution implements IGroupRenderCon // 处理hover颜色 if ((group as Group).role === 'cell') { const table = (group.stage as any).table as BaseTableAPI; - if (table && table.stateManager.interactionState !== InteractionState.scrolling) { + if (table) { const selectColor = getCellSelectColor(group as Group, table); if (selectColor) { + // show select highlight when scrolling (group.attribute as any)._vtableHightLightFill = selectColor; - } else { + } else if (table.stateManager.interactionState !== InteractionState.scrolling) { const hoverColor = getCellHoverColor(group as Group, table); if (hoverColor) { (group.attribute as any)._vtableHightLightFill = hoverColor; diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 895810202..756ef2074 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -8,7 +8,6 @@ import type { ColumnDefine, ColumnTypeOption, ImageColumnDefine, - MappingRule, ProgressbarColumnDefine, IRowSeriesNumber, TextColumnDefine, @@ -17,27 +16,25 @@ import type { import { dealWithCustom } from '../component/custom'; import type { Group } from '../graphic/group'; import { getProp } from '../utils/get-prop'; -import { createChartCellGroup } from './cell-type/chart-cell'; -import { createImageCellGroup } from './cell-type/image-cell'; -import { createProgressBarCell } from './cell-type/progress-bar-cell'; -import { createSparkLineCellGroup } from './cell-type/spark-line-cell'; -import { createCellGroup } from './cell-type/text-cell'; -import { createVideoCellGroup } from './cell-type/video-cell'; -import type { BaseTableAPI, HeaderData, PivotTableProtected } from '../../ts-types/base-table'; +import type { CreateChartCellGroup } from './cell-type/chart-cell'; +import type { CreateImageCellGroup } from './cell-type/image-cell'; +import type { CreateProgressBarCell } from './cell-type/progress-bar-cell'; +import type { CreateSparkLineCellGroup } from './cell-type/spark-line-cell'; +import type { CreateTextCellGroup } from './cell-type/text-cell'; +import type { CreateVideoCellGroup } from './cell-type/video-cell'; +import type { BaseTableAPI, HeaderData } from '../../ts-types/base-table'; import { getCellCornerRadius, getStyleTheme } from '../../core/tableHelper'; import { isPromise } from '../../tools/helper'; import { dealPromiseData } from '../utils/deal-promise-data'; -import { CartesianAxis } from '../../components/axis/axis'; -import { createCheckboxCellGroup } from './cell-type/checkbox-cell'; -// import type { PivotLayoutMap } from '../../layout/pivot-layout'; -import type { PivotHeaderLayoutMap } from '../../layout/pivot-header-layout'; +import type { ICartesianAxis } from '../../components/axis/axis'; +import { Factory } from '../../core/factory'; +import type { CreateCheckboxCellGroup } from './cell-type/checkbox-cell'; import { getHierarchyOffset } from '../utils/get-hierarchy-offset'; import { getQuadProps } from '../utils/padding'; -import { convertInternal } from '../../tools/util'; import { updateCellContentHeight, updateCellContentWidth } from '../utils/text-icon-layout'; import { isArray } from '@visactor/vutils'; import { breakString } from '../utils/break-string'; -import { createRadioCellGroup } from './cell-type/radio-cell'; +import type { CreateRadioCellGroup } from './cell-type/radio-cell'; export function createCell( type: ColumnTypeOption, @@ -164,7 +161,8 @@ export function createCell( } } - cellGroup = createCellGroup( + const createTextCellGroup = Factory.getFunction('createTextCellGroup') as CreateTextCellGroup; + cellGroup = createTextCellGroup( table, value, columnGroup, @@ -188,6 +186,7 @@ export function createCell( const axisConfig = table.internalProps.layoutMap.getAxisConfigInPivotChart(col, row); if (axisConfig) { + const CartesianAxis: ICartesianAxis = Factory.getComponent('axis'); const axis = new CartesianAxis(axisConfig, cellGroup.attribute.width, cellGroup.attribute.height, padding, table); cellGroup.clear(); cellGroup.appendChild(axis.component); @@ -203,6 +202,7 @@ export function createCell( } } else if (type === 'image') { // 创建图片单元格 + const createImageCellGroup = Factory.getFunction('createImageCellGroup') as CreateImageCellGroup; cellGroup = createImageCellGroup( columnGroup, 0, @@ -216,12 +216,15 @@ export function createCell( padding, textAlign, textBaseline, + mayHaveIcon, table, cellTheme, + range, isAsync ); } else if (type === 'video') { // 创建视频单元格 + const createVideoCellGroup = Factory.getFunction('createVideoCellGroup') as CreateVideoCellGroup; cellGroup = createVideoCellGroup( columnGroup, 0, @@ -235,12 +238,15 @@ export function createCell( padding, textAlign, textBaseline, + mayHaveIcon, table, cellTheme, + range, isAsync ); } else if (type === 'chart') { const chartInstance = table.internalProps.layoutMap.getChartInstance(col, row); + const createChartCellGroup = Factory.getFunction('createChartCellGroup') as CreateChartCellGroup; cellGroup = createChartCellGroup( null, columnGroup, @@ -265,7 +271,8 @@ export function createCell( const style = table._getCellStyle(col, row) as ProgressBarStyle; const dataValue = table.getCellOriginValue(col, row); // 创建基础文字单元格 - cellGroup = createCellGroup( + const createTextCellGroup = Factory.getFunction('createTextCellGroup') as CreateTextCellGroup; + cellGroup = createTextCellGroup( table, value, columnGroup, @@ -288,6 +295,7 @@ export function createCell( ); // 创建bar group + const createProgressBarCell = Factory.getFunction('createProgressBarCell') as CreateProgressBarCell; const progressBarGroup = createProgressBarCell( define as ProgressbarColumnDefine, style, @@ -306,6 +314,7 @@ export function createCell( cellGroup.appendChild(progressBarGroup); } } else if (type === 'sparkline') { + const createSparkLineCellGroup = Factory.getFunction('createSparkLineCellGroup') as CreateSparkLineCellGroup; cellGroup = createSparkLineCellGroup( null, columnGroup, @@ -321,6 +330,7 @@ export function createCell( isAsync ); } else if (type === 'checkbox') { + const createCheckboxCellGroup = Factory.getFunction('createCheckboxCellGroup') as CreateCheckboxCellGroup; cellGroup = createCheckboxCellGroup( null, columnGroup, @@ -334,12 +344,15 @@ export function createCell( padding, textAlign, textBaseline, + mayHaveIcon, table, cellTheme, define as CheckboxColumnDefine, + range, isAsync ); } else if (type === 'radio') { + const createRadioCellGroup = Factory.getFunction('createRadioCellGroup') as CreateRadioCellGroup; cellGroup = createRadioCellGroup( null, columnGroup, diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/chart-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/chart-cell.ts index 35298652e..a5fdc3ad5 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/chart-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/chart-cell.ts @@ -123,3 +123,5 @@ export function createChartCellGroup( return cellGroup; } + +export type CreateChartCellGroup = typeof createChartCellGroup; diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts index 6e7c77ca5..e85099cc0 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts @@ -1,8 +1,6 @@ -import type { ILine, ISymbol, IThemeSpec } from '@src/vrender'; -import { createLine, createSymbol } from '@src/vrender'; -import { PointScale, LinearScale } from '@visactor/vscale'; +import type { IThemeSpec } from '@src/vrender'; import { Group } from '../../graphic/group'; -import type { CellInfo, CheckboxColumnDefine, CheckboxStyleOption, SparklineSpec } from '../../../ts-types'; +import type { CellInfo, CellRange, CheckboxColumnDefine, CheckboxStyleOption, SparklineSpec } from '../../../ts-types'; import type { BaseTableAPI } from '../../../ts-types/base-table'; import { isObject } from '@visactor/vutils'; import type { CheckboxAttributes } from '@visactor/vrender-components'; @@ -12,6 +10,7 @@ import { getOrApply } from '../../../tools/helper'; import type { CheckboxStyle } from '../../../body-helper/style/CheckboxStyle'; import { getProp } from '../../utils/get-prop'; import { getCellBorderStrokeWidth } from '../../utils/cell-border-stroke-width'; +import { dealWithIconLayout } from '../../utils/text-icon-layout'; export function createCheckboxCellGroup( cellGroup: Group | null, @@ -20,15 +19,17 @@ export function createCheckboxCellGroup( yOrigin: number, col: number, row: number, - colWidth: number | 'auto', + colWidth: number, width: number, height: number, padding: number[], textAlign: CanvasTextAlign, textBaseline: CanvasTextBaseline, + mayHaveIcon: boolean, table: BaseTableAPI, cellTheme: IThemeSpec, define: CheckboxColumnDefine, + range: CellRange | undefined, isAsync: boolean ) { // cell @@ -82,22 +83,84 @@ export function createCheckboxCellGroup( } } + let icons; + if (mayHaveIcon) { + let iconCol = col; + let iconRow = row; + if (range) { + iconCol = range.start.col; + iconRow = range.start.row; + } + icons = table.getCellIcons(iconCol, iconRow); + } + + let iconWidth = 0; + let cellLeftIconWidth = 0; + let cellRightIconWidth = 0; + if (Array.isArray(icons) && icons.length !== 0) { + const { leftIconWidth, rightIconWidth, absoluteLeftIconWidth, absoluteRightIconWidth } = dealWithIconLayout( + icons, + cellGroup, + range, + table + ); + + iconWidth = leftIconWidth + rightIconWidth; + cellLeftIconWidth = leftIconWidth; + cellRightIconWidth = rightIconWidth; + + // 更新各个部分横向位置 + cellGroup.forEachChildren((child: any) => { + if (child.role === 'icon-left') { + child.setAttribute('x', child.attribute.x + padding[3]); + } else if (child.role === 'icon-right') { + child.setAttribute('x', child.attribute.x + width - rightIconWidth - padding[1]); + } else if (child.role === 'icon-absolute-right') { + child.setAttribute('x', child.attribute.x + width - absoluteRightIconWidth - padding[1]); + } + }); + + // 更新各个部分纵向位置 + cellGroup.forEachChildren((child: any) => { + if (textBaseline === 'middle') { + child.setAttribute('y', (height - child.AABBBounds.height()) / 2); + } else if (textBaseline === 'bottom') { + child.setAttribute('y', height - child.AABBBounds.height() - padding[2]); + } else { + child.setAttribute('y', padding[0]); + } + }); + } + // checkbox - const checkboxComponent = createCheckbox(col, row, colWidth, width, height, padding, cellTheme, define, table); + const checkboxComponent = createCheckbox( + col, + row, + colWidth - iconWidth, + width, + height, + padding, + cellTheme, + define, + table + ); if (checkboxComponent) { cellGroup.appendChild(checkboxComponent); } checkboxComponent.render(); - width -= padding[1] + padding[3]; + width -= padding[1] + padding[3] + iconWidth; height -= padding[0] + padding[2]; if (textAlign === 'center') { - checkboxComponent.setAttribute('x', padding[3] + (width - checkboxComponent.AABBBounds.width()) / 2); + checkboxComponent.setAttribute( + 'x', + padding[3] + cellLeftIconWidth + (width - checkboxComponent.AABBBounds.width()) / 2 + ); } else if (textAlign === 'right') { - checkboxComponent.setAttribute('x', padding[3] + width - checkboxComponent.AABBBounds.width()); + checkboxComponent.setAttribute('x', padding[3] + cellLeftIconWidth + width - checkboxComponent.AABBBounds.width()); } else { - checkboxComponent.setAttribute('x', padding[3]); + checkboxComponent.setAttribute('x', padding[3] + cellLeftIconWidth); } if (textBaseline === 'middle') { @@ -235,3 +298,5 @@ function createCheckbox( return checkbox; } + +export type CreateCheckboxCellGroup = typeof createCheckboxCellGroup; diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/image-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/image-cell.ts index 6fdd4ad66..6cb59600e 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/image-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/image-cell.ts @@ -11,6 +11,8 @@ import { isValid } from '@visactor/vutils'; import { getQuadProps } from '../../utils/padding'; import { getCellBorderStrokeWidth } from '../../utils/cell-border-stroke-width'; import type { BaseTableAPI } from '../../../ts-types/base-table'; +import type { CellRange } from '../../../ts-types'; +import { dealWithIconLayout } from '../../utils/text-icon-layout'; export function createImageCellGroup( columnGroup: Group, @@ -25,8 +27,10 @@ export function createImageCellGroup( padding: [number, number, number, number], textAlign: CanvasTextAlign, textBaseline: CanvasTextBaseline, + mayHaveIcon: boolean, table: BaseTableAPI, cellTheme: IThemeSpec, + range: CellRange | undefined, isAsync: boolean ) { const headerStyle = table._getCellStyle(col, row); // to be fixed @@ -94,12 +98,64 @@ export function createImageCellGroup( columnGroup?.addCellGroup(cellGroup); } + let cellIcons; + if (mayHaveIcon) { + let iconCol = col; + let iconRow = row; + if (range) { + iconCol = range.start.col; + iconRow = range.start.row; + } + cellIcons = table.getCellIcons(iconCol, iconRow); + } + + let iconWidth = 0; + let cellLeftIconWidth = 0; + let cellRightIconWidth = 0; + if (Array.isArray(cellIcons) && cellIcons.length !== 0) { + const { leftIconWidth, rightIconWidth, absoluteLeftIconWidth, absoluteRightIconWidth } = dealWithIconLayout( + cellIcons, + cellGroup, + range, + table + ); + + iconWidth = leftIconWidth + rightIconWidth; + cellLeftIconWidth = leftIconWidth; + cellRightIconWidth = rightIconWidth; + + // 更新各个部分横向位置 + cellGroup.forEachChildren((child: any) => { + if (child.role === 'icon-left') { + child.setAttribute('x', child.attribute.x + padding[3]); + } else if (child.role === 'icon-right') { + child.setAttribute('x', child.attribute.x + width - rightIconWidth - padding[1]); + } else if (child.role === 'icon-absolute-right') { + child.setAttribute('x', child.attribute.x + width - absoluteRightIconWidth - padding[1]); + } + }); + + // 更新各个部分纵向位置 + cellGroup.forEachChildren((child: any) => { + if (textBaseline === 'middle') { + child.setAttribute('y', (height - child.AABBBounds.height()) / 2); + } else if (textBaseline === 'bottom') { + child.setAttribute('y', height - child.AABBBounds.height() - padding[2]); + } else { + child.setAttribute('y', padding[0]); + } + }); + + (cellGroup as any)._cellLeftIconWidth = cellLeftIconWidth; + (cellGroup as any)._cellRightIconWidth = cellRightIconWidth; + } + // image const value = table.getCellValue(col, row); const image: IImage = createImage({ x: padding[3], y: padding[0], - width: width - padding[1] - padding[3], + width: width - padding[1] - padding[3] - iconWidth, height: height - padding[0] - padding[2], image: value, //?? (regedIcons.damage_pic as any).svg, cursor: 'pointer' as Cursor @@ -147,10 +203,10 @@ export function createImageCellGroup( image.resources.has(image.attribute.image) && image.resources.get(image.attribute.image).state === 'success' ) { - updateImageCellContentWhileResize(cellGroup, col, row, table); + updateImageCellContentWhileResize(cellGroup, col, row, 0, 0, table); } else { image.successCallback = () => { - updateImageCellContentWhileResize(cellGroup, col, row, table); + updateImageCellContentWhileResize(cellGroup, col, row, 0, 0, table); }; } } @@ -164,6 +220,8 @@ export function createImageCellGroup( return cellGroup; } +export type CreateImageCellGroup = typeof createImageCellGroup; + /** * 调整某个图片资源所在行列的行高列宽 之后重绘 * @param col @@ -231,7 +289,14 @@ export function _adjustWidthHeight( return false; } -export function updateImageCellContentWhileResize(cellGroup: Group, col: number, row: number, table: BaseTableAPI) { +export function updateImageCellContentWhileResize( + cellGroup: Group, + col: number, + row: number, + deltaX: number, + deltaY: number, + table: BaseTableAPI +) { const image = cellGroup.getChildByName('image') as Image; if (!image) { return; @@ -260,6 +325,9 @@ export function updateImageCellContentWhileResize(cellGroup: Group, col: number, const colEnd = cellGroup.mergeEndCol ?? cellGroup.col; const rowEnd = cellGroup.mergeEndCol ?? cellGroup.row; + const leftIconWidth = (cellGroup as any)._cellLeftIconWidth ?? 0; + const rightIconWidth = (cellGroup as any)._cellRightIconWidth ?? 0; + if ((image as any).keepAspectRatio) { const { width: imageWidth, height: imageHeight } = calcKeepAspectRatioSize( originImage.width || (originImage as any).videoWidth, @@ -302,11 +370,11 @@ export function updateImageCellContentWhileResize(cellGroup: Group, col: number, const cellGroup = table.scenegraph.getCell(col, row); const image = cellGroup.getChildByName('image') as Image; image?.setAttributes({ - x: padding[3], + x: leftIconWidth + padding[3], y: padding[0], // width: cellGroup.attribute.width - padding[1] - padding[3], // height: cellGroup.attribute.height - padding[0] - padding[2] - width: cellWidth - padding[1] - padding[3], + width: cellWidth - padding[1] - padding[3] - rightIconWidth - leftIconWidth, height: cellHeight - padding[0] - padding[2] }); } @@ -341,6 +409,30 @@ export function updateImageCellContentWhileResize(cellGroup: Group, col: number, } } + // 更新x方向位置 + cellGroup.forEachChildren((child: any) => { + if (child.role === 'icon-left') { + // do nothing + } else if (child.role === 'icon-right') { + child.setAttribute('x', child.attribute.x + deltaX); + } else if (child.role === 'icon-absolute-right') { + child.setAttribute('x', child.attribute.x + deltaX); + } + }); + + // 更新y方向位置 + cellGroup.forEachChildren((child: any) => { + if (child.type !== 'rect' && (!child.role || !child.role.startsWith('icon'))) { + // do nothing + } else if (textBaseline === 'middle') { + child.setAttribute('y', padding[0] + (cellHeight - padding[0] - padding[2] - child.AABBBounds.height()) / 2); + } else if (textBaseline === 'bottom') { + child.setAttribute('y', padding[0] + cellHeight - padding[0] - padding[2] - child.AABBBounds.height()); + } else { + child.setAttribute('y', padding[0]); + } + }); + if (isMerge) { updateImageDxDy( cellGroup.mergeStartCol, diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/index.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/index.ts new file mode 100644 index 000000000..36930f41a --- /dev/null +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/index.ts @@ -0,0 +1,43 @@ +import { Factory } from '../../../core/factory'; +import { createChartCellGroup } from './chart-cell'; +import { createCheckboxCellGroup } from './checkbox-cell'; +import { createImageCellGroup } from './image-cell'; +import { createRadioCellGroup } from './radio-cell'; +import { createSparkLineCellGroup } from './spark-line-cell'; +import { createVideoCellGroup } from './video-cell'; +import { createCellGroup as createTextCellGroup } from './text-cell'; +import { createProgressBarCell } from './progress-bar-cell'; +import { getAxisDomainRangeAndLabels } from '../../../layout/chart-helper/get-axis-domain'; + +export const registerChartCell = () => { + Factory.registerFunction('createChartCellGroup', createChartCellGroup); + Factory.registerFunction('getAxisDomainRangeAndLabels', getAxisDomainRangeAndLabels); +}; + +export const registerCheckboxCell = () => { + Factory.registerFunction('createCheckboxCellGroup', createCheckboxCellGroup); +}; + +export const registerImageCell = () => { + Factory.registerFunction('createImageCellGroup', createImageCellGroup); +}; + +export const registerProgressBarCell = () => { + Factory.registerFunction('createProgressBarCell', createProgressBarCell); +}; + +export const registerRadioCell = () => { + Factory.registerFunction('createRadioCellGroup', createRadioCellGroup); +}; + +export const registerSparkLineCell = () => { + Factory.registerFunction('createSparkLineCellGroup', createSparkLineCellGroup); +}; + +export const registerTextCell = () => { + Factory.registerFunction('createTextCellGroup', createTextCellGroup); +}; + +export const registerVideoCell = () => { + Factory.registerFunction('createVideoCellGroup', createVideoCellGroup); +}; diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/progress-bar-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/progress-bar-cell.ts index a1e5bf61f..b19a32ddb 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/progress-bar-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/progress-bar-cell.ts @@ -560,3 +560,5 @@ export function createProgressBarCell( } return percentCompleteBarGroup; } + +export type CreateProgressBarCell = typeof createProgressBarCell; diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts index eb30a4449..e62c97f9a 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts @@ -98,6 +98,8 @@ export function createRadioCellGroup( return cellGroup; } +export type CreateRadioCellGroup = typeof createRadioCellGroup; + function createRadio( col: number, row: number, diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/spark-line-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/spark-line-cell.ts index 58dee58c5..a7fb271f8 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/spark-line-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/spark-line-cell.ts @@ -85,6 +85,8 @@ export function createSparkLineCellGroup( return cellGroup; } +export type CreateSparkLineCellGroup = typeof createSparkLineCellGroup; + function createSparkLine( col: number, row: number, diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts index 901d6afe6..c6cf7f6b3 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts @@ -179,6 +179,8 @@ export function createCellGroup( return cellGroup; } +export type CreateTextCellGroup = typeof createCellGroup; + // /** // * @description: 获取函数式赋值的样式,记录在cellTheme中 // * @param {BaseTableAPI} table diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/video-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/video-cell.ts index c186a8376..e2b024fa1 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/video-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/video-cell.ts @@ -12,6 +12,8 @@ import { isValid } from '@visactor/vutils'; import type { BaseTableAPI } from '../../../ts-types/base-table'; import { getCellBorderStrokeWidth } from '../../utils/cell-border-stroke-width'; import { getQuadProps } from '../../utils/padding'; +import type { CellRange } from '../../../ts-types'; +import { dealWithIconLayout } from '../../utils/text-icon-layout'; const regedIcons = icons.get(); @@ -28,8 +30,10 @@ export function createVideoCellGroup( padding: [number, number, number, number], textAlign: CanvasTextAlign, textBaseline: CanvasTextBaseline, + mayHaveIcon: boolean, table: BaseTableAPI, cellTheme: IThemeSpec, + range: CellRange | undefined, isAsync: boolean ) { const headerStyle = table._getCellStyle(col, row); // to be fixed @@ -97,6 +101,58 @@ export function createVideoCellGroup( columnGroup?.addCellGroup(cellGroup); } + let cellIcons; + if (mayHaveIcon) { + let iconCol = col; + let iconRow = row; + if (range) { + iconCol = range.start.col; + iconRow = range.start.row; + } + cellIcons = table.getCellIcons(iconCol, iconRow); + } + + let iconWidth = 0; + let cellLeftIconWidth = 0; + let cellRightIconWidth = 0; + if (Array.isArray(cellIcons) && cellIcons.length !== 0) { + const { leftIconWidth, rightIconWidth, absoluteLeftIconWidth, absoluteRightIconWidth } = dealWithIconLayout( + cellIcons, + cellGroup, + range, + table + ); + + iconWidth = leftIconWidth + rightIconWidth; + cellLeftIconWidth = leftIconWidth; + cellRightIconWidth = rightIconWidth; + + // 更新各个部分横向位置 + cellGroup.forEachChildren((child: any) => { + if (child.role === 'icon-left') { + child.setAttribute('x', child.attribute.x + padding[3]); + } else if (child.role === 'icon-right') { + child.setAttribute('x', child.attribute.x + width - rightIconWidth - padding[1]); + } else if (child.role === 'icon-absolute-right') { + child.setAttribute('x', child.attribute.x + width - absoluteRightIconWidth - padding[1]); + } + }); + + // 更新各个部分纵向位置 + cellGroup.forEachChildren((child: any) => { + if (textBaseline === 'middle') { + child.setAttribute('y', (height - child.AABBBounds.height()) / 2); + } else if (textBaseline === 'bottom') { + child.setAttribute('y', height - child.AABBBounds.height() - padding[2]); + } else { + child.setAttribute('y', padding[0]); + } + }); + + (cellGroup as any)._cellLeftIconWidth = cellLeftIconWidth; + (cellGroup as any)._cellRightIconWidth = cellRightIconWidth; + } + // video const value = table.getCellValue(col, row); const video = document.createElement('video'); @@ -206,3 +262,5 @@ export function createVideoCellGroup( return cellGroup; } + +export type CreateVideoCellGroup = typeof createVideoCellGroup; diff --git a/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts b/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts index 0755fa15f..b251d645e 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts @@ -76,7 +76,7 @@ export function createGroupForFirstScreen( 0, // colStart table.frozenColCount - 1, // colEnd 0, // rowStart - table.columnHeaderLevelCount - 1, // rowEnd + table.frozenRowCount - 1, // rowEnd table.isListTable() ? 'columnHeader' : 'cornerHeader', // CellType table ); @@ -91,7 +91,7 @@ export function createGroupForFirstScreen( // Math.min(proxy.firstScreenColLimit, table.colCount - 1 - table.rightFrozenColCount), // colEnd distCol - table.rightFrozenColCount, 0, // rowStart - table.columnHeaderLevelCount - 1, // rowEnd + table.frozenRowCount - 1, // rowEnd 'columnHeader', // isHeader table ); @@ -105,7 +105,7 @@ export function createGroupForFirstScreen( yOrigin, 0, // colStart table.leftRowSeriesNumberCount - 1, // colEnd - table.columnHeaderLevelCount, // rowStart + table.frozenRowCount, // rowStart // Math.min(proxy.firstScreenRowLimit, table.rowCount - 1 - table.bottomFrozenRowCount), // rowEnd distRow - table.bottomFrozenRowCount, 'rowHeader', // isHeader @@ -119,7 +119,7 @@ export function createGroupForFirstScreen( yOrigin, table.leftRowSeriesNumberCount, // colStart table.leftRowSeriesNumberCount + table.rowHeaderLevelCount - 1, // colEnd - table.columnHeaderLevelCount, // rowStart + table.frozenRowCount, // rowStart // Math.min(proxy.firstScreenRowLimit, table.rowCount - 1 - table.bottomFrozenRowCount), // rowEnd distRow - table.bottomFrozenRowCount, 'rowHeader', // isHeader @@ -133,7 +133,7 @@ export function createGroupForFirstScreen( yOrigin, table.rowHeaderLevelCount + table.leftRowSeriesNumberCount, // colStart table.frozenColCount - 1, // colEnd - table.columnHeaderLevelCount, // rowStart + table.frozenRowCount, // rowStart // Math.min(proxy.firstScreenRowLimit, table.rowCount - 1 - table.bottomFrozenRowCount), // rowEnd distRow - table.bottomFrozenRowCount, 'body', @@ -240,7 +240,7 @@ export function createGroupForFirstScreen( table.colCount - 1 - table.rightFrozenColCount + 1, // colStart table.colCount - 1, // colEnd 0, // rowStart - table.columnHeaderLevelCount - 1, // rowEnd + table.frozenRowCount - 1, // rowEnd 'columnHeader', // isHeader table ); @@ -252,7 +252,7 @@ export function createGroupForFirstScreen( yOrigin, table.colCount - 1 - table.rightFrozenColCount + 1, // colStart table.colCount - 1, // colEnd - table.columnHeaderLevelCount, // rowStart + table.frozenRowCount, // rowStart // Math.min(proxy.firstScreenRowLimit, table.rowCount - 1 - table.bottomFrozenRowCount), // rowEnd distRow - table.bottomFrozenRowCount, table.isPivotChart() ? 'rowHeader' : 'body', // isHeader @@ -284,7 +284,7 @@ export function createGroupForFirstScreen( table.frozenColCount, // colStart // Math.min(proxy.firstScreenColLimit, table.colCount - 1 - table.rightFrozenColCount), // colEnd distCol - table.rightFrozenColCount, - table.columnHeaderLevelCount, // rowStart + table.frozenRowCount, // rowStart // Math.min(proxy.firstScreenRowLimit, table.rowCount - 1 - table.bottomFrozenRowCount), // rowEnd distRow - table.bottomFrozenRowCount, 'body', // isHeader diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index d52993b6a..be6b44e8f 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -131,7 +131,7 @@ export class SceneProxy { } setParamsForRow() { - this.bodyTopRow = this.table.columnHeaderLevelCount; + this.bodyTopRow = this.table.frozenRowCount; this.bodyBottomRow = this.table.rowCount - 1 - this.table.bottomFrozenRowCount; // this.bodyLeftCol = 0; // this.bodyRightCol = this.table.colCount - 1 - this.table.rightFrozenColCount; @@ -370,7 +370,7 @@ export class SceneProxy { } // create column - if (this.table.columnHeaderLevelCount) { + if (this.table.frozenRowCount) { // create colGroup const lastColumnGroup = ( this.table.scenegraph.colHeaderGroup.lastChild instanceof Group @@ -387,7 +387,7 @@ export class SceneProxy { this.currentCol + 1, // colStart endCol, // colEnd 0, // rowStart - this.table.columnHeaderLevelCount - 1, // rowEnd + this.table.frozenRowCount - 1, // rowEnd 'columnHeader', // isHeader this.table ); @@ -704,7 +704,7 @@ export class SceneProxy { // } if ( - row >= this.table.columnHeaderLevelCount && // not column header + row >= this.table.frozenRowCount && // not column header row < this.table.rowCount - this.table.bottomFrozenRowCount && // not bottom frozen (row < this.rowStart || row > this.rowEnd) // not in proxy row range ) { diff --git a/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-horizontal.ts b/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-horizontal.ts index ce6cdb98d..f52e87eb2 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-horizontal.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-horizontal.ts @@ -14,6 +14,13 @@ export async function sortHorizontal(proxy: SceneProxy) { }); } }); + + // 更新同步范围 + const syncLeftCol = Math.max(proxy.bodyLeftCol, proxy.screenLeftCol - proxy.screenColCount * 1); + const syncRightCol = Math.min(proxy.bodyRightCol, proxy.screenLeftCol + proxy.screenColCount * 2); + + computeColsWidth(proxy.table, syncLeftCol, syncRightCol); + for (let col = proxy.colStart; col <= proxy.colEnd; col++) { // 将该列的chartInstance清除掉 const columnGroup = proxy.table.scenegraph.getColGroup(col); @@ -33,11 +40,6 @@ export async function sortHorizontal(proxy: SceneProxy) { proxy.table.scenegraph.updateCellContent(col, row); } } - // 更新同步范围 - const syncLeftCol = Math.max(proxy.bodyLeftCol, proxy.screenLeftCol - proxy.screenColCount * 1); - const syncRightCol = Math.min(proxy.bodyRightCol, proxy.screenLeftCol + proxy.screenColCount * 2); - - computeColsWidth(proxy.table, syncLeftCol, syncRightCol); updateColContent(syncLeftCol, syncRightCol, proxy); diff --git a/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-vertical.ts b/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-vertical.ts index 1dd54ec48..6f8a21d3d 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-vertical.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/update-position/sort-vertical.ts @@ -15,6 +15,28 @@ export async function sortVertical(proxy: SceneProxy) { } }); + // 更新同步范围 + let syncTopRow; + let syncBottomRow; + if (proxy.table.heightMode === 'autoHeight') { + syncTopRow = proxy.rowStart; + syncBottomRow = proxy.rowEnd; + } else { + syncTopRow = Math.max(proxy.bodyTopRow, proxy.screenTopRow - proxy.screenRowCount * 1); + syncBottomRow = Math.min(proxy.bodyBottomRow, proxy.screenTopRow + proxy.screenRowCount * 2); + } + // console.log('sort更新同步范围', syncTopRow, syncBottomRow); + + const oldBodyHeight = proxy.table.getAllRowsHeight(); + + computeRowsHeight(proxy.table, syncTopRow, syncBottomRow); + + const newBodyHeight = proxy.table.getAllRowsHeight(); + + if (oldBodyHeight !== newBodyHeight) { + proxy.table.scenegraph.updateContainerHeight(proxy.table.frozenRowCount, newBodyHeight - oldBodyHeight); + } + for (let col = 0; col < proxy.table.frozenColCount ?? 0; col++) { // 将该列的chartInstance清除掉 const columnGroup = proxy.table.scenegraph.getColGroup(col); @@ -46,28 +68,6 @@ export async function sortVertical(proxy: SceneProxy) { } } - // 更新同步范围 - let syncTopRow; - let syncBottomRow; - if (proxy.table.heightMode === 'autoHeight') { - syncTopRow = proxy.rowStart; - syncBottomRow = proxy.rowEnd; - } else { - syncTopRow = Math.max(proxy.bodyTopRow, proxy.screenTopRow - proxy.screenRowCount * 1); - syncBottomRow = Math.min(proxy.bodyBottomRow, proxy.screenTopRow + proxy.screenRowCount * 2); - } - // console.log('sort更新同步范围', syncTopRow, syncBottomRow); - - const oldBodyHeight = proxy.table.getAllRowsHeight(); - - computeRowsHeight(proxy.table, syncTopRow, syncBottomRow); - - const newBodyHeight = proxy.table.getAllRowsHeight(); - - if (oldBodyHeight !== newBodyHeight) { - proxy.table.scenegraph.updateContainerHeight(proxy.table.frozenRowCount, newBodyHeight - oldBodyHeight); - } - updateRowContent(syncTopRow, syncBottomRow, proxy); if (proxy.table.heightMode === 'autoHeight') { diff --git a/packages/vtable/src/scenegraph/group-creater/progress/update-position/update-auto-row.ts b/packages/vtable/src/scenegraph/group-creater/progress/update-position/update-auto-row.ts index 051041227..dda7e6aff 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/update-position/update-auto-row.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/update-position/update-auto-row.ts @@ -34,7 +34,7 @@ export function updateAutoRow( } } else { // 估计位置 - y = table.getRowsHeight(table.columnHeaderLevelCount, cellGroup.row - 1); + y = table.getRowsHeight(table.frozenRowCount, cellGroup.row - 1); } if (isValid(y)) { cellGroup.setAttribute('y', y); @@ -61,8 +61,8 @@ export function updateAutoRow( } } else { // 估计位置 - y = table.getRowsHeight(table.columnHeaderLevelCount, cellGroup.row - 1); - // console.log('估计位置', table.getRowsHeight(table.columnHeaderLevelCount, cellGroup.row)); + y = table.getRowsHeight(table.frozenRowCount, cellGroup.row - 1); + // console.log('估计位置', table.getRowsHeight(table.frozenRowCount, cellGroup.row)); } if (isValid(y)) { cellGroup.setAttribute('y', y); @@ -77,11 +77,8 @@ export function updateAutoRow( table.scenegraph.proxy.bodyBottomRow - table.scenegraph.proxy.bodyTopRow + 1 ); // 渐进加载总row数量 - const totalBodyHeight = table.getRowsHeight( - table.columnHeaderLevelCount, - table.columnHeaderLevelCount + totalActualBodyRowCount - ); - const totalHeight = table.getRowsHeight(table.columnHeaderLevelCount, table.rowCount - 1); + const totalBodyHeight = table.getRowsHeight(table.frozenRowCount, table.frozenRowCount + totalActualBodyRowCount); + const totalHeight = table.getRowsHeight(table.frozenRowCount, table.rowCount - 1); table.scenegraph.proxy.yLimitTop = totalBodyHeight / 2; table.scenegraph.proxy.yLimitBottom = totalHeight - totalBodyHeight / 2; diff --git a/packages/vtable/src/scenegraph/group-creater/progress/update-position/util.ts b/packages/vtable/src/scenegraph/group-creater/progress/update-position/util.ts index 3e510473b..79788d12f 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/update-position/util.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/update-position/util.ts @@ -28,10 +28,7 @@ export function checkFirstRowMerge(row: number, proxy: SceneProxy) { newCellGroup.col = col; newCellGroup.row = row; - newCellGroup.setAttribute( - 'y', - proxy.table.getRowsHeight(proxy.table.columnHeaderLevelCount, range.start.row - 1) - ); + newCellGroup.setAttribute('y', proxy.table.getRowsHeight(proxy.table.frozenRowCount, range.start.row - 1)); oldCellGroup.parent.insertAfter(newCellGroup, oldCellGroup); oldCellGroup.parent.removeChild(oldCellGroup); @@ -50,7 +47,7 @@ export function checkFirstRowMerge(row: number, proxy: SceneProxy) { export function checkFirstColMerge(col: number, proxy: SceneProxy) { for (let row = 0; row < proxy.table.rowCount; row++) { if ( - (row >= proxy.table.columnHeaderLevelCount && row < proxy.rowStart) || + (row >= proxy.table.frozenRowCount && row < proxy.rowStart) || (row > proxy.rowEnd && row < proxy.table.rowCount - proxy.table.bottomFrozenRowCount) ) { continue; @@ -104,7 +101,7 @@ function clearHadMergedRow(rowStart: number, rowEnd: number, col: number, proxy: cellGroup.setAttributes({ width: 0, height: proxy.table.getRowHeight(cellGroup.row), - y: proxy.table.getRowsHeight(proxy.table.columnHeaderLevelCount, cellGroup.row - 1), + y: proxy.table.getRowsHeight(proxy.table.frozenRowCount, cellGroup.row - 1), x: 0 }); cellGroup.clear(); diff --git a/packages/vtable/src/scenegraph/layout/compute-col-width.ts b/packages/vtable/src/scenegraph/layout/compute-col-width.ts index 2b828463c..f7031bad5 100644 --- a/packages/vtable/src/scenegraph/layout/compute-col-width.ts +++ b/packages/vtable/src/scenegraph/layout/compute-col-width.ts @@ -14,8 +14,8 @@ import { getQuadProps } from '../utils/padding'; import { getProp } from '../utils/get-prop'; import type { BaseTableAPI, HeaderData } from '../../ts-types/base-table'; import type { PivotHeaderLayoutMap } from '../../layout/pivot-header-layout'; -import { getAxisConfigInPivotChart } from '../../layout/chart-helper/get-axis-config'; -import { computeAxisComponentWidth } from '../../components/axis/get-axis-component-size'; +import type { ComputeAxisComponentWidth } from '../../components/axis/get-axis-component-size'; +import { Factory } from '../../core/factory'; import { Group as VGroup } from '@src/vrender'; import { isArray, isFunction, isNumber, isObject, isValid } from '@visactor/vutils'; import { decodeReactDom, dealPercentCalc } from '../component/custom'; @@ -301,8 +301,9 @@ function computeAutoColWidth( // 判断透视图轴组件 if (table.isPivotChart()) { const layout = table.internalProps.layoutMap as PivotHeaderLayoutMap; - const axisConfig = getAxisConfigInPivotChart(col, row, layout); + const axisConfig = layout.getAxisConfigInPivotChart(col, row); if (axisConfig) { + const computeAxisComponentWidth: ComputeAxisComponentWidth = Factory.getFunction('computeAxisComponentWidth'); const axisWidth = computeAxisComponentWidth(axisConfig, table); if (typeof axisWidth === 'number') { maxWidth = Math.max(axisWidth, maxWidth); diff --git a/packages/vtable/src/scenegraph/layout/compute-row-height.ts b/packages/vtable/src/scenegraph/layout/compute-row-height.ts index 6ddd91d55..8ae8a201f 100644 --- a/packages/vtable/src/scenegraph/layout/compute-row-height.ts +++ b/packages/vtable/src/scenegraph/layout/compute-row-height.ts @@ -8,8 +8,8 @@ import type { ColumnData, ColumnDefine, TextColumnDefine } from '../../ts-types/ import { getProp } from '../utils/get-prop'; import { getQuadProps } from '../utils/padding'; import { dealWithRichTextIcon } from '../utils/text-icon-layout'; -import { getAxisConfigInPivotChart } from '../../layout/chart-helper/get-axis-config'; -import { computeAxisComponentHeight } from '../../components/axis/get-axis-component-size'; +import type { ComputeAxisComponentHeight } from '../../components/axis/get-axis-component-size'; +import { Factory } from '../../core/factory'; import { isArray, isFunction, isNumber, isObject, isValid } from '@visactor/vutils'; import { CheckBox } from '@visactor/vrender-components'; import { decodeReactDom, dealPercentCalc } from '../component/custom'; @@ -351,8 +351,10 @@ export function computeRowHeight(row: number, startCol: number, endCol: number, // Axis component height calculation if (table.isPivotChart()) { const layout = table.internalProps.layoutMap as PivotHeaderLayoutMap; - const axisConfig = getAxisConfigInPivotChart(col, row, layout); + const axisConfig = layout.getAxisConfigInPivotChart(col, row); if (axisConfig) { + const computeAxisComponentHeight: ComputeAxisComponentHeight = + Factory.getFunction('computeAxisComponentHeight'); const axisWidth = computeAxisComponentHeight(axisConfig, table); if (typeof axisWidth === 'number') { maxHeight = isValid(maxHeight) ? Math.max(axisWidth, maxHeight) : axisWidth; diff --git a/packages/vtable/src/scenegraph/layout/update-height.ts b/packages/vtable/src/scenegraph/layout/update-height.ts index 25e30f1e0..89be8e15b 100644 --- a/packages/vtable/src/scenegraph/layout/update-height.ts +++ b/packages/vtable/src/scenegraph/layout/update-height.ts @@ -1,7 +1,7 @@ import type { ProgressBarStyle } from '../../body-helper/style/ProgressBarStyle'; import type { Group } from '../graphic/group'; -import { createProgressBarCell } from '../group-creater/cell-type/progress-bar-cell'; -import { createSparkLineCellGroup } from '../group-creater/cell-type/spark-line-cell'; +import type { CreateProgressBarCell } from '../group-creater/cell-type/progress-bar-cell'; +import type { CreateSparkLineCellGroup } from '../group-creater/cell-type/spark-line-cell'; import type { Scenegraph } from '../scenegraph'; import { getCellMergeInfo } from '../utils/get-cell-merge'; import { getProp } from '../utils/get-prop'; @@ -17,6 +17,7 @@ import { resizeCellGroup, getCustomCellMergeCustom } from '../group-creater/cell import type { IGraphic } from '@src/vrender'; import { getCellMergeRange } from '../../tools/merge-range'; import type { ColumnDefine } from '../../ts-types'; +import { Factory } from '../../core/factory'; export function updateRowHeight(scene: Scenegraph, row: number, detaY: number, skipTableHeightMap?: boolean) { // 更新table行高存储 @@ -44,13 +45,13 @@ export function updateRowHeight(scene: Scenegraph, row: number, detaY: number, s let rowStart = 0; let rowEnd = 0; // 更新header 高度 - if (row < scene.table.columnHeaderLevelCount) { + if (row < scene.table.frozenRowCount) { // scene.colHeaderGroup.setAttribute('height', scene.colHeaderGroup.attribute.height + detaY); // scene.rowHeaderGroup.setAttribute('y', scene.rowHeaderGroup.attribute.y + detaY); // scene.bodyGroup.setAttribute('y', scene.bodyGroup.attribute.y + detaY); rowStart = row + 1; - rowEnd = scene.table.columnHeaderLevelCount - 1; + rowEnd = scene.table.frozenRowCount - 1; } else if (row >= scene.table.rowCount - scene.table.bottomFrozenRowCount) { rowStart = row + 1; rowEnd = scene.table.rowCount - 1; @@ -139,6 +140,7 @@ export function updateCellHeight( const dataValue = scene.table.getCellOriginValue(col, row); const padding = getQuadProps(getProp('padding', style, col, row, scene.table)); + const createProgressBarCell = Factory.getFunction('createProgressBarCell') as CreateProgressBarCell; const newBarCell = createProgressBarCell( columnDefine, style, @@ -163,6 +165,7 @@ export function updateCellHeight( cell.removeAllChild(); const headerStyle = scene.table._getCellStyle(col, row); const padding = getQuadProps(getProp('padding', headerStyle, col, row, scene.table)); + const createSparkLineCellGroup = Factory.getFunction('createSparkLineCellGroup') as CreateSparkLineCellGroup; createSparkLineCellGroup( cell, cell.parent, @@ -178,7 +181,7 @@ export function updateCellHeight( false ); } else if (type === 'image' || type === 'video') { - updateImageCellContentWhileResize(cell, col, row, scene.table); + updateImageCellContentWhileResize(cell, col, row, 0, detaY, scene.table); } else if (cell.firstChild?.name === 'axis') { (cell.firstChild as any)?.originAxis.resize(cell.attribute.width, cell.attribute.height); } else { diff --git a/packages/vtable/src/scenegraph/layout/update-width.ts b/packages/vtable/src/scenegraph/layout/update-width.ts index cb8199392..81b480558 100644 --- a/packages/vtable/src/scenegraph/layout/update-width.ts +++ b/packages/vtable/src/scenegraph/layout/update-width.ts @@ -1,14 +1,15 @@ import type { IGraphic } from '@src/vrender'; import type { ProgressBarStyle } from '../../body-helper/style/ProgressBarStyle'; -import { CartesianAxis } from '../../components/axis/axis'; +import type { ICartesianAxis } from '../../components/axis/axis'; +import { Factory } from '../../core/factory'; import { getStyleTheme } from '../../core/tableHelper'; import type { BaseTableAPI, HeaderData } from '../../ts-types/base-table'; import type { IProgressbarColumnBodyDefine } from '../../ts-types/list-table/define/progressbar-define'; import { CUSTOM_CONTAINER_NAME, CUSTOM_MERGE_CONTAINER_NAME, dealWithCustom } from '../component/custom'; import type { Group } from '../graphic/group'; import { updateImageCellContentWhileResize } from '../group-creater/cell-type/image-cell'; -import { createProgressBarCell } from '../group-creater/cell-type/progress-bar-cell'; -import { createSparkLineCellGroup } from '../group-creater/cell-type/spark-line-cell'; +import type { CreateProgressBarCell } from '../group-creater/cell-type/progress-bar-cell'; +import type { CreateSparkLineCellGroup } from '../group-creater/cell-type/spark-line-cell'; import { resizeCellGroup, getCustomCellMergeCustom } from '../group-creater/cell-helper'; import type { Scenegraph } from '../scenegraph'; import { getCellMergeInfo } from '../utils/get-cell-merge'; @@ -278,6 +279,7 @@ function updateCellWidth( const dataValue = scene.table.getCellOriginValue(col, row); const padding = getQuadProps(getProp('padding', style, col, row, scene.table)); + const createProgressBarCell = Factory.getFunction('createProgressBarCell') as CreateProgressBarCell; const newBarCell = createProgressBarCell( columnDefine, style, @@ -302,6 +304,7 @@ function updateCellWidth( cellGroup.removeAllChild(); const headerStyle = scene.table._getCellStyle(col, row); const padding = getQuadProps(getProp('padding', headerStyle, col, row, scene.table)); + const createSparkLineCellGroup = Factory.getFunction('createSparkLineCellGroup') as CreateSparkLineCellGroup; createSparkLineCellGroup( cellGroup, cellGroup.parent, @@ -320,13 +323,14 @@ function updateCellWidth( // // 只更新背景边框 // const rect = cell.firstChild as Rect; // rect.setAttribute('width', cell.attribute.width); - updateImageCellContentWhileResize(cellGroup, col, row, scene.table); + updateImageCellContentWhileResize(cellGroup, col, row, detaX, 0, scene.table); } else if (cellGroup.firstChild?.name === 'axis') { // recreate axis component const axisConfig = scene.table.internalProps.layoutMap.getAxisConfigInPivotChart(col, row); const cellStyle = scene.table._getCellStyle(col, row); const padding = getQuadProps(getProp('padding', cellStyle, col, row, scene.table)); if (axisConfig) { + const CartesianAxis: ICartesianAxis = Factory.getComponent('axis'); const axis = new CartesianAxis( axisConfig, cellGroup.attribute.width, diff --git a/packages/vtable/src/scenegraph/refresh-node/update-chart.ts b/packages/vtable/src/scenegraph/refresh-node/update-chart.ts index 3f4b06281..d302c3780 100644 --- a/packages/vtable/src/scenegraph/refresh-node/update-chart.ts +++ b/packages/vtable/src/scenegraph/refresh-node/update-chart.ts @@ -1,6 +1,7 @@ import { isEqual } from '@visactor/vutils'; import type { PivotChart } from '../../PivotChart'; -import { CartesianAxis } from '../../components/axis/axis'; +import type { ICartesianAxis } from '../../components/axis/axis'; +import { Factory } from '../../core/factory'; import type { BaseTableAPI } from '../../ts-types/base-table'; import type { Chart } from '../graphic/chart'; import type { Group } from '../graphic/group'; @@ -209,6 +210,7 @@ function updateTableAxes(containerGroup: Group, table: BaseTableAPI) { const axisConfig = table.internalProps.layoutMap.getAxisConfigInPivotChart(cell.col, cell.row); const cellStyle = table._getCellStyle(cell.col, cell.row); const padding = getQuadProps(getProp('padding', cellStyle, cell.col, cell.row, table)); + const CartesianAxis: ICartesianAxis = Factory.getComponent('axis'); const axis = new CartesianAxis(axisConfig, cell.attribute.width, cell.attribute.height, padding, table); cell.clear(); cell.appendChild(axis.component); diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 3ab8bdcaf..f144e14ab 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -72,7 +72,7 @@ import { dealWithAnimationAppear } from './animation/appear'; registerForVrender(); // VChart poptip theme -loadPoptip(); +// loadPoptip(); container.load(splitModule); container.load(textMeasureModule); // container.load(renderServiceModule); @@ -370,7 +370,7 @@ export class Scenegraph { this.clear = false; // this.frozenColCount = this.table.rowHeaderLevelCount; this.frozenColCount = this.table.frozenColCount; - this.frozenRowCount = this.table.columnHeaderLevelCount; + this.frozenRowCount = this.table.frozenRowCount; this.proxy = new SceneProxy(this.table); @@ -1544,7 +1544,7 @@ export class Scenegraph { const type = this.table.getBodyColumnType(col, row); const cellGroup = this.getCell(col, row); if (type === 'image' || type === 'video') { - updateImageCellContentWhileResize(cellGroup, col, row, this.table); + updateImageCellContentWhileResize(cellGroup, col, row, 0, 0, this.table); } } @@ -1619,7 +1619,7 @@ export class Scenegraph { const drawRange = this.table.getDrawRange(); if (abstractY >= drawRange.top && abstractY <= drawRange.bottom) { // to do: 处理最后一列外调整列宽 - cell = this.table.getCellAt(abstractX - offset, abstractY); + cell = this.table.getCellAtRelativePosition(abstractX - offset, abstractY); return cell; } return { col: -1, row: -1 }; @@ -1905,12 +1905,12 @@ export class Scenegraph { } getCellGroupY(row: number) { - if (row < this.table.columnHeaderLevelCount) { + if (row < this.table.frozenRowCount) { // column header return this.table.getRowsHeight(0, row - 1); } else if (row < this.table.rowCount - this.table.bottomFrozenRowCount) { // body - return this.table.getRowsHeight(this.table.columnHeaderLevelCount, row - 1); + return this.table.getRowsHeight(this.table.frozenRowCount, row - 1); } else if (row < this.table.rowCount) { // bottom frozen return this.table.getRowsHeight(this.table.rowCount - this.table.bottomFrozenRowCount, row - 1); diff --git a/packages/vtable/src/scenegraph/select/update-select-border.ts b/packages/vtable/src/scenegraph/select/update-select-border.ts index e27992e3b..1385309f9 100644 --- a/packages/vtable/src/scenegraph/select/update-select-border.ts +++ b/packages/vtable/src/scenegraph/select/update-select-border.ts @@ -21,6 +21,7 @@ function updateComponent( key: string, scene: Scenegraph ) { + const table = scene.table; const [startColStr, startRowStr, endColStr, endRowStr] = key.split('-'); const startCol = parseInt(startColStr, 10); const startRow = parseInt(startRowStr, 10); @@ -35,14 +36,14 @@ function updateComponent( let visibleCellRange; switch (selectComp.role) { case 'rowHeader': - visibleCellRange = scene.table.getBodyVisibleRowRange(); + visibleCellRange = table.getBodyVisibleRowRange(); if (visibleCellRange) { computeRectCellRangeStartRow = Math.max(startRow, visibleCellRange.rowStart - 1); computeRectCellRangeEndRow = Math.min(endRow, visibleCellRange.rowEnd + 1); } break; case 'columnHeader': - visibleCellRange = scene.table.getBodyVisibleCellRange(); + visibleCellRange = table.getBodyVisibleCellRange(); if (visibleCellRange) { computeRectCellRangeStartCol = Math.max(startCol, visibleCellRange.colStart - 1); computeRectCellRangeEndCol = Math.min(endCol, visibleCellRange.colEnd + 1); @@ -51,14 +52,14 @@ function updateComponent( case 'cornerHeader': break; case 'bottomFrozen': - visibleCellRange = scene.table.getBodyVisibleCellRange(); + visibleCellRange = table.getBodyVisibleCellRange(); if (visibleCellRange) { computeRectCellRangeStartCol = Math.max(startCol, visibleCellRange.colStart - 1); computeRectCellRangeEndCol = Math.min(endCol, visibleCellRange.colEnd + 1); } break; case 'rightFrozen': - visibleCellRange = scene.table.getBodyVisibleCellRange(); + visibleCellRange = table.getBodyVisibleCellRange(); if (visibleCellRange) { computeRectCellRangeStartRow = Math.max(startRow, visibleCellRange.rowStart - 1); computeRectCellRangeEndRow = Math.min(endRow, visibleCellRange.rowEnd + 1); @@ -71,7 +72,7 @@ function updateComponent( case 'rightBottomCorner': break; default: - visibleCellRange = scene.table.getBodyVisibleCellRange(); + visibleCellRange = table.getBodyVisibleCellRange(); if (visibleCellRange) { computeRectCellRangeStartRow = Math.max(startRow, visibleCellRange.rowStart - 1); computeRectCellRangeEndRow = Math.min(endRow, visibleCellRange.rowEnd + 1); @@ -80,11 +81,11 @@ function updateComponent( } break; } - // const cellRange = scene.table.getCellRange(startCol, startRow); - // const colsWidth = scene.table.getColsWidth(cellRange.start.col, endCol); - // const rowsHeight = scene.table.getRowsHeight(cellRange.start.row, endRow); - const colsWidth = scene.table.getColsWidth(computeRectCellRangeStartCol, computeRectCellRangeEndCol); - const rowsHeight = scene.table.getRowsHeight(computeRectCellRangeStartRow, computeRectCellRangeEndRow); + // const cellRange = table.getCellRange(startCol, startRow); + // const colsWidth = table.getColsWidth(cellRange.start.col, endCol); + // const rowsHeight = table.getRowsHeight(cellRange.start.row, endRow); + const colsWidth = table.getColsWidth(computeRectCellRangeStartCol, computeRectCellRangeEndCol); + const rowsHeight = table.getRowsHeight(computeRectCellRangeStartRow, computeRectCellRangeEndRow); const firstCellBound = scene.highPerformanceGetCell( computeRectCellRangeStartCol, computeRectCellRangeStartRow @@ -102,7 +103,7 @@ function updateComponent( visible: true }); if (selectComp.fillhandle) { - selectComp.fillhandle.setAttributes({ + selectComp.fillhandle?.setAttributes({ x: lastCellBound.x2 - scene.tableGroup.attribute.x - 3, // 调整小方块位置 y: lastCellBound.y2 - scene.tableGroup.attribute.y - 3, // 调整小方块位置 width: 6, @@ -112,14 +113,42 @@ function updateComponent( } //#region 判断是不是按着表头部分的选中框 因为绘制层级的原因 线宽会被遮住一半,因此需要动态调整层级 - const isNearRowHeader = scene.table.frozenColCount ? startCol === scene.table.frozenColCount : false; - const isNearRightRowHeader = scene.table.rightFrozenColCount - ? scene.table.rightFrozenColCount > 0 && endCol === scene.table.colCount - scene.table.rightFrozenColCount - 1 + let isNearRowHeader = table.frozenColCount ? startCol === table.frozenColCount : false; + if (!isNearRowHeader && table.frozenColCount && table.scrollLeft > 0 && startCol >= table.frozenColCount) { + const startColRelativePosition = table.getColsWidth(0, startCol - 1) - table.scrollLeft; + if (startColRelativePosition < table.getFrozenColsWidth()) { + isNearRowHeader = true; + } + } + + let isNearRightRowHeader = table.rightFrozenColCount + ? table.rightFrozenColCount > 0 && endCol === table.colCount - table.rightFrozenColCount - 1 : false; - const isNearColHeader = scene.table.frozenRowCount ? startRow === scene.table.frozenRowCount : true; - const isNearBottomColHeader = scene.table.bottomFrozenRowCount - ? endRow === scene.table.rowCount - scene.table.bottomFrozenRowCount - 1 + if (!isNearRightRowHeader && table.rightFrozenColCount && endCol < table.colCount - table.rightFrozenColCount) { + const endColRelativePosition = table.getColsWidth(0, endCol) - table.scrollLeft; + if (endColRelativePosition > table.tableNoFrameWidth - table.getRightFrozenColsWidth()) { + isNearRightRowHeader = true; + } + } + + let isNearColHeader = table.frozenRowCount ? startRow === table.frozenRowCount : true; + if (!isNearColHeader && table.frozenRowCount && table.scrollTop > 0 && startRow >= table.frozenRowCount) { + const startRowRelativePosition = table.getRowsHeight(0, startRow - 1) - table.scrollTop; + if (startRowRelativePosition < table.getFrozenRowsHeight()) { + isNearColHeader = true; + } + } + + let isNearBottomColHeader = table.bottomFrozenRowCount + ? endRow === table.rowCount - table.bottomFrozenRowCount - 1 : false; + if (!isNearBottomColHeader && table.bottomFrozenRowCount && endRow < table.rowCount - table.bottomFrozenRowCount) { + const endRowRelativePosition = table.getRowsHeight(0, endRow) - table.scrollTop; + if (endRowRelativePosition > table.tableNoFrameHeight - table.getBottomFrozenRowsHeight()) { + isNearBottomColHeader = true; + } + } + if ( (isNearRowHeader && selectComp.rect.attribute.stroke[3]) || (isNearRightRowHeader && selectComp.rect.attribute.stroke[1]) || @@ -171,24 +200,23 @@ function updateComponent( //#region 调整层级后 滚动情况下会出现绘制范围出界 如body的选中框 渲染在了rowheader上面,所有需要调整选中框rect的 边界 if ( - selectComp.rect.attribute.x < scene.table.getFrozenColsWidth() && + selectComp.rect.attribute.x < table.getFrozenColsWidth() && // selectComp.rect.attribute.x + selectComp.rect.attribute.width > scene.rowHeaderGroup.attribute.width && - scene.table.scrollLeft > 0 && + table.scrollLeft > 0 && (selectComp.role === 'body' || selectComp.role === 'columnHeader' || selectComp.role === 'bottomFrozen') ) { - const width = selectComp.rect.attribute.width - (scene.table.getFrozenColsWidth() - selectComp.rect.attribute.x); + const width = selectComp.rect.attribute.width - (table.getFrozenColsWidth() - selectComp.rect.attribute.x); selectComp.rect.setAttributes({ - x: selectComp.rect.attribute.x + (scene.table.getFrozenColsWidth() - selectComp.rect.attribute.x), + x: selectComp.rect.attribute.x + (table.getFrozenColsWidth() - selectComp.rect.attribute.x), width: width > 0 ? width : 0 }); - // selectComp.fillhandle.setAttributes({ - // x: selectComp.rect.attribute.x + (scene.table.getFrozenColsWidth() - selectComp.rect.attribute.x), - // width: width > 0 ? width : 0 - // }); + selectComp.fillhandle?.setAttributes({ + visible: width > 0 + }); } if ( // selectComp.rect.attribute.x < scene.rightFrozenGroup.attribute.x && - scene.table.getRightFrozenColsWidth() > 0 && // right冻结列存在的情况下 + table.getRightFrozenColsWidth() > 0 && // right冻结列存在的情况下 scene.rightFrozenGroup.attribute.height > 0 && selectComp.rect.attribute.x + selectComp.rect.attribute.width > scene.rightFrozenGroup.attribute.x && (selectComp.role === 'body' || selectComp.role === 'columnHeader' || selectComp.role === 'bottomFrozen') @@ -198,14 +226,13 @@ function updateComponent( x: selectComp.rect.attribute.x, width: width > 0 ? width : 0 }); - // selectComp.fillhandle.setAttributes({ - // x: selectComp.rect.attribute.x, - // width: width > 0 ? width : 0 - // }); + selectComp.fillhandle?.setAttributes({ + visible: width - colsWidth > 0 + }); } if ( selectComp.rect.attribute.y < scene.colHeaderGroup.attribute.height && - scene.table.scrollTop > 0 && + table.scrollTop > 0 && (selectComp.role === 'body' || selectComp.role === 'rowHeader' || selectComp.role === 'rightFrozen') ) { const height = @@ -214,10 +241,9 @@ function updateComponent( y: selectComp.rect.attribute.y + (scene.colHeaderGroup.attribute.height - selectComp.rect.attribute.y), height: height > 0 ? height : 0 }); - // selectComp.fillhandle.setAttributes({ - // y: selectComp.rect.attribute.y + (scene.colHeaderGroup.attribute.height - selectComp.rect.attribute.y), - // height: height > 0 ? height : 0 - // }); + selectComp.fillhandle?.setAttributes({ + visible: height > 0 + }); } if ( scene.bottomFrozenGroup.attribute.width > 0 && @@ -230,10 +256,9 @@ function updateComponent( y: selectComp.rect.attribute.y, height: height > 0 ? height : 0 }); - // selectComp.fillhandle.setAttributes({ - // y: selectComp.fillhandle.attribute.y, - // height: height > 0 ? height : 0 - // }); + selectComp.fillhandle?.setAttributes({ + visible: height - rowsHeight > 0 + }); } //#endregion } else { @@ -265,14 +290,14 @@ function updateComponent( if (typeof selectComp.rect.attribute.lineWidth === 'number') { diffSize = Math.ceil(selectComp.rect.attribute.lineWidth / 2); } - if (endCol === scene.table.colCount - 1) { + if (endCol === table.colCount - 1) { if (Array.isArray(selectComp.rect.attribute.lineWidth)) { diffSize = Math.ceil((selectComp.rect.attribute.lineWidth[1] ?? 0) / 2); } selectComp.rect.setAttributes({ width: selectComp.rect.attribute.width - diffSize }); - // selectComp.fillhandle.setAttributes({ + // selectComp.fillhandle?.setAttributes({ // width: selectComp.rect.attribute.width - diffSize // }); } @@ -284,19 +309,19 @@ function updateComponent( x: selectComp.rect.attribute.x + diffSize, width: selectComp.rect.attribute.width - diffSize }); - // selectComp.fillhandle.setAttributes({ + // selectComp.fillhandle?.setAttributes({ // x: selectComp.rect.attribute.x + diffSize, // width: selectComp.rect.attribute.width - diffSize // }); } - if (endRow === scene.table.rowCount - 1) { + if (endRow === table.rowCount - 1) { if (Array.isArray(selectComp.rect.attribute.lineWidth)) { diffSize = Math.ceil((selectComp.rect.attribute.lineWidth[2] ?? 0) / 2); } selectComp.rect.setAttributes({ height: selectComp.rect.attribute.height - diffSize }); - // selectComp.fillhandle.setAttributes({ + // selectComp.fillhandle?.setAttributes({ // height: selectComp.rect.attribute.height - diffSize // }); } @@ -308,7 +333,7 @@ function updateComponent( y: selectComp.rect.attribute.y + diffSize, height: selectComp.rect.attribute.height - diffSize }); - // selectComp.fillhandle.setAttributes({ + // selectComp.fillhandle?.setAttributes({ // y: selectComp.rect.attribute.y + diffSize, // height: selectComp.rect.attribute.height - diffSize // }); @@ -321,6 +346,7 @@ export function updateCellSelectBorder( selectRange: CellRange & { skipBodyMerge?: boolean }, ifExtendSelectRange: boolean = true ) { + const table = scene.table; const newStartCol = selectRange.start.col; const newStartRow = selectRange.start.row; const newEndCol = selectRange.end.col; @@ -329,18 +355,18 @@ export function updateCellSelectBorder( let startCol = Math.max(Math.min(newEndCol, newStartCol), 0); let startRow = Math.max(Math.min(newEndRow, newStartRow), 0); - let endCol = Math.min(Math.max(newEndCol, newStartCol), scene.table.colCount - 1); - let endRow = Math.min(Math.max(newEndRow, newStartRow), scene.table.rowCount - 1); + let endCol = Math.min(Math.max(newEndCol, newStartCol), table.colCount - 1); + let endRow = Math.min(Math.max(newEndRow, newStartRow), table.rowCount - 1); //#region region 校验四周的单元格有没有合并的情况,如有则扩大范围 const extendSelectRange = () => { let isExtend = false; for (let col = startCol; col <= endCol; col++) { if (col === startCol) { for (let row = startRow; row <= endRow; row++) { - if (!scene.table.isHeader(col, row) && skipBodyMerge) { + if (!table.isHeader(col, row) && skipBodyMerge) { continue; } - const mergeInfo = getCellMergeInfo(scene.table, col, row); + const mergeInfo = getCellMergeInfo(table, col, row); if (mergeInfo && mergeInfo.start.col < startCol) { startCol = mergeInfo.start.col; isExtend = true; @@ -350,11 +376,11 @@ export function updateCellSelectBorder( } if (!isExtend && col === endCol) { for (let row = startRow; row <= endRow; row++) { - if (!scene.table.isHeader(col, row) && skipBodyMerge) { + if (!table.isHeader(col, row) && skipBodyMerge) { continue; } - const mergeInfo = getCellMergeInfo(scene.table, col, row); - if (mergeInfo && Math.min(mergeInfo.end.col, scene.table.colCount - 1) > endCol) { + const mergeInfo = getCellMergeInfo(table, col, row); + if (mergeInfo && Math.min(mergeInfo.end.col, table.colCount - 1) > endCol) { endCol = mergeInfo.end.col; isExtend = true; break; @@ -370,10 +396,10 @@ export function updateCellSelectBorder( for (let row = startRow; row <= endRow; row++) { if (row === startRow) { for (let col = startCol; col <= endCol; col++) { - if (!scene.table.isHeader(col, row) && skipBodyMerge) { + if (!table.isHeader(col, row) && skipBodyMerge) { continue; } - const mergeInfo = getCellMergeInfo(scene.table, col, row); + const mergeInfo = getCellMergeInfo(table, col, row); if (mergeInfo && mergeInfo.start.row < startRow) { startRow = mergeInfo.start.row; isExtend = true; @@ -383,11 +409,11 @@ export function updateCellSelectBorder( } if (!isExtend && row === endRow) { for (let col = startCol; col <= endCol; col++) { - if (!scene.table.isHeader(col, row) && skipBodyMerge) { + if (!table.isHeader(col, row) && skipBodyMerge) { continue; } - const mergeInfo = getCellMergeInfo(scene.table, col, row); - if (mergeInfo && Math.min(mergeInfo.end.row, scene.table.rowCount - 1) > endRow) { + const mergeInfo = getCellMergeInfo(table, col, row); + if (mergeInfo && Math.min(mergeInfo.end.row, table.rowCount - 1) > endRow) { endRow = mergeInfo.end.row; isExtend = true; break; @@ -423,66 +449,63 @@ export function updateCellSelectBorder( let needRightTopCornerHeader = false; let needRightBottomCornerHeader = false; let needLeftBottomCornerHeader = false; - if (startCol <= scene.table.frozenColCount - 1 && startRow <= scene.table.frozenRowCount - 1) { + if (startCol <= table.frozenColCount - 1 && startRow <= table.frozenRowCount - 1) { needCornerHeader = true; } - if (endCol >= scene.table.colCount - scene.table.rightFrozenColCount && startRow <= scene.table.frozenRowCount - 1) { + if (endCol >= table.colCount - table.rightFrozenColCount && startRow <= table.frozenRowCount - 1) { needRightTopCornerHeader = true; } - if (startCol <= scene.table.frozenColCount - 1 && endRow >= scene.table.rowCount - scene.table.bottomFrozenRowCount) { + if (startCol <= table.frozenColCount - 1 && endRow >= table.rowCount - table.bottomFrozenRowCount) { needLeftBottomCornerHeader = true; } - if ( - endCol >= scene.table.colCount - scene.table.rightFrozenColCount && - endRow >= scene.table.rowCount - scene.table.bottomFrozenRowCount - ) { + if (endCol >= table.colCount - table.rightFrozenColCount && endRow >= table.rowCount - table.bottomFrozenRowCount) { needRightBottomCornerHeader = true; } if ( - startCol <= scene.table.frozenColCount - 1 && - endRow >= scene.table.frozenRowCount && - startRow <= scene.table.rowCount - scene.table.bottomFrozenRowCount - 1 + startCol <= table.frozenColCount - 1 && + endRow >= table.frozenRowCount && + startRow <= table.rowCount - table.bottomFrozenRowCount - 1 ) { needRowHeader = true; } if ( - endCol >= scene.table.colCount - scene.table.rightFrozenColCount && - endRow >= scene.table.frozenRowCount && - startRow <= scene.table.rowCount - scene.table.bottomFrozenRowCount - 1 + endCol >= table.colCount - table.rightFrozenColCount && + endRow >= table.frozenRowCount && + startRow <= table.rowCount - table.bottomFrozenRowCount - 1 ) { needRightRowHeader = true; } if ( - startRow <= scene.table.frozenRowCount - 1 && - endCol >= scene.table.frozenColCount && - startCol <= scene.table.colCount - scene.table.rightFrozenColCount - 1 + startRow <= table.frozenRowCount - 1 && + endCol >= table.frozenColCount && + startCol <= table.colCount - table.rightFrozenColCount - 1 ) { needColumnHeader = true; } if ( - endRow >= scene.table.rowCount - scene.table.bottomFrozenRowCount && - endCol >= scene.table.frozenColCount && - startCol <= scene.table.colCount - scene.table.rightFrozenColCount - 1 + endRow >= table.rowCount - table.bottomFrozenRowCount && + endCol >= table.frozenColCount && + startCol <= table.colCount - table.rightFrozenColCount - 1 ) { needBottomColumnHeader = true; } if ( - startCol <= scene.table.colCount - scene.table.rightFrozenColCount - 1 && - endCol >= scene.table.frozenColCount && - startRow <= scene.table.rowCount - scene.table.bottomFrozenRowCount - 1 && - endRow >= scene.table.frozenRowCount + startCol <= table.colCount - table.rightFrozenColCount - 1 && + endCol >= table.frozenColCount && + startRow <= table.rowCount - table.bottomFrozenRowCount - 1 && + endRow >= table.frozenRowCount ) { needBody = true; } // TODO 可以尝试不拆分三个表头和body【前提是theme中合并配置】 用一个SelectBorder 需要结合clip,并动态设置border的范围【依据区域范围 已经是否跨表头及body】 if (needCornerHeader) { - const cornerEndCol = Math.min(endCol, scene.table.frozenColCount - 1); - const cornerEndRow = Math.min(endRow, scene.table.frozenRowCount - 1); + const cornerEndCol = Math.min(endCol, table.frozenColCount - 1); + const cornerEndRow = Math.min(endRow, table.frozenRowCount - 1); const strokeArray = [true, !needColumnHeader, !needRowHeader, true]; scene.createCellSelectBorder( startCol, @@ -495,8 +518,8 @@ export function updateCellSelectBorder( ); } if (needRightTopCornerHeader) { - const cornerStartCol = Math.max(startCol, scene.table.colCount - scene.table.rightFrozenColCount); - const cornerEndRow = Math.min(endRow, scene.table.frozenRowCount - 1); + const cornerStartCol = Math.max(startCol, table.colCount - table.rightFrozenColCount); + const cornerEndRow = Math.min(endRow, table.frozenRowCount - 1); const strokeArray = [true, true, !needRightRowHeader, !needColumnHeader]; scene.createCellSelectBorder( cornerStartCol, @@ -510,8 +533,8 @@ export function updateCellSelectBorder( } if (needLeftBottomCornerHeader) { - const cornerEndCol = Math.min(endCol, scene.table.frozenColCount - 1); - const cornerStartRow = Math.max(startRow, scene.table.rowCount - scene.table.bottomFrozenRowCount); + const cornerEndCol = Math.min(endCol, table.frozenColCount - 1); + const cornerStartRow = Math.max(startRow, table.rowCount - table.bottomFrozenRowCount); const strokeArray = [!needRowHeader, !needBottomColumnHeader, true, true]; scene.createCellSelectBorder( startCol, @@ -524,8 +547,8 @@ export function updateCellSelectBorder( ); } if (needRightBottomCornerHeader) { - const cornerStartCol = Math.max(startCol, scene.table.colCount - scene.table.rightFrozenColCount); - const cornerStartRow = Math.max(startRow, scene.table.rowCount - scene.table.bottomFrozenRowCount); + const cornerStartCol = Math.max(startCol, table.colCount - table.rightFrozenColCount); + const cornerStartRow = Math.max(startRow, table.rowCount - table.bottomFrozenRowCount); const strokeArray = [!needRightRowHeader, true, true, !needBottomColumnHeader]; scene.createCellSelectBorder( cornerStartCol, @@ -538,9 +561,9 @@ export function updateCellSelectBorder( ); } if (needColumnHeader) { - const columnHeaderStartCol = Math.max(startCol, scene.table.frozenColCount); - const columnHeaderEndCol = Math.min(endCol, scene.table.colCount - scene.table.rightFrozenColCount - 1); - const columnHeaderEndRow = Math.min(endRow, scene.table.frozenRowCount - 1); + const columnHeaderStartCol = Math.max(startCol, table.frozenColCount); + const columnHeaderEndCol = Math.min(endCol, table.colCount - table.rightFrozenColCount - 1); + const columnHeaderEndRow = Math.min(endRow, table.frozenRowCount - 1); const strokeArray = [true, !needRightTopCornerHeader, !needBody, !needCornerHeader]; scene.createCellSelectBorder( columnHeaderStartCol, @@ -553,9 +576,9 @@ export function updateCellSelectBorder( ); } if (needBottomColumnHeader) { - const columnHeaderStartCol = Math.max(startCol, scene.table.frozenColCount); - const columnHeaderEndCol = Math.min(endCol, scene.table.colCount - scene.table.rightFrozenColCount - 1); - const columnHeaderStartRow = Math.max(startRow, scene.table.rowCount - scene.table.bottomFrozenRowCount); + const columnHeaderStartCol = Math.max(startCol, table.frozenColCount); + const columnHeaderEndCol = Math.min(endCol, table.colCount - table.rightFrozenColCount - 1); + const columnHeaderStartRow = Math.max(startRow, table.rowCount - table.bottomFrozenRowCount); const strokeArray = [!needBody, !needRightBottomCornerHeader, true, !needLeftBottomCornerHeader]; scene.createCellSelectBorder( columnHeaderStartCol, @@ -568,9 +591,9 @@ export function updateCellSelectBorder( ); } if (needRowHeader) { - const columnHeaderStartRow = Math.max(startRow, scene.table.frozenRowCount); - const columnHeaderEndRow = Math.min(endRow, scene.table.rowCount - scene.table.bottomFrozenRowCount - 1); - const columnHeaderEndCol = Math.min(endCol, scene.table.frozenColCount - 1); + const columnHeaderStartRow = Math.max(startRow, table.frozenRowCount); + const columnHeaderEndRow = Math.min(endRow, table.rowCount - table.bottomFrozenRowCount - 1); + const columnHeaderEndCol = Math.min(endCol, table.frozenColCount - 1); const strokeArray = [!needCornerHeader, !needBody, !needLeftBottomCornerHeader, true]; scene.createCellSelectBorder( startCol, @@ -583,9 +606,9 @@ export function updateCellSelectBorder( ); } if (needRightRowHeader) { - const columnHeaderStartRow = Math.max(startRow, scene.table.frozenRowCount); - const columnHeaderEndRow = Math.min(endRow, scene.table.rowCount - scene.table.bottomFrozenRowCount - 1); - const columnHeaderStartCol = Math.max(startCol, scene.table.colCount - scene.table.rightFrozenColCount); + const columnHeaderStartRow = Math.max(startRow, table.frozenRowCount); + const columnHeaderEndRow = Math.min(endRow, table.rowCount - table.bottomFrozenRowCount - 1); + const columnHeaderStartCol = Math.max(startCol, table.colCount - table.rightFrozenColCount); const strokeArray = [!needRightTopCornerHeader, true, !needRightBottomCornerHeader, !needBody]; scene.createCellSelectBorder( columnHeaderStartCol, @@ -598,10 +621,10 @@ export function updateCellSelectBorder( ); } if (needBody) { - const columnHeaderStartCol = Math.max(startCol, scene.table.frozenColCount); - const columnHeaderStartRow = Math.max(startRow, scene.table.frozenRowCount); - const columnHeaderEndCol = Math.min(endCol, scene.table.colCount - scene.table.rightFrozenColCount - 1); - const columnHeaderEndRow = Math.min(endRow, scene.table.rowCount - scene.table.bottomFrozenRowCount - 1); + const columnHeaderStartCol = Math.max(startCol, table.frozenColCount); + const columnHeaderStartRow = Math.max(startRow, table.frozenRowCount); + const columnHeaderEndCol = Math.min(endCol, table.colCount - table.rightFrozenColCount - 1); + const columnHeaderEndRow = Math.min(endRow, table.rowCount - table.bottomFrozenRowCount - 1); const strokeArray = [!needColumnHeader, !needRightRowHeader, !needBottomColumnHeader, !needRowHeader]; scene.createCellSelectBorder( columnHeaderStartCol, diff --git a/packages/vtable/src/scenegraph/utils/text-icon-layout.ts b/packages/vtable/src/scenegraph/utils/text-icon-layout.ts index 985d79e07..fd059fad4 100644 --- a/packages/vtable/src/scenegraph/utils/text-icon-layout.ts +++ b/packages/vtable/src/scenegraph/utils/text-icon-layout.ts @@ -54,22 +54,22 @@ export function createCellContent( cellTheme: IThemeSpec, range: CellRange | undefined ) { - const leftIcons: ColumnIconOption[] = []; - const rightIcons: ColumnIconOption[] = []; - const contentLeftIcons: ColumnIconOption[] = []; - const contentRightIcons: ColumnIconOption[] = []; - const inlineFrontIcons: ColumnIconOption[] = []; - const inlineEndIcons: ColumnIconOption[] = []; - const absoluteLeftIcons: ColumnIconOption[] = []; - const absoluteRightIcons: ColumnIconOption[] = []; + // const leftIcons: ColumnIconOption[] = []; + // const rightIcons: ColumnIconOption[] = []; + // const contentLeftIcons: ColumnIconOption[] = []; + // const contentRightIcons: ColumnIconOption[] = []; + // const inlineFrontIcons: ColumnIconOption[] = []; + // const inlineEndIcons: ColumnIconOption[] = []; + // const absoluteLeftIcons: ColumnIconOption[] = []; + // const absoluteRightIcons: ColumnIconOption[] = []; let contentWidth: number; let contentHeight: number; let leftIconWidth = 0; - let leftIconHeight = 0; + // let leftIconHeight = 0; let rightIconWidth = 0; - let rightIconHeight = 0; - let absoluteLeftIconWidth = 0; + // let rightIconHeight = 0; + // let absoluteLeftIconWidth = 0; let absoluteRightIconWidth = 0; if (!Array.isArray(icons) || icons.length === 0) { @@ -118,78 +118,98 @@ export function createCellContent( contentHeight = wrapText.AABBBounds.height(); } } else { - // icon分类 - icons.forEach(icon => { - switch (icon.positionType) { - case IconPosition.left: - leftIcons.push(icon); - break; - case IconPosition.right: - rightIcons.push(icon); - break; - case IconPosition.contentLeft: - contentLeftIcons.push(icon); - break; - case IconPosition.contentRight: - contentRightIcons.push(icon); - break; - // case IconPosition.absoluteLeft: - // absoluteLeftIcons.push(icon); - // break; - case IconPosition.absoluteRight: - absoluteRightIcons.push(icon); - break; - case IconPosition.inlineFront: - inlineFrontIcons.push(icon); - break; - case IconPosition.inlineEnd: - inlineEndIcons.push(icon); - break; - } - }); - - // 添加非cell icon & absolute icon - leftIcons.forEach(icon => { - const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); - iconMark.role = 'icon-left'; - iconMark.name = icon.name; - iconMark.setAttribute('x', leftIconWidth + (iconMark.attribute.marginLeft ?? 0)); - leftIconWidth += - iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); - leftIconHeight = Math.max(leftIconHeight, iconMark.AABBBounds.height()); - cellGroup.appendChild(iconMark); - }); - - rightIcons.forEach(icon => { - const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); - iconMark.role = 'icon-right'; - iconMark.name = icon.name; - iconMark.setAttribute('x', rightIconWidth + (iconMark.attribute.marginLeft ?? 0)); - rightIconWidth += - iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); - rightIconHeight = Math.max(rightIconHeight, iconMark.AABBBounds.height()); - cellGroup.appendChild(iconMark); - }); - - absoluteLeftIcons.forEach(icon => { - const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); - iconMark.role = 'icon-absolute-left'; - iconMark.name = icon.name; - iconMark.setAttribute('x', absoluteLeftIconWidth + (iconMark.attribute.marginLeft ?? 0)); - absoluteLeftIconWidth += - iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); - cellGroup.appendChild(iconMark); - }); - - absoluteRightIcons.forEach(icon => { - const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); - iconMark.role = 'icon-absolute-right'; - iconMark.name = icon.name; - iconMark.setAttribute('x', absoluteRightIconWidth + (iconMark.attribute.marginLeft ?? 0)); - absoluteRightIconWidth += - iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); - cellGroup.appendChild(iconMark); - }); + // // icon分类 + // icons.forEach(icon => { + // switch (icon.positionType) { + // case IconPosition.left: + // leftIcons.push(icon); + // break; + // case IconPosition.right: + // rightIcons.push(icon); + // break; + // case IconPosition.contentLeft: + // contentLeftIcons.push(icon); + // break; + // case IconPosition.contentRight: + // contentRightIcons.push(icon); + // break; + // // case IconPosition.absoluteLeft: + // // absoluteLeftIcons.push(icon); + // // break; + // case IconPosition.absoluteRight: + // absoluteRightIcons.push(icon); + // break; + // case IconPosition.inlineFront: + // inlineFrontIcons.push(icon); + // break; + // case IconPosition.inlineEnd: + // inlineEndIcons.push(icon); + // break; + // } + // }); + + // // 添加非cell icon & absolute icon + // leftIcons.forEach(icon => { + // const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + // iconMark.role = 'icon-left'; + // iconMark.name = icon.name; + // iconMark.setAttribute('x', leftIconWidth + (iconMark.attribute.marginLeft ?? 0)); + // leftIconWidth += + // iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + // leftIconHeight = Math.max(leftIconHeight, iconMark.AABBBounds.height()); + // cellGroup.appendChild(iconMark); + // }); + + // rightIcons.forEach(icon => { + // const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + // iconMark.role = 'icon-right'; + // iconMark.name = icon.name; + // iconMark.setAttribute('x', rightIconWidth + (iconMark.attribute.marginLeft ?? 0)); + // rightIconWidth += + // iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + // rightIconHeight = Math.max(rightIconHeight, iconMark.AABBBounds.height()); + // cellGroup.appendChild(iconMark); + // }); + + // absoluteLeftIcons.forEach(icon => { + // const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + // iconMark.role = 'icon-absolute-left'; + // iconMark.name = icon.name; + // iconMark.setAttribute('x', absoluteLeftIconWidth + (iconMark.attribute.marginLeft ?? 0)); + // absoluteLeftIconWidth += + // iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + // cellGroup.appendChild(iconMark); + // }); + + // absoluteRightIcons.forEach(icon => { + // const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + // iconMark.role = 'icon-absolute-right'; + // iconMark.name = icon.name; + // iconMark.setAttribute('x', absoluteRightIconWidth + (iconMark.attribute.marginLeft ?? 0)); + // absoluteRightIconWidth += + // iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + // cellGroup.appendChild(iconMark); + // }); + + const { + inlineFrontIcons, + inlineEndIcons, + contentLeftIcons, + contentRightIcons, + leftIconWidth: layoutLeftIconWidth, + // leftIconHeight: layoutLeftIconHeight, + rightIconWidth: layoutRightIconWidth, + // rightIconHeight: layoutRightIconHeight, + // absoluteLeftIconWidth: layoutAbsoluteLeftIconWidth, + absoluteRightIconWidth: layoutAbsoluteRightIconWidth + } = dealWithIconLayout(icons, cellGroup, range, table); + + leftIconWidth = layoutLeftIconWidth; + // leftIconHeight = layoutLeftIconHeight; + rightIconWidth = layoutRightIconWidth; + // rightIconHeight = layoutRightIconHeight; + // absoluteLeftIconWidth = layoutAbsoluteLeftIconWidth; + absoluteRightIconWidth = layoutAbsoluteRightIconWidth; // 添加text & content icon & inline icon let textMark; @@ -751,3 +771,116 @@ function isCellHeightUpdate(scene: Scenegraph, cellGroup: Group, newHeight: numb return false; } + +export function dealWithIconLayout( + icons: ColumnIconOption[], + cellGroup: Group, + range: CellRange | undefined, + table: BaseTableAPI +) { + const leftIcons: ColumnIconOption[] = []; + const rightIcons: ColumnIconOption[] = []; + const contentLeftIcons: ColumnIconOption[] = []; + const contentRightIcons: ColumnIconOption[] = []; + const inlineFrontIcons: ColumnIconOption[] = []; + const inlineEndIcons: ColumnIconOption[] = []; + const absoluteLeftIcons: ColumnIconOption[] = []; + const absoluteRightIcons: ColumnIconOption[] = []; + + let leftIconWidth = 0; + let leftIconHeight = 0; + let rightIconWidth = 0; + let rightIconHeight = 0; + let absoluteLeftIconWidth = 0; + let absoluteRightIconWidth = 0; + + // icon分类 + icons.forEach(icon => { + switch (icon.positionType) { + case IconPosition.left: + leftIcons.push(icon); + break; + case IconPosition.right: + rightIcons.push(icon); + break; + case IconPosition.contentLeft: + contentLeftIcons.push(icon); + break; + case IconPosition.contentRight: + contentRightIcons.push(icon); + break; + // case IconPosition.absoluteLeft: + // absoluteLeftIcons.push(icon); + // break; + case IconPosition.absoluteRight: + absoluteRightIcons.push(icon); + break; + case IconPosition.inlineFront: + inlineFrontIcons.push(icon); + break; + case IconPosition.inlineEnd: + inlineEndIcons.push(icon); + break; + } + }); + + // 添加非cell icon & absolute icon + leftIcons.forEach(icon => { + const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + iconMark.role = 'icon-left'; + iconMark.name = icon.name; + iconMark.setAttribute('x', leftIconWidth + (iconMark.attribute.marginLeft ?? 0)); + leftIconWidth += + iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + leftIconHeight = Math.max(leftIconHeight, iconMark.AABBBounds.height()); + cellGroup.appendChild(iconMark); + }); + + rightIcons.forEach(icon => { + const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + iconMark.role = 'icon-right'; + iconMark.name = icon.name; + iconMark.setAttribute('x', rightIconWidth + (iconMark.attribute.marginLeft ?? 0)); + rightIconWidth += + iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + rightIconHeight = Math.max(rightIconHeight, iconMark.AABBBounds.height()); + cellGroup.appendChild(iconMark); + }); + + absoluteLeftIcons.forEach(icon => { + const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + iconMark.role = 'icon-absolute-left'; + iconMark.name = icon.name; + iconMark.setAttribute('x', absoluteLeftIconWidth + (iconMark.attribute.marginLeft ?? 0)); + absoluteLeftIconWidth += + iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + cellGroup.appendChild(iconMark); + }); + + absoluteRightIcons.forEach(icon => { + const iconMark = dealWithIcon(icon, undefined, cellGroup.col, cellGroup.row, range, table); + iconMark.role = 'icon-absolute-right'; + iconMark.name = icon.name; + iconMark.setAttribute('x', absoluteRightIconWidth + (iconMark.attribute.marginLeft ?? 0)); + absoluteRightIconWidth += + iconMark.AABBBounds.width() + (iconMark.attribute.marginLeft ?? 0) + (iconMark.attribute.marginRight ?? 0); + cellGroup.appendChild(iconMark); + }); + + return { + leftIcons, + rightIcons, + contentLeftIcons, + contentRightIcons, + inlineFrontIcons, + inlineEndIcons, + absoluteLeftIcons, + absoluteRightIcons, + leftIconWidth, + leftIconHeight, + rightIconWidth, + rightIconHeight, + absoluteLeftIconWidth, + absoluteRightIconWidth + }; +} diff --git a/packages/vtable/src/state/select/update-position.ts b/packages/vtable/src/state/select/update-position.ts index aa4ff9322..c662c2ecf 100644 --- a/packages/vtable/src/state/select/update-position.ts +++ b/packages/vtable/src/state/select/update-position.ts @@ -25,6 +25,9 @@ export function updateSelectPosition( const { highlightScope, disableHeader, cellPos } = state.select; if ((disableHeader && table.isHeader(col, row)) || highlightScope === 'none') { + if (col !== -1 && row !== -1 && !isSelectMoving) { + table._makeVisibleCell(col, row); + } col = -1; row = -1; } diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index 745e458e3..69a9a1440 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -69,7 +69,8 @@ import type { WidthAdaptiveModeDef, HeightAdaptiveModeDef, ColumnInfo, - RowInfo + RowInfo, + CellAddressWithBound } from '.'; import type { TooltipOptions } from './tooltip'; import type { IWrapTextGraphicAttribute } from '../scenegraph/graphic/text'; @@ -271,6 +272,7 @@ export interface BaseTableConstructorOptions { * 当前需要冻结的列数 基本表格生效 */ frozenColCount?: number; + frozenRowCount?: number; rightFrozenColCount?: number; bottomFrozenRowCount?: number; @@ -560,7 +562,7 @@ export interface BaseTableAPI { ) => EventListenerId; // &((type: string, listener: AnyListener) => EventListenerId); - _vDataSet: DataSet; + _vDataSet?: DataSet; /** 场景树对象 */ scenegraph: Scenegraph; /** 状态管理模块 */ @@ -623,7 +625,8 @@ export interface BaseTableAPI { isFrozenCell: (col: number, row: number) => { row: boolean; col: boolean } | null; getRowAt: (absoluteY: number) => { top: number; row: number; bottom: number }; getColAt: (absoluteX: number) => { left: number; col: number; right: number }; - getCellAt: (absoluteX: number, absoluteY: number) => CellAddress; + getCellAt: (absoluteX: number, absoluteY: number) => CellAddressWithBound; + getCellAtRelativePosition: (absoluteX: number, absoluteY: number) => CellAddressWithBound; _makeVisibleCell: (col: number, row: number) => void; // setFocusCursor(col: number, row: number): void; // focusCell(col: number, row: number): void; diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index 8fadc8409..9dd669770 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -22,7 +22,7 @@ import type { ITitleDefine } from './pivot-table'; import type { ColumnsDefine } from './list-table'; -import type { ICellAxisOption, ITableAxisOption } from './component/axis'; +import type { ITableAxisOption } from './component/axis'; import type { IEditor } from '@visactor/vtable-editors'; import type { ITextStyleOption } from '../body-helper/style'; import type { DataSource } from '../data'; diff --git a/packages/vtable/src/ts-types/theme.ts b/packages/vtable/src/ts-types/theme.ts index 7579e0a78..d52e983f2 100644 --- a/packages/vtable/src/ts-types/theme.ts +++ b/packages/vtable/src/ts-types/theme.ts @@ -2,7 +2,6 @@ import type { ColorsDef, LineDashsDef, LineWidthsDef, LineWidthsPropertyDefine, LineDashsPropertyDefine } from '.'; import type { CheckboxStyle, ITextStyleOption, RadioStyle } from './column/style'; import type { ColorPropertyDefine, ColorsPropertyDefine } from './style-define'; -import type { ColumnIconOption } from './icon'; import type { ICellAxisOption } from './component/axis'; import type { PopTipAttributes } from '@visactor/vrender-components'; // ****** Custom Theme ******* diff --git a/packages/vtable/src/vrender.ts b/packages/vtable/src/vrender.ts index ad54c76f9..4c650f2e4 100644 --- a/packages/vtable/src/vrender.ts +++ b/packages/vtable/src/vrender.ts @@ -1,3 +1,4 @@ +import { loadPoptip } from '@visactor/vrender-components'; import '@visactor/vrender-core'; import { container, isBrowserEnv, isNodeEnv, preLoadAllModule } from '@visactor/vrender-core'; import { @@ -40,23 +41,25 @@ export function registerForVrender() { loadNodeEnv(container); } registerArc(); - registerArc3d(); - registerArea(); + // registerArc3d(); + // registerArea(); registerCircle(); - registerGlyph(); + // registerGlyph(); registerGroup(); registerImage(); registerLine(); - registerPath(); - registerPolygon(); - registerPyramid3d(); + // registerPath(); + // registerPolygon(); + // registerPyramid3d(); registerRect(); - registerRect3d(); + // registerRect3d(); registerRichtext(); - registerShadowRoot(); + // registerShadowRoot(); registerSymbol(); registerText(); - registerWrapText(); + // registerWrapText(); + + loadPoptip(); } export { Direction } from '@visactor/vrender-core'; diff --git a/packages/vtable/src/vutil-extension-temp/algorithm/binary-search.ts b/packages/vtable/src/vutil-extension-temp/algorithm/binary-search.ts deleted file mode 100644 index fe7c09731..000000000 --- a/packages/vtable/src/vutil-extension-temp/algorithm/binary-search.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 二分靠近框架,返回数组中第一个大于等于目标值的数的索引 - * @param arr 数组 - * @param compareFn 比较函数,返回(当前值-目标值) - */ -export const binaryFuzzySearch = (arr: T[], compareFn: (value: T) => number) => { - return binaryFuzzySearchInNumberRange(0, arr.length, value => compareFn(arr[value])); -}; - -/** - * 二分靠近框架,返回数字区间中第一个大于等于目标值的数字 - * @param x1 区间上界 - * @param x2 区间下界(不包含) - * @param compareFn 比较函数,返回(当前值-目标值) - */ -export const binaryFuzzySearchInNumberRange = (x1: number, x2: number, compareFn: (value: number) => number) => { - let left = x1; - let right = x2; - while (left < right) { - const mid = Math.floor((left + right) / 2); - if (compareFn(mid) >= 0) { - right = mid; // 第一个大于等于目标值的数 - } else { - left = mid + 1; - } - } - return left; -}; diff --git a/packages/vtable/src/vutil-extension-temp/algorithm/index.ts b/packages/vtable/src/vutil-extension-temp/algorithm/index.ts deleted file mode 100644 index e10f75227..000000000 --- a/packages/vtable/src/vutil-extension-temp/algorithm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './binary-search'; diff --git a/packages/vtable/src/vutil-extension-temp/index.ts b/packages/vtable/src/vutil-extension-temp/index.ts deleted file mode 100644 index f9d6e21f1..000000000 --- a/packages/vtable/src/vutil-extension-temp/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './transform/tick-data'; -export * from './utils'; -export * from './algorithm'; -export * from './spec'; diff --git a/packages/vtable/src/vutil-extension-temp/spec/clone-deep.ts b/packages/vtable/src/vutil-extension-temp/spec/clone-deep.ts deleted file mode 100644 index 74aec2e98..000000000 --- a/packages/vtable/src/vutil-extension-temp/spec/clone-deep.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { isArray, isBoolean, isDate, isNumber, isString, isValid } from '@visactor/vutils'; -import { isDataView, isHTMLElement } from './common'; - -/** - * 深拷贝 spec,为避免循环引用,DataView 维持原有引用 - * @param spec 原spec - */ -export function cloneDeepSpec(spec: any, excludeKeys: string[] = ['data']) { - const value = spec; - - let result; - if (!isValid(value) || typeof value !== 'object') { - return value; - } - - // 判断是不是不能深拷贝的对象 - if (isDataView(value) || isHTMLElement(value)) { - return value; - } - - const isArr = isArray(value); - const length = value.length; - // 不考虑特殊数组的额外处理 - if (isArr) { - result = new Array(length); - } - // 不考虑 buffer / arguments 类型的处理以及 prototype 的额外处理 - else if (typeof value === 'object') { - result = {}; - } - // 不建议使用作为 Boolean / Number / String 作为构造器 - else if (isBoolean(value) || isNumber(value) || isString(value)) { - result = value; - } else if (isDate(value)) { - result = new Date(+value); - } - // 不考虑 ArrayBuffer / DataView / TypedArray / map / set / regexp / symbol 类型 - else { - result = undefined; - } - - // 不考虑 map / set / TypedArray 类型的赋值 - - // 不考虑对象的 symbol 属性 - const props = isArr ? undefined : Object.keys(Object(value)); - - let index = -1; - if (result) { - while (++index < (props || value).length) { - const key = props ? props[index] : index; - const subValue = value[key]; - if (excludeKeys?.includes(key.toString())) { - result[key] = subValue; - } else { - result[key] = cloneDeepSpec(subValue, excludeKeys); - } - } - } - - return result; -} diff --git a/packages/vtable/src/vutil-extension-temp/spec/common.ts b/packages/vtable/src/vutil-extension-temp/spec/common.ts deleted file mode 100644 index a11af544c..000000000 --- a/packages/vtable/src/vutil-extension-temp/spec/common.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { DataView } from '@visactor/vdataset'; - -export function isDataView(obj: any): obj is DataView { - return obj instanceof DataView; -} - -export function isHTMLElement(obj: any): obj is Element { - try { - return obj instanceof Element; - } catch { - // 跨端 plan B - const htmlElementKeys: (keyof Element)[] = [ - 'children', - 'innerHTML', - 'classList', - 'setAttribute', - 'tagName', - 'getBoundingClientRect' - ]; - const keys = Object.keys(obj); - return htmlElementKeys.every(key => keys.includes(key)); - } -} diff --git a/packages/vtable/src/vutil-extension-temp/spec/index.ts b/packages/vtable/src/vutil-extension-temp/spec/index.ts deleted file mode 100644 index 1245ec93d..000000000 --- a/packages/vtable/src/vutil-extension-temp/spec/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './clone-deep'; -export * from './common'; -export * from './merge-spec'; diff --git a/packages/vtable/src/vutil-extension-temp/spec/merge-spec.ts b/packages/vtable/src/vutil-extension-temp/spec/merge-spec.ts deleted file mode 100644 index b7d3a8433..000000000 --- a/packages/vtable/src/vutil-extension-temp/spec/merge-spec.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { isArray, isArrayLike, isObject, isPlainObject, isValid } from '@visactor/vutils'; - -function baseMerge(target: any, source: any, shallowArray = false) { - if (source) { - if (target === source) { - return; - } - if (isValid(source) && typeof source === 'object') { - // baseFor - const iterable = Object(source); - const props = []; - // keysIn - for (const key in iterable) { - props.push(key); - } - let { length } = props; - let propIndex = -1; - while (length--) { - const key = props[++propIndex]; - if ( - isValid(iterable[key]) && - typeof iterable[key] === 'object' && - !isArray(target[key]) // VChart 特有逻辑 - ) { - baseMergeDeep(target, source, key, shallowArray); - } else { - assignMergeValue(target, key, iterable[key]); - } - } - } - } -} - -// 由于目前 VChart 内部对 spec 会先执行一次深拷贝,merge 暂时不考虑 source 中有环的问题 -function baseMergeDeep(target: object, source: object, key: string, shallowArray = false) { - const objValue = target[key]; - const srcValue = source[key]; - let newValue = source[key]; - let isCommon = true; - // 不考虑 buffer / typedArray 类型 - if (isArray(srcValue)) { - if (shallowArray) { - // 依据参数对数组做浅拷贝 - newValue = []; - } else if (isArray(objValue)) { - newValue = objValue; - } else if (isArrayLike(objValue)) { - // 如果 source 为数组,则 target 的 arrayLike 对象也视作为数组处理 - newValue = new Array(objValue.length); - let index = -1; - const length = objValue.length; - while (++index < length) { - newValue[index] = objValue[index]; - } - } - } - // else if (isArray(srcValue) && shallowArray) { - // newValue = []; - // } - // 不考虑 argument 类型 - else if (isPlainObject(srcValue)) { - newValue = objValue ?? {}; - // 不考虑 prototype 的额外处理 - if (typeof objValue === 'function' || typeof objValue !== 'object') { - newValue = {}; - } - } else { - isCommon = false; - } - // 对 class 等复杂对象或者浅拷贝的 array 不做拷贝处理 - if (isCommon) { - baseMerge(newValue, srcValue, shallowArray); - } - assignMergeValue(target, key, newValue); -} - -function assignMergeValue(target: object, key: string, value: any) { - if ((value !== undefined && !eq(target[key], value)) || (value === undefined && !(key in target))) { - // 不考虑 __proto__ 的赋值处理 - target[key] = value; - } -} - -function eq(value: any, other: any) { - return value === other || (Number.isNaN(value) && Number.isNaN(other)); -} - -/* 与原生的 lodash merge 差异在于对数组是否应用最后一个 source 的结果 - * 以及对一些特殊情况的处理,比如对数组类型 padding 和对象类型的 padding 的 merge - */ -export function mergeSpec(target: any, ...sources: any[]): any { - let sourceIndex = -1; - const length = sources.length; - while (++sourceIndex < length) { - const source = sources[sourceIndex]; - baseMerge(target, source, true); - } - return target; -} - -export function mergeSpecWithFilter( - target: any, - filter: string | { type: string; index: number }, - spec: any, - forceMerge: boolean -) { - Object.keys(target).forEach(k => { - if (isObject(filter)) { - if (filter.type === k) { - if (isArray(target[k])) { - if (target[k].length >= filter.index) { - target[k][filter.index] = forceMerge ? mergeSpec({}, target[k][filter.index], spec) : spec; - } - } else { - target[k] = forceMerge ? mergeSpec({}, target[k], spec) : spec; - } - } - } else { - // filter === user id - if (isArray(target[k])) { - const index = target[k].findIndex((_s: { id: string | number }) => _s.id === filter); - if (index >= 0) { - target[k][index] = forceMerge ? mergeSpec({}, target[k][index], spec) : spec; - } - } else if (target.id === filter) { - target[k] = forceMerge ? mergeSpec({}, target[k], spec) : spec; - } - } - }); -} diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/config.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/config.ts deleted file mode 100644 index af2881fac..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/config.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** 连续轴默认 tick 数量 */ -export const DEFAULT_CONTINUOUS_TICK_COUNT = 5; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/continuous.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/continuous.ts deleted file mode 100644 index 981390e38..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/continuous.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { LinearScale, ContinuousScale } from '@visactor/vscale'; -// eslint-disable-next-line no-duplicate-imports -import { isContinuous } from '@visactor/vscale'; -import { isFunction, isValid, last } from '@visactor/vutils'; -import { DEFAULT_CONTINUOUS_TICK_COUNT } from './config'; -import type { ICartesianTickDataOpt, ITickData, ITickDataOpt } from './interface'; -import type { ILabelItem } from './util'; -// eslint-disable-next-line no-duplicate-imports -import { convertDomainToTickData, getCartesianLabelBounds, hasOverlap, intersect } from './util'; - -/** - * 对于连续轴: - * - 如果spec配了tickCount、forceTickCount、tickStep,则直接输出LinearScale的ticks()、forceTicks()、stepTicks()结果; - * - 默认输出tickCount为10的ticks()结果。 - * - * @param scale - * @param op - * @returns - */ -export const continuousTicks = (scale: ContinuousScale, op: ITickDataOpt): ITickData[] => { - if (!isContinuous(scale.type)) { - return convertDomainToTickData(scale.domain()); - } - // if range is so small - const range = scale.range(); - const rangeSize = Math.abs(range[range.length - 1] - range[0]); - if (rangeSize < 2) { - return convertDomainToTickData([scale.domain()[0]]); - } - - const { tickCount, forceTickCount, tickStep, noDecimals = false, labelStyle } = op; - - let scaleTicks: number[]; - if (isValid(tickStep)) { - scaleTicks = (scale as LinearScale).stepTicks(tickStep); - } else if (isValid(forceTickCount)) { - scaleTicks = (scale as LinearScale).forceTicks(forceTickCount); - } else if (op.tickMode === 'd3') { - const count = isFunction(tickCount) ? tickCount({ axisLength: rangeSize, labelStyle }) : tickCount; - scaleTicks = (scale as LinearScale).d3Ticks(count ?? DEFAULT_CONTINUOUS_TICK_COUNT, { noDecimals }); - } else { - const count = isFunction(tickCount) ? tickCount({ axisLength: rangeSize, labelStyle }) : tickCount; - scaleTicks = (scale as LinearScale).ticks(count ?? DEFAULT_CONTINUOUS_TICK_COUNT, { noDecimals }); - } - - if (op.sampling) { - // 判断重叠 - if (op.coordinateType === 'cartesian' || (op.coordinateType === 'polar' && op.axisOrientType === 'radius')) { - const { labelGap = 4, labelFlush } = op as ICartesianTickDataOpt; - let items = getCartesianLabelBounds(scale, scaleTicks, op as ICartesianTickDataOpt).map( - (bounds, i) => - ({ - AABBBounds: bounds, - value: scaleTicks[i] - } as ILabelItem) - ); - while (items.length >= 3 && hasOverlap(items, labelGap)) { - items = methods.parity(items); - } - const ticks = items.map(item => item.value); - - if (ticks.length < 3 && labelFlush) { - if (ticks.length > 1) { - ticks.pop(); - } - if (last(ticks) !== last(scaleTicks)) { - ticks.push(last(scaleTicks)); - } - } - - scaleTicks = ticks; - } - } - - return convertDomainToTickData(scaleTicks); -}; - -const methods = { - parity: function (items: ILabelItem[]) { - return items.filter((item, i) => i % 2 === 0); - }, - greedy: function (items: ILabelItem[], sep: number) { - let a: ILabelItem; - return items.filter((b, i) => { - if (!i || !intersect(a.AABBBounds, b.AABBBounds, sep)) { - a = b; - return true; - } - return false; - }); - } -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/linear.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/linear.ts deleted file mode 100644 index 83cce6834..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/linear.ts +++ /dev/null @@ -1,241 +0,0 @@ -import type { BandScale, IBaseScale } from '@visactor/vscale'; -import { isFunction, isValid, maxInArray, minInArray } from '@visactor/vutils'; -import type { ICartesianTickDataOpt, ITickData } from '../interface'; -import { convertDomainToTickData, getCartesianLabelBounds, isAxisHorizontal } from '../util'; -import { binaryFuzzySearchInNumberRange } from '../../../algorithm'; - -/** x1, x2, length */ -type OneDimensionalBounds = [number, number, number]; - -const getOneDimensionalLabelBounds = ( - scale: IBaseScale, - domain: any[], - op: ICartesianTickDataOpt, - isHorizontal: boolean -): OneDimensionalBounds[] => { - const labelBoundsList = getCartesianLabelBounds(scale, domain, op); - return labelBoundsList.map(bounds => { - if (isHorizontal) { - return [bounds.x1, bounds.x2, bounds.width()]; - } - return [bounds.y1, bounds.y2, bounds.height()]; - }); -}; - -/** 判断两个 bounds 是否有重叠情况 */ -const boundsOverlap = (prevBounds: OneDimensionalBounds, nextBounds: OneDimensionalBounds, gap = 0): boolean => { - return Math.max(prevBounds[0], nextBounds[0]) - gap / 2 <= Math.min(prevBounds[1], nextBounds[1]) + gap / 2; -}; - -/** 判断两个不相交的 bounds 相隔的距离 */ -export const boundsDistance = (prevBounds: OneDimensionalBounds, nextBounds: OneDimensionalBounds): number => { - if (prevBounds[1] < nextBounds[0]) { - return nextBounds[0] - prevBounds[1]; - } else if (nextBounds[1] < prevBounds[0]) { - return prevBounds[0] - nextBounds[1]; - } - return 0; -}; - -/** - * 对于离散轴: - * - 如果spec配了tickCount、forceTickCount、tickStep,则直接输出BandScale的ticks()、forceTicks()、stepTicks()结果; - * - 估算所有轴label的宽度(或高度,在竖轴的情况下)并存为数组domainLengthList; - * - 通过循环来寻找最小的step,使:如果在这个step下采样,轴标签互不遮挡(此处用到domainLengthList和scale.range()); - * - 如果用户配置了spec.label.lastVisible,则处理右边界:强制采样最后一个tick数据,并删掉这个tick的label所覆盖的那些tick数据。 - * - * @param scale - * @param op - * @returns - */ -export const linearDiscreteTicks = (scale: BandScale, op: ICartesianTickDataOpt): ITickData[] => { - const domain = scale.domain(); - if (!domain.length) { - return []; - } - const { tickCount, forceTickCount, tickStep, labelGap = 4, axisOrientType, labelStyle } = op; - const isHorizontal = isAxisHorizontal(axisOrientType); - const range = scale.range(); - - // if range is so small - const rangeSize = scale.calculateWholeRangeSize(); - if (rangeSize < 2) { - if (op.labelLastVisible) { - return convertDomainToTickData([domain[domain.length - 1]]); - } - return convertDomainToTickData([domain[0]]); - } - - let scaleTicks; - if (isValid(tickStep)) { - scaleTicks = scale.stepTicks(tickStep); - } else if (isValid(forceTickCount)) { - scaleTicks = scale.forceTicks(forceTickCount); - } else if (isValid(tickCount)) { - const count = isFunction(tickCount) ? tickCount({ axisLength: rangeSize, labelStyle }) : tickCount; - scaleTicks = scale.ticks(count); - } else if (op.sampling) { - const fontSize = (op.labelStyle.fontSize ?? 12) + 2; - const rangeStart = minInArray(range); - const rangeEnd = maxInArray(range); - - if (domain.length <= rangeSize / fontSize) { - const incrementUnit = (rangeEnd - rangeStart) / domain.length; - const labelBoundsList = getOneDimensionalLabelBounds(scale, domain, op, isHorizontal); - const minBoundsLength = Math.min(...labelBoundsList.map(bounds => bounds[2])); - - const stepResult = getStep( - domain, - labelBoundsList, - labelGap, - op.labelLastVisible, - Math.floor(minBoundsLength / incrementUnit), // 给step赋上合适的初值,有效改善外层循环次数 - false - ); - - scaleTicks = (scale as BandScale).stepTicks(stepResult.step); - if (op.labelLastVisible) { - if (stepResult.delCount) { - scaleTicks = scaleTicks.slice(0, scaleTicks.length - stepResult.delCount); - } - scaleTicks.push(domain[domain.length - 1]); - } - } else { - // only check first middle last, use the max size to sampling - const tempDomain = [domain[0], domain[Math.floor(domain.length / 2)], domain[domain.length - 1]]; - const tempList = getOneDimensionalLabelBounds(scale, tempDomain, op, isHorizontal); - let maxBounds: OneDimensionalBounds = null; - tempList.forEach(current => { - if (!maxBounds) { - maxBounds = current; - return; - } - if (maxBounds[2] < current[2]) { - maxBounds = current; - } - }); - - const step = - rangeEnd - rangeStart - labelGap > 0 - ? Math.ceil((domain.length * (labelGap + maxBounds[2])) / (rangeEnd - rangeStart - labelGap)) - : domain.length - 1; - - scaleTicks = (scale as BandScale).stepTicks(step); - - if ( - op.labelLastVisible && - (!scaleTicks.length || scaleTicks[scaleTicks.length - 1] !== domain[domain.length - 1]) - ) { - if ( - scaleTicks.length && - Math.abs(scale.scale(scaleTicks[scaleTicks.length - 1]) - scale.scale(domain[domain.length - 1])) < - maxBounds[2] - ) { - scaleTicks = scaleTicks.slice(0, -1); - } - scaleTicks.push(domain[domain.length - 1]); - } - } - } else { - scaleTicks = scale.domain(); - } - - return convertDomainToTickData(scaleTicks); -}; - -/** 计算合适的step */ -const getStep = ( - domain: any[], - labelBoundsList: OneDimensionalBounds[], - labelGap: number, - labelLastVisible: boolean, - defaultStep: number, - areAllBoundsSame: boolean -) => { - let resultDelCount = 0; - let resultStep = 0; - let resultTickCount = -1; - let minDiff = Number.MAX_VALUE; - - /** 验证在当前 step 下是否会产生重叠 */ - const validateStep = (step: number) => { - let success = true; - let ptr = 0; - do { - if (ptr + step < domain.length && boundsOverlap(labelBoundsList[ptr], labelBoundsList[ptr + step], labelGap)) { - success = false; - } - ptr += step; - } while (success && ptr < domain.length); - return success; - }; - - // 通过二分来寻找最小的step,使:如果在这个step下采样,轴标签互不遮挡 - const minValidStep = binaryFuzzySearchInNumberRange(defaultStep, domain.length, step => - validateStep(step) ? 1 : -1 - ); - - // 对 step 进行微调 - let step = minValidStep; - do { - if (step > minValidStep && !areAllBoundsSame) { - if (!validateStep(step)) { - step++; - continue; - } - } - if (labelLastVisible) { - const lastIndex = domain.length - 1; - let delCount = 0; - let ptr; - if (domain.length % step > 0) { - ptr = domain.length - (domain.length % step) + step; - } else { - ptr = domain.length; - } - do { - ptr -= step; // 获取最后一个label位置 - if (ptr === lastIndex || boundsOverlap(labelBoundsList[ptr], labelBoundsList[lastIndex], labelGap)) { - delCount++; - } else { - break; - } - } while (ptr > 0); - if (ptr === lastIndex) { - // 采到的最后的一个 label 刚好是最后一项,直接退出 - resultStep = step; - resultDelCount = delCount; - break; - } else { - // 尝试获取最均匀的结果,防止倒数第二项和最后一项有大的空档 - const tickCount = Math.floor(domain.length / step) - delCount + 1; - if (tickCount < resultTickCount) { - break; - } else { - resultTickCount = tickCount; - const distance1 = boundsDistance(labelBoundsList[ptr], labelBoundsList[lastIndex]); // 倒数第2项和最后一项的距离 - const distance2 = - ptr - step >= 0 ? boundsDistance(labelBoundsList[ptr - step], labelBoundsList[ptr]) : distance1; // 倒数第3项和倒数第2项的距离 - const diff = Math.abs(distance1 - distance2); - if (diff < minDiff) { - minDiff = diff; - resultStep = step; // 记录最均匀的 step - resultDelCount = delCount; - } - if (distance1 <= distance2) { - break; - } - } - } - } else { - resultStep = step; - break; - } - step++; - } while (step <= domain.length); - - return { - step: resultStep, - delCount: resultDelCount - }; -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/polar-angle.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/polar-angle.ts deleted file mode 100644 index 361072dda..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/discrete/polar-angle.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { BandScale } from '@visactor/vscale'; -import { isFunction, isValid, maxInArray, minInArray } from '@visactor/vutils'; -import type { IPolarTickDataOpt, ITickData } from '../interface'; -import { convertDomainToTickData, getPolarAngleLabelBounds, labelOverlap } from '../util'; -import type { AABBBounds } from '@visactor/vutils'; - -/** - * 对于离散轴: - * - 如果spec配了tickCount、forceTickCount、tickStep,则直接输出BandScale的ticks()、forceTicks()、stepTicks()结果; - * - 估算所有轴label的宽高并存为数组labelBoundsList; - * - 通过循环来寻找最小的step,使:如果在这个step下采样,轴标签互不遮挡(此处用到labelBoundsList和scale.range()); - * - * @param scale - * @param op - * @returns - */ -export const polarAngleAxisDiscreteTicks = (scale: BandScale, op: IPolarTickDataOpt): ITickData[] => { - const { tickCount, forceTickCount, tickStep, getRadius, labelOffset, labelGap = 0, labelStyle } = op; - const radius = getRadius?.(); - if (!radius) { - return convertDomainToTickData(scale.domain()); - } - - let scaleTicks; - if (isValid(tickStep)) { - scaleTicks = scale.stepTicks(tickStep); - } else if (isValid(forceTickCount)) { - scaleTicks = scale.forceTicks(forceTickCount); - } else if (isValid(tickCount)) { - const range = scale.range(); - const rangeSize = Math.abs(range[range.length - 1] - range[0]); - const count = isFunction(tickCount) ? tickCount({ axisLength: rangeSize, labelStyle }) : tickCount; - scaleTicks = scale.ticks(count); - } else if (op.sampling) { - const domain = scale.domain(); - const range = scale.range(); - - const labelBoundsList = getPolarAngleLabelBounds(scale, domain, op); - - const rangeStart = minInArray(range); - const rangeEnd = maxInArray(range); - - const axisLength = Math.abs(rangeEnd - rangeStart) * (radius + labelOffset); - const incrementUnit = axisLength / domain.length; - const { step, delCount } = getStep( - domain, - labelBoundsList, - labelGap, - Math.floor( - labelBoundsList.reduce((min, curBounds) => { - return Math.min(min, curBounds.width(), curBounds.height()); - }, Number.MAX_VALUE) / incrementUnit - ) // 给step赋上合适的初值,有效改善外层循环次数 - ); - - scaleTicks = (scale as BandScale).stepTicks(step); - scaleTicks = scaleTicks.slice(0, scaleTicks.length - delCount); - } else { - scaleTicks = scale.domain(); - } - - return convertDomainToTickData(scaleTicks); -}; - -/** 计算合适的step */ -const getStep = (domain: any[], labelBoundsList: AABBBounds[], labelGap: number, defaultStep: number) => { - let step = defaultStep; - // 通过循环来寻找最小的step,使:如果在这个step下采样,轴标签互不遮挡 - do { - let success = true; - step++; - let ptr = 0; - do { - if (ptr + step < domain.length && labelOverlap(labelBoundsList[ptr], labelBoundsList[ptr + step], labelGap)) { - success = false; - } - ptr += step; - } while (success && ptr < domain.length); - if (success) { - break; - } - } while (step <= domain.length); - - let delCount = 0; - if (domain.length > 2) { - let ptr = domain.length - (domain.length % step); - if (ptr >= domain.length) { - ptr -= step; - } - // 判断首尾是否互相覆盖 - while (ptr > 0 && labelOverlap(labelBoundsList[0], labelBoundsList[ptr])) { - delCount++; - ptr -= step; - } - } - - return { - step, - delCount - }; -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/index.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/index.ts deleted file mode 100644 index 549fb07f7..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { BandScale, ContinuousScale, IBaseScale } from '@visactor/vscale'; -// eslint-disable-next-line no-duplicate-imports -import { isContinuous, isDiscrete } from '@visactor/vscale'; -import { continuousTicks } from './continuous'; -import { linearDiscreteTicks } from './discrete/linear'; -import { polarAngleAxisDiscreteTicks } from './discrete/polar-angle'; -import type { ICartesianTickDataOpt, IPolarTickDataOpt, ITickData, ITickDataOpt } from './interface'; -import { convertDomainToTickData } from './util'; - -export * from './interface'; -export { convertDomainToTickData }; - -// 总入口 -export const ticks = (scale: IBaseScale, op: ITickDataOpt): ITickData[] => { - if (isContinuous(scale.type)) { - return continuousTicks(scale as ContinuousScale, op); - } else if (isDiscrete(scale.type)) { - if (op.coordinateType === 'cartesian') { - return linearDiscreteTicks(scale as BandScale, op as ICartesianTickDataOpt); - } else if (op.coordinateType === 'polar') { - if (op.axisOrientType === 'angle') { - return polarAngleAxisDiscreteTicks(scale as BandScale, op as IPolarTickDataOpt); - } - } - } - return convertDomainToTickData(scale.domain()); -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/interface.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/interface.ts deleted file mode 100644 index 1c70971cd..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/interface.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { ITextGraphicAttribute } from '@visactor/vrender-core'; - -export type CoordinateType = 'cartesian' | 'polar' | 'geo' | 'none'; -export type IOrientType = 'left' | 'top' | 'right' | 'bottom' | 'z'; -export type IPolarOrientType = 'radius' | 'angle'; - -export interface ITickDataOpt { - /** - * 是否进行轴采样 - */ - sampling?: boolean; - tickCount?: number | ((option: ITickCallbackOption) => number); - forceTickCount?: number; - tickStep?: number; - tickMode?: 'average' | 'd3' | string; - noDecimals?: boolean; - - coordinateType: CoordinateType; - axisOrientType: IOrientType | IPolarOrientType; - startAngle?: number; - - labelFormatter?: (value: any) => string; - labelStyle: ITextGraphicAttribute; - labelGap?: number; -} - -export interface ICartesianTickDataOpt extends ITickDataOpt { - axisOrientType: IOrientType; - labelLastVisible: boolean; - labelFlush: boolean; -} - -export interface IPolarTickDataOpt extends ITickDataOpt { - axisOrientType: IPolarOrientType; - getRadius: () => number; - labelOffset: number; - inside: boolean; -} - -export interface ITickData { - index: number; - value: number | string; - // label: string; -} - -type ITickCallbackOption = { - /** - * 坐标轴占据的画布大小。 - * 直角坐标系中为轴的宽度或高度。 - * 极坐标系中半径轴的长度。 - */ - axisLength?: number; - /** - * 轴标签的样式 - */ - labelStyle?: ITextGraphicAttribute; -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/util.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/util.ts deleted file mode 100644 index abc95029a..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/util.ts +++ /dev/null @@ -1,182 +0,0 @@ -import type { IBaseScale } from '@visactor/vscale'; -import type { IBoundsLike } from '@visactor/vutils'; -// eslint-disable-next-line no-duplicate-imports -import { AABBBounds, degreeToRadian } from '@visactor/vutils'; -import type { IGraphic, TextAlignType, TextBaselineType } from '@visactor/vrender-core'; -import { initTextMeasure } from '../../utils/text'; -import type { ICartesianTickDataOpt, IOrientType, IPolarTickDataOpt, ITickData } from './interface'; -import { getLabelPosition } from './utils/polar-label-position'; - -export const convertDomainToTickData = (domain: any[]): ITickData[] => { - const ticks = domain.map((t: number, index: number) => { - return { - index, - value: t - }; - }); - return ticks; -}; - -/** 判断两个label是否有重叠情况 */ -export const labelOverlap = (prevLabel: AABBBounds, nextLabel: AABBBounds, gap = 0): boolean => { - const prevBounds = new AABBBounds(prevLabel).expand(gap / 2); - const nextBounds = new AABBBounds(nextLabel).expand(gap / 2); - return prevBounds.intersects(nextBounds); -}; - -/** 判断两个不相交的label相隔的距离 */ -export const labelDistance = (prevLabel: AABBBounds, nextLabel: AABBBounds): [number, number] => { - let horizontal = 0; - if (prevLabel.x2 < nextLabel.x1) { - horizontal = nextLabel.x1 - prevLabel.x2; - } else if (nextLabel.x2 < prevLabel.x1) { - horizontal = prevLabel.x1 - nextLabel.x2; - } - - let vertical = 0; - if (prevLabel.y2 < nextLabel.y1) { - vertical = nextLabel.y1 - prevLabel.y2; - } else if (nextLabel.y2 < prevLabel.y1) { - vertical = prevLabel.y1 - nextLabel.y2; - } - - return [horizontal, vertical]; -}; - -export function intersect(a: IBoundsLike, b: IBoundsLike, sep: number) { - return sep > Math.max(b.x1 - a.x2, a.x1 - b.x2, b.y1 - a.y2, a.y1 - b.y2); -} - -export interface ILabelItem extends Pick { - value?: T; -} - -export function hasOverlap(items: ILabelItem[], pad: number): boolean { - for (let i = 1, n = items.length, a = items[0], b; i < n; a = b, ++i) { - b = items[i]; - if (intersect(a.AABBBounds, b.AABBBounds, pad)) { - return true; - } - } - return false; -} - -export const MIN_TICK_GAP = 12; - -export const getCartesianLabelBounds = (scale: IBaseScale, domain: any[], op: ICartesianTickDataOpt): AABBBounds[] => { - const { labelStyle, axisOrientType, labelFlush, labelFormatter, startAngle = 0 } = op; - let labelAngle = labelStyle.angle ?? 0; - if (labelStyle.direction === 'vertical') { - labelAngle += degreeToRadian(90); - } - const isHorizontal = ['bottom', 'top'].includes(axisOrientType); - const isVertical = ['left', 'right'].includes(axisOrientType); - let scaleX = 1; - let scaleY = 0; - if (isHorizontal) { - // nothing to update - } else if (isVertical) { - scaleX = 0; - scaleY = 1; - } else if (startAngle) { - scaleX = Math.cos(startAngle); - scaleY = -Math.sin(startAngle); - } - - const textMeasure = initTextMeasure(labelStyle); - const labelBoundsList = domain.map((v: any, i: number) => { - const str = labelFormatter ? labelFormatter(v) : `${v}`; - - // 估算文本宽高 - const { width, height } = textMeasure.quickMeasure(str); - const textWidth = Math.max(width, MIN_TICK_GAP); - const textHeight = Math.max(height, MIN_TICK_GAP); - - // 估算文本位置 - const pos = scale.scale(v); - const baseTextX = scaleX * pos; - const baseTextY = scaleY * pos; - let textX = baseTextX; - let textY = baseTextY; - - let align: TextAlignType; - if (labelFlush && isHorizontal && i === 0) { - align = 'left'; - } else if (labelFlush && isHorizontal && i === domain.length - 1) { - align = 'right'; - } else { - align = labelStyle.textAlign ?? 'center'; - } - if (align === 'right') { - textX -= textWidth; - } else if (align === 'center') { - textX -= textWidth / 2; - } - - let baseline: TextBaselineType; - if (labelFlush && isVertical && i === 0) { - baseline = 'top'; - } else if (labelFlush && isVertical && i === domain.length - 1) { - baseline = 'bottom'; - } else { - baseline = labelStyle.textBaseline ?? 'middle'; - } - if (baseline === 'bottom') { - textY -= textHeight; - } else if (baseline === 'middle') { - textY -= textHeight / 2; - } - - // 计算 label 包围盒 - const bounds = new AABBBounds().set(textX, textY, textX + textWidth, textY + textHeight); - - if (labelAngle) { - bounds.rotate(labelAngle, baseTextX, baseTextY); - } - - return bounds; - }); - - return labelBoundsList; -}; - -export const getPolarAngleLabelBounds = (scale: IBaseScale, domain: any[], op: IPolarTickDataOpt): AABBBounds[] => { - const { labelStyle, getRadius, labelOffset, labelFormatter, inside } = op; - const radius = getRadius?.(); - const labelAngle = labelStyle.angle ?? 0; - - const textMeasure = initTextMeasure(labelStyle); - const labelBoundsList = domain.map((v: any) => { - const str = labelFormatter ? labelFormatter(v) : `${v}`; - - // 估算文本宽高 - const { width, height } = textMeasure.quickMeasure(str); - const textWidth = Math.max(width, MIN_TICK_GAP); - const textHeight = Math.max(height, MIN_TICK_GAP); - - // 估算文本位置 - const angle = scale.scale(v); - let textX = 0; - let textY = 0; - const orient = { - align: labelStyle.textAlign ?? 'center', - baseline: labelStyle.textBaseline ?? 'middle' - }; - - const { x, y } = getLabelPosition(angle, { x: 0, y: 0 }, radius, labelOffset, inside, str, labelStyle); - textX = x + (orient.align === 'right' ? -textWidth : orient.align === 'center' ? -textWidth / 2 : 0); - textY = y + (orient.baseline === 'bottom' ? -textHeight : orient.baseline === 'middle' ? -textHeight / 2 : 0); - - // 计算 label 包围盒 - const bounds = new AABBBounds() - .set(textX, textY, textX + textWidth, textY + textHeight) - .rotate(labelAngle, textX + textWidth / 2, textY + textHeight / 2); - return bounds; - }); - - return labelBoundsList; -}; - -export const isAxisHorizontal = (axisOrientType: IOrientType) => { - return (['bottom', 'top', 'z'] as IOrientType[]).includes(axisOrientType); -}; diff --git a/packages/vtable/src/vutil-extension-temp/transform/tick-data/utils/polar-label-position.ts b/packages/vtable/src/vutil-extension-temp/transform/tick-data/utils/polar-label-position.ts deleted file mode 100644 index cef160c38..000000000 --- a/packages/vtable/src/vutil-extension-temp/transform/tick-data/utils/polar-label-position.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ITextGraphicAttribute } from '@visactor/vrender-core'; -import { getCircleLabelPosition, getCircleVerticalVector, getVerticalCoord } from '@visactor/vrender-components'; -import { polarToCartesian } from '@visactor/vutils'; - -export function getLabelPosition( - angle: number, - center: { x: number; y: number }, - radius: number, - labelOffset: number, - inside: boolean, - text: string | number, - style: Partial -) { - const point = polarToCartesian({ x: 0, y: 0 }, radius, angle); - const labelPoint = getVerticalCoord(point, getCircleVerticalVector(labelOffset, point, center, inside)); - const vector = getCircleVerticalVector(labelOffset || 1, labelPoint, center, inside); - return getCircleLabelPosition(labelPoint, vector, text, style); -} diff --git a/packages/vtable/src/vutil-extension-temp/utils/index.ts b/packages/vtable/src/vutil-extension-temp/utils/index.ts deleted file mode 100644 index 35641ed3a..000000000 --- a/packages/vtable/src/vutil-extension-temp/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './object'; -export * from './polar'; -export * from './text'; diff --git a/packages/vtable/src/vutil-extension-temp/utils/object.ts b/packages/vtable/src/vutil-extension-temp/utils/object.ts deleted file mode 100644 index 8798664d3..000000000 --- a/packages/vtable/src/vutil-extension-temp/utils/object.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { get, isArray, isFunction, isNil, isObject } from '@visactor/vutils'; - -/** - * 判断一个 spec 是否包含另一个 spec 片段 - * @param spec 原始 spec - * @param searchSpec 要匹配的 spec 片段 - */ -export const includeSpec = (spec: Partial, searchSpec: Partial): boolean => { - if (spec === searchSpec) { - return true; - } - if (isFunction(spec) || isFunction(searchSpec)) { - return false; - } - if (isArray(spec) && isArray(searchSpec)) { - return searchSpec.every(searchItem => spec.some(item => includeSpec(item, searchItem))); - } - if (isObject(spec) && isObject(searchSpec)) { - return Object.keys(searchSpec).every(key => includeSpec(spec[key], searchSpec[key])); - } - return false; -}; - -export const setProperty = (target: T, path: Array, value: any): T => { - if (isNil(path)) { - return target; - } - const key = path[0]; - if (isNil(key)) { - return target; - } - if (path.length === 1) { - target[key] = value; - return target; - } - if (isNil(target[key])) { - if (typeof path[1] === 'number') { - target[key] = []; - } else { - target[key] = {}; - } - } - return setProperty(target[key], path.slice(1), value); -}; - -export const getProperty = (target: any, path: Array, defaultValue?: T): T => { - if (isNil(path)) { - return undefined; - } - return get(target, path as string[], defaultValue) as T; -}; diff --git a/packages/vtable/src/vutil-extension-temp/utils/polar.ts b/packages/vtable/src/vutil-extension-temp/utils/polar.ts deleted file mode 100644 index c64ea2cf4..000000000 --- a/packages/vtable/src/vutil-extension-temp/utils/polar.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { TextAlignType, TextBaselineType } from '@visactor/vrender-core'; - -/** - * 角度标准化处理 - * @param angle 弧度角 - */ -export function normalizeAngle(angle: number): number { - while (angle < 0) { - angle += Math.PI * 2; - } - while (angle >= Math.PI * 2) { - angle -= Math.PI * 2; - } - return angle; -} - -/** - * 计算对应角度下的角度轴标签定位属性 - * @param angle 弧度角,需要注意是逆时针计算的 - * @returns - */ -export function angleLabelOrientAttribute(angle: number) { - let align: TextAlignType = 'center'; - let baseline: TextBaselineType = 'middle'; - - angle = normalizeAngle(angle); - - // left: 5/3 - 1/3; right: 2/3 - 4/3; center: 5/3 - 1/3 & 2/3 - 4/3 - if (angle >= Math.PI * (5 / 3) || angle <= Math.PI * (1 / 3)) { - align = 'left'; - } else if (angle >= Math.PI * (2 / 3) && angle <= Math.PI * (4 / 3)) { - align = 'right'; - } else { - align = 'center'; - } - - // bottom: 7/6 - 11/6; top: 1/6 - 5/6; middle: 11/6 - 1/6 & 5/6 - 7/6 - if (angle >= Math.PI * (7 / 6) && angle <= Math.PI * (11 / 6)) { - baseline = 'bottom'; - } else if (angle >= Math.PI * (1 / 6) && angle <= Math.PI * (5 / 6)) { - baseline = 'top'; - } else { - baseline = 'middle'; - } - - return { align, baseline }; -} diff --git a/packages/vtable/src/vutil-extension-temp/utils/text.ts b/packages/vtable/src/vutil-extension-temp/utils/text.ts deleted file mode 100644 index e39d0a183..000000000 --- a/packages/vtable/src/vutil-extension-temp/utils/text.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ITextMeasureOption } from '@visactor/vutils'; -// eslint-disable-next-line no-duplicate-imports -import { TextMeasure } from '@visactor/vutils'; -import type { ITextGraphicAttribute } from '@visactor/vrender-core'; -import { getTextBounds } from '@visactor/vrender-core'; - -export const initTextMeasure = ( - textSpec?: Partial, - option?: Partial, - useNaiveCanvas?: boolean, - defaultFontParams?: Partial -): TextMeasure => { - return new TextMeasure( - { - defaultFontParams: { - fontFamily: - // eslint-disable-next-line max-len - 'PingFang SC,Helvetica Neue,Microsoft Yahei,system-ui,-apple-system,segoe ui,Roboto,Helvetica,Arial,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol', - fontSize: 14, - ...defaultFontParams - }, - getTextBounds: useNaiveCanvas ? undefined : getTextBounds, - specialCharSet: '-/: .,@%\'"~' + TextMeasure.ALPHABET_CHAR_SET + TextMeasure.ALPHABET_CHAR_SET.toUpperCase(), - ...(option ?? {}) - }, - textSpec - ); -};